Skip to content

Commit

Permalink
Support of pkcs12 certificate in memory with libcurl setopt
Browse files Browse the repository at this point in the history
  • Loading branch information
gvollant committed May 8, 2020
1 parent 1fa3733 commit 7a454db
Show file tree
Hide file tree
Showing 17 changed files with 493 additions and 93 deletions.
2 changes: 2 additions & 0 deletions docs/libcurl/curl_easy_setopt.3
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,8 @@ 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_SSLCERTTYPE
Expand Down
10 changes: 6 additions & 4 deletions docs/libcurl/opts/CURLOPT_SSLCERT.3
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
66 changes: 66 additions & 0 deletions docs/libcurl/opts/CURLOPT_SSLCERT_BLOB.3
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * 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 3 "17 Jun 2014" "libcurl 7.37.0" "curl_easy_setopt options"
.SH NAME
CURLOPT_SSLCERT \- set SSL client certificate
.SH SYNOPSIS
#include <curl/curl.h>

CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SSLCERT, char *cert);
.SH DESCRIPTION
Pass a pointer to a blob structure, which contain information (pointer and
size)about a memory block with binary data of certificate. The default format
must be "P12", and can be used with OpenSSL, Secure Transport or Schannel.
The string "P12" must be specified with \fICURLOPT_SSLCERTTYPE(3)\fP.

If the blob is initialized with curl_init_blob_dup, the application does not
have to keep the buffer around after setting this.
If the blob is initialized with curl_init_blob_persist, the application must
keep the buffer around after setting this.

option.
.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;
curl_init_blob_dup((&stblob), ((char*)certificateData), certificateSize);
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
If built TLS enabled.
.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), "
1 change: 1 addition & 0 deletions docs/libcurl/opts/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ 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 \
Expand Down
2 changes: 2 additions & 0 deletions docs/libcurl/symbols-in-versions
Original file line number Diff line number Diff line change
Expand Up @@ -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.69.0
CURLOPTTYPE_FUNCTIONPOINT 7.1
CURLOPTTYPE_LONG 7.1
CURLOPTTYPE_OBJECTPOINT 7.1
Expand Down Expand Up @@ -593,6 +594,7 @@ CURLOPT_SSH_PUBLIC_KEYFILE 7.16.1
CURLOPT_SSLCERT 7.1
CURLOPT_SSLCERTPASSWD 7.1.1 7.17.0
CURLOPT_SSLCERTTYPE 7.9.3
CURLOPT_SSLCERT_BLOB 7.69.0
CURLOPT_SSLENGINE 7.9.3
CURLOPT_SSLENGINE_DEFAULT 7.9.3
CURLOPT_SSLKEY 7.9.3
Expand Down
4 changes: 4 additions & 0 deletions include/curl/curl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -1959,6 +1960,9 @@ 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, 290),

CURLOPT_LASTENTRY /* the last unused */
} CURLoption;

Expand Down
92 changes: 92 additions & 0 deletions include/curl/easy.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,98 @@
extern "C" {
#endif

/* Application can use a memory PKCS12 certificate with CURLOPT_SSLCERT
* with OpenSSL, Schannel or SecTransport
*
*
* Example if with certificate binary data and size in:
* void* certdata;
* size_t certsize;
* ...
* set the certificate with:
* struct curl_blob structblob;
* curl_init_blob_dup(&structblob, certdata, certsize);
* curl_easy_setopt(curl, CURLOPT_KEYPASSWD, &structblob);
* my_setopt_str(curl, CURLOPT_SSLCERT, &structblob);
* struct curl_blob is just a 0x20 bytes structures which will
* start bt a magic string and contain data
*
* with curl_init_blob_dup, certdata can be discarded after
* calling curl_easy_setopt
* with curl_init_blob_persist, certdata need be valid when the
* connexion is made
*
* Note : never call curl_init_blob_xxx without a pointer to a
* valid 0x20 bytes block
* And never use the string CURL_BLOB_MAGIC without a correct
* blob block
*
*
* about the idea, see https://curl.haxx.se/mail/lib-2016-09/0074.html */

#define CURL_BLOB_MAGIC ("\x01" "CurlMemBlob" "\xff\x01")
#define CURL_BLOB_MAGIC_STRLEN (14)
#define CURL_BLOB_MAGIC_SIZE (CURL_BLOB_MAGIC_STRLEN + 1)
#define CURL_BLOB_OFFSET_DUPFLAG (CURL_BLOB_MAGIC_SIZE)
#define CURL_BLOB_SIZE_DUPFLAG (1)
#define CURL_BLOB_OFFSET_DATALEN (CURL_BLOB_OFFSET_DUPFLAG + \
CURL_BLOB_SIZE_DUPFLAG)
#define CURL_BLOB_SIZE_DATALEN (sizeof(size_t))
#define CURL_BLOB_OFFSET_DATAPTR (CURL_BLOB_OFFSET_DATALEN + \
CURL_BLOB_SIZE_DATALEN)
#define CURL_BLOB_SIZE_DATAPTR (sizeof(void *))
#define CURL_BLOB_SIZE (CURL_BLOB_OFFSET_DATAPTR + \
CURL_BLOB_SIZE_DATAPTR)

