X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=libraries%2Flibldap%2Ftls_o.c;h=338e5279696f2504500f939c6be102c44a8e02e8;hb=321b0fa93e6a8841cba23fac8663802d2d17a8c7;hp=f90eed2597666c1956c62f064e36949204575380;hpb=187efdad6cce9d809697529f4cff52b4d384d988;p=openldap diff --git a/libraries/libldap/tls_o.c b/libraries/libldap/tls_o.c index f90eed2597..338e527969 100644 --- a/libraries/libldap/tls_o.c +++ b/libraries/libldap/tls_o.c @@ -1,8 +1,8 @@ -/* tls_o.c - Handle tls/ssl using SSLeay or OpenSSL */ +/* tls_o.c - Handle tls/ssl using OpenSSL */ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * - * Copyright 2008 The OpenLDAP Foundation. + * Copyright 2008-2012 The OpenLDAP Foundation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,10 +37,6 @@ #include "ldap-int.h" #include "ldap-tls.h" -#ifdef LDAP_R_COMPILE -#include -#endif - #ifdef HAVE_OPENSSL_SSL_H #include #include @@ -51,18 +47,8 @@ #include #endif -typedef struct tlso_ctx { - tls_impl *tc_impl; - SSL_CTX *tc_ctx; - int tc_refcnt; -} tlso_ctx; - -typedef struct tlso_session { - tls_impl *ts_impl; - SSL *ts_session; -} tlso_session; - -extern tls_impl ldap_int_openssl_impl; +typedef SSL_CTX tlso_ctx; +typedef SSL tlso_session; static int tlso_opt_trace = 1; @@ -87,7 +73,7 @@ static int tlso_seed_PRNG( const char *randfile ); #ifdef LDAP_R_COMPILE /* - * provide mutexes for the SSLeay library. + * provide mutexes for the OpenSSL library. */ static ldap_pvt_thread_mutex_t tlso_mutexes[CRYPTO_NUM_LOCKS]; static ldap_pvt_thread_mutex_t tlso_dh_mutex; @@ -172,7 +158,8 @@ tlso_init( void ) #endif SSL_load_error_strings(); - SSLeay_add_ssl_algorithms(); + SSL_library_init(); + OpenSSL_add_all_digests(); /* FIXME: mod_ssl does this */ X509V3_add_standard_extensions(); @@ -201,36 +188,21 @@ tlso_destroy( void ) static tls_ctx * tlso_ctx_new( struct ldapoptions *lo ) { - tlso_ctx *ctx = LDAP_MALLOC( sizeof(tlso_ctx) ); - if ( ctx ) { - ctx->tc_ctx = SSL_CTX_new( SSLv23_method() ); - if ( ctx->tc_ctx ) { - ctx->tc_impl = &ldap_int_openssl_impl; - ctx->tc_refcnt = 1; - } else { - LDAP_FREE( ctx ); - ctx = NULL; - } - } - return (tls_ctx *)ctx; + return (tls_ctx *) SSL_CTX_new( SSLv23_method() ); } static void tlso_ctx_ref( tls_ctx *ctx ) { tlso_ctx *c = (tlso_ctx *)ctx; - c->tc_refcnt++; + CRYPTO_add( &c->references, 1, CRYPTO_LOCK_SSL_CTX ); } static void tlso_ctx_free ( tls_ctx *ctx ) { tlso_ctx *c = (tlso_ctx *)ctx; - c->tc_refcnt--; - if ( c->tc_refcnt < 1 ) { - SSL_CTX_free( c->tc_ctx ); - LDAP_FREE( c ); - } + SSL_CTX_free( c ); } /* @@ -239,8 +211,7 @@ tlso_ctx_free ( tls_ctx *ctx ) static int tlso_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server ) { - tlso_ctx *tc = (tlso_ctx *)lo->ldo_tls_ctx; - SSL_CTX *ctx = tc->tc_ctx; + tlso_ctx *ctx = (tlso_ctx *)lo->ldo_tls_ctx; int i; if ( is_server ) { @@ -248,6 +219,11 @@ tlso_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server ) (const unsigned char *) "OpenLDAP", sizeof("OpenLDAP")-1 ); } + if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_SSL3 ) + SSL_CTX_set_options( ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 ); + else if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_SSL2 ) + SSL_CTX_set_options( ctx, SSL_OP_NO_SSLv2 ); + if ( lo->ldo_tls_ciphersuite && !SSL_CTX_set_cipher_list( ctx, lt->lt_ciphersuite ) ) { @@ -375,17 +351,7 @@ static tls_session * tlso_session_new( tls_ctx *ctx, int is_server ) { tlso_ctx *c = (tlso_ctx *)ctx; - tlso_session *s = LDAP_MALLOC( sizeof(tlso_session)); - if ( s ) { - s->ts_session = SSL_new( c->tc_ctx ); - if ( s->ts_session ) { - s->ts_impl = &ldap_int_openssl_impl; - } else { - LDAP_FREE( s ); - s = NULL; - } - } - return (tls_session *)s; + return (tls_session *)SSL_new( c ); } static int @@ -394,7 +360,7 @@ tlso_session_connect( LDAP *ld, tls_session *sess ) tlso_session *s = (tlso_session *)sess; /* Caller expects 0 = success, OpenSSL returns 1 = success */ - return SSL_connect( s->ts_session ) - 1; + return SSL_connect( s ) - 1; } static int @@ -403,7 +369,7 @@ tlso_session_accept( tls_session *sess ) tlso_session *s = (tlso_session *)sess; /* Caller expects 0 = success, OpenSSL returns 1 = success */ - return SSL_accept( s->ts_session ) - 1; + return SSL_accept( s ) - 1; } static int @@ -412,7 +378,7 @@ tlso_session_upflags( Sockbuf *sb, tls_session *sess, int rc ) tlso_session *s = (tlso_session *)sess; /* 1 was subtracted above, offset it back now */ - rc = SSL_get_error(s->ts_session, rc+1); + rc = SSL_get_error(s, rc+1); if (rc == SSL_ERROR_WANT_READ) { sb->sb_trans_needs_read = 1; return 1; @@ -428,11 +394,22 @@ tlso_session_upflags( Sockbuf *sb, tls_session *sess, int rc ) } static char * -tlso_session_errmsg( int rc, char *buf, size_t len ) +tlso_session_errmsg( tls_session *sess, int rc, char *buf, size_t len ) { + char err[256] = ""; + const char *certerr=NULL; + tlso_session *s = (tlso_session *)sess; + rc = ERR_peek_error(); if ( rc ) { - ERR_error_string_n( rc, buf, len ); + ERR_error_string_n( rc, err, sizeof(err) ); + if ( ( ERR_GET_LIB(rc) == ERR_LIB_SSL ) && + ( ERR_GET_REASON(rc) == SSL_R_CERTIFICATE_VERIFY_FAILED ) ) { + int certrc = SSL_get_verify_result(s); + certerr = (char *)X509_verify_cert_error_string(certrc); + } + snprintf(buf, len, "%s%s%s%s", err, certerr ? " (" :"", + certerr ? certerr : "", certerr ? ")" : "" ); return buf; } return NULL; @@ -445,14 +422,14 @@ tlso_session_my_dn( tls_session *sess, struct berval *der_dn ) X509 *x; X509_NAME *xn; - x = SSL_get_certificate( s->ts_session ); + x = SSL_get_certificate( s ); if (!x) return LDAP_INVALID_CREDENTIALS; xn = X509_get_subject_name(x); der_dn->bv_len = i2d_X509_NAME( xn, NULL ); der_dn->bv_val = xn->bytes->data; - X509_free(x); + /* Don't X509_free, the session is still using it */ return 0; } @@ -470,7 +447,7 @@ static int tlso_session_peer_dn( tls_session *sess, struct berval *der_dn ) { tlso_session *s = (tlso_session *)sess; - X509 *x = tlso_get_cert( s->ts_session ); + X509 *x = tlso_get_cert( s ); X509_NAME *xn; if ( !x ) @@ -496,7 +473,7 @@ tlso_session_chkhost( LDAP *ld, tls_session *sess, const char *name_in ) X509 *x; const char *name; char *ptr; - int ntype = IS_DNS; + int ntype = IS_DNS, nlen; #ifdef LDAP_PF_INET6 struct in6_addr addr; #else @@ -510,8 +487,9 @@ tlso_session_chkhost( LDAP *ld, tls_session *sess, const char *name_in ) } else { name = name_in; } + nlen = strlen(name); - x = tlso_get_cert(s->ts_session); + x = tlso_get_cert(s); if (!x) { Debug( LDAP_DEBUG_ANY, "TLS: unable to get peer certificate.\n", @@ -523,12 +501,8 @@ tlso_session_chkhost( LDAP *ld, tls_session *sess, const char *name_in ) } #ifdef LDAP_PF_INET6 - if (name[0] == '[' && strchr(name, ']')) { - char *n2 = ldap_strdup(name+1); - *strchr(n2, ']') = 2; - if (inet_pton(AF_INET6, n2, &addr)) - ntype = IS_IP6; - LDAP_FREE(n2); + if (inet_pton(AF_INET6, name, &addr)) { + ntype = IS_IP6; } else #endif if ((ptr = strrchr(name, '.')) && isdigit((unsigned char)ptr[1])) { @@ -543,15 +517,14 @@ tlso_session_chkhost( LDAP *ld, tls_session *sess, const char *name_in ) ex = X509_get_ext(x, i); alt = X509V3_EXT_d2i(ex); if (alt) { - int n, len1 = 0, len2 = 0; + int n, len2 = 0; char *domain = NULL; GENERAL_NAME *gn; if (ntype == IS_DNS) { - len1 = strlen(name); domain = strchr(name, '.'); if (domain) { - len2 = len1 - (domain-name); + len2 = nlen - (domain-name); } } n = sk_GENERAL_NAME_num(alt); @@ -569,7 +542,7 @@ tlso_session_chkhost( LDAP *ld, tls_session *sess, const char *name_in ) if (sl == 0) continue; /* Is this an exact match? */ - if ((len1 == sl) && !strncasecmp(name, sn, len1)) { + if ((nlen == sl) && !strncasecmp(name, sn, nlen)) { break; } @@ -609,13 +582,28 @@ tlso_session_chkhost( LDAP *ld, tls_session *sess, const char *name_in ) if (ret != LDAP_SUCCESS) { X509_NAME *xn; - char buf[2048]; - buf[0] = '\0'; + X509_NAME_ENTRY *ne; + ASN1_OBJECT *obj; + ASN1_STRING *cn = NULL; + int navas; + + /* find the last CN */ + obj = OBJ_nid2obj( NID_commonName ); + if ( !obj ) goto no_cn; /* should never happen */ xn = X509_get_subject_name(x); - if( X509_NAME_get_text_by_NID( xn, NID_commonName, - buf, sizeof(buf)) == -1) + navas = X509_NAME_entry_count( xn ); + for ( i=navas-1; i>=0; i-- ) { + ne = X509_NAME_get_entry( xn, i ); + if ( !OBJ_cmp( ne->object, obj )) { + cn = X509_NAME_ENTRY_get_data( ne ); + break; + } + } + + if( !cn ) { +no_cn: Debug( LDAP_DEBUG_ANY, "TLS: unable to get common name from peer certificate.\n", 0, 0, 0 ); @@ -626,21 +614,20 @@ tlso_session_chkhost( LDAP *ld, tls_session *sess, const char *name_in ) ld->ld_error = LDAP_STRDUP( _("TLS: unable to get CN from peer certificate")); - } else if (strcasecmp(name, buf) == 0 ) { + } else if ( cn->length == nlen && + strncasecmp( name, (char *) cn->data, nlen ) == 0 ) { ret = LDAP_SUCCESS; - } else if (( buf[0] == '*' ) && ( buf[1] == '.' )) { + } else if (( cn->data[0] == '*' ) && ( cn->data[1] == '.' )) { char *domain = strchr(name, '.'); if( domain ) { - size_t dlen = 0; - size_t sl; + int dlen; - sl = strlen(name); - dlen = sl - (domain-name); - sl = strlen(buf); + dlen = nlen - (domain-name); /* Is this a wildcard match? */ - if ((dlen == sl-1) && !strncasecmp(domain, &buf[1], dlen)) { + if ((dlen == cn->length-1) && + !strncasecmp(domain, (char *) &cn->data[1], dlen)) { ret = LDAP_SUCCESS; } } @@ -648,8 +635,8 @@ tlso_session_chkhost( LDAP *ld, tls_session *sess, const char *name_in ) if( ret == LDAP_LOCAL_ERROR ) { Debug( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match " - "common name in certificate (%s).\n", - name, buf, 0 ); + "common name in certificate (%.*s).\n", + name, cn->length, cn->data ); ret = LDAP_CONNECT_ERROR; if ( ld->ld_error ) { LDAP_FREE( ld->ld_error ); @@ -666,10 +653,8 @@ static int tlso_session_strength( tls_session *sess ) { tlso_session *s = (tlso_session *)sess; - SSL_CIPHER *c; - c = SSL_get_current_cipher(s->ts_session); - return SSL_CIPHER_get_bits(c, NULL); + return SSL_CIPHER_get_bits(SSL_get_current_cipher(s), NULL); } /* @@ -807,7 +792,7 @@ tlso_sb_setup( Sockbuf_IO_Desc *sbiod, void *arg ) p->sbiod = sbiod; bio = BIO_new( &tlso_bio_method ); bio->ptr = (void *)p; - SSL_set_bio( p->session->ts_session, bio, bio ); + SSL_set_bio( p->session, bio, bio ); sbiod->sbiod_pvt = p; return 0; } @@ -821,8 +806,7 @@ tlso_sb_remove( Sockbuf_IO_Desc *sbiod ) assert( sbiod->sbiod_pvt != NULL ); p = (struct tls_data *)sbiod->sbiod_pvt; - SSL_free( p->session->ts_session ); - LDAP_FREE( p->session ); + SSL_free( p->session ); LBER_FREE( sbiod->sbiod_pvt ); sbiod->sbiod_pvt = NULL; return 0; @@ -837,7 +821,7 @@ tlso_sb_close( Sockbuf_IO_Desc *sbiod ) assert( sbiod->sbiod_pvt != NULL ); p = (struct tls_data *)sbiod->sbiod_pvt; - SSL_shutdown( p->session->ts_session ); + SSL_shutdown( p->session ); return 0; } @@ -856,7 +840,7 @@ tlso_sb_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) return 1; } else if ( opt == LBER_SB_OPT_DATA_READY ) { - if( SSL_pending( p->session->ts_session ) > 0 ) { + if( SSL_pending( p->session ) > 0 ) { return 1; } } @@ -876,11 +860,11 @@ tlso_sb_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) p = (struct tls_data *)sbiod->sbiod_pvt; - ret = SSL_read( p->session->ts_session, (char *)buf, len ); + ret = SSL_read( p->session, (char *)buf, len ); #ifdef HAVE_WINSOCK errno = WSAGetLastError(); #endif - err = SSL_get_error( p->session->ts_session, ret ); + err = SSL_get_error( p->session, ret ); if (err == SSL_ERROR_WANT_READ ) { sbiod->sbiod_sb->sb_trans_needs_read = 1; sock_errset(EWOULDBLOCK); @@ -902,11 +886,11 @@ tlso_sb_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) p = (struct tls_data *)sbiod->sbiod_pvt; - ret = SSL_write( p->session->ts_session, (char *)buf, len ); + ret = SSL_write( p->session, (char *)buf, len ); #ifdef HAVE_WINSOCK errno = WSAGetLastError(); #endif - err = SSL_get_error( p->session->ts_session, ret ); + err = SSL_get_error( p->session, ret ); if (err == SSL_ERROR_WANT_WRITE ) { sbiod->sbiod_sb->sb_trans_needs_write = 1; sock_errset(EWOULDBLOCK); @@ -1083,16 +1067,29 @@ static RSA * tlso_tmp_rsa_cb( SSL *ssl, int is_export, int key_length ) { RSA *tmp_rsa; - /* FIXME: Pregenerate the key on startup */ /* FIXME: Who frees the key? */ +#if OPENSSL_VERSION_NUMBER >= 0x00908000 + BIGNUM *bn = BN_new(); + tmp_rsa = NULL; + if ( bn ) { + if ( BN_set_word( bn, RSA_F4 )) { + tmp_rsa = RSA_new(); + if ( tmp_rsa && !RSA_generate_key_ex( tmp_rsa, key_length, bn, NULL )) { + RSA_free( tmp_rsa ); + tmp_rsa = NULL; + } + } + BN_free( bn ); + } +#else tmp_rsa = RSA_generate_key( key_length, RSA_F4, NULL, NULL ); +#endif if ( !tmp_rsa ) { Debug( LDAP_DEBUG_ANY, "TLS: Failed to generate temporary %d-bit %s RSA key\n", key_length, is_export ? "export" : "domestic", 0 ); - return NULL; } return tmp_rsa; } @@ -1207,14 +1204,10 @@ tlso_tmp_dh_cb( SSL *ssl, int is_export, int key_length ) int i; /* Do we have params of this length already? */ -#ifdef LDAP_R_COMPILE - ldap_pvt_thread_mutex_lock( &tlso_dh_mutex ); -#endif + LDAP_MUTEX_LOCK( &tlso_dh_mutex ); for ( p = tlso_dhparams; p; p=p->next ) { if ( p->keylength == key_length ) { -#ifdef LDAP_R_COMPILE - ldap_pvt_thread_mutex_unlock( &tlso_dh_mutex ); -#endif + LDAP_MUTEX_UNLOCK( &tlso_dh_mutex ); return p->param; } } @@ -1247,13 +1240,11 @@ tlso_tmp_dh_cb( SSL *ssl, int is_export, int key_length ) } } -#ifdef LDAP_R_COMPILE - ldap_pvt_thread_mutex_unlock( &tlso_dh_mutex ); -#endif + LDAP_MUTEX_UNLOCK( &tlso_dh_mutex ); return dh; } -tls_impl ldap_int_openssl_impl = { +tls_impl ldap_int_tls_impl = { "OpenSSL", tlso_init,