setopt: support certificate options in memory with struct curl_blob

This change introduces a generic way to provide binary data in setopt
options, called BLOBs.

This change introduces these new setopts:

CURLOPT_ISSUERCERT_BLOB, CURLOPT_PROXY_SSLCERT_BLOB,
CURLOPT_PROXY_SSLKEY_BLOB, CURLOPT_SSLCERT_BLOB and CURLOPT_SSLKEY_BLOB.

Reviewed-by: Daniel Stenberg
Closes #5357
This commit is contained in:
Gilles Vollant 2020-05-15 10:47:46 +02:00 committed by Daniel Stenberg
parent 8df455479f
commit cac5374298
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
21 changed files with 960 additions and 129 deletions

View File

@ -506,16 +506,24 @@ Sets the interval at which connection upkeep are performed. See
.SH SSL and SECURITY OPTIONS
.IP CURLOPT_SSLCERT
Client cert. See \fICURLOPT_SSLCERT(3)\fP
.IP CURLOPT_SSLCERT_BLOB
Client cert memory buffer. See \fICURLOPT_SSLCERT_BLOB(3)\fP
.IP CURLOPT_PROXY_SSLCERT
Proxy client cert. See \fICURLOPT_PROXY_SSLCERT(3)\fP
.IP CURLOPT_PROXY_SSLCERT_BLOB
Proxy client cert memory buffer. See \fICURLOPT_PROXY_SSLCERT_BLOB(3)\fP
.IP CURLOPT_SSLCERTTYPE
Client cert type. See \fICURLOPT_SSLCERTTYPE(3)\fP
.IP CURLOPT_PROXY_SSLCERTTYPE
Proxy client cert type. See \fICURLOPT_PROXY_SSLCERTTYPE(3)\fP
.IP CURLOPT_SSLKEY
Client key. See \fICURLOPT_SSLKEY(3)\fP
.IP CURLOPT_SSLKEY_BLOB
Client key memory buffer. See \fICURLOPT_SSLKEY_BLOB(3)\fP
.IP CURLOPT_PROXY_SSLKEY
Proxy client key. See \fICURLOPT_PROXY_SSLKEY(3)\fP
.IP CURLOPT_PROXY_SSLKEY_BLOB
Proxy client key. See \fICURLOPT_PROXY_SSLKEY_BLOB(3)\fP
.IP CURLOPT_SSLKEYTYPE
Client key type. See \fICURLOPT_SSLKEYTYPE(3)\fP
.IP CURLOPT_PROXY_SSLKEYTYPE
@ -554,6 +562,8 @@ CA cert bundle. See \fICURLOPT_CAINFO(3)\fP
Proxy CA cert bundle. See \fICURLOPT_PROXY_CAINFO(3)\fP
.IP CURLOPT_ISSUERCERT
Issuer certificate. See \fICURLOPT_ISSUERCERT(3)\fP
.IP CURLOPT_ISSUERCERT_BLOB
Issuer certificate memory buffer. See \fICURLOPT_ISSUERCERT_BLOB(3)\fP
.IP CURLOPT_CAPATH
Path to CA cert bundle. See \fICURLOPT_CAPATH(3)\fP
.IP CURLOPT_PROXY_CAPATH

View File

