Index: nss.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/nss.c,v
retrieving revision 1.14
diff -u -r1.14 nss.c
--- nss.c	5 Nov 2007 09:45:09 -0000	1.14
+++ nss.c	17 Nov 2007 03:51:08 -0000
@@ -232,6 +232,247 @@
   return SECSuccess;
 }
 
+<<<<<<< nss.c
+/*
+ * 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(const char *filename, PRBool cacert)
+{
+#ifdef HAVE_PK11_CREATEGENERICOBJECT
+  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 *slotname = NULL;
+#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);
+    goto done;
+  }
+
+#ifdef HAVE_PK11_CREATEGENERICOBJECT
+  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;
+  }
+#else
+  /* We don't have PK11_CreateGenericObject but a file-based cert was passed
+   * in. We need to fail.
+   */
+  return 0;
+#endif
+
+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, 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;
+}
+
+static int nss_load_key(struct connectdata *conn, char *key_file)
+{
+#ifdef HAVE_PK11_CREATEGENERICOBJECT
+  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;
+  pphrase_arg_t *parg = NULL;
+
+  attrs = theTemplate;
+
+  /* FIXME: grok the various file types */
+
+  slotID = 1; /* hardcoded for now */
+
+  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 */
+  SECMOD_WaitForAnyTokenEvent(mod, 0, 0);
+  PK11_IsPresent(slot);
+
+  parg = (pphrase_arg_t *) malloc(sizeof(*parg));
+  parg->retryCount = 0;
+  parg->data = conn->data;
+  /* parg is initialized in nss_Init_Tokens() */
+  if(PK11_Authenticate(slot, PR_TRUE, parg) != SECSuccess) {
+    free(parg);
+    return 0;
+  }
+  free(parg);
+
+  return 1;
+#else
+  /* If we don't have PK11_CreateGenericObject then we can't load a file-based
+   * key.
+   */
+  (void)conn; /* unused */
+  (void)key_file; /* unused */
+  return 0;
+#endif
+}
+
+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;
+    default:
+      break;
+  }
+  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(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(conn, key_file);
+    else
+      /* In case the cert file also has the key */
+      rv = nss_load_key(conn, 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;
+}
+
+=======
 /*
  * Determine whether the nickname passed in is a filename that needs to
  * be loaded as a PEM or a regular NSS nickname.
@@ -476,6 +717,7 @@
   return 1;
 }
 
+>>>>>>> 1.14
 static char * nss_get_password(PK11SlotInfo * slot, PRBool retry, void *arg)
 {
   pphrase_arg_t *parg;
@@ -533,9 +775,14 @@
 
     ret = PK11_Authenticate(slot, PR_TRUE, parg);
     if(SECSuccess != ret) {
+<<<<<<< nss.c
+      if (PR_GetError() == SEC_ERROR_BAD_PASSWORD)
+        infof(conn->data, "The password for token '%s' is incorrect\n", PK11_GetTokenName(slot));
+=======
       if(PR_GetError() == SEC_ERROR_BAD_PASSWORD)
         infof(conn->data, "The password for token '%s' is incorrect\n",
               PK11_GetTokenName(slot));
+>>>>>>> 1.14
       status = SECFailure;
       break;
     }
@@ -551,6 +798,54 @@
 static SECStatus BadCertHandler(void *arg, PRFileDesc *sock)
 {
   SECStatus success = SECSuccess;
+<<<<<<< nss.c
+  struct connectdata *conn = (struct connectdata *)arg;
+  PRErrorCode err = PR_GetError();
+  CERTCertificate *cert = NULL;
+  char *subject, *issuer;
+
+  if (conn->data->set.ssl.certverifyresult!=0)
+    return success;
+
+  conn->data->set.ssl.certverifyresult=err;
+  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);
+=======
   struct connectdata *conn = (struct connectdata *)arg;
   PRErrorCode err = PR_GetError();
   CERTCertificate *cert = NULL;
@@ -599,6 +894,7 @@
     infof(conn->data, "SSL certificate verify ok.\n");
   PR_Free(subject);
   PR_Free(issuer);
+>>>>>>> 1.14
 
   return success;
 }
@@ -613,6 +909,54 @@
   return SECSuccess;
 }
 
+<<<<<<< nss.c
+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, &notBefore, &notAfter);
+  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;
+}
+
+=======
 static void display_conn_info(struct connectdata *conn, PRFileDesc *sock)
 {
   SSLChannelInfo channel;
@@ -659,6 +1003,7 @@
   return;
 }
 
+>>>>>>> 1.14
 /**
  *
  * Callback to pick the SSL client certificate.
@@ -681,6 +1026,9 @@
   if(!nickname)
     return secStatus;
 
+  if (!nickname)
+    return secStatus;
+
   cert = PK11_FindCertFromNickname(nickname, proto_win);
   if(cert) {
 
@@ -707,10 +1055,17 @@
     *pRetCert = cert;
     *pRetKey = privKey;
   }
+<<<<<<< nss.c
+  else {
+    if (cert)
+      CERT_DestroyCertificate(cert);
+  }
+=======
   else {
     if(cert)
       CERT_DestroyCertificate(cert);
   }
+>>>>>>> 1.14
 
   return secStatus;
 }
@@ -800,6 +1155,13 @@
   curl_socket_t sockfd = conn->sock[sockindex];
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
   SECStatus rv;
+<<<<<<< nss.c
+  char *configstring = NULL;
+  char *certDir = NULL;
+  int curlerr;
+
+  curlerr = CURLE_SSL_CONNECT_ERROR;
+=======
 #ifdef HAVE_PK11_CREATEGENERICOBJECT
   char *configstring = NULL;
 #endif
@@ -807,9 +1169,27 @@
   int curlerr;
 
   curlerr = CURLE_SSL_CONNECT_ERROR;
+>>>>>>> 1.14
 
   /* FIXME. NSS doesn't support multiple databases open at the same time. */
   if(!initialized) {
+<<<<<<< nss.c
+    initialized = 1;
+
+    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 = (char *)SSL_DIR;
+        }
+    }
+
+    if(!certDir) {
+      rv = NSS_NoDB_Init(NULL);
+=======
     initialized = 1;
 
     certDir = getenv("SSL_DIR"); /* Look in $SSL_DIR */
@@ -825,6 +1205,7 @@
 
     if(!certDir) {
       rv = NSS_NoDB_Init(NULL);
+>>>>>>> 1.14
     }
     else {
       rv = NSS_Initialize(certDir, NULL, NULL, "secmod.db",
@@ -835,6 +1216,26 @@
       curlerr = CURLE_SSL_CACERT_BADFILE;
       goto error;
     }
+<<<<<<< nss.c
+
+    NSS_SetDomesticPolicy();
+
+#ifdef HAVE_PK11_CREATEGENERICOBJECT
+    configstring = (char *)malloc(PATH_MAX);
+
+    PR_snprintf(configstring, PATH_MAX, "library=%s name=PEM", pem_library);
+
+    mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE);
+    free(configstring);
+    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);
+    }
+#endif
+=======
 
     NSS_SetDomesticPolicy();
 
