Skip to content

Commit

Permalink
Add support CAfile in memory with CURLOPT_CAINFO_PEM and CURLOPT_PROX…
Browse files Browse the repository at this point in the history
…Y_CAINFO_PEM

Let applications provide the CA bundle to libcurl as a PEM formatted in-memory buffer.

Rebased branch bundled_cacert from moparisthebest

Validation : 397e11b [397e11b]
Parents : 4506607
Auteur : moparisthebest <admin@moparisthebest.com>
Date : vendredi 3 avril 2020 00:32:42
Auteur : moparisthebest
Date de validation : samedi 4 avril 2020 00:31:23
Add CURLOPT_CAINFO_PEM
  • Loading branch information
gvollant committed Feb 25, 2021
1 parent 09a5bff commit 087d295
Show file tree
Hide file tree
Showing 13 changed files with 251 additions and 3 deletions.
4 changes: 4 additions & 0 deletions docs/libcurl/curl_easy_setopt.3
Original file line number Diff line number Diff line change
Expand Up @@ -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_PEM
CA cert bundle as PEM string. See \fICURLOPT_CAINFO_PEM(3)\fP
.IP CURLOPT_PROXY_CAINFO
Proxy CA cert bundle. See \fICURLOPT_PROXY_CAINFO(3)\fP
.IP CURLOPT_PROXY_CAINFO_PEM
Proxy CA cert bundle as PEM string. See \fICURLOPT_PROXY_CAINFO_PEM(3)\fP
.IP CURLOPT_ISSUERCERT
Issuer certificate. See \fICURLOPT_ISSUERCERT(3)\fP
.IP CURLOPT_ISSUERCERT_BLOB
Expand Down
65 changes: 65 additions & 0 deletions docs/libcurl/opts/CURLOPT_CAINFO_PEM.3
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * 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_PEM 3 "31 March 2021" "libcurl 7.76.0" "curl_easy_setopt options"
.SH NAME
CURLOPT_CAINFO_PEM \- Certificate Authority (CA) bundle in PEM format
.SH SYNOPSIS
#include <curl/curl.h>

CURLcode curl_easy_setopt(CURL *handle, CURLOPT_CAINFO_PEM, char *pem_string);
.SH DESCRIPTION
Pass a char * to a null-terminated string with 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_PEM(3)\fP is not needed.

The application does not have to keep the string around after setting this
option. If \fICURLOPT_CA_INFO(3)\fP is set to a file, certificates from the
string is user in addition to other certificates.
.SH DEFAULT
NULL
.SH PROTOCOLS
All TLS based protocols: HTTPS, FTPS, IMAPS, POP3S, SMTPS etc.
.SH EXAMPLE
.nf
const char *strpem;
/* strpem must point to a PEM string */
CURL *curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/");
curl_easy_setopt(curl, CURLOPT_CAINFO_PEM, strpem);
ret = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
.fi
.SH AVAILABILITY
Added in 7.76.0

This option is supported by the OpenSSL 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_CAPATH "(3), "
.BR CURLOPT_SSL_VERIFYPEER "(3), " CURLOPT_SSL_VERIFYHOST "(3), "
71 changes: 71 additions & 0 deletions docs/libcurl/opts/CURLOPT_PROXY_CAINFO_PEM.3
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * 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_PEM 3 "31 March 2021" "libcurl 7.76.0" "curl_easy_setopt options"
.SH NAME
CURLOPT_PROXY_CAINFO_PEM \- proxy Certificate Authority (CA) bundle in PEM format
.SH SYNOPSIS
#include <curl/curl.h>

CURLcode curl_easy_setopt(CURL *handle, CURLOPT_PROXY_CAINFO_PEM, char *pem_string);
.SH DESCRIPTION
This option is for connecting to an HTTPS proxy, not an HTTPS server.

Pass a char * to a null-terminated string with 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_PEM(3)\fP is not needed.