@ -0,0 +1,79 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * 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.
.\" *
.\" **************************************************************************
.\"
.TH CURLOPT_ISSUERCERT_BLOB 3 "24 Jun 2020" "libcurl 7.71.0" "curl_easy_setopt options"
.SH NAME
CURLOPT_ISSUERCERT_BLOB \- issuer SSL certificate from memory blob
.SH SYNOPSIS
#include <curl/curl.h>
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_ISSUERCERT_BLOB, struct curl_blob *stblob);
.SH DESCRIPTION
Pass a pointer to a curl_blob structure, which contains information (pointer
and size) about a memory block with binary data of a CA certificate in PEM
format. If the option is set, an additional check against the peer certificate
is performed to verify the issuer is indeed the one associated with the
certificate provided by the option. This additional check is useful in
multi-level PKI where one needs to enforce that the peer certificate is from a
specific branch of the tree.
This option should be used in combination with the
\fICURLOPT_SSL_VERIFYPEER(3)\fP option. Otherwise, the result of the check is
not considered as failure.
A specific error code (CURLE_SSL_ISSUER_ERROR) is defined with the option,
which is returned if the setup of the SSL/TLS session has failed due to a
mismatch with the issuer of peer certificate (\fICURLOPT_SSL_VERIFYPEER(3)\fP
has to be set too for the check to fail).
If the blob is initialized with the flags member of struct curl_blob set to
CURL_BLOB_COPY, the application does not have to keep the buffer around after
setting this.
This option is an alternative to \fICURLOPT_ISSUERCERT(3)\fP which instead
expects a file name as input.
.SH DEFAULT
NULL
.SH PROTOCOLS
All TLS-based protocols
.SH EXAMPLE
.nf
CURL *curl = curl_easy_init();
if(curl) {
struct curl_blob blob;
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/");
blob.data = certificateData;
blob.len = filesize;
blob.flags = CURL_BLOB_COPY;
curl_easy_setopt(curl, CURLOPT_ISSUERCERT_BLOB, &blob);
ret = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
.fi
.SH AVAILABILITY
Added in libcurl 7.71.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_ISSUERCERT "(3),"
.BR CURLOPT_CRLFILE "(3), " CURLOPT_SSL_VERIFYPEER "(3), "

View File

@ -0,0 +1,72 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * 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.
.\" *
.\" **************************************************************************
.\"
.TH CURLOPT_PROXY_SSLCERT_BLOB 3 "24 Jun 2020" "libcurl 7.71.0" "curl_easy_setopt options"
.SH NAME
CURLOPT_PROXY_SSLCERT_BLOB \- SSL proxy client certificate from memory blob
.SH SYNOPSIS
#include <curl/curl.h>
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_PROXY_SSLCERT_BLOB, struct curl_blob *blob);
.SH DESCRIPTION
Pass a pointer to a curl_blob structure, which contains information (pointer
and size) about a memory block with binary data of the certificate used to
connect to the HTTPS proxy. The format must be "P12" on Secure Transport or
Schannel. The format must be "P12" or "PEM" on OpenSSL. The string "P12" or
"PEM" must be specified with \fICURLOPT_PROXY_SSLCERTTYPE(3)\fP.
If the blob is initialized with the flags member of struct curl_blob set to
CURL_BLOB_COPY, the application does not have to keep the buffer around after
setting this.
This option is an alternative to \fICURLOPT_PROXY_SSLCERT(3)\fP which instead
expects a file name as input.
.SH DEFAULT
NULL
.SH PROTOCOLS
All TLS based protocols: HTTPS, FTPS, IMAPS, POP3S, SMTPS etc.
.SH EXAMPLE
.nf
CURL *curl = curl_easy_init();
if(curl) {
struct curl_blob blob;
blob.data = certificateData;
blob.len = filesize;
blob.flags = CURL_BLOB_COPY;
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/");
curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy");
curl_easy_setopt(curl, CURLOPT_PROXY_SSLKEY, "key.pem");
curl_easy_setopt(curl, CURLOPT_PROXY_KEYPASSWD, "s3cret");
curl_easy_setopt(curl, CURLOPT_PROXY_SSLCERT_BLOB, &blob);
ret = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
.fi
.SH AVAILABILITY
Added in libcurl 7.71.0. This option is supported by the OpenSSL, Secure
Transport and Schannel backends.
.SH RETURN VALUE
Returns CURLE_OK if TLS enabled, CURLE_UNKNOWN_OPTION if not, or
CURLE_OUT_OF_MEMORY if there was insufficient heap space.
.SH "SEE ALSO"
.BR CURLOPT_PROXY_SSLCERTTYPE "(3), " CURLOPT_PROXY_SSLKEY "(3), "
.BR CURLOPT_PROXY_SSLCERT "(3), "

View File

@ -0,0 +1,73 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * 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.
.\" *
.\" **************************************************************************
.\"
.TH CURLOPT_PROXY_SSLKEY_BLOB 3 "24 Jun 2020" "libcurl 7.71.0" "curl_easy_setopt options"
.SH NAME
CURLOPT_PROXY_SSLKEY_BLOB \- private key for proxy cert from memory blob
.SH SYNOPSIS
.nf
#include <curl/curl.h>
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_PROXY_SSLKEY_BLOB,
struct curl_blob *blob);
.fi
.SH DESCRIPTION
Pass a pointer to a curl_blob structure that contains information (pointer and
size) about the private key for connecting to the HTTPS proxy. Compatible with
OpenSSL. The format (like "PEM") must be specified with
\fICURLOPT_PROXY_SSLKEYTYPE(3)\fP.
If the blob is initialized with the flags member of struct curl_blob set to
CURL_BLOB_COPY, the application does not have to keep the buffer around after
setting this.
.SH DEFAULT
NULL
.SH PROTOCOLS
All TLS based protocols: HTTPS, FTPS, IMAPS, POP3S, SMTPS etc.
.SH EXAMPLE
.nf
CURL *curl = curl_easy_init();
if(curl) {
struct curl_blob blob;
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/");
curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy");
blob.data = certificateData;
blob.len = filesize;
blob.flags = CURL_BLOB_COPY;
curl_easy_setopt(curl, CURLOPT_PROXY_SSLCERT_BLOB, &blob);
curl_easy_setopt(curl, CURLOPT_PROXY_SSLCERTTYPE, "PEM");
blob.data = privateKeyData;
blob.len = privateKeySize;
curl_easy_setopt(curl, CURLOPT_PROXY_SSLKEY_BLOB, &blob);
curl_easy_setopt(curl, CURLOPT_PROXY_KEYPASSWD, "s3cret");
ret = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
.fi
.SH AVAILABILITY
Added in libcurl 7.71.0. This option is supported by the OpenSSL backends.
.SH RETURN VALUE
Returns CURLE_OK if TLS enabled, CURLE_UNKNOWN_OPTION if not, or
CURLE_OUT_OF_MEMORY if there was insufficient heap space.
.SH "SEE ALSO"
.BR CURLOPT_SSLKEYTYPE "(3), " CURLOPT_SSLKEY "(3), "

View File

@ -38,15 +38,17 @@ you wish to authenticate with as it is named in the security database. If you
want to use a file from the current directory, please precede it with "./"
prefix, in order to avoid confusion with a nickname.
(Schannel only) Client certificates must be specified by a path expression to
a certificate store. (Loading PFX is not supported; you can import it to a
store first). You can use "<store location>\\<store name>\\<thumbprint>" to
refer to a certificate in the system certificates store, for example,
(Schannel only) Client certificates can be specified by a path expression to
a certificate store. (You can import PFX to a store first). You can use
"<store location>\\<store name>\\<thumbprint>" to refer to a certificate
in the system certificates store, for example,
"CurrentUser\\MY\\934a7ac6f8a5d579285a74fa61e19f23ddfe8d7a". Thumbprint is
usually a SHA-1 hex string which you can see in certificate details. Following
store locations are supported: CurrentUser, LocalMachine, CurrentService,
Services, CurrentUserGroupPolicy, LocalMachineGroupPolicy,
LocalMachineEnterprise.
Schannel also support P12 certificate file, with the string "P12" specified
with \fICURLOPT_SSLCERTTYPE(3)\fP.
When using a client certificate, you most likely also need to provide a
private key with \fICURLOPT_SSLKEY(3)\fP.

View File

@ -0,0 +1,69 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * 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.
.\" *
.\" **************************************************************************
.\"
.TH CURLOPT_SSLCERT_BLOB 3 "24 Jun 2020" "libcurl 7.71.0" "curl_easy_setopt options"
.SH NAME
CURLOPT_SSLCERT_BLOB \- SSL client certificate from memory blob
.SH SYNOPSIS
#include <curl/curl.h>
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SSLCERT_BLOB, struct curl_blob *stblob);
.SH DESCRIPTION
Pass a pointer to a curl_blob structure, which contains (pointer and size) a
client certificate. The format must be "P12" on Secure Transport or
Schannel. The format must be "P12" or "PEM" on OpenSSL. The string "P12" or
"PEM" must be specified with \fICURLOPT_SSLCERTTYPE(3)\fP.
If the blob is initialized with the flags member of struct curl_blob set to
CURL_BLOB_COPY, the application does not have to keep the buffer around after
setting this.
This option is an alternative to \fICURLOPT_SSLCERT(3)\fP which instead
expects a file name as input.
.SH DEFAULT
NULL
.SH PROTOCOLS
All TLS based protocols: HTTPS, FTPS, IMAPS, POP3S, SMTPS etc.
.SH EXAMPLE
.nf
CURL *curl = curl_easy_init();
if(curl) {
struct curl_blob stblob;
stblob.data = certificateData;
stblob.len = filesize;
stblob.flags = CURL_BLOB_COPY;
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/");
curl_easy_setopt(curl, CURLOPT_SSLCERT_BLOB, &stblob);
curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "P12");
curl_easy_setopt(curl, CURLOPT_KEYPASSWD, "s3cret");
ret = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
.fi
.SH AVAILABILITY
Added in libcurl 7.71.0. This option is supported by the OpenSSL, Secure
Transport and Schannel backends.
.SH RETURN VALUE
Returns CURLE_OK if TLS enabled, CURLE_UNKNOWN_OPTION if not, or
CURLE_OUT_OF_MEMORY if there was insufficient heap space.
.SH "SEE ALSO"
.BR CURLOPT_SSLCERTTYPE "(3), " CURLOPT_SSLKEY "(3), "

View File

