Browse Source

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 years ago
parent
commit
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
 .SH SSL and SECURITY OPTIONS
 .IP CURLOPT_SSLCERT
 .IP CURLOPT_SSLCERT
 Client cert. See \fICURLOPT_SSLCERT(3)\fP
 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
 .IP CURLOPT_PROXY_SSLCERT
 Proxy client cert. See \fICURLOPT_PROXY_SSLCERT(3)\fP
 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
 .IP CURLOPT_SSLCERTTYPE
 Client cert type.  See \fICURLOPT_SSLCERTTYPE(3)\fP
 Client cert type.  See \fICURLOPT_SSLCERTTYPE(3)\fP
 .IP CURLOPT_PROXY_SSLCERTTYPE
 .IP CURLOPT_PROXY_SSLCERTTYPE
 Proxy client cert type.  See \fICURLOPT_PROXY_SSLCERTTYPE(3)\fP
 Proxy client cert type.  See \fICURLOPT_PROXY_SSLCERTTYPE(3)\fP
 .IP CURLOPT_SSLKEY
 .IP CURLOPT_SSLKEY
 Client key. See \fICURLOPT_SSLKEY(3)\fP
 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
 .IP CURLOPT_PROXY_SSLKEY
 Proxy client key. See \fICURLOPT_PROXY_SSLKEY(3)\fP
 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
 .IP CURLOPT_SSLKEYTYPE
 Client key type. See \fICURLOPT_SSLKEYTYPE(3)\fP
 Client key type. See \fICURLOPT_SSLKEYTYPE(3)\fP
 .IP CURLOPT_PROXY_SSLKEYTYPE
 .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
 Proxy CA cert bundle. See \fICURLOPT_PROXY_CAINFO(3)\fP
 .IP CURLOPT_ISSUERCERT
 .IP CURLOPT_ISSUERCERT
 Issuer certificate. See \fICURLOPT_ISSUERCERT(3)\fP
 Issuer certificate. See \fICURLOPT_ISSUERCERT(3)\fP
+.IP CURLOPT_ISSUERCERT_BLOB
+Issuer certificate memory buffer. See \fICURLOPT_ISSUERCERT_BLOB(3)\fP
 .IP CURLOPT_CAPATH
 .IP CURLOPT_CAPATH
 Path to CA cert bundle. See \fICURLOPT_CAPATH(3)\fP
 Path to CA cert bundle. See \fICURLOPT_CAPATH(3)\fP
 .IP CURLOPT_PROXY_CAPATH
 .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 "./"
 want to use a file from the current directory, please precede it with "./"
 prefix, in order to avoid confusion with a nickname.
 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
 "CurrentUser\\MY\\934a7ac6f8a5d579285a74fa61e19f23ddfe8d7a". Thumbprint is
 usually a SHA-1 hex string which you can see in certificate details. Following
 usually a SHA-1 hex string which you can see in certificate details. Following
 store locations are supported: CurrentUser, LocalMachine, CurrentService,
 store locations are supported: CurrentUser, LocalMachine, CurrentService,
 Services, CurrentUserGroupPolicy, LocalMachineGroupPolicy,
 Services, CurrentUserGroupPolicy, LocalMachineGroupPolicy,
 LocalMachineEnterprise.
 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
 When using a client certificate, you most likely also need to provide a
 private key with \fICURLOPT_SSLKEY(3)\fP.
 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_IOCTLFUNCTION.3                       \
   CURLOPT_IPRESOLVE.3                           \
   CURLOPT_IPRESOLVE.3                           \
   CURLOPT_ISSUERCERT.3                          \
   CURLOPT_ISSUERCERT.3                          \
+  CURLOPT_ISSUERCERT_BLOB.3                     \
   CURLOPT_KEEP_SENDING_ON_ERROR.3               \
   CURLOPT_KEEP_SENDING_ON_ERROR.3               \
   CURLOPT_KEYPASSWD.3                           \
   CURLOPT_KEYPASSWD.3                           \
   CURLOPT_KRBLEVEL.3                            \
   CURLOPT_KRBLEVEL.3                            \