The application does not have to keep the string around after setting this
option. If \fICURLOPT_PROXY_CA_INFO(3)\fP is set to a file, certificates from
the string is user in addition to other certificates.
.SH DEFAULT
NULL
.SH PROTOCOLS
Used with HTTPS proxy
.SH EXAMPLE
.nf
const char *strpem;
/* strpem must point to a PEM string */
CURL *curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/");
/* using an HTTPS proxy */
curl_easy_setopt(curl, CURLOPT_PROXY, "https://localhost:443");
curl_easy_setopt(curl, CURLOPT_PROXY_CAINFO_PEM, strpem);
ret = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
.fi
.SH AVAILABILITY
Added in 7.76.0

This option is supported by the OpenSSL 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_CAPATH "(3), "
.BR CURLOPT_PROXY_SSL_VERIFYPEER "(3), " CURLOPT_PROXY_SSL_VERIFYHOST "(3), "
.BR CURLOPT_CAPATH "(3), "
.BR CURLOPT_SSL_VERIFYPEER "(3), " CURLOPT_SSL_VERIFYHOST "(3), "
2 changes: 2 additions & 0 deletions docs/libcurl/opts/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ man_MANS = \
CURLOPT_AUTOREFERER.3 \
CURLOPT_BUFFERSIZE.3 \
CURLOPT_CAINFO.3 \
CURLOPT_CAINFO_PEM.3 \
CURLOPT_CAPATH.3 \
CURLOPT_CERTINFO.3 \
CURLOPT_CHUNK_BGN_FUNCTION.3 \
Expand Down Expand Up @@ -267,6 +268,7 @@ man_MANS = \
CURLOPT_PROXYUSERNAME.3 \
CURLOPT_PROXYUSERPWD.3 \
CURLOPT_PROXY_CAINFO.3 \
CURLOPT_PROXY_CAINFO_PEM.3 \
CURLOPT_PROXY_CAPATH.3 \
CURLOPT_PROXY_CRLFILE.3 \
CURLOPT_PROXY_KEYPASSWD.3 \
Expand Down
2 changes: 2 additions & 0 deletions docs/libcurl/symbols-in-versions
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ CURLOPT_APPEND 7.17.0
CURLOPT_AUTOREFERER 7.1
CURLOPT_BUFFERSIZE 7.10
CURLOPT_CAINFO 7.4.2
CURLOPT_CAINFO_PEM 7.76.0
CURLOPT_CAPATH 7.9.8
CURLOPT_CERTINFO 7.19.1
CURLOPT_CHUNK_BGN_FUNCTION 7.21.0
Expand Down Expand Up @@ -542,6 +543,7 @@ CURLOPT_PROXYTYPE 7.10
CURLOPT_PROXYUSERNAME 7.19.1
CURLOPT_PROXYUSERPWD 7.1
CURLOPT_PROXY_CAINFO 7.52.0
CURLOPT_PROXY_CAINFO_PEM 7.76.0
CURLOPT_PROXY_CAPATH 7.52.0
CURLOPT_PROXY_CRLFILE 7.52.0
CURLOPT_PROXY_ISSUERCERT 7.71.0
Expand Down
8 changes: 8 additions & 0 deletions include/curl/curl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2088,6 +2088,14 @@ typedef enum {
/* Same as CURLOPT_SSL_VERIFYSTATUS but for DOH (DNS-over-HTTPS) servers. */
CURLOPT(CURLOPT_DOH_SSL_VERIFYSTATUS, CURLOPTTYPE_LONG, 308),

/* The CAfile in memory as PEM used to validate the proxy certificate
this option is used only if PROXY_SSL_VERIFYPEER is true */
CURLOPT(CURLOPT_CAINFO_PEM, CURLOPTTYPE_STRINGPOINT, 309),

/* The CAfile in memory as PEM used to validate the proxy certificate
this option is used only if PROXY_SSL_VERIFYPEER is true */
CURLOPT(CURLOPT_PROXY_CAINFO_PEM, CURLOPTTYPE_STRINGPOINT, 310),

CURLOPT_LASTENTRY /* the last unused */
} CURLoption;

