]> git.sur5r.net Git - openldap/blobdiff - libraries/libldap/tls.c
Fix off by one bug
[openldap] / libraries / libldap / tls.c
index f31c95239bdbfa64d7db84207916652fb5e09573..3e0d55e42b6f67f894b1904199a0d2b57ef5d092 100644 (file)
@@ -8,8 +8,6 @@
 
 #include "portable.h"
 
-#ifdef HAVE_TLS
-
 #include <stdio.h>
 
 #include <ac/stdlib.h>
 #include <ac/string.h>
 #include <ac/time.h>
 #include <ac/unistd.h>
+#include <ac/param.h>
 
 #include "ldap-int.h"
 
+#ifdef HAVE_TLS
+
 #ifdef LDAP_R_COMPILE
 #include <ldap_pvt_thread.h>
 #endif
@@ -97,7 +98,7 @@ static void tls_init_threads( void )
 #endif /* LDAP_R_COMPILE */
 
 /*
- * Initialize tls system. Should be called only once.
+ * Initialize TLS subsystem. Should be called only once.
  */
 int
 ldap_pvt_tls_init( void )
@@ -105,15 +106,17 @@ ldap_pvt_tls_init( void )
        static int tls_initialized = 0;
 
        if ( tls_initialized ) return 0;
+       tls_initialized = 1;
 
        (void) tls_seed_PRNG( tls_opt_randfile );
 
-       tls_initialized = 1;
 #ifdef LDAP_R_COMPILE
        tls_init_threads();
 #endif
+
        SSL_load_error_strings();
        SSLeay_add_ssl_algorithms();
+
        /* FIXME: mod_ssl does this */
        X509V3_add_standard_extensions();
        return 0;
@@ -199,9 +202,11 @@ ldap_pvt_tls_init_def_ctx( void )
                if ( tls_opt_trace ) {
                        SSL_CTX_set_info_callback( tls_def_ctx, tls_info_cb );
                }
-               SSL_CTX_set_verify( tls_def_ctx, (tls_opt_require_cert) ?
+               SSL_CTX_set_verify( tls_def_ctx,
+                       tls_opt_require_cert ?
                        (SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT) :
-                       SSL_VERIFY_PEER, tls_verify_cb );
+                       SSL_VERIFY_NONE,
+                       tls_verify_cb );
                SSL_CTX_set_tmp_rsa_callback( tls_def_ctx, tls_tmp_rsa_cb );
                /* SSL_CTX_set_tmp_dh_callback( tls_def_ctx, tls_tmp_dh_cb ); */
        }
@@ -356,6 +361,11 @@ sb_tls_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
        if ( opt == LBER_SB_OPT_GET_SSL ) {
                *((SSL **)arg) = p->ssl;
                return 1;
+
+       } else if ( opt == LBER_SB_OPT_DATA_READY ) {
+               if( SSL_pending( p->ssl ) > 0 ) {
+                       return 1;
+               }
        }
        
        return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg );
@@ -649,7 +659,8 @@ ldap_pvt_tls_sb_handle( Sockbuf *sb )
                ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_SSL, (void *)&p );
                return p;
        }
-               return NULL;
+
+       return NULL;
 }
 
 void *
@@ -658,16 +669,100 @@ ldap_pvt_tls_get_handle( LDAP *ld )
        return ldap_pvt_tls_sb_handle( ld->ld_sb );
 }
 
-const char *
-ldap_pvt_tls_get_peer( LDAP *ld )
+int
+ldap_pvt_tls_get_strength( void *s )
 {
-    return NULL;
+    SSL_CIPHER *c;
+
+    c = SSL_get_current_cipher((SSL *)s);
+    return SSL_CIPHER_get_bits(c, NULL);
+}
+
+
+char *
+ldap_pvt_tls_get_peer( void *s )
+{
+    X509 *x;
+    X509_NAME *xn;
+    char buf[2048], *p;
+
+    x = SSL_get_peer_certificate((SSL *)s);
+
+    if (!x)
+       return NULL;
+    
+    xn = X509_get_subject_name(x);
+    p = LDAP_STRDUP(X509_NAME_oneline(xn, buf, sizeof(buf)));
+    X509_free(x);
+    return p;
+}
+
+char *
+ldap_pvt_tls_get_peer_dn( void *s )
+{
+       X509 *x;
+       X509_NAME *xn;
+       char buf[2048], *p, *dn;
+
+       x = SSL_get_peer_certificate((SSL *)s);
+
+       if (!x) return NULL;
+    
+       xn = X509_get_subject_name(x);
+       p = X509_NAME_oneline(xn, buf, sizeof(buf));
+
+       dn = ldap_dcedn2dn( p );
+
+       X509_free(x);
+       return dn;
+}
+
+char *
+ldap_pvt_tls_get_peer_hostname( void *s )
+{
+       X509 *x;
+       X509_NAME *xn;
+       char buf[2048], *p;
+       int ret;
+
+       x = SSL_get_peer_certificate((SSL *)s);
+
+       if (!x)
+               return NULL;
+       
+       xn = X509_get_subject_name(x);
+
+       ret = X509_NAME_get_text_by_NID(xn, NID_commonName, buf, sizeof(buf));
+       if( ret == -1 ) {
+               X509_free(x);
+               return NULL;
+       }
+
+       p = LDAP_STRDUP(buf);
+       X509_free(x);
+       return p;
 }
 
 const char *
