nss: fix a bug in handling of CURLOPT_CAPATH
authorKamil Dudka <kdudka@redhat.com>
Tue, 18 Jan 2011 12:53:43 +0000 (13:53 +0100)
committerKamil Dudka <kdudka@redhat.com>
Tue, 18 Jan 2011 13:30:49 +0000 (14:30 +0100)
... and update the curl.1 and curl_easy_setopt.3 man pages such that
they do not suggest to use an OpenSSL utility if curl is not built
against OpenSSL.

Bug: https://bugzilla.redhat.com/669702

RELEASE-NOTES
docs/curl.1
docs/libcurl/curl_easy_setopt.3
lib/nss.c

index aa67721ab21af511f9876616f813ed823dd0e207..926b201d2e2f0d7574b1c81bfe798624349a8865 100644 (file)
@@ -27,6 +27,7 @@ This release includes the following bugfixes:
  o Curl_do: avoid using stale conn pointer
  o tftpd test server: avoid buffer overflow report from glibc
  o nss: avoid CURLE_OUT_OF_MEMORY given a file name without any slash
+ o nss: fix a bug in handling of CURLOPT_CAPATH
 
 This release includes the following known bugs:
 
index 0ff18324579edb4418ddf9cc76c2b6c349d5c907..33b5e0269ba240cf53dacd8f0c526b4b31e484ab 100644 (file)
@@ -394,11 +394,11 @@ may be loaded.
 If this option is used several times, the last one will be used.
 .IP "--capath <CA certificate directory>"
 (SSL) Tells curl to use the specified certificate directory to verify the
-peer. The certificates must be in PEM format, and the directory must have been
-processed using the c_rehash utility supplied with openssl. Using
-\fI--capath\fP can allow curl to make SSL-connections much more efficiently
-than using \fI--cacert\fP if the \fI--cacert\fP file contains many CA
-certificates.
+peer. The certificates must be in PEM format, and if curl is built against
+OpenSSL, the directory must have been processed using the c_rehash utility
+supplied with OpenSSL. Using \fI--capath\fP can allow OpenSSL-powered curl to
+make SSL-connections much more efficiently than using \fI--cacert\fP if the
+\fI--cacert\fP file contains many CA certificates.
 
 If this option is used several times, the last one will be used.
 .IP "-f/--fail"
index 8c14c7dcce436b413b6c5c7bfcb1c50f24f06b53..bd342a125add1767c239f0865c10490284e52e94 100644 (file)
@@ -1924,13 +1924,15 @@ mismatch with the issuer of peer certificate (\fICURLOPT_SSL_VERIFYPEER\fP has
 to be set too for the check to fail). (Added in 7.19.0)
 .IP CURLOPT_CAPATH
 Pass a char * to a zero terminated string naming a directory holding multiple
-CA certificates to verify the peer with. The certificate directory must be
-prepared using the openssl c_rehash utility. This makes sense only when used
-in combination with the \fICURLOPT_SSL_VERIFYPEER\fP option.  If
-\fICURLOPT_SSL_VERIFYPEER\fP is zero, \fICURLOPT_CAPATH\fP need not even
-indicate an accessible path.  The \fICURLOPT_CAPATH\fP function apparently
-does not work in Windows due to some limitation in openssl. This option is
-OpenSSL-specific and does nothing if libcurl is built to use GnuTLS.
+CA certificates to verify the peer with. If libcurl is built against OpenSSL,
+the certificate directory must be prepared using the openssl c_rehash utility.
+This makes sense only when used in combination with the
+\fICURLOPT_SSL_VERIFYPEER\fP option.  If \fICURLOPT_SSL_VERIFYPEER\fP is zero,
+\fICURLOPT_CAPATH\fP need not even indicate an accessible path.  The
+\fICURLOPT_CAPATH\fP function apparently does not work in Windows due to some
+limitation in openssl. This option is OpenSSL-specific and does nothing if
+libcurl is built to use GnuTLS. NSS-powered libcurl provides the option only
+for backward compatibility.
 .IP CURLOPT_CRLFILE
 Pass a char * to a zero terminated string naming a file with the concatenation
 of CRL (in PEM format) to use in the certificate validation that occurs during
index a5e11e2fcdae14c1b359957ddbb54635e26609a9..3d3e1c92c591f41b20554a4abc2d623b7fcd49d5 100644 (file)
--- a/lib/nss.c
+++ b/lib/nss.c
@@ -1108,6 +1108,55 @@ static bool handle_cc_error(PRInt32 err, struct SessionHandle *data)
 static Curl_recv nss_recv;
 static Curl_send nss_send;
 
+static CURLcode nss_load_ca_certificates(struct connectdata *conn,
+                                         int sockindex)
+{
+  struct SessionHandle *data = conn->data;
+  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(capath) {
+    struct_stat st;
+    if(stat(capath, &st) == -1)
+      return CURLE_SSL_CACERT_BADFILE;
+
+    if(S_ISDIR(st.st_mode)) {
+      PRDirEntry *entry;
+      PRDir *dir = PR_OpenDir(capath);
+      if(!dir)
+        return CURLE_SSL_CACERT_BADFILE;
+
+      while((entry = PR_ReadDir(dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN))) {
+        char *fullpath = aprintf("%s/%s", capath, entry->name);
+        if(!fullpath) {
+          PR_CloseDir(dir);
+          return CURLE_OUT_OF_MEMORY;
+        }
+
+        if(!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);
+
+        free(fullpath);
+      }
+
+      PR_CloseDir(dir);
+    }
+    else
+      infof(data, "warning: CURLOPT_CAPATH not a directory (%s)\n", capath);
+  }
+
+  infof(data, "  CAfile: %s\n  CApath: %s\n",
+      cafile ? cafile : "none",
+      capath ? capath : "none");
+
+  return CURLE_OK;
+}
+
 CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
 {
   PRInt32 err;
@@ -1249,62 +1298,9 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
                            NULL) != SECSuccess)
     goto error;
 
-  if(!data->set.ssl.verifypeer)
-    /* skip the verifying of the peer */
-    ;
-  else if(data->set.ssl.CAfile) {
-    int rc = nss_load_cert(&conn->ssl[sockindex], data->set.ssl.CAfile,
-                           PR_TRUE);
-    if(!rc) {
-      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 rc;
-
-      dir = PR_OpenDir(data->set.ssl.CApath);
-      do {
-        entry = PR_ReadDir(dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN);
-
-        if(entry) {
-          char *fullpath;
-          size_t pathlen = strlen(data->set.ssl.CApath) +
-            strlen(entry->name) + 2; /* add two, for slash and trailing zero */
-          fullpath = malloc(pathlen);
-          if(!fullpath) {
-            PR_CloseDir(dir);
-            curlerr = CURLE_OUT_OF_MEMORY;
-            goto error;
-          }
-
-          snprintf(fullpath, pathlen, "%s/%s", data->set.ssl.CApath,
-                   entry->name);
-          rc = nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE);
-          /* FIXME: check this return value! */
-          free(fullpath);
-        }
-        /* 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 && (CURLE_OK !=
+        (curlerr = nss_load_ca_certificates(conn, sockindex))))
+    goto error;
 
   if (data->set.ssl.CRLfile) {
     if(SECSuccess != nss_load_crl(data->set.ssl.CRLfile)) {