Expand Down
4 changes: 3 additions & 1 deletion include/curl/typecheck-gcc.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
* 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
Expand Down Expand Up @@ -256,6 +256,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
(option) == CURLOPT_ACCEPT_ENCODING || \
(option) == CURLOPT_ALTSVC || \
(option) == CURLOPT_CAINFO || \
(option) == CURLOPT_CAINFO_PEM || \
(option) == CURLOPT_CAPATH || \
(option) == CURLOPT_COOKIE || \
(option) == CURLOPT_COOKIEFILE || \
Expand Down Expand Up @@ -291,6 +292,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
(option) == CURLOPT_PROXYUSERNAME || \
(option) == CURLOPT_PROXYUSERPWD || \
(option) == CURLOPT_PROXY_CAINFO || \
(option) == CURLOPT_PROXY_CAINFO_PEM || \
(option) == CURLOPT_PROXY_CAPATH || \
(option) == CURLOPT_PROXY_CRLFILE || \
(option) == CURLOPT_PROXY_ISSUERCERT || \
Expand Down
4 changes: 3 additions & 1 deletion lib/easyoptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -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_PEM", CURLOPT_CAINFO_PEM, CURLOT_STRING, 0},
{"CAPATH", CURLOPT_CAPATH, CURLOT_STRING, 0},
{"CERTINFO", CURLOPT_CERTINFO, CURLOT_LONG, 0},
{"CHUNK_BGN_FUNCTION", CURLOPT_CHUNK_BGN_FUNCTION, CURLOT_FUNCTION, 0},
Expand Down Expand Up @@ -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_PEM", CURLOPT_PROXY_CAINFO_PEM, CURLOT_STRING, 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},
Expand Down Expand Up @@ -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
15 changes: 15 additions & 0 deletions lib/setopt.c
Original file line number Diff line number Diff line change
Expand Up @@ -2059,6 +2059,21 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
result = CURLE_NOT_BUILT_IN;
break;
#endif
case CURLOPT_CAINFO_PEM:
/*
* Set CA info for SSL connection. Specify entire PEM of the CA certificate
*/
result = Curl_setstropt(&data->set.str[STRING_SSL_CAFILE_PEM],
va_arg(param, char *));
break;
case CURLOPT_PROXY_CAINFO_PEM:
/*
* Set CA info for SSL connection proxy.
* Specify entire PEM of the CA certificate
*/
result = Curl_setstropt(&data->set.str[STRING_SSL_CAFILE_PEM_PROXY],
va_arg(param, char *));
break;
case CURLOPT_CRLFILE:
/*
* Set CRL file info for SSL connection. Specify file name of the CRL
Expand Down
3 changes: 3 additions & 0 deletions lib/url.c
Original file line number Diff line number Diff line change
Expand Up @@ -3722,6 +3722,7 @@ static CURLcode create_conn(struct Curl_easy *data,
*/
data->set.ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH];
data->set.ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE];
data->set.ssl.primary.ca_info_pem = data->set.str[STRING_SSL_CAFILE_PEM];
data->set.ssl.primary.random_file = data->set.str[STRING_SSL_RANDOM_FILE];
data->set.ssl.primary.egdsocket = data->set.str[STRING_SSL_EGDSOCKET];
data->set.ssl.primary.cipher_list =
Expand All @@ -3736,6 +3737,8 @@ static CURLcode create_conn(struct Curl_easy *data,
#ifndef CURL_DISABLE_PROXY
data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY];
data->set.proxy_ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY];
data->set.proxy_ssl.primary.ca_info_pem =
data->set.str[STRING_SSL_CAFILE_PEM_PROXY];
data->set.proxy_ssl.primary.random_file =
data->set.str[STRING_SSL_RANDOM_FILE];
data->set.proxy_ssl.primary.egdsocket = data->set.str[STRING_SSL_EGDSOCKET];
Expand Down
3 changes: 3 additions & 0 deletions lib/urldata.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ struct ssl_primary_config {
char *pinned_key;
struct curl_blob *cert_blob;
char *curves; /* list of curves to use */
char *ca_info_pem;
BIT(verifypeer); /* set TRUE if this is desired */
BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */
BIT(verifystatus); /* set TRUE if certificate status must be checked */
Expand Down Expand Up @@ -1546,6 +1547,8 @@ enum dupstring {
STRING_SSL_CAPATH_PROXY, /* CA directory name (doesn't work on windows) */
STRING_SSL_CAFILE, /* certificate file to verify peer against */
STRING_SSL_CAFILE_PROXY, /* certificate file to verify peer against */
STRING_SSL_CAFILE_PEM, /* PEM certificate to verify peer against */
STRING_SSL_CAFILE_PEM_PROXY, /* PEM certificate to verify peer against */
STRING_SSL_PINNEDPUBLICKEY, /* public key file to verify peer against */
STRING_SSL_PINNEDPUBLICKEY_PROXY, /* public key file to verify proxy */
STRING_SSL_CIPHER_LIST, /* list of ciphers to use */
Expand Down
70 changes: 69 additions & 1 deletion lib/vtls/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -2505,6 +2505,61 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
return res;
}

static CURLcode load_cacert_from_memory(SSL_CTX *ctx,
const char * const ca_info_pem)
{
/* 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;

CURLcode result = CURLE_SSL_CACERT_BADFILE;

do {
/* casting strlen to int feels wrong, but what to do with this API? */
cbio = BIO_new_mem_buf(ca_info_pem, (int) strlen(ca_info_pem));
if(!cbio)
return CURLE_OUT_OF_MEMORY;

cts = SSL_CTX_get_cert_store(ctx);
if(!cts)
break;

inf = PEM_X509_INFO_read_bio(cbio, NULL, NULL, NULL);
if(!inf)
break;

/* 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) {
X509_STORE_add_cert(cts, itmp->x509);
++count;
}
if(itmp->crl) {
X509_STORE_add_crl(cts, itmp->crl);
++count;
}
}

/* if we didn't end up importing anything, treat that as an error */
if(count > 0)
result = CURLE_OK;
} while(0);

if(inf)
sk_X509_INFO_pop_free(inf, X509_INFO_free);

if(cbio)
BIO_free(cbio);

return result;
}

