1
0
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:
Stefan Eissing
2017-09-01 12:11:38 +00:00
parent a3067173c9
commit c9f60810cf
27 changed files with 749 additions and 298 deletions

24
CHANGES
View File

@@ -1,7 +1,29 @@
-*- coding: utf-8 -*- -*- coding: utf-8 -*-
Changes with Apache 2.5.0 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 - 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 MDCertificateAuthority https://acme-staging.api.letsencrypt.org/directory
- When existing, complete certificates are renewed, the activation of the new ones is - When existing, complete certificates are renewed, the activation of the new ones is

View File

@@ -341,6 +341,40 @@ MDPortMap 80:- 443:5002
</usage> </usage>
</directivesynopsis> </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> <directivesynopsis>
<name>MDRenewWindow</name> <name>MDRenewWindow</name>
<description></description> <description></description>

View File

@@ -25,8 +25,10 @@ struct md_cert_t;
struct md_pkey_t; struct md_pkey_t;
struct md_store_t; struct md_store_t;
struct md_srv_conf_t; struct md_srv_conf_t;
struct md_pkey_spec_t;
#define MD_TLSSNI01_DNS_SUFFIX ".acme.invalid" #define MD_TLSSNI01_DNS_SUFFIX ".acme.invalid"
#define MD_PKEY_RSA_BITS_DEF 2048U
typedef enum { typedef enum {
MD_S_UNKNOWN, /* MD has not been analysed yet */ 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_COMPLETE, /* MD has all necessary information, can go live */
MD_S_EXPIRED, /* MD is complete, but credentials have expired */ 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_ERROR, /* MD data is flawed, unable to be processed as is */
MD_S_MISSING, /* MD is missing config information, cannot proceed */
} md_state_t; } md_state_t;
typedef enum { typedef enum {
@@ -70,7 +73,9 @@ struct md_t {
int transitive; /* != 0 iff VirtualHost names/aliases are auto-added */ int transitive; /* != 0 iff VirtualHost names/aliases are auto-added */
int drive_mode; /* mode of obtaining credentials */ 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 */ 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 */ apr_interval_time_t renew_window;/* time before expiration that starts renewal */
const char *ca_url; /* url of CA certificate service */ const char *ca_url; /* url of CA certificate service */
@@ -91,6 +96,7 @@ struct md_t {
#define MD_KEY_ACCOUNT "account" #define MD_KEY_ACCOUNT "account"
#define MD_KEY_AGREEMENT "agreement" #define MD_KEY_AGREEMENT "agreement"
#define MD_KEY_BITS "bits"
#define MD_KEY_CA "ca" #define MD_KEY_CA "ca"
#define MD_KEY_CA_URL "ca-url" #define MD_KEY_CA_URL "ca-url"
#define MD_KEY_CERT "cert" #define MD_KEY_CERT "cert"
@@ -112,8 +118,10 @@ struct md_t {
#define MD_KEY_KEYAUTHZ "keyAuthorization" #define MD_KEY_KEYAUTHZ "keyAuthorization"
#define MD_KEY_LOCATION "location" #define MD_KEY_LOCATION "location"
#define MD_KEY_NAME "name" #define MD_KEY_NAME "name"
#define MD_KEY_PKEY "privkey"
#define MD_KEY_PROTO "proto" #define MD_KEY_PROTO "proto"
#define MD_KEY_REGISTRATION "registration" #define MD_KEY_REGISTRATION "registration"
#define MD_KEY_RENEW_NORM "renew-norm"
#define MD_KEY_RENEW_WINDOW "renew-window" #define MD_KEY_RENEW_WINDOW "renew-window"
#define MD_KEY_RESOURCE "resource" #define MD_KEY_RESOURCE "resource"
#define MD_KEY_STATE "state" #define MD_KEY_STATE "state"
@@ -129,7 +137,8 @@ struct md_t {
#define MD_KEY_VERSION "version" #define MD_KEY_VERSION "version"
#define MD_FN_MD "md.json" #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_CERT "cert.pem"
#define MD_FN_CHAIN "chain.pem" #define MD_FN_CHAIN "chain.pem"
#define MD_FN_HTTPD_JSON "httpd.json" #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; typedef struct md_creds_t md_creds_t;
struct 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_cert_t *cert;
struct md_pkey_t *pkey;
struct apr_array_header_t *chain; /* list of md_cert* */
int expired; int expired;
}; };

View File

@@ -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 = apr_pcalloc(p, sizeof(*acme));
acme->url = url; acme->url = url;
acme->p = p; 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); base_product, MOD_MD_VERSION);
acme->pkey_bits = 4096;
acme->max_retries = 3; acme->max_retries = 3;
if (APR_SUCCESS != (rv = apr_uri_parse(p, url, &uri_parsed))) { if (APR_SUCCESS != (rv = apr_uri_parse(p, url, &uri_parsed))) {
@@ -260,9 +259,15 @@ static apr_status_t inspect_problem(md_acme_req_t *req, const md_http_response_t
ptype = md_json_gets(problem, "type", NULL); ptype = md_json_gets(problem, "type", NULL);
pdetail = md_json_gets(problem, "detail", NULL); pdetail = md_json_gets(problem, "detail", NULL);
req->rv = problem_status_get(ptype); req->rv = problem_status_get(ptype);
md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, req->rv, req->p, if (APR_STATUS_IS_EAGAIN(req->rv)) {
"acme problem %s: %s", ptype, pdetail); 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; return req->rv;
} }
} }

View File

@@ -62,7 +62,6 @@ struct md_acme_t {
const char *nonce; const char *nonce;
int max_retries; 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, * 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 * the server is notified of this. If the server requires a ToS that the account
* thinks it has already given, it is resend. * 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. * Get the ToS agreement for current account.

View File

@@ -327,6 +327,7 @@ static apr_status_t acct_register(md_acme_t *acme, apr_pool_t *p,
apr_status_t rv; apr_status_t rv;
md_pkey_t *pkey; md_pkey_t *pkey;
const char *err = NULL, *uri; const char *err = NULL, *uri;
md_pkey_spec_t spec;
int i; int i;
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, "create new account"); 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))) { && APR_SUCCESS == (rv = acct_make(&acme->acct, p, acme->url, NULL, contacts))) {
acct_ctx_t ctx; 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))); || (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; apr_status_t rv = APR_SUCCESS;
/* Check if (correct) Terms-of-Service for account were accepted */ /* Check if (correct) Terms-of-Service for account were accepted */
*prequired = NULL;
if (agreement_required(acme->acct)) { if (agreement_required(acme->acct)) {
const char *tos = acme->acct->tos_required; const char *tos = acme->acct->tos_required;
if (!tos) { 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); rv = md_acme_agree(acme, p, tos);
} }
else { else {
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, acme->p, *prequired = apr_pstrdup(p, tos);
"need to accept terms-of-service <%s> for account %s", rv = APR_INCOMPLETE;
tos, acme->acct->id);
rv = APR_EACCES;
} }
} }
return rv; return rv;

View File

