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