@@ -260,8 +261,10 @@ man_MANS =                                      \
   CURLOPT_PROXY_PINNEDPUBLICKEY.3               \
   CURLOPT_PROXY_PINNEDPUBLICKEY.3               \
   CURLOPT_PROXY_SERVICE_NAME.3                  \
   CURLOPT_PROXY_SERVICE_NAME.3                  \
   CURLOPT_PROXY_SSLCERT.3                       \
   CURLOPT_PROXY_SSLCERT.3                       \
+  CURLOPT_PROXY_SSLCERT_BLOB.3                  \
   CURLOPT_PROXY_SSLCERTTYPE.3                   \
   CURLOPT_PROXY_SSLCERTTYPE.3                   \
   CURLOPT_PROXY_SSLKEY.3                        \
   CURLOPT_PROXY_SSLKEY.3                        \
+  CURLOPT_PROXY_SSLKEY_BLOB.3                   \
   CURLOPT_PROXY_SSLKEYTYPE.3                    \
   CURLOPT_PROXY_SSLKEYTYPE.3                    \
   CURLOPT_PROXY_SSLVERSION.3                    \
   CURLOPT_PROXY_SSLVERSION.3                    \
   CURLOPT_PROXY_SSL_CIPHER_LIST.3               \
   CURLOPT_PROXY_SSL_CIPHER_LIST.3               \
@@ -313,10 +316,12 @@ man_MANS =                                      \
   CURLOPT_SSH_PRIVATE_KEYFILE.3                 \
   CURLOPT_SSH_PRIVATE_KEYFILE.3                 \
   CURLOPT_SSH_PUBLIC_KEYFILE.3                  \
   CURLOPT_SSH_PUBLIC_KEYFILE.3                  \
   CURLOPT_SSLCERT.3                             \
   CURLOPT_SSLCERT.3                             \
+  CURLOPT_SSLCERT_BLOB.3                        \
   CURLOPT_SSLCERTTYPE.3                         \
   CURLOPT_SSLCERTTYPE.3                         \
   CURLOPT_SSLENGINE.3                           \
   CURLOPT_SSLENGINE.3                           \
   CURLOPT_SSLENGINE_DEFAULT.3                   \
   CURLOPT_SSLENGINE_DEFAULT.3                   \
   CURLOPT_SSLKEY.3                              \
   CURLOPT_SSLKEY.3                              \
+  CURLOPT_SSLKEY_BLOB.3                         \
   CURLOPT_SSLKEYTYPE.3                          \
   CURLOPT_SSLKEYTYPE.3                          \
   CURLOPT_SSLVERSION.3                          \
   CURLOPT_SSLVERSION.3                          \
   CURLOPT_SSL_CIPHER_LIST.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_UNKNOWN_OPTION            7.15.4
 CURLM_WAKEUP_FAILURE            7.68.0
 CURLM_WAKEUP_FAILURE            7.68.0
 CURLOPT                         7.69.0
 CURLOPT                         7.69.0
+CURLOPTTYPE_BLOB                7.71.0
 CURLOPTTYPE_FUNCTIONPOINT       7.1
 CURLOPTTYPE_FUNCTIONPOINT       7.1
 CURLOPTTYPE_LONG                7.1
 CURLOPTTYPE_LONG                7.1
 CURLOPTTYPE_OBJECTPOINT         7.1
 CURLOPTTYPE_OBJECTPOINT         7.1
@@ -459,6 +460,7 @@ CURLOPT_IOCTLDATA               7.12.3
 CURLOPT_IOCTLFUNCTION           7.12.3
 CURLOPT_IOCTLFUNCTION           7.12.3
 CURLOPT_IPRESOLVE               7.10.8
 CURLOPT_IPRESOLVE               7.10.8
 CURLOPT_ISSUERCERT              7.19.0
 CURLOPT_ISSUERCERT              7.19.0
