summaryrefslogtreecommitdiffstats
path: root/modules/ssl
diff options
context:
space:
mode:
authorKaspar Brand <kbrand@apache.org>2012-12-26 11:54:54 +0100
committerKaspar Brand <kbrand@apache.org>2012-12-26 11:54:54 +0100
commit50eb694c34a7ee1c47b0c9c5aeb44da74983f38d (patch)
tree5225e29293b009b82fc01ab7717f24dc7b5f8109 /modules/ssl
parentCheck that AsyncRequestWorkerFactor is not negative (diff)
downloadapache2-50eb694c34a7ee1c47b0c9c5aeb44da74983f38d.tar.xz
apache2-50eb694c34a7ee1c47b0c9c5aeb44da74983f38d.zip
mod_ssl: add support for subjectAltName-based host name checking in proxy mode
(PR 54030) factor out code from ssl_engine_init.c:ssl_check_public_cert() to ssl_util_ssl.c:SSL_X509_match_name() introduce new SSLProxyCheckPeerName directive, which should eventually obsolete SSLProxyCheckPeerCN ssl_engine_io.c:ssl_io_filter_handshake(): avoid code duplication when aborting with HTTP_BAD_GATEWAY git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1425874 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'modules/ssl')
-rw-r--r--modules/ssl/mod_ssl.c7
-rw-r--r--modules/ssl/ssl_engine_config.c11
-rw-r--r--modules/ssl/ssl_engine_init.c57
-rw-r--r--modules/ssl/ssl_engine_io.c43
-rw-r--r--modules/ssl/ssl_private.h2
-rw-r--r--modules/ssl/ssl_util_ssl.c76
-rw-r--r--modules/ssl/ssl_util_ssl.h1
7 files changed, 130 insertions, 67 deletions
diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c
index 2e50dc979e..a6392d1d47 100644
--- a/modules/ssl/mod_ssl.c
+++ b/modules/ssl/mod_ssl.c
@@ -209,9 +209,12 @@ static const command_rec ssl_config_cmds[] = {
"of the client certificate "
"(`/path/to/file' - PEM encoded certificates)")
SSL_CMD_SRV(ProxyCheckPeerExpire, FLAG,
- "SSL Proxy: check the peers certificate expiration date")
+ "SSL Proxy: check the peer certificate's expiration date")
SSL_CMD_SRV(ProxyCheckPeerCN, FLAG,
- "SSL Proxy: check the peers certificate CN")
+ "SSL Proxy: check the peer certificate's CN")
+ SSL_CMD_SRV(ProxyCheckPeerName, FLAG,
+ "SSL Proxy: check the peer certificate's name "
+ "(must be present in subjectAltName extension or CN")
/*
* Per-directory context configuration directives
diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c
index bda5c0e4e4..20c46daeb7 100644
--- a/modules/ssl/ssl_engine_config.c
+++ b/modules/ssl/ssl_engine_config.c
@@ -214,6 +214,7 @@ static SSLSrvConfigRec *ssl_config_server_new(apr_pool_t *p)
sc->insecure_reneg = UNSET;
sc->proxy_ssl_check_peer_expire = SSL_ENABLED_UNSET;
sc->proxy_ssl_check_peer_cn = SSL_ENABLED_UNSET;
+ sc->proxy_ssl_check_peer_name = SSL_ENABLED_UNSET;
#ifndef OPENSSL_NO_TLSEXT
sc->strict_sni_vhost_check = SSL_ENABLED_UNSET;
#endif
@@ -352,6 +353,7 @@ void *ssl_config_server_merge(apr_pool_t *p, void *basev, void *addv)
cfgMergeBool(insecure_reneg);
cfgMerge(proxy_ssl_check_peer_expire, SSL_ENABLED_UNSET);
cfgMerge(proxy_ssl_check_peer_cn, SSL_ENABLED_UNSET);
+ cfgMerge(proxy_ssl_check_peer_name, SSL_ENABLED_UNSET);
#ifndef OPENSSL_NO_TLSEXT
cfgMerge(strict_sni_vhost_check, SSL_ENABLED_UNSET);
#endif
@@ -1696,6 +1698,15 @@ const char *ssl_cmd_SSLProxyCheckPeerCN(cmd_parms *cmd, void *dcfg, int flag)
return NULL;
}
+const char *ssl_cmd_SSLProxyCheckPeerName(cmd_parms *cmd, void *dcfg, int flag)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+ sc->proxy_ssl_check_peer_name = flag ? SSL_ENABLED_TRUE : SSL_ENABLED_FALSE;
+
+ return NULL;
+}
+
const char *ssl_cmd_SSLStrictSNIVHostCheck(cmd_parms *cmd, void *dcfg, int flag)
{
#ifndef OPENSSL_NO_TLSEXT
diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c
index 817f6fd88c..be3212defa 100644
--- a/modules/ssl/ssl_engine_init.c
+++ b/modules/ssl/ssl_engine_init.c
@@ -1154,7 +1154,6 @@ static void ssl_check_public_cert(server_rec *s,
int type)
{
int is_ca, pathlen;
- apr_array_header_t *ids;
if (!cert) {
return;
@@ -1187,56 +1186,12 @@ static void ssl_check_public_cert(server_rec *s,
}
}
- /*
- * Check if the server name is covered by the certificate.
- * Consider both dNSName entries in the subjectAltName extension
- * and, as a fallback, commonName attributes in the subject DN.
- * (DNS-IDs and CN-IDs as defined in RFC 6125).
- */
- if (SSL_X509_getIDs(ptemp, cert, &ids)) {
- char *cp;
- int i;
- char **id = (char **)ids->elts;
- BOOL is_wildcard, matched = FALSE;
-
- for (i = 0; i < ids->nelts; i++) {
- if (!id[i])
- continue;
-
- /*
- * Determine if it is a wildcard ID - we're restrictive
- * in the sense that we require the wildcard character to be
- * THE left-most label (i.e., the ID must start with "*.")
- */
- is_wildcard = (*id[i] == '*' && *(id[i]+1) == '.') ? TRUE : FALSE;
-
- /*
- * If the ID includes a wildcard character, check if it matches
- * for the left-most DNS label (i.e., the wildcard character
- * is not allowed to match a dot). Otherwise, try a simple
- * string compare, case insensitively.
- */
- if ((is_wildcard == TRUE &&
- (cp = strchr(s->server_hostname, '.')) &&
- !strcasecmp(id[i]+1, cp)) ||
- !strcasecmp(id[i], s->server_hostname)) {
- matched = TRUE;
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01908)
- "%sID '%s' in %s certificate configured "
- "for %s matches server name",
- is_wildcard ? "Wildcard " : "",
- id[i], ssl_asn1_keystr(type),
- (mySrvConfig(s))->vhost_id);
- break;
- }
- }
-
- if (matched == FALSE) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(01909)
- "%s certificate configured for %s does NOT include "
- "an ID which matches the server name",
- ssl_asn1_keystr(type), (mySrvConfig(s))->vhost_id);
- }
+ if (SSL_X509_match_name(ptemp, cert, (const char *)s->server_hostname,
+ TRUE, s) == FALSE) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(01909)
+ "%s certificate configured for %s does NOT include "
+ "an ID which matches the server name",
+ ssl_asn1_keystr(type), (mySrvConfig(s))->vhost_id);
}
}
diff --git a/modules/ssl/ssl_engine_io.c b/modules/ssl/ssl_engine_io.c
index 16f56d3e38..a4e76ca31b 100644
--- a/modules/ssl/ssl_engine_io.c
+++ b/modules/ssl/ssl_engine_io.c
@@ -1057,6 +1057,7 @@ static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx)
#endif
const char *hostname_note = apr_table_get(c->notes,
"proxy-request-hostname");
+ BOOL proxy_ssl_check_peer_ok = TRUE;
sc = mySrvConfig(server);
#ifndef OPENSSL_NO_TLSEXT
@@ -1094,26 +1095,32 @@ static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx)
return MODSSL_ERROR_BAD_GATEWAY;
}
+ cert = SSL_get_peer_certificate(filter_ctx->pssl);
+
if (sc->proxy_ssl_check_peer_expire != SSL_ENABLED_FALSE) {
- cert = SSL_get_peer_certificate(filter_ctx->pssl);
if (!cert
|| (X509_cmp_current_time(
X509_get_notBefore(cert)) >= 0)
|| (X509_cmp_current_time(
X509_get_notAfter(cert)) <= 0)) {
+ proxy_ssl_check_peer_ok = FALSE;
ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02004)
"SSL Proxy: Peer certificate is expired");
- if (cert) {
- X509_free(cert);
- }
- /* ensure that the SSL structures etc are freed, etc: */
- ssl_filter_io_shutdown(filter_ctx, c, 1);
- apr_table_setn(c->notes, "SSL_connect_rv", "err");
- return HTTP_BAD_GATEWAY;
}
- X509_free(cert);
}
- if ((sc->proxy_ssl_check_peer_cn != SSL_ENABLED_FALSE) &&
+ if ((sc->proxy_ssl_check_peer_name != SSL_ENABLED_FALSE) &&
+ hostname_note) {
+ apr_table_unset(c->notes, "proxy-request-hostname");
+ if (!cert
+ || SSL_X509_match_name(c->pool, cert, hostname_note,
+ TRUE, server) == FALSE) {
+ proxy_ssl_check_peer_ok = FALSE;
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02411)
+ "SSL Proxy: Peer certificate does not match "
+ "for hostname %s", hostname_note);
+ }
+ }
+ else if ((sc->proxy_ssl_check_peer_cn != SSL_ENABLED_FALSE) &&
hostname_note) {
const char *hostname;
int match = 0;
@@ -1132,17 +1139,25 @@ static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx)
}
if (!match) {
+ proxy_ssl_check_peer_ok = FALSE;
ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02005)
"SSL Proxy: Peer certificate CN mismatch:"
" Certificate CN: %s Requested hostname: %s",
hostname, hostname_note);
- /* ensure that the SSL structures etc are freed, etc: */
- ssl_filter_io_shutdown(filter_ctx, c, 1);
- apr_table_setn(c->notes, "SSL_connect_rv", "err");
- return HTTP_BAD_GATEWAY;
}
}
+ if (cert) {
+ X509_free(cert);
+ }
+
+ if (proxy_ssl_check_peer_ok != TRUE) {
+ /* ensure that the SSL structures etc are freed, etc: */
+ ssl_filter_io_shutdown(filter_ctx, c, 1);
+ apr_table_setn(c->notes, "SSL_connect_rv", "err");
+ return HTTP_BAD_GATEWAY;
+ }
+
apr_table_setn(c->notes, "SSL_connect_rv", "ok");
return APR_SUCCESS;
}
diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h
index 16e0bcd499..3ff3014bba 100644
--- a/modules/ssl/ssl_private.h
+++ b/modules/ssl/ssl_private.h
@@ -710,6 +710,7 @@ struct SSLSrvConfigRec {
modssl_ctx_t *proxy;
ssl_enabled_t proxy_ssl_check_peer_expire;
ssl_enabled_t proxy_ssl_check_peer_cn;
+ ssl_enabled_t proxy_ssl_check_peer_name;
#ifndef OPENSSL_NO_TLSEXT
ssl_enabled_t strict_sni_vhost_check;
#endif
@@ -808,6 +809,7 @@ const char *ssl_cmd_SSLSessionTicketKeyFile(cmd_parms *cmd, void *dcfg, const ch
#endif
const char *ssl_cmd_SSLProxyCheckPeerExpire(cmd_parms *cmd, void *dcfg, int flag);
const char *ssl_cmd_SSLProxyCheckPeerCN(cmd_parms *cmd, void *dcfg, int flag);
+const char *ssl_cmd_SSLProxyCheckPeerName(cmd_parms *cmd, void *dcfg, int flag);
const char *ssl_cmd_SSLOCSPOverrideResponder(cmd_parms *cmd, void *dcfg, int flag);
const char *ssl_cmd_SSLOCSPDefaultResponder(cmd_parms *cmd, void *dcfg, const char *arg);
diff --git a/modules/ssl/ssl_util_ssl.c b/modules/ssl/ssl_util_ssl.c
index 48b561dc3b..6cdbdc1b18 100644
--- a/modules/ssl/ssl_util_ssl.c
+++ b/modules/ssl/ssl_util_ssl.c
@@ -338,6 +338,82 @@ BOOL SSL_X509_getIDs(apr_pool_t *p, X509 *x509, apr_array_header_t **ids)
return apr_is_empty_array(*ids) ? FALSE : TRUE;
}
+/*
+ * Check if a certificate matches for a particular name, by iterating over its
+ * DNS-IDs and CN-IDs (RFC 6125), optionally with basic wildcard matching.
+ * If server_rec is non-NULL, some (debug/trace) logging is enabled.
+ */
+BOOL SSL_X509_match_name(apr_pool_t *p, X509 *x509, const char *name,
+ BOOL allow_wildcard, server_rec *s)
+{
+ BOOL matched = FALSE;
+ apr_array_header_t *ids;
+
+ /*
+ * At some day in the future, this might be replaced with X509_check_host()
+ * (available in OpenSSL 1.0.2 and later), but two points should be noted:
+ * 1) wildcard matching in X509_check_host() might yield different
+ * results (by default, it supports a broader set of patterns, e.g.
+ * wildcards in non-initial positions);
+ * 2) we lose the option of logging each DNS- and CN-ID (until a match
+ * is found).
+ */
+
+ if (SSL_X509_getIDs(p, x509, &ids)) {
+ char *cp;
+ int i;
+ char **id = (char **)ids->elts;
+ BOOL is_wildcard;
+
+ for (i = 0; i < ids->nelts; i++) {
+ if (!id[i])
+ continue;
+
+ /*
+ * Determine if it is a wildcard ID - we're restrictive
+ * in the sense that we require the wildcard character to be
+ * THE left-most label (i.e., the ID must start with "*.")
+ */
+ is_wildcard = (*id[i] == '*' && *(id[i]+1) == '.') ? TRUE : FALSE;
+
+ /*
+ * If the ID includes a wildcard character (and the caller is
+ * allowing wildcards), check if it matches for the left-most
+ * DNS label - i.e., the wildcard character is not allowed
+ * to match a dot. Otherwise, try a simple string compare.
+ */
+ if ((allow_wildcard == TRUE && is_wildcard == TRUE &&
+ (cp = strchr(name, '.')) && !strcasecmp(id[i]+1, cp)) ||
+ !strcasecmp(id[i], name)) {
+ matched = TRUE;
+ }
+
+ if (s) {
+ ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
+ "[%s] SSL_X509_match_name: expecting name '%s', "
+ "%smatched by ID '%s'",
+ (mySrvConfig(s))->vhost_id, name,
+ matched == TRUE ? "" : "NOT ", id[i]);
+ }
+
+ if (matched == TRUE) {
+ break;
+ }
+ }
+
+ }
+
+ if (s) {
+ ssl_log_xerror(SSLLOG_MARK, APLOG_DEBUG, 0, p, s, x509,
+ APLOGNO(02412) "[%s] Cert %s for name '%s'",
+ (mySrvConfig(s))->vhost_id,
+ matched == TRUE ? "matches" : "does not match",
+ name);
+ }
+
+ return matched;
+}
+
/* _________________________________________________________________
**
** Low-Level CA Certificate Loading
diff --git a/modules/ssl/ssl_util_ssl.h b/modules/ssl/ssl_util_ssl.h
index 1688bb6706..4b882db289 100644
--- a/modules/ssl/ssl_util_ssl.h
+++ b/modules/ssl/ssl_util_ssl.h
@@ -68,6 +68,7 @@ BOOL SSL_X509_getBC(X509 *, int *, int *);
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_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 *);
BOOL SSL_X509_INFO_load_path(apr_pool_t *, STACK_OF(X509_INFO) *, const char *);
int SSL_CTX_use_certificate_chain(SSL_CTX *, char *, int, pem_password_cb *);