Newer
Older
/***************************************************************************
Daniel Stenberg
committed
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* Copyright (C) 1998 - 2012, 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 http://curl.haxx.se/docs/copyright.html.
Daniel Stenberg
committed
*
* 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.
***************************************************************************/
Daniel Stenberg
committed
* Source file for all OpenSSL-specific code for the TLS/SSL layer. No code
* but sslgen.c should ever call or use these functions.
Daniel Stenberg
committed
*/
/*
* The original SSLeay-using code for curl was written by Linas Vepstas and
* Sampo Kellomaki 1998.
#include "urldata.h"
#include "sendf.h"
#include "formdata.h" /* for the boundary function */
#include "url.h" /* for the ssl config check function */
#include "inet_pton.h"
#include "ssluse.h"
#include "connect.h"
#include "strequal.h"
#include "select.h"
#include "sslgen.h"
#include "rawstr.h"
#include "hostcheck.h"
Daniel Stenberg
committed
#define _MPRINTF_REPLACE /* use the internal *printf() functions */
#include <curl/mprintf.h>
#ifdef USE_OPENSSL
#include <openssl/rand.h>
#include <openssl/x509v3.h>
Daniel Stenberg
committed
#include <openssl/dsa.h>
#include <openssl/dh.h>
#include <openssl/err.h>
#include <openssl/md5.h>
#else
#include <rand.h>
#include <x509v3.h>
#include "non-ascii.h" /* for Curl_convert_from_utf8 prototype */
/* The last #include file should be: */
#ifndef OPENSSL_VERSION_NUMBER
#error "OPENSSL_VERSION_NUMBER not defined"
#endif
#if OPENSSL_VERSION_NUMBER >= 0x0090581fL
#define HAVE_SSL_GET1_SESSION 1
#else
#undef HAVE_SSL_GET1_SESSION
#endif
#if OPENSSL_VERSION_NUMBER >= 0x00904100L
#define HAVE_USERDATA_IN_PWD_CALLBACK 1
#else
#undef HAVE_USERDATA_IN_PWD_CALLBACK
#endif
#if OPENSSL_VERSION_NUMBER >= 0x00907001L
/* ENGINE_load_private_key() takes four arguments */
#define HAVE_ENGINE_LOAD_FOUR_ARGS
Daniel Stenberg
committed
#include <openssl/ui.h>
#else
/* ENGINE_load_private_key() takes three arguments */
#undef HAVE_ENGINE_LOAD_FOUR_ARGS
#endif
Daniel Stenberg
committed
#if (OPENSSL_VERSION_NUMBER >= 0x00903001L) && defined(HAVE_OPENSSL_PKCS12_H)
/* OpenSSL has PKCS 12 support */
#define HAVE_PKCS12_SUPPORT
#else
/* OpenSSL/SSLEay does not have PKCS12 support */
#undef HAVE_PKCS12_SUPPORT
#endif
#if OPENSSL_VERSION_NUMBER >= 0x00906001L
#define HAVE_ERR_ERROR_STRING_N 1
#endif
#if OPENSSL_VERSION_NUMBER >= 0x00909000L
#define SSL_METHOD_QUAL const
#else
#define SSL_METHOD_QUAL
#endif
#if OPENSSL_VERSION_NUMBER >= 0x00907000L
/* 0.9.6 didn't have X509_STORE_set_flags() */
#define HAVE_X509_STORE_SET_FLAGS 1
#else
#define X509_STORE_set_flags(x,y) Curl_nop_stmt
#endif
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
#define HAVE_ERR_REMOVE_THREAD_STATE 1
#endif
#ifndef HAVE_SSLV2_CLIENT_METHOD
#undef OPENSSL_NO_SSL2 /* undef first to avoid compiler warnings */
#define OPENSSL_NO_SSL2
#endif
/*
* Number of bytes to read from the random number seed file. This must be
* a finite value (because some entropy "files" like /dev/urandom have
* an infinite length), but must be large enough to provide enough
* entopy to properly seed OpenSSL's PRNG.
*/
#define RAND_LOAD_LENGTH 1024
#ifndef HAVE_USERDATA_IN_PWD_CALLBACK
#endif
static int passwd_callback(char *buf, int num, int encrypting
/* This was introduced in 0.9.4, we can set this
using SSL_CTX_set_default_passwd_cb_userdata()
*/
, void *global_passwd
DEBUGASSERT(0 == encrypting);
if(!encrypting) {
int klen = curlx_uztosi(strlen((char *)global_passwd));
if(num > klen) {
memcpy(buf, global_passwd, klen+1);
return klen;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
/*
* rand_enough() is a function that returns TRUE if we have seeded the random
* engine properly. We use some preprocessor magic to provide a seed_enough()
* macro to use, just to prevent a compiler warning on this function if we
* pass in an argument that is never used.
*/
Daniel Stenberg
committed
#ifdef HAVE_RAND_STATUS
#define seed_enough(x) rand_enough()
static bool rand_enough(void)
{
return (0 != RAND_status()) ? TRUE : FALSE;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
#define seed_enough(x) rand_enough(x)
static bool rand_enough(int nread)
{
/* this is a very silly decision to make */
return (nread > 500) ? TRUE : FALSE;
Daniel Stenberg
committed
#endif
static int ossl_seed(struct SessionHandle *data)
Daniel Stenberg
committed
char *buf = data->state.buffer; /* point to the big buffer */
int nread=0;
/* Q: should we add support for a random file name as a libcurl option?
A: Yes, it is here */
#ifndef RANDOM_FILE
/* if RANDOM_FILE isn't defined, we only perform this if an option tells
us to! */
Daniel Stenberg
committed
if(data->set.ssl.random_file)
#define RANDOM_FILE "" /* doesn't matter won't be used */
{
/* let the option override the define */
Daniel Stenberg
committed
nread += RAND_load_file((data->set.str[STRING_SSL_RANDOM_FILE]?
data->set.str[STRING_SSL_RANDOM_FILE]:
RANDOM_FILE),
RAND_LOAD_LENGTH);
Daniel Stenberg
committed
if(seed_enough(nread))
#if defined(HAVE_RAND_EGD)
/* only available in OpenSSL 0.9.5 and later */
/* EGD_SOCKET is set at configure time or not at all */
#ifndef EGD_SOCKET
/* If we don't have the define set, we only do this if the egd-option
is set */
Daniel Stenberg
committed
if(data->set.str[STRING_SSL_EGDSOCKET])
#define EGD_SOCKET "" /* doesn't matter won't be used */
#endif
/* If there's an option and a define, the option overrides the
define */
Daniel Stenberg
committed
int ret = RAND_egd(data->set.str[STRING_SSL_EGDSOCKET]?
data->set.str[STRING_SSL_EGDSOCKET]:EGD_SOCKET);
if(-1 != ret) {
nread += ret;
Daniel Stenberg
committed
if(seed_enough(nread))
return nread;
}
}
#endif
/* If we get here, it means we need to seed the PRNG using a "silly"
approach! */
{
int len;
char *area;
/* Changed call to RAND_seed to use the underlying RAND_add implementation
* directly. Do this in a loop, with the amount of additional entropy
* being dependent upon the algorithm used by Curl_FormBoundary(): N bytes
* of a 7-bit ascii set. -- Richard Gorton, March 11 2003.
*/
Daniel Stenberg
committed
do {
area = Curl_FormBoundary();
if(!area)
return 3; /* out of memory */
Daniel Stenberg
committed
RAND_add(area, len, (len >> 1));
free(area); /* now remove the random junk */
Daniel Stenberg
committed
} while(!RAND_status());
/* generates a default path for the random seed file */
buf[0]=0; /* blank it first */
RAND_file_name(buf, BUFSIZE);
if(buf[0]) {
/* we got a file name to try */
nread += RAND_load_file(buf, RAND_LOAD_LENGTH);
Daniel Stenberg
committed
if(seed_enough(nread))
return nread;
}
Daniel Stenberg
committed
infof(data, "libcurl is now using a weak random seed!\n");
int Curl_ossl_seed(struct SessionHandle *data)
{
/* we have the "SSL is seeded" boolean static to prevent multiple
time-consuming seedings in vain */
static bool ssl_seeded = FALSE;
Daniel Stenberg
committed
if(!ssl_seeded || data->set.str[STRING_SSL_RANDOM_FILE] ||
data->set.str[STRING_SSL_EGDSOCKET]) {
ossl_seed(data);
ssl_seeded = TRUE;
}
return 0;
}
#ifndef SSL_FILETYPE_ENGINE
#define SSL_FILETYPE_ENGINE 42
#endif
#ifndef SSL_FILETYPE_PKCS12
#define SSL_FILETYPE_PKCS12 43
#endif
static int do_file_type(const char *type)
{
if(!type || !type[0])
Daniel Stenberg
committed
if(Curl_raw_equal(type, "PEM"))
Daniel Stenberg
committed
if(Curl_raw_equal(type, "DER"))
Daniel Stenberg
committed
if(Curl_raw_equal(type, "ENG"))
Daniel Stenberg
committed
if(Curl_raw_equal(type, "P12"))
return SSL_FILETYPE_PKCS12;
static
int cert_stuff(struct connectdata *conn,
Daniel Stenberg
committed
SSL_CTX* ctx,
char *cert_file,
const char *cert_type,
char *key_file,
const char *key_type)
Daniel Stenberg
committed
struct SessionHandle *data = conn->data;
Daniel Stenberg
committed
int file_type = do_file_type(cert_type);
if(cert_file != NULL || file_type == SSL_FILETYPE_ENGINE) {
int cert_done = 0;
Daniel Stenberg
committed
if(data->set.str[STRING_KEY_PASSWD]) {
#ifndef HAVE_USERDATA_IN_PWD_CALLBACK
/*
* If password has been given, we store that in the global
* area (*shudder*) for a while:
*/
size_t len = strlen(data->set.str[STRING_KEY_PASSWD]);
if(len < sizeof(global_passwd))
memcpy(global_passwd, data->set.str[STRING_KEY_PASSWD], len+1);
#else
/*
* We set the password in the callback userdata
*/
Daniel Stenberg
committed
SSL_CTX_set_default_passwd_cb_userdata(ctx,
Daniel Stenberg
committed
data->set.str[STRING_KEY_PASSWD]);
#endif
Daniel Stenberg
committed
SSL_CTX_set_default_passwd_cb(ctx, passwd_callback);
#define SSL_CLIENT_CERT_ERR \
"unable to use client certificate (no key found or wrong pass phrase?)"
switch(file_type) {
case SSL_FILETYPE_PEM:
/* SSL_CTX_use_certificate_chain_file() only works on PEM files */
Daniel Stenberg
committed
if(SSL_CTX_use_certificate_chain_file(ctx,
cert_file) != 1) {
failf(data, SSL_CLIENT_CERT_ERR);
case SSL_FILETYPE_ASN1:
/* SSL_CTX_use_certificate_file() works with either PEM or ASN1, but
we use the case above for PEM so this can only be performed with
ASN1 files. */
Daniel Stenberg
committed
if(SSL_CTX_use_certificate_file(ctx,
cert_file,
file_type) != 1) {
failf(data, SSL_CLIENT_CERT_ERR);
return 0;
}
break;
Daniel Stenberg
committed
{
if(data->state.engine) {
const char *cmd_name = "LOAD_CERT_CTRL";
struct {
const char *cert_id;
X509 *cert;
} params;
params.cert_id = cert_file;
params.cert = NULL;
/* Does the engine supports LOAD_CERT_CTRL ? */
if(!ENGINE_ctrl(data->state.engine, ENGINE_CTRL_GET_CMD_FROM_NAME,
0, (void *)cmd_name, NULL)) {
Daniel Stenberg
committed
failf(data, "ssl engine does not support loading certificates");
return 0;
}
Daniel Stenberg
committed
/* Load the certificate from the engine */
if(!ENGINE_ctrl_cmd(data->state.engine, cmd_name,
0, ¶ms, NULL, 1)) {
Daniel Stenberg
committed
failf(data, "ssl engine cannot load client cert with id"
" '%s' [%s]", cert_file,
ERR_error_string(ERR_get_error(), NULL));
return 0;
}
if(!params.cert) {
Daniel Stenberg
committed
failf(data, "ssl engine didn't initialized the certificate "
"properly.");
return 0;
}
if(SSL_CTX_use_certificate(ctx, params.cert) != 1) {
failf(data, "unable to set client certificate");
X509_free(params.cert);
return 0;
}
X509_free(params.cert); /* we don't need the handle any more... */
}
else {
failf(data, "crypto engine not set, can't load certificate");
return 0;
}
}
break;
#else
failf(data, "file type ENG for certificate not implemented");
return 0;
Daniel Stenberg
committed
#endif
case SSL_FILETYPE_PKCS12:
{
#ifdef HAVE_PKCS12_SUPPORT
FILE *f;
PKCS12 *p12;
EVP_PKEY *pri;
STACK_OF(X509) *ca = NULL;
int i;
f = fopen(cert_file,"rb");
Daniel Stenberg
committed
if(!f) {
failf(data, "could not open PKCS12 file '%s'", cert_file);
return 0;
}
p12 = d2i_PKCS12_fp(f, NULL);
fclose(f);
if(!p12) {
failf(data, "error reading PKCS12 file '%s'", cert_file );
return 0;
}
PKCS12_PBE_add();
Daniel Stenberg
committed
if(!PKCS12_parse(p12, data->set.str[STRING_KEY_PASSWD], &pri, &x509,
failf(data,
"could not parse PKCS12 file, check password, OpenSSL error %s",
ERR_error_string(ERR_get_error(), NULL) );
PKCS12_free(p12);
return 0;
}
PKCS12_free(p12);
if(SSL_CTX_use_certificate(ctx, x509) != 1) {
failf(data, SSL_CLIENT_CERT_ERR);
EVP_PKEY_free(pri);
X509_free(x509);
sk_X509_pop_free(ca, X509_free);
return 0;
}
if(SSL_CTX_use_PrivateKey(ctx, pri) != 1) {
failf(data, "unable to use private key from PKCS12 file '%s'",
cert_file);
EVP_PKEY_free(pri);
X509_free(x509);
sk_X509_pop_free(ca, X509_free);
return 0;
}
if(!SSL_CTX_check_private_key (ctx)) {
failf(data, "private key from PKCS12 file '%s' "
"does not match certificate in same file", cert_file);
EVP_PKEY_free(pri);
X509_free(x509);
sk_X509_pop_free(ca, X509_free);
return 0;
}
/* Set Certificate Verification chain */
if(ca && sk_X509_num(ca)) {
for(i = 0; i < sk_X509_num(ca); i++) {
if(!SSL_CTX_add_extra_chain_cert(ctx,sk_X509_value(ca, i))) {
failf(data, "cannot add certificate to certificate chain");
EVP_PKEY_free(pri);
X509_free(x509);
sk_X509_pop_free(ca, X509_free);
return 0;
}
if(!SSL_CTX_add_client_CA(ctx, sk_X509_value(ca, i))) {
EVP_PKEY_free(pri);
X509_free(x509);
sk_X509_pop_free(ca, X509_free);
return 0;
}
}
}
EVP_PKEY_free(pri);
X509_free(x509);
sk_X509_pop_free(ca, X509_free);
cert_done = 1;
break;
#else
failf(data, "file type P12 for certificate not supported");
return 0;
#endif
}
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(cert_done)
break;
if(key_file == NULL)
/* cert & key can only be in PEM case in the same file */
key_file=cert_file;
case SSL_FILETYPE_ASN1:
Daniel Stenberg
committed
if(SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type) != 1) {
Daniel Stenberg
committed
failf(data, "unable to set private key file: '%s' type %s",
key_file, key_type?key_type:"PEM");
return 0;
}
break;
case SSL_FILETYPE_ENGINE:
#ifdef HAVE_OPENSSL_ENGINE_H
{ /* XXXX still needs some work */
EVP_PKEY *priv_key = NULL;
Daniel Stenberg
committed
if(data->state.engine) {
#ifdef HAVE_ENGINE_LOAD_FOUR_ARGS
UI_METHOD *ui_method = UI_OpenSSL();
#endif
/* the typecast below was added to please mingw32 */
priv_key = (EVP_PKEY *)
Daniel Stenberg
committed
ENGINE_load_private_key(data->state.engine,key_file,
#ifdef HAVE_ENGINE_LOAD_FOUR_ARGS
#endif
Daniel Stenberg
committed
data->set.str[STRING_KEY_PASSWD]);
if(!priv_key) {
Daniel Stenberg
committed
failf(data, "failed to load private key from crypto engine");
Daniel Stenberg
committed
if(SSL_CTX_use_PrivateKey(ctx, priv_key) != 1) {
Daniel Stenberg
committed
failf(data, "unable to set private key");
EVP_PKEY_free(priv_key);
return 0;
}
EVP_PKEY_free(priv_key); /* we don't need the handle any more... */
}
else {
Daniel Stenberg
committed
failf(data, "crypto engine not set, can't load private key");
Daniel Stenberg
committed
failf(data, "file type ENG for private key not supported");
case SSL_FILETYPE_PKCS12:
if(!cert_done) {
Daniel Stenberg
committed
failf(data, "file type P12 for private key not supported");
Daniel Stenberg
committed
return 0;
}
break;
Daniel Stenberg
committed
failf(data, "not supported file type for private key");
Daniel Stenberg
committed
ssl=SSL_new(ctx);
Daniel Stenberg
committed
if(NULL == ssl) {
Daniel Stenberg
committed
failf(data,"unable to create an SSL structure");
/* This version was provided by Evan Jordan and is supposed to not
leak memory as the previous version: */
if(x509 != NULL) {
EVP_PKEY *pktmp = X509_get_pubkey(x509);
EVP_PKEY_copy_parameters(pktmp,SSL_get_privatekey(ssl));
EVP_PKEY_free(pktmp);
}
SSL_free(ssl);
/* If we are using DSA, we can copy the parameters from
* the private key */
Daniel Stenberg
committed
/* Now we know that a key and cert have been set against
* the SSL context */
Daniel Stenberg
committed
if(!SSL_CTX_check_private_key(ctx)) {
failf(data, "Private key does not match the certificate public key");
Daniel Stenberg
committed
#ifndef HAVE_USERDATA_IN_PWD_CALLBACK
/* erase it now */
memset(global_passwd, 0, sizeof(global_passwd));
#endif
Daniel Stenberg
committed
/* returns non-zero on failure */
static int x509_name_oneline(X509_NAME *a, char *buf, size_t size)
Daniel Stenberg
committed
{
#if 0
return X509_NAME_oneline(a, buf, size);
#else
BIO *bio_out = BIO_new(BIO_s_mem());
BUF_MEM *biomem;
int rc;
Daniel Stenberg
committed
if(!bio_out)
return 1; /* alloc failed! */
Daniel Stenberg
committed
rc = X509_NAME_print_ex(bio_out, a, 0, XN_FLAG_SEP_SPLUS_SPC);
Daniel Stenberg
committed
BIO_get_mem_ptr(bio_out, &biomem);
if((size_t)biomem->length < size)
Daniel Stenberg
committed
size = biomem->length;
else
size--; /* don't overwrite the buffer end */
memcpy(buf, biomem->data, size);
buf[size]=0;
BIO_free(bio_out);
return !rc;
#endif
}
static
int cert_verify_callback(int ok, X509_STORE_CTX *ctx)
{
X509 *err_cert;
char buf[256];
err_cert=X509_STORE_CTX_get_current_cert(ctx);
Daniel Stenberg
committed
(void)x509_name_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
return ok;
/* Return error string for last OpenSSL error
*/
static char *SSL_strerror(unsigned long error, char *buf, size_t size)
{
#ifdef HAVE_ERR_ERROR_STRING_N
/* OpenSSL 0.9.6 and later has a function named
ERRO_error_string_n() that takes the size of the buffer as a
third argument */
ERR_error_string_n(error, buf, size);
#else
(void) size;
ERR_error_string(error, buf);
#endif
Daniel Stenberg
committed
#ifdef USE_SSLEAY
/**
* Global SSL init
*
* @retval 0 error initializing SSL
* @retval 1 SSL initialized successfully
*/
Daniel Stenberg
committed
int Curl_ossl_init(void)
{
#ifdef HAVE_ENGINE_LOAD_BUILTIN_ENGINES
ENGINE_load_builtin_engines();
#endif
/* Lets get nice error messages */
SSL_load_error_strings();
Daniel Stenberg
committed
/* Init the global ciphers and digests */
if(!SSLeay_add_ssl_algorithms())
return 0;
Daniel Stenberg
committed
OpenSSL_add_all_algorithms();
}
Daniel Stenberg
committed
#endif /* USE_SSLEAY */
#ifdef USE_SSLEAY
Daniel Stenberg
committed
/* Global cleanup */
void Curl_ossl_cleanup(void)
{
/* Free ciphers and digests lists */
Daniel Stenberg
committed
EVP_cleanup();
Daniel Stenberg
committed
Daniel Stenberg
committed
#ifdef HAVE_ENGINE_CLEANUP
/* Free engine list */
Daniel Stenberg
committed
ENGINE_cleanup();
#ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA
/* Free OpenSSL ex_data table */
Daniel Stenberg
committed
CRYPTO_cleanup_all_ex_data();
/* Free OpenSSL error strings */
ERR_free_strings();
/* Free thread local error state, destroying hash upon zero refcount */
#ifdef HAVE_ERR_REMOVE_THREAD_STATE
ERR_remove_thread_state(NULL);
#else
ERR_remove_state(0);
#endif
/*
* This function uses SSL_peek to determine connection status.
*
* Return codes:
* 1 means the connection is still in place
* 0 means the connection has been closed
* -1 means the connection status is unknown
*/
int Curl_ossl_check_cxn(struct connectdata *conn)
{
int rc;
char buf;
rc = SSL_peek(conn->ssl[FIRSTSOCKET].handle, (void*)&buf, 1);
Daniel Stenberg
committed
if(rc > 0)
return 1; /* connection still in place */
Daniel Stenberg
committed
if(rc == 0)
return 0; /* connection has been closed */
return -1; /* connection status unknown */
}
/* Selects an OpenSSL crypto engine
*/
Daniel Stenberg
committed
CURLcode Curl_ossl_set_engine(struct SessionHandle *data, const char *engine)
{
#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H)
ENGINE *e;
#if OPENSSL_VERSION_NUMBER >= 0x00909000L
e = ENGINE_by_id(engine);
#else
/* avoid memory leak */
for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)) {
const char *e_id = ENGINE_get_id(e);
if(!strcmp(engine, e_id))
break;
}
#endif
Daniel Stenberg
committed
if(!e) {
failf(data, "SSL Engine '%s' not found", engine);
Daniel Stenberg
committed
if(data->state.engine) {
Daniel Stenberg
committed
ENGINE_finish(data->state.engine);
ENGINE_free(data->state.engine);
data->state.engine = NULL;
Daniel Stenberg
committed
if(!ENGINE_init(e)) {
failf(data, "Failed to initialise SSL Engine '%s':\n%s",
engine, SSL_strerror(ERR_get_error(), buf, sizeof(buf)));
Daniel Stenberg
committed
data->state.engine = e;
(void)engine;
failf(data, "SSL Engine not supported");
Daniel Stenberg
committed
/* Sets engine as default for all SSL operations
Daniel Stenberg
committed
CURLcode Curl_ossl_set_engine_default(struct SessionHandle *data)
Daniel Stenberg
committed
#ifdef HAVE_OPENSSL_ENGINE_H
Daniel Stenberg
committed
if(data->state.engine) {
if(ENGINE_set_default(data->state.engine, ENGINE_METHOD_ALL) > 0) {
infof(data,"set default crypto engine '%s'\n",
ENGINE_get_id(data->state.engine));
failf(data, "set default crypto engine '%s' failed",
ENGINE_get_id(data->state.engine));
return CURLE_SSL_ENGINE_SETFAILED;
}
}
#else
(void) data;
#endif
Daniel Stenberg
committed
return CURLE_OK;
/* Return list of OpenSSL crypto engine names.
Daniel Stenberg
committed
struct curl_slist *Curl_ossl_engines_list(struct SessionHandle *data)
#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H)
for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)) {
beg = curl_slist_append(list, ENGINE_get_id(e));
if(!beg) {
curl_slist_free_all(list);
/*
* This function is called when an SSL connection is closed.
*/
Daniel Stenberg
committed
void Curl_ossl_close(struct connectdata *conn, int sockindex)
{
Daniel Stenberg
committed
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
Daniel Stenberg
committed
if(connssl->handle) {
(void)SSL_shutdown(connssl->handle);
SSL_set_connect_state(connssl->handle);
Daniel Stenberg
committed
SSL_free (connssl->handle);
connssl->handle = NULL;
}
if(connssl->ctx) {
SSL_CTX_free (connssl->ctx);
connssl->ctx = NULL;
}
}
Daniel Stenberg
committed
/*
* This function is called to shut down the SSL layer but keep the
* socket open (CCC - Clear Command Channel)
*/
int Curl_ossl_shutdown(struct connectdata *conn, int sockindex)
{
int retval = 0;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct SessionHandle *data = conn->data;
char buf[120]; /* We will use this for the OpenSSL error buffer, so it has
to be at least 120 bytes long. */
unsigned long sslerror;
ssize_t nread;
Yang Tse
committed
int buffsize;
Daniel Stenberg
committed
int err;
int done = 0;
/* This has only been tested on the proftpd server, and the mod_tls code
sends a close notify alert without waiting for a close notify alert in
response. Thus we wait for a close notify alert from the server, but
we do not send one. Let's hope other servers do the same... */
Linus Nielsen
committed
if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)
(void)SSL_shutdown(connssl->handle);
Daniel Stenberg
committed
if(connssl->handle) {
Yang Tse
committed
buffsize = (int)sizeof(buf);
Daniel Stenberg
committed
while(!done) {
int what = Curl_socket_ready(conn->sock[sockindex],
CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
Daniel Stenberg
committed
if(what > 0) {
ERR_clear_error();
Daniel Stenberg
committed
/* Something to read, let's do it and hope that it is the close
notify alert from the server */
nread = (ssize_t)SSL_read(conn->ssl[sockindex].handle, buf,
Yang Tse
committed
buffsize);
Daniel Stenberg
committed
err = SSL_get_error(conn->ssl[sockindex].handle, (int)nread);
switch(err) {
case SSL_ERROR_NONE: /* this is not an error */
case SSL_ERROR_ZERO_RETURN: /* no more data */
/* This is the expected response. There was no data but only
the close notify alert */
done = 1;
break;
case SSL_ERROR_WANT_READ:
/* there's data pending, re-invoke SSL_read() */
infof(data, "SSL_ERROR_WANT_READ\n");
break;
case SSL_ERROR_WANT_WRITE:
/* SSL wants a write. Really odd. Let's bail out. */
infof(data, "SSL_ERROR_WANT_WRITE\n");
done = 1;
break;
default:
/* openssl/ssl.h says "look at error stack/return value/errno" */
sslerror = ERR_get_error();
failf(conn->data, "SSL read: %s, errno %d",
ERR_error_string(sslerror, buf),
SOCKERRNO);
Daniel Stenberg
committed
done = 1;
break;
}
}
else if(0 == what) {
/* timeout */
failf(data, "SSL shutdown timeout");
done = 1;
}
else {
/* anything that gets here is fatally bad */
failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
Daniel Stenberg
committed
retval = -1;
done = 1;
}
} /* while()-loop for the select() */
if(data->set.verbose) {
Daniel Stenberg
committed
#ifdef HAVE_SSL_GET_SHUTDOWN
Daniel Stenberg
committed
switch(SSL_get_shutdown(connssl->handle)) {
case SSL_SENT_SHUTDOWN:
infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN\n");
break;
case SSL_RECEIVED_SHUTDOWN:
infof(data, "SSL_get_shutdown() returned SSL_RECEIVED_SHUTDOWN\n");
break;
case SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN:
infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN|"
"SSL_RECEIVED__SHUTDOWN\n");
break;
}
Daniel Stenberg
committed
#endif
Daniel Stenberg
committed
}
SSL_free (connssl->handle);
connssl->handle = NULL;
}
return retval;
}
Daniel Stenberg
committed
void Curl_ossl_session_free(void *ptr)
{
Daniel Stenberg
committed
/* free the ID */
SSL_SESSION_free(ptr);
}
/*
* This function is called when the 'data' struct is going away. Close
* down everything and free all resources!
*/
Daniel Stenberg
committed
int Curl_ossl_close_all(struct SessionHandle *data)
{
Daniel Stenberg
committed
if(data->state.engine) {
ENGINE_finish(data->state.engine);
ENGINE_free(data->state.engine);
data->state.engine = NULL;
return 0;
}
Daniel Stenberg
committed
static int asn1_output(const ASN1_UTCTIME *tm,
char *buf,
size_t sizeofbuf)
{
const char *asn1_string;
int gmt=FALSE;