Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rebased version of [WIP] Bundled cacert #4679 #5677

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,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 @@ -5224,6 +5224,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 @@ -2031,6 +2031,14 @@ typedef enum {
*/
CURLOPT(CURLOPT_SSL_EC_CURVES, CURLOPTTYPE_STRINGPOINT, 298),

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

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

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 @@ -2020,6 +2020,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
21 changes: 21 additions & 0 deletions lib/url.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,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 @@ -530,6 +534,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 @@ -3609,6 +3625,11 @@ static CURLcode create_conn(struct Curl_easy *data,
*/
data->set.ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_ORIG];
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.ssl.primary.egdsocket = data->set.str[STRING_SSL_EGDSOCKET];
data->set.ssl.primary.cipher_list =
Expand Down
3 changes: 3 additions & 0 deletions lib/urldata.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ struct ssl_primary_config {
char *pinned_key;
struct curl_blob *cert_blob;
char *curves; /* list of curves to use */
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 @@ -1509,6 +1510,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
65 changes: 65 additions & 0 deletions lib/vtls/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -2457,6 +2457,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 @@ -2498,6 +2553,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 @@ -2987,6 +3043,15 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
infof(data, "error importing windows ca store, continuing anyway\n");
}
#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");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we really ignore the failure to load the cacert from memory when we are not verifying the peer certificate?

Would not it be better to fail anyway? Honest question, I don't have a specific opinion on this matter.

}

#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
/* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */
Expand Down
3 changes: 3 additions & 0 deletions lib/vtls/vtls.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ Curl_ssl_config_matches(struct ssl_primary_config *data,
blobcmp(data->cert_blob, needle->cert_blob) &&
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 @@ -159,6 +160,7 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
CLONE_BLOB(cert_blob);
CLONE_STRING(CApath);
CLONE_STRING(CAfile);
CLONE_STRING(ca_file_pem);
CLONE_STRING(clientcert);
CLONE_STRING(random_file);
CLONE_STRING(egdsocket);
Expand All @@ -174,6 +176,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