741 lines
21 KiB
Diff
741 lines
21 KiB
Diff
|
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 <ssl.h>
|
||
|
#include <sslerr.h>
|
||
|
#include <secerr.h>
|
||
|
+#include <secmod.h>
|
||
|
#include <sslproto.h>
|
||
|
#include <prtypes.h>
|
||
|
#include <pk11pub.h>
|
||
|
@@ -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;
|