nss: big cleanup in nss_load_cert() and cert_stuff()

This commit is contained in:
Kamil Dudka 2011-08-26 15:43:48 +02:00
parent 052a08ff59
commit 06e6755e87

151
lib/nss.c
View File

@ -319,6 +319,9 @@ static CURLcode nss_create_object(struct ssl_connect_data *ssl,
CK_BBOOL ckfalse = CK_FALSE; CK_BBOOL ckfalse = CK_FALSE;
CK_ATTRIBUTE attrs[/* max count of attributes */ 4]; CK_ATTRIBUTE attrs[/* max count of attributes */ 4];
int attr_cnt = 0; int attr_cnt = 0;
CURLcode err = (cacert)
? CURLE_SSL_CACERT_BADFILE
: CURLE_SSL_CERTPROBLEM;
const int slot_id = (cacert) ? 0 : 1; const int slot_id = (cacert) ? 0 : 1;
char *slot_name = aprintf("PEM Token #%d", slot_id); char *slot_name = aprintf("PEM Token #%d", slot_id);
@ -328,7 +331,7 @@ static CURLcode nss_create_object(struct ssl_connect_data *ssl,
slot = PK11_FindSlotByName(slot_name); slot = PK11_FindSlotByName(slot_name);
free(slot_name); free(slot_name);
if(!slot) if(!slot)
return CURLE_SSL_CERTPROBLEM; return err;
PK11_SETATTRS(attrs, attr_cnt, CKA_CLASS, &obj_class, sizeof(obj_class)); 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_TOKEN, &cktrue, sizeof(CK_BBOOL));
@ -343,7 +346,7 @@ static CURLcode nss_create_object(struct ssl_connect_data *ssl,
obj = PK11_CreateGenericObject(slot, attrs, attr_cnt, PR_FALSE); obj = PK11_CreateGenericObject(slot, attrs, attr_cnt, PR_FALSE);
PK11_FreeSlot(slot); PK11_FreeSlot(slot);
if(!obj) if(!obj)
return CURLE_SSL_CERTPROBLEM; return err;
if(!Curl_llist_insert_next(ssl->obj_list, ssl->obj_list->tail, obj)) { if(!Curl_llist_insert_next(ssl->obj_list, ssl->obj_list->tail, obj)) {
PK11_DestroyGenericObject(obj); PK11_DestroyGenericObject(obj);
@ -368,77 +371,21 @@ static void nss_destroy_object(void *user, void *ptr)
} }
#endif #endif
static int nss_load_cert(struct ssl_connect_data *ssl, static CURLcode nss_load_cert(struct ssl_connect_data *ssl,
const char *filename, PRBool cacert) const char *filename, PRBool cacert)
{ {
#ifdef HAVE_PK11_CREATEGENERICOBJECT CURLcode err = (cacert)
/* All CA and trust objects go into slot 0. Other slots are used ? CURLE_SSL_CACERT_BADFILE
* for storing certificates. : CURLE_SSL_CERTPROBLEM;
*/
const int slot_id = (cacert) ? 0 : 1;
#endif
CERTCertificate *cert;
char *nickname = NULL;
char *n = NULL;
/* If there is no slash in the filename it is assumed to be a regular
* NSS nickname.
*/
if(is_file(filename)) {
n = strrchr(filename, '/');
if(n)
n++;
if(!mod)
return 1;
}
else {
/* A nickname from the NSS internal database */
if(cacert)
return 0; /* You can't specify an NSS CA nickname this way */
nickname = strdup(filename);
if(!nickname)
return 0;
goto done;
}
#ifdef HAVE_PK11_CREATEGENERICOBJECT #ifdef HAVE_PK11_CREATEGENERICOBJECT
nickname = aprintf("PEM Token #%d:%s", slot_id, n); /* libnsspem.so leaks memory if the requested file does not exist. For more
if(!nickname) * details, go to <https://bugzilla.redhat.com/734760>. */
return 0; if(is_file(filename))
return nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert);
if(CURLE_OK != nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert)) {
free(nickname);
return 0;
}
#else
/* We don't have PK11_CreateGenericObject but a file-based cert was passed
* in. We need to fail.
*/
return 0;
#endif #endif
done: return err;
/* Double-check that the certificate or nickname requested exists in
* either the token or the NSS certificate database.
*/
if(!cacert) {
cert = PK11_FindCertFromNickname((char *)nickname, NULL);
/* An invalid nickname was passed in */
if(cert == NULL) {
free(nickname);
PR_SetError(SEC_ERROR_UNKNOWN_CERT, 0);
return 0;
}
CERT_DestroyCertificate(cert);
}
free(nickname);
return 1;
} }
/* add given CRL to cache if it is not already there */ /* add given CRL to cache if it is not already there */
@ -527,23 +474,23 @@ fail:
return SECFailure; return SECFailure;
} }
static int nss_load_key(struct connectdata *conn, int sockindex, static CURLcode nss_load_key(struct connectdata *conn, int sockindex,
char *key_file) char *key_file)
{ {
#ifdef HAVE_PK11_CREATEGENERICOBJECT #ifdef HAVE_PK11_CREATEGENERICOBJECT
PK11SlotInfo *slot; PK11SlotInfo *slot;
SECStatus status; SECStatus status;
struct ssl_connect_data *ssl = conn->ssl; struct ssl_connect_data *ssl = conn->ssl;
(void)sockindex; /* unused */
if(CURLE_OK != nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE)) { CURLcode rv = nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE);
if(CURLE_OK != rv) {
PR_SetError(SEC_ERROR_BAD_KEY, 0); PR_SetError(SEC_ERROR_BAD_KEY, 0);
return 0; return rv;
} }
slot = PK11_FindSlotByName("PEM Token #1"); slot = PK11_FindSlotByName("PEM Token #1");
if(!slot) if(!slot)
return 0; return CURLE_SSL_CERTPROBLEM;
/* 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);
@ -552,16 +499,18 @@ static int nss_load_key(struct connectdata *conn, int sockindex,
status = PK11_Authenticate(slot, PR_TRUE, status = PK11_Authenticate(slot, PR_TRUE,
conn->data->set.str[STRING_KEY_PASSWD]); conn->data->set.str[STRING_KEY_PASSWD]);
PK11_FreeSlot(slot); PK11_FreeSlot(slot);
return (SECSuccess == status) ? 1 : 0; return (SECSuccess == status)
? CURLE_OK
: CURLE_SSL_CERTPROBLEM;
#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.
*/ */
(void)conn; /* unused */ (void)conn; /* unused */
(void)key_file; /* unused */ (void)key_file; /* unused */
(void)sockindex; /* unused */ return CURLE_SSL_CERTPROBLEM;
return 0;
#endif #endif
(void)sockindex; /* unused */
} }
static int display_error(struct connectdata *conn, PRInt32 err, static int display_error(struct connectdata *conn, PRInt32 err,
@ -580,34 +529,37 @@ static int display_error(struct connectdata *conn, PRInt32 err,
return 0; /* The caller will print a generic error */ return 0; /* The caller will print a generic error */
} }
static int cert_stuff(struct connectdata *conn, static CURLcode cert_stuff(struct connectdata *conn, int sockindex,
int sockindex, char *cert_file, char *key_file) char *cert_file, char *key_file)
{ {
struct SessionHandle *data = conn->data; struct SessionHandle *data = conn->data;
int rv = 0; CURLcode rv;
if(cert_file) { if(cert_file) {
rv = nss_load_cert(&conn->ssl[sockindex], cert_file, PR_FALSE); rv = nss_load_cert(&conn->ssl[sockindex], cert_file, PR_FALSE);
if(!rv) { if(CURLE_OK != rv) {
if(!display_error(conn, PR_GetError(), cert_file)) if(!display_error(conn, PR_GetError(), cert_file))
failf(data, "Unable to load client cert %d.", PR_GetError()); failf(data, "Unable to load client cert %d.", PR_GetError());
return 0;
return rv;
} }
} }
if(key_file || (is_file(cert_file))) { if(key_file || (is_file(cert_file))) {
if(key_file) if(key_file)
rv = nss_load_key(conn, sockindex, key_file); rv = nss_load_key(conn, sockindex, key_file);
else else
/* In case the cert file also has the key */ /* In case the cert file also has the key */
rv = nss_load_key(conn, sockindex, cert_file); rv = nss_load_key(conn, sockindex, cert_file);
if(!rv) { if(CURLE_OK != rv) {
if(!display_error(conn, PR_GetError(), key_file)) if(!display_error(conn, PR_GetError(), key_file))
failf(data, "Unable to load client key %d.", PR_GetError()); failf(data, "Unable to load client key %d.", PR_GetError());
return 0; return rv;
} }
} }
return 1;
return CURLE_OK;
} }
static char * nss_get_password(PK11SlotInfo * slot, PRBool retry, void *arg) static char * nss_get_password(PK11SlotInfo * slot, PRBool retry, void *arg)
@ -774,7 +726,6 @@ static SECStatus check_issuer_cert(PRFileDesc *sock,
cert_issuer = CERT_FindCertIssuer(cert,PR_Now(),certUsageObjectSigner); cert_issuer = CERT_FindCertIssuer(cert,PR_Now(),certUsageObjectSigner);
proto_win = SSL_RevealPinArg(sock); proto_win = SSL_RevealPinArg(sock);
issuer = NULL;
issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win); issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win);
if((!cert_issuer) || (!issuer)) if((!cert_issuer) || (!issuer))
@ -1132,8 +1083,11 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn,
const char *cafile = data->set.ssl.CAfile; const char *cafile = data->set.ssl.CAfile;
const char *capath = data->set.ssl.CApath; const char *capath = data->set.ssl.CApath;
if(cafile && !nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE)) if(cafile) {
return CURLE_SSL_CACERT_BADFILE; CURLcode rv = nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE);
if(CURLE_OK != rv)
return rv;
}
if(capath) { if(capath) {
struct_stat st; struct_stat st;
@ -1153,7 +1107,7 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn,
return CURLE_OUT_OF_MEMORY; return CURLE_OUT_OF_MEMORY;
} }
if(!nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE)) if(CURLE_OK != nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE))
/* This is purposefully tolerant of errors so non-PEM files can /* This is purposefully tolerant of errors so non-PEM files can
* be in the same directory */ * be in the same directory */
infof(data, "failed to load '%s' from CURLOPT_CAPATH\n", fullpath); infof(data, "failed to load '%s' from CURLOPT_CAPATH\n", fullpath);
@ -1349,10 +1303,21 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
if(data->set.str[STRING_CERT]) { if(data->set.str[STRING_CERT]) {
char *nickname = dup_nickname(data, STRING_CERT); char *nickname = dup_nickname(data, STRING_CERT);
if(!nickname && !cert_stuff(conn, sockindex, data->set.str[STRING_CERT], if(nickname) {
data->set.str[STRING_KEY])) /* we are not going to use libnsspem.so to read the client cert */
/* failf() is already done in cert_stuff() */ #ifdef HAVE_PK11_CREATEGENERICOBJECT
return CURLE_SSL_CERTPROBLEM; connssl->obj_clicert = NULL;
#endif
}
else {
CURLcode rv = cert_stuff(conn, sockindex, data->set.str[STRING_CERT],
data->set.str[STRING_KEY]);
if(CURLE_OK != rv) {
/* failf() is already done in cert_stuff() */
curlerr = rv;
goto error;
}
}
/* store the nickname for SelectClientCert() called during handshake */ /* store the nickname for SelectClientCert() called during handshake */
connssl->client_nickname = nickname; connssl->client_nickname = nickname;