@@ -41,4 +41,8 @@ struct md_acme_acct_t {
#define MD_FN_ACCOUNT "account.json" #define MD_FN_ACCOUNT "account.json"
#define MD_FN_ACCT_KEY "account.pem" #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 */ #endif /* md_acme_acct_h */

View File

@@ -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) { for (i = 0; i < set->authzs->nelts; ++i) {
authz = APR_ARRAY_IDX(set->authzs, i, md_acme_authz_t *); authz = APR_ARRAY_IDX(set->authzs, i, md_acme_authz_t *);
if (!apr_strnatcasecmp(domain, authz->domain)) { if (!apr_strnatcasecmp(domain, authz->domain)) {
int n = i +1; int n = i + 1;
if (n < set->authzs->nelts) { if (n < set->authzs->nelts) {
void **elems = (void **)set->authzs->elts; 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; --set->authzs->nelts;
return APR_SUCCESS; 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, 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; const char *data;
apr_status_t rv; 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, 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_cert_t *cha_cert;
md_pkey_t *cha_key; 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)) if ((APR_SUCCESS == rv && !md_cert_covers_domain(cha_cert, cha_dns))
|| APR_STATUS_IS_ENOENT(rv)) { || 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", md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: create tls-sni-01 challgenge key",
authz->domain); authz->domain);
goto out; goto out;
@@ -405,7 +407,8 @@ out:
} }
typedef apr_status_t cha_starter(md_acme_authz_cha_t *cha, md_acme_authz_t *authz, 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 { typedef struct {
const char *name; 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_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; apr_status_t rv;
int i; 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) { for (i = 0; i < CHA_TYPES_LEN; ++i) {
if (!apr_strnatcasecmp(CHA_TYPES[i].name, fctx.accepted->type)) { 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->domain = md_json_dups(p, json, MD_KEY_DOMAIN, NULL);
authz->location = md_json_dups(p, json, MD_KEY_LOCATION, NULL); authz->location = md_json_dups(p, json, MD_KEY_LOCATION, NULL);
authz->dir = md_json_dups(p, json, MD_KEY_DIR, 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 authz;
} }
return NULL; 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; const char *md_name;
int create; int create;
group = va_arg(ap, int); group = (md_store_group_t)va_arg(ap, int);
md_name = va_arg(ap, const char *); md_name = va_arg(ap, const char *);
set = va_arg(ap, md_acme_authz_set_t *); set = va_arg(ap, md_acme_authz_set_t *);
create = va_arg(ap, int); 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; const char *md_name;
int i; int i;
group = va_arg(ap, int); group = (md_store_group_t)va_arg(ap, int);
md_name = va_arg(ap, const char *); md_name = va_arg(ap, const char *);
if (APR_SUCCESS == md_acme_authz_set_load(store, group, md_name, &authz_set, p)) { if (APR_SUCCESS == md_acme_authz_set_load(store, group, md_name, &authz_set, p)) {

View File

@@ -21,6 +21,7 @@ struct md_acme_t;
struct md_acme_acct_t; struct md_acme_acct_t;
struct md_json_t; struct md_json_t;
struct md_store_t; struct md_store_t;
struct md_pkey_spec_t;
typedef struct md_acme_challenge_t md_acme_challenge_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); 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, apr_status_t md_acme_authz_respond(md_acme_authz_t *authz, struct md_acme_t *acme,
struct md_store_t *store, struct md_store_t *store, apr_array_header_t *challenges,
apr_array_header_t *challenges, apr_pool_t *p); 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, 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); struct md_store_t *store, apr_pool_t *p);

View File

@@ -42,9 +42,11 @@ typedef struct {
const char *phase; const char *phase;
int complete; int complete;
md_pkey_t *pkey; md_pkey_t *privkey; /* the new private key */
md_cert_t *cert; apr_array_header_t *pubcert; /* the new certificate + chain certs */
apr_array_header_t *chain;
md_cert_t *cert; /* the new certificate */
apr_array_header_t *chain; /* the chain certificates */
md_acme_t *acme; md_acme_t *acme;
md_t *md; md_t *md;
@@ -122,8 +124,8 @@ static apr_status_t ad_set_acct(md_proto_driver_t *d)
goto out; goto out;
} }
if (APR_SUCCESS == (rv = md_acme_create_acct(ad->acme, d->p, if (APR_SUCCESS == (rv = md_acme_create_acct(ad->acme, d->p, md->contacts,
md->contacts, md->ca_agreement)) md->ca_agreement))
&& APR_SUCCESS == (rv = md_acme_acct_save_staged(ad->acme, d->store, md, d->p))) { && APR_SUCCESS == (rv = md_acme_acct_save_staged(ad->acme, d->store, md, d->p))) {
md->ca_account = MD_ACME_ACCT_STAGED; md->ca_account = MD_ACME_ACCT_STAGED;
update = 1; update = 1;
@@ -267,11 +269,13 @@ static apr_status_t ad_start_challenges(md_proto_driver_t *d)
switch (authz->state) { switch (authz->state) {
case MD_ACME_AUTHZ_S_VALID: case MD_ACME_AUTHZ_S_VALID:
break; 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; changed = 1;
break; break;
default: default:
rv = APR_EINVAL; rv = APR_EINVAL;
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, d->p, 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) static apr_status_t ad_setup_certificate(md_proto_driver_t *d)
{ {
md_acme_driver_t *ad = d->baton; md_acme_driver_t *ad = d->baton;
md_pkey_t *pkey; md_pkey_t *privkey;
apr_status_t rv; 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_STATUS_IS_ENOENT(rv)) {
if (APR_SUCCESS == (rv = md_pkey_gen_rsa(&pkey, d->p, ad->acme->pkey_bits))) { 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, pkey, 1); 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) { if (APR_SUCCESS == rv) {
ad->phase = "setup csr"; 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); 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; 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) { if (ad->md) {
/* staging in progress. look for new ACME account information collected there */ /* 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); 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 */ /* 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 */ /* 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); md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p, "%s: all data staged", d->md->name);
renew = 0; 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 * requests for new authorizations are denied. ToS may change during the
* lifetime of an account */ * lifetime of an account */
if (APR_SUCCESS == rv) { if (APR_SUCCESS == rv) {
const char *required;
ad->phase = "check agreement"; ad->phase = "check agreement";
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p, md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p,
"%s: check Terms-of-Service agreement", d->md->name); "%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 /* 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) { if (APR_SUCCESS == rv && !ad->chain) {
ad->phase = "install chain"; ad->phase = "install chain";
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p, 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); 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) { if (APR_SUCCESS == rv && ad->cert) {
apr_time_t now = apr_time_now(); apr_time_t now = apr_time_now();
apr_interval_time_t max_delay, delay_activation; 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) const char *name, apr_pool_t *p)
{ {
apr_status_t rv; apr_status_t rv;
md_pkey_t *pkey, *acct_key; md_pkey_t *privkey, *acct_key;
md_t *md; md_t *md;
md_cert_t *cert; apr_array_header_t *pubcert;
apr_array_header_t *chain;
struct md_acme_acct_t *acct; struct md_acme_acct_t *acct;
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, "%s: preload start", name); 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); md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "%s: loading md json", name);
return rv; return rv;
} }
if (APR_SUCCESS != (rv = md_cert_load(store, MD_SG_STAGING, name, &cert, 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 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))) {
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "%s: loading staging private key", name); md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "%s: loading staging private key", name);
return rv; 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 */ /* See if staging holds a new or modified account data */
rv = md_acme_acct_load(&acct, &acct_key, store, MD_SG_STAGING, name, p); 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); md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: saving md json", name);
return rv; return rv;
} }
if (APR_SUCCESS != (rv = md_cert_save(store, p, load_group, name, cert, 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 certificate", name);
return rv;
}
if (APR_SUCCESS != (rv = md_chain_save(store, p, load_group, name, chain, 1))) {
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: saving cert chain", name); md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: saving cert chain", name);
return rv; return rv;
} }
if (APR_SUCCESS != (rv = md_pkey_save(store, p, load_group, name, pkey, 1))) { 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 domain private key", name); md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: saving private key", name);
return rv; return rv;
} }

View File

