]> git.sur5r.net Git - openldap/blobdiff - libraries/libldap/tls2.c
ITS#8753 Public key pinning support in libldap
[openldap] / libraries / libldap / tls2.c
index e98d58b1aabf5dda531c1eefdc7bab57625a7361..1feba5befd56ceef025d91c15d2fc0c6313f1ec0 100644 (file)
@@ -2,7 +2,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 1998-2013 The OpenLDAP Foundation.
+ * Copyright 1998-2017 The OpenLDAP Foundation.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -43,7 +43,9 @@ static tls_impl *tls_imp = &ldap_int_tls_impl;
 
 #endif /* HAVE_TLS */
 
+#ifndef HAVE_MOZNSS
 #define LDAP_USE_NON_BLOCKING_TLS
+#endif
 
 /* RFC2459 minimum required set of supported attribute types
  * in a certificate DN
@@ -136,6 +138,14 @@ ldap_int_tls_destroy( struct ldapoptions *lo )
                LDAP_FREE( lo->ldo_tls_crlfile );
                lo->ldo_tls_crlfile = NULL;
        }
+       /* tls_pin_hashalg and tls_pin share the same buffer */
+       if ( lo->ldo_tls_pin_hashalg ) {
+               LDAP_FREE( lo->ldo_tls_pin_hashalg );
+               lo->ldo_tls_pin_hashalg = NULL;
+       } else {
+               LDAP_FREE( lo->ldo_tls_pin.bv_val );
+       }
+       BER_BVZERO( &lo->ldo_tls_pin );
 }
 
 /*
@@ -199,7 +209,9 @@ ldap_int_tls_init_ctx( struct ldapoptions *lo, int is_server )
        tls_init( ti );
 
        if ( is_server && !lts.lt_certfile && !lts.lt_keyfile &&
-               !lts.lt_cacertfile && !lts.lt_cacertdir ) {
+               !lts.lt_cacertfile && !lts.lt_cacertdir &&
+               !lts.lt_cacert.bv_val && !lts.lt_cert.bv_val &&
+               !lts.lt_key.bv_val ) {
                /* minimum configuration not provided */
                return LDAP_NOT_SUPPORTED;
        }
@@ -327,7 +339,7 @@ update_flags( Sockbuf *sb, tls_session * ssl, int rc )
  */
 
 static int
-ldap_int_tls_connect( LDAP *ld, LDAPConn *conn )
+ldap_int_tls_connect( LDAP *ld, LDAPConn *conn, const char *host )
 {
        Sockbuf *sb = conn->lconn_sb;
        int     err;
@@ -372,6 +384,10 @@ ldap_int_tls_connect( LDAP *ld, LDAPConn *conn )
        errno = WSAGetLastError();
 #endif
 
+       if ( err == 0 ) {
+               err = ldap_pvt_tls_check_hostname( ld, ssl, host );
+       }
+
        if ( err < 0 )
        {
                char buf[256], *msg;
@@ -502,7 +518,27 @@ ldap_pvt_tls_check_hostname( LDAP *ld, void *s, const char *name_in )
 {
        tls_session *session = s;
 
-       return tls_imp->ti_session_chkhost( ld, session, name_in );
+       if (ld->ld_options.ldo_tls_require_cert != LDAP_OPT_X_TLS_NEVER &&
+           ld->ld_options.ldo_tls_require_cert != LDAP_OPT_X_TLS_ALLOW) {
+               ld->ld_errno = tls_imp->ti_session_chkhost( ld, session, name_in );
+               if (ld->ld_errno != LDAP_SUCCESS) {
+                       return ld->ld_errno;
+               }
+       }
+
+       /*
+        * If instructed to do pinning, do it now
+        */
+       if ( !BER_BVISNULL( &ld->ld_options.ldo_tls_pin ) ) {
+               ld->ld_errno = tls_imp->ti_session_pinning( ld, s,
+                               ld->ld_options.ldo_tls_pin_hashalg,
+                               &ld->ld_options.ldo_tls_pin );
+               if (ld->ld_errno != LDAP_SUCCESS) {
+                       return ld->ld_errno;
+               }
+       }
+
+       return LDAP_SUCCESS;
 }
 
 int
@@ -518,6 +554,7 @@ ldap_pvt_tls_config( LDAP *ld, int option, const char *arg )
        case LDAP_OPT_X_TLS_RANDOM_FILE:
        case LDAP_OPT_X_TLS_CIPHER_SUITE:
        case LDAP_OPT_X_TLS_DHFILE:
+       case LDAP_OPT_X_TLS_PEERKEY_HASH:
        case LDAP_OPT_X_TLS_CRLFILE:    /* GnuTLS only */
                return ldap_pvt_tls_set_option( ld, option, (void *) arg );
 
@@ -732,6 +769,33 @@ ldap_pvt_tls_get_option( LDAP *ld, int option, void *arg )
                }
                break;
        }
