mirror of
https://github.com/apache/httpd.git
synced 2025-08-07 04:02:58 +03:00
On the trunk:
mod_md: v0.8.1 from github, new feats in CHANGES git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1806939 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
24
CHANGES
24
CHANGES
@@ -1,7 +1,29 @@
|
||||
-*- coding: utf-8 -*-
|
||||
Changes with Apache 2.5.0
|
||||
|
||||
*) mod_md: v0.7.0:
|
||||
*) mod_md: v0.8.1:
|
||||
- New directive ```MDPrivateKeys``` to specify the type and parameter to private key generation.
|
||||
Currently only 'RSA' is supported as type with an option number of bits >= 2048 as parameter.
|
||||
Simple test cases for config handling added.
|
||||
- Private RSA keys are now generated with 2048 bits by default. Use ```MDPrivateKeys``` for
|
||||
higher security.
|
||||
- IMPORTANT: store format change. The following changes will be made to an existing md store on
|
||||
first start with a new version (be it by mod_md in the server or a run by a new 'a2md'):
|
||||
- pkey.pem will be renamed to privkey.pem
|
||||
- cert.pem and chain.pem will be concatenated to pubcert.pem. The former files will remain,
|
||||
but no longer be used. They will disappear on next renewal.
|
||||
ADVICE: If the current store data is vital to you, please make a backup first!
|
||||
- Fixed test case clearing of store to keep key alive, enabling true random store key again.
|
||||
- Removed pun "Something, like certbot" from the User-Agent request header. Refs issue #34
|
||||
- Cleaned up reporting of missing/mismatched MDCertificateAgreement in the logs. This will
|
||||
no longer trigger early retries.
|
||||
- badNonce encounters are no longer reported as errors. Retries are attempted now silently.
|
||||
Refs github issue #35
|
||||
- new default MDRenewWindow. Instead of 14 days, the default is now a third before the end of
|
||||
the certificates lifetime. For the usual 90 days of Let's Encrypt certificates, this makes
|
||||
an effective renewal window of 30 days - as recommended by LE. Refs issue #30
|
||||
- Enabled conversion warnings if supported by compiler, eliminated several signed/unsigned
|
||||
warnings.
|
||||
- LIVE: the real Let's Encrypt CA is now live by default! If you need to experiment, configure
|
||||
MDCertificateAuthority https://acme-staging.api.letsencrypt.org/directory
|
||||
- When existing, complete certificates are renewed, the activation of the new ones is
|
||||
|
@@ -341,6 +341,40 @@ MDPortMap 80:- 443:5002
|
||||
</usage>
|
||||
</directivesynopsis>
|
||||
|
||||
<directivesynopsis>
|
||||
<name>MDPrivateKeys</name>
|
||||
<description></description>
|
||||
<syntax>MDPrivateKeys type [ params... ]</syntax>
|
||||
<default>MDPrivateKeys RSA 2048</default>
|
||||
<contextlist>
|
||||
<context>server config</context>
|
||||
</contextlist>
|
||||
<usage>
|
||||
<p>
|
||||
Defines what kind of private keys are generated for a managed domain and with
|
||||
what parameters. The only supported type right now is 'RSA' and the only parameter
|
||||
it takes is the number of bits used for the key.
|
||||
</p><p>
|
||||
The current (2017) recommendation is at least 2048 bits and a smaller number is
|
||||
not accepted here. Higher numbers offer longer security, but are computationally more
|
||||
expensive, e.g. increase the load on your server. That might or might not be an
|
||||
issue for you.
|
||||
</p><p>
|
||||
Other key types will be defined in the future.
|
||||
</p>
|
||||
<example><title>Example</title>
|
||||
<highlight language="config">
|
||||
MDPrivateKeys RSA 3072
|
||||
</highlight>
|
||||
</example>
|
||||
<p>
|
||||
Please note that this setting only has an effect on new keys. Any existing
|
||||
private key you have remains unaffected. Also, this only affects private keys
|
||||
generated for certificates. ACME account keys are unaffected by this.
|
||||
</p>
|
||||
</usage>
|
||||
</directivesynopsis>
|
||||
|
||||
<directivesynopsis>
|
||||
<name>MDRenewWindow</name>
|
||||
<description></description>
|
||||
|
@@ -25,8 +25,10 @@ struct md_cert_t;
|
||||
struct md_pkey_t;
|
||||
struct md_store_t;
|
||||
struct md_srv_conf_t;
|
||||
struct md_pkey_spec_t;
|
||||
|
||||
#define MD_TLSSNI01_DNS_SUFFIX ".acme.invalid"
|
||||
#define MD_PKEY_RSA_BITS_DEF 2048U
|
||||
|
||||
typedef enum {
|
||||
MD_S_UNKNOWN, /* MD has not been analysed yet */
|
||||
@@ -34,6 +36,7 @@ typedef enum {
|
||||
MD_S_COMPLETE, /* MD has all necessary information, can go live */
|
||||
MD_S_EXPIRED, /* MD is complete, but credentials have expired */
|
||||
MD_S_ERROR, /* MD data is flawed, unable to be processed as is */
|
||||
MD_S_MISSING, /* MD is missing config information, cannot proceed */
|
||||
} md_state_t;
|
||||
|
||||
typedef enum {
|
||||
@@ -70,7 +73,9 @@ struct md_t {
|
||||
|
||||
int transitive; /* != 0 iff VirtualHost names/aliases are auto-added */
|
||||
int drive_mode; /* mode of obtaining credentials */
|
||||
struct md_pkey_spec_t *pkey_spec;/* specification for generating new private keys */
|
||||
int must_staple; /* certificates should set the OCSP Must Staple extension */
|
||||
apr_interval_time_t renew_norm; /* if > 0, normalized cert lifetime */
|
||||
apr_interval_time_t renew_window;/* time before expiration that starts renewal */
|
||||
|
||||
const char *ca_url; /* url of CA certificate service */
|
||||
@@ -91,6 +96,7 @@ struct md_t {
|
||||
|
||||
#define MD_KEY_ACCOUNT "account"
|
||||
#define MD_KEY_AGREEMENT "agreement"
|
||||
#define MD_KEY_BITS "bits"
|
||||
#define MD_KEY_CA "ca"
|
||||
#define MD_KEY_CA_URL "ca-url"
|
||||
#define MD_KEY_CERT "cert"
|
||||
@@ -112,8 +118,10 @@ struct md_t {
|
||||
#define MD_KEY_KEYAUTHZ "keyAuthorization"
|
||||
#define MD_KEY_LOCATION "location"
|
||||
#define MD_KEY_NAME "name"
|
||||
#define MD_KEY_PKEY "privkey"
|
||||
#define MD_KEY_PROTO "proto"
|
||||
#define MD_KEY_REGISTRATION "registration"
|
||||
#define MD_KEY_RENEW_NORM "renew-norm"
|
||||
#define MD_KEY_RENEW_WINDOW "renew-window"
|
||||
#define MD_KEY_RESOURCE "resource"
|
||||
#define MD_KEY_STATE "state"
|
||||
@@ -129,7 +137,8 @@ struct md_t {
|
||||
#define MD_KEY_VERSION "version"
|
||||
|
||||
#define MD_FN_MD "md.json"
|
||||
#define MD_FN_PKEY "pkey.pem"
|
||||
#define MD_FN_PRIVKEY "privkey.pem"
|
||||
#define MD_FN_PUBCERT "pubcert.pem"
|
||||
#define MD_FN_CERT "cert.pem"
|
||||
#define MD_FN_CHAIN "chain.pem"
|
||||
#define MD_FN_HTTPD_JSON "httpd.json"
|
||||
@@ -230,9 +239,9 @@ md_t *md_from_json(struct md_json_t *json, apr_pool_t *p);
|
||||
|
||||
typedef struct md_creds_t md_creds_t;
|
||||
struct md_creds_t {
|
||||
struct md_pkey_t *privkey;
|
||||
struct apr_array_header_t *pubcert; /* complete md_cert* chain */
|
||||
struct md_cert_t *cert;
|
||||
struct md_pkey_t *pkey;
|
||||
struct apr_array_header_t *chain; /* list of md_cert* */
|
||||
int expired;
|
||||
};
|
||||
|
||||
|
@@ -111,9 +111,8 @@ apr_status_t md_acme_create(md_acme_t **pacme, apr_pool_t *p, const char *url)
|
||||
acme = apr_pcalloc(p, sizeof(*acme));
|
||||
acme->url = url;
|
||||
acme->p = p;
|
||||
acme->user_agent = apr_psprintf(p, "%s mod_md/%s (Something, like certbot)",
|
||||
acme->user_agent = apr_psprintf(p, "%s mod_md/%s",
|
||||
base_product, MOD_MD_VERSION);
|
||||
acme->pkey_bits = 4096;
|
||||
acme->max_retries = 3;
|
||||
|
||||
if (APR_SUCCESS != (rv = apr_uri_parse(p, url, &uri_parsed))) {
|
||||
@@ -261,8 +260,14 @@ static apr_status_t inspect_problem(md_acme_req_t *req, const md_http_response_t
|
||||
pdetail = md_json_gets(problem, "detail", NULL);
|
||||
req->rv = problem_status_get(ptype);
|
||||
|
||||
if (APR_STATUS_IS_EAGAIN(req->rv)) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, req->rv, req->p,
|
||||
"acme reports %s: %s", ptype, pdetail);
|
||||
}
|
||||
else {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, req->rv, req->p,
|
||||
"acme problem %s: %s", ptype, pdetail);
|
||||
}
|
||||
return req->rv;
|
||||
}
|
||||
}
|
||||
|
@@ -62,7 +62,6 @@ struct md_acme_t {
|
||||
|
||||
const char *nonce;
|
||||
int max_retries;
|
||||
unsigned int pkey_bits;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -126,8 +125,12 @@ apr_status_t md_acme_agree(md_acme_t *acme, apr_pool_t *p, const char *tos);
|
||||
* accounces the Tos URL it wants. If this is equal to the agreement specified,
|
||||
* the server is notified of this. If the server requires a ToS that the account
|
||||
* thinks it has already given, it is resend.
|
||||
*
|
||||
* If an agreement is required, different from the current one, APR_INCOMPLETE is
|
||||
* returned and the agreement url is returned in the parameter.
|
||||
*/
|
||||
apr_status_t md_acme_check_agreement(md_acme_t *acme, apr_pool_t *p, const char *agreement);
|
||||
apr_status_t md_acme_check_agreement(md_acme_t *acme, apr_pool_t *p,
|
||||
const char *agreement, const char **prequired);
|
||||
|
||||
/**
|
||||
* Get the ToS agreement for current account.
|
||||
|
@@ -327,6 +327,7 @@ static apr_status_t acct_register(md_acme_t *acme, apr_pool_t *p,
|
||||
apr_status_t rv;
|
||||
md_pkey_t *pkey;
|
||||
const char *err = NULL, *uri;
|
||||
md_pkey_spec_t spec;
|
||||
int i;
|
||||
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, "create new account");
|
||||
@@ -347,7 +348,10 @@ static apr_status_t acct_register(md_acme_t *acme, apr_pool_t *p,
|
||||
}
|
||||
}
|
||||
|
||||
if (APR_SUCCESS == (rv = md_pkey_gen_rsa(&pkey, acme->p, acme->pkey_bits))
|
||||
spec.type = MD_PKEY_TYPE_RSA;
|
||||
spec.params.rsa.bits = MD_ACME_ACCT_PKEY_BITS;
|
||||
|
||||
if (APR_SUCCESS == (rv = md_pkey_gen(&pkey, acme->p, &spec))
|
||||
&& APR_SUCCESS == (rv = acct_make(&acme->acct, p, acme->url, NULL, contacts))) {
|
||||
acct_ctx_t ctx;
|
||||
|
||||
@@ -614,11 +618,13 @@ static int agreement_required(md_acme_acct_t *acct)
|
||||
|| (acct->tos_required && strcmp(acct->tos_required, acct->agreement)));
|
||||
}
|
||||
|
||||
apr_status_t md_acme_check_agreement(md_acme_t *acme, apr_pool_t *p, const char *agreement)
|
||||
apr_status_t md_acme_check_agreement(md_acme_t *acme, apr_pool_t *p,
|
||||
const char *agreement, const char **prequired)
|
||||
{
|
||||
apr_status_t rv = APR_SUCCESS;
|
||||
|
||||
/* Check if (correct) Terms-of-Service for account were accepted */
|
||||
*prequired = NULL;
|
||||
if (agreement_required(acme->acct)) {
|
||||
const char *tos = acme->acct->tos_required;
|
||||
if (!tos) {
|
||||
@@ -642,10 +648,8 @@ apr_status_t md_acme_check_agreement(md_acme_t *acme, apr_pool_t *p, const char
|
||||
rv = md_acme_agree(acme, p, tos);
|
||||
}
|
||||
else {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, acme->p,
|
||||
"need to accept terms-of-service <%s> for account %s",
|
||||
tos, acme->acct->id);
|
||||
rv = APR_EACCES;
|
||||
*prequired = apr_pstrdup(p, tos);
|
||||
rv = APR_INCOMPLETE;
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
|
@@ -41,4 +41,8 @@ struct md_acme_acct_t {
|
||||
#define MD_FN_ACCOUNT "account.json"
|
||||
#define MD_FN_ACCT_KEY "account.pem"
|
||||
|
||||
/* ACME account private keys are always RSA and have that many bits. Since accounts
|
||||
* are expected to live long, better err on the safe side. */
|
||||
#define MD_ACME_ACCT_PKEY_BITS 3072
|
||||
|
||||
#endif /* md_acme_acct_h */
|
||||
|
@@ -91,10 +91,10 @@ apr_status_t md_acme_authz_set_remove(md_acme_authz_set_t *set, const char *doma
|
||||
for (i = 0; i < set->authzs->nelts; ++i) {
|
||||
authz = APR_ARRAY_IDX(set->authzs, i, md_acme_authz_t *);
|
||||
if (!apr_strnatcasecmp(domain, authz->domain)) {
|
||||
int n = i +1;
|
||||
int n = i + 1;
|
||||
if (n < set->authzs->nelts) {
|
||||
void **elems = (void **)set->authzs->elts;
|
||||
memmove(elems + i, elems + n, set->authzs->nelts - n);
|
||||
memmove(elems + i, elems + n, (size_t)(set->authzs->nelts - n));
|
||||
}
|
||||
--set->authzs->nelts;
|
||||
return APR_SUCCESS;
|
||||
@@ -292,7 +292,8 @@ static apr_status_t setup_key_authz(md_acme_authz_cha_t *cha, md_acme_authz_t *a
|
||||
}
|
||||
|
||||
static apr_status_t cha_http_01_setup(md_acme_authz_cha_t *cha, md_acme_authz_t *authz,
|
||||
md_acme_t *acme, md_store_t *store, apr_pool_t *p)
|
||||
md_acme_t *acme, md_store_t *store,
|
||||
md_pkey_spec_t *key_spec, apr_pool_t *p)
|
||||
{
|
||||
const char *data;
|
||||
apr_status_t rv;
|
||||
@@ -347,7 +348,8 @@ static apr_status_t setup_cha_dns(const char **pdns, md_acme_authz_cha_t *cha, a
|
||||
}
|
||||
|
||||
static apr_status_t cha_tls_sni_01_setup(md_acme_authz_cha_t *cha, md_acme_authz_t *authz,
|
||||
md_acme_t *acme, md_store_t *store, apr_pool_t *p)
|
||||
md_acme_t *acme, md_store_t *store,
|
||||
md_pkey_spec_t *key_spec, apr_pool_t *p)
|
||||
{
|
||||
md_cert_t *cha_cert;
|
||||
md_pkey_t *cha_key;
|
||||
@@ -365,7 +367,7 @@ static apr_status_t cha_tls_sni_01_setup(md_acme_authz_cha_t *cha, md_acme_authz
|
||||
if ((APR_SUCCESS == rv && !md_cert_covers_domain(cha_cert, cha_dns))
|
||||
|| APR_STATUS_IS_ENOENT(rv)) {
|
||||
|
||||
if (APR_SUCCESS != (rv = md_pkey_gen_rsa(&cha_key, p, acme->pkey_bits))) {
|
||||
if (APR_SUCCESS != (rv = md_pkey_gen(&cha_key, p, key_spec))) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: create tls-sni-01 challgenge key",
|
||||
authz->domain);
|
||||
goto out;
|
||||
@@ -405,7 +407,8 @@ out:
|
||||
}
|
||||
|
||||
typedef apr_status_t cha_starter(md_acme_authz_cha_t *cha, md_acme_authz_t *authz,
|
||||
md_acme_t *acme, md_store_t *store, apr_pool_t *p);
|
||||
md_acme_t *acme, md_store_t *store,
|
||||
md_pkey_spec_t *key_spec, apr_pool_t *p);
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
@@ -449,7 +452,8 @@ static apr_status_t find_type(void *baton, size_t index, md_json_t *json)
|
||||
}
|
||||
|
||||
apr_status_t md_acme_authz_respond(md_acme_authz_t *authz, md_acme_t *acme, md_store_t *store,
|
||||
apr_array_header_t *challenges, apr_pool_t *p)
|
||||
apr_array_header_t *challenges,
|
||||
md_pkey_spec_t *key_spec, apr_pool_t *p)
|
||||
{
|
||||
apr_status_t rv;
|
||||
int i;
|
||||
@@ -485,7 +489,7 @@ apr_status_t md_acme_authz_respond(md_acme_authz_t *authz, md_acme_t *acme, md_s
|
||||
|
||||
for (i = 0; i < CHA_TYPES_LEN; ++i) {
|
||||
if (!apr_strnatcasecmp(CHA_TYPES[i].name, fctx.accepted->type)) {
|
||||
return CHA_TYPES[i].start(fctx.accepted, authz, acme, store, p);
|
||||
return CHA_TYPES[i].start(fctx.accepted, authz, acme, store, key_spec, p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,7 +564,7 @@ md_acme_authz_t *md_acme_authz_from_json(struct md_json_t *json, apr_pool_t *p)
|
||||
authz->domain = md_json_dups(p, json, MD_KEY_DOMAIN, NULL);
|
||||
authz->location = md_json_dups(p, json, MD_KEY_LOCATION, NULL);
|
||||
authz->dir = md_json_dups(p, json, MD_KEY_DIR, NULL);
|
||||
authz->state = (int)md_json_getl(json, MD_KEY_STATE, NULL);
|
||||
authz->state = (md_acme_authz_state_t)md_json_getl(json, MD_KEY_STATE, NULL);
|
||||
return authz;
|
||||
}
|
||||
return NULL;
|
||||
@@ -631,7 +635,7 @@ static apr_status_t p_save(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_lis
|
||||
const char *md_name;
|
||||
int create;
|
||||
|
||||
group = va_arg(ap, int);
|
||||
group = (md_store_group_t)va_arg(ap, int);
|
||||
md_name = va_arg(ap, const char *);
|
||||
set = va_arg(ap, md_acme_authz_set_t *);
|
||||
create = va_arg(ap, int);
|
||||
@@ -657,7 +661,7 @@ static apr_status_t p_purge(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_li
|
||||
const char *md_name;
|
||||
int i;
|
||||
|
||||
group = va_arg(ap, int);
|
||||
group = (md_store_group_t)va_arg(ap, int);
|
||||
md_name = va_arg(ap, const char *);
|
||||
|
||||
if (APR_SUCCESS == md_acme_authz_set_load(store, group, md_name, &authz_set, p)) {
|
||||
|
@@ -21,6 +21,7 @@ struct md_acme_t;
|
||||
struct md_acme_acct_t;
|
||||
struct md_json_t;
|
||||
struct md_store_t;
|
||||
struct md_pkey_spec_t;
|
||||
|
||||
typedef struct md_acme_challenge_t md_acme_challenge_t;
|
||||
|
||||
@@ -67,8 +68,8 @@ apr_status_t md_acme_authz_update(md_acme_authz_t *authz, struct md_acme_t *acme
|
||||
struct md_store_t *store, apr_pool_t *p);
|
||||
|
||||
apr_status_t md_acme_authz_respond(md_acme_authz_t *authz, struct md_acme_t *acme,
|
||||
struct md_store_t *store,
|
||||
apr_array_header_t *challenges, apr_pool_t *p);
|
||||
struct md_store_t *store, apr_array_header_t *challenges,
|
||||
struct md_pkey_spec_t *key_spec, apr_pool_t *p);
|
||||
apr_status_t md_acme_authz_del(md_acme_authz_t *authz, struct md_acme_t *acme,
|
||||
struct md_store_t *store, apr_pool_t *p);
|
||||
|
||||
|
@@ -42,9 +42,11 @@ typedef struct {
|
||||
const char *phase;
|
||||
int complete;
|
||||
|
||||
md_pkey_t *pkey;
|
||||
md_cert_t *cert;
|
||||
apr_array_header_t *chain;
|
||||
md_pkey_t *privkey; /* the new private key */
|
||||
apr_array_header_t *pubcert; /* the new certificate + chain certs */
|
||||
|
||||
md_cert_t *cert; /* the new certificate */
|
||||
apr_array_header_t *chain; /* the chain certificates */
|
||||
|
||||
md_acme_t *acme;
|
||||
md_t *md;
|
||||
@@ -122,8 +124,8 @@ static apr_status_t ad_set_acct(md_proto_driver_t *d)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (APR_SUCCESS == (rv = md_acme_create_acct(ad->acme, d->p,
|
||||
md->contacts, md->ca_agreement))
|
||||
if (APR_SUCCESS == (rv = md_acme_create_acct(ad->acme, d->p, md->contacts,
|
||||
md->ca_agreement))
|
||||
&& APR_SUCCESS == (rv = md_acme_acct_save_staged(ad->acme, d->store, md, d->p))) {
|
||||
md->ca_account = MD_ACME_ACCT_STAGED;
|
||||
update = 1;
|
||||
@@ -267,11 +269,13 @@ static apr_status_t ad_start_challenges(md_proto_driver_t *d)
|
||||
switch (authz->state) {
|
||||
case MD_ACME_AUTHZ_S_VALID:
|
||||
break;
|
||||
case MD_ACME_AUTHZ_S_PENDING:
|
||||
|
||||
rv = md_acme_authz_respond(authz, ad->acme, d->store, ad->ca_challenges, d->p);
|
||||
case MD_ACME_AUTHZ_S_PENDING:
|
||||
rv = md_acme_authz_respond(authz, ad->acme, d->store, ad->ca_challenges,
|
||||
d->md->pkey_spec, d->p);
|
||||
changed = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
rv = APR_EINVAL;
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, d->p,
|
||||
@@ -464,22 +468,22 @@ static apr_status_t csr_req(md_acme_t *acme, const md_http_response_t *res, void
|
||||
static apr_status_t ad_setup_certificate(md_proto_driver_t *d)
|
||||
{
|
||||
md_acme_driver_t *ad = d->baton;
|
||||
md_pkey_t *pkey;
|
||||
md_pkey_t *privkey;
|
||||
apr_status_t rv;
|
||||
|
||||
ad->phase = "setup cert pkey";
|
||||
ad->phase = "setup cert privkey";
|
||||
|
||||
rv = md_pkey_load(d->store, MD_SG_STAGING, ad->md->name, &pkey, d->p);
|
||||
rv = md_pkey_load(d->store, MD_SG_STAGING, ad->md->name, &privkey, d->p);
|
||||
if (APR_STATUS_IS_ENOENT(rv)) {
|
||||
if (APR_SUCCESS == (rv = md_pkey_gen_rsa(&pkey, d->p, ad->acme->pkey_bits))) {
|
||||
rv = md_pkey_save(d->store, d->p, MD_SG_STAGING, ad->md->name, pkey, 1);
|
||||
if (APR_SUCCESS == (rv = md_pkey_gen(&privkey, d->p, d->md->pkey_spec))) {
|
||||
rv = md_pkey_save(d->store, d->p, MD_SG_STAGING, ad->md->name, privkey, 1);
|
||||
}
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: generate pkey", ad->md->name);
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: generate privkey", ad->md->name);
|
||||
}
|
||||
|
||||
if (APR_SUCCESS == rv) {
|
||||
ad->phase = "setup csr";
|
||||
rv = md_cert_req_create(&ad->csr_der_64, ad->md, pkey, d->p);
|
||||
rv = md_cert_req_create(&ad->csr_der_64, ad->md, privkey, d->p);
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: create CSR", ad->md->name);
|
||||
}
|
||||
|
||||
@@ -674,6 +678,12 @@ static apr_status_t acme_stage(md_proto_driver_t *d)
|
||||
ad->md = NULL;
|
||||
}
|
||||
|
||||
if (ad->md && ad->md->state == MD_S_MISSING) {
|
||||
/* There is config information missing. It makes no sense to drive this MD further */
|
||||
rv = APR_INCOMPLETE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ad->md) {
|
||||
/* staging in progress. look for new ACME account information collected there */
|
||||
rv = md_reg_creds_get(&ad->ncreds, d->reg, MD_SG_STAGING, d->md, d->p);
|
||||
@@ -684,7 +694,7 @@ static apr_status_t acme_stage(md_proto_driver_t *d)
|
||||
}
|
||||
|
||||
/* Find out where we're at with this managed domain */
|
||||
if (ad->ncreds && ad->ncreds->pkey && ad->ncreds->cert && ad->ncreds->chain) {
|
||||
if (ad->ncreds && ad->ncreds->privkey && ad->ncreds->pubcert) {
|
||||
/* There is a full set staged, to be loaded */
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p, "%s: all data staged", d->md->name);
|
||||
renew = 0;
|
||||
@@ -724,11 +734,32 @@ static apr_status_t acme_stage(md_proto_driver_t *d)
|
||||
* requests for new authorizations are denied. ToS may change during the
|
||||
* lifetime of an account */
|
||||
if (APR_SUCCESS == rv) {
|
||||
const char *required;
|
||||
|
||||
ad->phase = "check agreement";
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p,
|
||||
"%s: check Terms-of-Service agreement", d->md->name);
|
||||
|
||||
rv = md_acme_check_agreement(ad->acme, d->p, ad->md->ca_agreement);
|
||||
rv = md_acme_check_agreement(ad->acme, d->p, ad->md->ca_agreement, &required);
|
||||
|
||||
if (APR_STATUS_IS_INCOMPLETE(rv) && required) {
|
||||
/* The CA wants the user to agree to Terms-of-Services. Until the user
|
||||
* has reconfigured and restarted the server, this MD cannot be
|
||||
* driven further */
|
||||
ad->md->state = MD_S_MISSING;
|
||||
md_save(d->store, d->p, MD_SG_STAGING, ad->md, 0);
|
||||
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, d->p,
|
||||
"%s: the CA requires you to accept the terms-of-service "
|
||||
"as specified in <%s>. "
|
||||
"Please read the document that you find at that URL and, "
|
||||
"if you agree to the conditions, configure "
|
||||
"\"MDCertificateAgreement url\" "
|
||||
"with exactly that URL in your Apache. "
|
||||
"Then (graceful) restart the server to activate.",
|
||||
ad->md->name, required);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we know a cert's location, try to get it. Previous download might
|
||||
@@ -779,6 +810,10 @@ static apr_status_t acme_stage(md_proto_driver_t *d)
|
||||
|
||||
}
|
||||
|
||||
if (APR_SUCCESS == rv && !ad->chain) {
|
||||
/* have we created this already? */
|
||||
md_chain_load(d->store, MD_SG_STAGING, ad->md->name, &ad->chain, d->p);
|
||||
}
|
||||
if (APR_SUCCESS == rv && !ad->chain) {
|
||||
ad->phase = "install chain";
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p,
|
||||
@@ -786,6 +821,18 @@ static apr_status_t acme_stage(md_proto_driver_t *d)
|
||||
rv = ad_chain_install(d);
|
||||
}
|
||||
|
||||
if (APR_SUCCESS == rv && !ad->pubcert) {
|
||||
/* have we created this already? */
|
||||
md_pubcert_load(d->store, MD_SG_STAGING, ad->md->name, &ad->pubcert, d->p);
|
||||
}
|
||||
if (APR_SUCCESS == rv && !ad->pubcert) {
|
||||
/* combine cert + chain into the pubcert */
|
||||
ad->pubcert = apr_array_make(d->p, ad->chain->nelts + 1, sizeof(md_cert_t*));
|
||||
APR_ARRAY_PUSH(ad->pubcert, md_cert_t *) = ad->cert;
|
||||
apr_array_cat(ad->pubcert, ad->chain);
|
||||
rv = md_pubcert_save(d->store, d->p, MD_SG_STAGING, ad->md->name, ad->pubcert, 0);
|
||||
}
|
||||
|
||||
if (APR_SUCCESS == rv && ad->cert) {
|
||||
apr_time_t now = apr_time_now();
|
||||
apr_interval_time_t max_delay, delay_activation;
|
||||
@@ -832,10 +879,9 @@ static apr_status_t acme_preload(md_store_t *store, md_store_group_t load_group,
|
||||
const char *name, apr_pool_t *p)
|
||||
{
|
||||
apr_status_t rv;
|
||||
md_pkey_t *pkey, *acct_key;
|
||||
md_pkey_t *privkey, *acct_key;
|
||||
md_t *md;
|
||||
md_cert_t *cert;
|
||||
apr_array_header_t *chain;
|
||||
apr_array_header_t *pubcert;
|
||||
struct md_acme_acct_t *acct;
|
||||
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, "%s: preload start", name);
|
||||
@@ -852,18 +898,14 @@ static apr_status_t acme_preload(md_store_t *store, md_store_group_t load_group,
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "%s: loading md json", name);
|
||||
return rv;
|
||||
}
|
||||
if (APR_SUCCESS != (rv = md_cert_load(store, MD_SG_STAGING, name, &cert, p))) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "%s: loading certificate", name);
|
||||
return rv;
|
||||
}
|
||||
if (APR_SUCCESS != (rv = md_chain_load(store, MD_SG_STAGING, name, &chain, p))) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "%s: loading cert chain", name);
|
||||
return rv;
|
||||
}
|
||||
if (APR_SUCCESS != (rv = md_pkey_load(store, MD_SG_STAGING, name, &pkey, p))) {
|
||||
if (APR_SUCCESS != (rv = md_pkey_load(store, MD_SG_STAGING, name, &privkey, p))) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "%s: loading staging private key", name);
|
||||
return rv;
|
||||
}
|
||||
if (APR_SUCCESS != (rv = md_pubcert_load(store, MD_SG_STAGING, name, &pubcert, p))) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "%s: loading pubcert", name);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* See if staging holds a new or modified account data */
|
||||
rv = md_acme_acct_load(&acct, &acct_key, store, MD_SG_STAGING, name, p);
|
||||
@@ -904,16 +946,12 @@ static apr_status_t acme_preload(md_store_t *store, md_store_group_t load_group,
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: saving md json", name);
|
||||
return rv;
|
||||
}
|
||||
if (APR_SUCCESS != (rv = md_cert_save(store, p, load_group, name, cert, 1))) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: saving certificate", name);
|
||||
return rv;
|
||||
}
|
||||
if (APR_SUCCESS != (rv = md_chain_save(store, p, load_group, name, chain, 1))) {
|
||||
if (APR_SUCCESS != (rv = md_pubcert_save(store, p, load_group, name, pubcert, 1))) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: saving cert chain", name);
|
||||
return rv;
|
||||
}
|
||||
if (APR_SUCCESS != (rv = md_pkey_save(store, p, load_group, name, pkey, 1))) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: saving domain private key", name);
|
||||
if (APR_SUCCESS != (rv = md_pkey_save(store, p, load_group, name, privkey, 1))) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: saving private key", name);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@@ -103,7 +103,7 @@ static apr_status_t cmd_reg_list(md_cmd_ctx *ctx, const md_cmd_t *cmd)
|
||||
else {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_TRACE4, 0, ctx->p, "list do");
|
||||
md_reg_do(list_add_md, mdlist, ctx->reg, ctx->p);
|
||||
qsort(mdlist->elts, mdlist->nelts, sizeof(md_t *), md_name_cmp);
|
||||
qsort(mdlist->elts, (size_t)mdlist->nelts, sizeof(md_t *), md_name_cmp);
|
||||
|
||||
for (i = 0; i < mdlist->nelts; ++i) {
|
||||
md = APR_ARRAY_IDX(mdlist, i, const md_t*);
|
||||
@@ -317,7 +317,7 @@ static apr_status_t cmd_reg_drive(md_cmd_ctx *ctx, const md_cmd_t *cmd)
|
||||
}
|
||||
else {
|
||||
md_reg_do(list_add_md, mdlist, ctx->reg, ctx->p);
|
||||
qsort(mdlist->elts, mdlist->nelts, sizeof(md_t *), md_name_cmp);
|
||||
qsort(mdlist->elts, (size_t)mdlist->nelts, sizeof(md_t *), md_name_cmp);
|
||||
}
|
||||
|
||||
rv = APR_SUCCESS;
|
||||
|
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "md_json.h"
|
||||
#include "md.h"
|
||||
#include "md_crypt.h"
|
||||
#include "md_log.h"
|
||||
#include "md_store.h"
|
||||
#include "md_util.h"
|
||||
@@ -229,6 +230,8 @@ md_t *md_clone(apr_pool_t *p, const md_t *src)
|
||||
md->name = apr_pstrdup(p, src->name);
|
||||
md->drive_mode = src->drive_mode;
|
||||
md->domains = md_array_str_compact(p, src->domains, 0);
|
||||
md->pkey_spec = src->pkey_spec;
|
||||
md->renew_norm = src->renew_norm;
|
||||
md->renew_window = src->renew_window;
|
||||
md->contacts = md_array_str_clone(p, src->contacts);
|
||||
if (src->ca_url) md->ca_url = apr_pstrdup(p, src->ca_url);
|
||||
@@ -253,8 +256,10 @@ md_t *md_merge(apr_pool_t *p, const md_t *add, const md_t *base)
|
||||
n->ca_proto = add->ca_proto? add->ca_proto : base->ca_proto;
|
||||
n->ca_agreement = add->ca_agreement? add->ca_agreement : base->ca_agreement;
|
||||
n->drive_mode = (add->drive_mode != MD_DRIVE_DEFAULT)? add->drive_mode : base->drive_mode;
|
||||
n->renew_window = (add->renew_window <= 0)? add->renew_window : base->renew_window;
|
||||
n->transitive = (add->transitive < 0)? add->transitive : base->transitive;
|
||||
n->pkey_spec = add->pkey_spec? add->pkey_spec : base->pkey_spec;
|
||||
n->renew_norm = (add->renew_norm > 0)? add->renew_norm : base->renew_norm;
|
||||
n->renew_window = (add->renew_window > 0)? add->renew_window : base->renew_window;
|
||||
n->transitive = (add->transitive >= 0)? add->transitive : base->transitive;
|
||||
if (add->ca_challenges) {
|
||||
n->ca_challenges = apr_array_copy(p, add->ca_challenges);
|
||||
}
|
||||
@@ -264,7 +269,6 @@ md_t *md_merge(apr_pool_t *p, const md_t *add, const md_t *base)
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************************************/
|
||||
/* format conversion */
|
||||
|
||||
@@ -284,6 +288,9 @@ md_json_t *md_to_json(const md_t *md, apr_pool_t *p)
|
||||
if (md->cert_url) {
|
||||
md_json_sets(md->cert_url, json, MD_KEY_CERT, MD_KEY_URL, NULL);
|
||||
}
|
||||
if (md->pkey_spec) {
|
||||
md_json_setj(md_pkey_spec_to_json(md->pkey_spec, p), json, MD_KEY_PKEY, NULL);
|
||||
}
|
||||
md_json_setl(md->state, json, MD_KEY_STATE, NULL);
|
||||
md_json_setl(md->drive_mode, json, MD_KEY_DRIVE_MODE, NULL);
|
||||
if (md->expires > 0) {
|
||||
@@ -296,7 +303,13 @@ md_json_t *md_to_json(const md_t *md, apr_pool_t *p)
|
||||
apr_rfc822_date(ts, md->valid_from);
|
||||
md_json_sets(ts, json, MD_KEY_CERT, MD_KEY_VALID_FROM, NULL);
|
||||
}
|
||||
md_json_setl(apr_time_sec(md->renew_window), json, MD_KEY_RENEW_WINDOW, NULL);
|
||||
if (md->renew_norm > 0) {
|
||||
md_json_setl((long)apr_time_sec(md->renew_norm), json, MD_KEY_RENEW_NORM, NULL);
|
||||
md_json_setl((long)apr_time_sec(md->renew_window), json, MD_KEY_RENEW_WINDOW, NULL);
|
||||
}
|
||||
else {
|
||||
md_json_setl((long)apr_time_sec(md->renew_window), json, MD_KEY_RENEW_WINDOW, NULL);
|
||||
}
|
||||
if (md->ca_challenges && md->ca_challenges->nelts > 0) {
|
||||
apr_array_header_t *na;
|
||||
na = md_array_str_compact(p, md->ca_challenges, 0);
|
||||
@@ -320,7 +333,10 @@ md_t *md_from_json(md_json_t *json, apr_pool_t *p)
|
||||
md->ca_url = md_json_dups(p, json, MD_KEY_CA, MD_KEY_URL, NULL);
|
||||
md->ca_agreement = md_json_dups(p, json, MD_KEY_CA, MD_KEY_AGREEMENT, NULL);
|
||||
md->cert_url = md_json_dups(p, json, MD_KEY_CERT, MD_KEY_URL, NULL);
|
||||
md->state = (int)md_json_getl(json, MD_KEY_STATE, NULL);
|
||||
if (md_json_has_key(json, MD_KEY_PKEY, MD_KEY_TYPE, NULL)) {
|
||||
md->pkey_spec = md_pkey_spec_from_json(md_json_getj(json, MD_KEY_PKEY, NULL), p);
|
||||
}
|
||||
md->state = (md_state_t)md_json_getl(json, MD_KEY_STATE, NULL);
|
||||
md->drive_mode = (int)md_json_getl(json, MD_KEY_DRIVE_MODE, NULL);
|
||||
md->domains = md_array_str_compact(p, md->domains, 0);
|
||||
md->transitive = (int)md_json_getl(json, MD_KEY_TRANSITIVE, NULL);
|
||||
@@ -332,6 +348,7 @@ md_t *md_from_json(md_json_t *json, apr_pool_t *p)
|
||||
if (s && *s) {
|
||||
md->valid_from = apr_date_parse_rfc(s);
|
||||
}
|
||||
md->renew_norm = apr_time_from_sec(md_json_getl(json, MD_KEY_RENEW_NORM, NULL));
|
||||
md->renew_window = apr_time_from_sec(md_json_getl(json, MD_KEY_RENEW_WINDOW, NULL));
|
||||
if (md_json_has_key(json, MD_KEY_CA, MD_KEY_CHALLENGES, NULL)) {
|
||||
md->ca_challenges = apr_array_make(p, 5, sizeof(const char*));
|
||||
|
@@ -31,6 +31,7 @@
|
||||
|
||||
#include "md.h"
|
||||
#include "md_crypt.h"
|
||||
#include "md_json.h"
|
||||
#include "md_log.h"
|
||||
#include "md_http.h"
|
||||
#include "md_util.h"
|
||||
@@ -83,7 +84,7 @@ static void seed_RAND(int pid)
|
||||
{
|
||||
unsigned char stackdata[256];
|
||||
/* stolen from mod_ssl/ssl_engine_rand.c */
|
||||
apr_size_t n, l;
|
||||
int n;
|
||||
struct {
|
||||
time_t t;
|
||||
pid_t pid;
|
||||
@@ -99,8 +100,7 @@ static void seed_RAND(int pid)
|
||||
*/
|
||||
my_seed.pid = pid;
|
||||
|
||||
l = sizeof(my_seed);
|
||||
RAND_seed((unsigned char *)&my_seed, l);
|
||||
RAND_seed((unsigned char *)&my_seed, sizeof(my_seed));
|
||||
|
||||
/*
|
||||
* seed in some current state of the run-time stack (128 bytes)
|
||||
@@ -173,7 +173,7 @@ static int pem_passwd(char *buf, int size, int rwflag, void *baton)
|
||||
if (ctx->pass_len < size) {
|
||||
size = (int)ctx->pass_len;
|
||||
}
|
||||
memcpy(buf, ctx->pass_phrase, size);
|
||||
memcpy(buf, ctx->pass_phrase, (size_t)size);
|
||||
}
|
||||
return ctx->pass_len;
|
||||
}
|
||||
@@ -181,6 +181,50 @@ static int pem_passwd(char *buf, int size, int rwflag, void *baton)
|
||||
/**************************************************************************************************/
|
||||
/* private keys */
|
||||
|
||||
md_json_t *md_pkey_spec_to_json(const md_pkey_spec_t *spec, apr_pool_t *p)
|
||||
{
|
||||
md_json_t *json = md_json_create(p);
|
||||
if (json) {
|
||||
switch (spec->type) {
|
||||
case MD_PKEY_TYPE_DEFAULT:
|
||||
md_json_sets("Default", json, MD_KEY_TYPE, NULL);
|
||||
break;
|
||||
case MD_PKEY_TYPE_RSA:
|
||||
md_json_sets("RSA", json, MD_KEY_TYPE, NULL);
|
||||
if (spec->params.rsa.bits > 2048) {
|
||||
md_json_setl(spec->params.rsa.bits, json, MD_KEY_BITS, NULL);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
md_json_sets("Unsupported", json, MD_KEY_TYPE, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
md_pkey_spec_t *md_pkey_spec_from_json(struct md_json_t *json, apr_pool_t *p)
|
||||
{
|
||||
md_pkey_spec_t *spec = apr_pcalloc(p, sizeof(*spec));
|
||||
const char *s;
|
||||
long l;
|
||||
|
||||
if (spec) {
|
||||
s = md_json_gets(json, MD_KEY_TYPE, NULL);
|
||||
if (!s || !apr_strnatcasecmp("Default", s)) {
|
||||
spec->type = MD_PKEY_TYPE_DEFAULT;
|
||||
}
|
||||
else if (!apr_strnatcasecmp("RSA", s)) {
|
||||
spec->type = MD_PKEY_TYPE_RSA;
|
||||
l = md_json_getl(json, MD_KEY_BITS, NULL);
|
||||
if (l > 2048) {
|
||||
spec->params.rsa.bits = (unsigned int)l;
|
||||
}
|
||||
}
|
||||
}
|
||||
return spec;
|
||||
}
|
||||
|
||||
static md_pkey_t *make_pkey(apr_pool_t *p)
|
||||
{
|
||||
md_pkey_t *pkey = apr_pcalloc(p, sizeof(*pkey));
|
||||
@@ -231,7 +275,7 @@ apr_status_t md_pkey_fload(md_pkey_t **ppkey, apr_pool_t *p,
|
||||
apr_pool_cleanup_register(p, pkey, pkey_cleanup, apr_pool_cleanup_null);
|
||||
}
|
||||
else {
|
||||
long err = ERR_get_error();
|
||||
unsigned long err = ERR_get_error();
|
||||
rv = APR_EINVAL;
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, p,
|
||||
"error loading pkey %s: %s (pass phrase was %snull)", fname,
|
||||
@@ -251,6 +295,7 @@ static apr_status_t pkey_to_buffer(buffer *buffer, md_pkey_t *pkey, apr_pool_t *
|
||||
void *cb_baton = NULL;
|
||||
passwd_ctx ctx;
|
||||
unsigned long err;
|
||||
int i;
|
||||
|
||||
if (!bio) {
|
||||
return APR_ENOMEM;
|
||||
@@ -278,11 +323,12 @@ static apr_status_t pkey_to_buffer(buffer *buffer, md_pkey_t *pkey, apr_pool_t *
|
||||
return APR_EINVAL;
|
||||
}
|
||||
|
||||
buffer->len = BIO_pending(bio);
|
||||
if (buffer->len > 0) {
|
||||
buffer->data = apr_palloc(p, buffer->len+1);
|
||||
buffer->len = BIO_read(bio, buffer->data, (int)buffer->len);
|
||||
buffer->data[buffer->len] = '\0';
|
||||
i = BIO_pending(bio);
|
||||
if (i > 0) {
|
||||
buffer->data = apr_palloc(p, (apr_size_t)i + 1);
|
||||
i = BIO_read(bio, buffer->data, i);
|
||||
buffer->data[i] = '\0';
|
||||
buffer->len = (apr_size_t)i;
|
||||
}
|
||||
BIO_free(bio);
|
||||
return APR_SUCCESS;
|
||||
@@ -303,7 +349,7 @@ apr_status_t md_pkey_fsave(md_pkey_t *pkey, apr_pool_t *p,
|
||||
return rv;
|
||||
}
|
||||
|
||||
apr_status_t md_pkey_gen_rsa(md_pkey_t **ppkey, apr_pool_t *p, int bits)
|
||||
static apr_status_t gen_rsa(md_pkey_t **ppkey, apr_pool_t *p, unsigned int bits)
|
||||
{
|
||||
EVP_PKEY_CTX *ctx = NULL;
|
||||
apr_status_t rv;
|
||||
@@ -312,7 +358,7 @@ apr_status_t md_pkey_gen_rsa(md_pkey_t **ppkey, apr_pool_t *p, int bits)
|
||||
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
|
||||
if (ctx
|
||||
&& EVP_PKEY_keygen_init(ctx) >= 0
|
||||
&& EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) >= 0
|
||||
&& EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, (int)bits) >= 0
|
||||
&& EVP_PKEY_keygen(ctx, &(*ppkey)->pkey) >= 0) {
|
||||
rv = APR_SUCCESS;
|
||||
}
|
||||
@@ -328,6 +374,19 @@ apr_status_t md_pkey_gen_rsa(md_pkey_t **ppkey, apr_pool_t *p, int bits)
|
||||
return rv;
|
||||
}
|
||||
|
||||
apr_status_t md_pkey_gen(md_pkey_t **ppkey, apr_pool_t *p, md_pkey_spec_t *spec)
|
||||
{
|
||||
md_pkey_type_t ptype = spec? spec->type : MD_PKEY_TYPE_DEFAULT;
|
||||
switch (ptype) {
|
||||
case MD_PKEY_TYPE_DEFAULT:
|
||||
return gen_rsa(ppkey, p, MD_PKEY_RSA_BITS_DEF);
|
||||
case MD_PKEY_TYPE_RSA:
|
||||
return gen_rsa(ppkey, p, spec->params.rsa.bits);
|
||||
default:
|
||||
return APR_ENOTIMPL;
|
||||
}
|
||||
}
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
|
||||
#ifndef NID_tlsfeature
|
||||
@@ -350,7 +409,7 @@ static void RSA_get0_key(const RSA *r,
|
||||
static const char *bn64(const BIGNUM *b, apr_pool_t *p)
|
||||
{
|
||||
if (b) {
|
||||
int len = BN_num_bytes(b);
|
||||
apr_size_t len = (apr_size_t)BN_num_bytes(b);
|
||||
char *buffer = apr_pcalloc(p, len);
|
||||
if (buffer) {
|
||||
BN_bn2bin(b, (unsigned char *)buffer);
|
||||
@@ -393,7 +452,7 @@ apr_status_t md_crypt_sign64(const char **psign64, md_pkey_t *pkey, apr_pool_t *
|
||||
const char *sign64 = NULL;
|
||||
apr_status_t rv = APR_ENOMEM;
|
||||
|
||||
buffer = apr_pcalloc(p, EVP_PKEY_size(pkey->pkey));
|
||||
buffer = apr_pcalloc(p, (apr_size_t)EVP_PKEY_size(pkey->pkey));
|
||||
if (buffer) {
|
||||
ctx = EVP_MD_CTX_create();
|
||||
if (ctx) {
|
||||
@@ -728,6 +787,7 @@ apr_status_t md_cert_fload(md_cert_t **pcert, apr_pool_t *p, const char *fname)
|
||||
static apr_status_t cert_to_buffer(buffer *buffer, md_cert_t *cert, apr_pool_t *p)
|
||||
{
|
||||
BIO *bio = BIO_new(BIO_s_mem());
|
||||
int i;
|
||||
|
||||
if (!bio) {
|
||||
return APR_ENOMEM;
|
||||
@@ -740,11 +800,12 @@ static apr_status_t cert_to_buffer(buffer *buffer, md_cert_t *cert, apr_pool_t *
|
||||
return APR_EINVAL;
|
||||
}
|
||||
|
||||
buffer->len = BIO_pending(bio);
|
||||
if (buffer->len > 0) {
|
||||
buffer->data = apr_palloc(p, buffer->len+1);
|
||||
buffer->len = BIO_read(bio, buffer->data, (int)buffer->len);
|
||||
buffer->data[buffer->len] = '\0';
|
||||
i = BIO_pending(bio);
|
||||
if (i > 0) {
|
||||
buffer->data = apr_palloc(p, (apr_size_t)i + 1);
|
||||
i = BIO_read(bio, buffer->data, i);
|
||||
buffer->data[i] = '\0';
|
||||
buffer->len = (apr_size_t)i;
|
||||
}
|
||||
BIO_free(bio);
|
||||
return APR_SUCCESS;
|
||||
@@ -797,7 +858,7 @@ apr_status_t md_cert_read_http(md_cert_t **pcert, apr_pool_t *p,
|
||||
const unsigned char *bf = (const unsigned char*)der;
|
||||
X509 *x509;
|
||||
|
||||
if (NULL == (x509 = d2i_X509(NULL, &bf, der_len))) {
|
||||
if (NULL == (x509 = d2i_X509(NULL, &bf, (long)der_len))) {
|
||||
rv = APR_EINVAL;
|
||||
}
|
||||
else {
|
||||
@@ -818,19 +879,16 @@ md_cert_state_t md_cert_state_get(md_cert_t *cert)
|
||||
return MD_CERT_UNKNOWN;
|
||||
}
|
||||
|
||||
apr_status_t md_chain_fload(apr_array_header_t **pcerts, apr_pool_t *p, const char *fname)
|
||||
apr_status_t md_chain_fappend(struct apr_array_header_t *certs, apr_pool_t *p, const char *fname)
|
||||
{
|
||||
FILE *f;
|
||||
apr_status_t rv;
|
||||
apr_array_header_t *certs = NULL;
|
||||
X509 *x509;
|
||||
md_cert_t *cert;
|
||||
unsigned long err;
|
||||
|
||||
rv = md_util_fopen(&f, fname, "r");
|
||||
if (rv == APR_SUCCESS) {
|
||||
certs = apr_array_make(p, 5, sizeof(md_cert_t *));
|
||||
|
||||
ERR_clear_error();
|
||||
while (NULL != (x509 = PEM_read_X509(f, NULL, NULL, NULL))) {
|
||||
cert = make_cert(p, x509);
|
||||
@@ -862,6 +920,16 @@ apr_status_t md_chain_fload(apr_array_header_t **pcerts, apr_pool_t *p, const ch
|
||||
out:
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, p, "read chain file %s, found %d certs",
|
||||
fname, certs? certs->nelts : 0);
|
||||
return rv;
|
||||
}
|
||||
|
||||
apr_status_t md_chain_fload(apr_array_header_t **pcerts, apr_pool_t *p, const char *fname)
|
||||
{
|
||||
apr_array_header_t *certs;
|
||||
apr_status_t rv;
|
||||
|
||||
certs = apr_array_make(p, 5, sizeof(md_cert_t *));
|
||||
rv = md_chain_fappend(certs, p, fname);
|
||||
*pcerts = (APR_SUCCESS == rv)? certs : NULL;
|
||||
return rv;
|
||||
}
|
||||
@@ -1027,12 +1095,12 @@ apr_status_t md_cert_req_create(const char **pcsr_der_64, const md_t *md,
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: der length", md->name);
|
||||
rv = APR_EGENERAL; goto out;
|
||||
}
|
||||
s = csr_der = apr_pcalloc(p, csr_der_len + 1);
|
||||
s = csr_der = apr_pcalloc(p, (apr_size_t)csr_der_len + 1);
|
||||
if (i2d_X509_REQ(csr, (unsigned char**)&s) != csr_der_len) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: csr der enc", md->name);
|
||||
rv = APR_EGENERAL; goto out;
|
||||
}
|
||||
csr_der_64 = md_util_base64url_encode(csr_der, csr_der_len, p);
|
||||
csr_der_64 = md_util_base64url_encode(csr_der, (apr_size_t)csr_der_len, p);
|
||||
rv = APR_SUCCESS;
|
||||
|
||||
out:
|
||||
@@ -1104,7 +1172,7 @@ apr_status_t md_cert_self_sign(md_cert_t **pcert, const char *cn,
|
||||
rv = APR_EGENERAL; goto out;
|
||||
}
|
||||
|
||||
days = ((apr_time_sec(valid_for) + MD_SECS_PER_DAY - 1)/ MD_SECS_PER_DAY);
|
||||
days = (int)((apr_time_sec(valid_for) + MD_SECS_PER_DAY - 1)/ MD_SECS_PER_DAY);
|
||||
if (!X509_set_notBefore(x, ASN1_TIME_set(NULL, time(NULL)))) {
|
||||
rv = APR_EGENERAL; goto out;
|
||||
}
|
||||
|
@@ -41,9 +41,25 @@ apr_status_t md_crypt_sha256_digest_hex(const char **pdigesthex, apr_pool_t *p,
|
||||
|
||||
typedef struct md_pkey_t md_pkey_t;
|
||||
|
||||
typedef enum {
|
||||
MD_PKEY_TYPE_DEFAULT,
|
||||
MD_PKEY_TYPE_RSA,
|
||||
} md_pkey_type_t;
|
||||
|
||||
typedef struct md_pkey_rsa_spec_t {
|
||||
apr_uint32_t bits;
|
||||
} md_pkey_rsa_spec_t;
|
||||
|
||||
typedef struct md_pkey_spec_t {
|
||||
md_pkey_type_t type;
|
||||
union {
|
||||
md_pkey_rsa_spec_t rsa;
|
||||
} params;
|
||||
} md_pkey_spec_t;
|
||||
|
||||
apr_status_t md_crypt_init(apr_pool_t *pool);
|
||||
|
||||
apr_status_t md_pkey_gen_rsa(md_pkey_t **ppkey, apr_pool_t *p, int bits);
|
||||
apr_status_t md_pkey_gen(md_pkey_t **ppkey, apr_pool_t *p, md_pkey_spec_t *spec);
|
||||
void md_pkey_free(md_pkey_t *pkey);
|
||||
|
||||
const char *md_pkey_get_rsa_e64(md_pkey_t *pkey, apr_pool_t *p);
|
||||
@@ -62,6 +78,9 @@ apr_status_t md_crypt_sign64(const char **psign64, md_pkey_t *pkey, apr_pool_t *
|
||||
void *md_cert_get_X509(struct md_cert_t *cert);
|
||||
void *md_pkey_get_EVP_PKEY(struct md_pkey_t *pkey);
|
||||
|
||||
struct md_json_t *md_pkey_spec_to_json(const md_pkey_spec_t *spec, apr_pool_t *p);
|
||||
md_pkey_spec_t *md_pkey_spec_from_json(struct md_json_t *json, apr_pool_t *p);
|
||||
|
||||
/**************************************************************************************************/
|
||||
/* X509 certificates */
|
||||
|
||||
@@ -100,6 +119,8 @@ apr_status_t md_chain_fload(struct apr_array_header_t **pcerts,
|
||||
apr_pool_t *p, const char *fname);
|
||||
apr_status_t md_chain_fsave(struct apr_array_header_t *certs,
|
||||
apr_pool_t *p, const char *fname, apr_fileperms_t perms);
|
||||
apr_status_t md_chain_fappend(struct apr_array_header_t *certs,
|
||||
apr_pool_t *p, const char *fname);
|
||||
|
||||
apr_status_t md_cert_req_create(const char **pcsr_der_64, const struct md_t *md,
|
||||
md_pkey_t *pkey, apr_pool_t *p);
|
||||
|
@@ -99,7 +99,7 @@ static size_t resp_data_cb(void *data, size_t len, size_t nmemb, void *baton)
|
||||
if (res->req->resp_limit) {
|
||||
apr_off_t body_len = 0;
|
||||
apr_brigade_length(res->body, 0, &body_len);
|
||||
if (body_len + len > res->req->resp_limit) {
|
||||
if (body_len + (apr_off_t)len > res->req->resp_limit) {
|
||||
return 0; /* signal curl failure */
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,7 @@ static size_t header_cb(void *buffer, size_t elen, size_t nmemb, void *baton)
|
||||
md_http_response_t *res = baton;
|
||||
size_t len, clen = elen * nmemb;
|
||||
const char *name = NULL, *value = "", *b = buffer;
|
||||
int i;
|
||||
apr_size_t i;
|
||||
|
||||
len = (clen && b[clen-1] == '\n')? clen-1 : clen;
|
||||
len = (len && b[len-1] == '\r')? len-1 : len;
|
||||
@@ -183,7 +183,7 @@ static int curlify_headers(void *baton, const char *key, const char *value)
|
||||
static apr_status_t curl_perform(md_http_request_t *req)
|
||||
{
|
||||
apr_status_t rv = APR_SUCCESS;
|
||||
int curle;
|
||||
CURLcode curle;
|
||||
md_http_response_t *res;
|
||||
CURL *curl;
|
||||
struct curl_slist *req_hdrs = NULL;
|
||||
@@ -253,7 +253,8 @@ static apr_status_t curl_perform(md_http_request_t *req)
|
||||
}
|
||||
else {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, res->rv, req->pool,
|
||||
"request %ld failed(%d): %s", req->id, curle, curl_easy_strerror(curle));
|
||||
"request %ld failed(%d): %s", req->id, curle,
|
||||
curl_easy_strerror(curle));
|
||||
}
|
||||
|
||||
if (req->cb) {
|
||||
|
@@ -162,19 +162,14 @@ static apr_status_t state_init(md_reg_t *reg, apr_pool_t *p, md_t *md)
|
||||
|
||||
if (APR_SUCCESS == (rv = md_reg_creds_get(&creds, reg, MD_SG_DOMAINS, md, p))) {
|
||||
state = MD_S_INCOMPLETE;
|
||||
if (!creds->pkey) {
|
||||
if (!creds->privkey) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p,
|
||||
"md{%s}: incomplete, without private key", md->name);
|
||||
}
|
||||
else if (!creds->cert) {
|
||||
else if (!creds->pubcert) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p,
|
||||
"md{%s}: incomplete, has key but no certificate", md->name);
|
||||
}
|
||||
else if (!creds->chain) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p,
|
||||
"md{%s}: incomplete, has key and certificate, but no chain file.",
|
||||
md->name);
|
||||
}
|
||||
else {
|
||||
valid_from = md_cert_get_not_before(creds->cert);
|
||||
expires = md_cert_get_not_after(creds->cert);
|
||||
@@ -199,8 +194,8 @@ static apr_status_t state_init(md_reg_t *reg, apr_pool_t *p, md_t *md)
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < creds->chain->nelts; ++i) {
|
||||
cert = APR_ARRAY_IDX(creds->chain, i, const md_cert_t *);
|
||||
for (i = 1; i < creds->pubcert->nelts; ++i) {
|
||||
cert = APR_ARRAY_IDX(creds->pubcert, i, const md_cert_t *);
|
||||
if (!md_cert_is_valid_now(cert)) {
|
||||
state = MD_S_ERROR;
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p,
|
||||
@@ -281,17 +276,34 @@ apr_status_t md_reg_assess(md_reg_t *reg, md_t *md, int *perrored, int *prenew,
|
||||
md->state = MD_S_EXPIRED;
|
||||
renew = 1;
|
||||
}
|
||||
else if ((md->expires - now) <= md->renew_window) {
|
||||
int days = (int)(apr_time_sec(md->expires - now) / MD_SECS_PER_DAY);
|
||||
else {
|
||||
apr_interval_time_t renew_win, left, life;
|
||||
|
||||
renew_win = md->renew_window;
|
||||
if (md->renew_norm > 0
|
||||
&& md->renew_norm > renew_win
|
||||
&& md->expires > md->valid_from) {
|
||||
/* Calc renewal days as fraction of cert lifetime - if known */
|
||||
life = md->expires - md->valid_from;
|
||||
renew_win = life * md->renew_norm / renew_win;
|
||||
}
|
||||
|
||||
left = md->expires - now;
|
||||
if (left <= renew_win) {
|
||||
int days_left = (int)(apr_time_sec(left) / MD_SECS_PER_DAY);
|
||||
md_log_perror( MD_LOG_MARK, MD_LOG_DEBUG, 0, p,
|
||||
"md(%s): %d days to expiry, attempt renewal", md->name, days);
|
||||
"md(%s): %d days to expiry, attempt renewal",
|
||||
md->name, days_left);
|
||||
renew = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MD_S_INCOMPLETE:
|
||||
case MD_S_EXPIRED:
|
||||
renew = 1;
|
||||
break;
|
||||
case MD_S_MISSING:
|
||||
break;
|
||||
}
|
||||
*prenew = renew;
|
||||
*perrored = errored;
|
||||
@@ -417,16 +429,10 @@ apr_status_t md_reg_get_cred_files(md_reg_t *reg, const md_t *md, apr_pool_t *p,
|
||||
{
|
||||
apr_status_t rv;
|
||||
|
||||
rv = md_store_get_fname(pkeyfile, reg->store, MD_SG_DOMAINS, md->name, MD_FN_PKEY, p);
|
||||
if (APR_SUCCESS == rv) {
|
||||
rv = md_store_get_fname(pcertfile, reg->store, MD_SG_DOMAINS, md->name, MD_FN_CERT, p);
|
||||
}
|
||||
if (APR_SUCCESS == rv) {
|
||||
rv = md_store_get_fname(pchainfile, reg->store, MD_SG_DOMAINS, md->name, MD_FN_CHAIN, p);
|
||||
if (APR_STATUS_IS_ENOENT(rv)) {
|
||||
*pchainfile = NULL;
|
||||
rv = APR_SUCCESS;
|
||||
}
|
||||
rv = md_store_get_fname(pkeyfile, reg->store, MD_SG_DOMAINS, md->name, MD_FN_PRIVKEY, p);
|
||||
if (APR_SUCCESS == rv) {
|
||||
rv = md_store_get_fname(pcertfile, reg->store, MD_SG_DOMAINS, md->name, MD_FN_PUBCERT, p);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
@@ -513,6 +519,7 @@ static apr_status_t p_md_update(void *baton, apr_pool_t *p, apr_pool_t *ptemp, v
|
||||
}
|
||||
if (MD_UPD_RENEW_WINDOW & fields) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, ptemp, "update renew-window: %s", name);
|
||||
nmd->renew_norm = updates->renew_norm;
|
||||
nmd->renew_window = updates->renew_window;
|
||||
}
|
||||
if (MD_UPD_CA_CHALLENGES & fields) {
|
||||
@@ -544,29 +551,28 @@ static int ok_or_noent(apr_status_t rv)
|
||||
static apr_status_t creds_load(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_list ap)
|
||||
{
|
||||
md_reg_t *reg = baton;
|
||||
apr_status_t rv;
|
||||
md_cert_t *cert;
|
||||
md_pkey_t *pkey;
|
||||
apr_array_header_t *chain;
|
||||
md_pkey_t *privkey;
|
||||
apr_array_header_t *pubcert;
|
||||
md_creds_t *creds, **pcreds;
|
||||
const md_t *md;
|
||||
md_cert_state_t cert_state;
|
||||
md_store_group_t group;
|
||||
apr_status_t rv;
|
||||
|
||||
pcreds = va_arg(ap, md_creds_t **);
|
||||
group = va_arg(ap, int);
|
||||
group = (md_store_group_t)va_arg(ap, int);
|
||||
md = va_arg(ap, const md_t *);
|
||||
|
||||
if (ok_or_noent(rv = md_cert_load(reg->store, group, md->name, &cert, p))
|
||||
&& ok_or_noent(rv = md_pkey_load(reg->store, group, md->name, &pkey, p))
|
||||
&& ok_or_noent(rv = md_chain_load(reg->store, group, md->name, &chain, p))) {
|
||||
if (ok_or_noent(rv = md_pkey_load(reg->store, group, md->name, &privkey, p))
|
||||
&& ok_or_noent(rv = md_pubcert_load(reg->store, group, md->name, &pubcert, p))) {
|
||||
rv = APR_SUCCESS;
|
||||
|
||||
creds = apr_pcalloc(p, sizeof(*creds));
|
||||
creds->cert = cert;
|
||||
creds->pkey = pkey;
|
||||
creds->chain = chain;
|
||||
|
||||
creds->privkey = privkey;
|
||||
if (pubcert && pubcert->nelts > 0) {
|
||||
creds->pubcert = pubcert;
|
||||
creds->cert = APR_ARRAY_IDX(pubcert, 0, md_cert_t *);
|
||||
}
|
||||
if (creds->cert) {
|
||||
switch ((cert_state = md_cert_state_get(creds->cert))) {
|
||||
case MD_CERT_VALID:
|
||||
@@ -767,10 +773,12 @@ apr_status_t md_reg_sync(md_reg_t *reg, apr_pool_t *p, apr_pool_t *ptemp,
|
||||
smd->contacts = md->contacts;
|
||||
fields |= MD_UPD_CONTACTS;
|
||||
}
|
||||
if (MD_VAL_UPDATE(md, smd, renew_window)) {
|
||||
if (MD_VAL_UPDATE(md, smd, renew_window)
|
||||
|| MD_VAL_UPDATE(md, smd, renew_norm)) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p,
|
||||
"%s: update renew_window, old=%ld, new=%ld",
|
||||
smd->name, (long)smd->renew_window, md->renew_window);
|
||||
"%s: update renew norm=%ld, window=%ld",
|
||||
smd->name, (long)md->renew_norm, (long)md->renew_window);
|
||||
smd->renew_norm = md->renew_norm;
|
||||
smd->renew_window = md->renew_window;
|
||||
fields |= MD_UPD_RENEW_WINDOW;
|
||||
}
|
||||
@@ -880,7 +888,7 @@ apr_status_t md_reg_stage(md_reg_t *reg, const md_t *md, const char *challenge,
|
||||
return APR_SUCCESS;
|
||||
}
|
||||
|
||||
proto = apr_hash_get(reg->protos, md->ca_proto, strlen(md->ca_proto));
|
||||
proto = apr_hash_get(reg->protos, md->ca_proto, (apr_ssize_t)strlen(md->ca_proto));
|
||||
if (!proto) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, 0, p,
|
||||
"md %s has unknown CA protocol: %s", md->name, md->ca_proto);
|
||||
@@ -918,7 +926,7 @@ static apr_status_t run_load(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_l
|
||||
return APR_EINVAL;
|
||||
}
|
||||
|
||||
proto = apr_hash_get(reg->protos, md->ca_proto, strlen(md->ca_proto));
|
||||
proto = apr_hash_get(reg->protos, md->ca_proto, (apr_ssize_t)strlen(md->ca_proto));
|
||||
if (!proto) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, 0, p,
|
||||
"md %s has unknown CA protocol: %s", md->name, md->ca_proto);
|
||||
|
@@ -232,13 +232,13 @@ typedef struct {
|
||||
apr_status_t md_pkey_load(md_store_t *store, md_store_group_t group, const char *name,
|
||||
md_pkey_t **ppkey, apr_pool_t *p)
|
||||
{
|
||||
return md_store_load(store, group, name, MD_FN_PKEY, MD_SV_PKEY, (void**)ppkey, p);
|
||||
return md_store_load(store, group, name, MD_FN_PRIVKEY, MD_SV_PKEY, (void**)ppkey, p);
|
||||
}
|
||||
|
||||
apr_status_t md_pkey_save(md_store_t *store, apr_pool_t *p, md_store_group_t group, const char *name,
|
||||
struct md_pkey_t *pkey, int create)
|
||||
{
|
||||
return md_store_save(store, p, group, name, MD_FN_PKEY, MD_SV_PKEY, pkey, create);
|
||||
return md_store_save(store, p, group, name, MD_FN_PRIVKEY, MD_SV_PKEY, pkey, create);
|
||||
}
|
||||
|
||||
apr_status_t md_cert_load(md_store_t *store, md_store_group_t group, const char *name,
|
||||
@@ -267,6 +267,19 @@ apr_status_t md_chain_save(md_store_t *store, apr_pool_t *p,
|
||||
return md_store_save(store, p, group, name, MD_FN_CHAIN, MD_SV_CHAIN, chain, create);
|
||||
}
|
||||
|
||||
apr_status_t md_pubcert_load(md_store_t *store, md_store_group_t group, const char *name,
|
||||
struct apr_array_header_t **ppubcert, apr_pool_t *p)
|
||||
{
|
||||
return md_store_load(store, group, name, MD_FN_PUBCERT, MD_SV_CHAIN, (void**)ppubcert, p);
|
||||
}
|
||||
|
||||
apr_status_t md_pubcert_save(md_store_t *store, apr_pool_t *p,
|
||||
md_store_group_t group, const char *name,
|
||||
struct apr_array_header_t *pubcert, int create)
|
||||
{
|
||||
return md_store_save(store, p, group, name, MD_FN_PUBCERT, MD_SV_CHAIN, pubcert, create);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
md_store_t *store;
|
||||
md_store_group_t group;
|
||||
|
@@ -146,5 +146,11 @@ apr_status_t md_chain_load(md_store_t *store, md_store_group_t group,
|
||||
apr_status_t md_chain_save(md_store_t *store, apr_pool_t *p, md_store_group_t group,
|
||||
const char *name, struct apr_array_header_t *chain, int create);
|
||||
|
||||
apr_status_t md_pubcert_load(md_store_t *store, md_store_group_t group, const char *name,
|
||||
struct apr_array_header_t **ppubcert, apr_pool_t *p);
|
||||
apr_status_t md_pubcert_save(md_store_t *store, apr_pool_t *p,
|
||||
md_store_group_t group, const char *name,
|
||||
struct apr_array_header_t *pubcert, int create);
|
||||
|
||||
|
||||
#endif /* mod_md_md_store_h */
|
||||
|
@@ -37,7 +37,7 @@
|
||||
/**************************************************************************************************/
|
||||
/* file system based implementation of md_store_t */
|
||||
|
||||
#define MD_STORE_VERSION 1.0
|
||||
#define MD_STORE_VERSION 2
|
||||
|
||||
typedef struct {
|
||||
apr_fileperms_t dir;
|
||||
@@ -60,8 +60,6 @@ struct md_store_fs_t {
|
||||
|
||||
int port_80;
|
||||
int port_443;
|
||||
|
||||
const unsigned char *dupkey;
|
||||
};
|
||||
|
||||
#define FS_STORE(store) (md_store_fs_t*)(((char*)store)-offsetof(md_store_fs_t, s))
|
||||
@@ -98,32 +96,88 @@ static apr_status_t init_store_file(md_store_fs_t *s_fs, const char *fname,
|
||||
{
|
||||
md_json_t *json = md_json_create(p);
|
||||
const char *key64;
|
||||
unsigned char *key;
|
||||
apr_status_t rv;
|
||||
unsigned char key[FS_STORE_KLEN];
|
||||
int i;
|
||||
|
||||
md_json_sets(MOD_MD_VERSION, json, MD_KEY_VERSION, NULL);
|
||||
md_json_setn(MD_STORE_VERSION, json, MD_KEY_STORE, MD_KEY_VERSION, NULL);
|
||||
|
||||
/*if (APR_SUCCESS != (rv = md_rand_bytes(key, sizeof(key), p))) {
|
||||
s_fs->key_len = FS_STORE_KLEN;
|
||||
s_fs->key = key = apr_pcalloc(p, FS_STORE_KLEN);
|
||||
if (APR_SUCCESS != (rv = md_rand_bytes(key, s_fs->key_len, p))) {
|
||||
return rv;
|
||||
}*/
|
||||
for (i = 0; i < FS_STORE_KLEN; ++i) {
|
||||
key[i] = 'a' + (i % 26);
|
||||
}
|
||||
|
||||
s_fs->key_len = sizeof(key);
|
||||
s_fs->key = apr_pcalloc(p, sizeof(key) + 1);
|
||||
memcpy((void*)s_fs->key, key, sizeof(key));
|
||||
s_fs->dupkey = apr_pmemdup(p, key, sizeof(key));
|
||||
|
||||
key64 = md_util_base64url_encode((char *)key, sizeof(key), ptemp);
|
||||
key64 = md_util_base64url_encode((char *)key, s_fs->key_len, ptemp);
|
||||
md_json_sets(key64, json, MD_KEY_KEY, NULL);
|
||||
|
||||
rv = md_json_fcreatex(json, ptemp, MD_JSON_FMT_INDENT, fname, MD_FPROT_F_UONLY);
|
||||
memset((char*)key64, 0, strlen(key64));
|
||||
|
||||
assert(memcmp(s_fs->key, s_fs->dupkey, FS_STORE_KLEN) == 0);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static apr_status_t rename_pkey(void *baton, apr_pool_t *p, apr_pool_t *ptemp,
|
||||
const char *dir, const char *name,
|
||||
apr_filetype_e ftype)
|
||||
{
|
||||
const char *from, *to;
|
||||
apr_status_t rv = APR_SUCCESS;
|
||||
|
||||
if (APR_SUCCESS == (rv = md_util_path_merge(&from, ptemp, dir, name, NULL))
|
||||
&& APR_SUCCESS == (rv = md_util_path_merge(&to, ptemp, dir, MD_FN_PRIVKEY, NULL))) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, "renaming %s/%s to %s",
|
||||
dir, name, MD_FN_PRIVKEY);
|
||||
return apr_file_rename(from, to, ptemp);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static apr_status_t mk_pubcert(void *baton, apr_pool_t *p, apr_pool_t *ptemp,
|
||||
const char *dir, const char *name,
|
||||
apr_filetype_e ftype)
|
||||
{
|
||||
md_cert_t *cert;
|
||||
apr_array_header_t *chain, *pubcert;
|
||||
const char *fname, *fpubcert;
|
||||
apr_status_t rv = APR_SUCCESS;
|
||||
|
||||
if ( APR_SUCCESS == (rv = md_util_path_merge(&fpubcert, ptemp, dir, MD_FN_PUBCERT, NULL))
|
||||
&& APR_STATUS_IS_ENOENT((rv = md_chain_fload(&pubcert, ptemp, fpubcert)))
|
||||
&& APR_SUCCESS == (rv = md_util_path_merge(&fname, ptemp, dir, name, NULL))
|
||||
&& APR_SUCCESS == (rv = md_cert_fload(&cert, ptemp, fname))
|
||||
&& APR_SUCCESS == (rv = md_util_path_merge(&fname, ptemp, dir, MD_FN_CHAIN, NULL))) {
|
||||
|
||||
rv = md_chain_fload(&chain, ptemp, fname);
|
||||
if (APR_STATUS_IS_ENOENT(rv)) {
|
||||
chain = apr_array_make(ptemp, 1, sizeof(md_cert_t*));
|
||||
rv = APR_SUCCESS;
|
||||
}
|
||||
if (APR_SUCCESS == rv) {
|
||||
pubcert = apr_array_make(ptemp, chain->nelts + 1, sizeof(md_cert_t*));
|
||||
APR_ARRAY_PUSH(pubcert, md_cert_t *) = cert;
|
||||
apr_array_cat(pubcert, chain);
|
||||
rv = md_chain_fsave(pubcert, ptemp, fpubcert, MD_FPROT_F_UONLY);
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static apr_status_t upgrade_from_1_0(md_store_fs_t *s_fs, apr_pool_t *p, apr_pool_t *ptemp)
|
||||
{
|
||||
md_store_group_t g;
|
||||
apr_status_t rv = APR_SUCCESS;
|
||||
|
||||
/* Migrate pkey.pem -> privkey.pem */
|
||||
for (g = MD_SG_NONE; g < MD_SG_COUNT && APR_SUCCESS == rv; ++g) {
|
||||
rv = md_util_files_do(rename_pkey, s_fs, p, s_fs->base,
|
||||
md_store_group_name(g), "*", "pkey.pem", NULL);
|
||||
}
|
||||
/* Generate fullcert.pem from cert.pem and chain.pem where missing */
|
||||
rv = md_util_files_do(mk_pubcert, s_fs, p, s_fs->base,
|
||||
md_store_group_name(MD_SG_DOMAINS), "*", MD_FN_CERT, NULL);
|
||||
rv = md_util_files_do(mk_pubcert, s_fs, p, s_fs->base,
|
||||
md_store_group_name(MD_SG_ARCHIVE), "*", MD_FN_CERT, NULL);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -131,7 +185,7 @@ static apr_status_t read_store_file(md_store_fs_t *s_fs, const char *fname,
|
||||
apr_pool_t *p, apr_pool_t *ptemp)
|
||||
{
|
||||
md_json_t *json;
|
||||
const char *key64;
|
||||
const char *key64, *key;
|
||||
apr_status_t rv;
|
||||
double store_version;
|
||||
|
||||
@@ -145,9 +199,6 @@ static apr_status_t read_store_file(md_store_fs_t *s_fs, const char *fname,
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "version too new: %s", store_version);
|
||||
return APR_EINVAL;
|
||||
}
|
||||
else if (store_version > MD_STORE_VERSION) {
|
||||
/* migrate future store version changes */
|
||||
}
|
||||
|
||||
key64 = md_json_dups(p, json, MD_KEY_KEY, NULL);
|
||||
if (!key64) {
|
||||
@@ -155,12 +206,27 @@ static apr_status_t read_store_file(md_store_fs_t *s_fs, const char *fname,
|
||||
return APR_EINVAL;
|
||||
}
|
||||
|
||||
s_fs->key_len = md_util_base64url_decode((const char **)&s_fs->key, key64, p);
|
||||
if (s_fs->key_len < FS_STORE_KLEN) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "key too short: %d", s_fs->key_len);
|
||||
s_fs->key_len = md_util_base64url_decode(&key, key64, p);
|
||||
s_fs->key = (const unsigned char*)key;
|
||||
if (s_fs->key_len != FS_STORE_KLEN) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "key length unexpected: %d",
|
||||
s_fs->key_len);
|
||||
return APR_EINVAL;
|
||||
}
|
||||
s_fs->dupkey = apr_pmemdup(p, s_fs->key, FS_STORE_KLEN);
|
||||
|
||||
/* Need to migrate format? */
|
||||
if (store_version < MD_STORE_VERSION) {
|
||||
if (store_version <= 1.0) {
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, "migrating store v1.0 -> v1.1");
|
||||
rv = upgrade_from_1_0(s_fs, p, ptemp);
|
||||
}
|
||||
|
||||
if (APR_SUCCESS == rv) {
|
||||
md_json_setn(MD_STORE_VERSION, json, MD_KEY_STORE, MD_KEY_VERSION, NULL);
|
||||
rv = md_json_freplace(json, ptemp, MD_JSON_FMT_INDENT, fname, MD_FPROT_F_UONLY);
|
||||
}
|
||||
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, rv, p, "migrated store");
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
@@ -320,7 +386,6 @@ static void get_pass(const char **ppass, apr_size_t *plen,
|
||||
*plen = 0;
|
||||
}
|
||||
else {
|
||||
assert(memcmp(s_fs->key, s_fs->dupkey, FS_STORE_KLEN) == 0);
|
||||
*ppass = (const char *)s_fs->key;
|
||||
*plen = s_fs->key_len;
|
||||
}
|
||||
@@ -374,10 +439,10 @@ static apr_status_t pfs_load(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_l
|
||||
void **pvalue;
|
||||
apr_status_t rv;
|
||||
|
||||
group = va_arg(ap, int);
|
||||
group = (md_store_group_t)va_arg(ap, int);
|
||||
name = va_arg(ap, const char *);
|
||||
aspect = va_arg(ap, const char *);
|
||||
vtype = va_arg(ap, int);
|
||||
vtype = (md_store_vtype_t)va_arg(ap, int);
|
||||
pvalue= va_arg(ap, void **);
|
||||
|
||||
rv = fs_get_fname(&fpath, &s_fs->s, group, name, aspect, ptemp);
|
||||
@@ -438,8 +503,8 @@ static apr_status_t pfs_is_newer(void *baton, apr_pool_t *p, apr_pool_t *ptemp,
|
||||
int *pnewer;
|
||||
apr_status_t rv;
|
||||
|
||||
group1 = va_arg(ap, int);
|
||||
group2 = va_arg(ap, int);
|
||||
group1 = (md_store_group_t)va_arg(ap, int);
|
||||
group2 = (md_store_group_t)va_arg(ap, int);
|
||||
name = va_arg(ap, const char*);
|
||||
aspect = va_arg(ap, const char*);
|
||||
pnewer = va_arg(ap, int*);
|
||||
@@ -483,10 +548,10 @@ static apr_status_t pfs_save(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_l
|
||||
const char *pass;
|
||||
apr_size_t pass_len;
|
||||
|
||||
group = va_arg(ap, int);
|
||||
group = (md_store_group_t)va_arg(ap, int);
|
||||
name = va_arg(ap, const char*);
|
||||
aspect = va_arg(ap, const char*);
|
||||
vtype = va_arg(ap, int);
|
||||
vtype = (md_store_vtype_t)va_arg(ap, int);
|
||||
value = va_arg(ap, void *);
|
||||
create = va_arg(ap, int);
|
||||
|
||||
@@ -540,7 +605,7 @@ static apr_status_t pfs_remove(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va
|
||||
apr_finfo_t info;
|
||||
md_store_group_t group;
|
||||
|
||||
group = va_arg(ap, int);
|
||||
group = (md_store_group_t)va_arg(ap, int);
|
||||
name = va_arg(ap, const char*);
|
||||
aspect = va_arg(ap, const char *);
|
||||
force = va_arg(ap, int);
|
||||
@@ -599,7 +664,7 @@ static apr_status_t pfs_purge(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_
|
||||
md_store_group_t group;
|
||||
apr_status_t rv;
|
||||
|
||||
group = va_arg(ap, int);
|
||||
group = (md_store_group_t)va_arg(ap, int);
|
||||
name = va_arg(ap, const char*);
|
||||
|
||||
groupname = md_store_group_name(group);
|
||||
@@ -684,8 +749,8 @@ static apr_status_t pfs_move(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_l
|
||||
int archive;
|
||||
apr_status_t rv;
|
||||
|
||||
from = va_arg(ap, int);
|
||||
to = va_arg(ap, int);
|
||||
from = (md_store_group_t)va_arg(ap, int);
|
||||
to = (md_store_group_t)va_arg(ap, int);
|
||||
name = va_arg(ap, const char*);
|
||||
archive = va_arg(ap, int);
|
||||
|
||||
|
@@ -73,7 +73,7 @@ char *md_util_str_tolower(char *s)
|
||||
{
|
||||
char *orig = s;
|
||||
while (*s) {
|
||||
*s = apr_tolower(*s);
|
||||
*s = (char)apr_tolower(*s);
|
||||
++s;
|
||||
}
|
||||
return orig;
|
||||
@@ -759,26 +759,28 @@ const char *md_print_duration(apr_pool_t *p, apr_interval_time_t duration)
|
||||
|
||||
/* base64 url encoding ****************************************************************************/
|
||||
|
||||
static const int BASE64URL_UINT6[] = {
|
||||
#define N6 (unsigned int)-1
|
||||
|
||||
static const unsigned int BASE64URL_UINT6[] = {
|
||||
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 1 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, /* 2 */
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 3 */
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 4 */
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, /* 5 */
|
||||
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 6 */
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 7 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 8 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 9 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* a */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* b */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* c */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* d */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* e */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* f */
|
||||
N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* 0 */
|
||||
N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* 1 */
|
||||
N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, 62, N6, N6, /* 2 */
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, N6, N6, N6, N6, N6, N6, /* 3 */
|
||||
N6, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 4 */
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, N6, N6, N6, N6, 63, /* 5 */
|
||||
N6, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 6 */
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, N6, N6, N6, N6, N6, /* 7 */
|
||||
N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* 8 */
|
||||
N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* 9 */
|
||||
N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* a */
|
||||
N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* b */
|
||||
N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* c */
|
||||
N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* d */
|
||||
N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* e */
|
||||
N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6 /* f */
|
||||
};
|
||||
static const char BASE64URL_CHARS[] = {
|
||||
static const unsigned char BASE64URL_CHARS[] = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', /* 0 - 9 */
|
||||
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', /* 10 - 19 */
|
||||
'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', /* 20 - 29 */
|
||||
@@ -788,21 +790,23 @@ static const char BASE64URL_CHARS[] = {
|
||||
'8', '9', '-', '_', ' ', ' ', ' ', ' ', ' ', ' ', /* 60 - 69 */
|
||||
};
|
||||
|
||||
#define BASE64URL_CHAR(x) BASE64URL_CHARS[ (unsigned int)(x) & 0x3fu ]
|
||||
|
||||
apr_size_t md_util_base64url_decode(const char **decoded, const char *encoded,
|
||||
apr_pool_t *pool)
|
||||
{
|
||||
const unsigned char *e = (const unsigned char *)encoded;
|
||||
const unsigned char *p = e;
|
||||
unsigned char *d;
|
||||
int n;
|
||||
apr_size_t len, mlen, remain, i;
|
||||
unsigned int n;
|
||||
long len, mlen, remain, i;
|
||||
|
||||
while (*p && BASE64URL_UINT6[ *p ] != -1) {
|
||||
while (*p && BASE64URL_UINT6[ *p ] != N6) {
|
||||
++p;
|
||||
}
|
||||
len = p - e;
|
||||
mlen = (len/4)*4;
|
||||
*decoded = apr_pcalloc(pool, len+1);
|
||||
*decoded = apr_pcalloc(pool, (apr_size_t)len + 1);
|
||||
|
||||
i = 0;
|
||||
d = (unsigned char*)*decoded;
|
||||
@@ -811,60 +815,59 @@ apr_size_t md_util_base64url_decode(const char **decoded, const char *encoded,
|
||||
(BASE64URL_UINT6[ e[i+1] ] << 12) +
|
||||
(BASE64URL_UINT6[ e[i+2] ] << 6) +
|
||||
(BASE64URL_UINT6[ e[i+3] ]));
|
||||
*d++ = n >> 16;
|
||||
*d++ = n >> 8 & 0xffu;
|
||||
*d++ = n & 0xffu;
|
||||
*d++ = (unsigned char)(n >> 16);
|
||||
*d++ = (unsigned char)(n >> 8 & 0xffu);
|
||||
*d++ = (unsigned char)(n & 0xffu);
|
||||
}
|
||||
remain = len - mlen;
|
||||
switch (remain) {
|
||||
case 2:
|
||||
n = ((BASE64URL_UINT6[ e[mlen+0] ] << 18) +
|
||||
(BASE64URL_UINT6[ e[mlen+1] ] << 12));
|
||||
*d++ = n >> 16;
|
||||
*d++ = (unsigned char)(n >> 16);
|
||||
remain = 1;
|
||||
break;
|
||||
case 3:
|
||||
n = ((BASE64URL_UINT6[ e[mlen+0] ] << 18) +
|
||||
(BASE64URL_UINT6[ e[mlen+1] ] << 12) +
|
||||
(BASE64URL_UINT6[ e[mlen+2] ] << 6));
|
||||
*d++ = n >> 16;
|
||||
*d++ = n >> 8 & 0xffu;
|
||||
*d++ = (unsigned char)(n >> 16);
|
||||
*d++ = (unsigned char)(n >> 8 & 0xffu);
|
||||
remain = 2;
|
||||
break;
|
||||
default: /* do nothing */
|
||||
break;
|
||||
}
|
||||
return mlen/4*3 + remain;
|
||||
return (apr_size_t)(mlen/4*3 + remain);
|
||||
}
|
||||
|
||||
const char *md_util_base64url_encode(const char *data,
|
||||
apr_size_t dlen, apr_pool_t *pool)
|
||||
const char *md_util_base64url_encode(const char *data, apr_size_t dlen, apr_pool_t *pool)
|
||||
{
|
||||
long i, len = (int)dlen;
|
||||
int i, len = (int)dlen;
|
||||
apr_size_t slen = ((dlen+2)/3)*4 + 1; /* 0 terminated */
|
||||
const unsigned char *udata = (const unsigned char*)data;
|
||||
char *enc, *p = apr_pcalloc(pool, slen);
|
||||
unsigned char *enc, *p = apr_pcalloc(pool, slen);
|
||||
|
||||
enc = p;
|
||||
for (i = 0; i < len-2; i+= 3) {
|
||||
*p++ = BASE64URL_CHARS[ (udata[i] >> 2) & 0x3fu ];
|
||||
*p++ = BASE64URL_CHARS[ ((udata[i] << 4) + (udata[i+1] >> 4)) & 0x3fu ];
|
||||
*p++ = BASE64URL_CHARS[ ((udata[i+1] << 2) + (udata[i+2] >> 6)) & 0x3fu ];
|
||||
*p++ = BASE64URL_CHARS[ udata[i+2] & 0x3fu ];
|
||||
*p++ = BASE64URL_CHAR( (udata[i] >> 2) );
|
||||
*p++ = BASE64URL_CHAR( (udata[i] << 4) + (udata[i+1] >> 4) );
|
||||
*p++ = BASE64URL_CHAR( (udata[i+1] << 2) + (udata[i+2] >> 6) );
|
||||
*p++ = BASE64URL_CHAR( (udata[i+2]) );
|
||||
}
|
||||
|
||||
if (i < len) {
|
||||
*p++ = BASE64URL_CHARS[ (udata[i] >> 2) & 0x3fu ];
|
||||
*p++ = BASE64URL_CHAR( (udata[i] >> 2) );
|
||||
if (i == (len - 1)) {
|
||||
*p++ = BASE64URL_CHARS[ (udata[i] << 4) & 0x3fu ];
|
||||
*p++ = BASE64URL_CHARS[ ((unsigned int)udata[i] << 4) & 0x3fu ];
|
||||
}
|
||||
else {
|
||||
*p++ = BASE64URL_CHARS[ ((udata[i] << 4) + (udata[i+1] >> 4)) & 0x3fu ];
|
||||
*p++ = BASE64URL_CHARS[ (udata[i+1] << 2) & 0x3fu ];
|
||||
*p++ = BASE64URL_CHAR( (udata[i] << 4) + (udata[i+1] >> 4) );
|
||||
*p++ = BASE64URL_CHAR( (udata[i+1] << 2) );
|
||||
}
|
||||
}
|
||||
*p++ = '\0';
|
||||
return enc;
|
||||
return (char *)enc;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
@@ -874,12 +877,12 @@ const char *md_util_base64url_encode(const char *data,
|
||||
typedef struct {
|
||||
const char *s;
|
||||
apr_size_t slen;
|
||||
int i;
|
||||
int link_start;
|
||||
apr_size_t i;
|
||||
apr_size_t link_start;
|
||||
apr_size_t link_len;
|
||||
int pn_start;
|
||||
apr_size_t pn_start;
|
||||
apr_size_t pn_len;
|
||||
int pv_start;
|
||||
apr_size_t pv_start;
|
||||
apr_size_t pv_len;
|
||||
} link_ctx;
|
||||
|
||||
@@ -960,9 +963,9 @@ static int skip_nonws(link_ctx *ctx)
|
||||
return (ctx->i < ctx->slen);
|
||||
}
|
||||
|
||||
static int find_chr(link_ctx *ctx, char c, int *pidx)
|
||||
static unsigned int find_chr(link_ctx *ctx, char c, apr_size_t *pidx)
|
||||
{
|
||||
int j;
|
||||
apr_size_t j;
|
||||
for (j = ctx->i; j < ctx->slen; ++j) {
|
||||
if (ctx->s[j] == c) {
|
||||
*pidx = j;
|
||||
@@ -984,7 +987,7 @@ static int read_chr(link_ctx *ctx, char c)
|
||||
static int skip_qstring(link_ctx *ctx)
|
||||
{
|
||||
if (skip_ws(ctx) && read_chr(ctx, '\"')) {
|
||||
int end;
|
||||
apr_size_t end;
|
||||
if (find_chr(ctx, '\"', &end)) {
|
||||
ctx->i = end + 1;
|
||||
return 1;
|
||||
@@ -996,7 +999,7 @@ static int skip_qstring(link_ctx *ctx)
|
||||
static int skip_ptoken(link_ctx *ctx)
|
||||
{
|
||||
if (skip_ws(ctx)) {
|
||||
int i;
|
||||
apr_size_t i;
|
||||
for (i = ctx->i; i < ctx->slen && ptoken_char(ctx->s[i]); ++i) {
|
||||
/* nop */
|
||||
}
|
||||
@@ -1013,7 +1016,7 @@ static int read_link(link_ctx *ctx)
|
||||
{
|
||||
ctx->link_start = ctx->link_len = 0;
|
||||
if (skip_ws(ctx) && read_chr(ctx, '<')) {
|
||||
int end;
|
||||
apr_size_t end;
|
||||
if (find_chr(ctx, '>', &end)) {
|
||||
ctx->link_start = ctx->i;
|
||||
ctx->link_len = end - ctx->link_start;
|
||||
@@ -1027,7 +1030,7 @@ static int read_link(link_ctx *ctx)
|
||||
static int skip_pname(link_ctx *ctx)
|
||||
{
|
||||
if (skip_ws(ctx)) {
|
||||
int i;
|
||||
apr_size_t i;
|
||||
for (i = ctx->i; i < ctx->slen && attr_char(ctx->s[i]); ++i) {
|
||||
/* nop */
|
||||
}
|
||||
@@ -1068,7 +1071,7 @@ static int skip_param(link_ctx *ctx)
|
||||
|
||||
static int pv_contains(link_ctx *ctx, const char *s)
|
||||
{
|
||||
int pvstart = ctx->pv_start;
|
||||
apr_size_t pvstart = ctx->pv_start;
|
||||
apr_size_t pvlen = ctx->pv_len;
|
||||
|
||||
if (ctx->s[pvstart] == '\"' && pvlen > 1) {
|
||||
@@ -1078,7 +1081,7 @@ static int pv_contains(link_ctx *ctx, const char *s)
|
||||
if (pvlen > 0) {
|
||||
apr_size_t slen = strlen(s);
|
||||
link_ctx pvctx;
|
||||
int i;
|
||||
apr_size_t i;
|
||||
|
||||
memset(&pvctx, 0, sizeof(pvctx));
|
||||
pvctx.s = ctx->s + pvstart;
|
||||
@@ -1148,7 +1151,7 @@ static int find_url(void *baton, const char *key, const char *value)
|
||||
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.s = value;
|
||||
ctx.slen = (int)strlen(value);
|
||||
ctx.slen = strlen(value);
|
||||
|
||||
while (read_link(&ctx)) {
|
||||
while (skip_param(&ctx)) {
|
||||
|
@@ -26,7 +26,7 @@
|
||||
* @macro
|
||||
* Version number of the md module as c string
|
||||
*/
|
||||
#define MOD_MD_VERSION "0.7.0-git"
|
||||
#define MOD_MD_VERSION "0.8.1"
|
||||
|
||||
/**
|
||||
* @macro
|
||||
@@ -34,7 +34,7 @@
|
||||
* release. This is a 24 bit number with 8 bits for major number, 8 bits
|
||||
* for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
|
||||
*/
|
||||
#define MOD_MD_VERSION_NUM 0x000700
|
||||
#define MOD_MD_VERSION_NUM 0x000801
|
||||
|
||||
#define MD_EXPERIMENTAL 0
|
||||
#define MD_ACME_DEF_URL "https://acme-v01.api.letsencrypt.org/directory"
|
||||
|
@@ -78,6 +78,9 @@ static void md_merge_srv(md_t *md, md_srv_conf_t *base_sc, apr_pool_t *p)
|
||||
if (md->drive_mode == MD_DRIVE_DEFAULT) {
|
||||
md->drive_mode = md_config_geti(md->sc, MD_CONFIG_DRIVE_MODE);
|
||||
}
|
||||
if (md->renew_norm <= 0) {
|
||||
md->renew_norm = md_config_get_interval(md->sc, MD_CONFIG_RENEW_NORM);
|
||||
}
|
||||
if (md->renew_window <= 0) {
|
||||
md->renew_window = md_config_get_interval(md->sc, MD_CONFIG_RENEW_WINDOW);
|
||||
}
|
||||
@@ -87,6 +90,10 @@ static void md_merge_srv(md_t *md, md_srv_conf_t *base_sc, apr_pool_t *p)
|
||||
if (!md->ca_challenges && md->sc->ca_challenges) {
|
||||
md->ca_challenges = apr_array_copy(p, md->sc->ca_challenges);
|
||||
}
|
||||
if (!md->pkey_spec) {
|
||||
md->pkey_spec = md->sc->pkey_spec;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static apr_status_t check_coverage(md_t *md, const char *domain, server_rec *s, apr_pool_t *p)
|
||||
@@ -460,6 +467,9 @@ static apr_status_t drive_md(md_watchdog *wd, md_t *md, apr_pool_t *ptemp)
|
||||
int errored, renew;
|
||||
char ts[APR_RFC822_DATE_LEN];
|
||||
|
||||
if (md->state == MD_S_MISSING) {
|
||||
rv = APR_INCOMPLETE;
|
||||
}
|
||||
if (md->state == MD_S_COMPLETE && !md->expires) {
|
||||
/* This is our indicator that we did already renewed this managed domain
|
||||
* successfully and only wait on the next restart for it to activate */
|
||||
@@ -508,7 +518,7 @@ static apr_status_t run_watchdog(int state, void *baton, apr_pool_t *ptemp)
|
||||
md_watchdog *wd = baton;
|
||||
apr_status_t rv = APR_SUCCESS;
|
||||
md_t *md;
|
||||
apr_interval_time_t interval, now;
|
||||
apr_time_t next_run, now;
|
||||
int i;
|
||||
|
||||
switch (state) {
|
||||
@@ -519,9 +529,6 @@ static apr_status_t run_watchdog(int state, void *baton, apr_pool_t *ptemp)
|
||||
case AP_WATCHDOG_STATE_RUNNING:
|
||||
assert(wd->reg);
|
||||
|
||||
/* normally, we'd like to run at least twice a day */
|
||||
interval = apr_time_from_sec(MD_SECS_PER_DAY / 2);
|
||||
|
||||
wd->all_valid = 1;
|
||||
wd->valid_not_before = 0;
|
||||
wd->processed_count = 0;
|
||||
@@ -536,7 +543,13 @@ static apr_status_t run_watchdog(int state, void *baton, apr_pool_t *ptemp)
|
||||
for (i = 0; i < wd->mds->nelts; ++i) {
|
||||
md = APR_ARRAY_IDX(wd->mds, i, md_t *);
|
||||
|
||||
if (APR_SUCCESS != (rv = drive_md(wd, md, ptemp))) {
|
||||
rv = drive_md(wd, md, ptemp);
|
||||
|
||||
if (APR_STATUS_IS_INCOMPLETE(rv)) {
|
||||
/* configuration not complete, this MD cannot be driven further */
|
||||
wd->all_valid = 0;
|
||||
}
|
||||
else if (APR_SUCCESS != rv) {
|
||||
wd->all_valid = 0;
|
||||
++wd->error_count;
|
||||
ap_log_error( APLOG_MARK, APLOG_ERR, rv, wd->s, APLOGNO(10056)
|
||||
@@ -546,50 +559,57 @@ static apr_status_t run_watchdog(int state, void *baton, apr_pool_t *ptemp)
|
||||
|
||||
/* Determine when we want to run next */
|
||||
wd->error_runs = wd->error_count? (wd->error_runs + 1) : 0;
|
||||
|
||||
if (wd->all_valid) {
|
||||
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, wd->s, "all managed domains are valid");
|
||||
}
|
||||
else if (wd->error_count == 0) {
|
||||
ap_log_error(APLOG_MARK, APLOG_INFO, 0, wd->s, APLOGNO()
|
||||
"all managed domains driven as far as possible");
|
||||
}
|
||||
|
||||
now = apr_time_now();
|
||||
if (wd->next_valid > now && (wd->next_valid - now < interval)) {
|
||||
interval = wd->next_valid - now;
|
||||
/* normally, we'd like to run at least twice a day */
|
||||
next_run = now + apr_time_from_sec(MD_SECS_PER_DAY / 2);
|
||||
|
||||
/* Unless we know of an MD change before that */
|
||||
if (wd->next_change > 0 && wd->next_change < next_run) {
|
||||
next_run = wd->next_change;
|
||||
}
|
||||
|
||||
/* Or have to activate a new cert even before that */
|
||||
if (wd->next_valid > now && wd->next_valid < next_run) {
|
||||
next_run = wd->next_valid;
|
||||
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, wd->s,
|
||||
"Delaying activation of %d Managed Domain%s by %s",
|
||||
wd->processed_count, (wd->processed_count > 1)? "s have" : " has",
|
||||
md_print_duration(ptemp, interval));
|
||||
md_print_duration(ptemp, next_run - now));
|
||||
}
|
||||
else {
|
||||
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, wd->s,
|
||||
"all managed domains are valid");
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
/* Or encountered errors and like to retry even before that */
|
||||
if (wd->error_count > 0) {
|
||||
apr_interval_time_t delay;
|
||||
|
||||
/* back off duration, depending on the errors we encounter in a row */
|
||||
interval = apr_time_from_sec(5 << (wd->error_runs - 1));
|
||||
if (interval > apr_time_from_sec(60*60)) {
|
||||
interval = apr_time_from_sec(60*60);
|
||||
delay = apr_time_from_sec(5 << (wd->error_runs - 1));
|
||||
if (delay > apr_time_from_sec(60*60)) {
|
||||
delay = apr_time_from_sec(60*60);
|
||||
}
|
||||
if (now + delay < next_run) {
|
||||
ap_log_error(APLOG_MARK, APLOG_INFO, 0, wd->s, APLOGNO(10057)
|
||||
"encountered errors for the %d. time, next run in %d seconds",
|
||||
wd->error_runs, (int)apr_time_sec(interval));
|
||||
}
|
||||
|
||||
/* We follow the chosen min_interval for re-evaluation, unless we
|
||||
* know of a change (renewal) that happens before that. */
|
||||
if (wd->next_change) {
|
||||
apr_interval_time_t until_next = wd->next_change - apr_time_now();
|
||||
if (until_next < interval) {
|
||||
interval = until_next;
|
||||
"encountered errors for the %d. time, next try by %s",
|
||||
wd->error_runs, md_print_duration(ptemp, delay));
|
||||
next_run = now + delay;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set when we'd like to be run next time.
|
||||
* TODO: it seems that this is really only ticking down when the server
|
||||
* runs. When you wake up a hibernated machine, the watchdog will not run right away
|
||||
*/
|
||||
if (APLOGdebug(wd->s)) {
|
||||
ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, wd->s, "next run in %s",
|
||||
md_print_duration(ptemp, interval));
|
||||
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO()
|
||||
"next run in %s", md_print_duration(ptemp, next_run - now));
|
||||
}
|
||||
wd_set_interval(wd->watchdog, interval, wd, run_watchdog);
|
||||
wd_set_interval(wd->watchdog, next_run - now, wd, run_watchdog);
|
||||
break;
|
||||
|
||||
case AP_WATCHDOG_STATE_STOPPING:
|
||||
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO(10058)
|
||||
"md watchdog stopping");
|
||||
@@ -684,7 +704,7 @@ static apr_status_t start_watchdog(apr_array_header_t *names, apr_pool_t *p,
|
||||
if (!wd->mds->nelts) {
|
||||
ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10065)
|
||||
"no managed domain in state to drive, no watchdog needed, "
|
||||
"will check again on next server restart");
|
||||
"will check again on next server (graceful) restart");
|
||||
apr_pool_destroy(wd->p);
|
||||
return APR_SUCCESS;
|
||||
}
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#include <http_vhost.h>
|
||||
|
||||
#include "md.h"
|
||||
#include "md_crypt.h"
|
||||
#include "md_util.h"
|
||||
#include "mod_md_private.h"
|
||||
#include "mod_md_config.h"
|
||||
@@ -39,6 +40,7 @@
|
||||
#define MD_CMD_MEMBER "MDMember"
|
||||
#define MD_CMD_MEMBERS "MDMembers"
|
||||
#define MD_CMD_PORTMAP "MDPortMap"
|
||||
#define MD_CMD_PKEYS "MDPrivateKeys"
|
||||
#define MD_CMD_RENEWWINDOW "MDRenewWindow"
|
||||
#define MD_CMD_STOREDIR "MDStoreDir"
|
||||
|
||||
@@ -65,8 +67,9 @@ static md_srv_conf_t defconf = {
|
||||
1,
|
||||
MD_DRIVE_AUTO,
|
||||
0,
|
||||
apr_time_from_sec(14 * MD_SECS_PER_DAY),
|
||||
|
||||
NULL,
|
||||
apr_time_from_sec(90 * MD_SECS_PER_DAY), /* If the cert lifetime were 90 days, renew */
|
||||
apr_time_from_sec(30 * MD_SECS_PER_DAY), /* 30 days before. Adjust to actual lifetime */
|
||||
MD_ACME_DEF_URL,
|
||||
"ACME",
|
||||
NULL,
|
||||
@@ -109,6 +112,8 @@ static void srv_conf_props_clear(md_srv_conf_t *sc)
|
||||
sc->transitive = DEF_VAL;
|
||||
sc->drive_mode = DEF_VAL;
|
||||
sc->must_staple = DEF_VAL;
|
||||
sc->pkey_spec = NULL;
|
||||
sc->renew_norm = DEF_VAL;
|
||||
sc->renew_window = DEF_VAL;
|
||||
sc->ca_url = NULL;
|
||||
sc->ca_proto = NULL;
|
||||
@@ -121,6 +126,8 @@ static void srv_conf_props_copy(md_srv_conf_t *to, const md_srv_conf_t *from)
|
||||
to->transitive = from->transitive;
|
||||
to->drive_mode = from->drive_mode;
|
||||
to->must_staple = from->must_staple;
|
||||
to->pkey_spec = from->pkey_spec;
|
||||
to->renew_norm = from->renew_norm;
|
||||
to->renew_window = from->renew_window;
|
||||
to->ca_url = from->ca_url;
|
||||
to->ca_proto = from->ca_proto;
|
||||
@@ -133,6 +140,8 @@ static void srv_conf_props_apply(md_t *md, const md_srv_conf_t *from, apr_pool_t
|
||||
if (from->transitive != DEF_VAL) md->transitive = from->transitive;
|
||||
if (from->drive_mode != DEF_VAL) md->drive_mode = from->drive_mode;
|
||||
if (from->must_staple != DEF_VAL) md->must_staple = from->must_staple;
|
||||
if (from->pkey_spec) md->pkey_spec = from->pkey_spec;
|
||||
if (from->renew_norm != DEF_VAL) md->renew_norm = from->renew_norm;
|
||||
if (from->renew_window != DEF_VAL) md->renew_window = from->renew_window;
|
||||
|
||||
if (from->ca_url) md->ca_url = from->ca_url;
|
||||
@@ -166,6 +175,9 @@ static void *md_config_merge(apr_pool_t *pool, void *basev, void *addv)
|
||||
|
||||
nsc->transitive = (add->transitive != DEF_VAL)? add->transitive : base->transitive;
|
||||
nsc->drive_mode = (add->drive_mode != DEF_VAL)? add->drive_mode : base->drive_mode;
|
||||
nsc->must_staple = (add->must_staple != DEF_VAL)? add->must_staple : base->must_staple;
|
||||
nsc->pkey_spec = add->pkey_spec? add->pkey_spec : base->pkey_spec;
|
||||
nsc->renew_window = (add->renew_norm != DEF_VAL)? add->renew_norm : base->renew_norm;
|
||||
nsc->renew_window = (add->renew_window != DEF_VAL)? add->renew_window : base->renew_window;
|
||||
|
||||
nsc->ca_url = add->ca_url? add->ca_url : base->ca_url;
|
||||
@@ -242,7 +254,7 @@ static const char *md_config_sec_start(cmd_parms *cmd, void *mconfig, const char
|
||||
return MD_CMD_MD_SECTION "> directive missing closing '>'";
|
||||
}
|
||||
|
||||
arg = apr_pstrndup(cmd->pool, arg, endp-arg);
|
||||
arg = apr_pstrndup(cmd->pool, arg, (apr_size_t)(endp-arg));
|
||||
if (!arg || !*arg) {
|
||||
return MD_CMD_MD_SECTION " > section must specify a unique domain name";
|
||||
}
|
||||
@@ -444,23 +456,54 @@ static apr_status_t duration_parse(const char *value, apr_interval_time_t *ptime
|
||||
return rv;
|
||||
}
|
||||
|
||||
static apr_status_t percentage_parse(const char *value, int *ppercent)
|
||||
{
|
||||
char *endp;
|
||||
apr_int64_t n;
|
||||
|
||||
n = apr_strtoi64(value, &endp, 10);
|
||||
if (errno) {
|
||||
return errno;
|
||||
}
|
||||
if (*endp == '%') {
|
||||
if (n < 0 || n >= 100) {
|
||||
return APR_BADARG;
|
||||
}
|
||||
*ppercent = (int)n;
|
||||
return APR_SUCCESS;
|
||||
}
|
||||
return APR_EINVAL;
|
||||
}
|
||||
|
||||
static const char *md_config_set_renew_window(cmd_parms *cmd, void *dc, const char *value)
|
||||
{
|
||||
md_srv_conf_t *config = md_config_get(cmd->server);
|
||||
const char *err;
|
||||
apr_interval_time_t timeout;
|
||||
|
||||
/* Inspired by http_core.c */
|
||||
if (duration_parse(value, &timeout, "d") != APR_SUCCESS) {
|
||||
return "MDRenewWindow has wrong format";
|
||||
}
|
||||
int percent;
|
||||
|
||||
if (!inside_section(cmd, MD_CMD_MD_SECTION)
|
||||
&& (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Inspired by http_core.c */
|
||||
if (duration_parse(value, &timeout, "d") == APR_SUCCESS) {
|
||||
config->renew_norm = 0;
|
||||
config->renew_window = timeout;
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
switch (percentage_parse(value, &percent)) {
|
||||
case APR_SUCCESS:
|
||||
config->renew_norm = 100;
|
||||
config->renew_window = percent;
|
||||
return NULL;
|
||||
case APR_BADARG:
|
||||
return "MDRenewWindow as percent must be less than 100";
|
||||
}
|
||||
}
|
||||
return "MDRenewWindow has unrecognized format";
|
||||
}
|
||||
|
||||
static const char *md_config_set_store_dir(cmd_parms *cmd, void *arg, const char *value)
|
||||
@@ -555,6 +598,57 @@ static const char *md_config_set_cha_tyes(cmd_parms *cmd, void *dc,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *md_config_set_pkeys(cmd_parms *cmd, void *arg,
|
||||
int argc, char *const argv[])
|
||||
{
|
||||
md_srv_conf_t *config = md_config_get(cmd->server);
|
||||
const char *err, *ptype;
|
||||
apr_int64_t bits;
|
||||
|
||||
if (!inside_section(cmd, MD_CMD_MD_SECTION)
|
||||
&& (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
|
||||
return err;
|
||||
}
|
||||
if (argc <= 0) {
|
||||
return "needs to specify the private key type";
|
||||
}
|
||||
|
||||
ptype = argv[0];
|
||||
if (!apr_strnatcasecmp("Default", ptype)) {
|
||||
if (argc > 1) {
|
||||
return "type 'Default' takes no parameter";
|
||||
}
|
||||
if (!config->pkey_spec) {
|
||||
config->pkey_spec = apr_pcalloc(cmd->pool, sizeof(*config->pkey_spec));
|
||||
}
|
||||
config->pkey_spec->type = MD_PKEY_TYPE_DEFAULT;
|
||||
return NULL;
|
||||
}
|
||||
else if (!apr_strnatcasecmp("RSA", ptype)) {
|
||||
if (argc == 1) {
|
||||
bits = MD_PKEY_RSA_BITS_DEF;
|
||||
}
|
||||
else if (argc == 2) {
|
||||
bits = (int)apr_atoi64(argv[1]);
|
||||
if (bits < 2048 || bits >= INT_MAX) {
|
||||
return "must be a 2048 or higher in order to be considered safe. "
|
||||
"Too large a value will slow down everything. Larger then 4096 probably does "
|
||||
"not make sense unless quantum cryptography really changes spin.";
|
||||
}
|
||||
}
|
||||
else {
|
||||
return "key type 'RSA' has only one optinal parameter, the number of bits";
|
||||
}
|
||||
|
||||
if (!config->pkey_spec) {
|
||||
config->pkey_spec = apr_pcalloc(cmd->pool, sizeof(*config->pkey_spec));
|
||||
}
|
||||
config->pkey_spec->type = MD_PKEY_TYPE_RSA;
|
||||
config->pkey_spec->params.rsa.bits = (unsigned int)bits;
|
||||
return NULL;
|
||||
}
|
||||
return apr_pstrcat(cmd->pool, "unsupported private key type \"", ptype, "\"", NULL);
|
||||
}
|
||||
|
||||
const command_rec md_cmds[] = {
|
||||
AP_INIT_TAKE1( MD_CMD_CA, md_config_set_ca, NULL, RSRC_CONF,
|
||||
@@ -582,6 +676,8 @@ const command_rec md_cmds[] = {
|
||||
"to indicate that the server port 8000 is reachable as port 80 from the "
|
||||
"internet. Use 80:- to indicate that port 80 is not reachable from "
|
||||
"the outside."),
|
||||
AP_INIT_TAKE_ARGV( MD_CMD_PKEYS, md_config_set_pkeys, NULL, RSRC_CONF,
|
||||
"set the type and parameters for private key generation"),
|
||||
AP_INIT_TAKE1( MD_CMD_STOREDIR, md_config_set_store_dir, NULL, RSRC_CONF,
|
||||
"the directory for file system storage of managed domain data."),
|
||||
AP_INIT_TAKE1( MD_CMD_RENEWWINDOW, md_config_set_renew_window, NULL, RSRC_CONF,
|
||||
@@ -654,6 +750,8 @@ int md_config_geti(const md_srv_conf_t *sc, md_config_var_t var)
|
||||
apr_interval_time_t md_config_get_interval(const md_srv_conf_t *sc, md_config_var_t var)
|
||||
{
|
||||
switch (var) {
|
||||
case MD_CONFIG_RENEW_NORM:
|
||||
return (sc->renew_norm != DEF_VAL)? sc->renew_norm : defconf.renew_norm;
|
||||
case MD_CONFIG_RENEW_WINDOW:
|
||||
return (sc->renew_window != DEF_VAL)? sc->renew_window : defconf.renew_window;
|
||||
default:
|
||||
|
@@ -17,6 +17,7 @@
|
||||
#define mod_md_md_config_h
|
||||
|
||||
struct md_store_t;
|
||||
struct md_pkey_spec_t;
|
||||
|
||||
typedef enum {
|
||||
MD_CONFIG_CA_URL,
|
||||
@@ -26,6 +27,7 @@ typedef enum {
|
||||
MD_CONFIG_DRIVE_MODE,
|
||||
MD_CONFIG_LOCAL_80,
|
||||
MD_CONFIG_LOCAL_443,
|
||||
MD_CONFIG_RENEW_NORM,
|
||||
MD_CONFIG_RENEW_WINDOW,
|
||||
MD_CONFIG_TRANSITIVE,
|
||||
} md_config_var_t;
|
||||
@@ -51,6 +53,10 @@ typedef struct md_srv_conf_t {
|
||||
int transitive; /* != 0 iff VirtualHost names/aliases are auto-added */
|
||||
int drive_mode; /* mode of obtaining credentials */
|
||||
int must_staple; /* certificates should set the OCSP Must Staple extension */
|
||||
struct md_pkey_spec_t *pkey_spec; /* specification for generating private keys */
|
||||
apr_interval_time_t renew_norm; /* If > 0, use as normalizing value for cert lifetime
|
||||
* Example: renew_norm=90d renew_win=30d, cert lives
|
||||
* for 12 days => renewal 4 days before */
|
||||
apr_interval_time_t renew_window; /* time before expiration that starts renewal */
|
||||
|
||||
const char *ca_url; /* url of CA certificate service */
|
||||
|
@@ -34,7 +34,7 @@
|
||||
#include "md_util.h"
|
||||
#include "mod_md_os.h"
|
||||
|
||||
apr_status_t md_try_chown(const char *fname, int uid, int gid, apr_pool_t *p)
|
||||
apr_status_t md_try_chown(const char *fname, unsigned int uid, int gid, apr_pool_t *p)
|
||||
{
|
||||
#if AP_NEED_SET_MUTEX_PERMS
|
||||
if (-1 == chown(fname, (uid_t)uid, (gid_t)gid)) {
|
||||
|
@@ -20,7 +20,7 @@
|
||||
* Try chown'ing the file/directory. Give id -1 to not change uid/gid.
|
||||
* Will return APR_ENOTIMPL on platforms not supporting this operation.
|
||||
*/
|
||||
apr_status_t md_try_chown(const char *fname, int uid, int gid, apr_pool_t *p);
|
||||
apr_status_t md_try_chown(const char *fname, unsigned int uid, int gid, apr_pool_t *p);
|
||||
|
||||
/**
|
||||
* Make a file or directory read/write(/searchable) by httpd workers.
|
||||
|
@@ -1723,8 +1723,9 @@ static apr_status_t ssl_init_server_ctx(server_rec *s,
|
||||
else if (APR_STATUS_IS_EAGAIN(rv)) {
|
||||
/* Managed Domain not ready yet. This is not a reason to fail the config */
|
||||
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10085)
|
||||
"Init: (%s) disabling this host for now as certificate/key data "
|
||||
"for the Managed Domain is incomplete.", ssl_util_vhostid(p, s));
|
||||
"Init: %s will respond with '503 Service Unavailable' for now. This "
|
||||
"host is part of a Managed Domain, but no SSL certificate is "
|
||||
"available (yet).", ssl_util_vhostid(p, s));
|
||||
pks->service_unavailable = 1;
|
||||
return APR_SUCCESS;
|
||||
}
|
||||
|
Reference in New Issue
Block a user