@@ -103,7 +103,7 @@ static apr_status_t cmd_reg_list(md_cmd_ctx *ctx, const md_cmd_t *cmd)
else { else {
md_log_perror(MD_LOG_MARK, MD_LOG_TRACE4, 0, ctx->p, "list do"); 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); 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) { for (i = 0; i < mdlist->nelts; ++i) {
md = APR_ARRAY_IDX(mdlist, i, const md_t*); md = APR_ARRAY_IDX(mdlist, i, const md_t*);
@@ -274,7 +274,7 @@ static apr_status_t assess_and_drive(md_cmd_ctx *ctx, md_t *md)
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, rv, ctx->p, "%s: %s", md->name, msg); md_log_perror(MD_LOG_MARK, MD_LOG_INFO, rv, ctx->p, "%s: %s", md->name, msg);
if (APR_SUCCESS == (rv = md_reg_stage(ctx->reg, md, challenge, reset, NULL, ctx->p))) { if (APR_SUCCESS == (rv = md_reg_stage(ctx->reg, md, challenge, reset, NULL, ctx->p))) {
md_log_perror(MD_LOG_MARK, MD_LOG_INFO, rv, ctx->p, "%s: loading", md->name); md_log_perror(MD_LOG_MARK, MD_LOG_INFO, rv, ctx->p, "%s: loading", md->name);
rv = md_reg_load(ctx->reg, md->name, ctx->p); rv = md_reg_load(ctx->reg, md->name, ctx->p);
@@ -317,7 +317,7 @@ static apr_status_t cmd_reg_drive(md_cmd_ctx *ctx, const md_cmd_t *cmd)
} }
else { else {
md_reg_do(list_add_md, mdlist, ctx->reg, ctx->p); 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; rv = APR_SUCCESS;

View File

@@ -24,6 +24,7 @@
#include "md_json.h" #include "md_json.h"
#include "md.h" #include "md.h"
#include "md_crypt.h"
#include "md_log.h" #include "md_log.h"
#include "md_store.h" #include "md_store.h"
#include "md_util.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->name = apr_pstrdup(p, src->name);
md->drive_mode = src->drive_mode; md->drive_mode = src->drive_mode;
md->domains = md_array_str_compact(p, src->domains, 0); 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->renew_window = src->renew_window;
md->contacts = md_array_str_clone(p, src->contacts); md->contacts = md_array_str_clone(p, src->contacts);
if (src->ca_url) md->ca_url = apr_pstrdup(p, src->ca_url); 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_proto = add->ca_proto? add->ca_proto : base->ca_proto;
n->ca_agreement = add->ca_agreement? add->ca_agreement : base->ca_agreement; 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->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->pkey_spec = add->pkey_spec? add->pkey_spec : base->pkey_spec;
n->transitive = (add->transitive < 0)? add->transitive : base->transitive; 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) { if (add->ca_challenges) {
n->ca_challenges = apr_array_copy(p, 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; return n;
} }
/**************************************************************************************************/ /**************************************************************************************************/
/* format conversion */ /* format conversion */
@@ -284,6 +288,9 @@ md_json_t *md_to_json(const md_t *md, apr_pool_t *p)
if (md->cert_url) { if (md->cert_url) {
md_json_sets(md->cert_url, json, MD_KEY_CERT, MD_KEY_URL, NULL); 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->state, json, MD_KEY_STATE, NULL);
md_json_setl(md->drive_mode, json, MD_KEY_DRIVE_MODE, NULL); md_json_setl(md->drive_mode, json, MD_KEY_DRIVE_MODE, NULL);
if (md->expires > 0) { 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); apr_rfc822_date(ts, md->valid_from);
md_json_sets(ts, json, MD_KEY_CERT, MD_KEY_VALID_FROM, NULL); 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) { if (md->ca_challenges && md->ca_challenges->nelts > 0) {
apr_array_header_t *na; apr_array_header_t *na;
na = md_array_str_compact(p, md->ca_challenges, 0); 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_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->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->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->drive_mode = (int)md_json_getl(json, MD_KEY_DRIVE_MODE, NULL);
md->domains = md_array_str_compact(p, md->domains, 0); md->domains = md_array_str_compact(p, md->domains, 0);
md->transitive = (int)md_json_getl(json, MD_KEY_TRANSITIVE, NULL); 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) { if (s && *s) {
md->valid_from = apr_date_parse_rfc(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)); 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)) { if (md_json_has_key(json, MD_KEY_CA, MD_KEY_CHALLENGES, NULL)) {
md->ca_challenges = apr_array_make(p, 5, sizeof(const char*)); md->ca_challenges = apr_array_make(p, 5, sizeof(const char*));

View File

@@ -31,6 +31,7 @@
#include "md.h" #include "md.h"
#include "md_crypt.h" #include "md_crypt.h"
#include "md_json.h"
#include "md_log.h" #include "md_log.h"
#include "md_http.h" #include "md_http.h"
#include "md_util.h" #include "md_util.h"
@@ -83,7 +84,7 @@ static void seed_RAND(int pid)
{ {
unsigned char stackdata[256]; unsigned char stackdata[256];
/* stolen from mod_ssl/ssl_engine_rand.c */ /* stolen from mod_ssl/ssl_engine_rand.c */
apr_size_t n, l; int n;
struct { struct {
time_t t; time_t t;
pid_t pid; pid_t pid;
@@ -99,8 +100,7 @@ static void seed_RAND(int pid)
*/ */
my_seed.pid = pid; my_seed.pid = pid;
l = sizeof(my_seed); RAND_seed((unsigned char *)&my_seed, sizeof(my_seed));
RAND_seed((unsigned char *)&my_seed, l);
/* /*
* seed in some current state of the run-time stack (128 bytes) * 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) { if (ctx->pass_len < size) {
size = (int)ctx->pass_len; size = (int)ctx->pass_len;
} }
memcpy(buf, ctx->pass_phrase, size); memcpy(buf, ctx->pass_phrase, (size_t)size);
} }
return ctx->pass_len; return ctx->pass_len;
} }
@@ -181,6 +181,50 @@ static int pem_passwd(char *buf, int size, int rwflag, void *baton)
/**************************************************************************************************/ /**************************************************************************************************/
/* private keys */ /* 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) static md_pkey_t *make_pkey(apr_pool_t *p)
{ {
md_pkey_t *pkey = apr_pcalloc(p, sizeof(*pkey)); 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); apr_pool_cleanup_register(p, pkey, pkey_cleanup, apr_pool_cleanup_null);
} }
else { else {
long err = ERR_get_error(); unsigned long err = ERR_get_error();
rv = APR_EINVAL; rv = APR_EINVAL;
md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, p, md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, rv, p,
"error loading pkey %s: %s (pass phrase was %snull)", fname, "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; void *cb_baton = NULL;
passwd_ctx ctx; passwd_ctx ctx;
unsigned long err; unsigned long err;
int i;
if (!bio) { if (!bio) {
return APR_ENOMEM; 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; return APR_EINVAL;
} }
buffer->len = BIO_pending(bio); i = BIO_pending(bio);
if (buffer->len > 0) { if (i > 0) {
buffer->data = apr_palloc(p, buffer->len+1); buffer->data = apr_palloc(p, (apr_size_t)i + 1);
buffer->len = BIO_read(bio, buffer->data, (int)buffer->len); i = BIO_read(bio, buffer->data, i);
buffer->data[buffer->len] = '\0'; buffer->data[i] = '\0';
buffer->len = (apr_size_t)i;
} }
BIO_free(bio); BIO_free(bio);
return APR_SUCCESS; return APR_SUCCESS;
@@ -303,7 +349,7 @@ apr_status_t md_pkey_fsave(md_pkey_t *pkey, apr_pool_t *p,
return rv; 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; EVP_PKEY_CTX *ctx = NULL;
apr_status_t rv; 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); ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
if (ctx if (ctx
&& EVP_PKEY_keygen_init(ctx) >= 0 && 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) { && EVP_PKEY_keygen(ctx, &(*ppkey)->pkey) >= 0) {
rv = APR_SUCCESS; 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; 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 #if OPENSSL_VERSION_NUMBER < 0x10100000L
#ifndef NID_tlsfeature #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) static const char *bn64(const BIGNUM *b, apr_pool_t *p)
{ {
if (b) { 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); char *buffer = apr_pcalloc(p, len);
if (buffer) { if (buffer) {
BN_bn2bin(b, (unsigned char *)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; const char *sign64 = NULL;
apr_status_t rv = APR_ENOMEM; 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) { if (buffer) {
ctx = EVP_MD_CTX_create(); ctx = EVP_MD_CTX_create();
if (ctx) { 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) static apr_status_t cert_to_buffer(buffer *buffer, md_cert_t *cert, apr_pool_t *p)
{ {
BIO *bio = BIO_new(BIO_s_mem()); BIO *bio = BIO_new(BIO_s_mem());
int i;
if (!bio) { if (!bio) {
return APR_ENOMEM; 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; return APR_EINVAL;
} }
buffer->len = BIO_pending(bio); i = BIO_pending(bio);
if (buffer->len > 0) { if (i > 0) {
buffer->data = apr_palloc(p, buffer->len+1); buffer->data = apr_palloc(p, (apr_size_t)i + 1);
buffer->len = BIO_read(bio, buffer->data, (int)buffer->len); i = BIO_read(bio, buffer->data, i);
buffer->data[buffer->len] = '\0'; buffer->data[i] = '\0';
buffer->len = (apr_size_t)i;
} }
BIO_free(bio); BIO_free(bio);
return APR_SUCCESS; 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; const unsigned char *bf = (const unsigned char*)der;
X509 *x509; X509 *x509;
if (NULL == (x509 = d2i_X509(NULL, &bf, der_len))) { if (NULL == (x509 = d2i_X509(NULL, &bf, (long)der_len))) {
rv = APR_EINVAL; rv = APR_EINVAL;
} }
else { else {
@@ -818,19 +879,16 @@ md_cert_state_t md_cert_state_get(md_cert_t *cert)
return MD_CERT_UNKNOWN; 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; FILE *f;
apr_status_t rv; apr_status_t rv;
apr_array_header_t *certs = NULL;
X509 *x509; X509 *x509;
md_cert_t *cert; md_cert_t *cert;
unsigned long err; unsigned long err;
rv = md_util_fopen(&f, fname, "r"); rv = md_util_fopen(&f, fname, "r");
if (rv == APR_SUCCESS) { if (rv == APR_SUCCESS) {
certs = apr_array_make(p, 5, sizeof(md_cert_t *));
ERR_clear_error(); ERR_clear_error();
while (NULL != (x509 = PEM_read_X509(f, NULL, NULL, NULL))) { while (NULL != (x509 = PEM_read_X509(f, NULL, NULL, NULL))) {
cert = make_cert(p, x509); 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: out:
md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, p, "read chain file %s, found %d certs", md_log_perror(MD_LOG_MARK, MD_LOG_TRACE3, rv, p, "read chain file %s, found %d certs",
fname, certs? certs->nelts : 0); 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; *pcerts = (APR_SUCCESS == rv)? certs : NULL;
return rv; 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); md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: der length", md->name);
rv = APR_EGENERAL; goto out; 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) { 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); md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: csr der enc", md->name);
rv = APR_EGENERAL; goto out; 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; rv = APR_SUCCESS;
out: out:
@@ -1104,7 +1172,7 @@ apr_status_t md_cert_self_sign(md_cert_t **pcert, const char *cn,
rv = APR_EGENERAL; goto out; 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)))) { if (!X509_set_notBefore(x, ASN1_TIME_set(NULL, time(NULL)))) {
rv = APR_EGENERAL; goto out; rv = APR_EGENERAL; goto out;
} }

View File

@@ -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 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_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); void md_pkey_free(md_pkey_t *pkey);
const char *md_pkey_get_rsa_e64(md_pkey_t *pkey, apr_pool_t *p); 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_cert_get_X509(struct md_cert_t *cert);
void *md_pkey_get_EVP_PKEY(struct md_pkey_t *pkey); 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 */ /* 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_pool_t *p, const char *fname);
apr_status_t md_chain_fsave(struct apr_array_header_t *certs, apr_status_t md_chain_fsave(struct apr_array_header_t *certs,
apr_pool_t *p, const char *fname, apr_fileperms_t perms); 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, 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); md_pkey_t *pkey, apr_pool_t *p);

View File

@@ -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) { if (res->req->resp_limit) {
apr_off_t body_len = 0; apr_off_t body_len = 0;
apr_brigade_length(res->body, 0, &body_len); 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 */ 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; md_http_response_t *res = baton;
size_t len, clen = elen * nmemb; size_t len, clen = elen * nmemb;
const char *name = NULL, *value = "", *b = buffer; const char *name = NULL, *value = "", *b = buffer;
int i; apr_size_t i;
len = (clen && b[clen-1] == '\n')? clen-1 : clen; len = (clen && b[clen-1] == '\n')? clen-1 : clen;
len = (len && b[len-1] == '\r')? len-1 : len; 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) static apr_status_t curl_perform(md_http_request_t *req)
{ {
apr_status_t rv = APR_SUCCESS; apr_status_t rv = APR_SUCCESS;
int curle; CURLcode curle;
md_http_response_t *res; md_http_response_t *res;
CURL *curl; CURL *curl;
struct curl_slist *req_hdrs = NULL; struct curl_slist *req_hdrs = NULL;
@@ -253,7 +253,8 @@ static apr_status_t curl_perform(md_http_request_t *req)
} }
else { else {
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, res->rv, req->pool, 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) { if (req->cb) {

View File

@@ -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))) { if (APR_SUCCESS == (rv = md_reg_creds_get(&creds, reg, MD_SG_DOMAINS, md, p))) {
state = MD_S_INCOMPLETE; state = MD_S_INCOMPLETE;
if (!creds->pkey) { if (!creds->privkey) {
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p,
"md{%s}: incomplete, without private key", md->name); "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_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p,
"md{%s}: incomplete, has key but no certificate", md->name); "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 { else {
valid_from = md_cert_get_not_before(creds->cert); valid_from = md_cert_get_not_before(creds->cert);
expires = md_cert_get_not_after(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; goto out;
} }
for (i = 0; i < creds->chain->nelts; ++i) { for (i = 1; i < creds->pubcert->nelts; ++i) {
cert = APR_ARRAY_IDX(creds->chain, i, const md_cert_t *); cert = APR_ARRAY_IDX(creds->pubcert, i, const md_cert_t *);
if (!md_cert_is_valid_now(cert)) { if (!md_cert_is_valid_now(cert)) {
state = MD_S_ERROR; state = MD_S_ERROR;
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, 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; md->state = MD_S_EXPIRED;
renew = 1; renew = 1;
} }
else if ((md->expires - now) <= md->renew_window) { else {
int days = (int)(apr_time_sec(md->expires - now) / MD_SECS_PER_DAY); apr_interval_time_t renew_win, left, life;
md_log_perror( MD_LOG_MARK, MD_LOG_DEBUG, 0, p,
"md(%s): %d days to expiry, attempt renewal", md->name, days); renew_win = md->renew_window;
renew = 1; 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_left);
renew = 1;
}
} }
break; break;
case MD_S_INCOMPLETE: case MD_S_INCOMPLETE:
case MD_S_EXPIRED: case MD_S_EXPIRED:
renew = 1; renew = 1;
break; break;
case MD_S_MISSING:
break;
} }
*prenew = renew; *prenew = renew;
*perrored = errored; *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; apr_status_t rv;
rv = md_store_get_fname(pkeyfile, reg->store, MD_SG_DOMAINS, md->name, MD_FN_PKEY, p); *pchainfile = NULL;
rv = md_store_get_fname(pkeyfile, reg->store, MD_SG_DOMAINS, md->name, MD_FN_PRIVKEY, p);
if (APR_SUCCESS == rv) { if (APR_SUCCESS == rv) {
rv = md_store_get_fname(pcertfile, reg->store, MD_SG_DOMAINS, md->name, MD_FN_CERT, p); rv = md_store_get_fname(pcertfile, reg->store, MD_SG_DOMAINS, md->name, MD_FN_PUBCERT, 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;
}
} }
return rv; 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) { if (MD_UPD_RENEW_WINDOW & fields) {
md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, ptemp, "update renew-window: %s", name); 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; nmd->renew_window = updates->renew_window;
} }
if (MD_UPD_CA_CHALLENGES & fields) { 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) static apr_status_t creds_load(void *baton, apr_pool_t *p, apr_pool_t *ptemp, va_list ap)
{ {
md_reg_t *reg = baton; md_reg_t *reg = baton;
apr_status_t rv; md_pkey_t *privkey;
md_cert_t *cert; apr_array_header_t *pubcert;
md_pkey_t *pkey;
apr_array_header_t *chain;
md_creds_t *creds, **pcreds; md_creds_t *creds, **pcreds;
const md_t *md; const md_t *md;
md_cert_state_t cert_state; md_cert_state_t cert_state;
md_store_group_t group; md_store_group_t group;
apr_status_t rv;
pcreds = va_arg(ap, md_creds_t **); 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 *); md = va_arg(ap, const md_t *);
if (ok_or_noent(rv = md_cert_load(reg->store, group, md->name, &cert, p)) if (ok_or_noent(rv = md_pkey_load(reg->store, group, md->name, &privkey, p))
&& ok_or_noent(rv = md_pkey_load(reg->store, group, md->name, &pkey, p)) && ok_or_noent(rv = md_pubcert_load(reg->store, group, md->name, &pubcert, p))) {
&& ok_or_noent(rv = md_chain_load(reg->store, group, md->name, &chain, p))) {
rv = APR_SUCCESS; rv = APR_SUCCESS;
creds = apr_pcalloc(p, sizeof(*creds)); creds = apr_pcalloc(p, sizeof(*creds));
creds->cert = cert; creds->privkey = privkey;
creds->pkey = pkey; if (pubcert && pubcert->nelts > 0) {
creds->chain = chain; creds->pubcert = pubcert;
creds->cert = APR_ARRAY_IDX(pubcert, 0, md_cert_t *);
}
if (creds->cert) { if (creds->cert) {
switch ((cert_state = md_cert_state_get(creds->cert))) { switch ((cert_state = md_cert_state_get(creds->cert))) {
case MD_CERT_VALID: 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; smd->contacts = md->contacts;
fields |= MD_UPD_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, md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p,
"%s: update renew_window, old=%ld, new=%ld", "%s: update renew norm=%ld, window=%ld",
smd->name, (long)smd->renew_window, md->renew_window); smd->name, (long)md->renew_norm, (long)md->renew_window);
smd->renew_norm = md->renew_norm;
smd->renew_window = md->renew_window; smd->renew_window = md->renew_window;
fields |= MD_UPD_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; 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) { if (!proto) {
md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, 0, p, md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, 0, p,
"md %s has unknown CA protocol: %s", md->name, md->ca_proto); "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; 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) { if (!proto) {
md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, 0, p, md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, 0, p,
"md %s has unknown CA protocol: %s", md->name, md->ca_proto); "md %s has unknown CA protocol: %s", md->name, md->ca_proto);

View File

@@ -232,13 +232,13 @@ typedef struct {
apr_status_t md_pkey_load(md_store_t *store, md_store_group_t group, const char *name, 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) 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, 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) 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, 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); 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 { typedef struct {
md_store_t *store; md_store_t *store;
md_store_group_t group; md_store_group_t group;

View File

@@ -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, 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); 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 */ #endif /* mod_md_md_store_h */

View File

@@ -37,7 +37,7 @@
/**************************************************************************************************/ /**************************************************************************************************/
/* file system based implementation of md_store_t */ /* file system based implementation of md_store_t */
#define MD_STORE_VERSION 1.0 #define MD_STORE_VERSION 2
typedef struct { typedef struct {
apr_fileperms_t dir; apr_fileperms_t dir;
@@ -60,8 +60,6 @@ struct md_store_fs_t {
int port_80; int port_80;
int port_443; int port_443;
const unsigned char *dupkey;
}; };
#define FS_STORE(store) (md_store_fs_t*)(((char*)store)-offsetof(md_store_fs_t, s)) #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); md_json_t *json = md_json_create(p);
const char *key64; const char *key64;
unsigned char *key;
apr_status_t rv; 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_sets(MOD_MD_VERSION, json, MD_KEY_VERSION, NULL);
md_json_setn(MD_STORE_VERSION, json, MD_KEY_STORE, 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; 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); md_json_sets(key64, json, MD_KEY_KEY, NULL);
rv = md_json_fcreatex(json, ptemp, MD_JSON_FMT_INDENT, fname, MD_FPROT_F_UONLY); rv = md_json_fcreatex(json, ptemp, MD_JSON_FMT_INDENT, fname, MD_FPROT_F_UONLY);
memset((char*)key64, 0, strlen(key64)); 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; 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) apr_pool_t *p, apr_pool_t *ptemp)
{ {
md_json_t *json; md_json_t *json;
const char *key64; const char *key64, *key;
apr_status_t rv; apr_status_t rv;
double store_version; double store_version;
@@ -145,22 +199,34 @@ 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); md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "version too new: %s", store_version);
return APR_EINVAL; 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); key64 = md_json_dups(p, json, MD_KEY_KEY, NULL);
if (!key64) { if (!key64) {
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "missing key: %s", MD_KEY_KEY); md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "missing key: %s", MD_KEY_KEY);
return APR_EINVAL; return APR_EINVAL;
} }
s_fs->key_len = md_util_base64url_decode((const char **)&s_fs->key, key64, p); s_fs->key_len = md_util_base64url_decode(&key, key64, p);
if (s_fs->key_len < FS_STORE_KLEN) { s_fs->key = (const unsigned char*)key;
md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "key too short: %d", s_fs->key_len); 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; 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; return rv;
} }
@@ -320,7 +386,6 @@ static void get_pass(const char **ppass, apr_size_t *plen,
*plen = 0; *plen = 0;
} }
else { else {
assert(memcmp(s_fs->key, s_fs->dupkey, FS_STORE_KLEN) == 0);
*ppass = (const char *)s_fs->key; *ppass = (const char *)s_fs->key;
*plen = s_fs->key_len; *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; void **pvalue;
apr_status_t rv; apr_status_t rv;
group = va_arg(ap, int); group = (md_store_group_t)va_arg(ap, int);
name = va_arg(ap, const char *); name = va_arg(ap, const char *);
aspect = 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 **); pvalue= va_arg(ap, void **);
rv = fs_get_fname(&fpath, &s_fs->s, group, name, aspect, ptemp); 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; int *pnewer;
apr_status_t rv; apr_status_t rv;
group1 = va_arg(ap, int); group1 = (md_store_group_t)va_arg(ap, int);
group2 = va_arg(ap, int); group2 = (md_store_group_t)va_arg(ap, int);
name = va_arg(ap, const char*); name = va_arg(ap, const char*);
aspect = va_arg(ap, const char*); aspect = va_arg(ap, const char*);
pnewer = va_arg(ap, int*); 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; const char *pass;
apr_size_t pass_len; 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*); name = va_arg(ap, const char*);
aspect = 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 *); value = va_arg(ap, void *);
create = va_arg(ap, int); 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; apr_finfo_t info;
md_store_group_t group; 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*); name = va_arg(ap, const char*);
aspect = va_arg(ap, const char *); aspect = va_arg(ap, const char *);
force = va_arg(ap, int); 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; md_store_group_t group;
apr_status_t rv; apr_status_t rv;
group = va_arg(ap, int); group = (md_store_group_t)va_arg(ap, int);
name = va_arg(ap, const char*); name = va_arg(ap, const char*);
groupname = md_store_group_name(group); 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; int archive;
apr_status_t rv; apr_status_t rv;
from = va_arg(ap, int); from = (md_store_group_t)va_arg(ap, int);
to = va_arg(ap, int); to = (md_store_group_t)va_arg(ap, int);
name = va_arg(ap, const char*); name = va_arg(ap, const char*);
archive = va_arg(ap, int); archive = va_arg(ap, int);

