nss: big cleanup in nss_load_cert() and cert_stuff()
authorKamil Dudka <kdudka@redhat.com>
Fri, 26 Aug 2011 13:43:48 +0000 (15:43 +0200)
committerKamil Dudka <kdudka@redhat.com>
Mon, 17 Oct 2011 10:13:42 +0000 (12:13 +0200)
lib/nss.c

index c6ab42f573bb0d557bf17f7836d1754118de2c34..25293d5a5930ee28a0d25b654024ea16d6dfb1ce 100644 (file)
--- a/lib/nss.c
+++ b/lib/nss.c
@@ -319,6 +319,9 @@ static CURLcode nss_create_object(struct ssl_connect_data *ssl,
   CK_BBOOL ckfalse = CK_FALSE;
   CK_ATTRIBUTE attrs[/* max count of attributes */ 4];
   int attr_cnt = 0;
+  CURLcode err = (cacert)
+    ? CURLE_SSL_CACERT_BADFILE
+    : CURLE_SSL_CERTPROBLEM;
 
   const int slot_id = (cacert) ? 0 : 1;
   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);
   free(slot_name);
   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_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);
   PK11_FreeSlot(slot);
   if(!obj)
-    return CURLE_SSL_CERTPROBLEM;
+    return err;
 
   if(!Curl_llist_insert_next(ssl->obj_list, ssl->obj_list->tail, obj)) {
     PK11_DestroyGenericObject(obj);
@@ -368,77 +371,21 @@ static void nss_destroy_object(void *user, void *ptr)
 }
 #endif
 
-static int nss_load_cert(struct ssl_connect_data *ssl,
-                         const char *filename, PRBool cacert)
+static CURLcode nss_load_cert(struct ssl_connect_data *ssl,
+                              const char *filename, PRBool cacert)
 {
-#ifdef HAVE_PK11_CREATEGENERICOBJECT
-  /* All CA and trust objects go into slot 0. Other slots are used
-   * for storing certificates.
-   */
-  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;
-  }
+  CURLcode err = (cacert)
+    ? CURLE_SSL_CACERT_BADFILE
+    : CURLE_SSL_CERTPROBLEM;
 
 #ifdef HAVE_PK11_CREATEGENERICOBJECT
-  nickname = aprintf("PEM Token #%d:%s", slot_id, n);
-  if(!nickname)
-    return 0;
-
-  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;
+  /* libnsspem.so leaks memory if the requested file does not exist.  For more
+   * details, go to <https://bugzilla.redhat.com/734760>. */
+  if(is_file(filename))
+    return nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert);
 #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;
+  return err;
 }
 
 /* add given CRL to cache if it is not already there */
@@ -527,23 +474,23 @@ fail:
   return SECFailure;
 }
 
-static int nss_load_key(struct connectdata *conn, int sockindex,
-                        char *key_file)
+static CURLcode nss_load_key(struct connectdata *conn, int sockindex,
+                             char *key_file)
 {
 #ifdef HAVE_PK11_CREATEGENERICOBJECT
   PK11SlotInfo *slot;
   SECStatus status;
   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);
-    return 0;
+    return rv;
   }
 
   slot = PK11_FindSlotByName("PEM Token #1");
   if(!slot)
-    return 0;
+    return CURLE_SSL_CERTPROBLEM;
 
   /* This will force the token to be seen as re-inserted */
   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,
                              conn->data->set.str[STRING_KEY_PASSWD]);
   PK11_FreeSlot(slot);
-  return (SECSuccess == status) ? 1 : 0;
+  return (SECSuccess == status)
+    ? CURLE_OK
+    : CURLE_SSL_CERTPROBLEM;
 #else
   /* If we don't have PK11_CreateGenericObject then we can't load a file-based
    * key.
    */
   (void)conn; /* unused */
   (void)key_file; /* unused */
-  (void)sockindex; /* unused */
-  return 0;
+  return CURLE_SSL_CERTPROBLEM;
 #endif
+  (void)sockindex; /* unused */
 }
 
 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 */
 }
 
-static int cert_stuff(struct connectdata *conn,
-                      int sockindex, char *cert_file, char *key_file)
+static CURLcode cert_stuff(struct connectdata *conn, int sockindex,
+                           char *cert_file, char *key_file)
 {
   struct SessionHandle *data = conn->data;
-  int rv = 0;
+  CURLcode rv;
 
   if(cert_file) {
     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))
         failf(data, "Unable to load client cert %d.", PR_GetError());
-      return 0;
+
+      return rv;
     }
   }
+
   if(key_file || (is_file(cert_file))) {
     if(key_file)
       rv = nss_load_key(conn, sockindex, key_file);
     else
       /* In case the cert file also has the key */
       rv = nss_load_key(conn, sockindex, cert_file);
-    if(!rv) {
+    if(CURLE_OK != rv) {
       if(!display_error(conn, PR_GetError(), key_file))
         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)
@@ -774,7 +726,6 @@ static SECStatus check_issuer_cert(PRFileDesc *sock,
   cert_issuer = CERT_FindCertIssuer(cert,PR_Now(),certUsageObjectSigner);
 
   proto_win = SSL_RevealPinArg(sock);
-  issuer = NULL;
   issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win);
 
   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 *capath = data->set.ssl.CApath;
 
-  if(cafile && !nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE))
-    return CURLE_SSL_CACERT_BADFILE;
+  if(cafile) {
+    CURLcode rv = nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE);
+    if(CURLE_OK != rv)
+      return rv;
+  }
 
   if(capath) {
     struct_stat st;
@@ -1153,7 +1107,7 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn,
           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
            * be in the same directory */
           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]) {
     char *nickname = dup_nickname(data, STRING_CERT);
-    if(!nickname && !cert_stuff(conn, sockindex, data->set.str[STRING_CERT],
-                                data->set.str[STRING_KEY]))
-      /* failf() is already done in cert_stuff() */
-      return CURLE_SSL_CERTPROBLEM;
+    if(nickname) {
+      /* we are not going to use libnsspem.so to read the client cert */
+#ifdef HAVE_PK11_CREATEGENERICOBJECT
+      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 */
     connssl->client_nickname = nickname;