#include <base64.h>
#include <cert.h>
#include <prerror.h>
-#include <keyhi.h> /* for SECKEY_DestroyPublicKey() */
+#include <keyhi.h> /* for SECKEY_DestroyPublicKey() */
+#include <private/pprio.h> /* for PR_ImportTCPSocket */
#define NSSVERNUM ((NSS_VMAJOR<<16)|(NSS_VMINOR<<8)|NSS_VPATCH)
/* enough to fit the string "PEM Token #[0|1]" */
#define SLOTSIZE 13
-PRFileDesc *PR_ImportTCPSocket(PRInt32 osfd);
+struct ssl_backend_data {
+ PRFileDesc *handle;
+ char *client_nickname;
+ struct Curl_easy *data;
+ struct curl_llist obj_list;
+ PK11GenericObject *obj_clicert;
+};
+
+#define BACKEND connssl->backend
+
static PRLock *nss_initlock = NULL;
static PRLock *nss_crllock = NULL;
static PRLock *nss_findslot_lock = NULL;
-static struct curl_llist *nss_crl_list = NULL;
+static PRLock *nss_trustload_lock = NULL;
+static struct curl_llist nss_crl_list;
static NSSInitContext *nss_context = NULL;
static volatile int initialized = 0;
+/* type used to wrap pointers as list nodes */
+struct ptr_list_wrap {
+ void *ptr;
+ struct curl_llist_element node;
+};
+
typedef struct {
const char *name;
int num;
};
static const char *pem_library = "libnsspem.so";
-static SECMODModule *mod = NULL;
+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;
found = PR_FALSE;
- for(i=0; i<NUM_OF_CIPHERS; i++) {
+ for(i = 0; i<NUM_OF_CIPHERS; i++) {
if(strcasecompare(cipher, cipherlist[i].name)) {
cipher_state[i] = PR_TRUE;
found = PR_TRUE;
}
/* Finally actually enable the selected ciphers */
- for(i=0; i<NUM_OF_CIPHERS; i++) {
+ for(i = 0; i<NUM_OF_CIPHERS; i++) {
if(!cipher_state[i])
continue;
{
unsigned int i;
- for(i=0; i<NUM_OF_CIPHERS; i++) {
+ for(i = 0; i<NUM_OF_CIPHERS; i++) {
PRInt32 policy = 0;
SSL_CipherPolicyGet(cipherlist[i].num, &policy);
if(policy)
return slot;
}
+/* wrap 'ptr' as list node and tail-insert into 'list' */
+static CURLcode insert_wrapped_ptr(struct curl_llist *list, void *ptr)
+{
+ struct ptr_list_wrap *wrap = malloc(sizeof *wrap);
+ if(!wrap)
+ return CURLE_OUT_OF_MEMORY;
+
+ wrap->ptr = ptr;
+ Curl_llist_insert_next(list, list->tail, wrap, &wrap->node);
+ return CURLE_OK;
+}
+
/* Call PK11_CreateGenericObject() with the given obj_class and filename. If
* the call succeeds, append the object handle to the list of objects so that
* the object can be destroyed in Curl_nss_close(). */
-static CURLcode nss_create_object(struct ssl_connect_data *ssl,
+static CURLcode nss_create_object(struct ssl_connect_data *connssl,
CK_OBJECT_CLASS obj_class,
const char *filename, bool cacert)
{
PK11_SETATTRS(attrs, attr_cnt, CKA_CLASS, &obj_class, sizeof(obj_class));
PK11_SETATTRS(attrs, attr_cnt, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL));
PK11_SETATTRS(attrs, attr_cnt, CKA_LABEL, (unsigned char *)filename,
- strlen(filename) + 1);
+ (CK_ULONG)strlen(filename) + 1);
if(CKO_CERTIFICATE == obj_class) {
CK_BBOOL *pval = (cacert) ? (&cktrue) : (&ckfalse);
PK11_SETATTRS(attrs, attr_cnt, CKA_TRUST, pval, sizeof(*pval));
}
- obj = PK11_CreateGenericObject(slot, attrs, attr_cnt, PR_FALSE);
+ /* PK11_CreateManagedGenericObject() was introduced in NSS 3.34 because
+ * PK11_DestroyGenericObject() does not release resources allocated by
+ * PK11_CreateGenericObject() early enough. */
+ obj =
+#ifdef HAVE_PK11_CREATEMANAGEDGENERICOBJECT
+ PK11_CreateManagedGenericObject
+#else
+ PK11_CreateGenericObject
+#endif
+ (slot, attrs, attr_cnt, PR_FALSE);
+
PK11_FreeSlot(slot);
if(!obj)
return result;
- if(!Curl_llist_insert_next(ssl->obj_list, ssl->obj_list->tail, obj)) {
+ if(insert_wrapped_ptr(&BACKEND->obj_list, obj) != CURLE_OK) {
PK11_DestroyGenericObject(obj);
return CURLE_OUT_OF_MEMORY;
}
if(!cacert && CKO_CERTIFICATE == obj_class)
/* store reference to a client certificate */
- ssl->obj_clicert = obj;
+ BACKEND->obj_clicert = obj;
return CURLE_OK;
}
* NSS objects in Curl_nss_close() */
static void nss_destroy_object(void *user, void *ptr)
{
- PK11GenericObject *obj = (PK11GenericObject *)ptr;
+ struct ptr_list_wrap *wrap = (struct ptr_list_wrap *) ptr;
+ PK11GenericObject *obj = (PK11GenericObject *) wrap->ptr;
(void) user;
PK11_DestroyGenericObject(obj);
+ free(wrap);
}
/* same as nss_destroy_object() but for CRL items */
static void nss_destroy_crl_item(void *user, void *ptr)
{
- SECItem *crl_der = (SECItem *)ptr;
+ struct ptr_list_wrap *wrap = (struct ptr_list_wrap *) ptr;
+ SECItem *crl_der = (SECItem *) wrap->ptr;
(void) user;
SECITEM_FreeItem(crl_der, PR_TRUE);
+ free(wrap);
}
static CURLcode nss_load_cert(struct ssl_connect_data *ssl,
PR_Lock(nss_crllock);
/* store the CRL item so that we can free it in Curl_nss_cleanup() */
- if(!Curl_llist_insert_next(nss_crl_list, nss_crl_list->tail, crl_der)) {
+ if(insert_wrapped_ptr(&nss_crl_list, crl_der) != CURLE_OK) {
SECITEM_FreeItem(crl_der, PR_TRUE);
PR_Unlock(nss_crllock);
return CURLE_OUT_OF_MEMORY;
static CURLcode nss_load_key(struct connectdata *conn, int sockindex,
char *key_file)
{
- PK11SlotInfo *slot;
+ PK11SlotInfo *slot, *tmp;
SECStatus status;
CURLcode result;
struct ssl_connect_data *ssl = conn->ssl;
return CURLE_SSL_CERTPROBLEM;
/* This will force the token to be seen as re-inserted */
- SECMOD_WaitForAnyTokenEvent(mod, 0, 0);
+ tmp = SECMOD_WaitForAnyTokenEvent(pem_module, 0, 0);
+ if(tmp)
+ PK11_FreeSlot(tmp);
PK11_IsPresent(slot);
status = PK11_Authenticate(slot, PR_TRUE, SSL_SET_OPTION(key_passwd));
char *issuer_nickname)
{
CERTCertificate *cert, *cert_issuer, *issuer;
- SECStatus res=SECSuccess;
+ SECStatus res = SECSuccess;
void *proto_win = NULL;
cert = SSL_PeerCertificate(sock);
if((!cert_issuer) || (!issuer))
res = SECFailure;
else if(SECITEM_CompareItem(&cert_issuer->derCert,
- &issuer->derCert)!=SECEqual)
+ &issuer->derCert) != SECEqual)
res = SECFailure;
CERT_DestroyCertificate(cert);
const char *pinnedpubkey)
{
CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
- struct Curl_easy *data = connssl->data;
+ struct Curl_easy *data = BACKEND->data;
CERTCertificate *cert;
if(!pinnedpubkey)
return CURLE_OK;
/* get peer certificate */
- cert = SSL_PeerCertificate(connssl->handle);
+ cert = SSL_PeerCertificate(BACKEND->handle);
if(cert) {
/* extract public key from peer certificate */
SECKEYPublicKey *pubkey = CERT_ExtractPublicKey(cert);
struct SECKEYPrivateKeyStr **pRetKey)
{
struct ssl_connect_data *connssl = (struct ssl_connect_data *)arg;
- struct Curl_easy *data = connssl->data;
- const char *nickname = connssl->client_nickname;
+ struct Curl_easy *data = BACKEND->data;
+ const char *nickname = BACKEND->client_nickname;
static const char pem_slotname[] = "PEM Token #1";
- if(connssl->obj_clicert) {
+ if(BACKEND->obj_clicert) {
/* use the cert/key provided by PEM reader */
SECItem cert_der = { 0, NULL, 0 };
void *proto_win = SSL_RevealPinArg(sock);
return SECFailure;
}
- if(PK11_ReadRawAttribute(PK11_TypeGeneric, connssl->obj_clicert, CKA_VALUE,
+ if(PK11_ReadRawAttribute(PK11_TypeGeneric, BACKEND->obj_clicert, CKA_VALUE,
&cert_der) != SECSuccess) {
failf(data, "NSS: CKA_VALUE not found in PK11 generic object");
PK11_FreeSlot(slot);
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)
{
return CURLE_OK;
/* list of all CRL items we need to destroy in Curl_nss_cleanup() */
- nss_crl_list = Curl_llist_alloc(nss_destroy_crl_item);
- if(!nss_crl_list)
- return CURLE_OUT_OF_MEMORY;
+ Curl_llist_init(&nss_crl_list, nss_destroy_crl_item);
/* First we check if $SSL_DIR points to a valid dir */
cert_dir = getenv("SSL_DIR");
* @retval 0 error initializing SSL
* @retval 1 SSL initialized successfully
*/
-int Curl_nss_init(void)
+static int Curl_nss_init(void)
{
/* curl_global_init() is not thread-safe so this test is ok */
if(nss_initlock == NULL) {
nss_initlock = PR_NewLock();
nss_crllock = PR_NewLock();
nss_findslot_lock = PR_NewLock();
+ nss_trustload_lock = PR_NewLock();
}
/* We will actually initialize NSS later */
}
/* Global cleanup */
-void Curl_nss_cleanup(void)
+static void Curl_nss_cleanup(void)
{
/* This function isn't required to be threadsafe and this is only done
* as a safety feature.
* the certificates. */
SSL_ClearSessionCache();
- if(mod && SECSuccess == SECMOD_UnloadUserModule(mod)) {
- SECMOD_DestroyModule(mod);
- mod = NULL;
- }
+ nss_unload_module(&pem_module);
+ nss_unload_module(&trust_module);
NSS_ShutdownContext(nss_context);
nss_context = NULL;
}
/* destroy all CRL items */
- Curl_llist_destroy(nss_crl_list, NULL);
- nss_crl_list = NULL;
+ Curl_llist_destroy(&nss_crl_list, NULL);
PR_Unlock(nss_initlock);
PR_DestroyLock(nss_initlock);
PR_DestroyLock(nss_crllock);
PR_DestroyLock(nss_findslot_lock);
+ PR_DestroyLock(nss_trustload_lock);
nss_initlock = NULL;
initialized = 0;
* 0 means the connection has been closed
* -1 means the connection status is unknown
*/
-int
-Curl_nss_check_cxn(struct connectdata *conn)
+static int Curl_nss_check_cxn(struct connectdata *conn)
{
+ struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET];
int rc;
char buf;
rc =
- PR_Recv(conn->ssl[FIRSTSOCKET].handle, (void *)&buf, 1, PR_MSG_PEEK,
+ PR_Recv(BACKEND->handle, (void *)&buf, 1, PR_MSG_PEEK,
PR_SecondsToInterval(1));
if(rc > 0)
return 1; /* connection still in place */
static void nss_close(struct ssl_connect_data *connssl)
{
/* before the cleanup, check whether we are using a client certificate */
- const bool client_cert = (connssl->client_nickname != NULL)
- || (connssl->obj_clicert != NULL);
+ const bool client_cert = (BACKEND->client_nickname != NULL)
+ || (BACKEND->obj_clicert != NULL);
- free(connssl->client_nickname);
- connssl->client_nickname = NULL;
+ free(BACKEND->client_nickname);
+ BACKEND->client_nickname = NULL;
/* destroy all NSS objects in order to avoid failure of NSS shutdown */
- Curl_llist_destroy(connssl->obj_list, NULL);
- connssl->obj_list = NULL;
- connssl->obj_clicert = NULL;
+ Curl_llist_destroy(&BACKEND->obj_list, NULL);
+ BACKEND->obj_clicert = NULL;
- if(connssl->handle) {
+ if(BACKEND->handle) {
if(client_cert)
/* A server might require different authentication based on the
* particular path being requested by the client. To support this
* scenario, we must ensure that a connection will never reuse the
* authentication data from a previous connection. */
- SSL_InvalidateSession(connssl->handle);
+ SSL_InvalidateSession(BACKEND->handle);
- PR_Close(connssl->handle);
- connssl->handle = NULL;
+ PR_Close(BACKEND->handle);
+ BACKEND->handle = NULL;
}
}
/*
* This function is called when an SSL connection is closed.
*/
-void Curl_nss_close(struct connectdata *conn, int sockindex)
+static void Curl_nss_close(struct connectdata *conn, int sockindex)
{
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct ssl_connect_data *connssl_proxy = &conn->proxy_ssl[sockindex];
- if(connssl->handle || connssl_proxy->handle) {
+ if(BACKEND->handle || connssl_proxy->backend->handle) {
/* NSS closes the socket we previously handed to it, so we must mark it
as closed to avoid double close */
fake_sclose(conn->sock[sockindex]);
conn->sock[sockindex] = CURL_SOCKET_BAD;
}
- if(connssl->handle)
- /* nss_close(connssl) will transitively close also connssl_proxy->handle
- if both are used. Clear it to avoid a double close leading to crash. */
- connssl_proxy->handle = NULL;
+ if(BACKEND->handle)
+ /* nss_close(connssl) will transitively close also
+ connssl_proxy->backend->handle if both are used. Clear it to avoid
+ a double close leading to crash. */
+ connssl_proxy->backend->handle = NULL;
nss_close(connssl);
nss_close(connssl_proxy);
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;
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;
}
-static CURLcode nss_init_sslver(SSLVersionRange *sslver,
- struct Curl_easy *data,
- struct connectdata *conn)
+static CURLcode nss_sslver_from_curl(PRUint16 *nssver, long version)
{
- switch(SSL_CONN_CONFIG(version)) {
- case CURL_SSLVERSION_DEFAULT:
- /* map CURL_SSLVERSION_DEFAULT to NSS default */
- if(SSL_VersionRangeGetDefault(ssl_variant_stream, sslver) != SECSuccess)
- return CURLE_SSL_CONNECT_ERROR;
- /* ... but make sure we use at least TLSv1.0 according to libcurl API */
- if(sslver->min < SSL_LIBRARY_VERSION_TLS_1_0)
- sslver->min = SSL_LIBRARY_VERSION_TLS_1_0;
- return CURLE_OK;
-
+ switch(version) {
case CURL_SSLVERSION_TLSv1:
- sslver->min = SSL_LIBRARY_VERSION_TLS_1_0;
/* TODO: set sslver->max to SSL_LIBRARY_VERSION_TLS_1_3 once stable */
#ifdef SSL_LIBRARY_VERSION_TLS_1_2
- sslver->max = SSL_LIBRARY_VERSION_TLS_1_2;
+ *nssver = SSL_LIBRARY_VERSION_TLS_1_2;
#elif defined SSL_LIBRARY_VERSION_TLS_1_1
- sslver->max = SSL_LIBRARY_VERSION_TLS_1_1;
+ *nssver = SSL_LIBRARY_VERSION_TLS_1_1;
#else
- sslver->max = SSL_LIBRARY_VERSION_TLS_1_0;
+ *nssver = SSL_LIBRARY_VERSION_TLS_1_0;
#endif
return CURLE_OK;
case CURL_SSLVERSION_SSLv2:
- sslver->min = SSL_LIBRARY_VERSION_2;
- sslver->max = SSL_LIBRARY_VERSION_2;
+ *nssver = SSL_LIBRARY_VERSION_2;
return CURLE_OK;
case CURL_SSLVERSION_SSLv3:
- sslver->min = SSL_LIBRARY_VERSION_3_0;
- sslver->max = SSL_LIBRARY_VERSION_3_0;
+ *nssver = SSL_LIBRARY_VERSION_3_0;
return CURLE_OK;
case CURL_SSLVERSION_TLSv1_0:
- sslver->min = SSL_LIBRARY_VERSION_TLS_1_0;
- sslver->max = SSL_LIBRARY_VERSION_TLS_1_0;
+ *nssver = SSL_LIBRARY_VERSION_TLS_1_0;
return CURLE_OK;
case CURL_SSLVERSION_TLSv1_1:
#ifdef SSL_LIBRARY_VERSION_TLS_1_1
- sslver->min = SSL_LIBRARY_VERSION_TLS_1_1;
- sslver->max = SSL_LIBRARY_VERSION_TLS_1_1;
+ *nssver = SSL_LIBRARY_VERSION_TLS_1_1;
return CURLE_OK;
+#else
+ return CURLE_SSL_CONNECT_ERROR;
#endif
- break;
case CURL_SSLVERSION_TLSv1_2:
#ifdef SSL_LIBRARY_VERSION_TLS_1_2
- sslver->min = SSL_LIBRARY_VERSION_TLS_1_2;
- sslver->max = SSL_LIBRARY_VERSION_TLS_1_2;
+ *nssver = SSL_LIBRARY_VERSION_TLS_1_2;
return CURLE_OK;
+#else
+ return CURLE_SSL_CONNECT_ERROR;
#endif
- break;
case CURL_SSLVERSION_TLSv1_3:
#ifdef SSL_LIBRARY_VERSION_TLS_1_3
- sslver->min = SSL_LIBRARY_VERSION_TLS_1_3;
- sslver->max = SSL_LIBRARY_VERSION_TLS_1_3;
+ *nssver = SSL_LIBRARY_VERSION_TLS_1_3;
return CURLE_OK;
+#else
+ return CURLE_SSL_CONNECT_ERROR;
#endif
- break;
default:
- failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
return CURLE_SSL_CONNECT_ERROR;
}
+}
+
+static CURLcode nss_init_sslver(SSLVersionRange *sslver,
+ struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLcode result;
+ const long min = SSL_CONN_CONFIG(version);
+ const long max = SSL_CONN_CONFIG(version_max);
+
+ /* map CURL_SSLVERSION_DEFAULT to NSS default */
+ if(min == CURL_SSLVERSION_DEFAULT || max == CURL_SSLVERSION_MAX_DEFAULT) {
+ /* map CURL_SSLVERSION_DEFAULT to NSS default */
+ if(SSL_VersionRangeGetDefault(ssl_variant_stream, sslver) != SECSuccess)
+ return CURLE_SSL_CONNECT_ERROR;
+ /* ... but make sure we use at least TLSv1.0 according to libcurl API */
+ if(sslver->min < SSL_LIBRARY_VERSION_TLS_1_0)
+ sslver->min = SSL_LIBRARY_VERSION_TLS_1_0;
+ }
+
+ switch(min) {
+ case CURL_SSLVERSION_DEFAULT:
+ break;
+ case CURL_SSLVERSION_TLSv1:
+ sslver->min = SSL_LIBRARY_VERSION_TLS_1_0;
+ break;
+ default:
+ result = nss_sslver_from_curl(&sslver->min, min);
+ if(result) {
+ failf(data, "unsupported min version passed via CURLOPT_SSLVERSION");
+ return result;
+ }
+ if(max == CURL_SSLVERSION_MAX_NONE)
+ sslver->max = sslver->min;
+ }
+
+ switch(max) {
+ case CURL_SSLVERSION_MAX_NONE:
+ case CURL_SSLVERSION_MAX_DEFAULT:
+ break;
+ default:
+ result = nss_sslver_from_curl(&sslver->max, max >> 16);
+ if(result) {
+ failf(data, "unsupported max version passed via CURLOPT_SSLVERSION");
+ return result;
+ }
+ }
- failf(data, "TLS minor version cannot be set");
- return CURLE_SSL_CONNECT_ERROR;
+ return CURLE_OK;
}
static CURLcode nss_fail_connect(struct ssl_connect_data *connssl,
}
/* cleanup on connection failure */
- Curl_llist_destroy(connssl->obj_list, NULL);
- connssl->obj_list = NULL;
+ Curl_llist_destroy(&BACKEND->obj_list, NULL);
return curlerr;
}
sock_opt.option = PR_SockOpt_Nonblocking;
sock_opt.value.non_blocking = !blocking;
- if(PR_SetSocketOption(connssl->handle, &sock_opt) != PR_SUCCESS)
+ if(PR_SetSocketOption(BACKEND->handle, &sock_opt) != PR_SUCCESS)
return nss_fail_connect(connssl, data, CURLE_SSL_CONNECT_ERROR);
return CURLE_OK;
SSL_LIBRARY_VERSION_TLS_1_0 /* max */
};
- connssl->data = data;
+ BACKEND->data = data;
/* list of all NSS objects we need to destroy in Curl_nss_close() */
- connssl->obj_list = Curl_llist_alloc(nss_destroy_object);
- if(!connssl->obj_list)
- return CURLE_OUT_OF_MEMORY;
+ Curl_llist_init(&BACKEND->obj_list, nss_destroy_object);
/* FIXME. NSS doesn't support multiple databases open at the same time. */
PR_Lock(nss_initlock);
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)
goto error;
/* do not use SSL cache if disabled or we are not going to verify peer */
- ssl_no_cache = (data->set.general_ssl.sessionid
+ ssl_no_cache = (SSL_SET_OPTION(primary.sessionid)
&& SSL_CONN_CONFIG(verifypeer)) ? PR_FALSE : PR_TRUE;
if(SSL_OptionSet(model, SSL_NO_CACHE, ssl_no_cache) != SECSuccess)
goto error;
if(SSL_HandshakeCallback(model, HandshakeCallback, conn) != SECSuccess)
goto error;
- if(SSL_CONN_CONFIG(verifypeer)) {
+ {
const CURLcode rv = nss_load_ca_certificates(conn, sockindex);
- if(rv) {
+ if((rv == CURLE_SSL_CACERT_BADFILE) && !SSL_CONN_CONFIG(verifypeer))
+ /* not a fatal error because we are not going to verify the peer */
+ infof(data, "warning: CA certificates failed to load\n");
+ else if(rv) {
result = rv;
goto error;
}
char *nickname = dup_nickname(data, SSL_SET_OPTION(cert));
if(nickname) {
/* we are not going to use libnsspem.so to read the client cert */
- connssl->obj_clicert = NULL;
+ BACKEND->obj_clicert = NULL;
}
else {
CURLcode rv = cert_stuff(conn, sockindex, SSL_SET_OPTION(cert),
}
/* store the nickname for SelectClientCert() called during handshake */
- connssl->client_nickname = nickname;
+ BACKEND->client_nickname = nickname;
}
else
- connssl->client_nickname = NULL;
+ BACKEND->client_nickname = NULL;
if(SSL_GetClientAuthDataHook(model, SelectClientCert,
(void *)connssl) != SECSuccess) {
if(conn->proxy_ssl[sockindex].use) {
DEBUGASSERT(ssl_connection_complete == conn->proxy_ssl[sockindex].state);
- DEBUGASSERT(conn->proxy_ssl[sockindex].handle != NULL);
- nspr_io = conn->proxy_ssl[sockindex].handle;
+ DEBUGASSERT(conn->proxy_ssl[sockindex].backend->handle != NULL);
+ nspr_io = conn->proxy_ssl[sockindex].backend->handle;
second_layer = TRUE;
}
else {
}
/* import our model socket onto the current I/O stack */
- connssl->handle = SSL_ImportFD(model, nspr_io);
- if(!connssl->handle) {
+ BACKEND->handle = SSL_ImportFD(model, nspr_io);
+ if(!BACKEND->handle) {
if(!second_layer)
PR_Close(nspr_io);
goto error;
/* This is the password associated with the cert that we're using */
if(SSL_SET_OPTION(key_passwd)) {
- SSL_SetPKCS11PinArg(connssl->handle, SSL_SET_OPTION(key_passwd));
+ SSL_SetPKCS11PinArg(BACKEND->handle, SSL_SET_OPTION(key_passwd));
}
#ifdef SSL_ENABLE_OCSP_STAPLING
if(SSL_CONN_CONFIG(verifystatus)) {
- if(SSL_OptionSet(connssl->handle, SSL_ENABLE_OCSP_STAPLING, PR_TRUE)
+ if(SSL_OptionSet(BACKEND->handle, SSL_ENABLE_OCSP_STAPLING, PR_TRUE)
!= SECSuccess)
goto error;
}
#endif
#ifdef SSL_ENABLE_NPN
- if(SSL_OptionSet(connssl->handle, SSL_ENABLE_NPN, conn->bits.tls_enable_npn
+ if(SSL_OptionSet(BACKEND->handle, SSL_ENABLE_NPN, conn->bits.tls_enable_npn
? PR_TRUE : PR_FALSE) != SECSuccess)
goto error;
#endif
#ifdef SSL_ENABLE_ALPN
- if(SSL_OptionSet(connssl->handle, SSL_ENABLE_ALPN, conn->bits.tls_enable_alpn
+ if(SSL_OptionSet(BACKEND->handle, SSL_ENABLE_ALPN, conn->bits.tls_enable_alpn
? PR_TRUE : PR_FALSE) != SECSuccess)
goto error;
#endif
#if NSSVERNUM >= 0x030f04 /* 3.15.4 */
if(data->set.ssl.falsestart) {
- if(SSL_OptionSet(connssl->handle, SSL_ENABLE_FALSE_START, PR_TRUE)
+ if(SSL_OptionSet(BACKEND->handle, SSL_ENABLE_FALSE_START, PR_TRUE)
!= SECSuccess)
goto error;
- if(SSL_SetCanFalseStartCallback(connssl->handle, CanFalseStartCallback,
+ if(SSL_SetCanFalseStartCallback(BACKEND->handle, CanFalseStartCallback,
conn) != SECSuccess)
goto error;
}
unsigned char protocols[128];
#ifdef USE_NGHTTP2
- if(data->set.httpversion >= CURL_HTTP_VERSION_2) {
+ if(data->set.httpversion >= CURL_HTTP_VERSION_2 &&
+ (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy)) {
protocols[cur++] = NGHTTP2_PROTO_VERSION_ID_LEN;
memcpy(&protocols[cur], NGHTTP2_PROTO_VERSION_ID,
NGHTTP2_PROTO_VERSION_ID_LEN);
memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
cur += ALPN_HTTP_1_1_LENGTH;
- if(SSL_SetNextProtoNego(connssl->handle, protocols, cur) != SECSuccess)
+ if(SSL_SetNextProtoNego(BACKEND->handle, protocols, cur) != SECSuccess)
goto error;
}
#endif
/* Force handshake on next I/O */
- if(SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE)
+ if(SSL_ResetHandshake(BACKEND->handle, /* asServer */ PR_FALSE)
!= SECSuccess)
goto error;
/* propagate hostname to the TLS layer */
- if(SSL_SetURL(connssl->handle, SSL_IS_PROXY() ? conn->http_proxy.host.name :
+ if(SSL_SetURL(BACKEND->handle, SSL_IS_PROXY() ? conn->http_proxy.host.name :
conn->host.name) != SECSuccess)
goto error;
/* prevent NSS from re-using the session for a different hostname */
- if(SSL_SetSockPeerID(connssl->handle, SSL_IS_PROXY() ?
+ if(SSL_SetSockPeerID(BACKEND->handle, SSL_IS_PROXY() ?
conn->http_proxy.host.name : conn->host.name)
!= SECSuccess)
goto error;
/* check timeout situation */
- const long time_left = Curl_timeleft(data, NULL, TRUE);
- if(time_left < 0L) {
+ const time_t time_left = Curl_timeleft(data, NULL, TRUE);
+ if(time_left < 0) {
failf(data, "timed out before SSL handshake");
result = CURLE_OPERATION_TIMEDOUT;
goto error;
/* Force the handshake now */
timeout = PR_MillisecondsToInterval((PRUint32) time_left);
- if(SSL_ForceHandshakeWithTimeout(connssl->handle, timeout) != SECSuccess) {
+ if(SSL_ForceHandshakeWithTimeout(BACKEND->handle, timeout) != SECSuccess) {
if(PR_GetError() == PR_WOULD_BLOCK_ERROR)
/* blocking direction is updated by nss_update_connecting_state() */
return CURLE_AGAIN;
goto error;
}
- result = display_conn_info(conn, connssl->handle);
+ result = display_conn_info(conn, BACKEND->handle);
if(result)
goto error;
char *nickname = dup_nickname(data, SSL_SET_OPTION(issuercert));
if(nickname) {
/* we support only nicknames in case of issuercert for now */
- ret = check_issuer_cert(connssl->handle, nickname);
+ ret = check_issuer_cert(BACKEND->handle, nickname);
free(nickname);
}
return CURLE_OK;
}
-CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
+static CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
{
return nss_connect_common(conn, sockindex, /* blocking */ NULL);
}
-CURLcode Curl_nss_connect_nonblocking(struct connectdata *conn,
- int sockindex, bool *done)
+static CURLcode Curl_nss_connect_nonblocking(struct connectdata *conn,
+ int sockindex, bool *done)
{
return nss_connect_common(conn, sockindex, done);
}
size_t len, /* amount to write */
CURLcode *curlcode)
{
- ssize_t rc = PR_Send(conn->ssl[sockindex].handle, mem, (int)len, 0,
- PR_INTERVAL_NO_WAIT);
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ ssize_t rc;
+
+ /* The SelectClientCert() hook uses this for infof() and failf() but the
+ handle stored in nss_setup_connect() could have already been freed. */
+ BACKEND->data = conn->data;
+
+ rc = PR_Send(BACKEND->handle, mem, (int)len, 0, PR_INTERVAL_NO_WAIT);
if(rc < 0) {
PRInt32 err = PR_GetError();
if(err == PR_WOULD_BLOCK_ERROR)
return rc; /* number of bytes */
}
-static ssize_t nss_recv(struct connectdata * conn, /* connection data */
- int num, /* socketindex */
+static ssize_t nss_recv(struct connectdata *conn, /* connection data */
+ int sockindex, /* socketindex */
char *buf, /* store read data here */
size_t buffersize, /* max amount to read */
CURLcode *curlcode)
{
- ssize_t nread = PR_Recv(conn->ssl[num].handle, buf, (int)buffersize, 0,
- PR_INTERVAL_NO_WAIT);
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ ssize_t nread;
+
+ /* The SelectClientCert() hook uses this for infof() and failf() but the
+ handle stored in nss_setup_connect() could have already been freed. */
+ BACKEND->data = conn->data;
+
+ nread = PR_Recv(BACKEND->handle, buf, (int)buffersize, 0,
+ PR_INTERVAL_NO_WAIT);
if(nread < 0) {
/* failed SSL read */
PRInt32 err = PR_GetError();
return nread;
}
-size_t Curl_nss_version(char *buffer, size_t size)
+static size_t Curl_nss_version(char *buffer, size_t size)
{
return snprintf(buffer, size, "NSS/%s", NSS_VERSION);
}
/* data might be NULL */
-int Curl_nss_seed(struct Curl_easy *data)
+static int Curl_nss_seed(struct Curl_easy *data)
{
/* make sure that NSS is initialized */
return !!Curl_nss_force_init(data);
}
/* data might be NULL */
-CURLcode Curl_nss_random(struct Curl_easy *data,
- unsigned char *entropy,
- size_t length)
+static CURLcode Curl_nss_random(struct Curl_easy *data,
+ unsigned char *entropy,
+ size_t length)
{
Curl_nss_seed(data); /* Initiate the seed if not already done */
return CURLE_OK;
}
-void Curl_nss_md5sum(unsigned char *tmp, /* input */
- size_t tmplen,
- unsigned char *md5sum, /* output */
- size_t md5len)
+static CURLcode Curl_nss_md5sum(unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *md5sum, /* output */
+ size_t md5len)
{
PK11Context *MD5pw = PK11_CreateDigestContext(SEC_OID_MD5);
unsigned int MD5out;
PK11_DigestOp(MD5pw, tmp, curlx_uztoui(tmplen));
PK11_DigestFinal(MD5pw, md5sum, &MD5out, curlx_uztoui(md5len));
PK11_DestroyContext(MD5pw, PR_TRUE);
+
+ return CURLE_OK;
}
-void Curl_nss_sha256sum(const unsigned char *tmp, /* input */
- size_t tmplen,
- unsigned char *sha256sum, /* output */
- size_t sha256len)
+static void Curl_nss_sha256sum(const unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *sha256sum, /* output */
+ size_t sha256len)
{
PK11Context *SHA256pw = PK11_CreateDigestContext(SEC_OID_SHA256);
unsigned int SHA256out;
PK11_DestroyContext(SHA256pw, PR_TRUE);
}
-bool Curl_nss_cert_status_request(void)
+static bool Curl_nss_cert_status_request(void)
{
#ifdef SSL_ENABLE_OCSP_STAPLING
return TRUE;
#endif
}
-bool Curl_nss_false_start(void)
+static bool Curl_nss_false_start(void)
{
#if NSSVERNUM >= 0x030f04 /* 3.15.4 */
return TRUE;
#endif
}
+static void *Curl_nss_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
+{
+ (void)info;
+ return BACKEND->handle;
+}
+
+const struct Curl_ssl Curl_ssl_nss = {
+ { CURLSSLBACKEND_NSS, "nss" }, /* info */
+
+ 1, /* have_ca_path */
+ 1, /* have_certinfo */
+ 1, /* have_pinnedpubkey */
+ 0, /* have_ssl_ctx */
+ 1, /* support_https_proxy */
+
+ sizeof(struct ssl_backend_data),
+
+ Curl_nss_init, /* init */
+ Curl_nss_cleanup, /* cleanup */
+ Curl_nss_version, /* version */
+ Curl_nss_check_cxn, /* check_cxn */
+ /* NSS has no shutdown function provided and thus always fail */
+ Curl_none_shutdown, /* shutdown */
+ Curl_none_data_pending, /* data_pending */
+ Curl_nss_random, /* random */
+ Curl_nss_cert_status_request, /* cert_status_request */
+ Curl_nss_connect, /* connect */
+ Curl_nss_connect_nonblocking, /* connect_nonblocking */
+ Curl_nss_get_internals, /* get_internals */
+ Curl_nss_close, /* close_one */
+ Curl_none_close_all, /* close_all */
+ /* NSS has its own session ID cache */
+ Curl_none_session_free, /* session_free */
+ Curl_none_set_engine, /* set_engine */
+ Curl_none_set_engine_default, /* set_engine_default */
+ Curl_none_engines_list, /* engines_list */
+ Curl_nss_false_start, /* false_start */
+ Curl_nss_md5sum, /* md5sum */
+ Curl_nss_sha256sum /* sha256sum */
+};
+
#endif /* USE_NSS */