Gtz Babin-Ebell's OpenSSL ENGINE patch

This commit is contained in:
Daniel Stenberg 2001-12-17 23:01:39 +00:00
parent 558d12d7f6
commit af6c394785
11 changed files with 353 additions and 47 deletions

38
CHANGES
View File

@ -6,10 +6,46 @@
History of Changes
Daniel (17 December 2001)
- Götz Babin-Ebell dove into the dark dungeons of the OpenSSL ENGINE stuff and
made libcurl support it! This allows libcurl to do SSL connections with the
private key stored in external hardware.
To make this good, he had to add a bunch of new library options that'll be
useful to others as well:
CURLOPT_SSLCERTTYPE set SSL cert type (PEM/DER)
CURLOPT_SSLKEY set SSL private key (file)
CURLOPT_SSLKEYTYPE: set SSL key type (PEM/DER/ENG)
CURLOPT_SSLKEYPASSWD: set the passphrase for your private key
(CURLOPT_SSLCERTPASSWD is an alias)
CURLOPT_SSLENGINE: set the name of the crypto engine
(returns CURLE_SSL_ENGINE_NOTFOUND on error)
CURLOPT_SSLENGINE_DEFAULT: set the default engine
There are two new failure codes:
CURLE_SSL_ENGINE_NOTFOUND
CURLE_SSL_ENGINE_SETFAILED
Daniel (14 December 2001)
- We have "branched" the source-tree at a few places. Checkout the CVS sources
with the 'multi-dev' label to get the latest multi interface development
tree. The idea is to only branch affected files and to restrict the branch
to the v8 multi interface development only.
*NOTE* that if we get bug reports and patches etc, we might need to apply
them in both branches!
The multi-dev branch is what we are gonna use as main branch in the future
if it turns out successful. Thus, we must maintain both now in case we need
them. The current main branch will be used if we want to release a 7.9.3 or
perhaps a 7.10 release before version 8. Which is very likely.
- Marcus Webster provided code for the new CURLFORM_CONTENTHEADER option for
curl_formadd(), that lets an application add a set of headers for that
particular part in a multipart/form-post. We need to add
particular part in a multipart/form-post. He also provided a section to the
man page that describes the new option.
Daniel (11 December 2001)
- Ben Greear made me aware of the fact that the Curl_failf() usage internally

View File

@ -392,6 +392,10 @@ else
OPENSSL_ENABLED=1)
fi
dnl Check for the OpenSSL engine header, it is kind of "separated"
dnl from the main SSL check
AC_CHECK_HEADERS(openssl/engine.h)
AC_SUBST(OPENSSL_ENABLED)
fi

View File

@ -78,3 +78,4 @@ that have contributed with non-trivial parts:
- John Lask <johnlask@hotmail.com>
- Eric Lavigne <erlavigne@wanadoo.fr>
- Marcus Webster <marcus.webster@phocis.com>
- Götz Babin-Ebell <babin­ebell@trustcenter.de>

View File