+CURLOPT_ISSUERCERT_BLOB         7.71.0
 CURLOPT_KEEP_SENDING_ON_ERROR   7.51.0
 CURLOPT_KEEP_SENDING_ON_ERROR   7.51.0
 CURLOPT_KEYPASSWD               7.17.0
 CURLOPT_KEYPASSWD               7.17.0
 CURLOPT_KRB4LEVEL               7.3           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_PINNEDPUBLICKEY   7.52.0
 CURLOPT_PROXY_SERVICE_NAME      7.43.0
 CURLOPT_PROXY_SERVICE_NAME      7.43.0
 CURLOPT_PROXY_SSLCERT           7.52.0
 CURLOPT_PROXY_SSLCERT           7.52.0
+CURLOPT_PROXY_SSLCERT_BLOB      7.71.0
 CURLOPT_PROXY_SSLCERTTYPE       7.52.0
 CURLOPT_PROXY_SSLCERTTYPE       7.52.0
 CURLOPT_PROXY_SSLKEY            7.52.0
 CURLOPT_PROXY_SSLKEY            7.52.0
+CURLOPT_PROXY_SSLKEY_BLOB       7.71.0
 CURLOPT_PROXY_SSLKEYTYPE        7.52.0
 CURLOPT_PROXY_SSLKEYTYPE        7.52.0
 CURLOPT_PROXY_SSLVERSION        7.52.0
 CURLOPT_PROXY_SSLVERSION        7.52.0
 CURLOPT_PROXY_SSL_CIPHER_LIST   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_PRIVATE_KEYFILE     7.16.1
 CURLOPT_SSH_PUBLIC_KEYFILE      7.16.1
 CURLOPT_SSH_PUBLIC_KEYFILE      7.16.1
 CURLOPT_SSLCERT                 7.1
 CURLOPT_SSLCERT                 7.1
+CURLOPT_SSLCERT_BLOB            7.71.0
 CURLOPT_SSLCERTPASSWD           7.1.1         7.17.0
 CURLOPT_SSLCERTPASSWD           7.1.1         7.17.0
 CURLOPT_SSLCERTTYPE             7.9.3
 CURLOPT_SSLCERTTYPE             7.9.3
 CURLOPT_SSLENGINE               7.9.3
 CURLOPT_SSLENGINE               7.9.3
 CURLOPT_SSLENGINE_DEFAULT       7.9.3
 CURLOPT_SSLENGINE_DEFAULT       7.9.3
 CURLOPT_SSLKEY                  7.9.3
 CURLOPT_SSLKEY                  7.9.3
+CURLOPT_SSLKEY_BLOB             7.71.0
 CURLOPT_SSLKEYPASSWD            7.9.3         7.17.0
 CURLOPT_SSLKEYPASSWD            7.9.3         7.17.0
 CURLOPT_SSLKEYTYPE              7.9.3
 CURLOPT_SSLKEYTYPE              7.9.3
 CURLOPT_SSLVERSION              7.1
 CURLOPT_SSLVERSION              7.1

+ 8 - 0
include/curl/curl.h

