Diff for /libraries/libldap/tls_m.c between versions 1.8 and 1.11

version 1.8, 2009/07/03 00:06:24 version 1.11, 2009/07/31 01:26:03
Line 1 Line 1
 /* tls_m.c - Handle tls/ssl using Mozilla NSS. */  /* tls_m.c - Handle tls/ssl using Mozilla NSS. */
 /* $OpenLDAP: pkg/ldap/libraries/libldap/tls_m.c,v 1.7 2009/07/02 21:19:44 hyc Exp $ */  /* $OpenLDAP: pkg/ldap/libraries/libldap/tls_m.c,v 1.10 2009/07/30 23:51:34 hyc Exp $ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.  /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *   *
  * Copyright 2008-2009 The OpenLDAP Foundation.   * Copyright 2008-2009 The OpenLDAP Foundation.
Line 57 Line 57
 #include <private/pprio.h>  #include <private/pprio.h>
 #include <nss.h>  #include <nss.h>
 #include <ssl.h>  #include <ssl.h>
   #include <sslerr.h>
 #include <sslproto.h>  #include <sslproto.h>
 #include <pk11pub.h>  #include <pk11pub.h>
 #include <secerr.h>  #include <secerr.h>
Line 639  tlsm_bad_cert_handler(void *arg, PRFileD Line 640  tlsm_bad_cert_handler(void *arg, PRFileD
                         success = SECFailure;                          success = SECFailure;
                 }                  }
                 break;                  break;
           /* we bypass NSS's hostname checks and do our own */
           case SSL_ERROR_BAD_CERT_DOMAIN:
                   break;
         default:          default:
                 success = SECFailure;                  success = SECFailure;
                 break;                  break;
Line 685  tlsm_auth_cert_handler(void *arg, PRFile Line 689  tlsm_auth_cert_handler(void *arg, PRFile
 {  {
         SECStatus ret = SSL_AuthCertificate(arg, fd, checksig, isServer);          SECStatus ret = SSL_AuthCertificate(arg, fd, checksig, isServer);
   
           tlsm_dump_security_status( fd );
         Debug( LDAP_DEBUG_TRACE,          Debug( LDAP_DEBUG_TRACE,
                    "TLS certificate verification: %s: %s,",                     "TLS certificate verification: %s\n",
                    ret == SECSuccess ? "ok" : "bad",                     ret == SECSuccess ? "ok" : "bad", 0, 0 );
                    tlsm_dump_security_status( fd ), 0 );  
   
         if ( ret != SECSuccess ) {          if ( ret != SECSuccess ) {
                 PRErrorCode errcode = PORT_GetError();                  PRErrorCode errcode = PORT_GetError();
Line 1200  tlsm_ctx_new ( struct ldapoptions *lo ) Line 1204  tlsm_ctx_new ( struct ldapoptions *lo )
                 ctx->tc_model = NULL;                  ctx->tc_model = NULL;
                 memset(&ctx->tc_callonce, 0, sizeof(ctx->tc_callonce));                  memset(&ctx->tc_callonce, 0, sizeof(ctx->tc_callonce));
                 ctx->tc_require_cert = lo->ldo_tls_require_cert;                  ctx->tc_require_cert = lo->ldo_tls_require_cert;
         } else {                  ctx->tc_verify_cert = PR_FALSE;
                 LDAP_FREE( ctx );  
                 ctx = NULL;  
         }          }
         return (tls_ctx *)ctx;          return (tls_ctx *)ctx;
 }  }
Line 1237  tlsm_ctx_free ( tls_ctx *ctx ) Line 1239  tlsm_ctx_free ( tls_ctx *ctx )
 #endif  #endif
         if ( refcount )          if ( refcount )
                 return;                  return;
         PR_Close( c->tc_model );          if ( c->tc_model )
                   PR_Close( c->tc_model );
         c->tc_certdb = NULL; /* if not the default, may have to clean up */          c->tc_certdb = NULL; /* if not the default, may have to clean up */
         PL_strfree( c->tc_certname );          PL_strfree( c->tc_certname );
         c->tc_certname = NULL;          c->tc_certname = NULL;
Line 1366  tlsm_deferred_ctx_init( void *arg ) Line 1369  tlsm_deferred_ctx_init( void *arg )
                      ctx->tc_require_cert == LDAP_OPT_X_TLS_HARD ) {                       ctx->tc_require_cert == LDAP_OPT_X_TLS_HARD ) {
                         require_cert = SSL_REQUIRE_ALWAYS;                          require_cert = SSL_REQUIRE_ALWAYS;
                 }                  }
                 ctx->tc_verify_cert = PR_TRUE;                  if ( ctx->tc_require_cert != LDAP_OPT_X_TLS_ALLOW )
                           ctx->tc_verify_cert = PR_TRUE;
         } else {          } else {
                 ctx->tc_verify_cert = PR_FALSE;                  ctx->tc_verify_cert = PR_FALSE;
         }          }
