Skip to content

Commit

Permalink
Add CURLOPT_CAINFO_PEM
Browse files Browse the repository at this point in the history
  • Loading branch information
moparisthebest committed Apr 4, 2020
1 parent 4506607 commit 397e11b
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,8 @@ set(CURL_CA_FALLBACK OFF CACHE BOOL
"Set ON to use built-in CA store of TLS backend. Defaults to OFF")
set(CURL_CA_PATH "auto" CACHE STRING
"Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.")
set(CURL_CA_BUNDLE_PEM OFF CACHE BOOL
"Set ON to use built-in CACERT_PEM from generated cacert.h. Defaults to OFF")

if("${CURL_CA_BUNDLE}" STREQUAL "")
message(FATAL_ERROR "Invalid value of CURL_CA_BUNDLE. Use 'none', 'auto' or file path.")
Expand Down
18 changes: 18 additions & 0 deletions acinclude.m4
Original file line number Diff line number Diff line change
Expand Up @@ -2306,6 +2306,24 @@ AC_HELP_STRING([--without-ca-fallback], [Don't use the built in CA store of the
fi
AC_DEFINE_UNQUOTED(CURL_CA_FALLBACK, 1, [define "1" to use built in CA store of SSL library ])
fi
AC_MSG_CHECKING([whether to use built in CACERT_PEM from generated cacert.h])
AC_ARG_WITH(ca-built-in,
AC_HELP_STRING([--with-ca-built-in], [Use the built in CACERT_PEM from generated cacert.h])
AC_HELP_STRING([--without-ca-built-in], [Don't use the built in CACERT_PEM from generated cacert.h]),
[
if test "x$with_ca_built_in" != "xyes" -a "x$with_ca_built_in" != "xno"; then
AC_MSG_ERROR([--with-ca-built-in only allows yes or no as parameter])
fi
],
[ with_ca_fallback="no"])
AC_MSG_RESULT([$with_ca_built_in])
if test "x$with_ca_built_in" = "xyes"; then
if test "x$OPENSSL_ENABLED" != "x1"; then
AC_MSG_ERROR([--with-ca-built-in only works with OpenSSL])
fi
AC_DEFINE_UNQUOTED(CURL_CA_BUNDLE_PEM, 1, [define "1" to use built in CACERT_PEM from generated cacert.h ])
fi
])

dnl CURL_CHECK_WIN32_LARGEFILE
Expand Down
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -4982,6 +4982,7 @@ AC_MSG_NOTICE([Configured to build curl/libcurl:
ca cert bundle: ${ca}${ca_warning}
ca cert path: ${capath}${capath_warning}
ca fallback: ${with_ca_fallback}
ca built in: ${with_ca_built_in}
LDAP: ${curl_ldap_msg}
LDAPS: ${curl_ldaps_msg}
RTSP: ${curl_rtsp_msg}
Expand Down
8 changes: 8 additions & 0 deletions include/curl/curl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1954,6 +1954,14 @@ typedef enum {
/* allow RCPT TO command to fail for some recipients */
CURLOPT(CURLOPT_MAIL_RCPT_ALLLOWFAILS, CURLOPTTYPE_LONG, 290),

/* 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, 291),

/* 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, 292),

CURLOPT_LASTENTRY /* the last unused */
} CURLoption;

Expand Down
38 changes: 38 additions & 0 deletions lib/cacert.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2020, 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.haxx.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.
*
***************************************************************************/

/*
regenerate this file like so:
curl -Oz cacert.pem https://curl.haxx.se/ca/cacert.pem \
&& xxd -i -C cacert.pem | sed -r 's/(0x..)$/\1, 0x00/' > lib/cacert.h
unless CACERT_PEM is defined in here, there will be a compile error if
CURL_CA_BUNDLE_PEM is defined, CACERT_PEM must be a proper null-terminated
C string
unsigned char CACERT_PEM[] = {
0x62, 0x6f, 0x62, 0x0a, 0x00
};
unsigned int CACERT_PEM_LEN = 4;
*/
15 changes: 15 additions & 0 deletions lib/setopt.c
Original file line number Diff line number Diff line change
Expand Up @@ -1944,6 +1944,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_ORIG],
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
20 changes: 20 additions & 0 deletions lib/url.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ bool curl_win32_idn_to_ascii(const char *in, char **out);
#include "curl_memory.h"
#include "memdebug.h"

#if defined(CURL_CA_BUNDLE_PEM)
#include "cacert.h"
#endif

static void conn_free(struct connectdata *conn);
static unsigned int get_protocol_family(unsigned int protocol);

Expand Down Expand Up @@ -507,6 +511,18 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
set->socks5_gssapi_nec = FALSE;
#endif

#if defined(CURL_CA_BUNDLE_PEM)
result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_PEM_ORIG],
(const char *) CACERT_PEM);
if(result)
return result;

result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_PEM_PROXY],
(const char *) CACERT_PEM);
if(result)
return result;
#endif