View File

@@ -73,7 +73,7 @@ char *md_util_str_tolower(char *s)
{ {
char *orig = s; char *orig = s;
while (*s) { while (*s) {
*s = apr_tolower(*s); *s = (char)apr_tolower(*s);
++s; ++s;
} }
return orig; return orig;
@@ -759,26 +759,28 @@ const char *md_print_duration(apr_pool_t *p, apr_interval_time_t duration)
/* base64 url encoding ****************************************************************************/ /* 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 */ /* 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 */ N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* 0 */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 1 */ N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* 1 */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, /* 2 */ 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, -1, -1, -1, -1, -1, -1, /* 3 */ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, N6, N6, N6, N6, N6, N6, /* 3 */
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 4 */ 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, -1, -1, -1, -1, 63, /* 5 */ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, N6, N6, N6, N6, 63, /* 5 */
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 6 */ 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, -1, -1, -1, -1, -1, /* 7 */ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, N6, N6, N6, N6, N6, /* 7 */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 8 */ N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* 8 */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 9 */ N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* 9 */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* a */ N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* a */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* b */ N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* b */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* c */ N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* c */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* d */ N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* d */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* e */ N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /* 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 /* f */
}; };
static const char BASE64URL_CHARS[] = { static const unsigned char BASE64URL_CHARS[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', /* 0 - 9 */ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', /* 0 - 9 */
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', /* 10 - 19 */ 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', /* 10 - 19 */
'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', /* 20 - 29 */ '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 */ '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_size_t md_util_base64url_decode(const char **decoded, const char *encoded,
apr_pool_t *pool) apr_pool_t *pool)
{ {
const unsigned char *e = (const unsigned char *)encoded; const unsigned char *e = (const unsigned char *)encoded;
const unsigned char *p = e; const unsigned char *p = e;
unsigned char *d; unsigned char *d;
int n; unsigned int n;
apr_size_t len, mlen, remain, i; long len, mlen, remain, i;
while (*p && BASE64URL_UINT6[ *p ] != -1) { while (*p && BASE64URL_UINT6[ *p ] != N6) {
++p; ++p;
} }
len = p - e; len = p - e;
mlen = (len/4)*4; mlen = (len/4)*4;
*decoded = apr_pcalloc(pool, len+1); *decoded = apr_pcalloc(pool, (apr_size_t)len + 1);
i = 0; i = 0;
d = (unsigned char*)*decoded; 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+1] ] << 12) +
(BASE64URL_UINT6[ e[i+2] ] << 6) + (BASE64URL_UINT6[ e[i+2] ] << 6) +
(BASE64URL_UINT6[ e[i+3] ])); (BASE64URL_UINT6[ e[i+3] ]));
*d++ = n >> 16; *d++ = (unsigned char)(n >> 16);
*d++ = n >> 8 & 0xffu; *d++ = (unsigned char)(n >> 8 & 0xffu);
*d++ = n & 0xffu; *d++ = (unsigned char)(n & 0xffu);
} }
remain = len - mlen; remain = len - mlen;
switch (remain) { switch (remain) {
case 2: case 2:
n = ((BASE64URL_UINT6[ e[mlen+0] ] << 18) + n = ((BASE64URL_UINT6[ e[mlen+0] ] << 18) +
(BASE64URL_UINT6[ e[mlen+1] ] << 12)); (BASE64URL_UINT6[ e[mlen+1] ] << 12));
*d++ = n >> 16; *d++ = (unsigned char)(n >> 16);
remain = 1; remain = 1;
break; break;
case 3: case 3:
n = ((BASE64URL_UINT6[ e[mlen+0] ] << 18) + n = ((BASE64URL_UINT6[ e[mlen+0] ] << 18) +
(BASE64URL_UINT6[ e[mlen+1] ] << 12) + (BASE64URL_UINT6[ e[mlen+1] ] << 12) +
(BASE64URL_UINT6[ e[mlen+2] ] << 6)); (BASE64URL_UINT6[ e[mlen+2] ] << 6));
*d++ = n >> 16; *d++ = (unsigned char)(n >> 16);
*d++ = n >> 8 & 0xffu; *d++ = (unsigned char)(n >> 8 & 0xffu);
remain = 2; remain = 2;
break; break;
default: /* do nothing */ default: /* do nothing */
break; break;
} }
return mlen/4*3 + remain; return (apr_size_t)(mlen/4*3 + remain);
} }
const char *md_util_base64url_encode(const char *data, const char *md_util_base64url_encode(const char *data, apr_size_t dlen, apr_pool_t *pool)
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 */ apr_size_t slen = ((dlen+2)/3)*4 + 1; /* 0 terminated */
const unsigned char *udata = (const unsigned char*)data; 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; enc = p;
for (i = 0; i < len-2; i+= 3) { for (i = 0; i < len-2; i+= 3) {
*p++ = BASE64URL_CHARS[ (udata[i] >> 2) & 0x3fu ]; *p++ = BASE64URL_CHAR( (udata[i] >> 2) );
*p++ = BASE64URL_CHARS[ ((udata[i] << 4) + (udata[i+1] >> 4)) & 0x3fu ]; *p++ = BASE64URL_CHAR( (udata[i] << 4) + (udata[i+1] >> 4) );
*p++ = BASE64URL_CHARS[ ((udata[i+1] << 2) + (udata[i+2] >> 6)) & 0x3fu ]; *p++ = BASE64URL_CHAR( (udata[i+1] << 2) + (udata[i+2] >> 6) );
*p++ = BASE64URL_CHARS[ udata[i+2] & 0x3fu ]; *p++ = BASE64URL_CHAR( (udata[i+2]) );
} }
if (i < len) { if (i < len) {
*p++ = BASE64URL_CHARS[ (udata[i] >> 2) & 0x3fu ]; *p++ = BASE64URL_CHAR( (udata[i] >> 2) );
if (i == (len - 1)) { if (i == (len - 1)) {
*p++ = BASE64URL_CHARS[ (udata[i] << 4) & 0x3fu ]; *p++ = BASE64URL_CHARS[ ((unsigned int)udata[i] << 4) & 0x3fu ];
} }
else { else {
*p++ = BASE64URL_CHARS[ ((udata[i] << 4) + (udata[i+1] >> 4)) & 0x3fu ]; *p++ = BASE64URL_CHAR( (udata[i] << 4) + (udata[i+1] >> 4) );
*p++ = BASE64URL_CHARS[ (udata[i+1] << 2) & 0x3fu ]; *p++ = BASE64URL_CHAR( (udata[i+1] << 2) );
} }
} }
*p++ = '\0'; *p++ = '\0';
return enc; return (char *)enc;
} }
/******************************************************************************* /*******************************************************************************
@@ -874,12 +877,12 @@ const char *md_util_base64url_encode(const char *data,
typedef struct { typedef struct {
const char *s; const char *s;
apr_size_t slen; apr_size_t slen;
int i; apr_size_t i;
int link_start; apr_size_t link_start;
apr_size_t link_len; apr_size_t link_len;
int pn_start; apr_size_t pn_start;
apr_size_t pn_len; apr_size_t pn_len;
int pv_start; apr_size_t pv_start;
apr_size_t pv_len; apr_size_t pv_len;
} link_ctx; } link_ctx;
@@ -960,9 +963,9 @@ static int skip_nonws(link_ctx *ctx)
return (ctx->i < ctx->slen); 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) { for (j = ctx->i; j < ctx->slen; ++j) {
if (ctx->s[j] == c) { if (ctx->s[j] == c) {
*pidx = j; *pidx = j;
@@ -984,7 +987,7 @@ static int read_chr(link_ctx *ctx, char c)
static int skip_qstring(link_ctx *ctx) static int skip_qstring(link_ctx *ctx)
{ {
if (skip_ws(ctx) && read_chr(ctx, '\"')) { if (skip_ws(ctx) && read_chr(ctx, '\"')) {
int end; apr_size_t end;
if (find_chr(ctx, '\"', &end)) { if (find_chr(ctx, '\"', &end)) {
ctx->i = end + 1; ctx->i = end + 1;
return 1; return 1;
@@ -996,7 +999,7 @@ static int skip_qstring(link_ctx *ctx)
static int skip_ptoken(link_ctx *ctx) static int skip_ptoken(link_ctx *ctx)
{ {
if (skip_ws(ctx)) { if (skip_ws(ctx)) {
int i; apr_size_t i;
for (i = ctx->i; i < ctx->slen && ptoken_char(ctx->s[i]); ++i) { for (i = ctx->i; i < ctx->slen && ptoken_char(ctx->s[i]); ++i) {
/* nop */ /* nop */
} }
@@ -1013,7 +1016,7 @@ static int read_link(link_ctx *ctx)
{ {
ctx->link_start = ctx->link_len = 0; ctx->link_start = ctx->link_len = 0;
if (skip_ws(ctx) && read_chr(ctx, '<')) { if (skip_ws(ctx) && read_chr(ctx, '<')) {
int end; apr_size_t end;
if (find_chr(ctx, '>', &end)) { if (find_chr(ctx, '>', &end)) {
ctx->link_start = ctx->i; ctx->link_start = ctx->i;
ctx->link_len = end - ctx->link_start; 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) static int skip_pname(link_ctx *ctx)
{ {
if (skip_ws(ctx)) { if (skip_ws(ctx)) {
int i; apr_size_t i;
for (i = ctx->i; i < ctx->slen && attr_char(ctx->s[i]); ++i) { for (i = ctx->i; i < ctx->slen && attr_char(ctx->s[i]); ++i) {
/* nop */ /* nop */
} }
@@ -1068,7 +1071,7 @@ static int skip_param(link_ctx *ctx)
static int pv_contains(link_ctx *ctx, const char *s) 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; apr_size_t pvlen = ctx->pv_len;
if (ctx->s[pvstart] == '\"' && pvlen > 1) { if (ctx->s[pvstart] == '\"' && pvlen > 1) {
@@ -1078,7 +1081,7 @@ static int pv_contains(link_ctx *ctx, const char *s)
if (pvlen > 0) { if (pvlen > 0) {
apr_size_t slen = strlen(s); apr_size_t slen = strlen(s);
link_ctx pvctx; link_ctx pvctx;
int i; apr_size_t i;
memset(&pvctx, 0, sizeof(pvctx)); memset(&pvctx, 0, sizeof(pvctx));
pvctx.s = ctx->s + pvstart; 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)); memset(&ctx, 0, sizeof(ctx));
ctx.s = value; ctx.s = value;
ctx.slen = (int)strlen(value); ctx.slen = strlen(value);
while (read_link(&ctx)) { while (read_link(&ctx)) {
while (skip_param(&ctx)) { while (skip_param(&ctx)) {

View File

@@ -26,7 +26,7 @@
* @macro * @macro
* Version number of the md module as c string * 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 * @macro
@@ -34,7 +34,7 @@
* release. This is a 24 bit number with 8 bits for major number, 8 bits * 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. * 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_EXPERIMENTAL 0
#define MD_ACME_DEF_URL "https://acme-v01.api.letsencrypt.org/directory" #define MD_ACME_DEF_URL "https://acme-v01.api.letsencrypt.org/directory"

View File

@@ -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) { if (md->drive_mode == MD_DRIVE_DEFAULT) {
md->drive_mode = md_config_geti(md->sc, MD_CONFIG_DRIVE_MODE); 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) { if (md->renew_window <= 0) {
md->renew_window = md_config_get_interval(md->sc, MD_CONFIG_RENEW_WINDOW); 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) { if (!md->ca_challenges && md->sc->ca_challenges) {
md->ca_challenges = apr_array_copy(p, 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) 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; int errored, renew;
char ts[APR_RFC822_DATE_LEN]; char ts[APR_RFC822_DATE_LEN];
if (md->state == MD_S_MISSING) {
rv = APR_INCOMPLETE;
}
if (md->state == MD_S_COMPLETE && !md->expires) { if (md->state == MD_S_COMPLETE && !md->expires) {
/* This is our indicator that we did already renewed this managed domain /* This is our indicator that we did already renewed this managed domain
* successfully and only wait on the next restart for it to activate */ * 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; md_watchdog *wd = baton;
apr_status_t rv = APR_SUCCESS; apr_status_t rv = APR_SUCCESS;
md_t *md; md_t *md;
apr_interval_time_t interval, now; apr_time_t next_run, now;
int i; int i;
switch (state) { 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: case AP_WATCHDOG_STATE_RUNNING:
assert(wd->reg); 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->all_valid = 1;
wd->valid_not_before = 0; wd->valid_not_before = 0;
wd->processed_count = 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) { for (i = 0; i < wd->mds->nelts; ++i) {
md = APR_ARRAY_IDX(wd->mds, i, md_t *); 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->all_valid = 0;
++wd->error_count; ++wd->error_count;
ap_log_error( APLOG_MARK, APLOG_ERR, rv, wd->s, APLOGNO(10056) 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 */ /* Determine when we want to run next */
wd->error_runs = wd->error_count? (wd->error_runs + 1) : 0; wd->error_runs = wd->error_count? (wd->error_runs + 1) : 0;
if (wd->all_valid) { if (wd->all_valid) {
now = apr_time_now(); ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, wd->s, "all managed domains are valid");
if (wd->next_valid > now && (wd->next_valid - now < interval)) {
interval = wd->next_valid - now;
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));
}
else {
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, wd->s,
"all managed domains are valid");
}
} }
else { 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();
/* 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, next_run - now));
}
/* 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 */ /* back off duration, depending on the errors we encounter in a row */
interval = apr_time_from_sec(5 << (wd->error_runs - 1)); delay = apr_time_from_sec(5 << (wd->error_runs - 1));
if (interval > apr_time_from_sec(60*60)) { if (delay > apr_time_from_sec(60*60)) {
interval = apr_time_from_sec(60*60); delay = apr_time_from_sec(60*60);
} }
ap_log_error(APLOG_MARK, APLOG_INFO, 0, wd->s, APLOGNO(10057) if (now + delay < next_run) {
"encountered errors for the %d. time, next run in %d seconds", ap_log_error(APLOG_MARK, APLOG_INFO, 0, wd->s, APLOGNO(10057)
wd->error_runs, (int)apr_time_sec(interval)); "encountered errors for the %d. time, next try by %s",
} wd->error_runs, md_print_duration(ptemp, delay));
next_run = now + delay;
/* 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;
} }
} }
/* 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)) { if (APLOGdebug(wd->s)) {
ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, wd->s, "next run in %s", ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO()
md_print_duration(ptemp, interval)); "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; break;
case AP_WATCHDOG_STATE_STOPPING: case AP_WATCHDOG_STATE_STOPPING:
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO(10058) ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wd->s, APLOGNO(10058)
"md watchdog stopping"); "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) { if (!wd->mds->nelts) {
ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10065) ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10065)
"no managed domain in state to drive, no watchdog needed, " "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); apr_pool_destroy(wd->p);
return APR_SUCCESS; return APR_SUCCESS;
} }

View File

@@ -25,6 +25,7 @@
#include <http_vhost.h> #include <http_vhost.h>
#include "md.h" #include "md.h"
#include "md_crypt.h"
#include "md_util.h" #include "md_util.h"
#include "mod_md_private.h" #include "mod_md_private.h"
#include "mod_md_config.h" #include "mod_md_config.h"
@@ -39,6 +40,7 @@
#define MD_CMD_MEMBER "MDMember" #define MD_CMD_MEMBER "MDMember"
#define MD_CMD_MEMBERS "MDMembers" #define MD_CMD_MEMBERS "MDMembers"
#define MD_CMD_PORTMAP "MDPortMap" #define MD_CMD_PORTMAP "MDPortMap"
#define MD_CMD_PKEYS "MDPrivateKeys"
#define MD_CMD_RENEWWINDOW "MDRenewWindow" #define MD_CMD_RENEWWINDOW "MDRenewWindow"
#define MD_CMD_STOREDIR "MDStoreDir" #define MD_CMD_STOREDIR "MDStoreDir"
@@ -65,8 +67,9 @@ static md_srv_conf_t defconf = {
1, 1,
MD_DRIVE_AUTO, MD_DRIVE_AUTO,
0, 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, MD_ACME_DEF_URL,
"ACME", "ACME",
NULL, NULL,
@@ -109,6 +112,8 @@ static void srv_conf_props_clear(md_srv_conf_t *sc)
sc->transitive = DEF_VAL; sc->transitive = DEF_VAL;
sc->drive_mode = DEF_VAL; sc->drive_mode = DEF_VAL;
sc->must_staple = DEF_VAL; sc->must_staple = DEF_VAL;
sc->pkey_spec = NULL;
sc->renew_norm = DEF_VAL;
sc->renew_window = DEF_VAL; sc->renew_window = DEF_VAL;
sc->ca_url = NULL; sc->ca_url = NULL;
sc->ca_proto = 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->transitive = from->transitive;
to->drive_mode = from->drive_mode; to->drive_mode = from->drive_mode;
to->must_staple = from->must_staple; 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->renew_window = from->renew_window;
to->ca_url = from->ca_url; to->ca_url = from->ca_url;
to->ca_proto = from->ca_proto; 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->transitive != DEF_VAL) md->transitive = from->transitive;
if (from->drive_mode != DEF_VAL) md->drive_mode = from->drive_mode; 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->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->renew_window != DEF_VAL) md->renew_window = from->renew_window;
if (from->ca_url) md->ca_url = from->ca_url; 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->transitive = (add->transitive != DEF_VAL)? add->transitive : base->transitive;
nsc->drive_mode = (add->drive_mode != DEF_VAL)? add->drive_mode : base->drive_mode; 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->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; 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 '>'"; 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) { if (!arg || !*arg) {
return MD_CMD_MD_SECTION " > section must specify a unique domain name"; 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; 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) 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); md_srv_conf_t *config = md_config_get(cmd->server);
const char *err; const char *err;
apr_interval_time_t timeout; apr_interval_time_t timeout;
int percent;
/* Inspired by http_core.c */
if (duration_parse(value, &timeout, "d") != APR_SUCCESS) {
return "MDRenewWindow has wrong format";
}
if (!inside_section(cmd, MD_CMD_MD_SECTION) if (!inside_section(cmd, MD_CMD_MD_SECTION)
&& (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
return err; return err;
} }
config->renew_window = timeout;
return NULL; /* 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) 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; 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[] = { const command_rec md_cmds[] = {
AP_INIT_TAKE1( MD_CMD_CA, md_config_set_ca, NULL, RSRC_CONF, 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 " "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 " "internet. Use 80:- to indicate that port 80 is not reachable from "
"the outside."), "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, AP_INIT_TAKE1( MD_CMD_STOREDIR, md_config_set_store_dir, NULL, RSRC_CONF,
"the directory for file system storage of managed domain data."), "the directory for file system storage of managed domain data."),
AP_INIT_TAKE1( MD_CMD_RENEWWINDOW, md_config_set_renew_window, NULL, RSRC_CONF, 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) apr_interval_time_t md_config_get_interval(const md_srv_conf_t *sc, md_config_var_t var)
{ {
switch (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: case MD_CONFIG_RENEW_WINDOW:
return (sc->renew_window != DEF_VAL)? sc->renew_window : defconf.renew_window; return (sc->renew_window != DEF_VAL)? sc->renew_window : defconf.renew_window;
default: default:

View File

@@ -17,6 +17,7 @@
#define mod_md_md_config_h #define mod_md_md_config_h
struct md_store_t; struct md_store_t;
struct md_pkey_spec_t;
typedef enum { typedef enum {
MD_CONFIG_CA_URL, MD_CONFIG_CA_URL,
@@ -26,6 +27,7 @@ typedef enum {
MD_CONFIG_DRIVE_MODE, MD_CONFIG_DRIVE_MODE,
MD_CONFIG_LOCAL_80, MD_CONFIG_LOCAL_80,
MD_CONFIG_LOCAL_443, MD_CONFIG_LOCAL_443,
MD_CONFIG_RENEW_NORM,
MD_CONFIG_RENEW_WINDOW, MD_CONFIG_RENEW_WINDOW,
MD_CONFIG_TRANSITIVE, MD_CONFIG_TRANSITIVE,
} md_config_var_t; } 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 transitive; /* != 0 iff VirtualHost names/aliases are auto-added */
int drive_mode; /* mode of obtaining credentials */ int drive_mode; /* mode of obtaining credentials */
int must_staple; /* certificates should set the OCSP Must Staple extension */ 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 */ apr_interval_time_t renew_window; /* time before expiration that starts renewal */
const char *ca_url; /* url of CA certificate service */ const char *ca_url; /* url of CA certificate service */

View File

@@ -34,7 +34,7 @@
#include "md_util.h" #include "md_util.h"
#include "mod_md_os.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 AP_NEED_SET_MUTEX_PERMS
if (-1 == chown(fname, (uid_t)uid, (gid_t)gid)) { if (-1 == chown(fname, (uid_t)uid, (gid_t)gid)) {

View File

@@ -20,7 +20,7 @@
* Try chown'ing the file/directory. Give id -1 to not change uid/gid. * Try chown'ing the file/directory. Give id -1 to not change uid/gid.
* Will return APR_ENOTIMPL on platforms not supporting this operation. * 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. * Make a file or directory read/write(/searchable) by httpd workers.

View File

@@ -1723,8 +1723,9 @@ static apr_status_t ssl_init_server_ctx(server_rec *s,
else if (APR_STATUS_IS_EAGAIN(rv)) { else if (APR_STATUS_IS_EAGAIN(rv)) {
/* Managed Domain not ready yet. This is not a reason to fail the config */ /* 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) ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10085)
"Init: (%s) disabling this host for now as certificate/key data " "Init: %s will respond with '503 Service Unavailable' for now. This "
"for the Managed Domain is incomplete.", ssl_util_vhostid(p, s)); "host is part of a Managed Domain, but no SSL certificate is "
"available (yet).", ssl_util_vhostid(p, s));
pks->service_unavailable = 1; pks->service_unavailable = 1;
return APR_SUCCESS; return APR_SUCCESS;
} }