1
0
mirror of https://github.com/apache/httpd.git synced 2025-08-08 15:02:10 +03:00

Add support for extracting subjectAltName entries of type

rfc822Name and dNSName into SSL_{CLIENT,SERVER}_SAN_{Email,DNS}_n
variables.

* docs/manual/mod/mod_ssl.xml: add SSL_*_SAN_*_n entries to the
  environment variables table

* modules/ssl/ssl_engine_kernel.c: in ssl_hook_Fixup, add extraction
  of subjectAltName entries for the "StdEnvVars" case

* modules/ssl/ssl_engine_vars.c: add support for retrieving the
  SSL_{CLIENT,SERVER}_SAN_{Email,DNS}_n variables, either with
  individual on-demand lookup (ssl_var_lookup_ssl_cert_san),
  or with full-list extraction to the environment ("StdEnvVars")

* modules/ssl/ssl_private.h: add modssl_var_extract_san_entries prototype

* modules/ssl/ssl_util_ssl.c: implement SSL_X509_getSAN and
  SSL_ASN1_STRING_to_utf8 helper functions, with factoring out common
  code from SSL_X509_getIDs and SSL_X509_NAME_ENTRY_to_string where
  suitable. Limit SSL_X509_getSAN to the two most common subjectAltName
  entry types appearing in user or server certificates (i.e., rfc822Name
  and dNSName), for the time being.

* modules/ssl/ssl_util_ssl.h: add SSL_ASN1_STRING_to_utf8
  and SSL_X509_getSAN prototypes


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1650047 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Kaspar Brand
2015-01-07 12:24:48 +00:00
parent 1f6fd365c3
commit 9660add80a
7 changed files with 173 additions and 41 deletions

View File

@@ -1,6 +1,10 @@
-*- coding: utf-8 -*- -*- coding: utf-8 -*-
Changes with Apache 2.5.0 Changes with Apache 2.5.0
*) mod_ssl: Add support for extracting subjectAltName entries of type
rfc822Name and dNSName into SSL_{CLIENT,SERVER}_SAN_{Email,DNS}_n
environment variables. Also addresses PR 57207. [Kaspar Brand]
*) mod_proxy: Don't put non balancer-member workers in error state by *) mod_proxy: Don't put non balancer-member workers in error state by
default for connection or 500/503 errors, and honor status=+I for default for connection or 500/503 errors, and honor status=+I for
any error. PR 48388. [Yann Ylavic] any error. PR 48388. [Yann Ylavic]

View File