@ -0,0 +1,75 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * 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.
.\" *
.\" **************************************************************************
.\"
.TH CURLOPT_SSLKEY_BLOB 3 "24 Jun 2020" "libcurl 7.71.0" "curl_easy_setopt options"
.SH NAME
CURLOPT_SSLKEY_BLOB \- private key for client cert from memory blob
.SH SYNOPSIS
.nf
#include <curl/curl.h>
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SSLKEY_BLOB,
struct curl_blob *blob);
.fi
.SH DESCRIPTION
Pass a pointer to a curl_blob structure, which contains information (pointer
and size) for a private key. Compatible with OpenSSL. The format (like "PEM")
must be specified with \fICURLOPT_SSLKEYTYPE(3)\fP.
If the blob is initialized with the flags member of struct curl_blob set to
CURL_BLOB_COPY, the application does not have to keep the buffer around after
setting this.
This option is an alternative to \fICURLOPT_SSLKEY(3)\fP which instead expects
a file name as input.
.SH DEFAULT
NULL
.SH PROTOCOLS
All TLS based protocols: HTTPS, FTPS, IMAPS, POP3S, SMTPS etc.
.SH EXAMPLE
.nf
CURL *curl = curl_easy_init();
if(curl) {
struct curl_blob blob;
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/");
blob.data = certificateData;
blob.len = filesize;
blob.flags = CURL_BLOB_COPY;
curl_easy_setopt(curl, CURLOPT_SSLCERT_BLOB, &blob);
curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM");
blob.data = privateKeyData;
blob.len = privateKeySize;
curl_easy_setopt(curl, CURLOPT_SSLKEY_BLOB, &blob);
curl_easy_setopt(curl, CURLOPT_KEYPASSWD, "s3cret");
curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM");
ret = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
.fi
.SH AVAILABILITY
Added in libcurl 7.71.0. This option is supported by the OpenSSL backends.
.SH RETURN VALUE
Returns CURLE_OK if TLS enabled, CURLE_UNKNOWN_OPTION if not, or
CURLE_OUT_OF_MEMORY if there was insufficient heap space.
.SH "SEE ALSO"
.BR CURLOPT_SSLKEYTYPE "(3), " CURLOPT_SSLKEY "(3), "

View File

@ -198,6 +198,7 @@ man_MANS = \
CURLOPT_IOCTLFUNCTION.3 \
CURLOPT_IPRESOLVE.3 \
CURLOPT_ISSUERCERT.3 \
CURLOPT_ISSUERCERT_BLOB.3 \
CURLOPT_KEEP_SENDING_ON_ERROR.3 \
CURLOPT_KEYPASSWD.3 \
CURLOPT_KRBLEVEL.3 \
@ -260,8 +261,10 @@ man_MANS = \
CURLOPT_PROXY_PINNEDPUBLICKEY.3 \
CURLOPT_PROXY_SERVICE_NAME.3 \
CURLOPT_PROXY_SSLCERT.3 \
CURLOPT_PROXY_SSLCERT_BLOB.3 \
CURLOPT_PROXY_SSLCERTTYPE.3 \
CURLOPT_PROXY_SSLKEY.3 \
CURLOPT_PROXY_SSLKEY_BLOB.3 \
CURLOPT_PROXY_SSLKEYTYPE.3 \
CURLOPT_PROXY_SSLVERSION.3 \
CURLOPT_PROXY_SSL_CIPHER_LIST.3 \
@ -313,10 +316,12 @@ man_MANS = \
CURLOPT_SSH_PRIVATE_KEYFILE.3 \
CURLOPT_SSH_PUBLIC_KEYFILE.3 \
CURLOPT_SSLCERT.3 \
CURLOPT_SSLCERT_BLOB.3 \
CURLOPT_SSLCERTTYPE.3 \
CURLOPT_SSLENGINE.3 \
CURLOPT_SSLENGINE_DEFAULT.3 \
CURLOPT_SSLKEY.3 \
CURLOPT_SSLKEY_BLOB.3 \
CURLOPT_SSLKEYTYPE.3 \
CURLOPT_SSLVERSION.3 \
CURLOPT_SSL_CIPHER_LIST.3 \

View File

@ -347,6 +347,7 @@ CURLM_RECURSIVE_API_CALL 7.59.0
CURLM_UNKNOWN_OPTION 7.15.4
CURLM_WAKEUP_FAILURE 7.68.0
CURLOPT 7.69.0
CURLOPTTYPE_BLOB 7.71.0
CURLOPTTYPE_FUNCTIONPOINT 7.1
CURLOPTTYPE_LONG 7.1
CURLOPTTYPE_OBJECTPOINT 7.1
@ -459,6 +460,7 @@ CURLOPT_IOCTLDATA 7.12.3
CURLOPT_IOCTLFUNCTION 7.12.3
CURLOPT_IPRESOLVE 7.10.8
CURLOPT_ISSUERCERT 7.19.0
CURLOPT_ISSUERCERT_BLOB 7.71.0
CURLOPT_KEEP_SENDING_ON_ERROR 7.51.0
CURLOPT_KEYPASSWD 7.17.0
CURLOPT_KRB4LEVEL 7.3 7.17.0
@ -528,8 +530,10 @@ CURLOPT_PROXY_KEYPASSWD 7.52.0
CURLOPT_PROXY_PINNEDPUBLICKEY 7.52.0
CURLOPT_PROXY_SERVICE_NAME 7.43.0
CURLOPT_PROXY_SSLCERT 7.52.0
CURLOPT_PROXY_SSLCERT_BLOB 7.71.0
CURLOPT_PROXY_SSLCERTTYPE 7.52.0
CURLOPT_PROXY_SSLKEY 7.52.0
CURLOPT_PROXY_SSLKEY_BLOB 7.71.0
CURLOPT_PROXY_SSLKEYTYPE 7.52.0
CURLOPT_PROXY_SSLVERSION 7.52.0
CURLOPT_PROXY_SSL_CIPHER_LIST 7.52.0
@ -591,11 +595,13 @@ CURLOPT_SSH_KNOWNHOSTS 7.19.6
CURLOPT_SSH_PRIVATE_KEYFILE 7.16.1
CURLOPT_SSH_PUBLIC_KEYFILE 7.16.1
CURLOPT_SSLCERT 7.1
CURLOPT_SSLCERT_BLOB 7.71.0
CURLOPT_SSLCERTPASSWD 7.1.1 7.17.0
CURLOPT_SSLCERTTYPE 7.9.3
CURLOPT_SSLENGINE 7.9.3
CURLOPT_SSLENGINE_DEFAULT 7.9.3
CURLOPT_SSLKEY 7.9.3
CURLOPT_SSLKEY_BLOB 7.71.0
CURLOPT_SSLKEYPASSWD 7.9.3 7.17.0
CURLOPT_SSLKEYTYPE 7.9.3
CURLOPT_SSLVERSION 7.1

View File

@ -950,6 +950,7 @@ typedef enum {
#define CURLOPTTYPE_OBJECTPOINT 10000
#define CURLOPTTYPE_FUNCTIONPOINT 20000
#define CURLOPTTYPE_OFF_T 30000
#define CURLOPTTYPE_BLOB 40000
/* *STRINGPOINT is an alias for OBJECTPOINT to allow tools to extract the
string options from the header file */
@ -1959,6 +1960,13 @@ typedef enum {
/* allow RCPT TO command to fail for some recipients */
CURLOPT(CURLOPT_MAIL_RCPT_ALLLOWFAILS, CURLOPTTYPE_LONG, 290),
/* the private SSL-certificate as a "blob" */
CURLOPT(CURLOPT_SSLCERT_BLOB, CURLOPTTYPE_BLOB, 291),
CURLOPT(CURLOPT_SSLKEY_BLOB, CURLOPTTYPE_BLOB, 292),
CURLOPT(CURLOPT_PROXY_SSLCERT_BLOB, CURLOPTTYPE_BLOB, 293),
CURLOPT(CURLOPT_PROXY_SSLKEY_BLOB, CURLOPTTYPE_BLOB, 294),
CURLOPT(CURLOPT_ISSUERCERT_BLOB, CURLOPTTYPE_BLOB, 295),
CURLOPT_LASTENTRY /* the last unused */
} CURLoption;

View File

@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
* 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
@ -25,6 +25,17 @@
extern "C" {
#endif
/* Flag bits in the curl_blob struct: */
#define CURL_BLOB_COPY 1 /* tell libcurl to copy the data */
#define CURL_BLOB_NOCOPY 0 /* tell libcurl to NOT copy the data */
struct curl_blob {
void *data;
size_t len;
unsigned int flags; /* bit 0 is defined, the rest are reserved and should be
left zeroes */
};
CURL_EXTERN CURL *curl_easy_init(void);
CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...);
CURL_EXTERN CURLcode curl_easy_perform(CURL *curl);

View File

@ -764,6 +764,7 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src)
{
CURLcode result = CURLE_OK;
enum dupstring i;
enum dupblob j;
/* Copy src->set into dst->set first, then deal with the strings
afterwards */
@ -780,6 +781,16 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src)
return result;
}
/* clear all blob pointers first */
memset(dst->set.blobs, 0, BLOB_LAST * sizeof(struct curl_blob *));
/* duplicate all blobs */
for(j = (enum dupblob)0; j < BLOB_LAST; j++) {
result = Curl_setblobopt(&dst->set.blobs[j], src->set.blobs[j]);
/* Curl_setstropt return CURLE_BAD_FUNCTION_ARGUMENT with blob */
if(result)
return result;
}
/* duplicate memory areas pointed to */
i = STRING_COPYPOSTFIELDS;
if(src->set.postfieldsize && src->set.str[i]) {

View File

@ -77,6 +77,37 @@ CURLcode Curl_setstropt(char **charp, const char *s)
return CURLE_OK;
}
CURLcode Curl_setblobopt(struct curl_blob **blobp,
const struct curl_blob *blob)
{
/* free the previous storage at `blobp' and replace by a dynamic storage
copy of blob. If CURL_BLOB_COPY is set, the data is copied. */
Curl_safefree(*blobp);
if(blob) {
struct curl_blob *nblob;
if(blob->len > CURL_MAX_INPUT_LENGTH)
return CURLE_BAD_FUNCTION_ARGUMENT;
nblob = (struct curl_blob *)
malloc(sizeof(struct curl_blob) +
((blob->flags & CURL_BLOB_COPY) ? blob->len : 0));
if(!nblob)
return CURLE_OUT_OF_MEMORY;
*nblob = *blob;
if(blob->flags & CURL_BLOB_COPY) {
/* put the data after the blob struct in memory */
nblob->data = (char *)nblob + sizeof(struct curl_blob);
memcpy(nblob->data, blob->data, blob->len);
}
*blobp = nblob;
return CURLE_OK;
}
return CURLE_OK;
}
static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp)
{
CURLcode result = CURLE_OK;
@ -1606,6 +1637,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
result = Curl_setstropt(&data->set.str[STRING_CERT_ORIG],
va_arg(param, char *));
break;
case CURLOPT_SSLCERT_BLOB:
/*
* Blob that holds file name of the SSL certificate to use
*/
result = Curl_setblobopt(&data->set.blobs[BLOB_CERT_ORIG],
va_arg(param, struct curl_blob *));
break;
#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_SSLCERT:
/*
@ -1614,6 +1652,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
result = Curl_setstropt(&data->set.str[STRING_CERT_PROXY],
va_arg(param, char *));
break;
case CURLOPT_PROXY_SSLCERT_BLOB:
/*
* Blob that holds file name of the SSL certificate to use for proxy
*/
result = Curl_setblobopt(&data->set.blobs[BLOB_CERT_PROXY],
va_arg(param, struct curl_blob *));
break;
#endif
case CURLOPT_SSLCERTTYPE:
/*
@ -1638,6 +1683,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
result = Curl_setstropt(&data->set.str[STRING_KEY_ORIG],
va_arg(param, char *));
break;
case CURLOPT_SSLKEY_BLOB:
/*
* Blob that holds file name of the SSL key to use
*/
result = Curl_setblobopt(&data->set.blobs[BLOB_KEY_ORIG],
va_arg(param, struct curl_blob *));
break;
#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_SSLKEY:
/*
@ -1646,6 +1698,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
result = Curl_setstropt(&data->set.str[STRING_KEY_PROXY],
va_arg(param, char *));
break;
case CURLOPT_PROXY_SSLKEY_BLOB:
/*
* Blob that holds file name of the SSL key to use for proxy
*/
result = Curl_setblobopt(&data->set.blobs[BLOB_KEY_PROXY],
va_arg(param, struct curl_blob *));
break;
#endif
case CURLOPT_SSLKEYTYPE:
/*
@ -1970,6 +2029,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
result = Curl_setstropt(&data->set.str[STRING_SSL_ISSUERCERT_ORIG],
va_arg(param, char *));
break;
case CURLOPT_ISSUERCERT_BLOB:
/*
* Blob that holds Issuer certificate to check certificates issuer
*/
result = Curl_setblobopt(&data->set.blobs[BLOB_SSL_ISSUERCERT_ORIG],
va_arg(param, struct curl_blob *));
break;
#ifndef CURL_DISABLE_TELNET
case CURLOPT_TELNETOPTIONS:
/*

View File

@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
* 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
@ -23,6 +23,8 @@
***************************************************************************/
CURLcode Curl_setstropt(char **charp, const char *s);
CURLcode Curl_setblobopt(struct curl_blob **blobp,
const struct curl_blob *blob);
CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list arg);
#endif /* HEADER_CURL_SETOPT_H */