#define CURL_BLOB_DUPFLAG_COPY (1)
#define CURL_BLOB_DUPFLAG_NOCOPY (0)

/* the struct curl_blob store binary data parameters
* the structure size is 0x20 and can be followed by binary data */
struct curl_blob {
char blob_internal[CURL_BLOB_SIZE];
};


/*
* Macro WHILE_FALSE may be used to build single-iteration do-while loops,
* avoiding compiler warnings. Mostly intended for other macro definitions.
*/

#define WHILE_FALSE_EASYCURL while(0)

#if defined(_MSC_VER) && !defined(__POCC__)
# undef WHILE_FALSE_EASYCURL
# if (_MSC_VER < 1500)
# define WHILE_FALSE_EASYCURL while(1, 0)
# else
# define WHILE_FALSE_EASYCURL \
__pragma(warning(push)) \
__pragma(warning(disable:4127)) \
while(0) \
__pragma(warning(pop))
# endif
#endif

#define curl_init_blob_flag(structblob, ptr, len, flag) \
do { \
char *blob = (structblob)->blob_internal; \
char *data = (char *)(ptr); \
size_t size = (size_t)(len); \
memcpy(blob, CURL_BLOB_MAGIC, CURL_BLOB_MAGIC_SIZE); \
memcpy(blob + CURL_BLOB_OFFSET_DATALEN, &size, sizeof(size_t)); \
memcpy(blob + CURL_BLOB_OFFSET_DATAPTR, &data, sizeof(void *)); \
blob[CURL_BLOB_OFFSET_DUPFLAG] = (char)(flag); \
} WHILE_FALSE_EASYCURL

#define curl_init_blob_persist(structblob, ptr, len) \
curl_init_blob_flag((structblob), (ptr), (len), CURL_BLOB_DUPFLAG_NOCOPY)

#define curl_init_blob_dup(structblob, ptr, len) \
curl_init_blob_flag((structblob), (ptr), (len), CURL_BLOB_DUPFLAG_COPY)



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);
Expand Down
27 changes: 27 additions & 0 deletions lib/easy.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,30 @@
#include "curl_memory.h"
#include "memdebug.h"


/* curl_decode_data_blob is used by curl library to
* check if a curl parameter is a memory blob instead filename
* it return 1 and fill len and data if this is memory blob
* and return 0 if this is a filename (or another string) */
bool curl_decode_data_blob(char *str, size_t *len, void **data)
{
const char *magicblobstore = CURL_BLOB_MAGIC;

size_t walk;
for(walk = 0; walk < CURL_BLOB_MAGIC_SIZE; walk++)
if(str[walk] != magicblobstore[walk])
return FALSE;

/* The string is matches exacly CURL_BLOB_MAGIC
* This mean str is really a pointer to a 0x20 bytes structure
* with certificate data pointer and size */
memcpy(len, str + CURL_BLOB_OFFSET_DATALEN, sizeof(*len));
memcpy(data, str + CURL_BLOB_OFFSET_DATAPTR, sizeof(*data));
return TRUE;
}

void Curl_version_init(void);