Line 1663  tlsm_session_connect( LDAP *ld, tls_sess Line 1667  tlsm_session_connect( LDAP *ld, tls_sess
         int rc;          int rc;
         PRErrorCode err;          PRErrorCode err;
   
         /* By default, NSS checks the cert hostname for us */  
         rc = SSL_ResetHandshake( s, PR_FALSE /* server */ );          rc = SSL_ResetHandshake( s, PR_FALSE /* server */ );
         if (rc) {          if (rc) {
                 err = PR_GetError();                  err = PR_GetError();
Line 1673  tlsm_session_connect( LDAP *ld, tls_sess Line 1676  tlsm_session_connect( LDAP *ld, tls_sess
                            err ? PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ) : "unknown" );                             err ? PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ) : "unknown" );
         }          }
   
         rc = SSL_SetURL( s, ld->ld_options.ldo_defludp->lud_host );  
         if (rc) {  
                 err = PR_GetError();  
                 Debug( LDAP_DEBUG_TRACE,   
                            "TLS: error: connect - seturl failure %d - error %d:%s\n",  
                            rc, err,  
                            err ? PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ) : "unknown" );  
         }  
   
         rc = SSL_ForceHandshake( s );          rc = SSL_ForceHandshake( s );
         if (rc) {          if (rc) {
                 err = PR_GetError();                  err = PR_GetError();
Line 1754  tlsm_session_peer_dn( tls_session *sessi Line 1748  tlsm_session_peer_dn( tls_session *sessi
         return 0;          return 0;
 }  }
   
   /* what kind of hostname were we given? */
   #define IS_DNS  0
   #define IS_IP4  1
   #define IS_IP6  2
   
 static int  static int
 tlsm_session_chkhost( LDAP *ld, tls_session *session, const char *name_in )  tlsm_session_chkhost( LDAP *ld, tls_session *session, const char *name_in )
 {  {
 /* NSS already does a hostname check */          tlsm_session *s = (tlsm_session *)session;
         return LDAP_SUCCESS;          CERTCertificate *cert;
           const char *name, *domain = NULL, *ptr;
           int i, ret, ntype = IS_DNS, nlen, dlen;
   #ifdef LDAP_PF_INET6
           struct in6_addr addr;
   #else
           struct in_addr addr;
   #endif
           SECItem altname;
           SECStatus rv;
   
           if( ldap_int_hostname &&
                   ( !name_in || !strcasecmp( name_in, "localhost" ) ) )
           {
                   name = ldap_int_hostname;
           } else {
                   name = name_in;
           }
           nlen = strlen( name );
   
           cert = SSL_PeerCertificate( s );
           if (!cert) {
                   Debug( LDAP_DEBUG_ANY,
                           "TLS: unable to get peer certificate.\n",
                           0, 0, 0 );
                   /* if this was a fatal condition, things would have
                    * aborted long before now.
                    */
                   return LDAP_SUCCESS;
           }
   
   #ifdef LDAP_PF_INET6
           if (name[0] == '[' && strchr(name, ']')) {
                   char *n2 = ldap_strdup(name+1);
                   *strchr(n2, ']') = 0;
                   if (inet_pton(AF_INET6, n2, &addr))
                           ntype = IS_IP6;
                   LDAP_FREE(n2);
           } else 
   #endif
           if ((ptr = strrchr(name, '.')) && isdigit((unsigned char)ptr[1])) {
                   if (inet_aton(name, (struct in_addr *)&addr)) ntype = IS_IP4;
           }
           if (ntype == IS_DNS ) {
                   domain = strchr( name, '.' );
                   if ( domain )
                           dlen = nlen - ( domain - name );
           }
   
           ret = LDAP_LOCAL_ERROR;
   
           rv = CERT_FindCertExtension( cert, SEC_OID_X509_SUBJECT_ALT_NAME,
                   &altname );
           if ( rv == SECSuccess && altname.data ) {
                   PRArenaPool *arena;
                   CERTGeneralName *names, *cur;
   
                   arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
                   if ( !arena ) {
                           ret = LDAP_NO_MEMORY;
                           goto fail;
                   }
   
                   names = cur = CERT_DecodeAltNameExtension(arena, &altname);
                   if ( !cur )
                           goto altfail;
   
                   do {
                           char *host;
                           int hlen;
   
                           /* ignore empty */
                           if ( !cur->name.other.len ) continue;
   
                           host = cur->name.other.data;
                           hlen = cur->name.other.len;
   
                           if ( cur->type == certDNSName ) {
                                   if ( ntype != IS_DNS )  continue;
   
                                   /* is this an exact match? */
                                   if ( nlen == hlen && !strncasecmp( name, host, nlen )) {
                                           ret = LDAP_SUCCESS;
                                           break;
                                   }
   
                                   /* is this a wildcard match? */
                                   if ( domain && host[0] == '*' && host[1] == '.' &&
                                           dlen == hlen-1 && !strncasecmp( domain, host+1, dlen )) {
                                           ret = LDAP_SUCCESS;
                                           break;
                                   }
                           } else if ( cur->type == certIPAddress ) {
                                   if ( ntype == IS_DNS )  continue;
                                   
   #ifdef LDAP_PF_INET6
                                   if (ntype == IS_IP6 && hlen != sizeof(struct in6_addr)) {
                                           continue;
                                   } else
   #endif
                                   if (ntype == IS_IP4 && hlen != sizeof(struct in_addr)) {
                                           continue;
                                   }
                                   if (!memcmp(host, &addr, hlen)) {
                                           ret = LDAP_SUCCESS;
                                           break;
                                   }
                           }
                   } while (( cur = CERT_GetNextGeneralName( cur )) != names );
   altfail:
                   PORT_FreeArena( arena, PR_FALSE );
                   SECITEM_FreeItem( &altname, PR_FALSE );
           }
           /* no altnames matched, try the CN */
           if ( ret != LDAP_SUCCESS ) {
                   /* find the last CN */
                   CERTRDN *rdn, **rdns;
                   CERTAVA *lastava = NULL;
                   char buf[2048];
   
                   buf[0] = '\0';
                   rdns = cert->subject.rdns;
                   while ( rdns && ( rdn = *rdns++ )) {
                           CERTAVA *ava, **avas = rdn->avas;
                           while ( avas && ( ava = *avas++ )) {
                                   if ( CERT_GetAVATag( ava ) == SEC_OID_AVA_COMMON_NAME )
                                           lastava = ava;
                           }
                   }
                   if ( lastava ) {
                           SECItem *av = CERT_DecodeAVAValue( &lastava->value );
                           if ( av ) {
                                   if ( av->len == nlen && !strncasecmp( name, av->data, nlen )) {
                                           ret = LDAP_SUCCESS;
                                   } else if ( av->data[0] == '*' && av->data[1] == '.' &&
                                           domain && dlen == av->len - 1 && !strncasecmp( name,
                                                   av->data+1, dlen )) {
                                           ret = LDAP_SUCCESS;
                                   } else {
                                           int len = av->len;
                                           if ( len >= sizeof(buf) )
                                                   len = sizeof(buf)-1;
                                           memcpy( buf, av->data, len );
                                           buf[len] = '\0';
                                   }
                                   SECITEM_FreeItem( av, PR_TRUE );
                           }
                   }
                   if ( ret != LDAP_SUCCESS ) {
                           Debug( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match "
                                   "common name in certificate (%s).\n", 
                                   name, buf, 0 );
                           ret = LDAP_CONNECT_ERROR;
                           if ( ld->ld_error ) {
                                   LDAP_FREE( ld->ld_error );
                           }
                           ld->ld_error = LDAP_STRDUP(
                                   _("TLS: hostname does not match CN in peer certificate"));
                   }
           }
   
   fail:
           CERT_DestroyCertificate( cert );
           return ret;
 }  }
   
 static int  static int

Removed from v.1.8  
changed lines
  Added in v.1.11


______________
© Copyright 1998-2020, OpenLDAP Foundation, info@OpenLDAP.org