mirror of
https://github.com/apache/httpd.git
synced 2025-08-07 04:02:58 +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:
4
CHANGES
4
CHANGES
@@ -1,6 +1,10 @@
|
||||
-*- coding: utf-8 -*-
|
||||
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
|
||||
default for connection or 500/503 errors, and honor status=+I for
|
||||
any error. PR 48388. [Yann Ylavic]
|
||||
|
@@ -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_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_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><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>
|
||||
@@ -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_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_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_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>
|
||||
|
@@ -1199,6 +1199,7 @@ 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);
|
||||
modssl_var_extract_san_entries(env, sslconn->ssl, r->pool);
|
||||
|
||||
for (i = 0; ssl_hook_Fixup_vars[i]; i++) {
|
||||
var = (char *)ssl_hook_Fixup_vars[i];
|
||||
|
@@ -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_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_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_remain(apr_pool_t *p, ASN1_TIME *tm);
|
||||
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);
|
||||
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")) {
|
||||
nid = OBJ_obj2nid((ASN1_OBJECT *)(xs->cert_info->signature->algorithm));
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
* parse the extension type as a primitive string. This will fail for
|
||||
* any structured extension type per the docs. Returns non-zero on
|
||||
|
@@ -922,6 +922,10 @@ void ssl_var_log_config_register(apr_pool_t *p);
|
||||
* allocating from '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
|
||||
/* Perform OCSP validation of the current cert in the given context.
|
||||
* Returns non-zero on success or zero on failure. On failure, the
|
||||
|
@@ -185,8 +185,8 @@ BOOL SSL_X509_getBC(X509 *cert, int *ca, int *pathlen)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* convert a NAME_ENTRY to UTF8 string */
|
||||
char *SSL_X509_NAME_ENTRY_to_string(apr_pool_t *p, X509_NAME_ENTRY *xsne)
|
||||
/* convert an ASN.1 string to a UTF-8 string (escaping control characters) */
|
||||
char *SSL_ASN1_STRING_to_utf8(apr_pool_t *p, ASN1_STRING *asn1str)
|
||||
{
|
||||
char *result = NULL;
|
||||
BIO *bio;
|
||||
@@ -194,13 +194,23 @@ char *SSL_X509_NAME_ENTRY_to_string(apr_pool_t *p, X509_NAME_ENTRY *xsne)
|
||||
|
||||
if ((bio = BIO_new(BIO_s_mem())) == 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);
|
||||
if (len > 0) {
|
||||
result = apr_palloc(p, len+1);
|
||||
len = BIO_read(bio, result, len);
|
||||
result[len] = NUL;
|
||||
}
|
||||
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);
|
||||
return result;
|
||||
}
|
||||
@@ -237,51 +247,84 @@ char *SSL_X509_NAME_to_string(apr_pool_t *p, X509_NAME *dn, int maxlen)
|
||||
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 */
|
||||
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;
|
||||
char **cpp;
|
||||
int i, n;
|
||||
int i = -1;
|
||||
|
||||
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;
|
||||
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) */
|
||||
subj = X509_get_subject_name(x509);
|
||||
i = -1;
|
||||
while ((i = X509_NAME_get_index_by_NID(subj, NID_commonName, i)) != -1) {
|
||||
cpp = (char **)apr_array_push(*ids);
|
||||
*cpp = SSL_X509_NAME_ENTRY_to_string(p, X509_NAME_get_entry(subj, i));
|
||||
APR_ARRAY_PUSH(*ids, const char *) =
|
||||
SSL_X509_NAME_ENTRY_to_string(p, X509_NAME_get_entry(subj, i));
|
||||
}
|
||||
|
||||
return apr_is_empty_array(*ids) ? FALSE : TRUE;
|
||||
|
@@ -63,8 +63,10 @@ void SSL_set_app_data2(SSL *, void *);
|
||||
EVP_PKEY *SSL_read_PrivateKey(const char *, EVP_PKEY **, pem_password_cb *, void *);
|
||||
int SSL_smart_shutdown(SSL *ssl);
|
||||
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_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_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 *);
|
||||
|
Reference in New Issue
Block a user