/* true globals -- for curl_global_init() and curl_global_cleanup() */
static unsigned int initialized;
static long init_flags;
Expand Down Expand Up @@ -775,6 +799,9 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src)
/* duplicate all strings */
for(i = (enum dupstring)0; i< STRING_LASTZEROTERMINATED; i++) {
result = Curl_setstropt(&dst->set.str[i], src->set.str[i]);
/* Curl_setstropt return CURLE_BAD_FUNCTION_ARGUMENT with blob */
if(result == CURLE_BAD_FUNCTION_ARGUMENT)
result = Curl_setblobopt(&dst->set.str[i], src->set.str[i]);
if(result)
return result;
}
Expand Down
60 changes: 60 additions & 0 deletions lib/setopt.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ CURLcode Curl_setstropt(char **charp, const char *s)

if(str) {
size_t len = strlen(str);

/* Protect against malicious usage of BLOB magic */
if((len == CURL_BLOB_MAGIC_STRLEN) &&
(!memcmp(s, CURL_BLOB_MAGIC, CURL_BLOB_MAGIC_SIZE)))
return CURLE_BAD_FUNCTION_ARGUMENT;

if(len > CURL_MAX_INPUT_LENGTH) {
free(str);
return CURLE_BAD_FUNCTION_ARGUMENT;
Expand All @@ -77,6 +83,53 @@ CURLcode Curl_setstropt(char **charp, const char *s)
return CURLE_OK;
}

CURLcode Curl_setblobopt(char **charp, const char *sblob)
{
/* Release the previous storage at `charp' and replace by a dynamic storage
copy of `sblob'.
Return CURLE_OK, CURLE_BAD_FUNCTION_ARGUMENT or CURLE_OUT_OF_MEMORY. */

Curl_safefree(*charp);

if(sblob) {
size_t len = strlen(sblob);
if((len == CURL_BLOB_MAGIC_STRLEN) &&
(!memcmp(sblob, CURL_BLOB_MAGIC, CURL_BLOB_MAGIC_SIZE))) {
/* sblob contain struct curl_blob data, created with
* curl_init_blob_*. This is not ordinary string but a
* blob memory block */
size_t datalen;
char *srcdata;
char *dstdata;
char *dblob;
char dupflag = sblob[CURL_BLOB_OFFSET_DUPFLAG];
memcpy(&datalen, sblob + CURL_BLOB_OFFSET_DATALEN, sizeof(datalen));
memcpy(&srcdata, sblob + CURL_BLOB_OFFSET_DATAPTR, sizeof(srcdata));
dblob = (char *)malloc(CURL_BLOB_SIZE + (dupflag ? datalen : 0));
if(!dblob)
return CURLE_OUT_OF_MEMORY;
memcpy(dblob, CURL_BLOB_MAGIC, CURL_BLOB_MAGIC_SIZE);
dblob[CURL_BLOB_OFFSET_DUPFLAG] = dupflag;
memcpy(dblob + CURL_BLOB_OFFSET_DATALEN, &datalen, sizeof(datalen));

if(dupflag) {
/* dupflag is set by curl_init_blob_dup. We need copy the data,
* app don't need keep data pointer valid after setopt */
dstdata = dblob + CURL_BLOB_SIZE;
memcpy(dstdata, srcdata, datalen);
}
else
dstdata = srcdata;
memcpy(dblob + CURL_BLOB_OFFSET_DATAPTR, &dstdata, sizeof(dstdata));
*charp = (char *)dblob;
return CURLE_OK;
}
return CURLE_BAD_FUNCTION_ARGUMENT;
}

return CURLE_OK;
}

static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp)
{
CURLcode result = CURLE_OK;
Expand Down Expand Up @@ -1606,6 +1659,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:
/*
* String that holds file name of the SSL certificate to use
*/
result = Curl_setblobopt(&data->set.str[STRING_CERT_ORIG],
va_arg(param, char *));
break;
#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_SSLCERT:
/*
Expand Down
1 change: 1 addition & 0 deletions lib/setopt.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
***************************************************************************/

CURLcode Curl_setstropt(char **charp, const char *s);
CURLcode Curl_setblobopt(char **charp, const char *sblob);
CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list arg);

#endif /* HEADER_CURL_SETOPT_H */
Loading

0 comments on commit 7a454db

Please sign in to comment.