@@ -950,6 +950,7 @@ typedef enum {
 #define CURLOPTTYPE_OBJECTPOINT   10000
 #define CURLOPTTYPE_OBJECTPOINT   10000
 #define CURLOPTTYPE_FUNCTIONPOINT 20000
 #define CURLOPTTYPE_FUNCTIONPOINT 20000
 #define CURLOPTTYPE_OFF_T         30000
 #define CURLOPTTYPE_OFF_T         30000
+#define CURLOPTTYPE_BLOB          40000
 
 
 /* *STRINGPOINT is an alias for OBJECTPOINT to allow tools to extract the
 /* *STRINGPOINT is an alias for OBJECTPOINT to allow tools to extract the
    string options from the header file */
    string options from the header file */
@@ -1959,6 +1960,13 @@ typedef enum {
   /* allow RCPT TO command to fail for some recipients */
   /* allow RCPT TO command to fail for some recipients */
   CURLOPT(CURLOPT_MAIL_RCPT_ALLLOWFAILS, CURLOPTTYPE_LONG, 290),
   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 */
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 } 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
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
  * you should have received as part of this distribution. The terms
@@ -25,6 +25,17 @@
 extern "C" {
 extern "C" {
 #endif
 #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 CURL *curl_easy_init(void);
 CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...);
 CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...);
 CURL_EXTERN CURLcode curl_easy_perform(CURL *curl);
 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;
   CURLcode result = CURLE_OK;
   enum dupstring i;
   enum dupstring i;
+  enum dupblob j;
 
 
   /* Copy src->set into dst->set first, then deal with the strings
   /* Copy src->set into dst->set first, then deal with the strings
      afterwards */
      afterwards */
@@ -780,6 +781,16 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src)
       return result;
       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 */
   /* duplicate memory areas pointed to */
   i = STRING_COPYPOSTFIELDS;
   i = STRING_COPYPOSTFIELDS;
   if(src->set.postfieldsize && src->set.str[i]) {
   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;
   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)
 static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp)
 {
 {
   CURLcode result = CURLE_OK;
   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],
     result = Curl_setstropt(&data->set.str[STRING_CERT_ORIG],
                             va_arg(param, char *));
                             va_arg(param, char *));
     break;
     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
 #ifndef CURL_DISABLE_PROXY
   case CURLOPT_PROXY_SSLCERT:
   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],
     result = Curl_setstropt(&data->set.str[STRING_CERT_PROXY],
                             va_arg(param, char *));
                             va_arg(param, char *));
     break;
     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
 #endif
   case CURLOPT_SSLCERTTYPE:
   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],
     result = Curl_setstropt(&data->set.str[STRING_KEY_ORIG],
                             va_arg(param, char *));
                             va_arg(param, char *));
     break;
     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
 #ifndef CURL_DISABLE_PROXY
   case CURLOPT_PROXY_SSLKEY:
   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],
     result = Curl_setstropt(&data->set.str[STRING_KEY_PROXY],
                             va_arg(param, char *));
                             va_arg(param, char *));
     break;
     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
 #endif
   case CURLOPT_SSLKEYTYPE:
   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],
     result = Curl_setstropt(&data->set.str[STRING_SSL_ISSUERCERT_ORIG],
                             va_arg(param, char *));
                             va_arg(param, char *));
     break;
     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
 #ifndef CURL_DISABLE_TELNET
   case CURLOPT_TELNETOPTIONS:
   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
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
  * 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_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);
 CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list arg);
 
 
 #endif /* HEADER_CURL_SETOPT_H */
 #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. */
   /* Free all dynamic strings stored in the data->set substructure. */
   enum dupstring i;
   enum dupstring i;
