diff --git a/RELEASE-NOTES b/RELEASE-NOTES index aa67721ab..926b201d2 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -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: diff --git a/docs/curl.1 b/docs/curl.1 index 0ff183245..33b5e0269 100644 --- a/docs/curl.1 +++ b/docs/curl.1 @@ -394,11 +394,11 @@ may be loaded. If this option is used several times, the last one will be used. .IP "--capath " (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" diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index 8c14c7dcc..bd342a125 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -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 diff --git a/lib/nss.c b/lib/nss.c index a5e11e2fc..3d3e1c92c 100644 --- 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)) {