/* Set the default CA cert bundle/path detected/specified at build time.
*
* If Schannel is the selected SSL backend then these locations are
Expand Down Expand Up @@ -3555,6 +3571,10 @@ static CURLcode create_conn(struct Curl_easy *data,
data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY];
data->set.ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE_ORIG];
data->set.proxy_ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY];
data->set.ssl.primary.ca_file_pem =
data->set.str[STRING_SSL_CAFILE_PEM_ORIG];
data->set.proxy_ssl.primary.ca_file_pem =
data->set.str[STRING_SSL_CAFILE_PEM_PROXY];
data->set.ssl.primary.random_file = data->set.str[STRING_SSL_RANDOM_FILE];
data->set.proxy_ssl.primary.random_file =
data->set.str[STRING_SSL_RANDOM_FILE];
Expand Down
3 changes: 3 additions & 0 deletions lib/urldata.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ struct ssl_primary_config {
char *cipher_list; /* list of ciphers to use */
char *cipher_list13; /* list of TLS 1.3 cipher suites to use */
char *pinned_key;
char *ca_file_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 @@ -1529,6 +1530,8 @@ enum dupstring {
STRING_SSL_CAPATH_PROXY, /* CA directory name (doesn't work on windows) */
STRING_SSL_CAFILE_ORIG, /* certificate file to verify peer against */
STRING_SSL_CAFILE_PROXY, /* certificate file to verify peer against */
STRING_SSL_CAFILE_PEM_ORIG, /* PEM certificate to verify peer against */
STRING_SSL_CAFILE_PEM_PROXY, /* PEM certificate to verify peer against */
STRING_SSL_PINNEDPUBLICKEY_ORIG, /* public key file to verify peer against */
STRING_SSL_PINNEDPUBLICKEY_PROXY, /* public key file to verify proxy */
STRING_SSL_CIPHER_LIST_ORIG, /* list of ciphers to use */
Expand Down
66 changes: 66 additions & 0 deletions lib/vtls/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -2385,6 +2385,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_file_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_file_pem, (int) strlen(ca_file_pem));
if(!cbio)
break;

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 < 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 connectdata *conn, int sockindex)
{
CURLcode result = CURLE_OK;
Expand Down Expand Up @@ -2416,6 +2471,7 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
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_file_pem = SSL_CONN_CONFIG(ca_file_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 @@ -2720,6 +2776,16 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
}
#endif

if(ca_file_pem && load_cacert_from_memory(backend->ctx, ca_file_pem)) {
if(verifypeer) {
/* Fail if we insist on successfully verifying the server. */
failf(data, "error setting certificate from memory");
return CURLE_SSL_CACERT_BADFILE;
}
/* Continue with a warning if no certificate verification is required. */
infof(data, "error setting certificate file, continuing anyway\n");
}

#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
/* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */
if(ssl_cafile) {
Expand Down
3 changes: 3 additions & 0 deletions lib/vtls/vtls.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ Curl_ssl_config_matches(struct ssl_primary_config* data,
(data->verifystatus == needle->verifystatus) &&
Curl_safe_strcasecompare(data->CApath, needle->CApath) &&
Curl_safe_strcasecompare(data->CAfile, needle->CAfile) &&
Curl_safe_strcasecompare(data->ca_file_pem, needle->ca_file_pem) &&
Curl_safe_strcasecompare(data->clientcert, needle->clientcert) &&
Curl_safe_strcasecompare(data->random_file, needle->random_file) &&
Curl_safe_strcasecompare(data->egdsocket, needle->egdsocket) &&
Expand All @@ -117,6 +118,7 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source,

CLONE_STRING(CApath);
CLONE_STRING(CAfile);
CLONE_STRING(ca_file_pem);
CLONE_STRING(clientcert);
CLONE_STRING(random_file);
CLONE_STRING(egdsocket);
Expand All @@ -131,6 +133,7 @@ void Curl_free_primary_ssl_config(struct ssl_primary_config* sslc)
{
Curl_safefree(sslc->CApath);
Curl_safefree(sslc->CAfile);
Curl_safefree(sslc->ca_file_pem);
Curl_safefree(sslc->clientcert);
Curl_safefree(sslc->random_file);
Curl_safefree(sslc->egdsocket);
Expand Down

0 comments on commit 397e11b

Please sign in to comment.