Commit 514cab60 authored by Joe Orton's avatar Joe Orton
Browse files

Improve mod_ssl's environment variable extraction to correctly handle

DNs with duplicate tags:

* modules/ssl/ssl_engine_vars.c:
  Augment the ssl_var_lookup_ssl_cert_dn_rec table with a flag
  to indicate whether RDNs of the given NID should be extracted
  to the environment.
  (extract_dn, modssl_var_extract_dns): New functions.

* modules/ssl/ssl_private.h (modssl_var_extract_dns): Add prototype.

* modules/ssl/ssl_engine_kernel.c: Remove SSL_*_DN_ from the list
  of variables to insert into the environment.
  (ssl_hook_Fixup): Use modssl_var_extract_dns to insert the
  SSL_*_DN_ variables efficiently and accurately, handling
  certs with duplicate RDN tags correctly.

PR: 45875


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@724717 13f79535-47bb-0310-9956-ffa450edef68
parent 45810da6
Loading
Loading
Loading
Loading
+4 −0
Original line number Original line Diff line number Diff line
@@ -2,6 +2,10 @@
Changes with Apache 2.3.1
Changes with Apache 2.3.1
[ When backported to 2.2.x, remove entry from this file ]
[ When backported to 2.2.x, remove entry from this file ]


  *) mod_ssl: Improve environment variable extraction to be more
     efficient and to correctly handle DNs with duplicate tags.
     PR 45975.  [Joe Orton]

  *) Remove the obsolete serial attribute from the RPM spec file. Compile
  *) Remove the obsolete serial attribute from the RPM spec file. Compile
     against the external pcre. Add missing binaries fcgistarter, and
     against the external pcre. Add missing binaries fcgistarter, and
     mod_socache* and mod_session*. [Graham Leggett]
     mod_socache* and mod_session*. [Graham Leggett]
