nss: avoid memory leaks and failure of NSS shutdown
... in case more than one CA is loaded. Bug: https://bugzilla.redhat.com/670802
This commit is contained in:
190
lib/nss.c
190
lib/nss.c
@@ -42,6 +42,7 @@
|
|||||||
#include "strequal.h"
|
#include "strequal.h"
|
||||||
#include "select.h"
|
#include "select.h"
|
||||||
#include "sslgen.h"
|
#include "sslgen.h"
|
||||||
|
#include "llist.h"
|
||||||
|
|
||||||
#define _MPRINTF_REPLACE /* use the internal *printf() functions */
|
#define _MPRINTF_REPLACE /* use the internal *printf() functions */
|
||||||
#include <curl/mprintf.h>
|
#include <curl/mprintf.h>
|
||||||
@@ -90,8 +91,12 @@ typedef struct {
|
|||||||
PRInt32 version; /* protocol version valid for this cipher */
|
PRInt32 version; /* protocol version valid for this cipher */
|
||||||
} cipher_s;
|
} cipher_s;
|
||||||
|
|
||||||
#define PK11_SETATTRS(x,id,v,l) (x)->type = (id); \
|
#define PK11_SETATTRS(_attr, _idx, _type, _val, _len) do { \
|
||||||
(x)->pValue=(v); (x)->ulValueLen = (l)
|
CK_ATTRIBUTE *ptr = (_attr) + ((_idx)++); \
|
||||||
|
ptr->type = (_type); \
|
||||||
|
ptr->pValue = (_val); \
|
||||||
|
ptr->ulValueLen = (_len); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
#define CERT_NewTempCertificate __CERT_NewTempCertificate
|
#define CERT_NewTempCertificate __CERT_NewTempCertificate
|
||||||
|
|
||||||
@@ -308,18 +313,73 @@ static char *fmt_nickname(struct SessionHandle *data, enum dupstring cert_kind,
|
|||||||
return aprintf("PEM Token #%d:%s", 1, n);
|
return aprintf("PEM Token #%d:%s", 1, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_PK11_CREATEGENERICOBJECT
|
||||||
|
/* 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,
|
||||||
|
CK_OBJECT_CLASS obj_class,
|
||||||
|
const char *filename, bool cacert)
|
||||||
|
{
|
||||||
|
PK11SlotInfo *slot;
|
||||||
|
PK11GenericObject *obj;
|
||||||
|
CK_BBOOL cktrue = CK_TRUE;
|
||||||
|
CK_BBOOL ckfalse = CK_FALSE;
|
||||||
|
CK_ATTRIBUTE attrs[/* max count of attributes */ 4];
|
||||||
|
int attr_cnt = 0;
|
||||||
|
|
||||||
|
const int slot_id = (cacert) ? 0 : 1;
|
||||||
|
char *slot_name = aprintf("PEM Token #%d", slot_id);
|
||||||
|
if(!slot_name)
|
||||||
|
return CURLE_OUT_OF_MEMORY;
|
||||||
|
|
||||||
|
slot = PK11_FindSlotByName(slot_name);
|
||||||
|
free(slot_name);
|
||||||
|
if(!slot)
|
||||||
|
return CURLE_SSL_CERTPROBLEM;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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_FreeSlot(slot);
|
||||||
|
if(!obj)
|
||||||
|
return CURLE_SSL_CERTPROBLEM;
|
||||||
|
|
||||||
|
if(!Curl_llist_insert_next(ssl->obj_list, ssl->obj_list->tail, obj)) {
|
||||||
|
PK11_DestroyGenericObject(obj);
|
||||||
|
return CURLE_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CURLE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Destroy the NSS object whose handle is given by ptr. This function is
|
||||||
|
* a callback of Curl_llist_alloc() used by Curl_llist_destroy() to destroy
|
||||||
|
* NSS objects in Curl_nss_close() */
|
||||||
|
static void nss_destroy_object(void *user, void *ptr)
|
||||||
|
{
|
||||||
|
PK11GenericObject *obj = (PK11GenericObject *)ptr;
|
||||||
|
(void) user;
|
||||||
|
PK11_DestroyGenericObject(obj);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int nss_load_cert(struct ssl_connect_data *ssl,
|
static int nss_load_cert(struct ssl_connect_data *ssl,
|
||||||
const char *filename, PRBool cacert)
|
const char *filename, PRBool cacert)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_PK11_CREATEGENERICOBJECT
|
#ifdef HAVE_PK11_CREATEGENERICOBJECT
|
||||||
CK_SLOT_ID slotID;
|
/* All CA and trust objects go into slot 0. Other slots are used
|
||||||
PK11SlotInfo * slot = NULL;
|
* for storing certificates.
|
||||||
CK_ATTRIBUTE *attrs;
|
*/
|
||||||
CK_ATTRIBUTE theTemplate[20];
|
const int slot_id = (cacert) ? 0 : 1;
|
||||||
CK_BBOOL cktrue = CK_TRUE;
|
|
||||||
CK_BBOOL ckfalse = CK_FALSE;
|
|
||||||
CK_OBJECT_CLASS objClass = CKO_CERTIFICATE;
|
|
||||||
char slotname[SLOTSIZE];
|
|
||||||
#endif
|
#endif
|
||||||
CERTCertificate *cert;
|
CERTCertificate *cert;
|
||||||
char *nickname = NULL;
|
char *nickname = NULL;
|
||||||
@@ -346,57 +406,15 @@ static int nss_load_cert(struct ssl_connect_data *ssl,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_PK11_CREATEGENERICOBJECT
|
#ifdef HAVE_PK11_CREATEGENERICOBJECT
|
||||||
attrs = theTemplate;
|
nickname = aprintf("PEM Token #%d:%s", slot_id, n);
|
||||||
|
|
||||||
/* All CA and trust objects go into slot 0. Other slots are used
|
|
||||||
* for storing certificates. With each new user certificate we increment
|
|
||||||
* the slot count. We only support 1 user certificate right now.
|
|
||||||
*/
|
|
||||||
if(cacert)
|
|
||||||
slotID = 0;
|
|
||||||
else
|
|
||||||
slotID = 1;
|
|
||||||
|
|
||||||
snprintf(slotname, SLOTSIZE, "PEM Token #%ld", slotID);
|
|
||||||
|
|
||||||
nickname = aprintf("PEM Token #%ld:%s", slotID, n);
|
|
||||||
if(!nickname)
|
if(!nickname)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
slot = PK11_FindSlotByName(slotname);
|
if(CURLE_OK != nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert)) {
|
||||||
|
|
||||||
if(!slot) {
|
|
||||||
free(nickname);
|
free(nickname);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
PK11_SETATTRS(attrs, CKA_CLASS, &objClass, sizeof(objClass) );
|
|
||||||
attrs++;
|
|
||||||
PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL) );
|
|
||||||
attrs++;
|
|
||||||
PK11_SETATTRS(attrs, CKA_LABEL, (unsigned char *)filename,
|
|
||||||
strlen(filename)+1);
|
|
||||||
attrs++;
|
|
||||||
if(cacert) {
|
|
||||||
PK11_SETATTRS(attrs, CKA_TRUST, &cktrue, sizeof(CK_BBOOL) );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
PK11_SETATTRS(attrs, CKA_TRUST, &ckfalse, sizeof(CK_BBOOL) );
|
|
||||||
}
|
|
||||||
attrs++;
|
|
||||||
|
|
||||||
/* This load the certificate in our PEM module into the appropriate
|
|
||||||
* slot.
|
|
||||||
*/
|
|
||||||
ssl->cacert[slotID] = PK11_CreateGenericObject(slot, theTemplate, 4,
|
|
||||||
PR_FALSE /* isPerm */);
|
|
||||||
|
|
||||||
PK11_FreeSlot(slot);
|
|
||||||
|
|
||||||
if(ssl->cacert[slotID] == NULL) {
|
|
||||||
free(nickname);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
/* We don't have PK11_CreateGenericObject but a file-based cert was passed
|
/* We don't have PK11_CreateGenericObject but a file-based cert was passed
|
||||||
* in. We need to fail.
|
* in. We need to fail.
|
||||||
@@ -516,54 +534,27 @@ static int nss_load_key(struct connectdata *conn, int sockindex,
|
|||||||
char *key_file)
|
char *key_file)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_PK11_CREATEGENERICOBJECT
|
#ifdef HAVE_PK11_CREATEGENERICOBJECT
|
||||||
PK11SlotInfo * slot = NULL;
|
PK11SlotInfo *slot;
|
||||||
CK_ATTRIBUTE *attrs;
|
SECStatus status;
|
||||||
CK_ATTRIBUTE theTemplate[20];
|
struct ssl_connect_data *ssl = conn->ssl;
|
||||||
CK_BBOOL cktrue = CK_TRUE;
|
|
||||||
CK_OBJECT_CLASS objClass = CKO_PRIVATE_KEY;
|
|
||||||
CK_SLOT_ID slotID;
|
|
||||||
char slotname[SLOTSIZE];
|
|
||||||
struct ssl_connect_data *sslconn = &conn->ssl[sockindex];
|
|
||||||
|
|
||||||
attrs = theTemplate;
|
if(CURLE_OK != nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE)) {
|
||||||
|
|
||||||
/* FIXME: grok the various file types */
|
|
||||||
|
|
||||||
slotID = 1; /* hardcoded for now */
|
|
||||||
|
|
||||||
snprintf(slotname, sizeof(slotname), "PEM Token #%ld", slotID);
|
|
||||||
slot = PK11_FindSlotByName(slotname);
|
|
||||||
|
|
||||||
if(!slot)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
PK11_SETATTRS(attrs, CKA_CLASS, &objClass, sizeof(objClass) ); attrs++;
|
|
||||||
PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL) ); attrs++;
|
|
||||||
PK11_SETATTRS(attrs, CKA_LABEL, (unsigned char *)key_file,
|
|
||||||
strlen(key_file)+1); attrs++;
|
|
||||||
|
|
||||||
/* When adding an encrypted key the PKCS#11 will be set as removed */
|
|
||||||
sslconn->key = PK11_CreateGenericObject(slot, theTemplate, 3,
|
|
||||||
PR_FALSE /* isPerm */);
|
|
||||||
if(sslconn->key == NULL) {
|
|
||||||
PR_SetError(SEC_ERROR_BAD_KEY, 0);
|
PR_SetError(SEC_ERROR_BAD_KEY, 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
slot = PK11_FindSlotByName("PEM Token #1");
|
||||||
|
if(!slot)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* This will force the token to be seen as re-inserted */
|
/* This will force the token to be seen as re-inserted */
|
||||||
SECMOD_WaitForAnyTokenEvent(mod, 0, 0);
|
SECMOD_WaitForAnyTokenEvent(mod, 0, 0);
|
||||||
PK11_IsPresent(slot);
|
PK11_IsPresent(slot);
|
||||||
|
|
||||||
/* parg is initialized in nss_Init_Tokens() */
|
status = PK11_Authenticate(slot, PR_TRUE,
|
||||||
if(PK11_Authenticate(slot, PR_TRUE,
|
conn->data->set.str[STRING_KEY_PASSWD]);
|
||||||
conn->data->set.str[STRING_KEY_PASSWD]) != SECSuccess) {
|
|
||||||
|
|
||||||
PK11_FreeSlot(slot);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
PK11_FreeSlot(slot);
|
PK11_FreeSlot(slot);
|
||||||
|
return (SECSuccess == status) ? 1 : 0;
|
||||||
return 1;
|
|
||||||
#else
|
#else
|
||||||
/* If we don't have PK11_CreateGenericObject then we can't load a file-based
|
/* If we don't have PK11_CreateGenericObject then we can't load a file-based
|
||||||
* key.
|
* key.
|
||||||
@@ -1063,12 +1054,8 @@ void Curl_nss_close(struct connectdata *conn, int sockindex)
|
|||||||
connssl->client_nickname = NULL;
|
connssl->client_nickname = NULL;
|
||||||
}
|
}
|
||||||
#ifdef HAVE_PK11_CREATEGENERICOBJECT
|
#ifdef HAVE_PK11_CREATEGENERICOBJECT
|
||||||
if(connssl->key)
|
/* destroy all NSS objects in order to avoid failure of NSS shutdown */
|
||||||
(void)PK11_DestroyGenericObject(connssl->key);
|
Curl_llist_destroy(connssl->obj_list, NULL);
|
||||||
if(connssl->cacert[1])
|
|
||||||
(void)PK11_DestroyGenericObject(connssl->cacert[1]);
|
|
||||||
if(connssl->cacert[0])
|
|
||||||
(void)PK11_DestroyGenericObject(connssl->cacert[0]);
|
|
||||||
#endif
|
#endif
|
||||||
connssl->handle = NULL;
|
connssl->handle = NULL;
|
||||||
}
|
}
|
||||||
@@ -1179,9 +1166,10 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
|
|||||||
connssl->data = data;
|
connssl->data = data;
|
||||||
|
|
||||||
#ifdef HAVE_PK11_CREATEGENERICOBJECT
|
#ifdef HAVE_PK11_CREATEGENERICOBJECT
|
||||||
connssl->cacert[0] = NULL;
|
/* list of all NSS objects we need to destroy in Curl_nss_close() */
|
||||||
connssl->cacert[1] = NULL;
|
connssl->obj_list = Curl_llist_alloc(nss_destroy_object);
|
||||||
connssl->key = NULL;
|
if(!connssl->obj_list)
|
||||||
|
return CURLE_OUT_OF_MEMORY;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* FIXME. NSS doesn't support multiple databases open at the same time. */
|
/* FIXME. NSS doesn't support multiple databases open at the same time. */
|
||||||
|
@@ -271,8 +271,7 @@ struct ssl_connect_data {
|
|||||||
char *client_nickname;
|
char *client_nickname;
|
||||||
struct SessionHandle *data;
|
struct SessionHandle *data;
|
||||||
#ifdef HAVE_PK11_CREATEGENERICOBJECT
|
#ifdef HAVE_PK11_CREATEGENERICOBJECT
|
||||||
PK11GenericObject *key;
|
struct curl_llist *obj_list;
|
||||||
PK11GenericObject *cacert[2];
|
|
||||||
#endif
|
#endif
|
||||||
#endif /* USE_NSS */
|
#endif /* USE_NSS */
|
||||||
#ifdef USE_QSOSSL
|
#ifdef USE_QSOSSL
|
||||||
|
Reference in New Issue
Block a user