From 47acf8b410c5fbdb50411d9f665d340b451c59bb Mon Sep 17 00:00:00 2001 From: Jindrich Novy Date: Fri, 7 Sep 2007 12:51:08 +0000 Subject: [PATCH] add nsspem patch --- curl-7.16.4-nsspem.patch | 740 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 740 insertions(+) create mode 100644 curl-7.16.4-nsspem.patch diff --git a/curl-7.16.4-nsspem.patch b/curl-7.16.4-nsspem.patch new file mode 100644 index 0000000..3c77a0b --- /dev/null +++ b/curl-7.16.4-nsspem.patch @@ -0,0 +1,740 @@ +diff -up curl-7.16.4/configure.nsspem curl-7.16.4/configure +--- curl-7.16.4/configure.nsspem 2007-06-25 11:18:41.000000000 +0200 ++++ curl-7.16.4/configure 2007-09-06 08:35:31.000000000 +0200 +@@ -26760,7 +26760,7 @@ echo "$as_me: WARNING: Use --with-ssl, - + fi + + +-if test X"$USE_GNUTLS$OPENSSL_ENABLED" != "X"; then ++if test X"$USE_NSS$USE_GNUTLS$OPENSSL_ENABLED" != "X"; then + + { echo "$as_me:$LINENO: checking CA cert bundle install path" >&5 + echo $ECHO_N "checking CA cert bundle install path... $ECHO_C" >&6; } +diff -up curl-7.16.4/configure.ac.nsspem curl-7.16.4/configure.ac +--- curl-7.16.4/configure.ac.nsspem 2007-06-12 23:39:21.000000000 +0200 ++++ curl-7.16.4/configure.ac 2007-09-06 08:35:31.000000000 +0200 +@@ -1468,7 +1468,7 @@ dnl ************************************ + dnl Check for the CA bundle + dnl ********************************************************************** + +-if test X"$USE_GNUTLS$OPENSSL_ENABLED" != "X"; then ++if test X"$USE_NSS$USE_GNUTLS$OPENSSL_ENABLED" != "X"; then + + AC_MSG_CHECKING([CA cert bundle install path]) + +diff -up curl-7.16.4/lib/nss.c.nsspem curl-7.16.4/lib/nss.c +--- curl-7.16.4/lib/nss.c.nsspem 2007-05-25 23:56:27.000000000 +0200 ++++ curl-7.16.4/lib/nss.c 2007-09-06 09:26:39.000000000 +0200 +@@ -55,6 +55,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -69,10 +70,19 @@ + #define min(a, b) ((a) < (b) ? (a) : (b)) + #endif + ++#define SSL_DIR "/etc/pki/nssdb" ++ ++/* enough to fit the string "PEM Token #[0|1]" */ ++#define SLOTSIZE 13 ++ + PRFileDesc *PR_ImportTCPSocket(PRInt32 osfd); + + static int initialized = 0; +-static int noverify = 0; ++static int verify_done = 0; ++char * select_nickname = NULL; ++ ++/* Global so our callbacks can update as appropriate */ ++static int curlerr; + + #define HANDSHAKE_TIMEOUT 30 + +@@ -87,15 +97,25 @@ typedef struct { + PRInt32 version; /* protocol version valid for this cipher */ + } cipher_s; + +-/* the table itself is defined in nss_engine_init.c */ + #ifdef NSS_ENABLE_ECC + #define ciphernum 48 + #else + #define ciphernum 23 + #endif + ++#define PK11_SETATTRS(x,id,v,l) \ ++ do { \ ++ (x)->type = (id); \ ++ (x)->pValue=(v); \ ++ (x)->ulValueLen = (l); \ ++ } while (0) ++ ++#define CERT_NewTempCertificate __CERT_NewTempCertificate ++ + enum sslversion { SSL2 = 1, SSL3 = 2, TLS = 4 }; + ++pphrase_arg_t *parg = NULL; ++ + cipher_s cipherlist[ciphernum] = { + /* SSL2 cipher suites */ + {"rc4", SSL_EN_RC4_128_WITH_MD5, SSL2}, +@@ -154,6 +174,9 @@ cipher_s cipherlist[ciphernum] = { + #endif + }; + ++const char* pem_library = "libnsspem.so"; ++static SECMODModule* mod = NULL; ++ + static SECStatus set_ciphers(struct SessionHandle *data, PRFileDesc * model, + char *cipher_list) + { +@@ -197,9 +220,7 @@ static SECStatus set_ciphers(struct Sess + } + + if(found == PR_FALSE) { +- char buf[1024]; +- snprintf(buf, 1024, "Unknown cipher in list: %s", cipher); +- failf(data, buf); ++ failf(data, "Unknown cipher in list: %s", cipher); + return SECFailure; + } + +@@ -220,27 +241,240 @@ static SECStatus set_ciphers(struct Sess + return SECSuccess; + } + ++/* ++ * Determine whether the nickname passed in is a filename that needs to ++ * be loaded as a PEM or a regular NSS nickname. ++ * ++ * returns 1 for a file ++ * returns 0 for not a file (NSS nickname) ++ */ ++static int is_file(const char *filename) { ++ struct stat st; ++ ++ if (filename == NULL) ++ return 0; ++ ++ if (stat(filename, &st) == 0) { ++ if (S_ISREG(st.st_mode)) ++ return 1; ++ } ++ return 0; ++} ++ ++static int ++nss_load_cert(struct SessionHandle *data, const char *filename, PRBool cacert) ++{ ++ CERTCertificate *cert; ++ void *proto_win = NULL; ++ CK_SLOT_ID slotID; ++ PK11SlotInfo * slot = NULL; ++ PK11GenericObject *rv; ++ CK_ATTRIBUTE *attrs; ++ CK_ATTRIBUTE theTemplate[20]; ++ CK_BBOOL cktrue = CK_TRUE; ++ CK_BBOOL ckfalse = CK_FALSE; ++ CK_OBJECT_CLASS objClass = CKO_CERTIFICATE; ++ char *nickname = NULL; ++ char *slotname = NULL; ++ char *n; ++ ++ /* 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++; ++ } else { ++ /* A nickname from the NSS internal database */ ++ nickname = strdup(filename); ++ goto done; ++ } ++ ++ attrs = theTemplate; ++ ++ /* 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; ++ } ++ ++ slotname = (char *)malloc(SLOTSIZE); ++ nickname = (char *)malloc(PATH_MAX); ++ snprintf(slotname, SLOTSIZE, "PEM Token #%ld", slotID); ++ snprintf(nickname, PATH_MAX, "PEM Token #%ld:%s", slotID, n); ++ ++ slot = PK11_FindSlotByName(slotname); ++ ++ if (!slot) { ++ free(slotname); ++ free(nickname); ++ 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) ); attrs++; ++ } else { ++ PK11_SETATTRS(attrs, CKA_TRUST, &ckfalse, sizeof(CK_BBOOL) ); attrs++; ++ } ++ ++ /* This load the certificate in our PEM module into the appropriate ++ * slot. ++ */ ++ rv = PK11_CreateGenericObject(slot, theTemplate, 4, PR_FALSE /* isPerm */); ++ ++ PK11_FreeSlot(slot); ++ ++ free(slotname); ++ if (rv == NULL) { ++ free(nickname); ++ return 0; ++ } ++ ++done: ++ /* 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, proto_win); ++ ++ /* An invalid nickname was passed in */ ++ if (cert == NULL) { ++ PR_SetError(SEC_ERROR_UNKNOWN_CERT, 0); ++ return 0; ++ } ++ ++ CERT_DestroyCertificate(cert); ++ } ++ free(nickname); ++ ++ return 1; ++} ++ ++static int nss_load_key(char *key_file) ++{ ++ PK11SlotInfo * slot = NULL; ++ PK11GenericObject *rv; ++ CK_ATTRIBUTE *attrs; ++ CK_ATTRIBUTE theTemplate[20]; ++ CK_BBOOL cktrue = CK_TRUE; ++ CK_OBJECT_CLASS objClass = CKO_PRIVATE_KEY; ++ CK_SLOT_ID slotID; ++ char *slotname = NULL; ++ ++ attrs = theTemplate; ++ ++ /* FIXME: grok the various file types */ ++ ++ /* FIXME: shouldn't be hardcoded */ ++ slotID = 1; ++ ++ slotname = (char *)malloc(SLOTSIZE); ++ snprintf(slotname, SLOTSIZE, "PEM Token #%ld", slotID); ++ ++ slot = PK11_FindSlotByName(slotname); ++ free(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 */ ++ rv = PK11_CreateGenericObject(slot, theTemplate, 3, PR_FALSE /* isPerm */); ++ if (rv == NULL) { ++ PR_SetError(SEC_ERROR_BAD_KEY, 0); ++ return 0; ++ } ++ ++ /* This will force the token to be seen as re-inserted */ ++ PK11_IsPresent(slot); ++ ++ parg->retryCount = 0; ++ /* parg is initialized in nss_Init_Tokens() */ ++ if (PK11_Authenticate(slot, PR_TRUE, parg) != SECSuccess) { ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static int display_error(struct connectdata *conn, PRInt32 err, const char *filename) { ++ switch(err) { ++ case SEC_ERROR_BAD_PASSWORD: ++ failf(conn->data, "Unable to load client key: Incorrect password\n"); ++ return 1; ++ case SEC_ERROR_UNKNOWN_CERT: ++ failf(conn->data, "Unable to load certificate %s\n", filename); ++ return 1; ++ } ++ return 0; /* The caller will print a generic error */ ++} ++ ++static int cert_stuff(struct connectdata *conn, char *cert_file, char *key_file) ++{ ++ struct SessionHandle *data = conn->data; ++ int rv = 0; ++ ++ if (cert_file) ++ rv = nss_load_cert(data, cert_file, PR_FALSE); ++ if (!rv) { ++ if (!display_error(conn, PR_GetError(), cert_file)) ++ failf(data, "Unable to load client cert %d.", PR_GetError()); ++ return 0; ++ } ++ if (key_file || (is_file(cert_file) )) { ++ if (key_file) ++ rv = nss_load_key(key_file); ++ else ++ rv = nss_load_key(cert_file); ++ if (!rv) { ++ if (!display_error(conn, PR_GetError(), key_file)) ++ failf(data, "Unable to load client key %d.", PR_GetError()); ++ ++ return 0; ++ } ++ } ++ return 1; ++} ++ + static char * nss_get_password(PK11SlotInfo * slot, PRBool retry, void *arg) + { + pphrase_arg_t *parg = (pphrase_arg_t *) arg; +- (void)slot; /* unused */ +- (void)retry; /* unused */ ++ parg->retryCount++; ++ if (parg->retryCount > 2) ++ return NULL; + if(parg->data->set.key_passwd) + return (char *)PORT_Strdup((char *)parg->data->set.key_passwd); + else + return NULL; + } + +-static SECStatus nss_Init_Tokens(struct connectdata * conn) ++static char * nss_no_password(PK11SlotInfo *slot, PRBool retry, void *arg) ++{ ++ return NULL; ++} ++ ++static SECStatus nss_Init_Tokens(struct SessionHandle *data) + { + PK11SlotList *slotList; + PK11SlotListElement *listEntry; + SECStatus ret, status = SECSuccess; +- pphrase_arg_t *parg; + +- parg = (pphrase_arg_t *) malloc(sizeof(*parg)); ++ if (!parg) ++ parg = (pphrase_arg_t *) malloc(sizeof(*parg)); + parg->retryCount = 0; +- parg->data = conn->data; ++ parg->data = data; + + PK11_SetPasswordFunc(nss_get_password); + +@@ -253,10 +487,10 @@ static SECStatus nss_Init_Tokens(struct + + if(PK11_NeedLogin(slot) && PK11_NeedUserInit(slot)) { + if(slot == PK11_GetInternalKeySlot()) { +- failf(conn->data, "The NSS database has not been initialized.\n"); ++ failf(data, "The NSS database has not been initialized.\n"); + } + else { +- failf(conn->data, "The token %s has not been initialized.", ++ failf(data, "The token %s has not been initialized.", + PK11_GetTokenName(slot)); + } + PK11_FreeSlot(slot); +@@ -265,6 +499,8 @@ static SECStatus nss_Init_Tokens(struct + + ret = PK11_Authenticate(slot, PR_TRUE, parg); + if(SECSuccess != ret) { ++ if (PR_GetError() == SEC_ERROR_BAD_PASSWORD) ++ infof(data, "The password for token '%s' is incorrect\n", PK11_GetTokenName(slot)); + status = SECFailure; + break; + } +@@ -272,15 +508,59 @@ static SECStatus nss_Init_Tokens(struct + PK11_FreeSlot(slot); + } + +- free(parg); + return status; + } + + static SECStatus BadCertHandler(void *arg, PRFileDesc * socket) + { + SECStatus success = SECSuccess; +- (void)arg; +- (void)socket; ++ struct connectdata *conn = (struct connectdata *)arg; ++ PRErrorCode err = PR_GetError(); ++ CERTCertificate *cert = NULL; ++ char *subject, *issuer; ++ ++ if (verify_done) ++ return success; ++ ++ verify_done = 1; ++ cert = SSL_PeerCertificate(socket); ++ subject = CERT_NameToAscii(&cert->subject); ++ issuer = CERT_NameToAscii(&cert->issuer); ++ CERT_DestroyCertificate(cert); ++ ++ switch(err) { ++ case SEC_ERROR_CA_CERT_INVALID: ++ infof(conn->data, "Issuer certificate is invalid: '%s'\n", issuer); ++ if (conn->data->set.ssl.verifypeer) ++ success = SECFailure; ++ break; ++ case SEC_ERROR_UNTRUSTED_ISSUER: ++ if (conn->data->set.ssl.verifypeer) ++ success = SECFailure; ++ infof(conn->data, "Certificate is signed by an untrusted issuer: '%s'\n", issuer); ++ break; ++ case SSL_ERROR_BAD_CERT_DOMAIN: ++ if (conn->data->set.ssl.verifypeer) ++ success = SECFailure; ++ infof(conn->data, "common name: %s (does not match '%s')\n", ++ subject, conn->host.dispname); ++ break; ++ case SEC_ERROR_EXPIRED_CERTIFICATE: ++ if (conn->data->set.ssl.verifypeer) ++ success = SECFailure; ++ infof(conn->data, "Remote Certificate has expired.\n"); ++ break; ++ default: ++ if (conn->data->set.ssl.verifypeer) ++ success = SECFailure; ++ infof(conn->data, "Bad certificate received. Subject = '%s', Issuer = '%s'\n", subject, issuer); ++ break; ++ } ++ if (success == SECSuccess) ++ infof(conn->data, "SSL certificate verify ok.\n"); ++ PR_Free(subject); ++ PR_Free(issuer); ++ curlerr = CURLE_SSL_CACERT; + + return success; + } +@@ -295,6 +575,52 @@ static SECStatus HandshakeCallback(PRFil + return SECSuccess; + } + ++static void display_conn_info(struct connectdata *conn, PRFileDesc * socket) ++{ ++ SSLChannelInfo channel; ++ SSLCipherSuiteInfo suite; ++ CERTCertificate *cert; ++ char *subject, *issuer, *common_name; ++ PRExplodedTime printableTime; ++ char timeString[256]; ++ PRTime notBefore, notAfter; ++ ++ if (SSL_GetChannelInfo(socket, &channel, sizeof channel) == ++ SECSuccess && channel.length == sizeof channel && ++ channel.cipherSuite) { ++ if (SSL_GetCipherSuiteInfo(channel.cipherSuite, ++ &suite, sizeof suite) == SECSuccess) { ++ infof(conn->data, "SSL connection using %s\n", suite.cipherSuiteName); ++ } ++ } ++ ++ infof(conn->data, "Server certificate:\n"); ++ ++ cert = SSL_PeerCertificate(socket); ++ subject = CERT_NameToAscii(&cert->subject); ++ 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); ++ ++ return; ++} ++ + /** + * + * Callback to pick the SSL client certificate. +@@ -309,24 +635,42 @@ static SECStatus SelectClientCert(void * + char *nickname = (char *)arg; + void *proto_win = NULL; + SECStatus secStatus = SECFailure; ++ PK11SlotInfo *slot; + (void)caNames; + + proto_win = SSL_RevealPinArg(socket); + ++ if (!nickname) ++ return secStatus; ++ + cert = PK11_FindCertFromNickname(nickname, proto_win); + if(cert) { +- privKey = PK11_FindKeyByAnyCert(cert, proto_win); +- if(privKey) { +- secStatus = SECSuccess; +- } +- else { +- CERT_DestroyCertificate(cert); ++ ++ if (!strncmp(nickname, "PEM Token", 9)) { ++ CK_SLOT_ID slotID = 1; /* hardcoded for now */ ++ char * slotname = (char *)malloc(SLOTSIZE); ++ snprintf(slotname, SLOTSIZE, "PEM Token #%ld", slotID); ++ slot = PK11_FindSlotByName(slotname); ++ privKey = PK11_FindPrivateKeyFromCert(slot, cert, NULL); ++ PK11_FreeSlot(slot); ++ free(slotname); ++ if(privKey) { ++ secStatus = SECSuccess; ++ } ++ } else { ++ privKey = PK11_FindKeyByAnyCert(cert, proto_win); ++ if(privKey) { ++ secStatus = SECSuccess; ++ } + } + } + + if(secStatus == SECSuccess) { + *pRetCert = cert; + *pRetKey = privKey; ++ } else { ++ if (cert) ++ CERT_DestroyCertificate(cert); + } + + return secStatus; +@@ -397,6 +741,8 @@ void Curl_nss_close(struct connectdata * + } + connssl->use = FALSE; /* get back to ordinary socket usage */ + } ++ if (select_nickname) ++ free(select_nickname); + } + + /* +@@ -418,31 +764,52 @@ CURLcode Curl_nss_connect(struct connect + curl_socket_t sockfd = conn->sock[sockindex]; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + SECStatus rv; +- int curlerr = CURLE_SSL_CONNECT_ERROR; ++ char *configstring = NULL; ++ char *certDir = NULL; ++ ++ curlerr = CURLE_SSL_CONNECT_ERROR; ++ certDir = getenv("SSL_DIR"); /* Look in $SSL_DIR */ ++ ++ if (!certDir) { ++ struct stat st; ++ ++ if (stat(SSL_DIR, &st) == 0) ++ if (S_ISDIR(st.st_mode)) { ++ certDir = "/etc/pki/nssdb"; ++ } ++ } + + /* FIXME. NSS doesn't support multiple databases open at the same time. */ + if(!initialized) { +- if(!data->set.ssl.CAfile) { +- if(data->set.ssl.verifypeer) { +- failf(data, "No NSS cacert database specified."); +- return CURLE_SSL_CACERT_BADFILE; +- } +- else { +- rv = NSS_NoDB_Init(NULL); +- noverify = 1; +- } ++ if(!certDir) { ++ rv = NSS_NoDB_Init(NULL); + } + else { +- rv = NSS_Initialize(data->set.ssl.CAfile, NULL, NULL, "secmod.db", ++ rv = NSS_Initialize(certDir, NULL, NULL, "secmod.db", + NSS_INIT_READONLY); + } + if(rv != SECSuccess) { + curlerr = CURLE_SSL_CACERT_BADFILE; + goto error; + } +- } ++ configstring = (char *)malloc(4096); ++ ++ NSS_SetDomesticPolicy(); + +- NSS_SetDomesticPolicy(); ++ PR_snprintf(configstring, 4096, "library=%s name=PEM", pem_library); ++// PR_snprintf(configstring, 4096, "library=%s name=PEM parameters=\"NSS=\"trustorder=75\"\"", pem_library); ++// PR_snprintf(configstring, 4096, "library=/usr/lib/libnsspem.so name=PEM parameters=\"/etc/pki/tls/certs/ca-bundle.crt\" NSS=\"trustorder=75\""); ++ ++ mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE); ++ 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); ++ } ++ free(configstring); ++ } + + model = PR_NewTCPSocket(); + if(!model) +@@ -482,26 +849,92 @@ CURLcode Curl_nss_connect(struct connect + goto error; + + if(data->set.ssl.cipher_list) { +- if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) ++ if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) { ++ curlerr = CURLE_SSL_CIPHER; + goto error; ++ } + } + +- if(SSL_BadCertHook(model, (SSLBadCertHandler) BadCertHandler, NULL) +- != SECSuccess) ++ if(SSL_BadCertHook(model, (SSLBadCertHandler) BadCertHandler, conn) ++ != SECSuccess) { + goto error; ++ } + if(SSL_HandshakeCallback(model, (SSLHandshakeCallback) HandshakeCallback, + NULL) != SECSuccess) + goto error; + ++ if (mod && data->set.ssl.CAfile) { ++ rv = nss_load_cert(data, data->set.ssl.CAfile, PR_TRUE); ++ } else if (data->set.ssl.CApath) { ++ struct stat st; ++ PRDir *dir; ++ PRDirEntry *entry; ++ ++ if (stat(data->set.ssl.CApath, &st) == -1) ++ curlerr = CURLE_SSL_CACERT_BADFILE; ++ goto error; ++ ++ if (S_ISDIR(st.st_mode)) { ++ dir = PR_OpenDir(data->set.ssl.CApath); ++ int rv; ++ do { ++ entry = PR_ReadDir(dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN); ++ ++ if (entry) { ++ char fullpath[PATH_MAX]; ++ ++ snprintf(fullpath, sizeof(fullpath), "%s/%s", data->set.ssl.CApath, entry->name); ++ rv = nss_load_cert(data, fullpath, PR_TRUE); ++ } ++ /* This is purposefully tolerant of errors so non-PEM files ++ * can be in the same directory */ ++ } while (entry != NULL); ++ PR_CloseDir(dir); ++ } ++ } ++ infof(data, ++ " CAfile: %s\n" ++ " CApath: %s\n", ++ data->set.ssl.CAfile ? data->set.ssl.CAfile : "none", ++ data->set.ssl.CApath ? data->set.ssl.CApath : "none"); ++ + if(data->set.cert) { ++ char * n; ++ char * nickname; ++ ++ nickname = (char *)malloc(PATH_MAX); ++ if (is_file(data->set.cert)) { ++ n = strrchr(data->set.cert, '/'); ++ if (n) { ++ n++; /* skip last slash */ ++ snprintf(nickname, PATH_MAX, "PEM Token #%ld:%s", 1, n); ++ } ++ } else { ++ strncpy(nickname, data->set.cert, PATH_MAX); ++ } ++ if(nss_Init_Tokens(conn->data) != SECSuccess) { ++ free(nickname); ++ goto error; ++ } ++ if (!cert_stuff(conn, data->set.cert, data->set.key)) { ++ /* failf() is already done in cert_stuff() */ ++ free(nickname); ++ return CURLE_SSL_CERTPROBLEM; ++ } ++ ++ select_nickname = strdup(nickname); + if(SSL_GetClientAuthDataHook(model, + (SSLGetClientAuthData) SelectClientCert, +- (void *)data->set.cert) != SECSuccess) { ++ (void *)select_nickname) != SECSuccess) { + curlerr = CURLE_SSL_CERTPROBLEM; + goto error; + } +- if(nss_Init_Tokens(conn) != SECSuccess) +- goto error; ++ ++ free(nickname); ++ free(parg); ++ ++ /* No longer return the password, lets us free parg */ ++ PK11_SetPasswordFunc(nss_no_password); + } + + /* Import our model socket onto the existing file descriptor */ +@@ -509,6 +942,7 @@ CURLcode Curl_nss_connect(struct connect + connssl->handle = SSL_ImportFD(model, connssl->handle); + if(!connssl->handle) + goto error; ++ PR_Close(model); /* We don't need this any more */ + + /* Force handshake on next I/O */ + SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE); +@@ -518,14 +952,17 @@ CURLcode Curl_nss_connect(struct connect + /* Force the handshake now */ + if (SSL_ForceHandshakeWithTimeout(connssl->handle, + PR_SecondsToInterval(HANDSHAKE_TIMEOUT)) +- != SECSuccess) ++ != SECSuccess) { + goto error; ++ } ++ ++ display_conn_info(conn, connssl->handle); + + return CURLE_OK; + + error: + err = PR_GetError(); +- failf(data, "NSS error %d", err); ++ infof(data, "NSS error %d\n", err); + if(model) + PR_Close(model); + return curlerr;