View File

@ -281,10 +281,16 @@ void Curl_freeset(struct Curl_easy *data)
{
/* Free all dynamic strings stored in the data->set substructure. */
enum dupstring i;
enum dupblob j;
for(i = (enum dupstring)0; i < STRING_LAST; i++) {
Curl_safefree(data->set.str[i]);
}
for(j = (enum dupblob)0; j < BLOB_LAST; j++) {
Curl_safefree(data->set.blobs[j]);
}
if(data->change.referer_alloc) {
Curl_safefree(data->change.referer);
data->change.referer_alloc = FALSE;
@ -3617,6 +3623,12 @@ static CURLcode create_conn(struct Curl_easy *data,
data->set.proxy_ssl.password = data->set.str[STRING_TLSAUTH_PASSWORD_PROXY];
#endif
data->set.ssl.cert_blob = data->set.blobs[BLOB_CERT_ORIG];
data->set.proxy_ssl.cert_blob = data->set.blobs[BLOB_CERT_PROXY];
data->set.ssl.key_blob = data->set.blobs[BLOB_KEY_ORIG];
data->set.proxy_ssl.key_blob = data->set.blobs[BLOB_KEY_PROXY];
data->set.ssl.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT_ORIG];
if(!Curl_clone_primary_ssl_config(&data->set.ssl.primary,
&conn->ssl_config)) {
result = CURLE_OUT_OF_MEMORY;

View File

@ -240,11 +240,14 @@ struct ssl_config_data {
long certverifyresult; /* result from the certificate verification */
char *CRLfile; /* CRL to check certificate revocation */
char *issuercert;/* optional issuer certificate filename */
struct curl_blob *issuercert_blob;
curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */
void *fsslctxp; /* parameter for call back */
char *cert; /* client certificate file name */
struct curl_blob *cert_blob;
char *cert_type; /* format for certificate (default: PEM)*/
char *key; /* private key file name */
struct curl_blob *key_blob;
char *key_type; /* format for private key (default: PEM) */
char *key_passwd; /* plain text private key password */
#ifdef USE_TLS_SRP
@ -1580,6 +1583,15 @@ enum dupstring {
STRING_LAST /* not used, just an end-of-list marker */
};
enum dupblob {
BLOB_CERT_ORIG,
BLOB_CERT_PROXY,
BLOB_KEY_ORIG,
BLOB_KEY_PROXY,
BLOB_SSL_ISSUERCERT_ORIG,
BLOB_LAST
};
/* callback that gets called when this easy handle is completed within a multi
handle. Only used for internally created transfers, like for example
DoH. */
@ -1711,6 +1723,7 @@ struct UserDefined {
long new_directory_perms; /* Permissions to use when creating remote dirs */
long ssh_auth_types; /* allowed SSH auth types */
char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */
struct curl_blob *blobs[BLOB_LAST];
unsigned int scope_id; /* Scope id for IPv6 */
long allowed_protocols;
long redir_protocols;

View File

@ -621,12 +621,136 @@ static bool is_pkcs11_uri(const char *string)
static CURLcode Curl_ossl_set_engine(struct Curl_easy *data,
const char *engine);
static int
SSL_CTX_use_certificate_bio(SSL_CTX *ctx, BIO *in, int type,
const char *key_passwd)
{
int ret = 0;
X509 *x = NULL;
if(type == SSL_FILETYPE_ASN1) {
/* j = ERR_R_ASN1_LIB; */
x = d2i_X509_bio(in, NULL);
}
else if(type == SSL_FILETYPE_PEM) {
/* ERR_R_PEM_LIB; */
x = PEM_read_bio_X509(in, NULL,
passwd_callback, (void *)key_passwd);
}
else {
ret = 0;
goto end;
}
if(x == NULL) {
ret = 0;
goto end;
}
ret = SSL_CTX_use_certificate(ctx, x);
end:
X509_free(x);
return ret;
}
static int
SSL_CTX_use_PrivateKey_bio(SSL_CTX *ctx, BIO* in, int type,
const char *key_passwd)
{
int ret = 0;
EVP_PKEY *pkey = NULL;
if(type == SSL_FILETYPE_PEM)
pkey = PEM_read_bio_PrivateKey(in, NULL, passwd_callback,
(void *)key_passwd);
else if(type == SSL_FILETYPE_ASN1)
pkey = d2i_PrivateKey_bio(in, NULL);
else {
ret = 0;
goto end;
}
if(pkey == NULL) {
ret = 0;
goto end;
}
ret = SSL_CTX_use_PrivateKey(ctx, pkey);
EVP_PKEY_free(pkey);
end:
return ret;
}
static int
SSL_CTX_use_certificate_chain_bio(SSL_CTX *ctx, BIO* in,
const char *key_passwd)
{
/* SSL_CTX_add1_chain_cert introduced in OpenSSL 1.0.2 */
#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) /* 1.0.2 or later */
int ret = 0;
X509 *x = NULL;
void *passwd_callback_userdata = (void *)key_passwd;
ERR_clear_error();
x = PEM_read_bio_X509_AUX(in, NULL,
passwd_callback, (void *)key_passwd);
if(x == NULL) {
ret = 0;
goto end;
}
ret = SSL_CTX_use_certificate(ctx, x);
if(ERR_peek_error() != 0)
ret = 0;
if(ret) {
X509 *ca;
unsigned long err;
if(!SSL_CTX_clear_chain_certs(ctx)) {
ret = 0;
goto end;
}
while((ca = PEM_read_bio_X509(in, NULL, passwd_callback,
passwd_callback_userdata))
!= NULL) {
if(!SSL_CTX_add0_chain_cert(ctx, ca)) {
X509_free(ca);
ret = 0;
goto end;
}
}
err = ERR_peek_last_error();
if((ERR_GET_LIB(err) == ERR_LIB_PEM) &&
(ERR_GET_REASON(err) == PEM_R_NO_START_LINE))
ERR_clear_error();
else
ret = 0;
}
end:
X509_free(x);
return ret;
#else
(void)ctx; /* unused */
(void)in; /* unused */
(void)key_passwd; /* unused */
return 0;
#endif
}
static
int cert_stuff(struct connectdata *conn,
SSL_CTX* ctx,
char *cert_file,
BIO *cert_bio,
const char *cert_type,
char *key_file,
BIO* key_bio,
const char *key_type,
char *key_passwd)
{
@ -636,10 +760,11 @@ int cert_stuff(struct connectdata *conn,
int file_type = do_file_type(cert_type);
if(cert_file || (file_type == SSL_FILETYPE_ENGINE)) {
if(cert_file || cert_bio || (file_type == SSL_FILETYPE_ENGINE)) {
SSL *ssl;
X509 *x509;
int cert_done = 0;
int cert_use_result;
if(key_passwd) {
/* set the password in the callback userdata */
@ -652,8 +777,10 @@ int cert_stuff(struct connectdata *conn,
switch(file_type) {
case SSL_FILETYPE_PEM:
/* SSL_CTX_use_certificate_chain_file() only works on PEM files */
if(SSL_CTX_use_certificate_chain_file(ctx,
cert_file) != 1) {
cert_use_result = cert_bio ?
SSL_CTX_use_certificate_chain_bio(ctx, cert_bio, key_passwd) :
SSL_CTX_use_certificate_chain_file(ctx, cert_file);
if(cert_use_result != 1) {
failf(data,
"could not load PEM client certificate, " OSSL_PACKAGE
" error %s, "
@ -668,9 +795,12 @@ int cert_stuff(struct connectdata *conn,
/* SSL_CTX_use_certificate_file() works with either PEM or ASN1, but
we use the case above for PEM so this can only be performed with
ASN1 files. */
if(SSL_CTX_use_certificate_file(ctx,
cert_file,
file_type) != 1) {
cert_use_result = cert_bio ?
SSL_CTX_use_certificate_bio(ctx, cert_bio,
file_type, key_passwd) :
SSL_CTX_use_certificate_file(ctx, cert_file, file_type);
if(cert_use_result != 1) {
failf(data,
"could not load ASN1 client certificate, " OSSL_PACKAGE
" error %s, "
@ -750,27 +880,31 @@ int cert_stuff(struct connectdata *conn,
PKCS12 *p12 = NULL;
EVP_PKEY *pri;
STACK_OF(X509) *ca = NULL;
if(!cert_bio) {
fp = BIO_new(BIO_s_file());
if(fp == NULL) {
failf(data,
"BIO_new return NULL, " OSSL_PACKAGE
" error %s",
ossl_strerror(ERR_get_error(), error_buffer,
sizeof(error_buffer)) );
return 0;
}
fp = BIO_new(BIO_s_file());
if(fp == NULL) {
failf(data,
"BIO_new return NULL, " OSSL_PACKAGE
" error %s",
ossl_strerror(ERR_get_error(), error_buffer,
sizeof(error_buffer)) );
return 0;
if(BIO_read_filename(fp, cert_file) <= 0) {
failf(data, "could not open PKCS12 file '%s'", cert_file);
BIO_free(fp);
return 0;
}
}
if(BIO_read_filename(fp, cert_file) <= 0) {
failf(data, "could not open PKCS12 file '%s'", cert_file);
p12 = d2i_PKCS12_bio(cert_bio ? cert_bio : fp, NULL);
if(fp)
BIO_free(fp);
return 0;
}
p12 = d2i_PKCS12_bio(fp, NULL);
BIO_free(fp);
if(!p12) {
failf(data, "error reading PKCS12 file '%s'", cert_file);
failf(data, "error reading PKCS12 file '%s'",
cert_bio ? "(memory blob)" : cert_file);
return 0;
}
@ -851,8 +985,10 @@ int cert_stuff(struct connectdata *conn,
return 0;
}
if(!key_file)
if((!key_file) && (!key_bio)) {
key_file = cert_file;
key_bio = cert_bio;
}
else
file_type = do_file_type(key_type);
@ -862,9 +998,12 @@ int cert_stuff(struct connectdata *conn,
break;
/* FALLTHROUGH */
case SSL_FILETYPE_ASN1:
if(SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type) != 1) {
cert_use_result = key_bio ?
SSL_CTX_use_PrivateKey_bio(ctx, key_bio, file_type, key_passwd) :
SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type);
if(cert_use_result != 1) {
failf(data, "unable to set private key file: '%s' type %s",
key_file, key_type?key_type:"PEM");
key_file?key_file:"(memory blob)", key_type?key_type:"PEM");
return 0;
}
break;
@ -2418,6 +2557,7 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
const enum CURL_TLSAUTH ssl_authtype = SSL_SET_OPTION(authtype);
#endif
char * const ssl_cert = SSL_SET_OPTION(cert);
const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(cert_blob);
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);
@ -2662,10 +2802,33 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
}
#endif
if(ssl_cert || ssl_cert_type) {
if(!cert_stuff(conn, backend->ctx, ssl_cert, ssl_cert_type,
SSL_SET_OPTION(key), SSL_SET_OPTION(key_type),
SSL_SET_OPTION(key_passwd))) {
if(ssl_cert || ssl_cert_blob || ssl_cert_type) {
BIO *ssl_cert_bio = NULL;
BIO *ssl_key_bio = NULL;
int result_cert_stuff;
if(ssl_cert_blob) {
/* the typecast of blob->len is fine since it is guaranteed to never be
larger than CURL_MAX_INPUT_LENGTH */
ssl_cert_bio = BIO_new_mem_buf(ssl_cert_blob->data,
(int)ssl_cert_blob->len);
if(!ssl_cert_bio)
return CURLE_SSL_CERTPROBLEM;
}
if(SSL_SET_OPTION(key_blob)) {
ssl_key_bio = BIO_new_mem_buf(SSL_SET_OPTION(key_blob)->data,
(int)SSL_SET_OPTION(key_blob)->len);
if(!ssl_key_bio)
return CURLE_SSL_CERTPROBLEM;
}
result_cert_stuff = cert_stuff(conn, backend->ctx,
ssl_cert, ssl_cert_bio, ssl_cert_type,
SSL_SET_OPTION(key), ssl_key_bio,
SSL_SET_OPTION(key_type), SSL_SET_OPTION(key_passwd));
if(ssl_cert_bio)
BIO_free(ssl_cert_bio);
if(ssl_key_bio)
BIO_free(ssl_key_bio);
if(!result_cert_stuff) {
/* failf() is already done in cert_stuff() */
return CURLE_SSL_CERTPROBLEM;
}
@ -3714,27 +3877,32 @@ static CURLcode servercert(struct connectdata *conn,
deallocating the certificate. */
/* e.g. match issuer name with provided issuer certificate */
if(SSL_SET_OPTION(issuercert)) {
fp = BIO_new(BIO_s_file());
if(fp == NULL) {
failf(data,
"BIO_new return NULL, " OSSL_PACKAGE
" error %s",
ossl_strerror(ERR_get_error(), error_buffer,
sizeof(error_buffer)) );
X509_free(backend->server_cert);
backend->server_cert = NULL;
return CURLE_OUT_OF_MEMORY;
}
if(SSL_SET_OPTION(issuercert) || SSL_SET_OPTION(issuercert_blob)) {
if(SSL_SET_OPTION(issuercert_blob))
fp = BIO_new_mem_buf(SSL_SET_OPTION(issuercert_blob)->data,
(int)SSL_SET_OPTION(issuercert_blob)->len);
else {
fp = BIO_new(BIO_s_file());
if(fp == NULL) {
failf(data,
"BIO_new return NULL, " OSSL_PACKAGE
" error %s",
ossl_strerror(ERR_get_error(), error_buffer,
sizeof(error_buffer)) );
X509_free(backend->server_cert);
backend->server_cert = NULL;
return CURLE_OUT_OF_MEMORY;
}
if(BIO_read_filename(fp, SSL_SET_OPTION(issuercert)) <= 0) {
if(strict)
failf(data, "SSL: Unable to open issuer cert (%s)",
SSL_SET_OPTION(issuercert));
BIO_free(fp);
X509_free(backend->server_cert);
backend->server_cert = NULL;
return CURLE_SSL_ISSUER_ERROR;
if(BIO_read_filename(fp, SSL_SET_OPTION(issuercert)) <= 0) {
if(strict)
failf(data, "SSL: Unable to open issuer cert (%s)",
SSL_SET_OPTION(issuercert));
BIO_free(fp);
X509_free(backend->server_cert);
backend->server_cert = NULL;
return CURLE_SSL_ISSUER_ERROR;
}
}
issuer = PEM_read_bio_X509(fp, NULL, ZERO_NULL, NULL);

View File

@ -39,6 +39,7 @@
#include "schannel.h"
#include "vtls.h"
#include "strcase.h"
#include "sendf.h"
#include "connect.h" /* for the connect timeout */
#include "strerror.h"
@ -583,94 +584,122 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
#ifdef HAS_CLIENT_CERT_PATH
/* client certificate */
if(data->set.ssl.cert) {
DWORD cert_store_name;
if(data->set.ssl.cert || data->set.ssl.cert_blob) {
DWORD cert_store_name = 0;
TCHAR *cert_store_path = NULL;
TCHAR *cert_thumbprint_str;
TCHAR *cert_thumbprint_str = NULL;
CRYPT_HASH_BLOB cert_thumbprint;
BYTE cert_thumbprint_data[CERT_THUMBPRINT_DATA_LEN];
HCERTSTORE cert_store;
HCERTSTORE cert_store = NULL;
FILE *fInCert = NULL;
void *certdata = NULL;
size_t certsize = 0;
bool blob = data->set.ssl.cert_blob != NULL;
TCHAR *cert_path = NULL;
if(blob) {
certdata = data->set.ssl.cert_blob->data;
certsize = data->set.ssl.cert_blob->len;
}
else {
cert_path = curlx_convert_UTF8_to_tchar(data->set.ssl.cert);
if(!cert_path)
return CURLE_OUT_OF_MEMORY;
TCHAR *cert_path = curlx_convert_UTF8_to_tchar(data->set.ssl.cert);
if(!cert_path)
return CURLE_OUT_OF_MEMORY;
result = get_cert_location(cert_path, &cert_store_name,
&cert_store_path, &cert_thumbprint_str);
result = get_cert_location(cert_path, &cert_store_name,
&cert_store_path, &cert_thumbprint_str);
if((result != CURLE_OK) && (data->set.ssl.cert[0]!='\0'))
fInCert = fopen(data->set.ssl.cert, "rb");
if(result && (data->set.ssl.cert[0]!='\0'))
fInCert = fopen(data->set.ssl.cert, "rb");
if((result != CURLE_OK) && (fInCert == NULL)) {
failf(data, "schannel: Failed to get certificate location"
" or file for %s",
data->set.ssl.cert);
curlx_unicodefree(cert_path);
return result;
if(result && !fInCert) {
failf(data, "schannel: Failed to get certificate location"
" or file for %s",
data->set.ssl.cert);
curlx_unicodefree(cert_path);
return result;
}
}
if(fInCert) {
if((fInCert || blob) && (data->set.ssl.cert_type) &&
(!strcasecompare(data->set.ssl.cert_type, "P12"))) {
failf(data, "schannel: certificate format compatibility error "
" for %s",
blob ? "(memory blob)" : data->set.ssl.cert);
curlx_unicodefree(cert_path);
return CURLE_SSL_CERTPROBLEM;
}
if(fInCert || blob) {
/* Reading a .P12 or .pfx file, like the example at bottom of
https://social.msdn.microsoft.com/Forums/windowsdesktop/
en-US/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5
https://social.msdn.microsoft.com/Forums/windowsdesktop/
en-US/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5
*/
void *certdata = NULL;
long filesize = 0;
CRYPT_DATA_BLOB datablob;
WCHAR* pszPassword;
size_t pwd_len = 0;
int str_w_len = 0;
int continue_reading = fseek(fInCert, 0, SEEK_END) == 0;
if(continue_reading)
filesize = ftell(fInCert);
if(filesize < 0)
continue_reading = 0;
if(continue_reading)
continue_reading = fseek(fInCert, 0, SEEK_SET) == 0;
if(continue_reading)
certdata = malloc(((size_t)filesize) + 1);
if((certdata == NULL) ||
((int) fread(certdata, (size_t)filesize, 1, fInCert) != 1))
continue_reading = 0;
fclose(fInCert);
const char *cert_showfilename_error = blob ?
"(memory blob)" : data->set.ssl.cert;
curlx_unicodefree(cert_path);
if(!continue_reading) {
failf(data, "schannel: Failed to read cert file %s",
if(fInCert) {
long cert_tell = 0;
bool continue_reading = fseek(fInCert, 0, SEEK_END) == 0;
if(continue_reading)
cert_tell = ftell(fInCert);
if(cert_tell < 0)
continue_reading = FALSE;
else
certsize = (size_t)cert_tell;
if(continue_reading)
continue_reading = fseek(fInCert, 0, SEEK_SET) == 0;
if(continue_reading)
certdata = malloc(certsize + 1);
if((!certdata) ||
((int) fread(certdata, certsize, 1, fInCert) != 1))
continue_reading = FALSE;
fclose(fInCert);
if(!continue_reading) {
failf(data, "schannel: Failed to read cert file %s",
data->set.ssl.cert);
free(certdata);
return CURLE_SSL_CERTPROBLEM;
free(certdata);
return CURLE_SSL_CERTPROBLEM;
}
}
/* Convert key-pair data to the in-memory certificate store */
datablob.pbData = (BYTE*)certdata;
datablob.cbData = (DWORD)filesize;
datablob.cbData = (DWORD)certsize;
if(data->set.ssl.key_passwd != NULL)
pwd_len = strlen(data->set.ssl.key_passwd);
pszPassword = (WCHAR*)malloc(sizeof(WCHAR)*(pwd_len + 1));
if(pwd_len > 0)
str_w_len =
MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
data->set.ssl.key_passwd, (int)pwd_len,
pszPassword, (int)(pwd_len + 1));
if(pszPassword) {
if(pwd_len > 0)
str_w_len = MultiByteToWideChar(CP_UTF8,
MB_ERR_INVALID_CHARS,
data->set.ssl.key_passwd, (int)pwd_len,
pszPassword, (int)(pwd_len + 1));
if((str_w_len >= 0) && (str_w_len <= (int)pwd_len))
pszPassword[str_w_len] = 0;
else
pszPassword[0] = 0;
if((str_w_len >= 0) && (str_w_len <= (int)pwd_len))
pszPassword[str_w_len] = 0;
else
pszPassword[0] = 0;
cert_store = PFXImportCertStore(&datablob, pszPassword, 0);
free(pszPassword);
free(certdata);
cert_store = PFXImportCertStore(&datablob, pszPassword, 0);
free(pszPassword);
}
if(!blob)
free(certdata);
if(cert_store == NULL) {
DWORD errorcode = GetLastError();
if(errorcode == ERROR_INVALID_PASSWORD)
failf(data, "schannel: Failed to import cert file %s, "
"password is bad", data->set.ssl.cert);
"password is bad",
cert_showfilename_error);
else
failf(data, "schannel: Failed to import cert file %s, "
"last error is 0x%x", data->set.ssl.cert, errorcode);
"last error is 0x%x",
cert_showfilename_error, errorcode);
return CURLE_SSL_CERTPROBLEM;
}
@ -681,7 +710,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
if(client_certs[0] == NULL) {
failf(data, "schannel: Failed to get certificate from file %s"
", last error is 0x%x",
data->set.ssl.cert, GetLastError());
cert_showfilename_error, GetLastError());
CertCloseStore(cert_store, 0);
return CURLE_SSL_CERTPROBLEM;
}

View File

@ -1126,12 +1126,12 @@ static OSStatus CopyIdentityWithLabel(char *label,
}
static OSStatus CopyIdentityFromPKCS12File(const char *cPath,
const struct curl_blob *blob,
const char *cPassword,
SecIdentityRef *out_cert_and_key)
{
OSStatus status = errSecItemNotFound;
CFURLRef pkcs_url = CFURLCreateFromFileSystemRepresentation(NULL,
(const UInt8 *)cPath, strlen(cPath), false);
CFURLRef pkcs_url = NULL;
CFStringRef password = cPassword ? CFStringCreateWithCString(NULL,
cPassword, kCFStringEncodingUTF8) : NULL;
CFDataRef pkcs_data = NULL;
@ -1140,8 +1140,26 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath,
/* These constants are documented as having first appeared in 10.6 but they
raise linker errors when used on that cat for some reason. */
#if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS
if(CFURLCreateDataAndPropertiesFromResource(NULL, pkcs_url, &pkcs_data,
NULL, NULL, &status)) {
bool resource_imported;
if(blob) {
pkcs_data = CFDataCreate(kCFAllocatorDefault,
(const unsigned char *)blob->data, blob->len);
status = (pkcs_data != NULL) ? errSecSuccess : errSecAllocate;
resource_imported = (pkcs_data != NULL);
}
else {
pkcs_url =
CFURLCreateFromFileSystemRepresentation(NULL,
(const UInt8 *)cPath,
strlen(cPath), false);
resource_imported =
CFURLCreateDataAndPropertiesFromResource(NULL,
pkcs_url, &pkcs_data,
NULL, NULL, &status);
}
if(resource_imported) {
CFArrayRef items = NULL;
/* On iOS SecPKCS12Import will never add the client certificate to the
@ -1219,7 +1237,8 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath,
#endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */
if(password)
CFRelease(password);
CFRelease(pkcs_url);
if(pkcs_url)
CFRelease(pkcs_url);
return status;
}
@ -1376,8 +1395,10 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn,
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct ssl_backend_data *backend = connssl->backend;
const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile);
const struct curl_blob *ssl_cablob = NULL;
const bool verifypeer = SSL_CONN_CONFIG(verifypeer);
char * const ssl_cert = SSL_SET_OPTION(cert);
const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(cert_blob);
const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
conn->host.name;
const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port;
@ -1612,15 +1633,16 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn,
"Transport. The private key must be in the Keychain.\n");
}
if(ssl_cert) {
if(ssl_cert || ssl_cert_blob) {
bool is_cert_data = ssl_cert_blob != NULL;
bool is_cert_file = (!is_cert_data) && is_file(ssl_cert);
SecIdentityRef cert_and_key = NULL;
bool is_cert_file = is_file(ssl_cert);
/* User wants to authenticate with a client cert. Look for it:
If we detect that this is a file on disk, then let's load it.
Otherwise, assume that the user wants to use an identity loaded
from the Keychain. */
if(is_cert_file) {
if(is_cert_file || is_cert_data) {
if(!SSL_SET_OPTION(cert_type))
infof(data, "WARNING: SSL: Certificate type not set, assuming "
"PKCS#12 format.\n");
@ -1629,7 +1651,7 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn,
infof(data, "WARNING: SSL: The Security framework only supports "
"loading identities that are in PKCS#12 format.\n");
err = CopyIdentityFromPKCS12File(ssl_cert,
err = CopyIdentityFromPKCS12File(ssl_cert, ssl_cert_blob,
SSL_SET_OPTION(key_passwd), &cert_and_key);
}
else
@ -1669,27 +1691,30 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn,
CFRelease(cert_and_key);
}
else {
const char *cert_showfilename_error =
is_cert_data ? "(memory blob)" : ssl_cert;
switch(err) {
case errSecAuthFailed: case -25264: /* errSecPkcs12VerifyFailure */
failf(data, "SSL: Incorrect password for the certificate \"%s\" "
"and its private key.", ssl_cert);
"and its private key.", cert_showfilename_error);
break;
case -26275: /* errSecDecode */ case -25257: /* errSecUnknownFormat */
failf(data, "SSL: Couldn't make sense of the data in the "
"certificate \"%s\" and its private key.",
ssl_cert);
cert_showfilename_error);
break;
case -25260: /* errSecPassphraseRequired */
failf(data, "SSL The certificate \"%s\" requires a password.",
ssl_cert);
cert_showfilename_error);
break;
case errSecItemNotFound:
failf(data, "SSL: Can't find the certificate \"%s\" and its private "
"key in the Keychain.", ssl_cert);
"key in the Keychain.", cert_showfilename_error);
break;
default:
failf(data, "SSL: Can't load the certificate \"%s\" and its private "
"key: OSStatus %d", ssl_cert, err);
"key: OSStatus %d", cert_showfilename_error, err);
break;
}
return CURLE_SSL_CERTPROBLEM;
@ -1721,7 +1746,8 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn,
#else
if(SSLSetSessionOption != NULL) {
#endif /* CURL_BUILD_MAC */
bool break_on_auth = !conn->ssl_config.verifypeer || ssl_cafile;
bool break_on_auth = !conn->ssl_config.verifypeer ||
ssl_cafile || ssl_cablob;
err = SSLSetSessionOption(backend->ssl_ctx,
kSSLSessionOptionBreakOnServerAuth,
break_on_auth);
@ -1749,10 +1775,11 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn,
}
#endif /* CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS */
if(ssl_cafile && verifypeer) {
bool is_cert_file = is_file(ssl_cafile);
if((ssl_cafile || ssl_cablob) && verifypeer) {
bool is_cert_data = ssl_cablob != NULL;
bool is_cert_file = (!is_cert_data) && is_file(ssl_cafile);
if(!is_cert_file) {
if(!(is_cert_file || is_cert_data)) {
failf(data, "SSL: can't load CA certificate file %s", ssl_cafile);
return CURLE_SSL_CACERT_BADFILE;
}

View File

@ -1556,11 +1556,90 @@ static CURLcode single_transfer(struct GlobalConfig *global,
}
}
/* In debug build of curl tool, using
* --cert loadmem=<filename>:<password> --cert-type p12
* must do the same thing than classic:
* --cert <filename>:<password> --cert-type p12
* but is designed to test blob */
#if defined(CURLDEBUG) || defined(DEBUGBUILD)
if(config->cert && (strlen(config->cert) > 8) &&
(memcmp(config->cert, "loadmem=",8) == 0)) {
FILE *fInCert = fopen(config->cert + 8, "rb");
void *certdata = NULL;
long filesize = 0;
bool continue_reading = fInCert != NULL;
if(continue_reading)
continue_reading = fseek(fInCert, 0, SEEK_END) == 0;
if(continue_reading)
filesize = ftell(fInCert);
if(filesize < 0)
continue_reading = FALSE;
if(continue_reading)
continue_reading = fseek(fInCert, 0, SEEK_SET) == 0;
if(continue_reading)
certdata = malloc(((size_t)filesize) + 1);
if((!certdata) ||
((int)fread(certdata, (size_t)filesize, 1, fInCert) != 1))
continue_reading = FALSE;
if(fInCert)
fclose(fInCert);
if((filesize > 0) && continue_reading) {
struct curl_blob structblob;
structblob.data = certdata;
structblob.len = (size_t)filesize;
structblob.flags = CURL_BLOB_COPY;
my_setopt_str(curl, CURLOPT_SSLCERT_BLOB, &structblob);
/* if test run well, we are sure we don't reuse
* original mem pointer */
memset(certdata, 0, (size_t)filesize);
}
free(certdata);
}
else
#endif
my_setopt_str(curl, CURLOPT_SSLCERT, config->cert);
my_setopt_str(curl, CURLOPT_PROXY_SSLCERT, config->proxy_cert);
my_setopt_str(curl, CURLOPT_SSLCERTTYPE, config->cert_type);
my_setopt_str(curl, CURLOPT_PROXY_SSLCERTTYPE,
config->proxy_cert_type);
#if defined(CURLDEBUG) || defined(DEBUGBUILD)
if(config->key && (strlen(config->key) > 8) &&
(memcmp(config->key, "loadmem=",8) == 0)) {
FILE *fInCert = fopen(config->key + 8, "rb");
void *certdata = NULL;
long filesize = 0;
bool continue_reading = fInCert != NULL;
if(continue_reading)
continue_reading = fseek(fInCert, 0, SEEK_END) == 0;
if(continue_reading)
filesize = ftell(fInCert);
if(filesize < 0)
continue_reading = FALSE;
if(continue_reading)
continue_reading = fseek(fInCert, 0, SEEK_SET) == 0;
if(continue_reading)
certdata = malloc(((size_t)filesize) + 1);
if((!certdata) ||
((int)fread(certdata, (size_t)filesize, 1, fInCert) != 1))
continue_reading = FALSE;
if(fInCert)
fclose(fInCert);
if((filesize > 0) && continue_reading) {
struct curl_blob structblob;
structblob.data = certdata;
structblob.len = (size_t)filesize;
structblob.flags = CURL_BLOB_COPY;
my_setopt_str(curl, CURLOPT_SSLKEY_BLOB, &structblob);
/* if test run well, we are sure we don't reuse
* original mem pointer */
memset(certdata, 0, (size_t)filesize);
}
free(certdata);
}
else
#endif
my_setopt_str(curl, CURLOPT_SSLKEY, config->key);
my_setopt_str(curl, CURLOPT_PROXY_SSLKEY, config->proxy_key);
my_setopt_str(curl, CURLOPT_SSLKEYTYPE, config->key_type);

View File

@ -683,7 +683,7 @@ CURLcode tool_setopt(CURL *curl, bool str, struct GlobalConfig *config,
ret = curl_easy_setopt(curl, tag, pval);
}
else {
else if(tag < CURLOPTTYPE_BLOB) {
/* Value is expected to be curl_off_t */
curl_off_t oval = va_arg(arg, curl_off_t);
msnprintf(buf, sizeof(buf),
@ -694,6 +694,20 @@ CURLcode tool_setopt(CURL *curl, bool str, struct GlobalConfig *config,
if(!oval)
skip = TRUE;
}
else {
/* Value is a blob */
void *pblob = va_arg(arg, void *);
/* blobs are never printable */
if(pblob) {
value = "blobpointer";
remark = TRUE;
}
else
skip = TRUE;
ret = curl_easy_setopt(curl, tag, pblob);
}
va_end(arg);