+       case LDAP_OPT_X_TLS_CACERT: {
+               struct berval *bv = arg;
+               if ( lo->ldo_tls_cacert.bv_val ) {
+                       ber_dupbv( bv, &lo->ldo_tls_cacert );
+               } else {
+                       BER_BVZERO( bv );
+               }
+               break;
+       }
+       case LDAP_OPT_X_TLS_CERT: {
+               struct berval *bv = arg;
+               if ( lo->ldo_tls_cert.bv_val ) {
+                       ber_dupbv( bv, &lo->ldo_tls_cert );
+               } else {
+                       BER_BVZERO( bv );
+               }
+               break;
+       }
+       case LDAP_OPT_X_TLS_KEY: {
+               struct berval *bv = arg;
+               if ( lo->ldo_tls_key.bv_val ) {
+                       ber_dupbv( bv, &lo->ldo_tls_key );
+               } else {
+                       BER_BVZERO( bv );
+               }
+               break;
+       }
 
        default:
                return -1;
@@ -864,6 +928,107 @@ ldap_pvt_tls_set_option( LDAP *ld, int option, void *arg )
                        ldap_pvt_tls_ctx_free( lo->ldo_tls_ctx );
                lo->ldo_tls_ctx = NULL;
                return ldap_int_tls_init_ctx( lo, *(int *)arg );
+       case LDAP_OPT_X_TLS_CACERT:
+               if ( lo->ldo_tls_cacert.bv_val )
+                       LDAP_FREE( lo->ldo_tls_cacert.bv_val );
+               if ( arg ) {
+                       lo->ldo_tls_cacert.bv_len = ((struct berval *)arg)->bv_len;
+                       lo->ldo_tls_cacert.bv_val = LDAP_MALLOC( lo->ldo_tls_cacert.bv_len );
+                       if ( !lo->ldo_tls_cacert.bv_val )
+                               return -1;
+                       AC_MEMCPY( lo->ldo_tls_cacert.bv_val, ((struct berval *)arg)->bv_val, lo->ldo_tls_cacert.bv_len );
+               } else {
+                       BER_BVZERO( &lo->ldo_tls_cacert );
+               }
+               break;
+       case LDAP_OPT_X_TLS_CERT:
+               if ( lo->ldo_tls_cert.bv_val )
+                       LDAP_FREE( lo->ldo_tls_cert.bv_val );
+               if ( arg ) {
+                       lo->ldo_tls_cert.bv_len = ((struct berval *)arg)->bv_len;
+                       lo->ldo_tls_cert.bv_val = LDAP_MALLOC( lo->ldo_tls_cert.bv_len );
+                       if ( !lo->ldo_tls_cert.bv_val )
+                               return -1;
+                       AC_MEMCPY( lo->ldo_tls_cert.bv_val, ((struct berval *)arg)->bv_val, lo->ldo_tls_cert.bv_len );
+               } else {
+                       BER_BVZERO( &lo->ldo_tls_cert );
+               }
+               break;
+       case LDAP_OPT_X_TLS_KEY:
+               if ( lo->ldo_tls_key.bv_val )
+                       LDAP_FREE( lo->ldo_tls_key.bv_val );
+               if ( arg ) {
+                       lo->ldo_tls_key.bv_len = ((struct berval *)arg)->bv_len;
+                       lo->ldo_tls_key.bv_val = LDAP_MALLOC( lo->ldo_tls_key.bv_len );
+                       if ( !lo->ldo_tls_key.bv_val )
+                               return -1;
+                       AC_MEMCPY( lo->ldo_tls_key.bv_val, ((struct berval *)arg)->bv_val, lo->ldo_tls_key.bv_len );
+               } else {
+                       BER_BVZERO( &lo->ldo_tls_key );
+               }
+               break;
+       case LDAP_OPT_X_TLS_PEERKEY_HASH: {
+               /* arg = "[hashalg:]pubkey_hash" */
+               struct berval bv;
+               char *p, *pin = arg;
+               int rc = LDAP_SUCCESS;
+
+               if ( !tls_imp->ti_session_pinning ) return -1;
+
+               if ( !pin ) {
+                       if ( lo->ldo_tls_pin_hashalg ) {
+                               LDAP_FREE( lo->ldo_tls_pin_hashalg );
+                       } else if ( lo->ldo_tls_pin.bv_val ) {
+                               LDAP_FREE( lo->ldo_tls_pin.bv_val );
+                       }
+                       lo->ldo_tls_pin_hashalg = NULL;
+                       BER_BVZERO( &lo->ldo_tls_pin );
+                       return rc;
+               }
+
+               pin = LDAP_STRDUP( pin );
+               p = strchr( pin, ':' );
+
+               /* pubkey (its hash) goes in bv, alg in p */
+               if ( p ) {
+                       *p = '\0';
+                       bv.bv_val = p+1;
+                       p = pin;
+               } else {
+                       bv.bv_val = pin;
+               }
+
+               bv.bv_len = strlen(bv.bv_val);
+               if ( ldap_int_decode_b64_inplace( &bv ) ) {
+                       LDAP_FREE( pin );
+                       return -1;
+               }
+
+               if ( ld != NULL ) {
+                       LDAPConn *conn = ld->ld_defconn;
+                       if ( conn != NULL ) {
+                               Sockbuf *sb = conn->lconn_sb;
+                               void *sess = ldap_pvt_tls_sb_ctx( sb );
+                               if ( sess != NULL ) {
+                                       rc = tls_imp->ti_session_pinning( ld, sess, p, &bv );
+                               }
+                       }
+               }
+
+               if ( rc == LDAP_SUCCESS ) {
+                       if ( lo->ldo_tls_pin_hashalg ) {
+                               LDAP_FREE( lo->ldo_tls_pin_hashalg );
+                       } else if ( lo->ldo_tls_pin.bv_val ) {
+                               LDAP_FREE( lo->ldo_tls_pin.bv_val );
+                       }
+                       lo->ldo_tls_pin_hashalg = p;
+                       lo->ldo_tls_pin = bv;
+               } else {
+                       LDAP_FREE( pin );
+               }
+
+               return rc;
+       }
        default:
                return -1;
        }
