Newer
Older
/***************************************************************************
Daniel Stenberg
committed
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* Copyright (C) 1998 - 2009, 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.
*/
/*
* The original SSLeay-using code for curl was written by Linas Vepstas and
* Sampo Kellomaki 1998.
#include <ctype.h>
Daniel Stenberg
committed
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#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"
Daniel Stenberg
committed
#include "sslgen.h"
Daniel Stenberg
committed
#include "rawstr.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>
#else
#include <rand.h>
#include <x509v3.h>
#endif
#include "memory.h"
#include "easyif.h" /* for Curl_convert_from_utf8 prototype */
/* The last #include file should be: */
#include "memdebug.h"
#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
#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)
#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 verify
/* This was introduced in 0.9.4, we can set this
using SSL_CTX_set_default_passwd_cb_userdata()
*/
, void *global_passwd
#endif
)
{
if(verify)
fprintf(stderr, "%s\n", buf);
else {
if(num > (int)strlen((char *)global_passwd)) {
return (int)strlen(buf);
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)
{
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 */
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! */
#ifdef HAVE_RAND_SCREEN
/* This one gets a random value by reading the currently shown screen */
RAND_screen();
nread = 100; /* just a value */
#else
{
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
len = (int)strlen(area);
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;
if(cert_file != NULL) {
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.key_passwd);
if(len < sizeof(global_passwd))
memcpy(global_passwd, data->set.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);
file_type = do_file_type(cert_type);
#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;
case SSL_FILETYPE_ENGINE:
failf(data, "file type ENG for certificate not implemented");
return 0;
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);
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);
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);
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);
return 0;
}
if (!SSL_CTX_add_client_CA(ctx, sk_X509_value(ca, i))) {
failf(data, "cannot add certificate to client CA list",
cert_file);
EVP_PKEY_free(pri);
X509_free(x509);
return 0;
}
}
}
EVP_PKEY_free(pri);
X509_free(x509);
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
if(!key_file || !key_file[0]) {
Daniel Stenberg
committed
failf(data, "no key set to load from crypto engine");
/* 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_CPLUS_SPC);
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
return (buf);
}
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();
/* Setup all the global SSL stuff */
Daniel Stenberg
committed
if(!SSLeay_add_ssl_algorithms())
}
Daniel Stenberg
committed
#endif /* USE_SSLEAY */
#ifdef USE_SSLEAY
Daniel Stenberg
committed
/* Global cleanup */
void Curl_ossl_cleanup(void)
{
/* Free the SSL error strings */
ERR_free_strings();
Daniel Stenberg
committed
Daniel Stenberg
committed
/* EVP_cleanup() removes all ciphers and digests from the
table. */
EVP_cleanup();
Daniel Stenberg
committed
Daniel Stenberg
committed
ENGINE_cleanup();
#ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA
Daniel Stenberg
committed
/* this function was not present in 0.9.6b, but was added sometimes
later */
CRYPTO_cleanup_all_ex_data();
#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 = ENGINE_by_id(engine);
Daniel Stenberg
committed
if(!e) {
failf(data, "SSL Engine '%s' not found", engine);
return (CURLE_SSL_ENGINE_NOTFOUND);
}
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)));
return (CURLE_SSL_ENGINE_INITFAILED);
}
Daniel Stenberg
committed
data->state.engine = e;
(void)engine;
failf(data, "SSL Engine not supported");
return (CURLE_SSL_ENGINE_NOTFOUND);
#endif
}
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)) {
list = curl_slist_append(list, ENGINE_get_id(e));
Daniel Stenberg
committed
if(list == NULL) {
curl_slist_free_all(beg);
return NULL;
}
Daniel Stenberg
committed
else if(beg == NULL) {
/*
* 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;
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) {
while(!done) {
int what = Curl_socket_ready(conn->sock[sockindex],
Daniel Stenberg
committed
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
if(what > 0) {
/* 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,
sizeof(buf));
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;
break;
}
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
/*
ERR_remove_state() frees the error queue associated with
thread pid. If pid == 0, the current thread will have its
error queue removed.
Since error queue data structures are allocated
automatically for new threads, they must be freed when
threads are terminated in oder to avoid memory leaks.
*/
ERR_remove_state(0);
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;
int i;
int year=0,month=0,day=0,hour=0,minute=0,second=0;
i=tm->length;
asn1_string=(const char *)tm->data;
if(i < 10)
return 1;
if(asn1_string[i-1] == 'Z')
gmt=TRUE;
for (i=0; i<10; i++)
if((asn1_string[i] > '9') || (asn1_string[i] < '0'))
return 2;
year= (asn1_string[0]-'0')*10+(asn1_string[1]-'0');
if(year < 50)
year+=100;
month= (asn1_string[2]-'0')*10+(asn1_string[3]-'0');
if((month > 12) || (month < 1))
return 3;
day= (asn1_string[4]-'0')*10+(asn1_string[5]-'0');
hour= (asn1_string[6]-'0')*10+(asn1_string[7]-'0');
minute= (asn1_string[8]-'0')*10+(asn1_string[9]-'0');
if((asn1_string[10] >= '0') && (asn1_string[10] <= '9') &&
(asn1_string[11] >= '0') && (asn1_string[11] <= '9'))
second= (asn1_string[10]-'0')*10+(asn1_string[11]-'0');
Daniel Stenberg
committed
Daniel Stenberg
committed
snprintf(buf, sizeofbuf,
"%04d-%02d-%02d %02d:%02d:%02d %s",
year+1900, month, day, hour, minute, second, (gmt?"GMT":""));
return 0;
}
/* ====================================================== */
Daniel Stenberg
committed
/*
* Match a hostname against a wildcard pattern.
* E.g.
* "foo.host.com" matches "*.host.com".
*
* We are a bit more liberal than RFC2818 describes in that we
* accept multiple "*" in pattern (similar to what some other browsers do).
* E.g.
* "abc.def.domain.com" should strickly not match "*.domain.com", but we
* don't consider "." to be important in CERT checking.
*/
#define HOST_NOMATCH 0
#define HOST_MATCH 1
static int hostmatch(const char *hostname, const char *pattern)
{
Daniel Stenberg
committed
while(1) {
char c = *pattern++;
Daniel Stenberg
committed
if(c == '\0')
return (*hostname ? HOST_NOMATCH : HOST_MATCH);
Daniel Stenberg
committed
if(c == '*') {
Daniel Stenberg
committed
if(c == '\0') /* "*\0" matches anything remaining */
Daniel Stenberg
committed
Daniel Stenberg
committed
while(*hostname) {
/* The only recursive function in libcurl! */
Daniel Stenberg
committed
if(hostmatch(hostname++,pattern) == HOST_MATCH)
return HOST_MATCH;
}
if(Curl_raw_toupper(c) != Curl_raw_toupper(*hostname++))