@@ -75,6 +75,8 @@ compatibility variables.</p>
<tr><td><code>SSL_CLIENT_M_SERIAL</code></td> <td>string</td> <td>The serial of the client certificate</td></tr> <tr><td><code>SSL_CLIENT_M_SERIAL</code></td> <td>string</td> <td>The serial of the client certificate</td></tr>
<tr><td><code>SSL_CLIENT_S_DN</code></td> <td>string</td> <td>Subject DN in client's certificate</td></tr> <tr><td><code>SSL_CLIENT_S_DN</code></td> <td>string</td> <td>Subject DN in client's certificate</td></tr>
<tr><td><code>SSL_CLIENT_S_DN_</code><em>x509</em></td> <td>string</td> <td>Component of client's Subject DN</td></tr> <tr><td><code>SSL_CLIENT_S_DN_</code><em>x509</em></td> <td>string</td> <td>Component of client's Subject DN</td></tr>
<tr><td><code>SSL_CLIENT_SAN_Email_</code><em>n</em></td> <td>string</td> <td>Client certificate's subjectAltName extension entries of type rfc822Name</td></tr>
<tr><td><code>SSL_CLIENT_SAN_DNS_</code><em>n</em></td> <td>string</td> <td>Client certificate's subjectAltName extension entries of type dNSName</td></tr>
<tr><td><code>SSL_CLIENT_I_DN</code></td> <td>string</td> <td>Issuer DN of client's certificate</td></tr> <tr><td><code>SSL_CLIENT_I_DN</code></td> <td>string</td> <td>Issuer DN of client's certificate</td></tr>
<tr><td><code>SSL_CLIENT_I_DN_</code><em>x509</em></td> <td>string</td> <td>Component of client's Issuer DN</td></tr> <tr><td><code>SSL_CLIENT_I_DN_</code><em>x509</em></td> <td>string</td> <td>Component of client's Issuer DN</td></tr>
<tr><td><code>SSL_CLIENT_V_START</code></td> <td>string</td> <td>Validity of client's certificate (start time)</td></tr> <tr><td><code>SSL_CLIENT_V_START</code></td> <td>string</td> <td>Validity of client's certificate (start time)</td></tr>
@@ -88,6 +90,8 @@ compatibility variables.</p>
<tr><td><code>SSL_SERVER_M_VERSION</code></td> <td>string</td> <td>The version of the server certificate</td></tr> <tr><td><code>SSL_SERVER_M_VERSION</code></td> <td>string</td> <td>The version of the server certificate</td></tr>
<tr><td><code>SSL_SERVER_M_SERIAL</code></td> <td>string</td> <td>The serial of the server certificate</td></tr> <tr><td><code>SSL_SERVER_M_SERIAL</code></td> <td>string</td> <td>The serial of the server certificate</td></tr>
<tr><td><code>SSL_SERVER_S_DN</code></td> <td>string</td> <td>Subject DN in server's certificate</td></tr> <tr><td><code>SSL_SERVER_S_DN</code></td> <td>string</td> <td>Subject DN in server's certificate</td></tr>
<tr><td><code>SSL_SERVER_SAN_Email_</code><em>n</em></td> <td>string</td> <td>Server certificate's subjectAltName extension entries of type rfc822Name</td></tr>
<tr><td><code>SSL_SERVER_SAN_DNS_</code><em>n</em></td> <td>string</td> <td>Server certificate's subjectAltName extension entries of type dNSName</td></tr>
<tr><td><code>SSL_SERVER_S_DN_</code><em>x509</em></td> <td>string</td> <td>Component of server's Subject DN</td></tr> <tr><td><code>SSL_SERVER_S_DN_</code><em>x509</em></td> <td>string</td> <td>Component of server's Subject DN</td></tr>
<tr><td><code>SSL_SERVER_I_DN</code></td> <td>string</td> <td>Issuer DN of server's certificate</td></tr> <tr><td><code>SSL_SERVER_I_DN</code></td> <td>string</td> <td>Issuer DN of server's certificate</td></tr>
<tr><td><code>SSL_SERVER_I_DN_</code><em>x509</em></td> <td>string</td> <td>Component of server's Issuer DN</td></tr> <tr><td><code>SSL_SERVER_I_DN_</code><em>x509</em></td> <td>string</td> <td>Component of server's Issuer DN</td></tr>

View File

@@ -1199,6 +1199,7 @@ 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); modssl_var_extract_dns(env, sslconn->ssl, r->pool);
modssl_var_extract_san_entries(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];

View File

