]> git.sur5r.net Git - openldap/blobdiff - libraries/libldap/tls_m.c
ITS#8753 Public key pinning support in libldap
[openldap] / libraries / libldap / tls_m.c
index 9dfa2f00bd268372c32299fe224e4f9b7b9863dc..7e48b8a671c727eddd120feaa74c54bb3b8018cd 100644 (file)
@@ -2,7 +2,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 2008-2015 The OpenLDAP Foundation.
+ * Copyright 2008-2017 The OpenLDAP Foundation.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -38,6 +38,7 @@
 #include <ac/unistd.h>
 #include <ac/param.h>
 #include <ac/dirent.h>
+#include <ac/regex.h>
 
 #include "ldap-int.h"
 #include "ldap-tls.h"
@@ -118,9 +119,7 @@ static const PRIOMethods tlsm_PR_methods;
 
 #define PEM_LIBRARY    "nsspem"
 #define PEM_MODULE     "PEM"
-/* hash files for use with cacertdir have this file name suffix */
-#define PEM_CA_HASH_FILE_SUFFIX        ".0"
-#define PEM_CA_HASH_FILE_SUFFIX_LEN 2
+#define PEM_CA_HASH_FILE_REGEX "^[0-9a-f]{8}\\.[0-9]+$"
 
 static SECMODModule *pem_module;
 
