mirror of
https://github.com/curl/curl.git
synced 2026-08-06 10:06:13 +03:00
curl: support embedding a CA bundle
Add the ability to embed a CA bundle into the curl binary. It is used when no other runtime or build-time option set one. This helps curl-for-win macOS and Linux builds to run standalone, and also helps Windows builds to avoid picking up the CA bundle from an arbitrary (possibly world-writable) location (though this behaviour is not currently disablable). Usage: - cmake: `-DCURL_CA_EMBED=/path/to/curl-ca-bundle.crt` - autotools: `--with-ca-embed=/path/to/curl-ca-bundle.crt` - Makefile.mk: `CURL_CA_EMBED=/path/to/curl-ca-bundle.crt` Also add new command-line option `--dump-ca-embed` to dump the embedded CA bundle to standard output. Closes #14059
This commit is contained in:
parent
87aa4ebd82
commit
8a3740bc8e
26 changed files with 268 additions and 14 deletions
1
src/.gitignore
vendored
1
src/.gitignore
vendored
|
|
@ -10,5 +10,6 @@ curl
|
|||
curl_config.h
|
||||
curl_config.h.in
|
||||
stamp-h2
|
||||
tool_ca_embed.c
|
||||
tool_hugehelp.c
|
||||
tool_version.h.dist
|
||||
|
|
|
|||
|
|
@ -54,6 +54,22 @@ endif()
|
|||
transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
|
||||
include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake)
|
||||
|
||||
if(CURL_CA_EMBED_SET)
|
||||
if(PERL_FOUND)
|
||||
add_definitions("-DCURL_CA_EMBED")
|
||||
add_custom_command(
|
||||
OUTPUT tool_ca_embed.c
|
||||
COMMAND "${PERL_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/mk-file-embed.pl" --var curl_ca_embed < "${CURL_CA_EMBED}" > tool_ca_embed.c
|
||||
DEPENDS
|
||||
"${CURL_CA_EMBED}"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/mk-file-embed.pl"
|
||||
VERBATIM)
|
||||
list(APPEND CURL_CFILES tool_ca_embed.c)
|
||||
else()
|
||||
message(WARNING "Perl not found. Will not embed the CA bundle.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND CURL_CFILES curl.rc)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ CLEANFILES = tool_hugehelp.c
|
|||
# embedded text.
|
||||
NROFF=env LC_ALL=C @NROFF@ @MANOPT@ 2>/dev/null # figured out by the configure script
|
||||
|
||||
EXTRA_DIST = mkhelp.pl \
|
||||
EXTRA_DIST = mk-file-embed.pl mkhelp.pl \
|
||||
Makefile.mk curl.rc Makefile.inc CMakeLists.txt .checksrc
|
||||
|
||||
# Use absolute directory to disable VPATH
|
||||
|
|
@ -135,11 +135,25 @@ $(HUGE):
|
|||
echo '#include "tool_hugehelp.h"' >> $(HUGE)
|
||||
endif
|
||||
|
||||
# ignore tool_hugehelp.c since it is generated source code and it plays
|
||||
# by slightly different rules!
|
||||
CA_EMBED_CSOURCE = tool_ca_embed.c
|
||||
CURL_CFILES += $(CA_EMBED_CSOURCE)
|
||||
CLEANFILES += $(CA_EMBED_CSOURCE)
|
||||
if CURL_CA_EMBED_SET
|
||||
AM_CPPFLAGS += -DCURL_CA_EMBED
|
||||
MK_FILE_EMBED = $(top_srcdir)/src/mk-file-embed.pl
|
||||
$(CA_EMBED_CSOURCE): $(MK_FILE_EMBED)
|
||||
$(PERL) $(MK_FILE_EMBED) --var curl_ca_embed < $(CURL_CA_EMBED) > $(CA_EMBED_CSOURCE)
|
||||
else
|
||||
$(CA_EMBED_CSOURCE):
|
||||
echo 'extern const void *curl_ca_embed; const void *curl_ca_embed;' > $(CA_EMBED_CSOURCE)
|
||||
endif
|
||||
|
||||
# ignore generated C files since they play by slightly different rules!
|
||||
checksrc:
|
||||
$(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) \
|
||||
-W$(srcdir)/tool_hugehelp.c $(srcdir)/*.[ch])
|
||||
-W$(srcdir)/$(HUGE) \
|
||||
-W$(srcdir)/$(CA_EMBED_CSOURCE) \
|
||||
$(srcdir)/*.[ch])
|
||||
|
||||
if DEBUGBUILD
|
||||
# for debug builds, we scan the sources on all regular make invokes
|
||||
|
|
|
|||
|
|
@ -45,6 +45,11 @@ TARGETS := curl$(BIN_EXT)
|
|||
|
||||
CURL_CFILES += $(notdir $(CURLX_CFILES))
|
||||
|
||||
ifneq ($(CURL_CA_EMBED),)
|
||||
CPPFLAGS += -DCURL_CA_EMBED
|
||||
CURL_CFILES += tool_ca_embed.c
|
||||
endif
|
||||
|
||||
curl_OBJECTS := $(patsubst %.c,$(OBJ_DIR)/%.o,$(strip $(CURL_CFILES)))
|
||||
ifdef MAP
|
||||
CURL_MAP := curl.map
|
||||
|
|
@ -57,8 +62,9 @@ TOCLEAN := $(curl_OBJECTS)
|
|||
|
||||
### Rules
|
||||
|
||||
ifneq ($(wildcard tool_hugehelp.c.cvs),)
|
||||
PERL ?= perl
|
||||
|
||||
ifneq ($(wildcard tool_hugehelp.c.cvs),)
|
||||
NROFF ?= groff
|
||||
|
||||
TOCLEAN += tool_hugehelp.c
|
||||
|
|
@ -84,6 +90,12 @@ tool_hugehelp.c:
|
|||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(CURL_CA_EMBED),)
|
||||
TOCLEAN += tool_ca_embed.c
|
||||
tool_ca_embed.c: mk-file-embed.pl
|
||||
$(PERL) mk-file-embed.pl --var curl_ca_embed < $(CURL_CA_EMBED) > $@
|
||||
endif
|
||||
|
||||
$(TARGETS): $(curl_OBJECTS) $(PROOT)/lib/libcurl.a
|
||||
$(CC) $(LDFLAGS) -o $@ $(curl_OBJECTS) $(LIBS)
|
||||
|
||||
|
|
|
|||
56
src/mk-file-embed.pl
Executable file
56
src/mk-file-embed.pl
Executable file
|
|
@ -0,0 +1,56 @@
|
|||
#!/usr/bin/env perl
|
||||
#***************************************************************************
|
||||
# _ _ ____ _
|
||||
# Project ___| | | | _ \| |
|
||||
# / __| | | | |_) | |
|
||||
# | (__| |_| | _ <| |___
|
||||
# \___|\___/|_| \_\_____|
|
||||
#
|
||||
# Copyright (C) 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.
|
||||
#
|
||||
# SPDX-License-Identifier: curl
|
||||
#
|
||||
###########################################################################
|
||||
|
||||
my $varname = "var";
|
||||
if($ARGV[0] eq "--var") {
|
||||
shift;
|
||||
$varname = shift @ARGV;
|
||||
}
|
||||
|
||||
print <<HEAD
|
||||
/*
|
||||
* NEVER EVER edit this manually, fix the mk-file-embed.pl script instead!
|
||||
*/
|
||||
extern const unsigned char ${varname}[];
|
||||
const unsigned char ${varname}[] = {
|
||||
HEAD
|
||||
;
|
||||
|
||||
while (<STDIN>) {
|
||||
my $line = $_;
|
||||
foreach my $n (split //, $line) {
|
||||
my $ord = ord($n);
|
||||
printf("%s,", $ord);
|
||||
if($ord == 10) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print <<ENDLINE
|
||||
0
|
||||
};
|
||||
ENDLINE
|
||||
;
|
||||
|
|
@ -122,6 +122,7 @@ typedef enum {
|
|||
C_DOH_CERT_STATUS,
|
||||
C_DOH_INSECURE,
|
||||
C_DOH_URL,
|
||||
C_DUMP_CA_EMBED,
|
||||
C_DUMP_HEADER,
|
||||
C_ECH,
|
||||
C_EGD_FILE,
|
||||
|
|
@ -408,6 +409,7 @@ static const struct LongShort aliases[]= {
|
|||
{"doh-cert-status", ARG_BOOL, ' ', C_DOH_CERT_STATUS},
|
||||
{"doh-insecure", ARG_BOOL, ' ', C_DOH_INSECURE},
|
||||
{"doh-url" , ARG_STRG, ' ', C_DOH_URL},
|
||||
{"dump-ca-embed", ARG_NONE, ' ', C_DUMP_CA_EMBED},
|
||||
{"dump-header", ARG_FILE, 'D', C_DUMP_HEADER},
|
||||
{"ech", ARG_STRG, ' ', C_ECH},
|
||||
{"egd-file", ARG_STRG, ' ', C_EGD_FILE},
|
||||
|
|
@ -2113,6 +2115,9 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
|
|||
case C_URL_QUERY: /* --url-query */
|
||||
err = url_query(nextarg, global, config);
|
||||
break;
|
||||
case C_DUMP_CA_EMBED: /* --dump-ca-embed */
|
||||
err = PARAM_CA_EMBED_REQUESTED;
|
||||
break;
|
||||
case C_DUMP_HEADER: /* --dump-header */
|
||||
err = getstr(&config->headerfile, nextarg, DENY_BLANK);
|
||||
break;
|
||||
|
|
@ -2984,7 +2989,8 @@ ParameterError parse_args(struct GlobalConfig *global, int argc,
|
|||
if(result && result != PARAM_HELP_REQUESTED &&
|
||||
result != PARAM_MANUAL_REQUESTED &&
|
||||
result != PARAM_VERSION_INFO_REQUESTED &&
|
||||
result != PARAM_ENGINES_REQUESTED) {
|
||||
result != PARAM_ENGINES_REQUESTED &&
|
||||
result != PARAM_CA_EMBED_REQUESTED) {
|
||||
const char *reason = param2text(result);
|
||||
|
||||
if(orig_opt && strcmp(":", orig_opt))
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ typedef enum {
|
|||
PARAM_MANUAL_REQUESTED,
|
||||
PARAM_VERSION_INFO_REQUESTED,
|
||||
PARAM_ENGINES_REQUESTED,
|
||||
PARAM_CA_EMBED_REQUESTED,
|
||||
PARAM_GOT_EXTRA_PARAMETER,
|
||||
PARAM_BAD_NUMERIC,
|
||||
PARAM_NEGATIVE_NUMERIC,
|
||||
|
|
|
|||
|
|
@ -244,10 +244,28 @@ void tool_version_info(void)
|
|||
puts(""); /* newline */
|
||||
}
|
||||
if(feature_names[0]) {
|
||||
printf("Features:");
|
||||
for(builtin = feature_names; *builtin; ++builtin)
|
||||
printf(" %s", *builtin);
|
||||
puts(""); /* newline */
|
||||
const char **feat_ext;
|
||||
size_t feat_ext_count = feature_count;
|
||||
#ifdef CURL_CA_EMBED
|
||||
++feat_ext_count;
|
||||
#endif
|
||||
feat_ext = malloc(sizeof(*feature_names) * (feat_ext_count + 1));
|
||||
if(feat_ext) {
|
||||
memcpy((void *)feat_ext, feature_names,
|
||||
sizeof(*feature_names) * feature_count);
|
||||
feat_ext_count = feature_count;
|
||||
#ifdef CURL_CA_EMBED
|
||||
feat_ext[feat_ext_count++] = "CAcert";
|
||||
#endif
|
||||
feat_ext[feat_ext_count] = NULL;
|
||||
qsort((void *)feat_ext, feat_ext_count, sizeof(*feat_ext),
|
||||
struplocompare4sort);
|
||||
printf("Features:");
|
||||
for(builtin = feat_ext; *builtin; ++builtin)
|
||||
printf(" %s", *builtin);
|
||||
puts(""); /* newline */
|
||||
free((void *)feat_ext);
|
||||
}
|
||||
}
|
||||
if(strcmp(CURL_VERSION, curlinfo->version)) {
|
||||
printf("WARNING: curl and libcurl versions do not match. "
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ static struct feature_name_presentp {
|
|||
|
||||
static const char *fnames[sizeof(maybe_feature) / sizeof(maybe_feature[0])];
|
||||
const char * const *feature_names = fnames;
|
||||
size_t feature_count;
|
||||
|
||||
/*
|
||||
* libcurl_info_init: retrieves runtime information about libcurl,
|
||||
|
|
@ -182,6 +183,7 @@ CURLcode get_libcurl_info(void)
|
|||
*p->feature_presentp = TRUE;
|
||||
break;
|
||||
}
|
||||
++feature_count;
|
||||
}
|
||||
|
||||
return CURLE_OK;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ extern const char * const *built_in_protos;
|
|||
extern size_t proto_count;
|
||||
|
||||
extern const char * const *feature_names;
|
||||
extern size_t feature_count;
|
||||
|
||||
extern const char *proto_file;
|
||||
extern const char *proto_ftp;
|
||||
|
|
|
|||
|
|
@ -165,6 +165,9 @@ const struct helptxt helptext[] = {
|
|||
{" --doh-url <URL>",
|
||||
"Resolve hostnames over DoH",
|
||||
CURLHELP_DNS},
|
||||
{" --dump-ca-embed",
|
||||
"Write the embedded CA bundle to standard output",
|
||||
CURLHELP_HTTP | CURLHELP_PROXY | CURLHELP_TLS},
|
||||
{"-D, --dump-header <filename>",
|
||||
"Write the received headers to <filename>",
|
||||
CURLHELP_HTTP | CURLHELP_FTP},
|
||||
|
|
|
|||
|
|
@ -94,6 +94,10 @@
|
|||
|
||||
#include "memdebug.h" /* keep this as LAST include */
|
||||
|
||||
#ifdef CURL_CA_EMBED
|
||||
extern const unsigned char curl_ca_embed[];
|
||||
#endif
|
||||
|
||||
#ifndef O_BINARY
|
||||
/* since O_BINARY as used in bitmasks, setting it to zero makes it usable in
|
||||
source code but yet it does not ruin anything */
|
||||
|
|
@ -1657,6 +1661,37 @@ static CURLcode single_transfer(struct GlobalConfig *global,
|
|||
break;
|
||||
}
|
||||
|
||||
#ifdef CURL_CA_EMBED
|
||||
if(!config->cacert && !config->capath) {
|
||||
struct curl_blob blob;
|
||||
blob.data = (void *)curl_ca_embed;
|
||||
blob.len = strlen((const char *)curl_ca_embed);
|
||||
blob.flags = CURL_BLOB_NOCOPY;
|
||||
notef(config->global,
|
||||
"Using embedded CA bundle (%zu bytes)",
|
||||
blob.len);
|
||||
result = curl_easy_setopt(curl, CURLOPT_CAINFO_BLOB, &blob);
|
||||
if(result == CURLE_NOT_BUILT_IN) {
|
||||
warnf(global,
|
||||
"ignoring embedded CA bundle, not supported by libcurl");
|
||||
}
|
||||
}
|
||||
if(!config->proxy_cacert && !config->proxy_capath) {
|
||||
struct curl_blob blob;
|
||||
blob.data = (void *)curl_ca_embed;
|
||||
blob.len = strlen((const char *)curl_ca_embed);
|
||||
blob.flags = CURL_BLOB_NOCOPY;
|
||||
notef(config->global,
|
||||
"Using embedded CA bundle, for proxies (%zu bytes)",
|
||||
blob.len);
|
||||
result = curl_easy_setopt(curl, CURLOPT_PROXY_CAINFO_BLOB, &blob);
|
||||
if(result == CURLE_NOT_BUILT_IN) {
|
||||
warnf(global,
|
||||
"ignoring embedded CA bundle, not supported by libcurl");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if(config->crlfile)
|
||||
my_setopt_str(curl, CURLOPT_CRLFILE, config->crlfile);
|
||||
if(config->proxy_crlfile)
|
||||
|
|
@ -2842,6 +2877,12 @@ CURLcode operate(struct GlobalConfig *global, int argc, argv_item_t argv[])
|
|||
/* Check if we were asked to list the SSL engines */
|
||||
else if(res == PARAM_ENGINES_REQUESTED)
|
||||
tool_list_engines();
|
||||
/* Check if we were asked to dump the embedded CA bundle */
|
||||
else if(res == PARAM_CA_EMBED_REQUESTED) {
|
||||
#ifdef CURL_CA_EMBED
|
||||
printf("%s", curl_ca_embed);
|
||||
#endif
|
||||
}
|
||||
else if(res == PARAM_LIBCURL_UNSUPPORTED_PROTOCOL)
|
||||
result = CURLE_UNSUPPORTED_PROTOCOL;
|
||||
else if(res == PARAM_READ_ERROR)
|
||||
|
|
|
|||
|
|
@ -262,7 +262,8 @@ int parseconfig(const char *filename, struct GlobalConfig *global)
|
|||
if(res != PARAM_HELP_REQUESTED &&
|
||||
res != PARAM_MANUAL_REQUESTED &&
|
||||
res != PARAM_VERSION_INFO_REQUESTED &&
|
||||
res != PARAM_ENGINES_REQUESTED) {
|
||||
res != PARAM_ENGINES_REQUESTED &&
|
||||
res != PARAM_CA_EMBED_REQUESTED) {
|
||||
const char *reason = param2text(res);
|
||||
errorf(operation->global, "%s:%d: '%s' %s",
|
||||
filename, lineno, option, reason);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue