diff options
author | Gilles Vollant <info@winimage.com> | 2020-07-13 03:17:56 +0200 |
---|---|---|
committer | Jay Satiro <raysatiro@yahoo.com> | 2021-05-05 02:29:16 -0400 |
commit | 77fc3859b24470b5c173174e2aba3b099b92adfd (patch) | |
tree | 010c3dfb572cd78b1b88187bb5dfc4200eeb1638 | |
parent | 70cf50fb4aa5e55ca2e732716a4f455d39192088 (diff) | |
download | curl-77fc3859b24470b5c173174e2aba3b099b92adfd.tar.gz |
SSL: support in-memory CA certs for some backends
- New options CURLOPT_CAINFO_BLOB and CURLOPT_PROXY_CAINFO_BLOB to
specify in-memory PEM certificates for OpenSSL, Schannel (Windows)
and Secure Transport (Apple) SSL backends.
Prior to this change PEM certificates could only be imported from a file
and not from memory.
Co-authored-by: moparisthebest@users.noreply.github.com
Ref: https://github.com/curl/curl/pull/4679
Ref: https://github.com/curl/curl/pull/5677
Ref: https://github.com/curl/curl/pull/6109
Closes https://github.com/curl/curl/pull/6662
-rw-r--r-- | docs/libcurl/curl_easy_setopt.3 | 4 | ||||
-rw-r--r-- | docs/libcurl/opts/CURLOPT_CAINFO.3 | 2 | ||||
-rw-r--r-- | docs/libcurl/opts/CURLOPT_CAINFO_BLOB.3 | 68 | ||||
-rw-r--r-- | docs/libcurl/opts/CURLOPT_PROXY_CAINFO.3 | 3 | ||||
-rw-r--r-- | docs/libcurl/opts/CURLOPT_PROXY_CAINFO_BLOB.3 | 75 | ||||
-rw-r--r-- | docs/libcurl/opts/Makefile.inc | 2 | ||||
-rw-r--r-- | docs/libcurl/symbols-in-versions | 2 | ||||
-rw-r--r-- | include/curl/curl.h | 8 | ||||
-rw-r--r-- | lib/doh.c | 4 | ||||
-rw-r--r-- | lib/easyoptions.c | 4 | ||||
-rw-r--r-- | lib/setopt.c | 27 | ||||
-rw-r--r-- | lib/url.c | 3 | ||||
-rw-r--r-- | lib/urldata.h | 3 | ||||
-rw-r--r-- | lib/vtls/openssl.c | 83 | ||||
-rw-r--r-- | lib/vtls/schannel.c | 7 | ||||
-rw-r--r-- | lib/vtls/schannel_verify.c | 271 | ||||
-rw-r--r-- | lib/vtls/sectransp.c | 78 | ||||
-rw-r--r-- | lib/vtls/vtls.c | 3 | ||||
-rw-r--r-- | lib/vtls/vtls.h | 1 | ||||
-rw-r--r-- | packages/OS400/curl.inc.in | 4 | ||||
-rw-r--r-- | tests/data/Makefile.inc | 2 | ||||
-rw-r--r-- | tests/data/test678 | 59 | ||||
-rw-r--r-- | tests/libtest/Makefile.inc | 6 | ||||
-rw-r--r-- | tests/libtest/lib678.c | 120 |
24 files changed, 697 insertions, 142 deletions
diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index 62d4bee2f..0c4f9b7ca 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -584,8 +584,12 @@ Verify the DOH (DNS-over-HTTPS) SSL certificate's status. See \fICURLOPT_DOH_SSL_VERIFYSTATUS(3)\fP .IP CURLOPT_CAINFO CA cert bundle. See \fICURLOPT_CAINFO(3)\fP +.IP CURLOPT_CAINFO_BLOB +CA cert bundle memory buffer. See \fICURLOPT_CAINFO_BLOB(3)\fP .IP CURLOPT_PROXY_CAINFO Proxy CA cert bundle. See \fICURLOPT_PROXY_CAINFO(3)\fP +.IP CURLOPT_PROXY_CAINFO_BLOB +Proxy CA cert bundle memory buffer. See \fICURLOPT_PROXY_CAINFO_BLOB(3)\fP .IP CURLOPT_ISSUERCERT Issuer certificate. See \fICURLOPT_ISSUERCERT(3)\fP .IP CURLOPT_ISSUERCERT_BLOB diff --git a/docs/libcurl/opts/CURLOPT_CAINFO.3 b/docs/libcurl/opts/CURLOPT_CAINFO.3 index e7eb8b01d..fe7c8187f 100644 --- a/docs/libcurl/opts/CURLOPT_CAINFO.3 +++ b/docs/libcurl/opts/CURLOPT_CAINFO.3 @@ -79,5 +79,5 @@ option is ignored. Schannel support added in libcurl 7.60. Returns CURLE_OK if the option is supported, CURLE_UNKNOWN_OPTION if not, or CURLE_OUT_OF_MEMORY if there was insufficient heap space. .SH "SEE ALSO" -.BR CURLOPT_CAPATH "(3), " +.BR CURLOPT_CAINFO_BLOB "(3), " CURLOPT_CAPATH "(3), " .BR CURLOPT_SSL_VERIFYPEER "(3), " CURLOPT_SSL_VERIFYHOST "(3), " diff --git a/docs/libcurl/opts/CURLOPT_CAINFO_BLOB.3 b/docs/libcurl/opts/CURLOPT_CAINFO_BLOB.3 new file mode 100644 index 000000000..c980d99d0 --- /dev/null +++ b/docs/libcurl/opts/CURLOPT_CAINFO_BLOB.3 @@ -0,0 +1,68 @@ +.\" ************************************************************************** +.\" * _ _ ____ _ +.\" * Project ___| | | | _ \| | +.\" * / __| | | | |_) | | +.\" * | (__| |_| | _ <| |___ +.\" * \___|\___/|_| \_\_____| +.\" * +.\" * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. +.\" * +.\" * This software is licensed as described in the file COPYING, which +.\" * you should have received as part of this distribution. The terms +.\" * are also available at https://curl.se/docs/copyright.html. +.\" * +.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell +.\" * copies of the Software, and permit persons to whom the Software is +.\" * furnished to do so, under the terms of the COPYING file. +.\" * +.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +.\" * KIND, either express or implied. +.\" * +.\" ************************************************************************** +.\" +.TH CURLOPT_CAINFO_BLOB 3 "31 March 2021" "libcurl 7.77.0" "curl_easy_setopt options" +.SH NAME +CURLOPT_CAINFO_BLOB \- Certificate Authority (CA) bundle in PEM format +.SH SYNOPSIS +#include <curl/curl.h> + +CURLcode curl_easy_setopt(CURL *handle, CURLOPT_CAINFO_BLOB, struct curl_blob *stblob); +.SH DESCRIPTION +Pass a pointer to a curl_blob structure, which contains information (pointer +and size) about a memory block with binary data of PEM encoded content holding +one or more certificates to verify the HTTPS server with. + +If \fICURLOPT_SSL_VERIFYPEER(3)\fP is zero and you avoid verifying the +server's certificate, \fICURLOPT_CAINFO_BLOB(3)\fP is not needed. + +This option overrides \fICURLOPT_CAINFO(3)\fP. +.SH DEFAULT +NULL +.SH PROTOCOLS +All TLS based protocols: HTTPS, FTPS, IMAPS, POP3S, SMTPS etc. +.SH EXAMPLE +.nf +char *strpem; /* strpem must point to a PEM string */ +CURL *curl = curl_easy_init(); +if(curl) { + struct curl_blob blob; + curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); + blob.data = strpem; + blob.len = strlen(strpem); + blob.flags = CURL_BLOB_COPY; + curl_easy_setopt(curl, CURLOPT_CAINFO_BLOB, &blob); + ret = curl_easy_perform(curl); + curl_easy_cleanup(curl); +} +.fi +.SH AVAILABILITY +Added in 7.77.0. + +This option is supported by the OpenSSL, Secure +Transport and Schannel backends. +.SH RETURN VALUE +Returns CURLE_OK if the option is supported, CURLE_UNKNOWN_OPTION if not, or +CURLE_OUT_OF_MEMORY if there was insufficient heap space. +.SH "SEE ALSO" +.BR CURLOPT_CAINFO "(3), " CURLOPT_CAPATH "(3), " +.BR CURLOPT_SSL_VERIFYPEER "(3), " CURLOPT_SSL_VERIFYHOST "(3), " diff --git a/docs/libcurl/opts/CURLOPT_PROXY_CAINFO.3 b/docs/libcurl/opts/CURLOPT_PROXY_CAINFO.3 index 17a870bb3..b0434d2b9 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_CAINFO.3 +++ b/docs/libcurl/opts/CURLOPT_PROXY_CAINFO.3 @@ -77,7 +77,8 @@ https://curl.se/docs/ssl-compared.html Returns CURLE_OK if the option is supported, CURLE_UNKNOWN_OPTION if not, or CURLE_OUT_OF_MEMORY if there was insufficient heap space. .SH "SEE ALSO" -.BR CURLOPT_PROXY_CAPATH "(3), " +.BR CURLOPT_PROXY_CAINFO_BLOB "(3), " CURLOPT_PROXY_CAPATH "(3), " .BR CURLOPT_PROXY_SSL_VERIFYPEER "(3), " CURLOPT_PROXY_SSL_VERIFYHOST "(3), " +.BR CURLOPT_CAINFO "(3), " CURLOPT_CAINFO_BLOB "(3), " .BR CURLOPT_CAPATH "(3), " .BR CURLOPT_SSL_VERIFYPEER "(3), " CURLOPT_SSL_VERIFYHOST "(3), " diff --git a/docs/libcurl/opts/CURLOPT_PROXY_CAINFO_BLOB.3 b/docs/libcurl/opts/CURLOPT_PROXY_CAINFO_BLOB.3 new file mode 100644 index 000000000..d1c15fcb2 --- /dev/null +++ b/docs/libcurl/opts/CURLOPT_PROXY_CAINFO_BLOB.3 @@ -0,0 +1,75 @@ +.\" ************************************************************************** +.\" * _ _ ____ _ +.\" * Project ___| | | | _ \| | +.\" * / __| | | | |_) | | +.\" * | (__| |_| | _ <| |___ +.\" * \___|\___/|_| \_\_____| +.\" * +.\" * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. +.\" * +.\" * This software is licensed as described in the file COPYING, which +.\" * you should have received as part of this distribution. The terms +.\" * are also available at https://curl.se/docs/copyright.html. +.\" * +.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell +.\" * copies of the Software, and permit persons to whom the Software is +.\" * furnished to do so, under the terms of the COPYING file. +.\" * +.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +.\" * KIND, either express or implied. +.\" * +.\" ************************************************************************** +.\" +.TH CURLOPT_PROXY_CAINFO_BLOB 3 "31 March 2021" "libcurl 7.77.0" "curl_easy_setopt options" +.SH NAME +CURLOPT_PROXY_CAINFO_BLOB \- proxy Certificate Authority (CA) bundle in PEM format +.SH SYNOPSIS +#include <curl/curl.h> + +CURLcode curl_easy_setopt(CURL *handle, CURLOPT_PROXY_CAINFO_BLOB, struct curl_blob *stblob); +.SH DESCRIPTION +This option is for connecting to an HTTPS proxy, not an HTTPS server. + +Pass a pointer to a curl_blob structure, which contains information (pointer +and size) about a memory block with binary data of PEM encoded content holding +one or more certificates to verify the HTTPS proxy with. + +If \fICURLOPT_PROXY_SSL_VERIFYPEER(3)\fP is zero and you avoid verifying the +server's certificate, \fICURLOPT_PROXY_CAINFO_BLOB(3)\fP is not needed. + +This option overrides \fICURLOPT_PROXY_CAINFO(3)\fP. +.SH DEFAULT +NULL +.SH PROTOCOLS +Used with HTTPS proxy +.SH EXAMPLE +.nf +char *strpem; /* strpem must point to a PEM string */ +CURL *curl = curl_easy_init(); +if(curl) { + struct curl_blob blob; + curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); + /* using an HTTPS proxy */ + curl_easy_setopt(curl, CURLOPT_PROXY, "https://localhost:443"); + blob.data = strpem; + blob.len = strlen(strpem); + blob.flags = CURL_BLOB_COPY; + curl_easy_setopt(curl, CURLOPT_PROXY_CAINFO_BLOB, &blob); + ret = curl_easy_perform(curl); + curl_easy_cleanup(curl); +} +.fi +.SH AVAILABILITY +Added in 7.77.0. + +This option is supported by the OpenSSL, Secure +Transport and Schannel backends. +.SH RETURN VALUE +Returns CURLE_OK if the option is supported, CURLE_UNKNOWN_OPTION if not, or +CURLE_OUT_OF_MEMORY if there was insufficient heap space. +.SH "SEE ALSO" +.BR CURLOPT_PROXY_CAINFO "(3), " CURLOPT_PROXY_CAPATH "(3), " +.BR CURLOPT_PROXY_SSL_VERIFYPEER "(3), " CURLOPT_PROXY_SSL_VERIFYHOST "(3), " +.BR CURLOPT_CAINFO "(3), " CURLOPT_CAINFO_BLOB "(3), " +.BR CURLOPT_CAPATH "(3), " +.BR CURLOPT_SSL_VERIFYPEER "(3), " CURLOPT_SSL_VERIFYHOST "(3), " diff --git a/docs/libcurl/opts/Makefile.inc b/docs/libcurl/opts/Makefile.inc index 69e241862..1181331b9 100644 --- a/docs/libcurl/opts/Makefile.inc +++ b/docs/libcurl/opts/Makefile.inc @@ -115,6 +115,7 @@ man_MANS = \ CURLOPT_AUTOREFERER.3 \ CURLOPT_BUFFERSIZE.3 \ CURLOPT_CAINFO.3 \ + CURLOPT_CAINFO_BLOB.3 \ CURLOPT_CAPATH.3 \ CURLOPT_CERTINFO.3 \ CURLOPT_CHUNK_BGN_FUNCTION.3 \ @@ -267,6 +268,7 @@ man_MANS = \ CURLOPT_PROXYUSERNAME.3 \ CURLOPT_PROXYUSERPWD.3 \ CURLOPT_PROXY_CAINFO.3 \ + CURLOPT_PROXY_CAINFO_BLOB.3 \ CURLOPT_PROXY_CAPATH.3 \ CURLOPT_PROXY_CRLFILE.3 \ CURLOPT_PROXY_KEYPASSWD.3 \ diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index 9e27f5ef0..4fb46f801 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -374,6 +374,7 @@ CURLOPT_APPEND 7.17.0 CURLOPT_AUTOREFERER 7.1 CURLOPT_BUFFERSIZE 7.10 CURLOPT_CAINFO 7.4.2 +CURLOPT_CAINFO_BLOB 7.77.0 CURLOPT_CAPATH 7.9.8 CURLOPT_CERTINFO 7.19.1 CURLOPT_CHUNK_BGN_FUNCTION 7.21.0 @@ -543,6 +544,7 @@ CURLOPT_PROXYTYPE 7.10 CURLOPT_PROXYUSERNAME 7.19.1 CURLOPT_PROXYUSERPWD 7.1 CURLOPT_PROXY_CAINFO 7.52.0 +CURLOPT_PROXY_CAINFO_BLOB 7.77.0 CURLOPT_PROXY_CAPATH 7.52.0 CURLOPT_PROXY_CRLFILE 7.52.0 CURLOPT_PROXY_ISSUERCERT 7.71.0 diff --git a/include/curl/curl.h b/include/curl/curl.h index 1354fba32..e8f9db52f 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -2093,6 +2093,14 @@ typedef enum { /* Same as CURLOPT_SSL_VERIFYSTATUS but for DOH (DNS-over-HTTPS) servers. */ CURLOPT(CURLOPT_DOH_SSL_VERIFYSTATUS, CURLOPTTYPE_LONG, 308), + /* The CA certificates as "blob" used to validate the peer certificate + this option is used only if SSL_VERIFYPEER is true */ + CURLOPT(CURLOPT_CAINFO_BLOB, CURLOPTTYPE_BLOB, 309), + + /* The CA certificates as "blob" used to validate the proxy certificate + this option is used only if PROXY_SSL_VERIFYPEER is true */ + CURLOPT(CURLOPT_PROXY_CAINFO_BLOB, CURLOPTTYPE_BLOB, 310), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; @@ -313,6 +313,10 @@ static CURLcode dohprobe(struct Curl_easy *data, ERROR_CHECK_SETOPT(CURLOPT_CAINFO, data->set.str[STRING_SSL_CAFILE]); } + if(data->set.blobs[BLOB_CAINFO]) { + ERROR_CHECK_SETOPT(CURLOPT_CAINFO_BLOB, + data->set.blobs[BLOB_CAINFO]); + } if(data->set.str[STRING_SSL_CAPATH]) { ERROR_CHECK_SETOPT(CURLOPT_CAPATH, data->set.str[STRING_SSL_CAPATH]); diff --git a/lib/easyoptions.c b/lib/easyoptions.c index db8337b04..4e65e3525 100644 --- a/lib/easyoptions.c +++ b/lib/easyoptions.c @@ -38,6 +38,7 @@ struct curl_easyoption Curl_easyopts[] = { {"AWS_SIGV4", CURLOPT_AWS_SIGV4, CURLOT_STRING, 0}, {"BUFFERSIZE", CURLOPT_BUFFERSIZE, CURLOT_LONG, 0}, {"CAINFO", CURLOPT_CAINFO, CURLOT_STRING, 0}, + {"CAINFO_BLOB", CURLOPT_CAINFO_BLOB, CURLOT_BLOB, 0}, {"CAPATH", CURLOPT_CAPATH, CURLOT_STRING, 0}, {"CERTINFO", CURLOPT_CERTINFO, CURLOT_LONG, 0}, {"CHUNK_BGN_FUNCTION", CURLOPT_CHUNK_BGN_FUNCTION, CURLOT_FUNCTION, 0}, @@ -205,6 +206,7 @@ struct curl_easyoption Curl_easyopts[] = { {"PROXYUSERNAME", CURLOPT_PROXYUSERNAME, CURLOT_STRING, 0}, {"PROXYUSERPWD", CURLOPT_PROXYUSERPWD, CURLOT_STRING, 0}, {"PROXY_CAINFO", CURLOPT_PROXY_CAINFO, CURLOT_STRING, 0}, + {"PROXY_CAINFO_BLOB", CURLOPT_PROXY_CAINFO_BLOB, CURLOT_BLOB, 0}, {"PROXY_CAPATH", CURLOPT_PROXY_CAPATH, CURLOT_STRING, 0}, {"PROXY_CRLFILE", CURLOPT_PROXY_CRLFILE, CURLOT_STRING, 0}, {"PROXY_ISSUERCERT", CURLOPT_PROXY_ISSUERCERT, CURLOT_STRING, 0}, @@ -352,6 +354,6 @@ struct curl_easyoption Curl_easyopts[] = { */ int Curl_easyopts_check(void) { - return ((CURLOPT_LASTENTRY%10000) != (308 + 1)); + return ((CURLOPT_LASTENTRY%10000) != (310 + 1)); } #endif diff --git a/lib/setopt.c b/lib/setopt.c index 16b83321d..9ad984e79 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -2041,6 +2041,20 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) result = Curl_setstropt(&data->set.str[STRING_SSL_CAFILE], va_arg(param, char *)); break; + case CURLOPT_CAINFO_BLOB: + /* + * Blob that holds CA info for SSL connection. + * Specify entire PEM of the CA certificate + */ +#ifdef USE_SSL + if(Curl_ssl->supports & SSLSUPP_CAINFO_BLOB) + result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO], + va_arg(param, struct curl_blob *)); + else +#endif + return CURLE_NOT_BUILT_IN; + + break; #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_CAINFO: /* @@ -2050,6 +2064,19 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) result = Curl_setstropt(&data->set.str[STRING_SSL_CAFILE_PROXY], va_arg(param, char *)); break; + case CURLOPT_PROXY_CAINFO_BLOB: + /* + * Blob that holds CA info for SSL connection proxy. + * Specify entire PEM of the CA certificate + */ +#ifdef USE_SSL + if(Curl_ssl->supports & SSLSUPP_CAINFO_BLOB) + result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO_PROXY], + va_arg(param, struct curl_blob *)); + else +#endif + return CURLE_NOT_BUILT_IN; + break; #endif case CURLOPT_CAPATH: /* @@ -3738,6 +3738,7 @@ static CURLcode create_conn(struct Curl_easy *data, data->set.ssl.primary.pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT]; + data->set.ssl.primary.ca_info_blob = data->set.blobs[BLOB_CAINFO]; data->set.ssl.primary.curves = data->set.str[STRING_SSL_EC_CURVES]; #ifndef CURL_DISABLE_PROXY @@ -3753,6 +3754,8 @@ static CURLcode create_conn(struct Curl_easy *data, data->set.proxy_ssl.primary.pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]; data->set.proxy_ssl.primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY]; + data->set.proxy_ssl.primary.ca_info_blob = + data->set.blobs[BLOB_CAINFO_PROXY]; data->set.proxy_ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE_PROXY]; data->set.proxy_ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT_PROXY]; data->set.proxy_ssl.cert_type = data->set.str[STRING_CERT_TYPE_PROXY]; diff --git a/lib/urldata.h b/lib/urldata.h index c8797698a..04daf778a 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -253,6 +253,7 @@ struct ssl_primary_config { char *cipher_list13; /* list of TLS 1.3 cipher suites to use */ char *pinned_key; struct curl_blob *cert_blob; + struct curl_blob *ca_info_blob; char *curves; /* list of curves to use */ BIT(verifypeer); /* set TRUE if this is desired */ BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */ @@ -1604,6 +1605,8 @@ enum dupblob { BLOB_KEY_PROXY, BLOB_SSL_ISSUERCERT, BLOB_SSL_ISSUERCERT_PROXY, + BLOB_CAINFO, + BLOB_CAINFO_PROXY, BLOB_LAST }; diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index de484d563..ded79dc70 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -2510,6 +2510,67 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) return res; } +static CURLcode load_cacert_from_memory(SSL_CTX *ctx, + const struct curl_blob *ca_info_blob) +{ + /* these need freed at the end */ + BIO *cbio = NULL; + STACK_OF(X509_INFO) *inf = NULL; + + /* everything else is just a reference */ + int i, count = 0; + X509_STORE *cts = NULL; + X509_INFO *itmp = NULL; + + if(ca_info_blob->len > (size_t)INT_MAX) + return CURLE_SSL_CACERT_BADFILE; + + cts = SSL_CTX_get_cert_store(ctx); + if(!cts) + return CURLE_OUT_OF_MEMORY; + + cbio = BIO_new_mem_buf(ca_info_blob->data, (int)ca_info_blob->len); + if(!cbio) + return CURLE_OUT_OF_MEMORY; + + inf = PEM_X509_INFO_read_bio(cbio, NULL, NULL, NULL); + if(!inf) { + BIO_free(cbio); + return CURLE_SSL_CACERT_BADFILE; + } + + /* add each entry from PEM file to x509_store */ + for(i = 0; i < (int)sk_X509_INFO_num(inf); ++i) { + itmp = sk_X509_INFO_value(inf, i); + if(itmp->x509) { + if(X509_STORE_add_cert(cts, itmp->x509)) { + ++count; + } + else { + /* set count to 0 to return an error */ + count = 0; + break; + } + } + if(itmp->crl) { + if(X509_STORE_add_crl(cts, itmp->crl)) { + ++count; + } + else { + /* set count to 0 to return an error */ + count = 0; + break; + } + } + } + + sk_X509_INFO_pop_free(inf, X509_INFO_free); + BIO_free(cbio); + + /* if we didn't end up importing anything, treat that as an error */ + return (count > 0 ? CURLE_OK : CURLE_SSL_CACERT_BADFILE); +} + static CURLcode ossl_connect_step1(struct Curl_easy *data, struct connectdata *conn, int sockindex) { @@ -2537,8 +2598,11 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, #endif char * const ssl_cert = SSL_SET_OPTION(primary.clientcert); const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob); + const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob); const char * const ssl_cert_type = SSL_SET_OPTION(cert_type); - const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile); + const char * const ssl_cafile = + /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ + (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile)); const char * const ssl_capath = SSL_CONN_CONFIG(CApath); const bool verifypeer = SSL_CONN_CONFIG(verifypeer); const char * const ssl_crlfile = SSL_SET_OPTION(CRLfile); @@ -2962,6 +3026,19 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, } #endif + if(ca_info_blob) { + result = load_cacert_from_memory(backend->ctx, ca_info_blob); + if(result) { + if(result == CURLE_OUT_OF_MEMORY || + (verifypeer && !imported_native_ca)) { + failf(data, "error importing CA certificate blob"); + return result; + } + /* Only warning if no certificate verification is required. */ + infof(data, "error importing CA certificate blob, continuing anyway\n"); + } + } + #if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ { @@ -3018,7 +3095,8 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, #endif #ifdef CURL_CA_FALLBACK - if(verifypeer && !ssl_cafile && !ssl_capath && !imported_native_ca) { + if(verifypeer && + !ca_info_blob && !ssl_cafile && !ssl_capath && !imported_native_ca) { /* verifying the peer without any CA certificates won't work so use openssl's built in default as fallback */ SSL_CTX_set_default_verify_paths(backend->ctx); @@ -4425,6 +4503,7 @@ const struct Curl_ssl Curl_ssl_openssl = { { CURLSSLBACKEND_OPENSSL, "openssl" }, /* info */ SSLSUPP_CA_PATH | + SSLSUPP_CAINFO_BLOB | SSLSUPP_CERTINFO | SSLSUPP_PINNEDPUBKEY | SSLSUPP_SSL_CTX | diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 4ddab55d2..d3fb642d1 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -473,7 +473,7 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, #endif #else #ifdef HAS_MANUAL_VERIFY_API - if(SSL_CONN_CONFIG(CAfile)) { + if(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) { if(curlx_verify_windows_version(6, 1, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) { BACKEND->use_manual_cred_validation = true; @@ -487,7 +487,7 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, else BACKEND->use_manual_cred_validation = false; #else - if(SSL_CONN_CONFIG(CAfile)) { + if(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) { failf(data, "schannel: CA cert support not built in"); return CURLE_NOT_BUILT_IN; } @@ -2403,6 +2403,9 @@ const struct Curl_ssl Curl_ssl_schannel = { { CURLSSLBACKEND_SCHANNEL, "schannel" }, /* info */ SSLSUPP_CERTINFO | +#ifdef HAS_MANUAL_VERIFY_API + SSLSUPP_CAINFO_BLOB | +#endif SSLSUPP_PINNEDPUBKEY, sizeof(struct ssl_backend_data), diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index 25b7767a2..25d47b808 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -77,21 +77,156 @@ static int is_cr_or_lf(char c) return c == '\r' || c == '\n'; } -static CURLcode add_certs_to_store(HCERTSTORE trust_store, - const char *ca_file, - struct Curl_easy *data) +/* Search the substring needle,needlelen into string haystack,haystacklen + * Strings don't need to be terminated by a '\0'. + * Similar of OSX/Linux memmem (not available on Visual Studio). + * Return position of beginning of first occurence or NULL if not found + */ +static const char *c_memmem(const void *haystack, size_t haystacklen, + const void *needle, size_t needlelen) +{ + const char *p; + char first; + const char *str_limit = (const char *)haystack + haystacklen; + if(!needlelen || needlelen > haystacklen) + return NULL; + first = *(const char *)needle; + for(p = (const char *)haystack; p <= (str_limit - needlelen); p++) + if(((*p) == first) && (memcmp(p, needle, needlelen) == 0)) + return p; + + return NULL; +} + +static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, + const char *ca_buffer, + size_t ca_buffer_size, + const char *ca_file_text, + struct Curl_easy *data) +{ + const size_t begin_cert_len = strlen(BEGIN_CERT); + const size_t end_cert_len = strlen(END_CERT); + CURLcode result = CURLE_OK; + int num_certs = 0; + bool more_certs = 1; + const char *current_ca_file_ptr = ca_buffer; + const char *ca_buffer_limit = ca_buffer + ca_buffer_size; + + while(more_certs && (current_ca_file_ptr<ca_buffer_limit)) { + const char *begin_cert_ptr = c_memmem(current_ca_file_ptr, + ca_buffer_limit-current_ca_file_ptr, + BEGIN_CERT, + begin_cert_len); + if(!begin_cert_ptr || !is_cr_or_lf(begin_cert_ptr[begin_cert_len])) { + more_certs = 0; + } + else { + const char *end_cert_ptr = c_memmem(begin_cert_ptr, + ca_buffer_limit-begin_cert_ptr, + END_CERT, + end_cert_len); + if(!end_cert_ptr) { + failf(data, + "schannel: CA file '%s' is not correctly formatted", + ca_file_text); + result = CURLE_SSL_CACERT_BADFILE; + more_certs = 0; + } + else { + CERT_BLOB cert_blob; + CERT_CONTEXT *cert_context = NULL; + BOOL add_cert_result = FALSE; + DWORD actual_content_type = 0; + DWORD cert_size = (DWORD) + ((end_cert_ptr + end_cert_len) - begin_cert_ptr); + + cert_blob.pbData = (BYTE *)begin_cert_ptr; + cert_blob.cbData = cert_size; + if(!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, + &cert_blob, + CERT_QUERY_CONTENT_FLAG_CERT, + CERT_QUERY_FORMAT_FLAG_ALL, + 0, + NULL, + &actual_content_type, + NULL, + NULL, + NULL, + (const void **)&cert_context)) { + char buffer[STRERROR_LEN]; + failf(data, + "schannel: failed to extract certificate from CA file " + "'%s': %s", + ca_file_text, + Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); + result = CURLE_SSL_CACERT_BADFILE; + more_certs = 0; + } + else { + current_ca_file_ptr = begin_cert_ptr + cert_size; + + /* Sanity check that the cert_context object is the right type */ + if(CERT_QUERY_CONTENT_CERT != actual_content_type) { + failf(data, + "schannel: unexpected content type '%d' when extracting " + "certificate from CA file '%s'", + actual_content_type, ca_file_text); + result = CURLE_SSL_CACERT_BADFILE; + more_certs = 0; + } + else { + add_cert_result = + CertAddCertificateContextToStore(trust_store, + cert_context, + CERT_STORE_ADD_ALWAYS, + NULL); + CertFreeCertificateContext(cert_context); + if(!add_cert_result) { + char buffer[STRERROR_LEN]; + failf(data, + "schannel: failed to add certificate from CA file '%s' " + "to certificate store: %s", + ca_file_text, + Curl_winapi_strerror(GetLastError(), buffer, + sizeof(buffer))); + result = CURLE_SSL_CACERT_BADFILE; + more_certs = 0; + } + else { + num_certs++; + } + } + } + } + } + } + + if(result == CURLE_OK) { + if(!num_certs) { + infof(data, + "schannel: did not add any certificates from CA file '%s'\n", + ca_file_text); + } + else { + infof(data, + "schannel: added %d certificate(s) from CA file '%s'\n", + num_certs, ca_file_text); + } + } + return result; +} + +static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, + const char *ca_file, + struct Curl_easy *data) { CURLcode result; HANDLE ca_file_handle = INVALID_HANDLE_VALUE; LARGE_INTEGER file_size; char *ca_file_buffer = NULL; - char *current_ca_file_ptr = NULL; TCHAR *ca_file_tstr = NULL; size_t ca_file_bufsize = 0; DWORD total_bytes_read = 0; - bool more_certs = 0; - int num_certs = 0; - size_t END_CERT_LEN; ca_file_tstr = curlx_convert_UTF8_to_tchar((char *)ca_file); if(!ca_file_tstr) { @@ -181,106 +316,10 @@ static CURLcode add_certs_to_store(HCERTSTORE trust_store, if(result != CURLE_OK) { goto cleanup; } - - END_CERT_LEN = strlen(END_CERT); - - more_certs = 1; - current_ca_file_ptr = ca_file_buffer; - while(more_certs && *current_ca_file_ptr != '\0') { - char *begin_cert_ptr = strstr(current_ca_file_ptr, BEGIN_CERT); - if(!begin_cert_ptr || !is_cr_or_lf(begin_cert_ptr[strlen(BEGIN_CERT)])) { - more_certs = 0; - } - else { - char *end_cert_ptr = strstr(begin_cert_ptr, END_CERT); - if(!end_cert_ptr) { - failf(data, - "schannel: CA file '%s' is not correctly formatted", - ca_file); - result = CURLE_SSL_CACERT_BADFILE; - more_certs = 0; - } - else { - CERT_BLOB cert_blob; - CERT_CONTEXT *cert_context = NULL; - BOOL add_cert_result = FALSE; - DWORD actual_content_type = 0; - DWORD cert_size = (DWORD) - ((end_cert_ptr + END_CERT_LEN) - begin_cert_ptr); - - cert_blob.pbData = (BYTE *)begin_cert_ptr; - cert_blob.cbData = cert_size; - if(!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, - &cert_blob, - CERT_QUERY_CONTENT_FLAG_CERT, - CERT_QUERY_FORMAT_FLAG_ALL, - 0, - NULL, - &actual_content_type, - NULL, - NULL, - NULL, - (const void **)&cert_context)) { - char buffer[STRERROR_LEN]; - failf(data, - "schannel: failed to extract certificate from CA file " - "'%s': %s", - ca_file, - Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - more_certs = 0; - } - else { - current_ca_file_ptr = begin_cert_ptr + cert_size; - - /* Sanity check that the cert_context object is the right type */ - if(CERT_QUERY_CONTENT_CERT != actual_content_type) { - failf(data, - "schannel: unexpected content type '%d' when extracting " - "certificate from CA file '%s'", - actual_content_type, ca_file); - result = CURLE_SSL_CACERT_BADFILE; - more_certs = 0; - } - else { - add_cert_result = - CertAddCertificateContextToStore(trust_store, - cert_context, - CERT_STORE_ADD_ALWAYS, - NULL); - CertFreeCertificateContext(cert_context); - if(!add_cert_result) { - char buffer[STRERROR_LEN]; - failf(data, - "schannel: failed to add certificate from CA file '%s' " - "to certificate store: %s", - ca_file, - Curl_winapi_strerror(GetLastError(), buffer, - sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - more_certs = 0; - } - else { - num_certs++; - } - } - } - } - } - } - - if(result == CURLE_OK) { - if(!num_certs) { - infof(data, - "schannel: did not add any certificates from CA file '%s'\n", - ca_file); - } - else { - infof(data, - "schannel: added %d certificate(s) from CA file '%s'\n", - num_certs, ca_file); - } - } + result = add_certs_data_to_store(trust_store, + ca_file_buffer, ca_file_bufsize, + ca_file, + data); cleanup: if(ca_file_handle != INVALID_HANDLE_VALUE) { @@ -550,7 +589,8 @@ CURLcode Curl_verify_certificate(struct Curl_easy *data, result = CURLE_PEER_FAILED_VERIFICATION; } - if(result == CURLE_OK && SSL_CONN_CONFIG(CAfile) && + if(result == CURLE_OK && + (SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) && BACKEND->use_manual_cred_validation) { /* * Create a chain engine that uses the certificates in the CA file as @@ -576,8 +616,19 @@ CURLcode Curl_verify_certificate(struct Curl_easy *data, result = CURLE_SSL_CACERT_BADFILE; } else { - result = add_certs_to_store(trust_store, SSL_CONN_CONFIG(CAfile), - data); + const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob); + if(ca_info_blob) { + result = add_certs_data_to_store(trust_store, + (const char *)ca_info_blob->data, + ca_info_blob->len, + "(memory blob)", + data); + } + else { + result = add_certs_file_to_store(trust_store, + SSL_CONN_CONFIG(CAfile), + data); + } } } diff --git a/lib/vtls/sectransp.c b/lib/vtls/sectransp.c index 6ec37a3cc..4276b89cf 100644 --- a/lib/vtls/sectransp.c +++ b/lib/vtls/sectransp.c @@ -1659,8 +1659,10 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data, curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; - const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile); - const struct curl_blob *ssl_cablob = NULL; + const struct curl_blob *ssl_cablob = SSL_CONN_CONFIG(ca_info_blob); + const char * const ssl_cafile = + /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ + (ssl_cablob ? NULL : SSL_CONN_CONFIG(CAfile)); const bool verifypeer = SSL_CONN_CONFIG(verifypeer); char * const ssl_cert = SSL_SET_OPTION(primary.clientcert); const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob); @@ -2007,7 +2009,8 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data, bool is_cert_file = (!is_cert_data) && is_file(ssl_cafile); if(!(is_cert_file || is_cert_data)) { - failf(data, "SSL: can't load CA certificate file %s", ssl_cafile); + failf(data, "SSL: can't load CA certificate file %s", + ssl_cafile ? ssl_cafile : "(blob memory)"); return CURLE_SSL_CACERT_BADFILE; } } @@ -2084,7 +2087,8 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data, else { CURLcode result; ssl_sessionid = - aprintf("%s:%d:%d:%s:%ld", ssl_cafile, + aprintf("%s:%d:%d:%s:%ld", + ssl_cafile ? ssl_cafile : "(blob memory)", verifypeer, SSL_CONN_CONFIG(verifyhost), hostname, port); ssl_sessionid_len = strlen(ssl_sessionid); @@ -2224,7 +2228,7 @@ static int read_cert(const char *file, unsigned char **out, size_t *outlen) } static int append_cert_to_array(struct Curl_easy *data, - unsigned char *buf, size_t buflen, + const unsigned char *buf, size_t buflen, CFMutableArrayRef array) { CFDataRef certdata = CFDataCreate(kCFAllocatorDefault, buf, buflen); @@ -2262,18 +2266,14 @@ static int append_cert_to_array(struct Curl_easy *data, return CURLE_OK; } -static CURLcode verify_cert(const char *cafile, struct Curl_easy *data, - SSLContextRef ctx) +static CURLcode verify_cert_buf(struct Curl_easy *data, + const unsigned char *certbuf, size_t buflen, + SSLContextRef ctx) { int n = 0, rc; long res; - unsigned char *certbuf, *der; - size_t buflen, derlen, offset = 0; - - if(read_cert(cafile, &certbuf, &buflen) < 0) { - failf(data, "SSL: failed to read or invalid CA certificate"); - return CURLE_SSL_CACERT_BADFILE; - } + unsigned char *der; + size_t derlen, offset = 0; /* * Certbuf now contains the contents of the certificate file, which can be @@ -2287,7 +2287,6 @@ static CURLcode verify_cert(const char *cafile, struct Curl_easy *data, CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if(!array) { - free(certbuf); failf(data, "SSL: out of memory creating CA certificate array"); return CURLE_OUT_OF_MEMORY; } @@ -2301,7 +2300,6 @@ static CURLcode verify_cert(const char *cafile, struct Curl_easy *data, */ res = pem_to_der((const char *)certbuf + offset, &der, &derlen); if(res < 0) { - free(certbuf); CFRelease(array); failf(data, "SSL: invalid CA certificate #%d (offset %zu) in bundle", n, offset); @@ -2312,7 +2310,6 @@ static CURLcode verify_cert(const char *cafile, struct Curl_easy *data, if(res == 0 && offset == 0) { /* This is not a PEM file, probably a certificate in DER format. */ rc = append_cert_to_array(data, certbuf, buflen, array); - free(certbuf); if(rc != CURLE_OK) { CFRelease(array); return rc; @@ -2321,14 +2318,12 @@ static CURLcode verify_cert(const char *cafile, struct Curl_easy *data, } else if(res == 0) { /* No more certificates in the bundle. */ - free(certbuf); break; } rc = append_cert_to_array(data, der, derlen, array); free(der); if(rc != CURLE_OK) { - free(certbuf); CFRelease(array); return rc; } @@ -2385,6 +2380,38 @@ static CURLcode verify_cert(const char *cafile, struct Curl_easy *data, } } +static CURLcode verify_cert(struct Curl_easy *data, const char *cafile, + const struct curl_blob *ca_info_blob, + SSLContextRef ctx) +{ + int result; + unsigned char *certbuf; + size_t buflen; + + if(ca_info_blob) { + certbuf = (unsigned char *)malloc(ca_info_blob->len + 1); + if(!certbuf) { + return CURLE_OUT_OF_MEMORY; + } + buflen = ca_info_blob->len; + memcpy(certbuf, ca_info_blob->data, ca_info_blob->len); + certbuf[ca_info_blob->len]='\0'; + } + else if(cafile) { + if(read_cert(cafile, &certbuf, &buflen) < 0) { + failf(data, "SSL: failed to read or invalid CA certificate"); + return CURLE_SSL_CACERT_BADFILE; + } + } + else + return CURLE_SSL_CACERT_BADFILE; + + result = verify_cert_buf(data, certbuf, buflen, ctx); + free(certbuf); + return result; +} + + #ifdef SECTRANSP_PINNEDPUBKEY static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, SSLContextRef ctx, @@ -2520,8 +2547,10 @@ sectransp_connect_step2(struct Curl_easy *data, struct connectdata *conn, /* The below is errSSLServerAuthCompleted; it's not defined in Leopard's headers */ case -9841: - if(SSL_CONN_CONFIG(CAfile) && SSL_CONN_CONFIG(verifypeer)) { - CURLcode result = verify_cert(SSL_CONN_CONFIG(CAfile), data, + if((SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) && + SSL_CONN_CONFIG(verifypeer)) { + CURLcode result = verify_cert(data, SSL_CONN_CONFIG(CAfile), + SSL_CONN_CONFIG(ca_info_blob), backend->ssl_ctx); if(result) return result; @@ -3365,8 +3394,10 @@ static ssize_t sectransp_recv(struct Curl_easy *data, /* The below is errSSLPeerAuthCompleted; it's not defined in Leopard's headers */ case -9841: - if(SSL_CONN_CONFIG(CAfile) && SSL_CONN_CONFIG(verifypeer)) { - CURLcode result = verify_cert(SSL_CONN_CONFIG(CAfile), data, + if((SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) && + SSL_CONN_CONFIG(verifypeer)) { + CURLcode result = verify_cert(data, SSL_CONN_CONFIG(CAfile), + SSL_CONN_CONFIG(ca_info_blob), backend->ssl_ctx); if(result) return result; @@ -3393,6 +3424,7 @@ static void *sectransp_get_internals(struct ssl_connect_data *connssl, const struct Curl_ssl Curl_ssl_sectransp = { { CURLSSLBACKEND_SECURETRANSPORT, "secure-transport" }, /* info */ + SSLSUPP_CAINFO_BLOB | #ifdef SECTRANSP_PINNEDPUBKEY SSLSUPP_PINNEDPUBKEY, #else diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 4bd60f6b9..d63fd5c76 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -135,6 +135,7 @@ Curl_ssl_config_matches(struct ssl_primary_config *data, (data->verifyhost == needle->verifyhost) && (data->verifystatus == needle->verifystatus) && blobcmp(data->cert_blob, needle->cert_blob) && + blobcmp(data->ca_info_blob, needle->ca_info_blob) && Curl_safe_strcasecompare(data->CApath, needle->CApath) && Curl_safe_strcasecompare(data->CAfile, needle->CAfile) && Curl_safe_strcasecompare(data->clientcert, needle->clientcert) && @@ -161,6 +162,7 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source, dest->sessionid = source->sessionid; CLONE_BLOB(cert_blob); + CLONE_BLOB(ca_info_blob); CLONE_STRING(CApath); CLONE_STRING(CAfile); CLONE_STRING(clientcert); @@ -185,6 +187,7 @@ void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc) Curl_safefree(sslc->cipher_list13); Curl_safefree(sslc->pinned_key); Curl_safefree(sslc->cert_blob); + Curl_safefree(sslc->ca_info_blob); Curl_safefree(sslc->curves); } diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index ac9365dbe..a22d526ca 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -32,6 +32,7 @@ struct ssl_connect_data; #define SSLSUPP_SSL_CTX (1<<3) /* supports CURLOPT_SSL_CTX */ #define SSLSUPP_HTTPS_PROXY (1<<4) /* supports access via HTTPS proxies */ #define SSLSUPP_TLS13_CIPHERSUITES (1<<5) /* supports TLS 1.3 ciphersuites */ +#define SSLSUPP_CAINFO_BLOB (1<<6) struct Curl_ssl { /* diff --git a/packages/OS400/curl.inc.in b/packages/OS400/curl.inc.in index 73aaaccb1..018cabad2 100644 --- a/packages/OS400/curl.inc.in +++ b/packages/OS400/curl.inc.in @@ -1579,6 +1579,10 @@ d c 00307 d CURLOPT_DOH_SSL_VERIFYSTATUS... d c 00308 + d CURLOPT_CAINFO_BLOB... + d c 40309 + d CURLOPT_PROXY_CAINFO_BLOB... + d c 40310 * /if not defined(CURL_NO_OLDIES) d CURLOPT_FILE c 10001 diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc index d21c9fa15..f61527089 100644 --- a/tests/data/Makefile.inc +++ b/tests/data/Makefile.inc @@ -90,7 +90,7 @@ test635 test636 test637 test638 test639 test640 test641 test642 \ test643 test644 test645 test646 test647 test648 test649 test650 test651 \ test652 test653 test654 test655 test656 test658 test659 test660 test661 \ test662 test663 test664 test665 test666 test667 test668 test669 \ -test670 test671 test672 test673 test674 test675 test676 \ +test670 test671 test672 test673 test674 test675 test676 test678 \ \ test700 test701 test702 test703 test704 test705 test706 test707 test708 \ test709 test710 test711 test712 test713 test714 test715 test716 test717 \ diff --git a/tests/data/test678 b/tests/data/test678 new file mode 100644 index 000000000..69ae232b3 --- /dev/null +++ b/tests/data/test678 @@ -0,0 +1,59 @@ +<testcase> +<info> +<keywords> +HTTPS +HTTP GET +PEM certificate +</keywords> +</info> + +# +# Server-side +<reply> +<data> +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 7 + +MooMoo +</data> +</reply> + +# +# Client-side +<client> +<features> +SSL +</features> +<server> +https Server-localhost-sv.pem +</server> + <name> +HTTPS GET using CURLOPT_CAINFO_BLOB + </name> +<tool> +lib%TESTNUMBER +</tool> +# provide URL and ca-cert +<command> +https://localhost:%HTTPSPORT/%TESTNUMBER %SRCDIR/certs/EdelCurlRoot-ca.crt +</command> +# Ensure that we're running on localhost because we're checking the host name +<precheck> +./libtest/lib%TESTNUMBER check +</precheck> +</client> + +# +# Verify data after the test has been "shot" +<verify> +<protocol> +GET /%TESTNUMBER HTTP/1.1
+Host: localhost:%HTTPSPORT
+User-Agent: CURLOPT_CAINFO_BLOB
+Accept: */*
+
+</protocol> +</verify> +</testcase> diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index ff39de377..5c80f88c9 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -48,7 +48,7 @@ noinst_PROGRAMS = chkhostname libauthretry libntlmconnect \ lib599 \ lib643 lib644 lib645 lib650 lib651 lib652 lib653 lib654 lib655 lib658 \ lib659 lib661 lib666 lib667 lib668 \ - lib670 lib671 lib672 lib673 lib674 lib676 \ + lib670 lib671 lib672 lib673 lib674 lib676 lib678 \ lib1156 \ lib1500 lib1501 lib1502 lib1503 lib1504 lib1505 lib1506 lib1507 lib1508 \ lib1509 lib1510 lib1511 lib1512 lib1513 lib1514 lib1515 lib1517 \ @@ -409,6 +409,10 @@ lib676_SOURCES = lib676.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) lib676_LDADD = $(TESTUTIL_LIBS) lib676_CPPFLAGS = $(AM_CPPFLAGS) +lib678_SOURCES = lib678.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) +lib678_LDADD = $(TESTUTIL_LIBS) +lib678_CPPFLAGS = $(AM_CPPFLAGS) + lib1500_SOURCES = lib1500.c $(SUPPORTFILES) $(TESTUTIL) lib1500_LDADD = $(TESTUTIL_LIBS) lib1500_CPPFLAGS = $(AM_CPPFLAGS) diff --git a/tests/libtest/lib678.c b/tests/libtest/lib678.c new file mode 100644 index 000000000..89ceb8573 --- /dev/null +++ b/tests/libtest/lib678.c @@ -0,0 +1,120 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "test.h" + +#include "testutil.h" +#include "warnless.h" +#include "memdebug.h" + +static int loadfile(const char *filename, void **filedata, size_t *filesize) +{ + size_t datasize = 0; + void *data = NULL; + if(filename) { + FILE *fInCert = fopen(filename, "rb"); + + if(fInCert) { + long cert_tell = 0; + bool continue_reading = fseek(fInCert, 0, SEEK_END) == 0; + if(continue_reading) + cert_tell = ftell(fInCert); + if(cert_tell < 0) + continue_reading = FALSE; + else + datasize = (size_t)cert_tell; + if(continue_reading) + continue_reading = fseek(fInCert, 0, SEEK_SET) == 0; + if(continue_reading) + data = malloc(datasize + 1); + if((!data) || + ((int)fread(data, datasize, 1, fInCert) != 1)) + continue_reading = FALSE; + fclose(fInCert); + if(!continue_reading) { + free(data); + datasize = 0; + data = NULL; + } + } + } + *filesize = datasize; + *filedata = data; + return data ? 1 : 0; +} + +static int test_cert_blob(const char *url, const char *cafile) +{ + CURLcode code = CURLE_OUT_OF_MEMORY; + CURL *curl; + struct curl_blob blob; + size_t certsize; + void *certdata; + + curl = curl_easy_init(); + if(!curl) { + fprintf(stderr, "curl_easy_init() failed\n"); + return CURLE_FAILED_INIT; + } + + if(loadfile(cafile, &certdata, &certsize)) { + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(curl, CURLOPT_HEADER, 1L); + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "CURLOPT_CAINFO_BLOB"); + curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, + CURLSSLOPT_REVOKE_BEST_EFFORT); + + blob.data = certdata; + blob.len = certsize; + blob.flags = CURL_BLOB_COPY; + curl_easy_setopt(curl, CURLOPT_CAINFO_BLOB, &blob); + free(certdata); + code = curl_easy_perform(curl); + } + curl_easy_cleanup(curl); + + return (int)code; +} + +int test(char *URL) +{ + int res = 0; + curl_global_init(CURL_GLOBAL_DEFAULT); + if(!strcmp("check", URL)) { + CURL *e; + CURLcode w = CURLE_OK; + struct curl_blob blob = {0}; + e = curl_easy_init(); + if(e) { + w = curl_easy_setopt(e, CURLOPT_CAINFO_BLOB, &blob); + if(w) + printf("CURLOPT_CAINFO_BLOB is not supported\n"); + curl_easy_cleanup(e); + } + res = (int)w; + } + else + res = test_cert_blob(URL, libtest_arg2); + + curl_global_cleanup(); + return res; +} |