@@ -854,6 +1255,7 @@
             "PEM certificates will not work.\n", pem_library);
     }
 #endif
+>>>>>>> 1.14
   }
 
   model = PR_NewTCPSocket();
@@ -909,6 +1311,50 @@
                            NULL) != SECSuccess)
     goto error;
 
+<<<<<<< nss.c
+  if (data->set.ssl.CAfile) {
+    rv = nss_load_cert(data->set.ssl.CAfile, PR_TRUE);
+    if (!rv) {
+      curlerr = CURLE_SSL_CACERT_BADFILE;
+      goto error;
+    }
+  }
+  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)) {
+      int rv;
+
+      dir = PR_OpenDir(data->set.ssl.CApath);
+      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(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.ssl.verifypeer)
     /* skip the verifying of the peer */
     ;
@@ -956,7 +1402,36 @@
         data->set.ssl.CAfile ? data->set.ssl.CAfile : "none",
         data->set.ssl.CApath ? data->set.ssl.CApath : "none");
 
+>>>>>>> 1.14
   if(data->set.str[STRING_CERT]) {
+<<<<<<< nss.c
+    char * n;
+    char * nickname;
+
+    nickname = (char *)malloc(PATH_MAX);
+    if(is_file(data->set.str[STRING_CERT])) {
+      n = strrchr(data->set.str[STRING_CERT], '/');
+      if (n) {
+        n++; /* skip last slash */
+        snprintf(nickname, PATH_MAX, "PEM Token #%ld:%s", 1, n);
+      }
+    }
+    else {
+      strncpy(nickname, data->set.str[STRING_CERT], PATH_MAX);
+    }
+    if(nss_Init_Tokens(conn) != SECSuccess) {
+      free(nickname);
+      goto error;
+    }
+    if (!cert_stuff(conn, data->set.str[STRING_CERT],
+        data->set.str[STRING_KEY])) {
+      /* failf() is already done in cert_stuff() */
+      free(nickname);
+      return CURLE_SSL_CERTPROBLEM;
+    }
+
+    connssl->client_nickname = strdup(nickname);
+=======
     char *n;
     char *nickname;
 
@@ -983,10 +1458,16 @@
     }
 
     connssl->client_nickname = strdup(nickname);
+>>>>>>> 1.14
     if(SSL_GetClientAuthDataHook(model,
                                  (SSLGetClientAuthData) SelectClientCert,
+<<<<<<< nss.c
+                                 (void *)connssl->client_nickname) !=
+                                 SECSuccess) {
+=======
                                  (void *)connssl->client_nickname) !=
        SECSuccess) {
+>>>>>>> 1.14
       curlerr = CURLE_SSL_CERTPROBLEM;
       goto error;
     }
@@ -1013,9 +1494,15 @@
   /* Force the handshake now */
   if(SSL_ForceHandshakeWithTimeout(connssl->handle,
                                     PR_SecondsToInterval(HANDSHAKE_TIMEOUT))
+<<<<<<< nss.c
+      != SECSuccess) {
+    if (conn->data->set.ssl.certverifyresult!=0)
+      curlerr = CURLE_SSL_CACERT;
+=======
       != SECSuccess) {
     if(conn->data->set.ssl.certverifyresult!=0)
       curlerr = CURLE_SSL_CACERT;
+>>>>>>> 1.14
     goto error;
   }
 
Index: sslgen.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/sslgen.c,v
retrieving revision 1.30
diff -u -r1.30 sslgen.c
--- sslgen.c	7 Nov 2007 09:21:36 -0000	1.30
+++ sslgen.c	17 Nov 2007 03:51:08 -0000
@@ -243,6 +243,7 @@
 #else
 #ifdef USE_NSS
   *done = TRUE; /* fallback to BLOCKING */
+  conn->ssl[sockindex].use = TRUE;
   return Curl_nss_connect(conn, sockindex);
 #else
 #ifdef USE_QSOSSL

