mirror of
https://github.com/moparisthebest/curl
synced 2024-11-11 20:15:03 -05:00
- Claes Jakobsson improved the support for client certificates handling
in NSS-powered libcurl. Now the client certificates can be selected automatically by a NSS built-in hook. Additionally pre-login to all PKCS11 slots is no more performed. It used to cause problems with HW tokens. - Fixed reference counting for NSS client certificates. Now the PEM reader module should be always properly unloaded on Curl_nss_cleanup(). If the unload fails though, libcurl will try to reuse the already loaded instance.
This commit is contained in:
parent
95c2ab77e7
commit
5f0cae8037
10
CHANGES
10
CHANGES
@ -6,6 +6,16 @@
|
|||||||
|
|
||||||
Changelog
|
Changelog
|
||||||
|
|
||||||
|
Kamil Dudka (20 Jul 2009)
|
||||||
|
- Claes Jakobsson improved the support for client certificates handling
|
||||||
|
in NSS-powered libcurl. Now the client certificates can be selected
|
||||||
|
automatically by a NSS built-in hook. Additionally pre-login to all PKCS11
|
||||||
|
slots is no more performed. It used to cause problems with HW tokens.
|
||||||
|
|
||||||
|
- Fixed reference counting for NSS client certificates. Now the PEM reader
|
||||||
|
module should be always properly unloaded on Curl_nss_cleanup(). If the unload
|
||||||
|
fails though, libcurl will try to reuse the already loaded instance.
|
||||||
|
|
||||||
Daniel Fandrich (15 Jul 2009)
|
Daniel Fandrich (15 Jul 2009)
|
||||||
- Added nonblock.c to the non-automake makefiles (note that the dependencies
|
- Added nonblock.c to the non-automake makefiles (note that the dependencies
|
||||||
in the Watcom makefiles aren't quite correct).
|
in the Watcom makefiles aren't quite correct).
|
||||||
|
221
lib/nss.c
221
lib/nss.c
@ -585,48 +585,6 @@ static char * nss_get_password(PK11SlotInfo * slot, PRBool retry, void *arg)
|
|||||||
return (char *)PORT_Strdup((char *)arg);
|
return (char *)PORT_Strdup((char *)arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static SECStatus nss_Init_Tokens(struct connectdata * conn)
|
|
||||||
{
|
|
||||||
PK11SlotList *slotList;
|
|
||||||
PK11SlotListElement *listEntry;
|
|
||||||
SECStatus ret, status = SECSuccess;
|
|
||||||
|
|
||||||
PK11_SetPasswordFunc(nss_get_password);
|
|
||||||
|
|
||||||
slotList =
|
|
||||||
PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_TRUE, NULL);
|
|
||||||
|
|
||||||
for(listEntry = PK11_GetFirstSafe(slotList);
|
|
||||||
listEntry; listEntry = listEntry->next) {
|
|
||||||
PK11SlotInfo *slot = listEntry->slot;
|
|
||||||
|
|
||||||
if(PK11_NeedLogin(slot) && PK11_NeedUserInit(slot)) {
|
|
||||||
if(slot == PK11_GetInternalKeySlot()) {
|
|
||||||
failf(conn->data, "The NSS database has not been initialized");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
failf(conn->data, "The token %s has not been initialized",
|
|
||||||
PK11_GetTokenName(slot));
|
|
||||||
}
|
|
||||||
PK11_FreeSlot(slot);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = PK11_Authenticate(slot, PR_TRUE,
|
|
||||||
conn->data->set.str[STRING_KEY_PASSWD]);
|
|
||||||
if(SECSuccess != ret) {
|
|
||||||
if(PR_GetError() == SEC_ERROR_BAD_PASSWORD)
|
|
||||||
infof(conn->data, "The password for token '%s' is incorrect\n",
|
|
||||||
PK11_GetTokenName(slot));
|
|
||||||
status = SECFailure;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
PK11_FreeSlot(slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SECStatus BadCertHandler(void *arg, PRFileDesc *sock)
|
static SECStatus BadCertHandler(void *arg, PRFileDesc *sock)
|
||||||
{
|
{
|
||||||
SECStatus success = SECSuccess;
|
SECStatus success = SECSuccess;
|
||||||
@ -692,15 +650,37 @@ static SECStatus HandshakeCallback(PRFileDesc *sock, void *arg)
|
|||||||
return SECSuccess;
|
return SECSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void display_cert_info(struct SessionHandle *data, CERTCertificate *cert) {
|
||||||
|
char *subject, *issuer, *common_name;
|
||||||
|
PRExplodedTime printableTime;
|
||||||
|
char timeString[256];
|
||||||
|
PRTime notBefore, notAfter;
|
||||||
|
|
||||||
|
subject = CERT_NameToAscii(&cert->subject);
|
||||||
|
issuer = CERT_NameToAscii(&cert->issuer);
|
||||||
|
common_name = CERT_GetCommonName(&cert->subject);
|
||||||
|
infof(data, "\tsubject: %s\n", subject);
|
||||||
|
|
||||||
|
CERT_GetCertTimes(cert, ¬Before, ¬After);
|
||||||
|
PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime);
|
||||||
|
PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime);
|
||||||
|
infof(data, "\tstart date: %s\n", timeString);
|
||||||
|
PR_ExplodeTime(notAfter, PR_GMTParameters, &printableTime);
|
||||||
|
PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime);
|
||||||
|
infof(data, "\texpire date: %s\n", timeString);
|
||||||
|
infof(data, "\tcommon name: %s\n", common_name);
|
||||||
|
infof(data, "\tissuer: %s\n", issuer);
|
||||||
|
|
||||||
|
PR_Free(subject);
|
||||||
|
PR_Free(issuer);
|
||||||
|
PR_Free(common_name);
|
||||||
|
}
|
||||||
|
|
||||||
static void display_conn_info(struct connectdata *conn, PRFileDesc *sock)
|
static void display_conn_info(struct connectdata *conn, PRFileDesc *sock)
|
||||||
{
|
{
|
||||||
SSLChannelInfo channel;
|
SSLChannelInfo channel;
|
||||||
SSLCipherSuiteInfo suite;
|
SSLCipherSuiteInfo suite;
|
||||||
CERTCertificate *cert;
|
CERTCertificate *cert;
|
||||||
char *subject, *issuer, *common_name;
|
|
||||||
PRExplodedTime printableTime;
|
|
||||||
char timeString[256];
|
|
||||||
PRTime notBefore, notAfter;
|
|
||||||
|
|
||||||
if(SSL_GetChannelInfo(sock, &channel, sizeof channel) ==
|
if(SSL_GetChannelInfo(sock, &channel, sizeof channel) ==
|
||||||
SECSuccess && channel.length == sizeof channel &&
|
SECSuccess && channel.length == sizeof channel &&
|
||||||
@ -714,25 +694,7 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc *sock)
|
|||||||
infof(conn->data, "Server certificate:\n");
|
infof(conn->data, "Server certificate:\n");
|
||||||
|
|
||||||
cert = SSL_PeerCertificate(sock);
|
cert = SSL_PeerCertificate(sock);
|
||||||
subject = CERT_NameToAscii(&cert->subject);
|
display_cert_info(conn->data, cert);
|
||||||
issuer = CERT_NameToAscii(&cert->issuer);
|
|
||||||
common_name = CERT_GetCommonName(&cert->subject);
|
|
||||||
infof(conn->data, "\tsubject: %s\n", subject);
|
|
||||||
|
|
||||||
CERT_GetCertTimes(cert, ¬Before, ¬After);
|
|
||||||
PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime);
|
|
||||||
PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime);
|
|
||||||
infof(conn->data, "\tstart date: %s\n", timeString);
|
|
||||||
PR_ExplodeTime(notAfter, PR_GMTParameters, &printableTime);
|
|
||||||
PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime);
|
|
||||||
infof(conn->data, "\texpire date: %s\n", timeString);
|
|
||||||
infof(conn->data, "\tcommon name: %s\n", common_name);
|
|
||||||
infof(conn->data, "\tissuer: %s\n", issuer);
|
|
||||||
|
|
||||||
PR_Free(subject);
|
|
||||||
PR_Free(issuer);
|
|
||||||
PR_Free(common_name);
|
|
||||||
|
|
||||||
CERT_DestroyCertificate(cert);
|
CERT_DestroyCertificate(cert);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -786,48 +748,71 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock,
|
|||||||
struct CERTCertificateStr **pRetCert,
|
struct CERTCertificateStr **pRetCert,
|
||||||
struct SECKEYPrivateKeyStr **pRetKey)
|
struct SECKEYPrivateKeyStr **pRetKey)
|
||||||
{
|
{
|
||||||
SECKEYPrivateKey *privKey = NULL;
|
static const char pem_nickname[] = "PEM Token #1";
|
||||||
CERTCertificate *cert;
|
const char *pem_slotname = pem_nickname;
|
||||||
|
|
||||||
struct ssl_connect_data *connssl = (struct ssl_connect_data *)arg;
|
struct ssl_connect_data *connssl = (struct ssl_connect_data *)arg;
|
||||||
char *nickname = connssl->client_nickname;
|
struct SessionHandle *data = connssl->data;
|
||||||
void *proto_win = NULL;
|
const char *nickname = connssl->client_nickname;
|
||||||
SECStatus secStatus = SECFailure;
|
|
||||||
|
if (mod && nickname &&
|
||||||
|
0 == strncmp(nickname, pem_nickname, /* length of "PEM Token" */ 9)) {
|
||||||
|
|
||||||
|
/* use the cert/key provided by PEM reader */
|
||||||
PK11SlotInfo *slot;
|
PK11SlotInfo *slot;
|
||||||
(void)caNames;
|
void *proto_win = SSL_RevealPinArg(sock);
|
||||||
|
*pRetKey = NULL;
|
||||||
|
|
||||||
proto_win = SSL_RevealPinArg(sock);
|
*pRetCert = PK11_FindCertFromNickname(nickname, proto_win);
|
||||||
|
if (NULL == *pRetCert) {
|
||||||
|
failf(data, "NSS: client certificate not found: %s", nickname);
|
||||||
|
return SECFailure;
|
||||||
|
}
|
||||||
|
|
||||||
if(!nickname)
|
slot = PK11_FindSlotByName(pem_slotname);
|
||||||
return secStatus;
|
if (NULL == slot) {
|
||||||
|
failf(data, "NSS: PK11 slot not found: %s", pem_slotname);
|
||||||
|
return SECFailure;
|
||||||
|
}
|
||||||
|
|
||||||
cert = PK11_FindCertFromNickname(nickname, proto_win);
|
*pRetKey = PK11_FindPrivateKeyFromCert(slot, *pRetCert, NULL);
|
||||||
if(cert) {
|
|
||||||
if(!strncmp(nickname, "PEM Token", 9)) {
|
|
||||||
CK_SLOT_ID slotID = 1; /* hardcoded for now */
|
|
||||||
char slotname[SLOTSIZE];
|
|
||||||
snprintf(slotname, SLOTSIZE, "PEM Token #%ld", slotID);
|
|
||||||
slot = PK11_FindSlotByName(slotname);
|
|
||||||
privKey = PK11_FindPrivateKeyFromCert(slot, cert, NULL);
|
|
||||||
PK11_FreeSlot(slot);
|
PK11_FreeSlot(slot);
|
||||||
if(privKey) {
|
if (NULL == *pRetKey) {
|
||||||
secStatus = SECSuccess;
|
failf(data, "NSS: private key not found for certificate: %s", nickname);
|
||||||
}
|
return SECFailure;
|
||||||
}
|
|
||||||
else {
|
|
||||||
privKey = PK11_FindKeyByAnyCert(cert, proto_win);
|
|
||||||
if(privKey)
|
|
||||||
secStatus = SECSuccess;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*pRetCert = cert;
|
infof(data, "NSS: Client client certificate: %s\n", nickname);
|
||||||
*pRetKey = privKey;
|
display_cert_info(data, *pRetCert);
|
||||||
|
return SECSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
/* There's no need to destroy either cert or privKey as
|
/* use the default NSS hook */
|
||||||
* NSS will do that for us even if returning SECFailure
|
if (SECSuccess != NSS_GetClientAuthData((void *)nickname, sock, caNames,
|
||||||
*/
|
pRetCert, pRetKey)
|
||||||
|
|| NULL == *pRetCert) {
|
||||||
|
|
||||||
return secStatus;
|
if (NULL == nickname)
|
||||||
|
failf(data, "NSS: client certificate not found (nickname not specified)");
|
||||||
|
else
|
||||||
|
failf(data, "NSS: client certificate not found: %s", nickname);
|
||||||
|
|
||||||
|
return SECFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get certificate nickname if any */
|
||||||
|
nickname = (*pRetCert)->nickname;
|
||||||
|
if (NULL == nickname)
|
||||||
|
nickname = "[unknown]";
|
||||||
|
|
||||||
|
if (NULL == *pRetKey) {
|
||||||
|
failf(data, "NSS: private key not found for certificate: %s", nickname);
|
||||||
|
return SECFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
infof(data, "NSS: using client certificate: %s\n", nickname);
|
||||||
|
display_cert_info(data, *pRetCert);
|
||||||
|
return SECSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -857,9 +842,15 @@ void Curl_nss_cleanup(void)
|
|||||||
*/
|
*/
|
||||||
PR_Lock(nss_initlock);
|
PR_Lock(nss_initlock);
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
if(mod)
|
/* Free references to client certificates held in the SSL session cache.
|
||||||
|
* Omitting this hampers destruction of the security module owning
|
||||||
|
* the certificates. */
|
||||||
|
SSL_ClearSessionCache();
|
||||||
|
|
||||||
|
if(mod && SECSuccess == SECMOD_UnloadUserModule(mod)) {
|
||||||
SECMOD_DestroyModule(mod);
|
SECMOD_DestroyModule(mod);
|
||||||
mod = NULL;
|
mod = NULL;
|
||||||
|
}
|
||||||
NSS_Shutdown();
|
NSS_Shutdown();
|
||||||
}
|
}
|
||||||
PR_Unlock(nss_initlock);
|
PR_Unlock(nss_initlock);
|
||||||
@ -940,9 +931,6 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
|
|||||||
curl_socket_t sockfd = conn->sock[sockindex];
|
curl_socket_t sockfd = conn->sock[sockindex];
|
||||||
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
|
||||||
SECStatus rv;
|
SECStatus rv;
|
||||||
#ifdef HAVE_PK11_CREATEGENERICOBJECT
|
|
||||||
char *configstring = NULL;
|
|
||||||
#endif
|
|
||||||
char *certDir = NULL;
|
char *certDir = NULL;
|
||||||
int curlerr;
|
int curlerr;
|
||||||
const int *cipher_to_enable;
|
const int *cipher_to_enable;
|
||||||
@ -952,6 +940,8 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
|
|||||||
if (connssl->state == ssl_connection_complete)
|
if (connssl->state == ssl_connection_complete)
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
|
|
||||||
|
connssl->data = data;
|
||||||
|
|
||||||
#ifdef HAVE_PK11_CREATEGENERICOBJECT
|
#ifdef HAVE_PK11_CREATEGENERICOBJECT
|
||||||
connssl->cacert[0] = NULL;
|
connssl->cacert[0] = NULL;
|
||||||
connssl->cacert[1] = NULL;
|
connssl->cacert[1] = NULL;
|
||||||
@ -995,7 +985,8 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
|
|||||||
NSS_SetDomesticPolicy();
|
NSS_SetDomesticPolicy();
|
||||||
|
|
||||||
#ifdef HAVE_PK11_CREATEGENERICOBJECT
|
#ifdef HAVE_PK11_CREATEGENERICOBJECT
|
||||||
configstring = aprintf("library=%s name=PEM", pem_library);
|
if(!mod) {
|
||||||
|
char *configstring = aprintf("library=%s name=PEM", pem_library);
|
||||||
if(!configstring) {
|
if(!configstring) {
|
||||||
PR_Unlock(nss_initlock);
|
PR_Unlock(nss_initlock);
|
||||||
goto error;
|
goto error;
|
||||||
@ -1011,7 +1002,11 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
|
|||||||
infof(data, "WARNING: failed to load NSS PEM library %s. Using OpenSSL "
|
infof(data, "WARNING: failed to load NSS PEM library %s. Using OpenSSL "
|
||||||
"PEM certificates will not work.\n", pem_library);
|
"PEM certificates will not work.\n", pem_library);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
PK11_SetPasswordFunc(nss_get_password);
|
||||||
|
|
||||||
}
|
}
|
||||||
PR_Unlock(nss_initlock);
|
PR_Unlock(nss_initlock);
|
||||||
|
|
||||||
@ -1159,11 +1154,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
|
|||||||
else {
|
else {
|
||||||
nickname = data->set.str[STRING_CERT];
|
nickname = data->set.str[STRING_CERT];
|
||||||
}
|
}
|
||||||
if(nss_Init_Tokens(conn) != SECSuccess) {
|
|
||||||
if(nickname_alloc)
|
|
||||||
free(nickname);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if(!cert_stuff(conn, sockindex, data->set.str[STRING_CERT],
|
if(!cert_stuff(conn, sockindex, data->set.str[STRING_CERT],
|
||||||
data->set.str[STRING_KEY])) {
|
data->set.str[STRING_KEY])) {
|
||||||
/* failf() is already done in cert_stuff() */
|
/* failf() is already done in cert_stuff() */
|
||||||
@ -1178,16 +1169,15 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
|
|||||||
if(!connssl->client_nickname)
|
if(!connssl->client_nickname)
|
||||||
return CURLE_OUT_OF_MEMORY;
|
return CURLE_OUT_OF_MEMORY;
|
||||||
|
|
||||||
if(SSL_GetClientAuthDataHook(model,
|
|
||||||
(SSLGetClientAuthData) SelectClientCert,
|
|
||||||
(void *)connssl) != SECSuccess) {
|
|
||||||
curlerr = CURLE_SSL_CERTPROBLEM;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
connssl->client_nickname = NULL;
|
connssl->client_nickname = NULL;
|
||||||
|
|
||||||
|
if(SSL_GetClientAuthDataHook(model, SelectClientCert,
|
||||||
|
(void *)connssl) != SECSuccess) {
|
||||||
|
curlerr = CURLE_SSL_CERTPROBLEM;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
/* Import our model socket onto the existing file descriptor */
|
/* Import our model socket onto the existing file descriptor */
|
||||||
connssl->handle = PR_ImportTCPSocket(sockfd);
|
connssl->handle = PR_ImportTCPSocket(sockfd);
|
||||||
@ -1196,6 +1186,11 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
|
|||||||
goto error;
|
goto error;
|
||||||
PR_Close(model); /* We don't need this any more */
|
PR_Close(model); /* We don't need this any more */
|
||||||
|
|
||||||
|
/* This is the password associated with the cert that we're using */
|
||||||
|
if (data->set.str[STRING_KEY_PASSWD]) {
|
||||||
|
SSL_SetPKCS11PinArg(connssl->handle, data->set.str[STRING_KEY_PASSWD]);
|
||||||
|
}
|
||||||
|
|
||||||
/* Force handshake on next I/O */
|
/* Force handshake on next I/O */
|
||||||
SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE);
|
SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE);
|
||||||
|
|
||||||
|
@ -211,6 +211,7 @@ struct ssl_connect_data {
|
|||||||
#ifdef USE_NSS
|
#ifdef USE_NSS
|
||||||
PRFileDesc *handle;
|
PRFileDesc *handle;
|
||||||
char *client_nickname;
|
char *client_nickname;
|
||||||
|
struct SessionHandle *data;
|
||||||
#ifdef HAVE_PK11_CREATEGENERICOBJECT
|
#ifdef HAVE_PK11_CREATEGENERICOBJECT
|
||||||
PK11GenericObject *key;
|
PK11GenericObject *key;
|
||||||
PK11GenericObject *cacert[2];
|
PK11GenericObject *cacert[2];
|
||||||
|
Loading…
Reference in New Issue
Block a user