Commit f720854d authored by Stefan Eissing's avatar Stefan Eissing
Browse files

On the trunk:

mod_md: v0.9.5:
     - New directive (srly: what do you expect at this point?) "MDMustStaple on|off" to control if
       new certificates are requested with the OCSP Must Staple extension.
     - Known limitation: when the server is configured to ditch and restart child processes, for example
       after a certain number of connections/requests, the mod_md watchdog instance might migrate 
       to a new child process. Since not all its state is persisted, some messsages might appear a
       second time in the logs.
     - Adding checks when 'MDRequireHttps' is used. It is considered an error when 'MDPortMap 443:-'
       is used - which negates that a https: port exists. Also, a warning is logged if no 
       VirtualHost can be found for a Managed Domain that has port 443 (or the mapped one) in
       its address list.
     - New directive 'MDRequireHttps' for redirecting http: traffic to a Managed Domain, permanently
       or temporarily.
     - Fix for using a fallback certificate on initial signup of a Managed Domain. Requires also
       a changed mod_ssl patch (v5) to take effect.
     - compatibility with libressl



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1808241 13f79535-47bb-0310-9956-ffa450edef68
parent e837836f
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
                                                         -*- coding: utf-8 -*-
Changes with Apache 2.5.0

  *) mod_md: v0.9.5:
     - New directive (srly: what do you expect at this point?) "MDMustStaple on|off" to control if
       new certificates are requested with the OCSP Must Staple extension.
     - Known limitation: when the server is configured to ditch and restart child processes, for example
       after a certain number of connections/requests, the mod_md watchdog instance might migrate 
       to a new child process. Since not all its state is persisted, some messsages might appear a
       second time in the logs.
     - Adding checks when 'MDRequireHttps' is used. It is considered an error when 'MDPortMap 443:-'
       is used - which negates that a https: port exists. Also, a warning is logged if no 
       VirtualHost can be found for a Managed Domain that has port 443 (or the mapped one) in
       its address list.
     - New directive 'MDRequireHttps' for redirecting http: traffic to a Managed Domain, permanently
       or temporarily.
     - Fix for using a fallback certificate on initial signup of a Managed Domain. Requires also
       a changed mod_ssl patch (v5) to take effect.
     - compatibility with libressl
       [Stefan Eissing]

  *) htdigest: prevent a buffer overflow when a string exceeds the allowed max
     length in a password file.
     [Luca Toscano, Hanno Böck <hanno hboeck de>]
+13 −0
Original line number Diff line number Diff line
@@ -41,6 +41,13 @@ typedef enum {
    MD_S_MISSING,                   /* MD is missing config information, cannot proceed */
} md_state_t;

typedef enum {
    MD_REQUIRE_UNSET = -1,
    MD_REQUIRE_OFF,
    MD_REQUIRE_TEMPORARY,
    MD_REQUIRE_PERMANENT,
} md_require_t;