+2 −56
Original line number Original line Diff line number Diff line
@@ -1012,68 +1012,12 @@ static const char *ssl_hook_Fixup_vars[] = {
    "SSL_CLIENT_V_START",
    "SSL_CLIENT_V_START",
    "SSL_CLIENT_V_END",
    "SSL_CLIENT_V_END",
    "SSL_CLIENT_V_REMAIN",
    "SSL_CLIENT_V_REMAIN",
    "SSL_CLIENT_S_DN",
    "SSL_CLIENT_S_DN_C",
    "SSL_CLIENT_S_DN_ST",
    "SSL_CLIENT_S_DN_L",
    "SSL_CLIENT_S_DN_O",
    "SSL_CLIENT_S_DN_OU",
    "SSL_CLIENT_S_DN_CN",
    "SSL_CLIENT_S_DN_T",
    "SSL_CLIENT_S_DN_I",
    "SSL_CLIENT_S_DN_G",
    "SSL_CLIENT_S_DN_S",
    "SSL_CLIENT_S_DN_D",
    "SSL_CLIENT_S_DN_UID",
    "SSL_CLIENT_S_DN_Email",
    "SSL_CLIENT_I_DN",
    "SSL_CLIENT_I_DN_C",
    "SSL_CLIENT_I_DN_ST",
    "SSL_CLIENT_I_DN_L",
    "SSL_CLIENT_I_DN_O",
    "SSL_CLIENT_I_DN_OU",
    "SSL_CLIENT_I_DN_CN",
    "SSL_CLIENT_I_DN_T",
    "SSL_CLIENT_I_DN_I",
    "SSL_CLIENT_I_DN_G",
    "SSL_CLIENT_I_DN_S",
    "SSL_CLIENT_I_DN_D",
    "SSL_CLIENT_I_DN_UID",
    "SSL_CLIENT_I_DN_Email",
    "SSL_CLIENT_A_KEY",
    "SSL_CLIENT_A_KEY",
    "SSL_CLIENT_A_SIG",
    "SSL_CLIENT_A_SIG",
    "SSL_SERVER_M_VERSION",
    "SSL_SERVER_M_VERSION",
    "SSL_SERVER_M_SERIAL",
    "SSL_SERVER_M_SERIAL",
    "SSL_SERVER_V_START",
    "SSL_SERVER_V_START",
    "SSL_SERVER_V_END",
    "SSL_SERVER_V_END",
    "SSL_SERVER_S_DN",
    "SSL_SERVER_S_DN_C",
    "SSL_SERVER_S_DN_ST",
    "SSL_SERVER_S_DN_L",
    "SSL_SERVER_S_DN_O",
    "SSL_SERVER_S_DN_OU",
    "SSL_SERVER_S_DN_CN",
    "SSL_SERVER_S_DN_T",
    "SSL_SERVER_S_DN_I",
    "SSL_SERVER_S_DN_G",
    "SSL_SERVER_S_DN_S",
    "SSL_SERVER_S_DN_D",
    "SSL_SERVER_S_DN_UID",
    "SSL_SERVER_S_DN_Email",
    "SSL_SERVER_I_DN",
    "SSL_SERVER_I_DN_C",
    "SSL_SERVER_I_DN_ST",
    "SSL_SERVER_I_DN_L",
    "SSL_SERVER_I_DN_O",
    "SSL_SERVER_I_DN_OU",
    "SSL_SERVER_I_DN_CN",
    "SSL_SERVER_I_DN_T",
    "SSL_SERVER_I_DN_I",
    "SSL_SERVER_I_DN_G",
    "SSL_SERVER_I_DN_S",
    "SSL_SERVER_I_DN_D",
    "SSL_SERVER_I_DN_UID",
    "SSL_SERVER_I_DN_Email",
    "SSL_SERVER_A_KEY",
    "SSL_SERVER_A_KEY",
    "SSL_SERVER_A_SIG",
    "SSL_SERVER_A_SIG",
    "SSL_SESSION_ID",
    "SSL_SESSION_ID",
@@ -1125,6 +1069,8 @@ int ssl_hook_Fixup(request_rec *r)


    /* standard SSL environment variables */
    /* standard SSL environment variables */
    if (dc->nOptions & SSL_OPT_STDENVVARS) {
    if (dc->nOptions & SSL_OPT_STDENVVARS) {
        modssl_var_extract_dns(env, sslconn->ssl, r->pool);

        for (i = 0; ssl_hook_Fixup_vars[i]; i++) {
        for (i = 0; ssl_hook_Fixup_vars[i]; i++) {
            var = (char *)ssl_hook_Fixup_vars[i];
            var = (char *)ssl_hook_Fixup_vars[i];
            val = ssl_var_lookup(r->pool, r->server, r->connection, r, var);
            val = ssl_var_lookup(r->pool, r->server, r->connection, r, var);
+108 −15
Original line number Original line Diff line number Diff line
@@ -402,28 +402,32 @@ static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, X509 *xs, char *var)
    return result;
    return result;
}
}


/* In this table, .extract is non-zero if RDNs using the NID should be
 * extracted to for the SSL_{CLIENT,SERVER}_{I,S}_DN_* environment
 * variables. */
static const struct {
static const struct {
    char *name;
    char *name;
    int   nid;
    int   nid;
    int   extract;
} ssl_var_lookup_ssl_cert_dn_rec[] = {
} ssl_var_lookup_ssl_cert_dn_rec[] = {
    { "C",     NID_countryName            },
    { "C",     NID_countryName,            1 },
    { "ST",    NID_stateOrProvinceName    }, /* officially    (RFC2156) */
    { "ST",    NID_stateOrProvinceName,    1 }, /* officially    (RFC2156) */
    { "SP",    NID_stateOrProvinceName    }, /* compatibility (SSLeay)  */
    { "SP",    NID_stateOrProvinceName,    0 }, /* compatibility (SSLeay)  */
    { "L",     NID_localityName           },
    { "L",     NID_localityName,           1 },
    { "O",     NID_organizationName       },
    { "O",     NID_organizationName,       1 },
    { "OU",    NID_organizationalUnitName },
    { "OU",    NID_organizationalUnitName, 1 },
    { "CN",    NID_commonName             },
    { "CN",    NID_commonName,             1 },
    { "T",     NID_title                  },
    { "T",     NID_title,                  1 },
    { "I",     NID_initials               },
    { "I",     NID_initials,               1 },
    { "G",     NID_givenName              },
    { "G",     NID_givenName,              1 },
    { "S",     NID_surname                },
    { "S",     NID_surname,                1 },
    { "D",     NID_description            },
    { "D",     NID_description,            1 },
#ifdef NID_x500UniqueIdentifier /* new name as of Openssl 0.9.7 */
#ifdef NID_x500UniqueIdentifier /* new name as of Openssl 0.9.7 */
    { "UID",   NID_x500UniqueIdentifier   },
    { "UID",   NID_x500UniqueIdentifier,   1 },
#else /* old name, OpenSSL < 0.9.7 */
#else /* old name, OpenSSL < 0.9.7 */
    { "UID",   NID_uniqueIdentifier       },
    { "UID",   NID_uniqueIdentifier,       1 },
#endif
#endif
    { "Email", NID_pkcs9_emailAddress     },
    { "Email", NID_pkcs9_emailAddress,     1 },
    { NULL,    0                          }
    { NULL,    0                          }
};
};


@@ -673,6 +677,95 @@ static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var)
    return NULL;
    return NULL;
}
}
  
  
/* Add each RDN in 'xn' to the table 't' where the NID is present in
 * 'nids', using key prefix 'pfx'.  */
