From 50582c5be1a613ee1d212855a8945b4c8ef8950f Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Mon, 10 Apr 2017 17:05:05 +0200 Subject: [PATCH 1/2] nss: factorize out nss_{un,}load_module to separate fncs No change of behavior is intended by this commit. Upstream-commit: fab3d1ec650e17fd15cf8b6d4ffa5bfd523501dc Signed-off-by: Kamil Dudka --- lib/vtls/nss.c | 83 +++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 27 deletions(-) diff --git a/lib/vtls/nss.c b/lib/vtls/nss.c index 099f364..6b8f8c0 100644 --- a/lib/vtls/nss.c +++ b/lib/vtls/nss.c @@ -201,7 +201,7 @@ static const cipher_s cipherlist[] = { }; static const char *pem_library = "libnsspem.so"; -static SECMODModule *mod = NULL; +static SECMODModule *pem_module = NULL; /* NSPR I/O layer we use to detect blocking direction during SSL handshake */ static PRDescIdentity nspr_io_identity = PR_INVALID_IO_LAYER; @@ -600,7 +600,7 @@ static CURLcode nss_load_key(struct connectdata *conn, int sockindex, return CURLE_SSL_CERTPROBLEM; /* This will force the token to be seen as re-inserted */ - tmp = SECMOD_WaitForAnyTokenEvent(mod, 0, 0); + tmp = SECMOD_WaitForAnyTokenEvent(pem_module, 0, 0); if(tmp) PK11_FreeSlot(tmp); PK11_IsPresent(slot); @@ -1180,6 +1180,50 @@ static PRStatus nspr_io_close(PRFileDesc *fd) return close_fn(fd); } +/* load a PKCS #11 module */ +static CURLcode nss_load_module(SECMODModule **pmod, const char *library, + const char *name) +{ + char *config_string; + SECMODModule *module = *pmod; + if(module) + /* already loaded */ + return CURLE_OK; + + config_string = aprintf("library=%s name=%s", library, name); + if(!config_string) + return CURLE_OUT_OF_MEMORY; + + module = SECMOD_LoadUserModule(config_string, NULL, PR_FALSE); + free(config_string); + + if(module && module->loaded) { + /* loaded successfully */ + *pmod = module; + return CURLE_OK; + } + + if(module) + SECMOD_DestroyModule(module); + return CURLE_FAILED_INIT; +} + +/* unload a PKCS #11 module */ +static void nss_unload_module(SECMODModule **pmod) +{ + SECMODModule *module = *pmod; + if(!module) + /* not loaded */ + return; + + if(SECMOD_UnloadUserModule(module) != SECSuccess) + /* unload failed */ + return; + + SECMOD_DestroyModule(module); + *pmod = NULL; +} + /* data might be NULL */ static CURLcode nss_init_core(struct Curl_easy *data, const char *cert_dir) { @@ -1327,10 +1371,7 @@ void Curl_nss_cleanup(void) * the certificates. */ SSL_ClearSessionCache(); - if(mod && SECSuccess == SECMOD_UnloadUserModule(mod)) { - SECMOD_DestroyModule(mod); - mod = NULL; - } + nss_unload_module(&pem_module); NSS_ShutdownContext(nss_context); nss_context = NULL; } @@ -1685,29 +1726,17 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex) goto error; } - result = CURLE_SSL_CONNECT_ERROR; - - if(!mod) { - char *configstring = aprintf("library=%s name=PEM", pem_library); - if(!configstring) { - PR_Unlock(nss_initlock); - goto error; - } - mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE); - free(configstring); - - if(!mod || !mod->loaded) { - if(mod) { - SECMOD_DestroyModule(mod); - mod = NULL; - } - infof(data, "WARNING: failed to load NSS PEM library %s. Using " - "OpenSSL PEM certificates will not work.\n", pem_library); - } - } - PK11_SetPasswordFunc(nss_get_password); + + result = nss_load_module(&pem_module, pem_library, "PEM"); PR_Unlock(nss_initlock); + if(result == CURLE_FAILED_INIT) + infof(data, "WARNING: failed to load NSS PEM library %s. Using " + "OpenSSL PEM certificates will not work.\n", pem_library); + else if(result) + goto error; + + result = CURLE_SSL_CONNECT_ERROR; model = PR_NewTCPSocket(); if(!model) -- 2.9.3 From cf6f9017f0e144ad510fd5913b44b22a2146dd4b Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Mon, 10 Apr 2017 17:40:30 +0200 Subject: [PATCH 2/2] nss: load libnssckbi.so if no other trust is specified The module contains a more comprehensive set of trust information than supported by nss-pem, because libnssckbi.so also includes information about distrusted certificates. Reviewed-by: Kai Engert Closes #1414 Upstream-commit: e3e8d0204b72509cfd63d97a159d1ac3fdea703b Signed-off-by: Kamil Dudka --- docs/libcurl/opts/CURLOPT_CAINFO.3 | 5 ++++ lib/vtls/nss.c | 51 ++++++++++++++++++++++++++++++++------ 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/docs/libcurl/opts/CURLOPT_CAINFO.3 b/docs/libcurl/opts/CURLOPT_CAINFO.3 index 91b7e23..85436c3 100644 --- a/docs/libcurl/opts/CURLOPT_CAINFO.3 +++ b/docs/libcurl/opts/CURLOPT_CAINFO.3 @@ -41,6 +41,11 @@ is assumed to be stored, as established at build time. If curl is built against the NSS SSL library, the NSS PEM PKCS#11 module (libnsspem.so) needs to be available for this option to work properly. +Starting with curl-7.55.0, if both \fICURLOPT_CAINFO(3)\fP and +\fICURLOPT_CAPATH(3)\fP are unset, NSS-linked libcurl tries to load +libnssckbi.so, which contains a more comprehensive set of trust information +than supported by nss-pem, because libnssckbi.so also includes information +about distrusted certificates. (iOS and macOS only) If curl is built against Secure Transport, then this option is supported for backward compatibility with other SSL engines, but it diff --git a/lib/vtls/nss.c b/lib/vtls/nss.c index 6b8f8c0..62665e7 100644 --- a/lib/vtls/nss.c +++ b/lib/vtls/nss.c @@ -81,6 +81,7 @@ static PRLock *nss_initlock = NULL; static PRLock *nss_crllock = NULL; static PRLock *nss_findslot_lock = NULL; +static PRLock *nss_trustload_lock = NULL; static struct curl_llist nss_crl_list; static NSSInitContext *nss_context = NULL; static volatile int initialized = 0; @@ -203,6 +204,9 @@ static const cipher_s cipherlist[] = { static const char *pem_library = "libnsspem.so"; static SECMODModule *pem_module = NULL; +static const char *trust_library = "libnssckbi.so"; +static SECMODModule *trust_module = NULL; + /* NSPR I/O layer we use to detect blocking direction during SSL handshake */ static PRDescIdentity nspr_io_identity = PR_INVALID_IO_LAYER; static PRIOMethods nspr_io_methods; @@ -1333,6 +1337,7 @@ int Curl_nss_init(void) nss_initlock = PR_NewLock(); nss_crllock = PR_NewLock(); nss_findslot_lock = PR_NewLock(); + nss_trustload_lock = PR_NewLock(); } /* We will actually initialize NSS later */ @@ -1372,6 +1377,7 @@ void Curl_nss_cleanup(void) SSL_ClearSessionCache(); nss_unload_module(&pem_module); + nss_unload_module(&trust_module); NSS_ShutdownContext(nss_context); nss_context = NULL; } @@ -1384,6 +1390,7 @@ void Curl_nss_cleanup(void) PR_DestroyLock(nss_initlock); PR_DestroyLock(nss_crllock); PR_DestroyLock(nss_findslot_lock); + PR_DestroyLock(nss_trustload_lock); nss_initlock = NULL; initialized = 0; @@ -1505,12 +1512,44 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn, struct Curl_easy *data = conn->data; const char *cafile = SSL_CONN_CONFIG(CAfile); const char *capath = SSL_CONN_CONFIG(CApath); + bool use_trust_module; + CURLcode result = CURLE_OK; - if(cafile) { - CURLcode result = nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE); - if(result) - return result; + /* treat empty string as unset */ + if(cafile && !cafile[0]) + cafile = NULL; + if(capath && !capath[0]) + capath = NULL; + + infof(data, " CAfile: %s\n CApath: %s\n", + cafile ? cafile : "none", + capath ? capath : "none"); + + /* load libnssckbi.so if no other trust roots were specified */ + use_trust_module = !cafile && !capath; + + PR_Lock(nss_trustload_lock); + if(use_trust_module && !trust_module) { + /* libnssckbi.so needed but not yet loaded --> load it! */ + result = nss_load_module(&trust_module, trust_library, "trust"); + infof(data, "%s %s\n", (result) ? "failed to load" : "loaded", + trust_library); + if(result == CURLE_FAILED_INIT) + /* make the error non-fatal if we are not going to verify peer */ + result = CURLE_SSL_CACERT_BADFILE; } + else if(!use_trust_module && trust_module) { + /* libnssckbi.so not needed but already loaded --> unload it! */ + infof(data, "unloading %s\n", trust_library); + nss_unload_module(&trust_module); + } + PR_Unlock(nss_trustload_lock); + + if(cafile) + result = nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE); + + if(result) + return result; if(capath) { struct_stat st; @@ -1544,10 +1583,6 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn, infof(data, "warning: CURLOPT_CAPATH not a directory (%s)\n", capath); } - infof(data, " CAfile: %s\n CApath: %s\n", - cafile ? cafile : "none", - capath ? capath : "none"); - return CURLE_OK; } -- 2.9.3