typedef enum {
    MD_SV_TEXT,
    MD_SV_JSON,
@@ -74,6 +81,8 @@ struct md_t {
    struct apr_array_header_t *contacts;   /* list of contact uris, e.g. mailto:xxx */

    int transitive;                 /* != 0 iff VirtualHost names/aliases are auto-added */
    md_require_t require_https;     /* Iff https: is required for this MD */
    
    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 */
@@ -119,16 +128,20 @@ struct md_t {
#define MD_KEY_KEY              "key"
#define MD_KEY_KEYAUTHZ         "keyAuthorization"
#define MD_KEY_LOCATION         "location"
#define MD_KEY_MUST_STAPLE      "must-staple"
#define MD_KEY_NAME             "name"
#define MD_KEY_PERMANENT        "permanent"
#define MD_KEY_PKEY             "privkey"
#define MD_KEY_PROTO            "proto"
#define MD_KEY_REGISTRATION     "registration"
#define MD_KEY_RENEW            "renew"
#define MD_KEY_RENEW_WINDOW     "renew-window"
#define MD_KEY_REQUIRE_HTTPS    "require-https"
#define MD_KEY_RESOURCE         "resource"
#define MD_KEY_STATE            "state"
#define MD_KEY_STATUS           "status"
#define MD_KEY_STORE            "store"
#define MD_KEY_TEMPORARY        "temporary"
#define MD_KEY_TOKEN            "token"
#define MD_KEY_TRANSITIVE       "transitive"
#define MD_KEY_TYPE             "type"
+4 −1
Original line number Diff line number Diff line
@@ -356,6 +356,7 @@ static apr_status_t cha_tls_sni_01_setup(md_acme_authz_cha_t *cha, md_acme_authz
    const char *cha_dns;
    apr_status_t rv;
    int notify_server;
    apr_array_header_t *domains;
    
    if (   APR_SUCCESS != (rv = setup_key_authz(cha, authz, acme, p, &notify_server))
        || APR_SUCCESS != (rv = setup_cha_dns(&cha_dns, cha, p))) {
@@ -374,7 +375,9 @@ static apr_status_t cha_tls_sni_01_setup(md_acme_authz_cha_t *cha, md_acme_authz
        }

        /* setup a certificate containing the challenge dns */
        rv = md_cert_self_sign(&cha_cert, authz->domain, cha_dns, cha_key, 
        domains = apr_array_make(p, 5, sizeof(const char*));
        APR_ARRAY_PUSH(domains, const char*) = cha_dns;
        rv = md_cert_self_sign(&cha_cert, authz->domain, domains, cha_key, 
                               apr_time_from_sec(7 * MD_SECS_PER_DAY), p);
        
        if (APR_SUCCESS != rv) {
+28 −1
Original line number Diff line number Diff line
@@ -85,6 +85,8 @@ md_t *md_create_empty(apr_pool_t *p)
        md->domains = apr_array_make(p, 5, sizeof(const char *));
        md->contacts = apr_array_make(p, 5, sizeof(const char *));
        md->drive_mode = MD_DRIVE_DEFAULT;
        md->require_https = MD_REQUIRE_UNSET;
        md->must_staple = -1;
        md->transitive = -1;
        md->defn_name = "unknown";
        md->defn_line_number = 0;
@@ -256,6 +258,8 @@ md_t *md_clone(apr_pool_t *p, const md_t *src)
    if (md) {
        md->state = src->state;
        md->name = apr_pstrdup(p, src->name);
        md->require_https = src->require_https;
        md->must_staple = src->must_staple;
        md->drive_mode = src->drive_mode;
        md->domains = md_array_str_compact(p, src->domains, 0);
        md->pkey_spec = src->pkey_spec;
@@ -283,6 +287,8 @@ md_t *md_merge(apr_pool_t *p, const md_t *add, const md_t *base)
    n->ca_url = add->ca_url? add->ca_url : base->ca_url;
    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->require_https = (add->require_https != MD_REQUIRE_UNSET)? add->require_https : base->require_https;
    n->must_staple = (add->must_staple >= 0)? add->must_staple : base->must_staple;
    n->drive_mode = (add->drive_mode != MD_DRIVE_DEFAULT)? add->drive_mode : base->drive_mode;
    n->pkey_spec = add->pkey_spec? add->pkey_spec : base->pkey_spec;
    n->renew_norm = (add->renew_norm > 0)? add->renew_norm : base->renew_norm;
@@ -344,6 +350,17 @@ md_json_t *md_to_json(const md_t *md, apr_pool_t *p)
            na = md_array_str_compact(p, md->ca_challenges, 0);
            md_json_setsa(na, json, MD_KEY_CA, MD_KEY_CHALLENGES, NULL);
        }
        switch (md->require_https) {
            case MD_REQUIRE_TEMPORARY:
                md_json_sets(MD_KEY_TEMPORARY, json, MD_KEY_REQUIRE_HTTPS, NULL);
                break;
            case MD_REQUIRE_PERMANENT:
                md_json_sets(MD_KEY_PERMANENT, json, MD_KEY_REQUIRE_HTTPS, NULL);
                break;
            default:
                break;
        }
        md_json_setb(md->must_staple > 0, json, MD_KEY_MUST_STAPLE, NULL);
        return json;
    }
    return NULL;
@@ -380,7 +397,7 @@ md_t *md_from_json(md_json_t *json, apr_pool_t *p)
        md->renew_norm = 0;
        md->renew_window = apr_time_from_sec(md_json_getl(json, MD_KEY_RENEW_WINDOW, NULL));
        if (md->renew_window <= 0) {
            const char *s = md_json_gets(json, MD_KEY_RENEW_WINDOW, NULL);
            s = md_json_gets(json, MD_KEY_RENEW_WINDOW, NULL);
            if (s && strchr(s, '%')) {
                int percent = atoi(s);
                if (0 < percent && percent < 100) {
@@ -393,6 +410,16 @@ md_t *md_from_json(md_json_t *json, apr_pool_t *p)
            md->ca_challenges = apr_array_make(p, 5, sizeof(const char*));
            md_json_dupsa(md->ca_challenges, p, json, MD_KEY_CA, MD_KEY_CHALLENGES, NULL);
        }
        md->require_https = MD_REQUIRE_OFF;
        s = md_json_gets(json, MD_KEY_REQUIRE_HTTPS, NULL);
        if (s && !strcmp(MD_KEY_TEMPORARY, s)) {
            md->require_https = MD_REQUIRE_TEMPORARY;
        }
        else if (s && !strcmp(MD_KEY_PERMANENT, s)) {
            md->require_https = MD_REQUIRE_PERMANENT;
        }
        md->must_staple = (int)md_json_getb(json, MD_KEY_MUST_STAPLE, NULL);
        
        return md;
    }
    return NULL;
+86 −35
Original line number Diff line number Diff line
@@ -178,6 +178,66 @@ static int pem_passwd(char *buf, int size, int rwflag, void *baton)
    return ctx->pass_len;
}

/**************************************************************************************************/
/* date time things */

/* Get the apr time (micro seconds, since 1970) from an ASN1 time, as stored in X509
 * certificates. OpenSSL now has a utility function, but other *SSL derivatives have
 * not caughts up yet or chose to ignore. An alternative is implemented, we prefer 
 * however the *SSL to maintain such things.
 */
static apr_time_t md_asn1_time_get(const ASN1_TIME* time)
{
#ifdef LIBRESSL_VERSION_NUMBER
    /* courtesy: https://stackoverflow.com/questions/10975542/asn1-time-to-time-t-conversion#11263731
     * all bugs are mine */
    apr_time_exp_t t;
    apr_time_t ts;
    const char* str = (const char*) time->data;
    apr_size_t i = 0;

    memset(&t, 0, sizeof(t));

    if (time->type == V_ASN1_UTCTIME) {/* two digit year */
        t.tm_year = (str[i++] - '0') * 10;
        t.tm_year += (str[i++] - '0');
        if (t.tm_year < 70)
            t.tm_year += 100;
    } 
    else if (time->type == V_ASN1_GENERALIZEDTIME) {/* four digit year */
        t.tm_year = (str[i++] - '0') * 1000;
        t.tm_year+= (str[i++] - '0') * 100;
        t.tm_year+= (str[i++] - '0') * 10;
        t.tm_year+= (str[i++] - '0');
        t.tm_year -= 1900;
    }
    t.tm_mon  = (str[i++] - '0') * 10;
    t.tm_mon += (str[i++] - '0') - 1; /* -1 since January is 0 not 1. */
    t.tm_mday = (str[i++] - '0') * 10;
    t.tm_mday+= (str[i++] - '0');
    t.tm_hour = (str[i++] - '0') * 10;
    t.tm_hour+= (str[i++] - '0');
    t.tm_min  = (str[i++] - '0') * 10;
    t.tm_min += (str[i++] - '0');
    t.tm_sec  = (str[i++] - '0') * 10;
    t.tm_sec += (str[i++] - '0');
    
    if (APR_SUCCESS == apr_time_exp_gmt_get(&ts, &t)) {
        return ts;
    }
    return 0;
#else 
    int secs, days;
    apr_time_t ts = apr_time_now();
    
    if (ASN1_TIME_diff(&days, &secs, NULL, time)) {
        ts += apr_time_from_sec((days * MD_SECS_PER_DAY) + secs); 
    }
    return ts;
#endif
}


/**************************************************************************************************/
/* private keys */

@@ -409,7 +469,7 @@ apr_status_t md_pkey_gen(md_pkey_t **ppkey, apr_pool_t *p, md_pkey_spec_t *spec)
    }
}

#if OPENSSL_VERSION_NUMBER < 0x10100000L
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)

#ifndef NID_tlsfeature
#define NID_tlsfeature          1020
@@ -658,26 +718,12 @@ int md_cert_has_expired(const md_cert_t *cert)

apr_time_t md_cert_get_not_after(md_cert_t *cert)
{
    int secs, days;
    apr_time_t time = apr_time_now();
    ASN1_TIME *not_after = X509_get_notAfter(cert->x509);
    
    if (ASN1_TIME_diff(&days, &secs, NULL, not_after)) {
        time += apr_time_from_sec((days * MD_SECS_PER_DAY) + secs); 
    }
    return time;
    return md_asn1_time_get(X509_get_notAfter(cert->x509));
}

apr_time_t md_cert_get_not_before(md_cert_t *cert)
{
    int secs, days;
    apr_time_t time = apr_time_now();
    ASN1_TIME *not_after = X509_get_notBefore(cert->x509);
    
    if (ASN1_TIME_diff(&days, &secs, NULL, not_after)) {
        time += apr_time_from_sec((days * MD_SECS_PER_DAY) + secs); 
    }
    return time;
    return md_asn1_time_get(X509_get_notBefore(cert->x509));
}

int md_cert_covers_domain(md_cert_t *cert, const char *domain_name)
@@ -991,11 +1037,6 @@ apr_status_t md_chain_fsave(apr_array_header_t *certs, apr_pool_t *p,
/**************************************************************************************************/
/* certificate signing requests */

static const char *alt_name(const char *domain, apr_pool_t *p)
{
    return apr_psprintf(p, "DNS:%s", domain);
}

static const char *alt_names(apr_array_header_t *domains, apr_pool_t *p)
{
    const char *alts = "", *sep = "", *domain;
@@ -1051,9 +1092,19 @@ static apr_status_t add_must_staple(STACK_OF(X509_EXTENSION) *exts, const md_t *
{
    
    if (md->must_staple) {
        X509_EXTENSION *x = X509V3_EXT_conf_nid(NULL, NULL, 
                                                NID_tlsfeature, (char*)"DER:30:03:02:01:05");
        X509_EXTENSION *x;
        int nid;
        
        nid = OBJ_create("1.3.6.1.5.5.7.1.24", "OCSPReq", "OCSP Request");
        if (NID_undef == nid) {
            md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, 
                          "%s: unable to get NID for must-staple", md->name);
            return APR_EGENERAL;
        }
        x = X509V3_EXT_conf_nid(NULL, NULL, nid, (char*)"DER:30:03:02:01:05");
        if (NULL == x) {
            md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, 
                          "%s: unable to get x509 extension for must-staple", md->name);
            return APR_EGENERAL;
        }
        sk_X509_EXTENSION_push(exts, x);
@@ -1140,7 +1191,7 @@ out:
}

apr_status_t md_cert_self_sign(md_cert_t **pcert, const char *cn, 
                               const char *domain, md_pkey_t *pkey,
                               apr_array_header_t *domains, md_pkey_t *pkey,
                               apr_interval_time_t valid_for, apr_pool_t *p)
{
    X509 *x;
@@ -1152,45 +1203,45 @@ apr_status_t md_cert_self_sign(md_cert_t **pcert, const char *cn,
    ASN1_INTEGER *asn1_rnd = NULL;
    unsigned char rnd[20];
    
    assert(domain);
    assert(domains);
    
    if (NULL == (x = X509_new()) 
        || NULL == (n = X509_NAME_new())) {
        rv = APR_ENOMEM;
        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: openssl alloc X509 things", domain);
        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: openssl alloc X509 things", cn);
        goto out; 
    }
    
    if (APR_SUCCESS != (rv = md_rand_bytes(rnd, sizeof(rnd), p))
        || !(big_rnd = BN_bin2bn(rnd, sizeof(rnd), NULL))
        || !(asn1_rnd = BN_to_ASN1_INTEGER(big_rnd, NULL))) {
        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: setup random serial", domain);
        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: setup random serial", cn);
        rv = APR_EGENERAL; goto out;
    } 
    if (!X509_set_serialNumber(x, asn1_rnd)) {
        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: set serial number", domain);
        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: set serial number", cn);
        rv = APR_EGENERAL; goto out;
    }
    /* set common name and issue */
    if (!X509_NAME_add_entry_by_txt(n, "CN", MBSTRING_ASC, (const unsigned char*)cn, -1, -1, 0)
        || !X509_set_subject_name(x, n)
        || !X509_set_issuer_name(x, n)) {
        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: name add entry", domain);
        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, p, "%s: name add entry", cn);
        rv = APR_EGENERAL; goto out;
    }
    /* cert are uncontrained (but not very trustworthy) */
    if (APR_SUCCESS != (rv = add_ext(x, NID_basic_constraints, "CA:TRUE, pathlen:0", p))) {
        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set basic constraints ext", domain);
        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set basic constraints ext", cn);
        goto out;
    }
    /* add the domain as alt name */
    if (APR_SUCCESS != (rv = add_ext(x, NID_subject_alt_name, alt_name(domain, p), p))) {
        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set alt_name ext", domain);
    if (APR_SUCCESS != (rv = add_ext(x, NID_subject_alt_name, alt_names(domains, p), p))) {
        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set alt_name ext", cn);
        goto out;
    }
    /* add our key */
    if (!X509_set_pubkey(x, pkey->pkey)) {
        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set pkey in x509", domain);
        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: set pkey in x509", cn);
        rv = APR_EGENERAL; goto out;
    }
    
@@ -1204,7 +1255,7 @@ apr_status_t md_cert_self_sign(md_cert_t **pcert, const char *cn,

    /* sign with same key */
    if (!X509_sign(x, pkey->pkey, EVP_sha256())) {
        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: sign x509", domain);
        md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: sign x509", cn);
        rv = APR_EGENERAL; goto out;
    }

Loading