You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by kg...@apache.org on 2012/12/14 20:17:43 UTC
svn commit: r1422047 - in /qpid/proton/trunk/proton-c:
bindings/python/proton.py include/proton/ssl.h src/ssl/openssl.c
Author: kgiusti
Date: Fri Dec 14 19:17:39 2012
New Revision: 1422047
URL: http://svn.apache.org/viewvc?rev=1422047&view=rev
Log:
PROTON-161: add certificate wildcard dns name matching
Modified:
qpid/proton/trunk/proton-c/bindings/python/proton.py
qpid/proton/trunk/proton-c/include/proton/ssl.h
qpid/proton/trunk/proton-c/src/ssl/openssl.c
Modified: qpid/proton/trunk/proton-c/bindings/python/proton.py
URL: http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/bindings/python/proton.py?rev=1422047&r1=1422046&r2=1422047&view=diff
==============================================================================
--- qpid/proton/trunk/proton-c/bindings/python/proton.py (original)
+++ qpid/proton/trunk/proton-c/bindings/python/proton.py Fri Dec 14 19:17:39 2012
@@ -2364,8 +2364,8 @@ class SSL(object):
return name
return None
- def set_peer_hostname(self, hostname):
- pn_ssl_set_peer_hostname( self._ssl, hostname )
+ def set_peer_hostname(self, hostname, check=True):
+ pn_ssl_set_peer_hostname( self._ssl, hostname, check )
__all__ = ["Messenger", "Message", "ProtonException", "MessengerException",
"MessageException", "Timeout", "Condition", "Data", "Endpoint",
Modified: qpid/proton/trunk/proton-c/include/proton/ssl.h
URL: http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/include/proton/ssl.h?rev=1422047&r1=1422046&r2=1422047&view=diff
==============================================================================
--- qpid/proton/trunk/proton-c/include/proton/ssl.h (original)
+++ qpid/proton/trunk/proton-c/include/proton/ssl.h Fri Dec 14 19:17:39 2012
@@ -216,49 +216,22 @@ bool pn_ssl_get_protocol_name(pn_ssl_t *
*
* Setting this name causes the client to 1) send this name to the server during the
* handshake (if Server Name Indication is supported), and 2) check this name against the
- * CommonName provided in the server's certificate. If the supplied name does not exactly
- * match a CommonName entry in the server's certificate, the server is considered
- * unauthenticated, and the SSL connection is aborted.
+ * identifying name provided in the server's certificate. If the supplied name does not
+ * exactly match a SubjectAltName (type DNS name), or the CommonName entry in the server's
+ * certificate, the server is considered unauthenticated, and the SSL connection is
+ * aborted.
*
- * @note Verification of the CommonName is only done if PN_SSL_VERIFY_PEER is enabled.
+ * @note Verification of the hostname is only done if PN_SSL_VERIFY_PEER is enabled.
* See ::pn_ssl_set_peer_authentication.
*
- * @note the CommonName check algorithm can be modified using
- * ::pn_ssl_set_peer_hostname_match.
- *
* @param[in] ssl the ssl client
* @param[in] hostname the value to check against the peer's CommonName field. Expected
* to conform to the syntax as given in RFC1034, Section 3.5.
+ * @param[in] check_cert - if true, check hostname against the SubjectAltName (dns) or the
+ * CommonName fields in the peer's certificate. If no match is found, reject the
+ * connection.
*/
-void pn_ssl_set_peer_hostname( pn_ssl_t *ssl, const char *hostname);
-
-/** Specify how to match the server's CommonName field
- *
- * Expects the CommonName field to contain a DNS name as described in RFC1034, Sec 3.5
- * Host Name Syntax.
- */
-typedef enum {
- PN_SSL_MATCH_EXACT, /**< case insensitive text match */
- PN_SSL_MATCH_WILDCARD /**< domain label wildcard match */
-} pn_ssl_match_flag;
-
-/** Check the server's CommonName field to ensure authenticity.
- *
- * Controls how the SSL client will check the CommonName field in the server's
- * certificate. This check must be used in order to ensure the certificate belongs to the
- * expected target. If the check fails, the SSL connection is aborted.
- *
- * @note Verification of the CommonName is only done if PN_SSL_VERIFY_PEER is enabled.
- * See ::pn_ssl_set_peer_authentication.
- *
- * @param[in] ssl the ssl client
- * @param[in] pattern if not NULL the pattern to use to check against the peer's
- * CommonName field, based on the match flag. If NULL, CommonName checking is disabled.
- * @param[in] flag describes how pattern should be used to match against the CommonName
- * field
- * @return 0 if pattern/flag is valid, < 0 if error.
- */
-int pn_ssl_set_peer_hostname_match( pn_ssl_t *ssl, const char *pattern, pn_ssl_match_flag flag);
+void pn_ssl_set_peer_hostname( pn_ssl_t *ssl, const char *hostname, bool check_cert );
#ifdef __cplusplus
}
Modified: qpid/proton/trunk/proton-c/src/ssl/openssl.c
URL: http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/src/ssl/openssl.c?rev=1422047&r1=1422046&r2=1422047&view=diff
==============================================================================
--- qpid/proton/trunk/proton-c/src/ssl/openssl.c (original)
+++ qpid/proton/trunk/proton-c/src/ssl/openssl.c Fri Dec 14 19:17:39 2012
@@ -57,9 +57,8 @@ struct pn_ssl_t {
pn_ssl_verify_mode_t verify_mode;
char *trusted_CAs;
- char *peer_hostname;
- char *peer_match_pattern;
- pn_ssl_match_flag match_type;
+ const char *peer_hostname;
+ bool check_cert_host; // if true: check hostname in cert.
pn_transport_t *transport;
@@ -169,6 +168,60 @@ static int ssl_failed(pn_ssl_t *ssl)
return pn_error_format( ssl->transport->error, PN_ERR, "SSL Failure: %s", buf );
}
+/* match the DNS name pattern from the peer certificate against our configured peer
+ hostname */
+static bool match_dns_pattern( const char *hostname,
+ const char *pattern, int plen )
+{
+
+ if (memchr( pattern, '*', plen ) == NULL)
+ return (plen == strlen(hostname) &&
+ strncasecmp( pattern, hostname, plen ) == 0);
+
+ /* dns wildcarded pattern - RFC2818 */
+ char plabel[64]; /* max label length < 63 - RFC1034 */
+ char slabel[64];
+ int slen = strlen(hostname);
+
+ while (plen > 0 && slen > 0) {
+ const char *cptr;
+ int len;
+
+ cptr = memchr( pattern, '.', plen );
+ len = (cptr) ? cptr - pattern : plen;
+ if (len > sizeof(plabel) - 1) return false;
+ memcpy( plabel, pattern, len );
+ plabel[len] = 0;
+ pattern = cptr + 1;
+ plen -= len;
+
+ cptr = memchr( hostname, '.', slen );
+ len = (cptr) ? cptr - hostname : slen;
+ if (len > sizeof(slabel) - 1) return false;
+ memcpy( slabel, hostname, len );
+ slabel[len] = 0;
+ hostname = cptr + 1;
+ slen -= len;
+
+ char *star = strchr( plabel, '*' );
+ if (!star) {
+ if (strcasecmp( plabel, slabel )) return false;
+ } else {
+ *star = '\0';
+ char *prefix = plabel;
+ int prefix_len = strlen(prefix);
+ char *suffix = star + 1;
+ int suffix_len = strlen(suffix);
+ if (prefix_len && strncasecmp( prefix, slabel, prefix_len )) return false;
+ if (suffix_len && strncasecmp( suffix,
+ slabel + (strlen(slabel) - suffix_len),
+ suffix_len )) return false;
+ }
+ }
+
+ return plen == slen;
+}
+
// Certificate chain verification callback: return 1 if verified,
// 0 if remote cannot be verified (fail handshake).
//
@@ -191,9 +244,9 @@ static int verify_callback(int preverify
return 0; // fail connection
}
- if (!ssl->peer_match_pattern) return preverify_ok;
+ if (!ssl->peer_hostname || !ssl->check_cert_host) return preverify_ok;
- _log( ssl, "Checking commonName in peer cert against host pattern (%s)\n", ssl->peer_match_pattern);
+ _log( ssl, "Checking identifying name in peer cert against '%s'\n", ssl->peer_hostname);
bool matched = false;
@@ -211,15 +264,7 @@ static int verify_callback(int preverify
int len = ASN1_STRING_to_UTF8( &str, asn1 );
if (len >= 0) {
_log( ssl, "SubjectAltName (dns) from peer cert = '%.*s'\n", len, str );
- switch (ssl->match_type) {
- case PN_SSL_MATCH_EXACT:
- matched = (len == strlen(ssl->peer_match_pattern) &&
- strncasecmp( (const char *)str, ssl->peer_match_pattern, len ) == 0);
- break;
- case PN_SSL_MATCH_WILDCARD:
- // TBD
- break;
- }
+ matched = match_dns_pattern( ssl->peer_hostname, (const char *)str, len );
OPENSSL_free( str );
}
}
@@ -239,15 +284,7 @@ static int verify_callback(int preverify
int len = ASN1_STRING_to_UTF8( &str, name_asn1);
if (len >= 0) {
_log( ssl, "commonName from peer cert = '%.*s'\n", len, str );
- switch (ssl->match_type) {
- case PN_SSL_MATCH_EXACT:
- matched = (len == strlen(ssl->peer_match_pattern) &&
- strncasecmp( (const char *)str, ssl->peer_match_pattern, len ) == 0);
- break;
- case PN_SSL_MATCH_WILDCARD:
- // TBD
- break;
- }
+ matched = match_dns_pattern( ssl->peer_hostname, (const char *)str, len );
OPENSSL_free(str);
}
}
@@ -255,13 +292,13 @@ static int verify_callback(int preverify
if (!matched) {
_log( ssl, "Error: no name matching %s found in peer cert - rejecting handshake.\n",
- ssl->peer_match_pattern);
+ ssl->peer_hostname);
preverify_ok = 0;
#ifdef X509_V_ERR_APPLICATION_VERIFICATION
X509_STORE_CTX_set_error( ctx, X509_V_ERR_APPLICATION_VERIFICATION );
#endif
} else {
- _log( ssl, "commonName matched - peer is valid.\n" );
+ _log( ssl, "Name from peer cert matched - peer is valid.\n" );
}
return preverify_ok;
}
@@ -640,8 +677,7 @@ void pn_ssl_free( pn_ssl_t *ssl)
if (ssl->keyfile_pw) free(ssl->keyfile_pw);
if (ssl->trusted_CAs) free(ssl->trusted_CAs);
- if (ssl->peer_hostname) free(ssl->peer_hostname);
- if (ssl->peer_match_pattern) free(ssl->peer_match_pattern);
+ if (ssl->peer_hostname) free((void *)ssl->peer_hostname);
free(ssl);
}
@@ -1077,35 +1113,20 @@ void pn_ssl_trace(pn_ssl_t *ssl, pn_trac
ssl->trace = trace;
}
-void pn_ssl_set_peer_hostname( pn_ssl_t *ssl, const char *hostname)
+void pn_ssl_set_peer_hostname( pn_ssl_t *ssl, const char *hostname, bool check_cert)
{
if (!ssl) return;
- if (ssl->peer_hostname) free(ssl->peer_hostname);
+ if (ssl->peer_hostname) free((void *)ssl->peer_hostname);
ssl->peer_hostname = NULL;
+ ssl->check_cert_host = false;
if (hostname) {
ssl->peer_hostname = pn_strdup(hostname);
+ ssl->check_cert_host = check_cert;
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
if (ssl->ssl) {
SSL_set_tlsext_host_name(ssl->ssl, ssl->peer_hostname);
}
#endif
- if (!ssl->peer_match_pattern)
- pn_ssl_set_peer_hostname_match( ssl, hostname, PN_SSL_MATCH_EXACT );
}
}
-
-int pn_ssl_set_peer_hostname_match( pn_ssl_t *ssl, const char *pattern, pn_ssl_match_flag flag)
-{
- if (!ssl) return -1;
-
- if (flag != PN_SSL_MATCH_EXACT) return -1; // @todo support for wildcard
-
- if (ssl->peer_match_pattern) free(ssl->peer_match_pattern);
- ssl->peer_match_pattern = NULL;
- if (pattern) {
- ssl->peer_match_pattern = strdup(pattern);
- ssl->match_type = flag;
- }
- return 0;
-}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org