Add CURLOPT_CAINFO_PEM

This commit is contained in:
Travis Burtrum 2020-04-03 00:32:42 -04:00
parent 4506607b44
commit 397e11bfa6
10 changed files with 174 additions and 0 deletions

View File

@ -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.")

View File

@ -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

View File

@ -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}

View File

@ -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;

38
lib/cacert.h Normal file
View File

@ -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;
*/

View File

@ -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

View File

@ -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);
@ -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
@ -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];

View File

@ -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 */
@ -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 */

View File

@ -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;
@ -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];
@ -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) {

View File

@ -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) &&
@ -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);
@ -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);