@@ -625,7 +624,7 @@ nss_parse_ciphers(const char *cipherstr, int cipher_list[ciphernum])
                                                 (ciphers_def[i].strength & strength) ||
                                                 (ciphers_def[i].version & protocol)) &&
                                                (cipher_list[i] != -1)) {
-                                               /* Enable the NULL ciphers only if explicity
+                                               /* Enable the NULL ciphers only if explicitly
                                                 * requested */
                                                if (ciphers_def[i].attr & SSL_eNULL) {
                                                        if (mask & SSL_eNULL)
@@ -1345,7 +1344,7 @@ tlsm_ctx_load_private_key( tlsm_ctx *ctx )
        /* prefer unlocked key, then key from opened certdb, then any other */
        if ( unlocked_key )
                ctx->tc_private_key = unlocked_key;
-       else if ( ctx->tc_certdb_slot )
+       else if ( ctx->tc_certdb_slot && !ctx->tc_using_pem )
                ctx->tc_private_key = PK11_FindKeyByDERCert( ctx->tc_certdb_slot, ctx->tc_certificate, pin_arg );
        else
                ctx->tc_private_key = PK11_FindKeyByAnyCert( ctx->tc_certificate, pin_arg );
@@ -1473,6 +1472,7 @@ tlsm_init_ca_certs( tlsm_ctx *ctx, const char *cacertfile, const char *cacertdir
                PRDir *dir;
                PRDirEntry *entry;
                PRStatus fistatus = PR_FAILURE;
+               regex_t hashfile_re;
 
                memset( &fi, 0, sizeof(fi) );
                fistatus = PR_GetFileInfo( cacertdir, &fi );
@@ -1502,20 +1502,30 @@ tlsm_init_ca_certs( tlsm_ctx *ctx, const char *cacertfile, const char *cacertdir
                        goto done;
                }
 
+               if ( regcomp( &hashfile_re, PEM_CA_HASH_FILE_REGEX, REG_NOSUB|REG_EXTENDED ) != 0 ) {
+                       Debug( LDAP_DEBUG_ANY, "TLS: cannot compile regex for CA hash files matching\n", 0, 0, 0 );
+                       goto done;
+               }
+
                do {
                        entry = PR_ReadDir( dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN );
                        if ( ( NULL != entry ) && ( NULL != entry->name ) ) {
                                char *fullpath = NULL;
-                               char *ptr;
+                               int match;
 
-                               ptr = PL_strrstr( entry->name, PEM_CA_HASH_FILE_SUFFIX );
-                               if ( ( ptr == NULL ) || ( *(ptr + PEM_CA_HASH_FILE_SUFFIX_LEN) != '\0' ) ) {
+                               match = regexec( &hashfile_re, entry->name, 0, NULL, 0 );
+                               if ( match == REG_NOMATCH ) {
                                        Debug( LDAP_DEBUG_TRACE,
-                                                  "TLS: file %s does not end in [%s] - does not appear to be a CA certificate "
-                                                  "directory file with a properly hashed file name - skipping.\n",
-                                                  entry->name, PEM_CA_HASH_FILE_SUFFIX, 0 );
+                                                  "TLS: skipping '%s' - filename does not have expected format "
+                                                  "(certificate hash with numeric suffix)\n", entry->name, 0, 0 );
+                                       continue;
+                               } else if ( match != 0 ) {
+                                       Debug( LDAP_DEBUG_ANY,
+                                                  "TLS: cannot execute regex for CA hash file matching (%d).\n",
+                                                  match, 0, 0 );
                                        continue;
                                }
+
                                fullpath = PR_smprintf( "%s/%s", cacertdir, entry->name );
                                if ( !tlsm_add_cert_from_file( ctx, fullpath, isca ) ) {
                                        Debug( LDAP_DEBUG_TRACE,
@@ -1531,6 +1541,7 @@ tlsm_init_ca_certs( tlsm_ctx *ctx, const char *cacertfile, const char *cacertdir
                                PR_smprintf_free( fullpath );
                        }
                } while ( NULL != entry );
+               regfree ( &hashfile_re );
                PR_CloseDir( dir );
        }
 done:
@@ -1581,7 +1592,7 @@ tlsm_get_certdb_prefix( const char *certdir, char **realcertdir, char **prefix )
 }
 
 /*
- * Currently mutiple MozNSS contexts share one certificate storage. When the
+ * Currently multiple MozNSS contexts share one certificate storage. When the
  * certdb is being opened, only new certificates are added to the storage.
  * When different databases are used, conflicting nicknames make the
  * certificate lookup by the nickname impossible. In addition a token
@@ -1823,8 +1834,6 @@ tlsm_deferred_init( void *arg )
                                }
                                return -1;
                        }
-
-                       ctx->tc_using_pem = PR_TRUE;
                }
 
                /*
@@ -2289,15 +2298,9 @@ tlsm_deferred_ctx_init( void *arg )
 
        /* set up our cert and key, if any */
        if ( lt->lt_certfile ) {
-               /* if using the PEM module, load the PEM file specified by lt_certfile */
-               /* otherwise, assume this is the name of a cert already in the db */
-               if ( ctx->tc_using_pem ) {
-                       /* this sets ctx->tc_certificate to the correct value */
-                       int rc = tlsm_add_cert_from_file( ctx, lt->lt_certfile, PR_FALSE );
-                       if ( rc ) {
-                               return rc;
-                       }
-               } else {
+
+               /* first search in certdb (lt_certfile is nickname) */
+               if ( ctx->tc_certdb ) {
                        char *tmp_certname;
 
                        if ( tlsm_is_tokenname_certnick( lt->lt_certfile )) {
@@ -2317,8 +2320,31 @@ tlsm_deferred_ctx_init( void *arg )
                                Debug( LDAP_DEBUG_ANY,
                                           "TLS: error: the certificate '%s' could not be found in the database - error %d:%s.\n",
                                           lt->lt_certfile, errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+                       }
+               }
+
+               /* fallback to PEM module (lt_certfile is filename) */
+               if ( !ctx->tc_certificate ) {
+                       if ( !pem_module && tlsm_init_pem_module() ) {
+                               int pem_errcode = PORT_GetError();
+                               Debug( LDAP_DEBUG_ANY,
+                                          "TLS: fallback to PEM impossible, module cannot be loaded - error %d:%s.\n",
+                                          pem_errcode, PR_ErrorToString( pem_errcode, PR_LANGUAGE_I_DEFAULT ), 0 );
                                return -1;
                        }
+
+                       /* this sets ctx->tc_certificate to the correct value */
+                       if ( !tlsm_add_cert_from_file( ctx, lt->lt_certfile, PR_FALSE ) ) {
+                               ctx->tc_using_pem = PR_TRUE;
+                       }
+               }
+
+               if ( ctx->tc_certificate ) {
+                       Debug( LDAP_DEBUG_ANY,
+                                  "TLS: certificate '%s' successfully loaded from %s.\n", lt->lt_certfile,
+                                  ctx->tc_using_pem ? "PEM file" : "moznss database", 0);
+               } else {
+                       return -1;
                }
        }
 
@@ -3360,6 +3386,7 @@ tls_impl ldap_int_tls_impl = {
        tlsm_session_version,
        tlsm_session_cipher,
        tlsm_session_peercert,
+       NULL,
 
        &tlsm_sbio,