@@ -42,6 +42,7 @@
static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, request_rec *r, char *var); static char *ssl_var_lookup_ssl(apr_pool_t *p, conn_rec *c, request_rec *r, char *var);
static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, request_rec *r, X509 *xs, char *var); static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, request_rec *r, X509 *xs, char *var);
static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var); static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *var);
static char *ssl_var_lookup_ssl_cert_san(apr_pool_t *p, X509 *xs, char *var);
static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm); static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm);
static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_TIME *tm); static char *ssl_var_lookup_ssl_cert_remain(apr_pool_t *p, ASN1_TIME *tm);
static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs); static char *ssl_var_lookup_ssl_cert_serial(apr_pool_t *p, X509 *xs);
@@ -564,6 +565,10 @@ static char *ssl_var_lookup_ssl_cert(apr_pool_t *p, request_rec *r, X509 *xs,
result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5); result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5);
resdup = FALSE; resdup = FALSE;
} }
else if (strlen(var) > 4 && strcEQn(var, "SAN_", 4)) {
result = ssl_var_lookup_ssl_cert_san(p, xs, var+4);
resdup = FALSE;
}
else if (strcEQ(var, "A_SIG")) { else if (strcEQ(var, "A_SIG")) {
nid = OBJ_obj2nid((ASN1_OBJECT *)(xs->cert_info->signature->algorithm)); nid = OBJ_obj2nid((ASN1_OBJECT *)(xs->cert_info->signature->algorithm));
result = apr_pstrdup(p, result = apr_pstrdup(p,
@@ -652,6 +657,34 @@ static char *ssl_var_lookup_ssl_cert_dn(apr_pool_t *p, X509_NAME *xsname, char *
return result; return result;
} }
static char *ssl_var_lookup_ssl_cert_san(apr_pool_t *p, X509 *xs, char *var)
{
int type, numlen;
apr_array_header_t *entries;
if (strcEQn(var, "Email_", 6)) {
type = GEN_EMAIL;
var += 6;
}
else if (strcEQn(var, "DNS_", 4)) {
type = GEN_DNS;
var += 4;
}
else
return NULL;
/* sanity check: number must be between 1 and 4 digits */
numlen = strspn(var, "0123456789");
if ((numlen < 1) || (numlen > 4) || (numlen != strlen(var)))
return NULL;
if (SSL_X509_getSAN(p, xs, type, atoi(var), &entries))
/* return the first entry from this 1-element array */
return APR_ARRAY_IDX(entries, 0, char *);
else
return NULL;
}
static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm) static char *ssl_var_lookup_ssl_cert_valid(apr_pool_t *p, ASN1_TIME *tm)
{ {
char *result; char *result;
@@ -945,6 +978,47 @@ void modssl_var_extract_dns(apr_table_t *t, SSL *ssl, apr_pool_t *p)
} }
} }
static void extract_san_array(apr_table_t *t, const char *pfx,
apr_array_header_t *entries, apr_pool_t *p)
{
int i;
for (i = 0; i < entries->nelts; i++) {
const char *key = apr_psprintf(p, "%s_%d", pfx, i);
apr_table_setn(t, key, APR_ARRAY_IDX(entries, i, const char *));
}
}
void modssl_var_extract_san_entries(apr_table_t *t, SSL *ssl, apr_pool_t *p)
{
X509 *xs;
apr_array_header_t *entries;
/* subjectAltName entries of the server certificate */
xs = SSL_get_certificate(ssl);
if (xs) {
if (SSL_X509_getSAN(p, xs, GEN_EMAIL, -1, &entries)) {
extract_san_array(t, "SSL_SERVER_SAN_Email", entries, p);
}
if (SSL_X509_getSAN(p, xs, GEN_DNS, -1, &entries)) {
extract_san_array(t, "SSL_SERVER_SAN_DNS", entries, p);
}
/* no need to free xs (refcount does not increase) */
}
/* subjectAltName entries of the client certificate */
xs = SSL_get_peer_certificate(ssl);
if (xs) {
if (SSL_X509_getSAN(p, xs, GEN_EMAIL, -1, &entries)) {
extract_san_array(t, "SSL_CLIENT_SAN_Email", entries, p);
}
if (SSL_X509_getSAN(p, xs, GEN_DNS, -1, &entries)) {
extract_san_array(t, "SSL_CLIENT_SAN_DNS", entries, p);
}
X509_free(xs);
}
}
/* For an extension type which OpenSSL does not recognize, attempt to /* For an extension type which OpenSSL does not recognize, attempt to
* parse the extension type as a primitive string. This will fail for * parse the extension type as a primitive string. This will fail for
* any structured extension type per the docs. Returns non-zero on * any structured extension type per the docs. Returns non-zero on

View File

@@ -922,6 +922,10 @@ void ssl_var_log_config_register(apr_pool_t *p);
* allocating from 'p': */ * allocating from 'p': */
void modssl_var_extract_dns(apr_table_t *t, SSL *ssl, apr_pool_t *p); void modssl_var_extract_dns(apr_table_t *t, SSL *ssl, apr_pool_t *p);
/* Extract SSL_*_SAN_* variables (subjectAltName entries) into table 't'
* from SSL object 'ssl', allocating from 'p'. */
void modssl_var_extract_san_entries(apr_table_t *t, SSL *ssl, apr_pool_t *p);
#ifndef OPENSSL_NO_OCSP #ifndef OPENSSL_NO_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

View File

