]> git.sur5r.net Git - openldap/blobdiff - libraries/libldap/tls_m.c
ITS#8625 Separate Avlnode and TAvlnode types
[openldap] / libraries / libldap / tls_m.c
index 4b5727bd03361bbc77af46d3dbb9c4bb38aca42c..a8691c59776cbe51658ab23bff17eb579549d80c 100644 (file)
@@ -2,7 +2,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 2008-2012 The OpenLDAP Foundation.
+ * Copyright 2008-2017 The OpenLDAP Foundation.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -901,9 +901,9 @@ tlsm_get_pin(PK11SlotInfo *slot, PRBool retry, tlsm_ctx *ctx)
         * capability the server would have to be started in foreground mode
         * if using an encrypted key.
         */
-       if ( ctx->tc_pin_file ) {
+       if ( ctx && ctx->tc_pin_file ) {
                pwdstr = tlsm_get_pin_from_file( token_name, ctx );
-               if (retry && pwdstr != NULL)
+               if ( retry && pwdstr != NULL )
                        return NULL;
        }
 #endif /* RETRIEVE_PASSWORD_FROM_FILE */
@@ -912,6 +912,7 @@ tlsm_get_pin(PK11SlotInfo *slot, PRBool retry, tlsm_ctx *ctx)
                int infd = PR_FileDesc2NativeHandle( PR_STDIN );
                int isTTY = isatty( infd );
                unsigned char phrase[200];
+               char *dummy;
                /* Prompt for password */
                if ( isTTY ) {
                        fprintf( stdout,
@@ -919,7 +920,8 @@ tlsm_get_pin(PK11SlotInfo *slot, PRBool retry, tlsm_ctx *ctx)
                                 token_name ? token_name : DEFAULT_TOKEN_NAME );
                        echoOff( infd );
                }
-               fgets( (char*)phrase, sizeof(phrase), stdin );
+               dummy = fgets( (char*)phrase, sizeof(phrase), stdin );
+               (void) dummy;
                if ( isTTY ) {
                        fprintf( stdout, "\n" );
                        echoOn( infd );
@@ -950,7 +952,7 @@ tlsm_pin_prompt(PK11SlotInfo *slot, PRBool retry, void *arg)
 static char *
 tlsm_ctx_subject_name(tlsm_ctx *ctx)
 {
-       if (!ctx || !ctx->tc_certificate)
+       if ( !ctx || !ctx->tc_certificate )
                return "(unknown)";
 
        return ctx->tc_certificate->subjectName;
@@ -990,16 +992,49 @@ tlsm_cert_is_self_issued( CERTCertificate *cert )
        return is_self_issued;
 }
 
+/*
+ * The private key for used certificate can be already unlocked by other
+ * thread or library. Find the unlocked key if possible.
+ */
+static SECKEYPrivateKey *
+tlsm_find_unlocked_key( tlsm_ctx *ctx, void *pin_arg )
+{
+       SECKEYPrivateKey *result = NULL;
+
+       PK11SlotList *slots = PK11_GetAllSlotsForCert( ctx->tc_certificate, NULL );
+       if ( !slots ) {
+               PRErrorCode errcode = PR_GetError();
+               Debug( LDAP_DEBUG_ANY,
+                               "TLS: cannot get all slots for certificate '%s' (error %d: %s)",
+                               tlsm_ctx_subject_name( ctx ), errcode,
+                               PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+               return result;
+       }
+
+       PK11SlotListElement *le;
+       for ( le = slots->head; le; le = le->next ) {
+               PK11SlotInfo *slot = le->slot;
+               if ( PK11_IsLoggedIn( slot, NULL ) ) {
+                       result = PK11_FindKeyByDERCert( slot, ctx->tc_certificate, pin_arg );
+                       break;
+               }
+       }
+
+       PK11_FreeSlotList( slots );
+       return result;
+}
+
 static SECStatus
 tlsm_verify_cert(CERTCertDBHandle *handle, CERTCertificate *cert, void *pinarg,
-                                PRBool checksig, SECCertificateUsage certUsage, int errorToIgnore )
+                                PRBool checksig, SECCertificateUsage certUsage, PRBool warn_only,
+                                PRBool ignore_issuer )
 {
        CERTVerifyLog verifylog;
        SECStatus ret = SECSuccess;
        const char *name;
        int debug_level = LDAP_DEBUG_ANY;
 
-       if ( errorToIgnore == -1 ) {
+       if ( warn_only ) {
                debug_level = LDAP_DEBUG_TRACE;
        }
 
@@ -1061,9 +1096,13 @@ tlsm_verify_cert(CERTCertDBHandle *handle, CERTCertificate *cert, void *pinarg,
                                                           name, 0, 0 );
                                        }
 
-                                       PR_SetError(orig_error, orig_oserror);
+                                       PR_SetError( orig_error, orig_oserror );
 
-                               } else if ( errorToIgnore && ( node->error == errorToIgnore ) ) {
+                               } else if ( warn_only || ( ignore_issuer && (
+                                       node->error == SEC_ERROR_UNKNOWN_ISSUER ||
+                                       node->error == SEC_ERROR_UNTRUSTED_ISSUER )
+                               ) ) {
+                                       ret = SECSuccess;
                                        Debug( debug_level,
                                                   "TLS: Warning: ignoring error for certificate [%s] - error %ld:%s.\n",
                                                   name, node->error, PR_ErrorToString( node->error, PR_LANGUAGE_I_DEFAULT ) );
@@ -1084,8 +1123,6 @@ tlsm_verify_cert(CERTCertDBHandle *handle, CERTCertificate *cert, void *pinarg,
        if ( ret == SECSuccess ) {
                Debug( LDAP_DEBUG_TRACE,
                           "TLS: certificate [%s] is valid\n", name, 0, 0 );
-       } else if ( errorToIgnore == -1 ) {
-               ret = SECSuccess;
        }
 
        return ret;
@@ -1098,15 +1135,11 @@ tlsm_auth_cert_handler(void *arg, PRFileDesc *fd,
        SECCertificateUsage certUsage = isServer ? certificateUsageSSLClient : certificateUsageSSLServer;
        SECStatus ret = SECSuccess;
        CERTCertificate *peercert = SSL_PeerCertificate( fd );
-       int errorToIgnore = 0;
        tlsm_ctx *ctx = (tlsm_ctx *)arg;
 
-       if (ctx && ctx->tc_warn_only )
-               errorToIgnore = -1;
-
        ret = tlsm_verify_cert( ctx->tc_certdb, peercert,
                                                        SSL_RevealPinArg( fd ),
-                                                       checksig, certUsage, errorToIgnore );
+                                                       checksig, certUsage, ctx->tc_warn_only, PR_FALSE );
        CERT_DestroyCertificate( peercert );
 
        return ret;
@@ -1244,10 +1277,10 @@ tlsm_add_cert_from_file( tlsm_ctx *ctx, const char *filename, PRBool isca )
                return -1;
        }
 
-       PK11_SETATTRS( attrs[0], CKA_CLASS, &objClass, sizeof(objClass) );
-       PK11_SETATTRS( attrs[1], CKA_TOKEN, &cktrue, sizeof(CK_BBOOL) );
-       PK11_SETATTRS( attrs[2], CKA_LABEL, (unsigned char *)filename, strlen(filename)+1 );
-       PK11_SETATTRS( attrs[3], CKA_TRUST, isca ? &cktrue : &ckfalse, sizeof(CK_BBOOL) );
+       PK11_SETATTRS( attrs[0], CKA_CLASS, &objClass, sizeof( objClass ) );
+       PK11_SETATTRS( attrs[1], CKA_TOKEN, &cktrue, sizeof( CK_BBOOL ) );
+       PK11_SETATTRS( attrs[2], CKA_LABEL, (unsigned char *) filename, strlen( filename ) + 1 );
+       PK11_SETATTRS( attrs[3], CKA_TRUST, isca ? &cktrue : &ckfalse, sizeof( CK_BBOOL ) );
 
        cert = PK11_CreateGenericObject( slot, attrs, 4, PR_FALSE /* isPerm */ );
 
@@ -1294,23 +1327,35 @@ tlsm_add_cert_from_file( tlsm_ctx *ctx, const char *filename, PRBool isca )
 }
 
 static int
-tlsm_ctx_load_private_key(tlsm_ctx *ctx)
+tlsm_ctx_load_private_key( tlsm_ctx *ctx )
 {
-       if (!ctx->tc_certificate)
+       if ( !ctx->tc_certificate )
                return -1;
 
-       if (ctx->tc_private_key)
+       if ( ctx->tc_private_key )
                return 0;
 
-       void *pin_arg = SSL_RevealPinArg(ctx->tc_model);
+       void *pin_arg = SSL_RevealPinArg( ctx->tc_model );
+
+       SECKEYPrivateKey *unlocked_key = tlsm_find_unlocked_key( ctx, pin_arg );
+       Debug( LDAP_DEBUG_ANY,
+                       "TLS: %s unlocked certificate for certificate '%s'.\n",
+                       unlocked_key ? "found" : "no", tlsm_ctx_subject_name( ctx ), 0 );
 
-       ctx->tc_private_key = PK11_FindKeyByAnyCert(ctx->tc_certificate, pin_arg);
-       if (!ctx->tc_private_key) {
+       /* 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 )
+               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 );
+
+       if ( !ctx->tc_private_key ) {
                PRErrorCode errcode = PR_GetError();
                Debug(LDAP_DEBUG_ANY,
                                "TLS: cannot find private key for certificate '%s' (error %d: %s)",
-                               tlsm_ctx_subject_name(ctx), errcode,
-                               PR_ErrorToString(errcode, PR_LANGUAGE_I_DEFAULT));
+                               tlsm_ctx_subject_name( ctx ), errcode,
+                               PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
                return -1;
        }
 
@@ -1358,9 +1403,9 @@ tlsm_add_key_from_file( tlsm_ctx *ctx, const char *filename )
                return -1;
        }
 
-       PK11_SETATTRS( attrs[0], CKA_CLASS, &objClass, sizeof(objClass) );
-       PK11_SETATTRS( attrs[1], CKA_TOKEN, &cktrue, sizeof(CK_BBOOL) );
-       PK11_SETATTRS( attrs[2], CKA_LABEL, (unsigned char *)filename, strlen(filename)+1 );
+       PK11_SETATTRS( attrs[0], CKA_CLASS, &objClass, sizeof( objClass ) );
+       PK11_SETATTRS( attrs[1], CKA_TOKEN, &cktrue, sizeof( CK_BBOOL ) );
+       PK11_SETATTRS( attrs[2], CKA_LABEL, (unsigned char *)filename, strlen( filename ) + 1 );
 
        key = PK11_CreateGenericObject( slot, attrs, 3, PR_FALSE /* isPerm */ );
 
@@ -1548,28 +1593,28 @@ tlsm_get_certdb_prefix( const char *certdir, char **realcertdir, char **prefix )
  * which is unique for each context.
  */
 static PK11SlotInfo *
-tlsm_init_open_certdb(tlsm_ctx *ctx, const char *dbdir, const char *prefix)
+tlsm_init_open_certdb( tlsm_ctx *ctx, const char *dbdir, const char *prefix )
 {
        PK11SlotInfo *slot = NULL;
        char *token_desc = NULL;
        char *config = NULL;
 
-       token_desc = PR_smprintf(TLSM_CERTDB_DESC_FMT, ctx->tc_unique);
-       config = PR_smprintf("configDir='%s' tokenDescription='%s' certPrefix='%s' keyPrefix='%s' flags=readOnly",
-                                                                               dbdir, token_desc, prefix, prefix);
-       Debug(LDAP_DEBUG_TRACE, "TLS: certdb config: %s\n", config, 0, 0);
+       token_desc = PR_smprintf( TLSM_CERTDB_DESC_FMT, ctx->tc_unique );
+       config = PR_smprintf( "configDir='%s' tokenDescription='%s' certPrefix='%s' keyPrefix='%s' flags=readOnly",
+                                                                               dbdir, token_desc, prefix, prefix );
+       Debug( LDAP_DEBUG_TRACE, "TLS: certdb config: %s\n", config, 0, 0 );
 
-       slot = SECMOD_OpenUserDB(config);
-       if (!slot) {
+       slot = SECMOD_OpenUserDB( config );
+       if ( !slot ) {
                PRErrorCode errcode = PR_GetError();
-               Debug(LDAP_DEBUG_TRACE, "TLS: cannot open certdb '%s', error %d:%s\n", dbdir, errcode,
-                                                       PR_ErrorToString(errcode, PR_LANGUAGE_I_DEFAULT));
+               Debug( LDAP_DEBUG_TRACE, "TLS: cannot open certdb '%s', error %d:%s\n", dbdir, errcode,
+                                                       PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
        }
 
-       if (token_desc)
-               PR_smprintf_free(token_desc);
-       if (config)
-               PR_smprintf_free(config);
+       if ( token_desc )
+               PR_smprintf_free( token_desc );
+       if ( config )
+               PR_smprintf_free( config );
 
        return slot;
 }
@@ -1596,6 +1641,8 @@ tlsm_deferred_init( void *arg )
        NSSInitContext *initctx = NULL;
        PK11SlotInfo *certdb_slot = NULL;
 #endif
+       SSLVersionRange range;
+       SSLProtocolVariant variant;
        SECStatus rc;
        int done = 0;
 
@@ -1671,14 +1718,14 @@ tlsm_deferred_init( void *arg )
 #endif
                        rc = SECFailure;
 
-                       if (initctx != NULL) {
-                               certdb_slot = tlsm_init_open_certdb(ctx, realcertdir, prefix);
-                               if (certdb_slot) {
+                       if ( initctx != NULL ) {
+                               certdb_slot = tlsm_init_open_certdb( ctx, realcertdir, prefix );
+                               if ( certdb_slot ) {
                                        rc = SECSuccess;
                                        ctx->tc_initctx = initctx;
                                        ctx->tc_certdb_slot = certdb_slot;
                                } else {
-                                       NSS_ShutdownContext(initctx);
+                                       NSS_ShutdownContext( initctx );
                                        initctx = NULL;
                                }
                        }
@@ -1722,7 +1769,7 @@ tlsm_deferred_init( void *arg )
 #else
                        initctx = NSS_InitContext( CERTDB_NONE, PREFIX_NONE, PREFIX_NONE, SECMOD_DB,
                                                                           &initParams, flags );
-                       if (initctx) {
+                       if ( initctx ) {
                                ctx->tc_initctx = initctx;
                                rc = SECSuccess;
                        } else {
@@ -1780,6 +1827,16 @@ tlsm_deferred_init( void *arg )
                        ctx->tc_using_pem = PR_TRUE;
                }
 
+               /*
+                * Set the SSL version range.  MozNSS SSL versions are the same as openldap's:
+                *
+                * SSL_LIBRARY_VERSION_TLS_1_* are equivalent to LDAP_OPT_X_TLS_PROTOCOL_TLS1_*
+                */
+               SSL_VersionRangeGetSupported(ssl_variant_stream, &range); /* this sets the max */
+               range.min = lt->lt_protocol_min ? lt->lt_protocol_min : range.min;
+               variant = ssl_variant_stream;
+               SSL_VersionRangeSetDefault(variant, &range);
+
                NSS_SetDomesticPolicy();
 
                PK11_SetPasswordFunc( tlsm_pin_prompt );
@@ -1810,27 +1867,22 @@ tlsm_deferred_init( void *arg )
  * The key is loaded and stored in ctx->tc_private_key
  */
 static int
-tlsm_find_and_verify_cert_key(tlsm_ctx *ctx)
+tlsm_find_and_verify_cert_key( tlsm_ctx *ctx )
 {
        SECCertificateUsage certUsage;
        PRBool checkSig;
        SECStatus status;
-       int errorToIgnore;
        void *pin_arg;
 
-       if (tlsm_ctx_load_private_key(ctx))
+       if ( tlsm_ctx_load_private_key( ctx ) )
                return -1;
 
-       pin_arg = SSL_RevealPinArg(ctx->tc_model);
+       pin_arg = SSL_RevealPinArg( ctx->tc_model );
        certUsage = ctx->tc_is_server ? certificateUsageSSLServer : certificateUsageSSLClient;
        checkSig = ctx->tc_verify_cert ? PR_TRUE : PR_FALSE;
-       if ( ctx->tc_warn_only )
-               errorToIgnore = -1;
-       else
-               errorToIgnore = SEC_ERROR_UNKNOWN_ISSUER; /* may not have a CA cert */
 
        status = tlsm_verify_cert( ctx->tc_certdb, ctx->tc_certificate, pin_arg,
-                                                          checkSig, certUsage, errorToIgnore );
+                                                          checkSig, certUsage, ctx->tc_warn_only, PR_TRUE );
 
        return status == SECSuccess ? 0 : -1;
 }
@@ -1842,11 +1894,11 @@ tlsm_get_client_auth_data( void *arg, PRFileDesc *fd,
 {
        tlsm_ctx *ctx = (tlsm_ctx *)arg;
 
-       if (pRetCert)
-               *pRetCert = CERT_DupCertificate(ctx->tc_certificate);
+       if ( pRetCert )
+               *pRetCert = CERT_DupCertificate( ctx->tc_certificate );
 
-       if (pRetKey)
-               *pRetKey = SECKEY_CopyPrivateKey(ctx->tc_private_key);
+       if ( pRetKey )
+               *pRetKey = SECKEY_CopyPrivateKey( ctx->tc_private_key );
 
        return SECSuccess;
 }
@@ -1897,30 +1949,30 @@ tlsm_copy_config ( const struct ldaptls *config )
 {
        struct ldaptls *copy;
 
-       assert(config);
+       assert( config );
 
-       copy = LDAP_MALLOC(sizeof(*copy));
-       if (!copy)
+       copy = LDAP_MALLOC( sizeof( *copy ) );
+       if ( !copy )
                return NULL;
 
-       memset(copy, 0, sizeof(*copy));
-
-       if (config->lt_certfile)
-               copy->lt_certfile = LDAP_STRDUP(config->lt_certfile);
-       if (config->lt_keyfile)
-               copy->lt_keyfile = LDAP_STRDUP(config->lt_keyfile);
-       if (config->lt_dhfile)
-               copy->lt_dhfile = LDAP_STRDUP(config->lt_dhfile);
-       if (config->lt_cacertfile)
-               copy->lt_cacertfile = LDAP_STRDUP(config->lt_cacertfile);
-       if (config->lt_cacertdir)
-               copy->lt_cacertdir = LDAP_STRDUP(config->lt_cacertdir);
-       if (config->lt_ciphersuite)
-               copy->lt_ciphersuite = LDAP_STRDUP(config->lt_ciphersuite);
-       if (config->lt_crlfile)
-               copy->lt_crlfile = LDAP_STRDUP(config->lt_crlfile);
-       if (config->lt_randfile)
-               copy->lt_randfile = LDAP_STRDUP(config->lt_randfile);
+       memset( copy, 0, sizeof( *copy ) );
+
+       if ( config->lt_certfile )
+               copy->lt_certfile = LDAP_STRDUP( config->lt_certfile );
+       if ( config->lt_keyfile )
+               copy->lt_keyfile = LDAP_STRDUP( config->lt_keyfile );
+       if ( config->lt_dhfile )
+               copy->lt_dhfile = LDAP_STRDUP( config->lt_dhfile );
+       if ( config->lt_cacertfile )
+               copy->lt_cacertfile = LDAP_STRDUP( config->lt_cacertfile );
+       if ( config->lt_cacertdir )
+               copy->lt_cacertdir = LDAP_STRDUP( config->lt_cacertdir );
+       if ( config->lt_ciphersuite )
+               copy->lt_ciphersuite = LDAP_STRDUP( config->lt_ciphersuite );
+       if ( config->lt_crlfile )
+               copy->lt_crlfile = LDAP_STRDUP( config->lt_crlfile );
+       if ( config->lt_randfile )
+               copy->lt_randfile = LDAP_STRDUP( config->lt_randfile );
 
        copy->lt_protocol_min = config->lt_protocol_min;
 
@@ -1930,26 +1982,26 @@ tlsm_copy_config ( const struct ldaptls *config )
 static void
 tlsm_free_config ( struct ldaptls *config )
 {
-       assert(config);
+       assert( config );
 
-       if (config->lt_certfile)
-               LDAP_FREE(config->lt_certfile);
-       if (config->lt_keyfile)
-               LDAP_FREE(config->lt_keyfile);
-       if (config->lt_dhfile)
-               LDAP_FREE(config->lt_dhfile);
-       if (config->lt_cacertfile)
-               LDAP_FREE(config->lt_cacertfile);
-       if (config->lt_cacertdir)
-               LDAP_FREE(config->lt_cacertdir);
-       if (config->lt_ciphersuite)
-               LDAP_FREE(config->lt_ciphersuite);
-       if (config->lt_crlfile)
-               LDAP_FREE(config->lt_crlfile);
-       if (config->lt_randfile)
-               LDAP_FREE(config->lt_randfile);
+       if ( config->lt_certfile )
+               LDAP_FREE( config->lt_certfile );
+       if ( config->lt_keyfile )
+               LDAP_FREE( config->lt_keyfile );
+       if ( config->lt_dhfile )
+               LDAP_FREE( config->lt_dhfile );
+       if ( config->lt_cacertfile )
+               LDAP_FREE( config->lt_cacertfile );
+       if ( config->lt_cacertdir )
+               LDAP_FREE( config->lt_cacertdir );
+       if ( config->lt_ciphersuite )
+               LDAP_FREE( config->lt_ciphersuite );
+       if ( config->lt_crlfile )
+               LDAP_FREE( config->lt_crlfile );
+       if ( config->lt_randfile )
+               LDAP_FREE( config->lt_randfile );
 
-       LDAP_FREE(config);
+       LDAP_FREE( config );
 }
 
 static tls_ctx *
@@ -2013,10 +2065,10 @@ tlsm_ctx_free ( tls_ctx *ctx )
        LDAP_MUTEX_LOCK( &tlsm_init_mutex );
        if ( c->tc_model )
                PR_Close( c->tc_model );
-       if (c->tc_certificate)
-               CERT_DestroyCertificate(c->tc_certificate);
-       if (c->tc_private_key)
-               SECKEY_DestroyPrivateKey(c->tc_private_key);
+       if ( c->tc_certificate )
+               CERT_DestroyCertificate( c->tc_certificate );
+       if ( c->tc_private_key )
+               SECKEY_DestroyPrivateKey( c->tc_private_key );
        c->tc_certdb = NULL; /* if not the default, may have to clean up */
        if ( c->tc_certdb_slot ) {
                if ( SECMOD_CloseUserDB( c->tc_certdb_slot ) ) {
@@ -2026,8 +2078,10 @@ tlsm_ctx_free ( tls_ctx *ctx )
                                   errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 );
                }
        }
-       PL_strfree( c->tc_pin_file );
-       c->tc_pin_file = NULL;
+       if ( c->tc_pin_file ) {
+               PL_strfree( c->tc_pin_file );
+               c->tc_pin_file = NULL;
+       }
        tlsm_free_pem_objs( c );
 #ifdef HAVE_NSS_INITCONTEXT
        if ( c->tc_initctx ) {
@@ -2058,12 +2112,28 @@ static int
 tlsm_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server )
 {
        tlsm_ctx *ctx = (tlsm_ctx *)lo->ldo_tls_ctx;
-       ctx->tc_config = tlsm_copy_config(lt);
+       ctx->tc_config = tlsm_copy_config( lt );
        ctx->tc_is_server = is_server;
 
        return 0;
 }
 
+/* returns true if the given string looks like
+   "tokenname" ":" "certnickname"
+   This is true if there is a ':' colon character
+   in the string and the colon is not the first
+   or the last character in the string
+*/
+static int
+tlsm_is_tokenname_certnick( const char *certfile )
+{
+       if ( certfile ) {
+               const char *ptr = PL_strchr( certfile, ':' );
+               return ptr && (ptr != certfile) && (*(ptr+1));
+       }
+       return 0;
+}
+
 static int
 tlsm_deferred_ctx_init( void *arg )
 {
@@ -2230,16 +2300,19 @@ tlsm_deferred_ctx_init( void *arg )
                } else {
                        char *tmp_certname;
 
-                       if (ctx->tc_certdb_slot) {
-                               tmp_certname = PR_smprintf(TLSM_CERTDB_DESC_FMT ":%s", ctx->tc_unique, lt->lt_certfile);
+                       if ( tlsm_is_tokenname_certnick( lt->lt_certfile )) {
+                               /* assume already in form tokenname:certnickname */
+                               tmp_certname = PL_strdup( lt->lt_certfile );
+                       } else if ( ctx->tc_certdb_slot ) {
+                               tmp_certname = PR_smprintf( TLSM_CERTDB_DESC_FMT ":%s", ctx->tc_unique, lt->lt_certfile );
                        } else {
-                               tmp_certname = PR_smprintf("%s", lt->lt_certfile);
+                               tmp_certname = PR_smprintf( "%s", lt->lt_certfile );
                        }
 
-                       ctx->tc_certificate = PK11_FindCertFromNickname(tmp_certname, SSL_RevealPinArg(ctx->tc_model));
-                       PR_smprintf_free(tmp_certname);
+                       ctx->tc_certificate = PK11_FindCertFromNickname( tmp_certname, SSL_RevealPinArg( ctx->tc_model ) );
+                       PR_smprintf_free( tmp_certname );
 
-                       if (!ctx->tc_certificate) {
+                       if ( !ctx->tc_certificate ) {
                                PRErrorCode errcode = PR_GetError();
                                Debug( LDAP_DEBUG_ANY,
                                           "TLS: error: the certificate '%s' could not be found in the database - error %d:%s.\n",
@@ -2258,7 +2331,8 @@ tlsm_deferred_ctx_init( void *arg )
                                return rc;
                        }
                } else {
-                       PL_strfree( ctx->tc_pin_file );
+                       if ( ctx->tc_pin_file )
+                               PL_strfree( ctx->tc_pin_file );
                        ctx->tc_pin_file = PL_strdup( lt->lt_keyfile );
                }
        }
@@ -2304,7 +2378,7 @@ tlsm_deferred_ctx_init( void *arg )
                        return -1;
                }
 
-               if (tlsm_find_and_verify_cert_key(ctx)) {
+               if ( tlsm_find_and_verify_cert_key( ctx ) ) {
                        Debug( LDAP_DEBUG_ANY, 
                               "TLS: error: unable to find and verify server's cert and key for certificate %s\n",
                               tlsm_ctx_subject_name(ctx), 0, 0 );
@@ -2781,6 +2855,79 @@ tlsm_session_strength( tls_session *session )
        return rc ? 0 : keySize;
 }
 
+static int
+tlsm_session_unique( tls_session *sess, struct berval *buf, int is_server)
+{
+       /* Need upstream support https://bugzilla.mozilla.org/show_bug.cgi?id=563276 */
+       return 0;
+}
+
+/*
+ * Yet again, we're pasting in glue that MozNSS ought to provide itself.
+ *
+ * SSL_LIBRARY_VERSION_TLS_1_* are equivalent to LDAP_OPT_X_TLS_PROTOCOL_TLS1_*
+ */
+static struct {
+       const char *name;
+       int num;
+} pvers[] = {
+       { "SSLv2", SSL_LIBRARY_VERSION_2 },
+       { "SSLv3", SSL_LIBRARY_VERSION_3_0 },
+       { "TLSv1", SSL_LIBRARY_VERSION_TLS_1_0 },
+       { "TLSv1.1", SSL_LIBRARY_VERSION_TLS_1_1 },
+       { "TLSv1.2", SSL_LIBRARY_VERSION_TLS_1_2 },
+       { "TLSv1.3", SSL_LIBRARY_VERSION_TLS_1_3 },
+       { NULL, 0 }
+};
+
+static const char *
+tlsm_session_version( tls_session *sess )
+{
+       tlsm_session *s = (tlsm_session *)sess;
+       SSLChannelInfo info;
+       int rc;
+       rc = SSL_GetChannelInfo( s, &info, sizeof( info ));
+       if ( rc == 0 ) {
+               int i;
+               for (i=0; pvers[i].name; i++)
+                       if (pvers[i].num == info.protocolVersion)
+                               return pvers[i].name;
+       }
+       return "unknown";
+}
+
+static const char *
+tlsm_session_cipher( tls_session *sess )
+{
+       tlsm_session *s = (tlsm_session *)sess;
+       SSLChannelInfo info;
+       int rc;
+       rc = SSL_GetChannelInfo( s, &info, sizeof( info ));
+       if ( rc == 0 ) {
+               SSLCipherSuiteInfo csinfo;
+               rc = SSL_GetCipherSuiteInfo( info.cipherSuite, &csinfo, sizeof( csinfo ));
+               if ( rc == 0 )
+                       return csinfo.cipherSuiteName;
+       }
+       return "unknown";
+}
+
+static int
+tlsm_session_peercert( tls_session *sess, struct berval *der )
+{
+       tlsm_session *s = (tlsm_session *)sess;
+       CERTCertificate *cert;
+       cert = SSL_PeerCertificate( s );
+       if (!cert)
+               return -1;
+       der->bv_len = cert->derCert.len;
+       der->bv_val = LDAP_MALLOC( der->bv_len );
+       if (!der->bv_val)
+               return -1;
+       memcpy( der->bv_val, cert->derCert.data, der->bv_len );
+       return 0;
+}
+
 /*
  * TLS support for LBER Sockbufs
  */
@@ -3209,6 +3356,10 @@ tls_impl ldap_int_tls_impl = {
        tlsm_session_peer_dn,
        tlsm_session_chkhost,
        tlsm_session_strength,
+       tlsm_session_unique,
+       tlsm_session_version,
+       tlsm_session_cipher,
+       tlsm_session_peercert,
 
        &tlsm_sbio,