static void extract_dn(apr_table_t *t, apr_hash_t *nids, const char *pfx, 
                       X509_NAME *xn, apr_pool_t *p)
{
    STACK_OF(X509_NAME_ENTRY) *ents = X509_NAME_get_entries(xn);
    X509_NAME_ENTRY *xsne;
    apr_hash_t *count;
    int i, nid;

    /* Hash of (int) NID -> (int *) counter to count each time an RDN
     * with the given NID has been seen. */
    count = apr_hash_make(p);

    /* For each RDN... */
    for (i = 0; i < sk_X509_NAME_ENTRY_num(ents); i++) {
         const char *tag;

         xsne = sk_X509_NAME_ENTRY_value(ents, i);

         /* Retrieve the nid, and check whether this is one of the nids
          * which are to be extracted. */
         nid = OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));

         tag = apr_hash_get(nids, &nid, sizeof nid);
         if (tag) {
             unsigned char *data = X509_NAME_ENTRY_get_data_ptr(xsne);
             const char *key;
             int *dup;
             char *value;

             /* Check whether a variable with this nid was already
              * been used; if so, use the foo_N=bar syntax. */
             dup = apr_hash_get(count, &nid, sizeof nid);
             if (dup) {
                 key = apr_psprintf(p, "%s%s_%d", pfx, tag, ++(*dup));
             }
             else {
                 /* Otherwise, use the plain foo=bar syntax. */
                 dup = apr_pcalloc(p, sizeof *dup);
                 apr_hash_set(count, &nid, sizeof nid, dup);
                 key = apr_pstrcat(p, pfx, tag, NULL);
             }
             
             /* cast needed from 'unsigned char *' to 'char *' */
             value = apr_pstrmemdup(p, (char *)data,
                                    X509_NAME_ENTRY_get_data_len(xsne));
#if APR_CHARSET_EBCDIC
             ap_xlate_proto_from_ascii(value, X509_NAME_ENTRY_get_data_len(xsne));
#endif /* APR_CHARSET_EBCDIC */
             apr_table_setn(t, key, value);
         }
    }
}

void modssl_var_extract_dns(apr_table_t *t, SSL *ssl, apr_pool_t *p)
{
    apr_hash_t *nids;
    unsigned n;
    X509 *xs;

    /* Build up a hash table of (int *)NID->(char *)short-name for all
     * the tags which are to be extracted: */
    nids = apr_hash_make(p);
    for (n = 0; ssl_var_lookup_ssl_cert_dn_rec[n].name; n++) {
        if (ssl_var_lookup_ssl_cert_dn_rec[n].extract) {
            apr_hash_set(nids, &ssl_var_lookup_ssl_cert_dn_rec[n].nid,
                         sizeof(ssl_var_lookup_ssl_cert_dn_rec[0].nid),
                         ssl_var_lookup_ssl_cert_dn_rec[n].name);
        }
    }
    
    /* Extract the server cert DNS -- note that the refcount does NOT
     * increase: */
    xs = SSL_get_certificate(ssl);
    if (xs) {
        extract_dn(t, nids, "SSL_SERVER_S_DN_", X509_get_subject_name(xs), p);
        extract_dn(t, nids, "SSL_SERVER_I_DN_", X509_get_issuer_name(xs), p);
    }
    
    /* Extract the client cert DNs -- note that the refcount DOES
     * increase: */
    xs = SSL_get_peer_certificate(ssl);
    if (xs) {
        extract_dn(t, nids, "SSL_CLIENT_S_DN_", X509_get_subject_name(xs), p);
        extract_dn(t, nids, "SSL_CLIENT_I_DN_", X509_get_issuer_name(xs), p);
        X509_free(xs);
    }
}


apr_array_header_t *ssl_ext_list(apr_pool_t *p, conn_rec *c, int peer,
apr_array_header_t *ssl_ext_list(apr_pool_t *p, conn_rec *c, int peer,
                                 const char *extension)
                                 const char *extension)
+4 −0
Original line number Original line Diff line number Diff line
@@ -647,6 +647,10 @@ apr_array_header_t *ssl_ext_list(apr_pool_t *p, conn_rec *c, int peer, const cha


void         ssl_var_log_config_register(apr_pool_t *p);
void         ssl_var_log_config_register(apr_pool_t *p);


/* Extract SSL_*_DN_* variables into table 't' from SSL object 'ssl',
 * allocating from 'p': */
void modssl_var_extract_dns(apr_table_t *t, SSL *ssl, apr_pool_t *p);

#ifdef HAVE_OCSP
#ifdef HAVE_OCSP
/* Perform OCSP validation of the current cert in the given context.
/* Perform OCSP validation of the current cert in the given context.
 * Returns non-zero on success or zero on failure.  On failure, the
 * Returns non-zero on success or zero on failure.  On failure, the