@@ -185,22 +185,32 @@ BOOL SSL_X509_getBC(X509 *cert, int *ca, int *pathlen)
return TRUE; return TRUE;
} }
/* convert a NAME_ENTRY to UTF8 string */ /* convert an ASN.1 string to a UTF-8 string (escaping control characters) */
char *SSL_X509_NAME_ENTRY_to_string(apr_pool_t *p, X509_NAME_ENTRY *xsne) char *SSL_ASN1_STRING_to_utf8(apr_pool_t *p, ASN1_STRING *asn1str)
{ {
char *result = NULL; char *result = NULL;
BIO* bio; BIO *bio;
int len; int len;
if ((bio = BIO_new(BIO_s_mem())) == NULL) if ((bio = BIO_new(BIO_s_mem())) == NULL)
return NULL; return NULL;
ASN1_STRING_print_ex(bio, X509_NAME_ENTRY_get_data(xsne),
ASN1_STRFLGS_ESC_CTRL|ASN1_STRFLGS_UTF8_CONVERT); ASN1_STRING_print_ex(bio, asn1str, ASN1_STRFLGS_ESC_CTRL|
ASN1_STRFLGS_UTF8_CONVERT);
len = BIO_pending(bio); len = BIO_pending(bio);
result = apr_palloc(p, len+1); if (len > 0) {
len = BIO_read(bio, result, len); result = apr_palloc(p, len+1);
result[len] = NUL; len = BIO_read(bio, result, len);
result[len] = NUL;
}
BIO_free(bio); BIO_free(bio);
return result;
}
/* convert a NAME_ENTRY to UTF8 string */
char *SSL_X509_NAME_ENTRY_to_string(apr_pool_t *p, X509_NAME_ENTRY *xsne)
{
char *result = SSL_ASN1_STRING_to_utf8(p, X509_NAME_ENTRY_get_data(xsne));
ap_xlate_proto_from_ascii(result, len); ap_xlate_proto_from_ascii(result, len);
return result; return result;
} }
@@ -237,51 +247,84 @@ char *SSL_X509_NAME_to_string(apr_pool_t *p, X509_NAME *dn, int maxlen)
return result; return result;
} }
/*
* Return an array of subjectAltName entries of type "type". If idx is -1,
* return all entries of the given type, otherwise return an array consisting
* of the n-th occurrence of that type only. Currently supported types:
* GEN_EMAIL (rfc822Name)
* GEN_DNS (dNSName)
*/
BOOL SSL_X509_getSAN(apr_pool_t *p, X509 *x509, int type, int idx,
apr_array_header_t **entries)
{
STACK_OF(GENERAL_NAME) *names;
if (!x509 || (type < GEN_OTHERNAME) || (type > GEN_RID) || (idx < -1) ||
!(*entries = apr_array_make(p, 0, sizeof(char *)))) {
*entries = NULL;
return FALSE;
}
if ((names = X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL))) {
int i, n = 0;
GENERAL_NAME *name;
const char *utf8str;
for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
name = sk_GENERAL_NAME_value(names, i);
if (name->type == type) {
if ((idx == -1) || (n == idx)) {
switch (type) {
case GEN_EMAIL:
case GEN_DNS:
utf8str = SSL_ASN1_STRING_to_utf8(p, name->d.ia5);
if (utf8str) {
APR_ARRAY_PUSH(*entries, const char *) = utf8str;
}
break;
default:
/*
* Not implemented right now:
* GEN_OTHERNAME (otherName)
* GEN_X400 (x400Address)
* GEN_DIRNAME (directoryName)
* GEN_EDIPARTY (ediPartyName)
* GEN_URI (uniformResourceIdentifier)
* GEN_IPADD (iPAddress)
* GEN_RID (registeredID)
*/
break;
}
}
if ((idx != -1) && (n++ > idx))
break;
}
}
sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
}
return apr_is_empty_array(*entries) ? FALSE : TRUE;
}
/* return an array of (RFC 6125 coined) DNS-IDs and CN-IDs in a certificate */ /* return an array of (RFC 6125 coined) DNS-IDs and CN-IDs in a certificate */
BOOL SSL_X509_getIDs(apr_pool_t *p, X509 *x509, apr_array_header_t **ids) BOOL SSL_X509_getIDs(apr_pool_t *p, X509 *x509, apr_array_header_t **ids)
{ {
STACK_OF(GENERAL_NAME) *names;
BIO *bio;
X509_NAME *subj; X509_NAME *subj;
char **cpp; int i = -1;
int i, n;
if (!x509 || !(*ids = apr_array_make(p, 0, sizeof(char *)))) { /* First, the DNS-IDs (dNSName entries in the subjectAltName extension) */
if (!x509 ||
(SSL_X509_getSAN(p, x509, GEN_DNS, -1, ids) == FALSE && !*ids)) {
*ids = NULL; *ids = NULL;
return FALSE; return FALSE;
} }
/* First, the DNS-IDs (dNSName entries in the subjectAltName extension) */
if ((names = X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL)) &&
(bio = BIO_new(BIO_s_mem()))) {
GENERAL_NAME *name;
for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
name = sk_GENERAL_NAME_value(names, i);
if (name->type == GEN_DNS) {
ASN1_STRING_print_ex(bio, name->d.ia5, ASN1_STRFLGS_ESC_CTRL|
ASN1_STRFLGS_UTF8_CONVERT);
n = BIO_pending(bio);
if (n > 0) {
cpp = (char **)apr_array_push(*ids);
*cpp = apr_palloc(p, n+1);
n = BIO_read(bio, *cpp, n);
(*cpp)[n] = NUL;
}
}
}
BIO_free(bio);
}
if (names)
sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
/* Second, the CN-IDs (commonName attributes in the subject DN) */ /* Second, the CN-IDs (commonName attributes in the subject DN) */
subj = X509_get_subject_name(x509); subj = X509_get_subject_name(x509);
i = -1;
while ((i = X509_NAME_get_index_by_NID(subj, NID_commonName, i)) != -1) { while ((i = X509_NAME_get_index_by_NID(subj, NID_commonName, i)) != -1) {
cpp = (char **)apr_array_push(*ids); APR_ARRAY_PUSH(*ids, const char *) =
*cpp = SSL_X509_NAME_ENTRY_to_string(p, X509_NAME_get_entry(subj, i)); SSL_X509_NAME_ENTRY_to_string(p, X509_NAME_get_entry(subj, i));
} }
return apr_is_empty_array(*ids) ? FALSE : TRUE; return apr_is_empty_array(*ids) ? FALSE : TRUE;