@ -156,7 +156,9 @@ typedef enum {
CURLE_OBSOLETE, /* 50 - removed after 7.7.3 */
CURLE_SSL_PEER_CERTIFICATE, /* 51 - peer's certificate wasn't ok */
CURLE_GOT_NOTHING, /* 52 - when this is a specific error */
CURLE_SSL_ENGINE_NOTFOUND, /* 53 - SSL crypto engine not found */
CURLE_SSL_ENGINE_SETFAILED, /* 54 - can not set SSL crypto engine as default */
CURL_LAST /* never use! */
} CURLcode;
@ -279,8 +281,10 @@ typedef enum {
/* name of the file keeping your private SSL-certificate */
CINIT(SSLCERT, OBJECTPOINT, 25),
/* password for the SSL-certificate */
/* password for the SSL-private key, keep this for compatibility */
CINIT(SSLCERTPASSWD, OBJECTPOINT, 26),
/* password for the SSL private key */
CINIT(SSLKEYPASSWD, OBJECTPOINT, 26),
/* send TYPE parameter? */
CINIT(CRLF, LONG, 27),
@ -466,7 +470,24 @@ typedef enum {
default, that one will always be attempted before the more traditional
PASV command. */
CINIT(FTP_USE_EPSV, LONG, 85),
/* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") */
CINIT(SSLCERTTYPE, OBJECTPOINT, 86),
/* name of the file keeping your private SSL-key */
CINIT(SSLKEY, OBJECTPOINT, 87),
/* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") */
CINIT(SSLKEYTYPE, OBJECTPOINT, 88),
/* crypto engine for the SSL-sub system */
CINIT(SSLENGINE, OBJECTPOINT, 89),
/* set the crypto engine for the SSL-sub system as default
the param has no meaning...
*/
CINIT(SSLENGINE_DEFAULT, LONG, 90),
CURLOPT_LASTENTRY /* the last unusued */
} CURLoption;

View File

@ -1,6 +1,14 @@
#ifdef MALLOCDEBUG
#include "setup.h"
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include <stdio.h>
#ifdef HAVE_MEMORY_H
#include <memory.h>

View File

@ -34,9 +34,9 @@
#ifdef HAVE_CONFIG_H
#ifdef VMS
#include "config-vms.h"
#include "../config-vms.h"
#else
#include "config.h" /* the configure script results */
#include "../config.h" /* the configure script results */
#endif
#else
@ -46,13 +46,14 @@
#endif
#ifdef macintosh
/* hand-modified MacOS config.h! */
#include "config-mac.h"
#include "../config-mac.h"
#endif
#endif
#ifndef __cplusplus /* (rabe) */
typedef char bool;
#define typedef_bool
#endif /* (rabe) */
#ifdef NEED_REENTRANT

View File

@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* In order to be useful for every potential user, curl and libcurl are
* dual-licensed under the MPL and the MIT/X-derivate licenses.
@ -22,11 +22,12 @@
*****************************************************************************/
/*
* The original SSL code was written by
* The original SSL code for curl was written by
* Linas Vepstas <linas@linas.org> and Sampo Kellomaki <sampo@iki.fi>
*/
#include "setup.h"
#include <string.h>
#include <stdlib.h>
@ -171,33 +172,55 @@ int random_the_seed(struct connectdata *conn)
return nread;
}
#ifndef SSL_FILETYPE_ENGINE
#define SSL_FILETYPE_ENGINE 42
#endif
static int do_file_type(const char *type)
{
if (!type || !type[0])
return SSL_FILETYPE_PEM;
if (curl_strequal(type, "PEM"))
return SSL_FILETYPE_PEM;
if (curl_strequal(type, "DER"))
return SSL_FILETYPE_ASN1;
if (curl_strequal(type, "ENG"))
return SSL_FILETYPE_ENGINE;
return -1;
}
static
int cert_stuff(struct connectdata *conn,
char *cert_file,
char *key_file)
const char *cert_type,
char *key_file,
const char *key_type)
{
struct SessionHandle *data = conn->data;
int file_type;
if (cert_file != NULL) {
SSL *ssl;
X509 *x509;
if(data->set.cert_passwd) {
if(data->set.key_passwd) {
#ifndef HAVE_USERDATA_IN_PWD_CALLBACK
/*
* If password has been given, we store that in the global
* area (*shudder*) for a while:
*/
strcpy(global_passwd, data->set.cert_passwd);
strcpy(global_passwd, data->set.key_passwd);
#else
/*
* We set the password in the callback userdata
*/
SSL_CTX_set_default_passwd_cb_userdata(conn->ssl.ctx, data->set.cert_passwd);
SSL_CTX_set_default_passwd_cb_userdata(conn->ssl.ctx,
data->set.key_passwd);
#endif
/* Set passwd callback: */
SSL_CTX_set_default_passwd_cb(conn->ssl.ctx, passwd_callback);
}
#if 0
if (SSL_CTX_use_certificate_file(conn->ssl.ctx,
cert_file,
SSL_FILETYPE_PEM) != 1) {
@ -213,6 +236,83 @@ int cert_stuff(struct connectdata *conn,
failf(data, "unable to set public key file");
return(0);
}
#else
/* The '#ifdef 0' section above was removed on 17-dec-2001 */
file_type = do_file_type(cert_type);
switch(file_type) {
case SSL_FILETYPE_PEM:
case SSL_FILETYPE_ASN1:
if (SSL_CTX_use_certificate_file(conn->ssl.ctx,
cert_file,
file_type) != 1) {
failf(data, "unable to set certificate file (wrong password?)");
return 0;
}
break;
case SSL_FILETYPE_ENGINE:
failf(data, "file type ENG for certificate not implemented");
return 0;
default:
failf(data, "not supported file type '%s' for certificate", cert_type);
return 0;
}
file_type = do_file_type(key_type);
switch(file_type) {
case SSL_FILETYPE_PEM:
if (key_file == NULL)
/* cert & key can only be in PEM case in the same file */
key_file=cert_file;
case SSL_FILETYPE_ASN1:
if (SSL_CTX_use_PrivateKey_file(conn->ssl.ctx,
key_file,
file_type) != 1) {
failf(data, "unable to set private key file\n");
return 0;
}
break;
case SSL_FILETYPE_ENGINE:
#ifdef HAVE_OPENSSL_ENGINE_H
{ /* XXXX still needs some work */
EVP_PKEY *priv_key = NULL;
if (conn && conn->data && conn->data->engine) {
if (!key_file || !key_file[0]) {
failf(data, "no key set to load from crypto engine\n");
return 0;
}
priv_key = ENGINE_load_private_key(conn->data->engine,key_file,
data->set.key_passwd);
if (!priv_key) {
failf(data, "failed to load private key from crypto engine\n");
return 0;
}
if (SSL_CTX_use_PrivateKey(conn->ssl.ctx, priv_key) != 1) {
failf(data, "unable to set private key\n");
EVP_PKEY_free(priv_key);
return 0;
}
EVP_PKEY_free(priv_key); /* we don't need the handle any more... */
}
else {
failf(data, "crypto engine not set, can't load private key\n");
return 0;
}
}
#else
failf(data, "file type ENG for private key not supported\n");
return 0;
#endif
break;
default:
failf(data, "not supported file type for private key\n");
return 0;
}
#endif
ssl=SSL_new(conn->ssl.ctx);
x509=SSL_get_certificate(ssl);
@ -269,6 +369,10 @@ void Curl_SSL_init(void)
init_ssl++; /* never again */
#ifdef HAVE_ENGINE_LOAD_BUILTIN_ENGINES
ENGINE_load_builtin_engines();
#endif
/* Lets get nice error messages */
SSL_load_error_strings();
@ -293,6 +397,10 @@ void Curl_SSL_cleanup(void)
table. */
EVP_cleanup();
#ifdef HAVE_ENGINE_cleanup
ENGINE_cleanup();
#endif
init_ssl=0; /* not inited any more */
}
#else
@ -428,6 +536,13 @@ int Curl_SSL_Close_All(struct SessionHandle *data)
/* free the cache data */
free(data->state.session);
}
#ifdef HAVE_OPENSSL_ENGINE_H
if (data->engine)
{
ENGINE_free(data->engine);
data->engine = NULL;
}
#endif
return 0;
}
@ -569,7 +684,11 @@ Curl_SSLConnect(struct connectdata *conn)
}
if(data->set.cert) {
if (!cert_stuff(conn, data->set.cert, data->set.cert)) {
if (!cert_stuff(conn,
data->set.cert,
data->set.cert_type,
data->set.key,
data->set.key_type)) {
/* failf() is already done in cert_stuff() */
return CURLE_SSL_CONNECT_ERROR;
}

View File

@ -790,11 +790,75 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...)
*/
data->set.cert = va_arg(param, char *);
break;
case CURLOPT_SSLCERTPASSWD:
case CURLOPT_SSLCERTTYPE:
/*
* String that holds the SSL certificate password.
* String that holds file type of the SSL certificate to use
*/
data->set.cert_passwd = va_arg(param, char *);
data->set.cert_type = va_arg(param, char *);
break;
case CURLOPT_SSLKEY:
/*
* String that holds file name of the SSL certificate to use
*/
data->set.key = va_arg(param, char *);
break;
case CURLOPT_SSLKEYTYPE:
/*
* String that holds file type of the SSL certificate to use
*/
data->set.key_type = va_arg(param, char *);
break;
case CURLOPT_SSLKEYPASSWD:
/*
* String that holds the SSL private key password.
*/
data->set.key_passwd = va_arg(param, char *);
break;
case CURLOPT_SSLENGINE:
/*
* String that holds the SSL crypto engine.
*/
#ifdef HAVE_OPENSSL_ENGINE_H
{
const char *cpTemp = va_arg(param, char *);
ENGINE *e;
if (cpTemp && cpTemp[0]) {
e = ENGINE_by_id(cpTemp);
if (e) {
if (data->engine) {
ENGINE_free(data->engine);
}
data->engine = e;
}
else {
failf(data, "SSL Engine '%s' not found", cpTemp);
return CURLE_SSL_ENGINE_NOTFOUND;
}
}
}
#else
return CURLE_SSL_ENGINE_NOTFOUND;
#endif
break;
case CURLOPT_SSLENGINE_DEFAULT:
/*
* flag to set engine as default.
*/
#ifdef HAVE_OPENSSL_ENGINE_H
if (data->engine) {
if (ENGINE_set_default(data->engine, ENGINE_METHOD_ALL) > 0) {
#ifdef DEBUG
fprintf(stderr,"set default crypto engine\n");
#endif
}
else {
#ifdef DEBUG
failf(data, "set default crypto engine failed");
#endif
return CURLE_SSL_ENGINE_SETFAILED;
}
}
#endif
break;
case CURLOPT_CRLF:
/*

View File

@ -58,6 +58,9 @@
#include "openssl/pem.h"
#include "openssl/ssl.h"
#include "openssl/err.h"
#ifdef HAVE_OPENSSL_ENGINE_H
#include <openssl/engine.h>
#endif
#else
#include "rsa.h"
#include "crypto.h"
@ -111,6 +114,9 @@ enum protection_level {
};
#endif
#ifndef HAVE_OPENSSL_ENGINE_H
typedef void ENGINE;
#endif
/* struct for data related to SSL and SSL connections */
struct ssl_connect_data {
bool use; /* use ssl encrypted communications TRUE/FALSE */
@ -525,8 +531,12 @@ struct UserDefined {
char *cookie; /* HTTP cookie string to send */
struct curl_slist *headers; /* linked list of extra headers */
struct HttpPost *httppost; /* linked list of POST data */
char *cert; /* PEM-formatted certificate */
char *cert_passwd; /* plain text certificate password */
char *cert; /* certificate */
char *cert_type; /* format for certificate (default: PEM) */
char *key; /* private key */
char *key_type; /* format for private key (default: PEM) */
char *key_passwd; /* plain text private key password */
char *crypto_engine; /* name of the crypto engine to use */
char *cookiejar; /* dump all cookies to this file */
bool crlf; /* convert crlf on ftp upload(?) */
struct curl_slist *quote; /* before the transfer */
@ -594,6 +604,9 @@ struct SessionHandle {
struct UrlState state; /* struct for fields used for state info and
other dynamic purposes */
struct PureInfo info; /* stats, reports and info data */
#ifdef USE_SSLEAY
ENGINE* engine;
#endif /* USE_SSLEAY */
};
#define LIBCURL_NAME "libcurl"

View File

@ -77,7 +77,9 @@
#define DEFAULT_MAXREDIRS 50L
#ifndef __cplusplus /* (rabe) */
#ifndef typedef_bool
typedef char bool;
#endif
#endif /* (rabe) */
#define CURL_PROGRESS_STATS 0 /* default progress display */
@ -318,6 +320,11 @@ static void help(void)
" --egd-file <file> EGD socket path for random data (SSL)\n"
" -e/--referer Referer page (H)");
puts(" -E/--cert <cert[:passwd]> Specifies your certificate file and password (HTTPS)\n"
" --cert-type <type> Specifies your certificate file type (DER/PEM/ENG) (HTTPS)\n"
" --key <key> Specifies your private key file (HTTPS)\n"
" --key-type <type> Specifies your private key file type (DER/PEM/ENG) (HTTPS)\n"
" --pass <pass> Specifies your passphrase for the private key (HTTPS)");
puts(" --engine <eng> Specifies the crypto engine to use (HTTPS)\n"
" --cacert <file> CA certifciate to verify peer against (SSL)\n"
" --ciphers <list> What SSL ciphers to use (SSL)\n"
" --connect-timeout <seconds> Maximum time allowed for connection\n"
@ -420,8 +427,12 @@ struct Configurable {
char *cipher_list;
char *cert;
char *cert_type;
char *cacert;
char *cert_passwd;
char *key;
char *key_type;
char *key_passwd;
char *engine;
bool crlf;
char *customrequest;
char *krb4level;
@ -884,6 +895,11 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
{"e", "referer", TRUE},
{"E", "cert", TRUE},
{"Ea", "cacert", TRUE},
{"Eb","cert-type", TRUE},
{"Ec","key", TRUE},
{"Ed","key-type", TRUE},
{"Ee","pass", TRUE},
{"Ef","engine", TRUE},
{"f", "fail", FALSE},
{"F", "form", TRUE},
{"g", "globoff", FALSE},
@ -1180,35 +1196,53 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
}
break;
case 'E':
if(subletter == 'a') {
switch(subletter) {
case 'a': /* CA info PEM file */
/* CA info PEM file */
GetStr(&config->cacert, nextarg);
}
else {
char *ptr = strchr(nextarg, ':');
/* Since we live in a world of weirdness and confusion, the win32
dudes can use : when using drive letters and thus
c:\file:password needs to work. In order not to break
compatibility, we still use : as separator, but we try to detect
when it is used for a file name! On windows. */
break;
case 'b': /* cert file type */
GetStr(&config->cert_type, nextarg);
break;
case 'c': /* private key file */
GetStr(&config->key, nextarg);
break;
case 'd': /* private key file type */
GetStr(&config->key_type, nextarg);
break;
case 'e': /* private key passphrase */
GetStr(&config->key_passwd, nextarg);
break;
case 'f': /* crypto engine */
GetStr(&config->engine, nextarg);
break;
default: /* certificate file */
{
char *ptr = strchr(nextarg, ':');
/* Since we live in a world of weirdness and confusion, the win32
dudes can use : when using drive letters and thus
c:\file:password needs to work. In order not to break
compatibility, we still use : as separator, but we try to detect
when it is used for a file name! On windows. */
#ifdef WIN32
if(ptr &&
(ptr == &nextarg[1]) &&
(nextarg[2] == '\\') &&
(isalpha((int)nextarg[0])) )
/* colon in the second column, followed by a backslash, and the
first character is an alphabetic letter:
if(ptr &&
(ptr == &nextarg[1]) &&
(nextarg[2] == '\\') &&
(isalpha((int)nextarg[0])) )
/* colon in the second column, followed by a backslash, and the
first character is an alphabetic letter:
this is a drive letter colon */
ptr = strchr(&nextarg[3], ':'); /* find the next one instead */
this is a drive letter colon */
ptr = strchr(&nextarg[3], ':'); /* find the next one instead */
#endif
if(ptr) {
/* we have a password too */
*ptr=0;
ptr++;
GetStr(&config->cert_passwd, ptr);
}
GetStr(&config->cert, nextarg);
if(ptr) {
/* we have a password too */
*ptr=0;
ptr++;
GetStr(&config->key_passwd, ptr);
}
GetStr(&config->cert, nextarg);
}
}
break;
case 'f':
@ -2214,6 +2248,8 @@ operate(struct Configurable *config, int argc, char *argv[])
}
#endif
curl_easy_setopt(curl, CURLOPT_SSLENGINE, config->engine);
curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1);
curl_easy_setopt(curl, CURLOPT_FILE, (FILE *)&outs); /* where to store */
/* what call to write: */
@ -2261,7 +2297,10 @@ operate(struct Configurable *config, int argc, char *argv[])
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, config->headers);
curl_easy_setopt(curl, CURLOPT_HTTPPOST, config->httppost);
curl_easy_setopt(curl, CURLOPT_SSLCERT, config->cert);
curl_easy_setopt(curl, CURLOPT_SSLCERTPASSWD, config->cert_passwd);
curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, config->cert_type);
curl_easy_setopt(curl, CURLOPT_SSLKEY, config->key);
curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, config->key_type);
curl_easy_setopt(curl, CURLOPT_SSLKEYPASSWD, config->key_passwd);
if(config->cacert) {
curl_easy_setopt(curl, CURLOPT_CAINFO, config->cacert);

View File

@ -1,5 +1,5 @@
#ifndef __SETUP_H
#define __SETUP_H
#ifndef __CLIENT_SETUP_H
#define __CLIENT_SETUP_H
/*****************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |