X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=libraries%2Flibldap%2Ftls_o.c;h=2cf503bcf97cb0e8234baddf15adc27fbc544f9b;hb=f5da6638ecb8ea97df53856542231df4a3befca1;hp=8a83766c0348a42835224625ae1f2219a32ecf94;hpb=6ced84af79fea8058dad314a3c753e0b57caffd7;p=openldap diff --git a/libraries/libldap/tls_o.c b/libraries/libldap/tls_o.c index 8a83766c03..2cf503bcf9 100644 --- a/libraries/libldap/tls_o.c +++ b/libraries/libldap/tls_o.c @@ -107,10 +107,14 @@ static void tlso_thr_init( void ) CRYPTO_set_id_callback( tlso_thread_self ); } #endif /* LDAP_R_COMPILE */ +#else +#ifdef LDAP_R_COMPILE +static void tlso_thr_init( void ) {} +#endif #endif /* OpenSSL 1.1 */ static STACK_OF(X509_NAME) * -tlso_ca_list( char * bundle, char * dir ) +tlso_ca_list( char * bundle, char * dir, X509 *cert ) { STACK_OF(X509_NAME) *ca_list = NULL; @@ -132,6 +136,14 @@ tlso_ca_list( char * bundle, char * dir ) } } #endif + if ( cert ) { + X509_NAME *xn = X509_get_subject_name( cert ); + xn = X509_NAME_dup( xn ); + if ( !ca_list ) + ca_list = sk_X509_NAME_new_null(); + if ( xn && ca_list ) + sk_X509_NAME_push( ca_list, xn ); + } return ca_list; } @@ -262,7 +274,8 @@ tlso_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server ) return -1; } - if ( lo->ldo_tls_cacertfile == NULL && lo->ldo_tls_cacertdir == NULL ) { + if ( lo->ldo_tls_cacertfile == NULL && lo->ldo_tls_cacertdir == NULL && + lo->ldo_tls_cacert.bv_val == NULL ) { if ( !SSL_CTX_set_default_verify_paths( ctx ) ) { Debug( LDAP_DEBUG_ANY, "TLS: " "could not use default certificate paths", 0, 0, 0 ); @@ -270,7 +283,19 @@ tlso_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server ) return -1; } } else { - if ( !SSL_CTX_load_verify_locations( ctx, + X509 *cert = NULL; + if ( lo->ldo_tls_cacert.bv_val ) { + const unsigned char *pp = lo->ldo_tls_cacert.bv_val; + cert = d2i_X509( NULL, &pp, lo->ldo_tls_cacert.bv_len ); + X509_STORE *store = SSL_CTX_get_cert_store( ctx ); + if ( !X509_STORE_add_cert( store, cert )) { + Debug( LDAP_DEBUG_ANY, "TLS: " + "could not use CA certificate", 0, 0, 0 ); + tlso_report_error(); + return -1; + } + } + if (( lt->lt_cacertfile || lt->lt_cacertdir ) && !SSL_CTX_load_verify_locations( ctx, lt->lt_cacertfile, lt->lt_cacertdir ) ) { Debug( LDAP_DEBUG_ANY, "TLS: " @@ -285,7 +310,7 @@ tlso_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server ) if ( is_server ) { STACK_OF(X509_NAME) *calist; /* List of CA names to send to a client */ - calist = tlso_ca_list( lt->lt_cacertfile, lt->lt_cacertdir ); + calist = tlso_ca_list( lt->lt_cacertfile, lt->lt_cacertdir, cert ); if ( !calist ) { Debug( LDAP_DEBUG_ANY, "TLS: " "could not load client CA list (file:`%s',dir:`%s').\n", @@ -298,20 +323,47 @@ tlso_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server ) SSL_CTX_set_client_CA_list( ctx, calist ); } + if ( cert ) + X509_free( cert ); } + if ( lo->ldo_tls_cert.bv_val ) + { + const unsigned char *pp = lo->ldo_tls_cert.bv_val; + X509 *cert = d2i_X509( NULL, &pp, lo->ldo_tls_cert.bv_len ); + if ( !SSL_CTX_use_certificate( ctx, cert )) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not use certificate.\n", 0,0,0); + tlso_report_error(); + return -1; + } + X509_free( cert ); + } else if ( lo->ldo_tls_certfile && !SSL_CTX_use_certificate_file( ctx, lt->lt_certfile, SSL_FILETYPE_PEM ) ) { Debug( LDAP_DEBUG_ANY, - "TLS: could not use certificate `%s'.\n", + "TLS: could not use certificate file `%s'.\n", lo->ldo_tls_certfile,0,0); tlso_report_error(); return -1; } /* Key validity is checked automatically if cert has already been set */ + if ( lo->ldo_tls_key.bv_val ) + { + const unsigned char *pp = lo->ldo_tls_key.bv_val; + EVP_PKEY *pkey = d2i_AutoPrivateKey( NULL, &pp, lo->ldo_tls_key.bv_len ); + if ( !SSL_CTX_use_PrivateKey( ctx, pkey )) + { + Debug( LDAP_DEBUG_ANY, + "TLS: could not use private key.\n", 0,0,0); + tlso_report_error(); + return -1; + } + EVP_PKEY_free( pkey ); + } else if ( lo->ldo_tls_keyfile && !SSL_CTX_use_PrivateKey_file( ctx, lt->lt_keyfile, SSL_FILETYPE_PEM ) ) @@ -783,6 +835,77 @@ tlso_session_peercert( tls_session *sess, struct berval *der ) return 0; } +static int +tlso_session_pinning( LDAP *ld, tls_session *sess, char *hashalg, struct berval *hash ) +{ + tlso_session *s = (tlso_session *)sess; + char *tmp, digest[EVP_MAX_MD_SIZE]; + struct berval key, + keyhash = { .bv_val = digest, .bv_len = sizeof(digest) }; + X509 *cert = SSL_get_peer_certificate(s); + int len, rc = LDAP_SUCCESS; + + len = i2d_X509_PUBKEY( X509_get_X509_PUBKEY(cert), NULL ); + + key.bv_val = tmp = LDAP_MALLOC( len ); + if ( !key.bv_val ) { + return -1; + } + + key.bv_len = i2d_X509_PUBKEY( X509_get_X509_PUBKEY(cert), &tmp ); + + if ( hashalg ) { + const EVP_MD *md; + EVP_MD_CTX *mdctx; + unsigned int len = keyhash.bv_len; + + md = EVP_get_digestbyname( hashalg ); + if ( !md ) { + Debug( LDAP_DEBUG_TRACE, "tlso_session_pinning: " + "hash %s not recognised by OpenSSL\n", hashalg, 0, 0 ); + rc = -1; + goto done; + } + +#if OPENSSL_VERSION_NUMBER >= 0x10100000 + mdctx = EVP_MD_CTX_new(); +#else + mdctx = EVP_MD_CTX_create(); +#endif + if ( !mdctx ) { + rc = -1; + goto done; + } + + EVP_DigestInit_ex( mdctx, md, NULL ); + EVP_DigestUpdate( mdctx, key.bv_val, key.bv_len ); + EVP_DigestFinal_ex( mdctx, (unsigned char *)keyhash.bv_val, &len ); + keyhash.bv_len = len; +#if OPENSSL_VERSION_NUMBER >= 0x10100000 + EVP_MD_CTX_free( mdctx ); +#else + EVP_MD_CTX_destroy( mdctx ); +#endif + } else { + keyhash = key; + } + + if ( ber_bvcmp( hash, &keyhash ) ) { + rc = LDAP_CONNECT_ERROR; + Debug( LDAP_DEBUG_ANY, "tlso_session_pinning: " + "public key hash does not match provided pin.\n", 0, 0, 0 ); + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( + _("TLS: public key hash does not match provided pin")); + } + +done: + LDAP_FREE( key.bv_val ); + return rc; +} + /* * TLS support for LBER Sockbufs */ @@ -1144,7 +1267,7 @@ tlso_verify_cb( int ok, X509_STORE_CTX *ctx ) */ subject = X509_get_subject_name( cert ); issuer = X509_get_issuer_name( cert ); - /* X509_NAME_oneline, if passed a NULL buf, allocate memomry */ + /* X509_NAME_oneline, if passed a NULL buf, allocate memory */ sname = X509_NAME_oneline( subject, NULL, 0 ); iname = X509_NAME_oneline( issuer, NULL, 0 ); if ( !ok ) certerr = (char *)X509_verify_cert_error_string( errnum ); @@ -1257,11 +1380,13 @@ tlso_seed_PRNG( const char *randfile ) * The fact is that when $HOME is NULL, .rnd is used. */ randfile = RAND_file_name( buffer, sizeof( buffer ) ); - - } else if (RAND_egd(randfile) > 0) { + } +#ifndef OPENSSL_NO_EGD + else if (RAND_egd(randfile) > 0) { /* EGD socket */ return 0; } +#endif if (randfile == NULL) { Debug( LDAP_DEBUG_ANY, @@ -1314,10 +1439,11 @@ tls_impl ldap_int_tls_impl = { tlso_session_version, tlso_session_cipher, tlso_session_peercert, + tlso_session_pinning, &tlso_sbio, -#if defined(LDAP_R_COMPILE) && OPENSSL_VERSION_NUMBER < 0x10100000 +#ifdef LDAP_R_COMPILE tlso_thr_init, #else NULL,