+  enum dupblob j;
+
   for(i = (enum dupstring)0; i < STRING_LAST; i++) {
   for(i = (enum dupstring)0; i < STRING_LAST; i++) {
     Curl_safefree(data->set.str[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) {
   if(data->change.referer_alloc) {
     Curl_safefree(data->change.referer);
     Curl_safefree(data->change.referer);
     data->change.referer_alloc = FALSE;
     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];
   data->set.proxy_ssl.password = data->set.str[STRING_TLSAUTH_PASSWORD_PROXY];
 #endif
 #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,
   if(!Curl_clone_primary_ssl_config(&data->set.ssl.primary,
      &conn->ssl_config)) {
      &conn->ssl_config)) {
     result = CURLE_OUT_OF_MEMORY;
     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 */
   long certverifyresult; /* result from the certificate verification */
   char *CRLfile;   /* CRL to check certificate revocation */
   char *CRLfile;   /* CRL to check certificate revocation */
   char *issuercert;/* optional issuer certificate filename */
   char *issuercert;/* optional issuer certificate filename */
+  struct curl_blob *issuercert_blob;
   curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */
   curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */
   void *fsslctxp;        /* parameter for call back */
   void *fsslctxp;        /* parameter for call back */
   char *cert; /* client certificate file name */
   char *cert; /* client certificate file name */
+  struct curl_blob *cert_blob;
   char *cert_type; /* format for certificate (default: PEM)*/
   char *cert_type; /* format for certificate (default: PEM)*/
   char *key; /* private key file name */
   char *key; /* private key file name */
+  struct curl_blob *key_blob;
   char *key_type; /* format for private key (default: PEM) */
   char *key_type; /* format for private key (default: PEM) */
   char *key_passwd; /* plain text private key password */
   char *key_passwd; /* plain text private key password */
 #ifdef USE_TLS_SRP
 #ifdef USE_TLS_SRP
@@ -1580,6 +1583,15 @@ enum dupstring {
   STRING_LAST /* not used, just an end-of-list marker */
   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
 /* callback that gets called when this easy handle is completed within a multi
    handle.  Only used for internally created transfers, like for example
    handle.  Only used for internally created transfers, like for example
    DoH. */
    DoH. */
@@ -1711,6 +1723,7 @@ struct UserDefined {
   long new_directory_perms; /* Permissions to use when creating remote dirs */
   long new_directory_perms; /* Permissions to use when creating remote dirs */
   long ssh_auth_types;   /* allowed SSH auth types */
   long ssh_auth_types;   /* allowed SSH auth types */
   char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */
   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 */
   unsigned int scope_id;  /* Scope id for IPv6 */
   long allowed_protocols;
   long allowed_protocols;
   long redir_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,
 static CURLcode Curl_ossl_set_engine(struct Curl_easy *data,
                                      const char *engine);
                                      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
 static
 int cert_stuff(struct connectdata *conn,
 int cert_stuff(struct connectdata *conn,
                SSL_CTX* ctx,
                SSL_CTX* ctx,
                char *cert_file,
                char *cert_file,
+               BIO *cert_bio,
                const char *cert_type,
                const char *cert_type,
                char *key_file,
                char *key_file,
+               BIO* key_bio,
                const char *key_type,
                const char *key_type,
                char *key_passwd)
                char *key_passwd)
 {
 {
@@ -636,10 +760,11 @@ int cert_stuff(struct connectdata *conn,
 
 
   int file_type = do_file_type(cert_type);
   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;
     SSL *ssl;
     X509 *x509;
     X509 *x509;
     int cert_done = 0;
     int cert_done = 0;
+    int cert_use_result;
 
 
     if(key_passwd) {
     if(key_passwd) {
       /* set the password in the callback userdata */
       /* set the password in the callback userdata */
@@ -652,8 +777,10 @@ int cert_stuff(struct connectdata *conn,
     switch(file_type) {
     switch(file_type) {
     case SSL_FILETYPE_PEM:
     case SSL_FILETYPE_PEM:
       /* SSL_CTX_use_certificate_chain_file() only works on PEM files */
       /* 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,
         failf(data,
               "could not load PEM client certificate, " OSSL_PACKAGE
               "could not load PEM client certificate, " OSSL_PACKAGE
               " error %s, "
               " error %s, "
@@ -668,9 +795,12 @@ int cert_stuff(struct connectdata *conn,
       /* SSL_CTX_use_certificate_file() works with either PEM or ASN1, but
       /* 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
          we use the case above for PEM so this can only be performed with
          ASN1 files. */
          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,
         failf(data,
               "could not load ASN1 client certificate, " OSSL_PACKAGE
               "could not load ASN1 client certificate, " OSSL_PACKAGE
               " error %s, "
               " error %s, "
@@ -750,27 +880,31 @@ int cert_stuff(struct connectdata *conn,
       PKCS12 *p12 = NULL;
       PKCS12 *p12 = NULL;
       EVP_PKEY *pri;
       EVP_PKEY *pri;
       STACK_OF(X509) *ca = NULL;
       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);
         BIO_free(fp);
-        return 0;
-      }
-      p12 = d2i_PKCS12_bio(fp, NULL);
-      BIO_free(fp);
 
 
       if(!p12) {
       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;
         return 0;
       }
       }
 
 
@@ -851,8 +985,10 @@ int cert_stuff(struct connectdata *conn,
       return 0;
       return 0;
     }
     }
 
 
-    if(!key_file)
+    if((!key_file) && (!key_bio)) {
       key_file = cert_file;
       key_file = cert_file;
+      key_bio = cert_bio;
+    }
     else
     else
       file_type = do_file_type(key_type);
       file_type = do_file_type(key_type);
 
 
@@ -862,9 +998,12 @@ int cert_stuff(struct connectdata *conn,
         break;
         break;
       /* FALLTHROUGH */
       /* FALLTHROUGH */
     case SSL_FILETYPE_ASN1:
     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",
         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;
         return 0;
       }
       }
       break;
       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);
   const enum CURL_TLSAUTH ssl_authtype = SSL_SET_OPTION(authtype);
 #endif
 #endif
   char * const ssl_cert = SSL_SET_OPTION(cert);
   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_cert_type = SSL_SET_OPTION(cert_type);
   const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile);
   const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile);
   const char * const ssl_capath = SSL_CONN_CONFIG(CApath);
   const char * const ssl_capath = SSL_CONN_CONFIG(CApath);
@@ -2662,10 +2802,33 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
   }
   }
 #endif
 #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() */
       /* failf() is already done in cert_stuff() */
       return CURLE_SSL_CERTPROBLEM;
       return CURLE_SSL_CERTPROBLEM;
     }
     }
@@ -3714,27 +3877,32 @@ static CURLcode servercert(struct connectdata *conn,
        deallocating the certificate. */
        deallocating the certificate. */
 
 
     /* e.g. match issuer name with provided issuer 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);
       issuer = PEM_read_bio_X509(fp, NULL, ZERO_NULL, NULL);

+ 86 - 57
lib/vtls/schannel.c

@@ -39,6 +39,7 @@
 
 
 #include "schannel.h"
 #include "schannel.h"
 #include "vtls.h"
 #include "vtls.h"
+#include "strcase.h"
 #include "sendf.h"
 #include "sendf.h"
 #include "connect.h" /* for the connect timeout */
 #include "connect.h" /* for the connect timeout */
 #include "strerror.h"
 #include "strerror.h"
@@ -583,94 +584,122 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
 
 
 #ifdef HAS_CLIENT_CERT_PATH
 #ifdef HAS_CLIENT_CERT_PATH
     /* client certificate */
     /* 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_store_path = NULL;
-      TCHAR *cert_thumbprint_str;
+      TCHAR *cert_thumbprint_str = NULL;
       CRYPT_HASH_BLOB cert_thumbprint;
       CRYPT_HASH_BLOB cert_thumbprint;
       BYTE cert_thumbprint_data[CERT_THUMBPRINT_DATA_LEN];
       BYTE cert_thumbprint_data[CERT_THUMBPRINT_DATA_LEN];
-      HCERTSTORE cert_store;
+      HCERTSTORE cert_store = NULL;
       FILE *fInCert = 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);
         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
         /* 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;
         CRYPT_DATA_BLOB datablob;
         WCHAR* pszPassword;
         WCHAR* pszPassword;
         size_t pwd_len = 0;
         size_t pwd_len = 0;
         int str_w_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);
         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);
                 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 */
         /* Convert key-pair data to the in-memory certificate store */
         datablob.pbData = (BYTE*)certdata;
         datablob.pbData = (BYTE*)certdata;
-        datablob.cbData = (DWORD)filesize;
+        datablob.cbData = (DWORD)certsize;
 
 
         if(data->set.ssl.key_passwd != NULL)
         if(data->set.ssl.key_passwd != NULL)
           pwd_len = strlen(data->set.ssl.key_passwd);
           pwd_len = strlen(data->set.ssl.key_passwd);
         pszPassword = (WCHAR*)malloc(sizeof(WCHAR)*(pwd_len + 1));
         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) {
         if(cert_store == NULL) {
           DWORD errorcode = GetLastError();
           DWORD errorcode = GetLastError();
           if(errorcode == ERROR_INVALID_PASSWORD)
           if(errorcode == ERROR_INVALID_PASSWORD)
             failf(data, "schannel: Failed to import cert file %s, "
             failf(data, "schannel: Failed to import cert file %s, "
-                  "password is bad", data->set.ssl.cert);
+                  "password is bad",
+                  cert_showfilename_error);
           else
           else
             failf(data, "schannel: Failed to import cert file %s, "
             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;
           return CURLE_SSL_CERTPROBLEM;
         }
         }
 
 
@@ -681,7 +710,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
         if(client_certs[0] == NULL) {
         if(client_certs[0] == NULL) {
           failf(data, "schannel: Failed to get certificate from file %s"
           failf(data, "schannel: Failed to get certificate from file %s"
                 ", last error is 0x%x",
                 ", last error is 0x%x",
-                data->set.ssl.cert, GetLastError());
+                cert_showfilename_error, GetLastError());
           CertCloseStore(cert_store, 0);
           CertCloseStore(cert_store, 0);
           return CURLE_SSL_CERTPROBLEM;
           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,
 static OSStatus CopyIdentityFromPKCS12File(const char *cPath,
+                                           const struct curl_blob *blob,
                                            const char *cPassword,
                                            const char *cPassword,
                                            SecIdentityRef *out_cert_and_key)
                                            SecIdentityRef *out_cert_and_key)
 {
 {
   OSStatus status = errSecItemNotFound;
   OSStatus status = errSecItemNotFound;
-  CFURLRef pkcs_url = CFURLCreateFromFileSystemRepresentation(NULL,
-    (const UInt8 *)cPath, strlen(cPath), false);
+  CFURLRef pkcs_url = NULL;
   CFStringRef password = cPassword ? CFStringCreateWithCString(NULL,
   CFStringRef password = cPassword ? CFStringCreateWithCString(NULL,
     cPassword, kCFStringEncodingUTF8) : NULL;
     cPassword, kCFStringEncodingUTF8) : NULL;
   CFDataRef pkcs_data = 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
   /* These constants are documented as having first appeared in 10.6 but they
      raise linker errors when used on that cat for some reason. */
      raise linker errors when used on that cat for some reason. */
 #if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS
 #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;
     CFArrayRef items = NULL;
 
 
   /* On iOS SecPKCS12Import will never add the client certificate to the
   /* 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 */
 #endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */
   if(password)
   if(password)
     CFRelease(password);
     CFRelease(password);
-  CFRelease(pkcs_url);
+  if(pkcs_url)
+    CFRelease(pkcs_url);
   return status;
   return status;
 }
 }
 
 
@@ -1376,8 +1395,10 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn,
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
   struct ssl_backend_data *backend = connssl->backend;
   struct ssl_backend_data *backend = connssl->backend;
   const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile);
   const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile);
+  const struct curl_blob *ssl_cablob = NULL;
   const bool verifypeer = SSL_CONN_CONFIG(verifypeer);
   const bool verifypeer = SSL_CONN_CONFIG(verifypeer);
   char * const ssl_cert = SSL_SET_OPTION(cert);
   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 :
   const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
     conn->host.name;
     conn->host.name;
   const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port;
   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");
           "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;
     SecIdentityRef cert_and_key = NULL;
-    bool is_cert_file = is_file(ssl_cert);
 
 
     /* User wants to authenticate with a client cert. Look for it:
     /* 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.
        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
        Otherwise, assume that the user wants to use an identity loaded
        from the Keychain. */
        from the Keychain. */
-    if(is_cert_file) {
+    if(is_cert_file || is_cert_data) {
       if(!SSL_SET_OPTION(cert_type))
       if(!SSL_SET_OPTION(cert_type))
         infof(data, "WARNING: SSL: Certificate type not set, assuming "
         infof(data, "WARNING: SSL: Certificate type not set, assuming "
                     "PKCS#12 format.\n");
                     "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 "
         infof(data, "WARNING: SSL: The Security framework only supports "
                     "loading identities that are in PKCS#12 format.\n");
                     "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);
         SSL_SET_OPTION(key_passwd), &cert_and_key);
     }
     }
     else
     else
@@ -1669,27 +1691,30 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn,
       CFRelease(cert_and_key);
       CFRelease(cert_and_key);
     }
     }
     else {
     else {
+      const char *cert_showfilename_error =
+        is_cert_data ? "(memory blob)" : ssl_cert;
+
       switch(err) {
       switch(err) {
       case errSecAuthFailed: case -25264: /* errSecPkcs12VerifyFailure */
       case errSecAuthFailed: case -25264: /* errSecPkcs12VerifyFailure */
         failf(data, "SSL: Incorrect password for the certificate \"%s\" "
         failf(data, "SSL: Incorrect password for the certificate \"%s\" "
-                    "and its private key.", ssl_cert);
+                    "and its private key.", cert_showfilename_error);
         break;
         break;
       case -26275: /* errSecDecode */ case -25257: /* errSecUnknownFormat */
       case -26275: /* errSecDecode */ case -25257: /* errSecUnknownFormat */
         failf(data, "SSL: Couldn't make sense of the data in the "
         failf(data, "SSL: Couldn't make sense of the data in the "
                     "certificate \"%s\" and its private key.",
                     "certificate \"%s\" and its private key.",
-                    ssl_cert);
+                    cert_showfilename_error);
         break;
         break;
       case -25260: /* errSecPassphraseRequired */
       case -25260: /* errSecPassphraseRequired */
         failf(data, "SSL The certificate \"%s\" requires a password.",
         failf(data, "SSL The certificate \"%s\" requires a password.",
-                    ssl_cert);
+                    cert_showfilename_error);
         break;
         break;
       case errSecItemNotFound:
       case errSecItemNotFound:
         failf(data, "SSL: Can't find the certificate \"%s\" and its private "
         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;
         break;
       default:
       default:
         failf(data, "SSL: Can't load the certificate \"%s\" and its private "
         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;
         break;
       }
       }
       return CURLE_SSL_CERTPROBLEM;
       return CURLE_SSL_CERTPROBLEM;
@@ -1721,7 +1746,8 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn,
 #else
 #else
   if(SSLSetSessionOption != NULL) {
   if(SSLSetSessionOption != NULL) {
 #endif /* CURL_BUILD_MAC */
 #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,
     err = SSLSetSessionOption(backend->ssl_ctx,
                               kSSLSessionOptionBreakOnServerAuth,
                               kSSLSessionOptionBreakOnServerAuth,
                               break_on_auth);
                               break_on_auth);
@@ -1749,10 +1775,11 @@ static CURLcode sectransp_connect_step1(struct connectdata *conn,
   }
   }
 #endif /* CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS */
 #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);
       failf(data, "SSL: can't load CA certificate file %s", ssl_cafile);
       return CURLE_SSL_CACERT_BADFILE;
       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_SSLCERT, config->cert);
           my_setopt_str(curl, CURLOPT_PROXY_SSLCERT, config->proxy_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_SSLCERTTYPE, config->cert_type);
           my_setopt_str(curl, CURLOPT_PROXY_SSLCERTTYPE,
           my_setopt_str(curl, CURLOPT_PROXY_SSLCERTTYPE,
                         config->proxy_cert_type);
                         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_SSLKEY, config->key);
           my_setopt_str(curl, CURLOPT_PROXY_SSLKEY, config->proxy_key);
           my_setopt_str(curl, CURLOPT_PROXY_SSLKEY, config->proxy_key);
           my_setopt_str(curl, CURLOPT_SSLKEYTYPE, config->key_type);
           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);
     ret = curl_easy_setopt(curl, tag, pval);
 
 
   }
   }
-  else {
+  else if(tag < CURLOPTTYPE_BLOB) {
     /* Value is expected to be curl_off_t */
     /* Value is expected to be curl_off_t */
     curl_off_t oval = va_arg(arg, curl_off_t);
     curl_off_t oval = va_arg(arg, curl_off_t);
     msnprintf(buf, sizeof(buf),
     msnprintf(buf, sizeof(buf),
@@ -694,6 +694,20 @@ CURLcode tool_setopt(CURL *curl, bool str, struct GlobalConfig *config,
     if(!oval)
     if(!oval)
       skip = TRUE;
       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);
   va_end(arg);