From: Joe Orton Date: Tue, 9 Dec 2008 14:41:21 +0000 (+0000) Subject: Improve mod_ssl's environment variable extraction to correctly handle X-Git-Tag: 2.3.1~157 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=778a79de6533d9bf45088cd63f7d16d2174f9e9b;p=thirdparty%2Fapache%2Fhttpd.git 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 --- diff --git a/CHANGES b/CHANGES index 31c1a92f7f5..05e3fbf1b6a 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,10 @@ Changes with Apache 2.3.1 [ 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 against the external pcre. Add missing binaries fcgistarter, and mod_socache* and mod_session*. [Graham Leggett] diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c index e938d0599fe..88b664ffffd 100644 --- a/modules/ssl/ssl_engine_kernel.c +++ b/modules/ssl/ssl_engine_kernel.c @@ -1012,68 +1012,12 @@ static const char *ssl_hook_Fixup_vars[] = { "SSL_CLIENT_V_START", "SSL_CLIENT_V_END", "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_SIG", "SSL_SERVER_M_VERSION", "SSL_SERVER_M_SERIAL", "SSL_SERVER_V_START", "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_SIG", "SSL_SESSION_ID", @@ -1125,6 +1069,8 @@ int ssl_hook_Fixup(request_rec *r) /* standard SSL environment variables */ if (dc->nOptions & SSL_OPT_STDENVVARS) { + modssl_var_extract_dns(env, sslconn->ssl, r->pool); + for (i = 0; ssl_hook_Fixup_vars[i]; i++) { var = (char *)ssl_hook_Fixup_vars[i]; val = ssl_var_lookup(r->pool, r->server, r->connection, r, var); diff --git a/modules/ssl/ssl_engine_vars.c b/modules/ssl/ssl_engine_vars.c index 3d688cd7389..27fac9fdd59 100644 --- a/modules/ssl/ssl_engine_vars.c +++ b/modules/ssl/ssl_engine_vars.c @@ -402,28 +402,32 @@ static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, X509 *xs, char *var) 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 { char *name; int nid; + int extract; } ssl_var_lookup_ssl_cert_dn_rec[] = { - { "C", NID_countryName }, - { "ST", NID_stateOrProvinceName }, /* officially (RFC2156) */ - { "SP", NID_stateOrProvinceName }, /* compatibility (SSLeay) */ - { "L", NID_localityName }, - { "O", NID_organizationName }, - { "OU", NID_organizationalUnitName }, - { "CN", NID_commonName }, - { "T", NID_title }, - { "I", NID_initials }, - { "G", NID_givenName }, - { "S", NID_surname }, - { "D", NID_description }, + { "C", NID_countryName, 1 }, + { "ST", NID_stateOrProvinceName, 1 }, /* officially (RFC2156) */ + { "SP", NID_stateOrProvinceName, 0 }, /* compatibility (SSLeay) */ + { "L", NID_localityName, 1 }, + { "O", NID_organizationName, 1 }, + { "OU", NID_organizationalUnitName, 1 }, + { "CN", NID_commonName, 1 }, + { "T", NID_title, 1 }, + { "I", NID_initials, 1 }, + { "G", NID_givenName, 1 }, + { "S", NID_surname, 1 }, + { "D", NID_description, 1 }, #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 */ - { "UID", NID_uniqueIdentifier }, + { "UID", NID_uniqueIdentifier, 1 }, #endif - { "Email", NID_pkcs9_emailAddress }, + { "Email", NID_pkcs9_emailAddress, 1 }, { NULL, 0 } }; @@ -673,6 +677,95 @@ static char *ssl_var_lookup_ssl_version(apr_pool_t *p, char *var) 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, const char *extension) diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h index 1bbbcb2ab94..84daac6717c 100644 --- a/modules/ssl/ssl_private.h +++ b/modules/ssl/ssl_private.h @@ -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); +/* 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 /* Perform OCSP validation of the current cert in the given context. * Returns non-zero on success or zero on failure. On failure, the