From af6c394785d9ca41ec34ab26a9308b8a4d2a1260 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 17 Dec 2001 23:01:39 +0000 Subject: [PATCH] =?UTF-8?q?G=F6tz=20Babin-Ebell's=20OpenSSL=20ENGINE=20pat?= =?UTF-8?q?ch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES | 38 ++++++++++++- configure.in | 4 ++ docs/THANKS | 1 + include/curl/curl.h | 27 ++++++++- lib/memdebug.h | 8 +++ lib/setup.h | 7 ++- lib/ssluse.c | 133 +++++++++++++++++++++++++++++++++++++++++--- lib/url.c | 70 ++++++++++++++++++++++- lib/urldata.h | 17 +++++- src/main.c | 91 +++++++++++++++++++++--------- src/setup.h | 4 +- 11 files changed, 353 insertions(+), 47 deletions(-) diff --git a/CHANGES b/CHANGES index b98076d4c..6199b8955 100644 --- a/CHANGES +++ b/CHANGES @@ -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 diff --git a/configure.in b/configure.in index 42777b136..d3d9d5b5f 100644 --- a/configure.in +++ b/configure.in @@ -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 diff --git a/docs/THANKS b/docs/THANKS index 04e5f9f1a..f2e84fc57 100644 --- a/docs/THANKS +++ b/docs/THANKS @@ -78,3 +78,4 @@ that have contributed with non-trivial parts: - John Lask - Eric Lavigne - Marcus Webster + - Götz Babin-Ebell diff --git a/include/curl/curl.h b/include/curl/curl.h index 6ce918e53..11574175d 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -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; diff --git a/lib/memdebug.h b/lib/memdebug.h index ebb240928..3c5c090f6 100644 --- a/lib/memdebug.h +++ b/lib/memdebug.h @@ -1,6 +1,14 @@ #ifdef MALLOCDEBUG +#include "setup.h" + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_SYS_SOCKET_H #include +#endif #include #ifdef HAVE_MEMORY_H #include diff --git a/lib/setup.h b/lib/setup.h index bb9fc7355..cdec0f9d6 100644 --- a/lib/setup.h +++ b/lib/setup.h @@ -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 diff --git a/lib/ssluse.c b/lib/ssluse.c index df891e2db..55b0a2e09 100644 --- a/lib/ssluse.c +++ b/lib/ssluse.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2000, Daniel Stenberg, , et al. + * Copyright (C) 2001, Daniel Stenberg, , 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 and Sampo Kellomaki */ #include "setup.h" + #include #include @@ -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; } diff --git a/lib/url.c b/lib/url.c index 3b5482c2b..808f9c0be 100644 --- a/lib/url.c +++ b/lib/url.c @@ -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: /* diff --git a/lib/urldata.h b/lib/urldata.h index 3a7509dcd..23aaac379 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -58,6 +58,9 @@ #include "openssl/pem.h" #include "openssl/ssl.h" #include "openssl/err.h" +#ifdef HAVE_OPENSSL_ENGINE_H +#include +#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" diff --git a/src/main.c b/src/main.c index e4bd9efa0..54ad587e1 100644 --- a/src/main.c +++ b/src/main.c @@ -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 EGD socket path for random data (SSL)\n" " -e/--referer Referer page (H)"); puts(" -E/--cert Specifies your certificate file and password (HTTPS)\n" + " --cert-type Specifies your certificate file type (DER/PEM/ENG) (HTTPS)\n" + " --key Specifies your private key file (HTTPS)\n" + " --key-type Specifies your private key file type (DER/PEM/ENG) (HTTPS)\n" + " --pass Specifies your passphrase for the private key (HTTPS)"); + puts(" --engine Specifies the crypto engine to use (HTTPS)\n" " --cacert CA certifciate to verify peer against (SSL)\n" " --ciphers What SSL ciphers to use (SSL)\n" " --connect-timeout 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); diff --git a/src/setup.h b/src/setup.h index cfb8efff3..50537d071 100644 --- a/src/setup.h +++ b/src/setup.h @@ -1,5 +1,5 @@ -#ifndef __SETUP_H -#define __SETUP_H +#ifndef __CLIENT_SETUP_H +#define __CLIENT_SETUP_H /***************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| |