Преглед изворни кода

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
Gilles Vollant пре 4 година
родитељ
комит
cac5374298

+ 10 - 0
docs/libcurl/curl_easy_setopt.3

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

+ 79 - 0
docs/libcurl/opts/CURLOPT_ISSUERCERT_BLOB.3

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

+ 72 - 0
docs/libcurl/opts/CURLOPT_PROXY_SSLCERT_BLOB.3

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

+ 73 - 0
docs/libcurl/opts/CURLOPT_PROXY_SSLKEY_BLOB.3

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

+ 6 - 4
docs/libcurl/opts/CURLOPT_SSLCERT.3

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

+ 69 - 0
docs/libcurl/opts/CURLOPT_SSLCERT_BLOB.3

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

+ 75 - 0
docs/libcurl/opts/CURLOPT_SSLKEY_BLOB.3

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

+ 5 - 0
docs/libcurl/opts/Makefile.inc

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

+ 6 - 0
docs/libcurl/symbols-in-versions

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

+ 8 - 0
include/curl/curl.h

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

+ 12 - 1
include/curl/easy.h

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

+ 11 - 0
lib/easy.c

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

+ 66 - 0
lib/setopt.c

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

+ 3 - 1
lib/setopt.h

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

+ 12 - 0
lib/url.c

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

+ 13 - 0
lib/urldata.h

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

+ 216 - 48
lib/vtls/openssl.c

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

+ 86 - 57
lib/vtls/schannel.c

@@ -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);
+
+        if(result && (data->set.ssl.cert[0]!='\0'))
+          fInCert = fopen(data->set.ssl.cert, "rb");
 
-      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 && !fInCert) {
+          failf(data, "schannel: Failed to get certificate location"
+                " or file for %s",
+                data->set.ssl.cert);
+          curlx_unicodefree(cert_path);
+          return result;
+        }
+      }
 
-      if((result != CURLE_OK) && (fInCert == NULL)) {
-        failf(data, "schannel: Failed to get certificate location"
-              " or file for %s",
-              data->set.ssl.cert);
+      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 result;
+        return CURLE_SSL_CERTPROBLEM;
       }
 
-      if(fInCert) {
+      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((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);
+        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;
+
+          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;
         }

+ 45 - 18
lib/vtls/sectransp.c

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

+ 79 - 0
src/tool_operate.c

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

+ 15 - 1
src/tool_setopt.c

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