@@ -904,7 +1069,7 @@ ldap_int_tls_start ( LDAP *ld, LDAPConn *conn, LDAPURLDesc *srv )
         * Use non-blocking io during SSL Handshake when a timeout is configured
         */
        if ( ld->ld_options.ldo_tm_net.tv_sec >= 0 ) {
-               ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_SET_NONBLOCK, sb );
+               ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_NONBLOCK, (void*)1 );
                ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
                tv = ld->ld_options.ldo_tm_net;
                tv0 = tv;
@@ -919,7 +1084,7 @@ ldap_int_tls_start ( LDAP *ld, LDAPConn *conn, LDAPURLDesc *srv )
 #endif /* LDAP_USE_NON_BLOCKING_TLS */
 
        ld->ld_errno = LDAP_SUCCESS;
-       ret = ldap_int_tls_connect( ld, conn );
+       ret = ldap_int_tls_connect( ld, conn, host );
 
 #ifdef LDAP_USE_NON_BLOCKING_TLS
        while ( ret > 0 ) { /* this should only happen for non-blocking io */
@@ -939,8 +1104,8 @@ ldap_int_tls_start ( LDAP *ld, LDAPConn *conn, LDAPURLDesc *srv )
                        break;
                } else {
                        /* ldap_int_poll called ldap_pvt_ndelay_off */
-                       ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_SET_NONBLOCK, sb );
-                       ret = ldap_int_tls_connect( ld, conn );
+                       ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_NONBLOCK, (void*)1 );
+                       ret = ldap_int_tls_connect( ld, conn, host );
                        if ( ret > 0 ) { /* need to call tls_connect once more */
                                struct timeval curr_time_tv, delta_tv;
 
@@ -987,7 +1152,7 @@ ldap_int_tls_start ( LDAP *ld, LDAPConn *conn, LDAPURLDesc *srv )
                }
        }
        if ( ld->ld_options.ldo_tm_net.tv_sec >= 0 ) {
-               ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_SET_NONBLOCK, NULL );
+               ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_NONBLOCK, NULL );
        }
 #endif /* LDAP_USE_NON_BLOCKING_TLS */
 
@@ -997,20 +1162,6 @@ ldap_int_tls_start ( LDAP *ld, LDAPConn *conn, LDAPURLDesc *srv )
                return (ld->ld_errno);
        }
 
-       ssl = ldap_pvt_tls_sb_ctx( sb );
-       assert( ssl != NULL );
-
-       /* 
-        * compare host with name(s) in certificate
-        */
-       if (ld->ld_options.ldo_tls_require_cert != LDAP_OPT_X_TLS_NEVER &&
-           ld->ld_options.ldo_tls_require_cert != LDAP_OPT_X_TLS_ALLOW) {
-               ld->ld_errno = ldap_pvt_tls_check_hostname( ld, ssl, host );
-               if (ld->ld_errno != LDAP_SUCCESS) {
-                       return ld->ld_errno;
-               }
-       }
-
        return LDAP_SUCCESS;
 }