static CURLcode ossl_connect_step1(struct Curl_easy *data,
struct connectdata *conn, int sockindex)
{
Expand Down Expand Up @@ -2535,6 +2590,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
const char * const ssl_cert_type = SSL_SET_OPTION(cert_type);
const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile);
const char * const ssl_capath = SSL_CONN_CONFIG(CApath);
const char * const ca_info_pem = SSL_CONN_CONFIG(ca_info_pem);
const bool verifypeer = SSL_CONN_CONFIG(verifypeer);
const char * const ssl_crlfile = SSL_SET_OPTION(CRLfile);
char error_buffer[256];
Expand Down Expand Up @@ -3081,8 +3137,20 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
}
#endif

if(ca_info_pem) {
result = load_cacert_from_memory(backend->ctx, ca_info_pem);
if(result == CURLE_OUT_OF_MEMORY) {
failf(data, "memory error importing CA certificate");
return CURLE_OUT_OF_MEMORY;
}

/* Only warning if no certificate verification is required. */
infof(data, "error importing CA certificate, continuing anyway");
}

#ifdef CURL_CA_FALLBACK
if(verifypeer && !ssl_cafile && !ssl_capath && !imported_native_ca) {
if(verifypeer &&
!ca_info_pem && !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);
Expand Down
Loading

0 comments on commit 087d295

Please sign in to comment.