-ldap_pvt_tls_get_peer_issuer( LDAP *ld )
+ldap_pvt_tls_get_peer_issuer( void *s )
 {
+#if 0  /* currently unused; see ldap_pvt_tls_get_peer() if needed */
+    X509 *x;
+    X509_NAME *xn;
+    char buf[2048], *p;
+
+    x = SSL_get_peer_certificate((SSL *)s);
+
+    if (!x)
+       return NULL;
+    
+    xn = X509_get_issuer_name(x);
+    p = LDAP_STRDUP(X509_NAME_oneline(xn, buf, sizeof(buf)));
+    X509_free(x);
+    return p;
+#else
     return NULL;
+#endif
 }
 
 int
@@ -783,30 +878,30 @@ ldap_pvt_tls_set_option( struct ldapoptions *lo, int option, void *arg )
 
        switch( option ) {
        case LDAP_OPT_X_TLS_CACERTFILE:
-               if ( tls_opt_cacertfile ) free( tls_opt_cacertfile );
+               if ( tls_opt_cacertfile ) LDAP_FREE( tls_opt_cacertfile );
                tls_opt_cacertfile = arg ? LDAP_STRDUP( (char *) arg ) : NULL;
                break;
        case LDAP_OPT_X_TLS_CACERTDIR:
-               if ( tls_opt_cacertdir ) free( tls_opt_cacertdir );
+               if ( tls_opt_cacertdir ) LDAP_FREE( tls_opt_cacertdir );
                tls_opt_cacertdir = arg ? LDAP_STRDUP( (char *) arg ) : NULL;
                break;
        case LDAP_OPT_X_TLS_CERTFILE:
-               if ( tls_opt_certfile ) free( tls_opt_certfile );
+               if ( tls_opt_certfile ) LDAP_FREE( tls_opt_certfile );
                tls_opt_certfile = arg ? LDAP_STRDUP( (char *) arg ) : NULL;
                break;
        case LDAP_OPT_X_TLS_KEYFILE:
-               if ( tls_opt_keyfile ) free( tls_opt_keyfile );
+               if ( tls_opt_keyfile ) LDAP_FREE( tls_opt_keyfile );
                tls_opt_keyfile = arg ? LDAP_STRDUP( (char *) arg ) : NULL;
                break;
        case LDAP_OPT_X_TLS_REQUIRE_CERT:
                tls_opt_require_cert = * (int *) arg;
                break;
        case LDAP_OPT_X_TLS_CIPHER_SUITE:
-               if ( tls_opt_ciphersuite ) free( tls_opt_ciphersuite );
+               if ( tls_opt_ciphersuite ) LDAP_FREE( tls_opt_ciphersuite );
                tls_opt_ciphersuite = arg ? LDAP_STRDUP( (char *) arg ) : NULL;
                break;
        case LDAP_OPT_X_TLS_RANDOM_FILE:
-               if (tls_opt_randfile ) free (tls_opt_randfile );
+               if (tls_opt_randfile ) LDAP_FREE (tls_opt_randfile );
                tls_opt_randfile = arg ? LDAP_STRDUP( (char *) arg ) : NULL;
                break;
        default:
@@ -818,8 +913,10 @@ ldap_pvt_tls_set_option( struct ldapoptions *lo, int option, void *arg )
 int
 ldap_pvt_tls_start ( LDAP *ld, Sockbuf *sb, void *ctx_arg )
 {
-       /* Make sure tls is initialized, including PRNG properly seeded. */
-       ldap_pvt_tls_init();
+       char *peer_cert_cn, *peer_hostname;
+       void *ssl;
+
+       (void) ldap_pvt_tls_init();
 
        /*
         * Fortunately, the lib uses blocking io...
@@ -828,9 +925,55 @@ ldap_pvt_tls_start ( LDAP *ld, Sockbuf *sb, void *ctx_arg )
                return LDAP_CONNECT_ERROR;
        }
 
-       /* FIXME: hostname of server must be compared with name in
-        * certificate....
+       ssl = (void *) ldap_pvt_tls_sb_handle( sb );
+       /* 
+        * compare hostname of server with name in certificate 
+        */
+       peer_cert_cn = ldap_pvt_tls_get_peer_hostname( ssl );
+       if ( !peer_cert_cn ) {
+               /* could not get hostname from peer certificate */
+               Debug( LDAP_DEBUG_ANY,
+                       "TLS: unable to get common name from peer certificate.\n",
+                       0, 0, 0 );
+               return LDAP_LOCAL_ERROR;
+       }
+       
+       peer_hostname = ldap_host_connected_to( sb );
+       if ( !peer_hostname ) {
+               /* could not lookup hostname */
+               Debug( LDAP_DEBUG_ANY,
+                       "TLS: unable to reverse lookup peer hostname.\n",
+                       0, 0, 0 );
+               LDAP_FREE( peer_cert_cn );
+               return LDAP_LOCAL_ERROR;
+       }
+
+       if ( strcasecmp(peer_hostname, peer_cert_cn) != 0 ) {
+               Debug( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match "
+                       "common name in certificate (%s).", 
+                       peer_hostname, peer_cert_cn, 0 );
+               LDAP_FREE( peer_cert_cn );
+               LDAP_FREE( peer_hostname );
+               return LDAP_CONNECT_ERROR;
+
+       } else {
+               LDAP_FREE( peer_cert_cn );
+               LDAP_FREE( peer_hostname );
+       }
+
+       /*
+        * set SASL properties to TLS ssf and authid
         */
+       {
+               const char *authid;
+               ber_len_t ssf;
+
+               /* we need to let SASL know */
+               ssf = ldap_pvt_tls_get_strength( ssl );
+               authid = ldap_pvt_tls_get_peer( ssl );
+
+               (void) ldap_int_sasl_external( ld, authid, ssf );
+       }
 
        return LDAP_SUCCESS;
 }
@@ -950,9 +1093,8 @@ tls_seed_PRNG( const char *randfile )
 {
 #ifndef URANDOM_DEVICE
        /* no /dev/urandom (or equiv) */
-
-       char buffer[1024];
-       static int egdsocket = 0;
+       long total=0;
+       char buffer[MAXPATHLEN];
 
        if (randfile == NULL) {
                /* The seed file is $RANDFILE if defined, otherwise $HOME/.rnd.
@@ -960,29 +1102,34 @@ tls_seed_PRNG( const char *randfile )
                 * an error occurs.    - From RAND_file_name() man page.
                 * The fact is that when $HOME is NULL, .rnd is used.
                 */
-               randfile = RAND_file_name(buffer, sizeof( buffer ));
+               randfile = RAND_file_name( buffer, sizeof( buffer ) );
 
        } else if (RAND_egd(randfile) > 0) {
                /* EGD socket */
-               egdsocket = 1;
                return 0;
        }
 
        if (randfile == NULL) {
                Debug( LDAP_DEBUG_ANY,
-                       "TLS: Use configuration file or $RANDFILE to define seed file",
+                       "TLS: Use configuration file or $RANDFILE to define seed PRNG\n",
                        0, 0, 0);
                return -1;
        }
 
-       RAND_load_file(randfile, -1);
+       total = RAND_load_file(randfile, -1);
 
        if (RAND_status() == 0) {
                Debug( LDAP_DEBUG_ANY,
-                       "TLS: PRNG has not been seeded with enough data",
+                       "TLS: PRNG not been seeded with enough data\n",
                        0, 0, 0);
                return -1;
        }
+
+       /* assume if there was enough bits to seed that it's okay
+        * to write derived bits to the file
+        */
+       RAND_write_file(randfile);
+
 #endif
 
        return 0;
@@ -995,7 +1142,42 @@ tls_tmp_dh_cb( SSL *ssl, int is_export, int key_length )
        return NULL;
 }
 #endif
+#endif
+
+int
+ldap_start_tls_s ( LDAP *ld,
+       LDAPControl **serverctrls,
+       LDAPControl **clientctrls )
+{
+#ifdef HAVE_TLS
+       int rc;
+       char *rspoid = NULL;
+       struct berval *rspdata = NULL;
+
+       /* XXYYZ: this initiates operaton only on default connection! */
 
+       if ( ldap_pvt_tls_inplace( ld->ld_sb ) != 0 ) {
+               return LDAP_LOCAL_ERROR;
+       }
+
+       rc = ldap_extended_operation_s( ld, LDAP_EXOP_START_TLS,
+               NULL, serverctrls, clientctrls, &rspoid, &rspdata );
+       if ( rc != LDAP_SUCCESS ) {
+               return rc;
+       }
+
+       if ( rspoid != NULL ) {
+               LDAP_FREE(rspoid);
+       }
+
+       if ( rspdata != NULL ) {
+               ber_bvfree( rspdata );
+       }
+
+       rc = ldap_pvt_tls_start( ld, ld->ld_sb, ld->ld_options.ldo_tls_ctx );
+       return rc;
 #else
-static int dummy;
+       return LDAP_NOT_SUPPORTED;
 #endif
+}
+