View File

@@ -63,8 +63,10 @@ void SSL_set_app_data2(SSL *, void *);
EVP_PKEY *SSL_read_PrivateKey(const char *, EVP_PKEY **, pem_password_cb *, void *); EVP_PKEY *SSL_read_PrivateKey(const char *, EVP_PKEY **, pem_password_cb *, void *);
int SSL_smart_shutdown(SSL *ssl); int SSL_smart_shutdown(SSL *ssl);
BOOL SSL_X509_getBC(X509 *, int *, int *); BOOL SSL_X509_getBC(X509 *, int *, int *);
char *SSL_ASN1_STRING_to_utf8(apr_pool_t *, ASN1_STRING *);
char *SSL_X509_NAME_ENTRY_to_string(apr_pool_t *p, X509_NAME_ENTRY *xsne); char *SSL_X509_NAME_ENTRY_to_string(apr_pool_t *p, X509_NAME_ENTRY *xsne);
char *SSL_X509_NAME_to_string(apr_pool_t *, X509_NAME *, int); char *SSL_X509_NAME_to_string(apr_pool_t *, X509_NAME *, int);
BOOL SSL_X509_getSAN(apr_pool_t *, X509 *, int, int, apr_array_header_t **);
BOOL SSL_X509_getIDs(apr_pool_t *, X509 *, apr_array_header_t **); BOOL SSL_X509_getIDs(apr_pool_t *, X509 *, apr_array_header_t **);
BOOL SSL_X509_match_name(apr_pool_t *, X509 *, const char *, BOOL, server_rec *); BOOL SSL_X509_match_name(apr_pool_t *, X509 *, const char *, BOOL, server_rec *);
BOOL SSL_X509_INFO_load_file(apr_pool_t *, STACK_OF(X509_INFO) *, const char *); BOOL SSL_X509_INFO_load_file(apr_pool_t *, STACK_OF(X509_INFO) *, const char *);