#include "curl_setup.h"
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#include "urldata.h"
#include "sendf.h"
#include "formdata.h"
#include "url.h"
#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"
#define _MPRINTF_REPLACE
#include <curl/mprintf.h>
#ifdef USE_SSLEAY
#ifdef USE_OPENSSL
#include <openssl/rand.h>
#include <openssl/x509v3.h>
#include <openssl/dsa.h>
#include <openssl/dh.h>
#include <openssl/err.h>
#include <openssl/md5.h>
#else
#include <rand.h>
#include <x509v3.h>
#include <md5.h>
#endif
#include "warnless.h"
#include "curl_memory.h"
#include "non-ascii.h"
#include "memdebug.h"
#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
#define HAVE_ENGINE_LOAD_FOUR_ARGS
#include <openssl/ui.h>
#else
#undef HAVE_ENGINE_LOAD_FOUR_ARGS
#endif
#if (OPENSSL_VERSION_NUMBER >= 0x00903001L) && defined(HAVE_OPENSSL_PKCS12_H)
#define HAVE_PKCS12_SUPPORT
#else
#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
#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
#define OPENSSL_NO_SSL2
#endif
#define RAND_LOAD_LENGTH 1024
#ifndef HAVE_USERDATA_IN_PWD_CALLBACK
static char global_passwd[64];
#endif
static int passwd_callback(char *buf, int num, int encrypting
#ifdef HAVE_USERDATA_IN_PWD_CALLBACK
, void *global_passwd
#endif
)
{
DEBUGASSERT(0 == encrypting);
if(!encrypting) {
int klen = curlx_uztosi(strlen((char *)global_passwd));
if(num > klen) {
memcpy(buf, global_passwd, klen+1);
return klen;
}
}
return 0;
}
#ifdef HAVE_RAND_STATUS
#define seed_enough(x) rand_enough()
static bool rand_enough(void)
{
return (0 != RAND_status()) ? TRUE : FALSE;
}
#else
#define seed_enough(x) rand_enough(x)
static bool rand_enough(int nread)
{
return (nread > 500) ? TRUE : FALSE;
}
#endif
static int ossl_seed(struct SessionHandle *data)
{
char *buf = data->state.buffer;
int nread=0;
#ifndef RANDOM_FILE
if(data->set.ssl.random_file)
#define RANDOM_FILE ""
#endif
{
nread += RAND_load_file((data->set.str[STRING_SSL_RANDOM_FILE]?
data->set.str[STRING_SSL_RANDOM_FILE]:
RANDOM_FILE),
RAND_LOAD_LENGTH);
if(seed_enough(nread))
return nread;
}
#if defined(HAVE_RAND_EGD)
#ifndef EGD_SOCKET
if(data->set.str[STRING_SSL_EGDSOCKET])
#define EGD_SOCKET ""
#endif
{
int ret = RAND_egd(data->set.str[STRING_SSL_EGDSOCKET]?
data->set.str[STRING_SSL_EGDSOCKET]:EGD_SOCKET);
if(-1 != ret) {
nread += ret;
if(seed_enough(nread))
return nread;
}
}
#endif
do {
unsigned char randb[64];
int len = sizeof(randb);
RAND_bytes(randb, len);
RAND_add(randb, len, (len >> 1));
} while(!RAND_status());
buf[0]=0;
RAND_file_name(buf, BUFSIZE);
if(buf[0]) {
nread += RAND_load_file(buf, RAND_LOAD_LENGTH);
if(seed_enough(nread))
return nread;
}
infof(data, "libcurl is now using a weak random seed!\n");
return nread;
}
int Curl_ossl_seed(struct SessionHandle *data)
{
static bool ssl_seeded = FALSE;
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])
return SSL_FILETYPE_PEM;
if(Curl_raw_equal(type, "PEM"))
return SSL_FILETYPE_PEM;
if(Curl_raw_equal(type, "DER"))
return SSL_FILETYPE_ASN1;
if(Curl_raw_equal(type, "ENG"))
return SSL_FILETYPE_ENGINE;
if(Curl_raw_equal(type, "P12"))
return SSL_FILETYPE_PKCS12;
return -1;
}
static
int cert_stuff(struct connectdata *conn,
SSL_CTX* ctx,
char *cert_file,
const char *cert_type,
char *key_file,
const char *key_type)
{
struct SessionHandle *data = conn->data;
int file_type = do_file_type(cert_type);
if(cert_file != NULL || file_type == SSL_FILETYPE_ENGINE) {
SSL *ssl;
X509 *x509;
int cert_done = 0;
if(data->set.str[STRING_KEY_PASSWD]) {
#ifndef HAVE_USERDATA_IN_PWD_CALLBACK
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
global_passwd[0] = '\0';
#else
SSL_CTX_set_default_passwd_cb_userdata(ctx,
data->set.str[STRING_KEY_PASSWD]);
#endif
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:
if(SSL_CTX_use_certificate_chain_file(ctx,
cert_file) != 1) {
failf(data, SSL_CLIENT_CERT_ERR);
return 0;
}
break;
case SSL_FILETYPE_ASN1:
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:
#if defined(HAVE_OPENSSL_ENGINE_H) && defined(ENGINE_CTRL_GET_CMD_FROM_NAME)
{
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;
if(!ENGINE_ctrl(data->state.engine, ENGINE_CTRL_GET_CMD_FROM_NAME,
0, (void *)cmd_name, NULL)) {
failf(data, "ssl engine does not support loading certificates");
return 0;
}
if(!ENGINE_ctrl_cmd(data->state.engine, cmd_name,
0, ¶ms, NULL, 1)) {
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) {
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);
}
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;
#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");
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();
if(!PKCS12_parse(p12, data->set.str[STRING_KEY_PASSWD], &pri, &x509,
&ca)) {
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;
}
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))) {
failf(data, "cannot add certificate to client CA list");
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)
key_file=cert_file;
case SSL_FILETYPE_ASN1:
if(SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type) != 1) {
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
{
EVP_PKEY *priv_key = NULL;
if(data->state.engine) {
#ifdef HAVE_ENGINE_LOAD_FOUR_ARGS
UI_METHOD *ui_method = UI_OpenSSL();
#endif
priv_key = (EVP_PKEY *)
ENGINE_load_private_key(data->state.engine,key_file,
#ifdef HAVE_ENGINE_LOAD_FOUR_ARGS
ui_method,
#endif
data->set.str[STRING_KEY_PASSWD]);
if(!priv_key) {
failf(data, "failed to load private key from crypto engine");
return 0;
}
if(SSL_CTX_use_PrivateKey(ctx, priv_key) != 1) {
failf(data, "unable to set private key");
EVP_PKEY_free(priv_key);
return 0;
}
EVP_PKEY_free(priv_key);
}
else {
failf(data, "crypto engine not set, can't load private key");
return 0;
}
}
break;
#else
failf(data, "file type ENG for private key not supported");
return 0;
#endif
case SSL_FILETYPE_PKCS12:
if(!cert_done) {
failf(data, "file type P12 for private key not supported");
return 0;
}
break;
default:
failf(data, "not supported file type for private key");
return 0;
}
ssl=SSL_new(ctx);
if(NULL == ssl) {
failf(data,"unable to create an SSL structure");
return 0;
}
x509=SSL_get_certificate(ssl);
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(!SSL_CTX_check_private_key(ctx)) {
failf(data, "Private key does not match the certificate public key");
return 0;
}
#ifndef HAVE_USERDATA_IN_PWD_CALLBACK
memset(global_passwd, 0, sizeof(global_passwd));
#endif
}
return 1;
}
static int x509_name_oneline(X509_NAME *a, char *buf, size_t size)
{
#if 0
return X509_NAME_oneline(a, buf, size);
#else
BIO *bio_out = BIO_new(BIO_s_mem());
BUF_MEM *biomem;
int rc;
if(!bio_out)
return 1;
rc = X509_NAME_print_ex(bio_out, a, 0, XN_FLAG_SEP_SPLUS_SPC);
BIO_get_mem_ptr(bio_out, &biomem);
if((size_t)biomem->length < size)
size = biomem->length;
else
size--;
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);
(void)x509_name_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
return ok;
}
static char *SSL_strerror(unsigned long error, char *buf, size_t size)
{
#ifdef HAVE_ERR_ERROR_STRING_N
ERR_error_string_n(error, buf, size);
#else
(void) size;
ERR_error_string(error, buf);
#endif
return buf;
}
#endif
#ifdef USE_SSLEAY
int Curl_ossl_init(void)
{
#ifdef HAVE_ENGINE_LOAD_BUILTIN_ENGINES
ENGINE_load_builtin_engines();
#endif
SSL_load_error_strings();
if(!SSLeay_add_ssl_algorithms())
return 0;
OpenSSL_add_all_algorithms();
return 1;
}
#endif
#ifdef USE_SSLEAY
void Curl_ossl_cleanup(void)
{
EVP_cleanup();
#ifdef HAVE_ENGINE_CLEANUP
ENGINE_cleanup();
#endif
#ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA
CRYPTO_cleanup_all_ex_data();
#endif
ERR_free_strings();
#ifdef HAVE_ERR_REMOVE_THREAD_STATE
ERR_remove_thread_state(NULL);
#else
ERR_remove_state(0);
#endif
}
int Curl_ossl_check_cxn(struct connectdata *conn)
{
int rc;
char buf;
rc = SSL_peek(conn->ssl[FIRSTSOCKET].handle, (void*)&buf, 1);
if(rc > 0)
return 1;
if(rc == 0)
return 0;
return -1;
}
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
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
if(!e) {
failf(data, "SSL Engine '%s' not found", engine);
return CURLE_SSL_ENGINE_NOTFOUND;
}
if(data->state.engine) {
ENGINE_finish(data->state.engine);
ENGINE_free(data->state.engine);
data->state.engine = NULL;
}
if(!ENGINE_init(e)) {
char buf[256];
ENGINE_free(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;
}
data->state.engine = e;
return CURLE_OK;
#else
(void)engine;
failf(data, "SSL Engine not supported");
return CURLE_SSL_ENGINE_NOTFOUND;
#endif
}
CURLcode Curl_ossl_set_engine_default(struct SessionHandle *data)
{
#ifdef HAVE_OPENSSL_ENGINE_H
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));
}
else {
failf(data, "set default crypto engine '%s' failed",
ENGINE_get_id(data->state.engine));
return CURLE_SSL_ENGINE_SETFAILED;
}
}
#else
(void) data;
#endif
return CURLE_OK;
}
struct curl_slist *Curl_ossl_engines_list(struct SessionHandle *data)
{
struct curl_slist *list = NULL;
#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H)
struct curl_slist *beg;
ENGINE *e;
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);
return NULL;
}
list = beg;
}
#endif
(void) data;
return list;
}
void Curl_ossl_close(struct connectdata *conn, int sockindex)
{
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
if(connssl->handle) {
(void)SSL_shutdown(connssl->handle);
SSL_set_connect_state(connssl->handle);
SSL_free (connssl->handle);
connssl->handle = NULL;
}
if(connssl->ctx) {
SSL_CTX_free (connssl->ctx);
connssl->ctx = NULL;
}
}
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];
unsigned long sslerror;
ssize_t nread;
int buffsize;
int err;
int done = 0;
if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)
(void)SSL_shutdown(connssl->handle);
if(connssl->handle) {
buffsize = (int)sizeof(buf);
while(!done) {
int what = Curl_socket_ready(conn->sock[sockindex],
CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
if(what > 0) {
ERR_clear_error();
nread = (ssize_t)SSL_read(conn->ssl[sockindex].handle, buf,
buffsize);
err = SSL_get_error(conn->ssl[sockindex].handle, (int)nread);
switch(err) {
case SSL_ERROR_NONE:
case SSL_ERROR_ZERO_RETURN:
done = 1;
break;
case SSL_ERROR_WANT_READ:
infof(data, "SSL_ERROR_WANT_READ\n");
break;
case SSL_ERROR_WANT_WRITE:
infof(data, "SSL_ERROR_WANT_WRITE\n");
done = 1;
break;
default:
sslerror = ERR_get_error();
failf(conn->data, "SSL read: %s, errno %d",
ERR_error_string(sslerror, buf),
SOCKERRNO);
done = 1;
break;
}
}
else if(0 == what) {
failf(data, "SSL shutdown timeout");
done = 1;
}
else {
failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
retval = -1;
done = 1;
}
}
if(data->set.verbose) {
#ifdef HAVE_SSL_GET_SHUTDOWN
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;
}
#endif
}
SSL_free (connssl->handle);
connssl->handle = NULL;
}
return retval;
}
void Curl_ossl_session_free(void *ptr)
{
SSL_SESSION_free(ptr);
}
int Curl_ossl_close_all(struct SessionHandle *data)
{
#ifdef HAVE_OPENSSL_ENGINE_H
if(data->state.engine) {
ENGINE_finish(data->state.engine);
ENGINE_free(data->state.engine);
data->state.engine = NULL;
}
#else
(void)data;
#endif
return 0;
}
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');
snprintf(buf, sizeofbuf,
"%04d-%02d-%02d %02d:%02d:%02d %s",
year+1900, month, day, hour, minute, second, (gmt?"GMT":""));
return 0;
}
static CURLcode verifyhost(struct connectdata *conn,
X509 *server_cert)
{
int matched = -1;
int target = GEN_DNS;
size_t addrlen = 0;
struct SessionHandle *data = conn->data;
STACK_OF(GENERAL_NAME) *altnames;
#ifdef ENABLE_IPV6
struct in6_addr addr;
#else
struct in_addr addr;
#endif
CURLcode res = CURLE_OK;
#ifdef ENABLE_IPV6
if(conn->bits.ipv6_ip &&
Curl_inet_pton(AF_INET6, conn->host.name, &addr)) {
target = GEN_IPADD;
addrlen = sizeof(struct in6_addr);
}
else
#endif
if(Curl_inet_pton(AF_INET, conn->host.name, &addr)) {
target = GEN_IPADD;
addrlen = sizeof(struct in_addr);
}
altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL);
if(altnames) {
int numalts;
int i;
numalts = sk_GENERAL_NAME_num(altnames);
for(i=0; (i<numalts) && (matched != 1); i++) {
const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i);
if(check->type == target) {
const char *altptr = (char *)ASN1_STRING_data(check->d.ia5);
size_t altlen = (size_t) ASN1_STRING_length(check->d.ia5);
switch(target) {
case GEN_DNS:
if((altlen == strlen(altptr)) &&
Curl_cert_hostcheck(altptr, conn->host.name))
matched = 1;
else
matched = 0;
break;
case GEN_IPADD:
if((altlen == addrlen) && !memcmp(altptr, &addr, altlen))
matched = 1;
else
matched = 0;
break;
}
}
}
GENERAL_NAMES_free(altnames);
}
if(matched == 1)
infof(data, "\t subjectAltName: %s matched\n", conn->host.dispname);
else if(matched == 0) {
infof(data, "\t subjectAltName does not match %s\n", conn->host.dispname);
res = CURLE_PEER_FAILED_VERIFICATION;
}
else {
int j,i=-1 ;
unsigned char *nulstr = (unsigned char *)"";
unsigned char *peer_CN = nulstr;
X509_NAME *name = X509_get_subject_name(server_cert) ;
if(name)
while((j = X509_NAME_get_index_by_NID(name, NID_commonName, i))>=0)
i=j;
if(i>=0) {
ASN1_STRING *tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name,i));
if(tmp) {
if(ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) {
j = ASN1_STRING_length(tmp);
if(j >= 0) {
peer_CN = OPENSSL_malloc(j+1);
if(peer_CN) {
memcpy(peer_CN, ASN1_STRING_data(tmp), j);
peer_CN[j] = '\0';
}
}
}
else
j = ASN1_STRING_to_UTF8(&peer_CN, tmp);
if(peer_CN && (curlx_uztosi(strlen((char *)peer_CN)) != j)) {
failf(data, "SSL: illegal cert name field");
res = CURLE_PEER_FAILED_VERIFICATION;
}
}
}
if(peer_CN == nulstr)
peer_CN = NULL;
else {
CURLcode rc = Curl_convert_from_utf8(data, peer_CN, strlen(peer_CN));
if(rc) {
OPENSSL_free(peer_CN);
return rc;
}
}
if(res)
;
else if(!peer_CN) {
failf(data,
"SSL: unable to obtain common name from peer certificate");
res = CURLE_PEER_FAILED_VERIFICATION;
}
else if(!Curl_cert_hostcheck((const char *)peer_CN, conn->host.name)) {
failf(data, "SSL: certificate subject name '%s' does not match "
"target host name '%s'", peer_CN, conn->host.dispname);
res = CURLE_PEER_FAILED_VERIFICATION;
}
else {
infof(data, "\t common name: %s (matched)\n", peer_CN);
}
if(peer_CN)
OPENSSL_free(peer_CN);
}
return res;
}
#endif
#ifdef SSL_CTRL_SET_MSG_CALLBACK
static const char *ssl_msg_type(int ssl_ver, int msg)
{
if(ssl_ver == SSL2_VERSION_MAJOR) {
switch (msg) {
case SSL2_MT_ERROR:
return "Error";
case SSL2_MT_CLIENT_HELLO:
return "Client hello";
case SSL2_MT_CLIENT_MASTER_KEY:
return "Client key";
case SSL2_MT_CLIENT_FINISHED:
return "Client finished";
case SSL2_MT_SERVER_HELLO:
return "Server hello";
case SSL2_MT_SERVER_VERIFY:
return "Server verify";
case SSL2_MT_SERVER_FINISHED:
return "Server finished";
case SSL2_MT_REQUEST_CERTIFICATE:
return "Request CERT";
case SSL2_MT_CLIENT_CERTIFICATE:
return "Client CERT";
}
}
else if(ssl_ver == SSL3_VERSION_MAJOR) {
switch (msg) {
case SSL3_MT_HELLO_REQUEST:
return "Hello request";
case SSL3_MT_CLIENT_HELLO:
return "Client hello";
case SSL3_MT_SERVER_HELLO:
return "Server hello";
case SSL3_MT_CERTIFICATE:
return "CERT";
case SSL3_MT_SERVER_KEY_EXCHANGE:
return "Server key exchange";
case SSL3_MT_CLIENT_KEY_EXCHANGE:
return "Client key exchange";
case SSL3_MT_CERTIFICATE_REQUEST:
return "Request CERT";
case SSL3_MT_SERVER_DONE:
return "Server finished";
case SSL3_MT_CERTIFICATE_VERIFY:
return "CERT verify";
case SSL3_MT_FINISHED:
return "Finished";
}
}
return "Unknown";
}
static const char *tls_rt_type(int type)
{
return (
type == SSL3_RT_CHANGE_CIPHER_SPEC ? "TLS change cipher, " :
type == SSL3_RT_ALERT ? "TLS alert, " :
type == SSL3_RT_HANDSHAKE ? "TLS handshake, " :
type == SSL3_RT_APPLICATION_DATA ? "TLS app data, " :
"TLS Unknown, ");
}
static void ssl_tls_trace(int direction, int ssl_ver, int content_type,
const void *buf, size_t len, const SSL *ssl,
struct connectdata *conn)
{
struct SessionHandle *data;
const char *msg_name, *tls_rt_name;
char ssl_buf[1024];
int ver, msg_type, txt_len;
if(!conn || !conn->data || !conn->data->set.fdebug ||
(direction != 0 && direction != 1))
return;
data = conn->data;
ssl_ver >>= 8;
ver = (ssl_ver == SSL2_VERSION_MAJOR ? '2' :
ssl_ver == SSL3_VERSION_MAJOR ? '3' : '?');
if(ssl_ver == SSL3_VERSION_MAJOR && content_type != 0)
tls_rt_name = tls_rt_type(content_type);
else
tls_rt_name = "";
msg_type = *(char*)buf;
msg_name = ssl_msg_type(ssl_ver, msg_type);
txt_len = snprintf(ssl_buf, sizeof(ssl_buf), "SSLv%c, %s%s (%d):\n",
ver, tls_rt_name, msg_name, msg_type);
Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len, NULL);
Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT :
CURLINFO_SSL_DATA_IN, (char *)buf, len, NULL);
(void) ssl;
}
#endif
#ifdef USE_SSLEAY
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
# define use_sni(x) sni = (x)
#else
# define use_sni(x) Curl_nop_stmt
#endif
static CURLcode
ossl_connect_step1(struct connectdata *conn,
int sockindex)
{
CURLcode retcode = CURLE_OK;
struct SessionHandle *data = conn->data;
SSL_METHOD_QUAL SSL_METHOD *req_method=NULL;
void *ssl_sessionid=NULL;
X509_LOOKUP *lookup=NULL;
curl_socket_t sockfd = conn->sock[sockindex];
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
long ctx_options;
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
bool sni;
#ifdef ENABLE_IPV6
struct in6_addr addr;
#else
struct in_addr addr;
#endif
#endif
DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
Curl_ossl_seed(data);
switch(data->set.ssl.version) {
default:
case CURL_SSLVERSION_DEFAULT:
#ifdef USE_TLS_SRP
if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) {
infof(data, "Set version TLSv1 for SRP authorisation\n");
req_method = TLSv1_client_method() ;
}
else
#endif
req_method = SSLv23_client_method();
use_sni(TRUE);
break;
case CURL_SSLVERSION_TLSv1:
req_method = TLSv1_client_method();
use_sni(TRUE);
break;
case CURL_SSLVERSION_SSLv2:
#ifdef OPENSSL_NO_SSL2
failf(data, "OpenSSL was built without SSLv2 support");
return CURLE_NOT_BUILT_IN;
#else
#ifdef USE_TLS_SRP
if(data->set.ssl.authtype == CURL_TLSAUTH_SRP)
return CURLE_SSL_CONNECT_ERROR;
#endif
req_method = SSLv2_client_method();
use_sni(FALSE);
break;
#endif
case CURL_SSLVERSION_SSLv3:
#ifdef USE_TLS_SRP
if(data->set.ssl.authtype == CURL_TLSAUTH_SRP)
return CURLE_SSL_CONNECT_ERROR;
#endif
req_method = SSLv3_client_method();
use_sni(FALSE);
break;
}
if(connssl->ctx)
SSL_CTX_free(connssl->ctx);
connssl->ctx = SSL_CTX_new(req_method);
if(!connssl->ctx) {
failf(data, "SSL: couldn't create a context: %s",
ERR_error_string(ERR_peek_error(), NULL));
return CURLE_OUT_OF_MEMORY;
}
#ifdef SSL_MODE_RELEASE_BUFFERS
SSL_CTX_set_mode(connssl->ctx, SSL_MODE_RELEASE_BUFFERS);
#endif
#ifdef SSL_CTRL_SET_MSG_CALLBACK
if(data->set.fdebug && data->set.verbose) {
if(!SSL_CTX_callback_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK,
(void (*)(void))ssl_tls_trace)) {
infof(data, "SSL: couldn't set callback!\n");
}
else if(!SSL_CTX_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK_ARG, 0,
conn)) {
infof(data, "SSL: couldn't set callback argument!\n");
}
}
#endif
ctx_options = SSL_OP_ALL;
#ifdef SSL_OP_NO_TICKET
ctx_options |= SSL_OP_NO_TICKET;
#endif
#ifdef SSL_OP_NO_COMPRESSION
ctx_options |= SSL_OP_NO_COMPRESSION;
#endif
#ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
ctx_options &= ~SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG;
#endif
#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
if(!conn->data->set.ssl_enable_beast)
ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
#endif
if(data->set.ssl.version == CURL_SSLVERSION_DEFAULT)
ctx_options |= SSL_OP_NO_SSLv2;
SSL_CTX_set_options(connssl->ctx, ctx_options);
if(data->set.str[STRING_CERT] || data->set.str[STRING_CERT_TYPE]) {
if(!cert_stuff(conn,
connssl->ctx,
data->set.str[STRING_CERT],
data->set.str[STRING_CERT_TYPE],
data->set.str[STRING_KEY],
data->set.str[STRING_KEY_TYPE])) {
return CURLE_SSL_CERTPROBLEM;
}
}
if(data->set.str[STRING_SSL_CIPHER_LIST]) {
if(!SSL_CTX_set_cipher_list(connssl->ctx,
data->set.str[STRING_SSL_CIPHER_LIST])) {
failf(data, "failed setting cipher list");
return CURLE_SSL_CIPHER;
}
}
#ifdef USE_TLS_SRP
if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) {
infof(data, "Using TLS-SRP username: %s\n", data->set.ssl.username);
if(!SSL_CTX_set_srp_username(connssl->ctx, data->set.ssl.username)) {
failf(data, "Unable to set SRP user name");
return CURLE_BAD_FUNCTION_ARGUMENT;
}
if(!SSL_CTX_set_srp_password(connssl->ctx,data->set.ssl.password)) {
failf(data, "failed setting SRP password");
return CURLE_BAD_FUNCTION_ARGUMENT;
}
if(!data->set.str[STRING_SSL_CIPHER_LIST]) {
infof(data, "Setting cipher list SRP\n");
if(!SSL_CTX_set_cipher_list(connssl->ctx, "SRP")) {
failf(data, "failed setting SRP cipher list");
return CURLE_SSL_CIPHER;
}
}
}
#endif
if(data->set.str[STRING_SSL_CAFILE] || data->set.str[STRING_SSL_CAPATH]) {
if(!SSL_CTX_load_verify_locations(connssl->ctx,
data->set.str[STRING_SSL_CAFILE],
data->set.str[STRING_SSL_CAPATH])) {
if(data->set.ssl.verifypeer) {
failf(data,"error setting certificate verify locations:\n"
" CAfile: %s\n CApath: %s",
data->set.str[STRING_SSL_CAFILE]?
data->set.str[STRING_SSL_CAFILE]: "none",
data->set.str[STRING_SSL_CAPATH]?
data->set.str[STRING_SSL_CAPATH] : "none");
return CURLE_SSL_CACERT_BADFILE;
}
else {
infof(data, "error setting certificate verify locations,"
" continuing anyway:\n");
}
}
else {
infof(data, "successfully set certificate verify locations:\n");
}
infof(data,
" CAfile: %s\n"
" CApath: %s\n",
data->set.str[STRING_SSL_CAFILE] ? data->set.str[STRING_SSL_CAFILE]:
"none",
data->set.str[STRING_SSL_CAPATH] ? data->set.str[STRING_SSL_CAPATH]:
"none");
}
if(data->set.str[STRING_SSL_CRLFILE]) {
lookup=X509_STORE_add_lookup(SSL_CTX_get_cert_store(connssl->ctx),
X509_LOOKUP_file());
if(!lookup ||
(!X509_load_crl_file(lookup,data->set.str[STRING_SSL_CRLFILE],
X509_FILETYPE_PEM)) ) {
failf(data,"error loading CRL file: %s",
data->set.str[STRING_SSL_CRLFILE]);
return CURLE_SSL_CRL_BADFILE;
}
else {
infof(data, "successfully load CRL file:\n");
X509_STORE_set_flags(SSL_CTX_get_cert_store(connssl->ctx),
X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
}
infof(data,
" CRLfile: %s\n", data->set.str[STRING_SSL_CRLFILE] ?
data->set.str[STRING_SSL_CRLFILE]: "none");
}
SSL_CTX_set_verify(connssl->ctx,
data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
cert_verify_callback);
if(data->set.ssl.fsslctx) {
retcode = (*data->set.ssl.fsslctx)(data, connssl->ctx,
data->set.ssl.fsslctxp);
if(retcode) {
failf(data,"error signaled by ssl ctx callback");
return retcode;
}
}
if(connssl->handle)
SSL_free(connssl->handle);
connssl->handle = SSL_new(connssl->ctx);
if(!connssl->handle) {
failf(data, "SSL: couldn't create a context (handle)!");
return CURLE_OUT_OF_MEMORY;
}
SSL_set_connect_state(connssl->handle);
connssl->server_cert = 0x0;
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
if((0 == Curl_inet_pton(AF_INET, conn->host.name, &addr)) &&
#ifdef ENABLE_IPV6
(0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr)) &&
#endif
sni &&
!SSL_set_tlsext_host_name(connssl->handle, conn->host.name))
infof(data, "WARNING: failed to configure server name indication (SNI) "
"TLS extension\n");
#endif
if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
if(!SSL_set_session(connssl->handle, ssl_sessionid)) {
failf(data, "SSL: SSL_set_session failed: %s",
ERR_error_string(ERR_get_error(),NULL));
return CURLE_SSL_CONNECT_ERROR;
}
infof (data, "SSL re-using session ID\n");
}
if(!SSL_set_fd(connssl->handle, (int)sockfd)) {
failf(data, "SSL: SSL_set_fd failed: %s",
ERR_error_string(ERR_get_error(),NULL));
return CURLE_SSL_CONNECT_ERROR;
}
connssl->connecting_state = ssl_connect_2;
return CURLE_OK;
}
static CURLcode
ossl_connect_step2(struct connectdata *conn, int sockindex)
{
struct SessionHandle *data = conn->data;
int err;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
DEBUGASSERT(ssl_connect_2 == connssl->connecting_state
|| ssl_connect_2_reading == connssl->connecting_state
|| ssl_connect_2_writing == connssl->connecting_state);
ERR_clear_error();
err = SSL_connect(connssl->handle);
if(1 != err) {
int detail = SSL_get_error(connssl->handle, err);
if(SSL_ERROR_WANT_READ == detail) {
connssl->connecting_state = ssl_connect_2_reading;
return CURLE_OK;
}
else if(SSL_ERROR_WANT_WRITE == detail) {
connssl->connecting_state = ssl_connect_2_writing;
return CURLE_OK;
}
else {
unsigned long errdetail;
char error_buffer[256];
CURLcode rc;
const char *cert_problem = NULL;
long lerr;
connssl->connecting_state = ssl_connect_2;
errdetail = ERR_get_error();
switch(errdetail) {
case 0x1407E086:
case 0x14090086:
rc = CURLE_SSL_CACERT;
lerr = SSL_get_verify_result(connssl->handle);
if(lerr != X509_V_OK) {
snprintf(error_buffer, sizeof(error_buffer),
"SSL certificate problem: %s",
X509_verify_cert_error_string(lerr));
}
else
cert_problem = "SSL certificate problem, verify that the CA cert is"
" OK.";
break;
default:
rc = CURLE_SSL_CONNECT_ERROR;
SSL_strerror(errdetail, error_buffer, sizeof(error_buffer));
break;
}
if(CURLE_SSL_CONNECT_ERROR == rc && errdetail == 0) {
failf(data, "Unknown SSL protocol error in connection to %s:%ld ",
conn->host.name, conn->port);
return rc;
}
failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer);
return rc;
}
}
else {
connssl->connecting_state = ssl_connect_3;
infof (data, "SSL connection using %s\n",
SSL_get_cipher(connssl->handle));
return CURLE_OK;
}
}
static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len)
{
int i, ilen;
if((ilen = (int)len) < 0)
return 1;
i = i2t_ASN1_OBJECT(buf, ilen, a);
if(i >= ilen)
return 1;
return 0;
}
static CURLcode push_certinfo_len(struct SessionHandle *data,
int certnum,
const char *label,
const char *value,
size_t valuelen)
{
struct curl_certinfo *ci = &data->info.certs;
char *output;
struct curl_slist *nl;
CURLcode res = CURLE_OK;
size_t labellen = strlen(label);
size_t outlen = labellen + 1 + valuelen + 1;
output = malloc(outlen);
if(!output)
return CURLE_OUT_OF_MEMORY;
snprintf(output, outlen, "%s:", label);
memcpy(&output[labellen+1], value, valuelen);
output[labellen + 1 + valuelen] = 0;
nl = curl_slist_append(ci->certinfo[certnum], output);
free(output);
if(!nl) {
curl_slist_free_all(ci->certinfo[certnum]);
ci->certinfo[certnum] = NULL;
res = CURLE_OUT_OF_MEMORY;
}
else
ci->certinfo[certnum] = nl;
return res;
}
static CURLcode push_certinfo(struct SessionHandle *data,
int certnum,
const char *label,
const char *value)
{
size_t valuelen = strlen(value);
return push_certinfo_len(data, certnum, label, value, valuelen);
}
static void pubkey_show(struct SessionHandle *data,
int num,
const char *type,
const char *name,
unsigned char *raw,
int len)
{
size_t left;
int i;
char namebuf[32];
char *buffer;
left = len*3 + 1;
buffer = malloc(left);
if(buffer) {
char *ptr=buffer;
snprintf(namebuf, sizeof(namebuf), "%s(%s)", type, name);
for(i=0; i< len; i++) {
snprintf(ptr, left, "%02x:", raw[i]);
ptr += 3;
left -= 3;
}
infof(data, " %s: %s\n", namebuf, buffer);
push_certinfo(data, num, namebuf, buffer);
free(buffer);
}
}
#define print_pubkey_BN(_type, _name, _num) \
do { \
if(pubkey->pkey._type->_name != NULL) { \
int len = BN_num_bytes(pubkey->pkey._type->_name); \
if(len < CERTBUFFERSIZE) { \
BN_bn2bin(pubkey->pkey._type->_name, (unsigned char*)bufp); \
bufp[len] = 0; \
pubkey_show(data, _num, #_type, #_name, (unsigned char*)bufp, len); \
} \
} \
} WHILE_FALSE
static int X509V3_ext(struct SessionHandle *data,
int certnum,
STACK_OF(X509_EXTENSION) *exts)
{
int i;
size_t j;
if(sk_X509_EXTENSION_num(exts) <= 0)
return 1;
for(i=0; i<sk_X509_EXTENSION_num(exts); i++) {
ASN1_OBJECT *obj;
X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i);
BUF_MEM *biomem;
char buf[512];
char *ptr=buf;
char namebuf[128];
BIO *bio_out = BIO_new(BIO_s_mem());
if(!bio_out)
return 1;
obj = X509_EXTENSION_get_object(ext);
asn1_object_dump(obj, namebuf, sizeof(namebuf));
infof(data, "%s: %s\n", namebuf,
X509_EXTENSION_get_critical(ext)?"(critical)":"");
if(!X509V3_EXT_print(bio_out, ext, 0, 0))
M_ASN1_OCTET_STRING_print(bio_out, ext->value);
BIO_get_mem_ptr(bio_out, &biomem);
for(j=0; j<(size_t)biomem->length; j++) {
const char *sep="";
if(biomem->data[j] == '\n') {
sep=", ";
j++;
};
while((biomem->data[j] == ' ') && (j<(size_t)biomem->length))
j++;
if(j<(size_t)biomem->length)
ptr+=snprintf(ptr, sizeof(buf)-(ptr-buf), "%s%c", sep,
biomem->data[j]);
}
infof(data, " %s\n", buf);
push_certinfo(data, certnum, namebuf, buf);
BIO_free(bio_out);
}
return 0;
}
static void X509_signature(struct SessionHandle *data,
int numcert,
ASN1_STRING *sig)
{
char buf[1024];
char *ptr = buf;
int i;
for(i=0; i<sig->length; i++)
ptr+=snprintf(ptr, sizeof(buf)-(ptr-buf), "%02x:", sig->data[i]);
infof(data, " Signature: %s\n", buf);
push_certinfo(data, numcert, "Signature", buf);
}
static void dumpcert(struct SessionHandle *data, X509 *x, int numcert)
{
BIO *bio_out = BIO_new(BIO_s_mem());
BUF_MEM *biomem;
PEM_write_bio_X509(bio_out, x);
BIO_get_mem_ptr(bio_out, &biomem);
infof(data, "%s\n", biomem->data);
push_certinfo_len(data, numcert, "Cert", biomem->data, biomem->length);
BIO_free(bio_out);
}
static int init_certinfo(struct SessionHandle *data,
int num)
{
struct curl_certinfo *ci = &data->info.certs;
struct curl_slist **table;
Curl_ssl_free_certinfo(data);
ci->num_of_certs = num;
table = calloc((size_t)num, sizeof(struct curl_slist *));
if(!table)
return 1;
ci->certinfo = table;
return 0;
}
#define CERTBUFFERSIZE 8192
static CURLcode get_cert_chain(struct connectdata *conn,
struct ssl_connect_data *connssl)
{
STACK_OF(X509) *sk;
int i;
char *bufp;
struct SessionHandle *data = conn->data;
int numcerts;
bufp = malloc(CERTBUFFERSIZE);
if(!bufp)
return CURLE_OUT_OF_MEMORY;
sk = SSL_get_peer_cert_chain(connssl->handle);
if(!sk) {
free(bufp);
return CURLE_OUT_OF_MEMORY;
}
numcerts = sk_X509_num(sk);
if(init_certinfo(data, numcerts)) {
free(bufp);
return CURLE_OUT_OF_MEMORY;
}
infof(data, "--- Certificate chain\n");
for(i=0; i<numcerts; i++) {
long value;
ASN1_INTEGER *num;
ASN1_TIME *certdate;
#if 0
X509 *x = sk_X509_value(sk, numcerts - i - 1);
#else
X509 *x = sk_X509_value(sk, i);
#endif
X509_CINF *cinf;
EVP_PKEY *pubkey=NULL;
int j;
char *ptr;
(void)x509_name_oneline(X509_get_subject_name(x), bufp, CERTBUFFERSIZE);
infof(data, "%2d Subject: %s\n", i, bufp);
push_certinfo(data, i, "Subject", bufp);
(void)x509_name_oneline(X509_get_issuer_name(x), bufp, CERTBUFFERSIZE);
infof(data, " Issuer: %s\n", bufp);
push_certinfo(data, i, "Issuer", bufp);
value = X509_get_version(x);
infof(data, " Version: %lu (0x%lx)\n", value+1, value);
snprintf(bufp, CERTBUFFERSIZE, "%lx", value);
push_certinfo(data, i, "Version", bufp);
num=X509_get_serialNumber(x);
if(num->length <= 4) {
value = ASN1_INTEGER_get(num);
infof(data," Serial Number: %ld (0x%lx)\n", value, value);
snprintf(bufp, CERTBUFFERSIZE, "%lx", value);
}
else {
int left = CERTBUFFERSIZE;
ptr = bufp;
*ptr++ = 0;
if(num->type == V_ASN1_NEG_INTEGER)
*ptr++='-';
for(j=0; (j<num->length) && (left>=4); j++) {
snprintf(ptr, 3, "%02x%c",num->data[j],
((j+1 == num->length)?'\n':':'));
ptr += 3;
left-=4;
}
if(num->length)
infof(data," Serial Number: %s\n", bufp);
else
bufp[0]=0;
}
if(bufp[0])
push_certinfo(data, i, "Serial Number", bufp);
cinf = x->cert_info;
j = asn1_object_dump(cinf->signature->algorithm, bufp, CERTBUFFERSIZE);
if(!j) {
infof(data, " Signature Algorithm: %s\n", bufp);
push_certinfo(data, i, "Signature Algorithm", bufp);
}
certdate = X509_get_notBefore(x);
asn1_output(certdate, bufp, CERTBUFFERSIZE);
infof(data, " Start date: %s\n", bufp);
push_certinfo(data, i, "Start date", bufp);
certdate = X509_get_notAfter(x);
asn1_output(certdate, bufp, CERTBUFFERSIZE);
infof(data, " Expire date: %s\n", bufp);
push_certinfo(data, i, "Expire date", bufp);
j = asn1_object_dump(cinf->key->algor->algorithm, bufp, CERTBUFFERSIZE);
if(!j) {
infof(data, " Public Key Algorithm: %s\n", bufp);
push_certinfo(data, i, "Public Key Algorithm", bufp);
}
pubkey = X509_get_pubkey(x);
if(!pubkey)
infof(data, " Unable to load public key\n");
else {
switch(pubkey->type) {
case EVP_PKEY_RSA:
infof(data, " RSA Public Key (%d bits)\n",
BN_num_bits(pubkey->pkey.rsa->n));
snprintf(bufp, CERTBUFFERSIZE, "%d", BN_num_bits(pubkey->pkey.rsa->n));
push_certinfo(data, i, "RSA Public Key", bufp);
print_pubkey_BN(rsa, n, i);
print_pubkey_BN(rsa, e, i);
print_pubkey_BN(rsa, d, i);
print_pubkey_BN(rsa, p, i);
print_pubkey_BN(rsa, q, i);
print_pubkey_BN(rsa, dmp1, i);
print_pubkey_BN(rsa, dmq1, i);
print_pubkey_BN(rsa, iqmp, i);
break;
case EVP_PKEY_DSA:
print_pubkey_BN(dsa, p, i);
print_pubkey_BN(dsa, q, i);
print_pubkey_BN(dsa, g, i);
print_pubkey_BN(dsa, priv_key, i);
print_pubkey_BN(dsa, pub_key, i);
break;
case EVP_PKEY_DH:
print_pubkey_BN(dh, p, i);
print_pubkey_BN(dh, g, i);
print_pubkey_BN(dh, priv_key, i);
print_pubkey_BN(dh, pub_key, i);
break;
#if 0
case EVP_PKEY_EC:
break;
#endif
}
EVP_PKEY_free(pubkey);
}
X509V3_ext(data, i, cinf->extensions);
X509_signature(data, i, x->signature);
dumpcert(data, x, i);
}
free(bufp);
return CURLE_OK;
}
static CURLcode servercert(struct connectdata *conn,
struct ssl_connect_data *connssl,
bool strict)
{
CURLcode retcode = CURLE_OK;
int rc;
long lerr;
ASN1_TIME *certdate;
struct SessionHandle *data = conn->data;
X509 *issuer;
FILE *fp;
char *buffer = data->state.buffer;
if(data->set.ssl.certinfo)
(void)get_cert_chain(conn, connssl);
data->set.ssl.certverifyresult = !X509_V_OK;
connssl->server_cert = SSL_get_peer_certificate(connssl->handle);
if(!connssl->server_cert) {
if(strict)
failf(data, "SSL: couldn't get peer certificate!");
return CURLE_PEER_FAILED_VERIFICATION;
}
infof (data, "Server certificate:\n");
rc = x509_name_oneline(X509_get_subject_name(connssl->server_cert),
buffer, BUFSIZE);
if(rc) {
if(strict)
failf(data, "SSL: couldn't get X509-subject!");
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
return CURLE_SSL_CONNECT_ERROR;
}
infof(data, "\t subject: %s\n", buffer);
certdate = X509_get_notBefore(connssl->server_cert);
asn1_output(certdate, buffer, BUFSIZE);
infof(data, "\t start date: %s\n", buffer);
certdate = X509_get_notAfter(connssl->server_cert);
asn1_output(certdate, buffer, BUFSIZE);
infof(data, "\t expire date: %s\n", buffer);
if(data->set.ssl.verifyhost) {
retcode = verifyhost(conn, connssl->server_cert);
if(retcode) {
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
return retcode;
}
}
rc = x509_name_oneline(X509_get_issuer_name(connssl->server_cert),
buffer, BUFSIZE);
if(rc) {
if(strict)
failf(data, "SSL: couldn't get X509-issuer name!");
retcode = CURLE_SSL_CONNECT_ERROR;
}
else {
infof(data, "\t issuer: %s\n", buffer);
if(data->set.str[STRING_SSL_ISSUERCERT]) {
fp=fopen(data->set.str[STRING_SSL_ISSUERCERT],"r");
if(!fp) {
if(strict)
failf(data, "SSL: Unable to open issuer cert (%s)",
data->set.str[STRING_SSL_ISSUERCERT]);
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
return CURLE_SSL_ISSUER_ERROR;
}
issuer = PEM_read_X509(fp,NULL,ZERO_NULL,NULL);
if(!issuer) {
if(strict)
failf(data, "SSL: Unable to read issuer cert (%s)",
data->set.str[STRING_SSL_ISSUERCERT]);
X509_free(connssl->server_cert);
X509_free(issuer);
fclose(fp);
return CURLE_SSL_ISSUER_ERROR;
}
fclose(fp);
if(X509_check_issued(issuer,connssl->server_cert) != X509_V_OK) {
if(strict)
failf(data, "SSL: Certificate issuer check failed (%s)",
data->set.str[STRING_SSL_ISSUERCERT]);
X509_free(connssl->server_cert);
X509_free(issuer);
connssl->server_cert = NULL;
return CURLE_SSL_ISSUER_ERROR;
}
infof(data, "\t SSL certificate issuer check ok (%s)\n",
data->set.str[STRING_SSL_ISSUERCERT]);
X509_free(issuer);
}
lerr = data->set.ssl.certverifyresult=
SSL_get_verify_result(connssl->handle);
if(data->set.ssl.certverifyresult != X509_V_OK) {
if(data->set.ssl.verifypeer) {
if(strict)
failf(data, "SSL certificate verify result: %s (%ld)",
X509_verify_cert_error_string(lerr), lerr);
retcode = CURLE_PEER_FAILED_VERIFICATION;
}
else
infof(data, "\t SSL certificate verify result: %s (%ld),"
" continuing anyway.\n",
X509_verify_cert_error_string(lerr), lerr);
}
else
infof(data, "\t SSL certificate verify ok.\n");
}
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
connssl->connecting_state = ssl_connect_done;
return retcode;
}
static CURLcode
ossl_connect_step3(struct connectdata *conn,
int sockindex)
{
CURLcode retcode = CURLE_OK;
void *old_ssl_sessionid=NULL;
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
int incache;
SSL_SESSION *our_ssl_sessionid;
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
#ifdef HAVE_SSL_GET1_SESSION
our_ssl_sessionid = SSL_get1_session(connssl->handle);
#else
our_ssl_sessionid = SSL_get_session(connssl->handle);
#endif
incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL));
if(incache) {
if(old_ssl_sessionid != our_ssl_sessionid) {
infof(data, "old SSL session ID is stale, removing\n");
Curl_ssl_delsessionid(conn, old_ssl_sessionid);
incache = FALSE;
}
}
if(!incache) {
retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
0 );
if(retcode) {
failf(data, "failed to store ssl session");
return retcode;
}
}
#ifdef HAVE_SSL_GET1_SESSION
else {
SSL_SESSION_free(our_ssl_sessionid);
}
#endif
if(!data->set.ssl.verifypeer)
(void)servercert(conn, connssl, FALSE);
else
retcode = servercert(conn, connssl, TRUE);
if(CURLE_OK == retcode)
connssl->connecting_state = ssl_connect_done;
return retcode;
}
static Curl_recv ossl_recv;
static Curl_send ossl_send;
static CURLcode
ossl_connect_common(struct connectdata *conn,
int sockindex,
bool nonblocking,
bool *done)
{
CURLcode retcode;
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
curl_socket_t sockfd = conn->sock[sockindex];
long timeout_ms;
int what;
if(ssl_connection_complete == connssl->state) {
*done = TRUE;
return CURLE_OK;
}
if(ssl_connect_1==connssl->connecting_state) {
timeout_ms = Curl_timeleft(data, NULL, TRUE);
if(timeout_ms < 0) {
failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEDOUT;
}
retcode = ossl_connect_step1(conn, sockindex);
if(retcode)
return retcode;
}
while(ssl_connect_2 == connssl->connecting_state ||
ssl_connect_2_reading == connssl->connecting_state ||
ssl_connect_2_writing == connssl->connecting_state) {
timeout_ms = Curl_timeleft(data, NULL, TRUE);
if(timeout_ms < 0) {
failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEDOUT;
}
if(connssl->connecting_state == ssl_connect_2_reading
|| connssl->connecting_state == ssl_connect_2_writing) {
curl_socket_t writefd = ssl_connect_2_writing==
connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
curl_socket_t readfd = ssl_connect_2_reading==
connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
what = Curl_socket_ready(readfd, writefd, nonblocking?0:timeout_ms);
if(what < 0) {
failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
return CURLE_SSL_CONNECT_ERROR;
}
else if(0 == what) {
if(nonblocking) {
*done = FALSE;
return CURLE_OK;
}
else {
failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEDOUT;
}
}
}
retcode = ossl_connect_step2(conn, sockindex);
if(retcode || (nonblocking &&
(ssl_connect_2 == connssl->connecting_state ||
ssl_connect_2_reading == connssl->connecting_state ||
ssl_connect_2_writing == connssl->connecting_state)))
return retcode;
}
if(ssl_connect_3==connssl->connecting_state) {
retcode = ossl_connect_step3(conn, sockindex);
if(retcode)
return retcode;
}
if(ssl_connect_done==connssl->connecting_state) {
connssl->state = ssl_connection_complete;
conn->recv[sockindex] = ossl_recv;
conn->send[sockindex] = ossl_send;
*done = TRUE;
}
else
*done = FALSE;
connssl->connecting_state = ssl_connect_1;
return CURLE_OK;
}
CURLcode
Curl_ossl_connect_nonblocking(struct connectdata *conn,
int sockindex,
bool *done)
{
return ossl_connect_common(conn, sockindex, TRUE, done);
}
CURLcode
Curl_ossl_connect(struct connectdata *conn,
int sockindex)
{
CURLcode retcode;
bool done = FALSE;
retcode = ossl_connect_common(conn, sockindex, FALSE, &done);
if(retcode)
return retcode;
DEBUGASSERT(done);
return CURLE_OK;
}
bool Curl_ossl_data_pending(const struct connectdata *conn,
int connindex)
{
if(conn->ssl[connindex].handle)
return (0 != SSL_pending(conn->ssl[connindex].handle)) ? TRUE : FALSE;
else
return FALSE;
}
static ssize_t ossl_send(struct connectdata *conn,
int sockindex,
const void *mem,
size_t len,
CURLcode *curlcode)
{
int err;
char error_buffer[120];
unsigned long sslerror;
int memlen;
int rc;
ERR_clear_error();
memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
rc = SSL_write(conn->ssl[sockindex].handle, mem, memlen);
if(rc < 0) {
err = SSL_get_error(conn->ssl[sockindex].handle, rc);
switch(err) {
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
*curlcode = CURLE_AGAIN;
return -1;
case SSL_ERROR_SYSCALL:
failf(conn->data, "SSL_write() returned SYSCALL, errno = %d",
SOCKERRNO);
*curlcode = CURLE_SEND_ERROR;
return -1;
case SSL_ERROR_SSL:
sslerror = ERR_get_error();
failf(conn->data, "SSL_write() error: %s",
ERR_error_string(sslerror, error_buffer));
*curlcode = CURLE_SEND_ERROR;
return -1;
}
failf(conn->data, "SSL_write() return error %d", err);
*curlcode = CURLE_SEND_ERROR;
return -1;
}
return (ssize_t)rc;
}
static ssize_t ossl_recv(struct connectdata *conn,
int num,
char *buf,
size_t buffersize,
CURLcode *curlcode)
{
char error_buffer[120];
unsigned long sslerror;
ssize_t nread;
int buffsize;
ERR_clear_error();
buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
nread = (ssize_t)SSL_read(conn->ssl[num].handle, buf, buffsize);
if(nread < 0) {
int err = SSL_get_error(conn->ssl[num].handle, (int)nread);
switch(err) {
case SSL_ERROR_NONE:
case SSL_ERROR_ZERO_RETURN:
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
*curlcode = CURLE_AGAIN;
return -1;
default:
sslerror = ERR_get_error();
failf(conn->data, "SSL read: %s, errno %d",
ERR_error_string(sslerror, error_buffer),
SOCKERRNO);
*curlcode = CURLE_RECV_ERROR;
return -1;
}
}
return nread;
}
size_t Curl_ossl_version(char *buffer, size_t size)
{
#ifdef YASSL_VERSION
return snprintf(buffer, size, "yassl/%s", YASSL_VERSION);
#else
#if(SSLEAY_VERSION_NUMBER >= 0x905000)
{
char sub[2];
unsigned long ssleay_value;
sub[1]='\0';
ssleay_value=SSLeay();
if(ssleay_value < 0x906000) {
ssleay_value=SSLEAY_VERSION_NUMBER;
sub[0]='\0';
}
else {
if(ssleay_value&0xff0) {
sub[0]=(char)(((ssleay_value>>4)&0xff) + 'a' -1);
}
else
sub[0]='\0';
}
return snprintf(buffer, size, "OpenSSL/%lx.%lx.%lx%s",
(ssleay_value>>28)&0xf,
(ssleay_value>>20)&0xff,
(ssleay_value>>12)&0xff,
sub);
}
#else
#if(SSLEAY_VERSION_NUMBER >= 0x900000)
return snprintf(buffer, size, "OpenSSL/%lx.%lx.%lx",
(SSLEAY_VERSION_NUMBER>>28)&0xff,
(SSLEAY_VERSION_NUMBER>>20)&0xff,
(SSLEAY_VERSION_NUMBER>>12)&0xf);
#else
{
char sub[2];
sub[1]='\0';
if(SSLEAY_VERSION_NUMBER&0x0f) {
sub[0]=(SSLEAY_VERSION_NUMBER&0x0f) + 'a' -1;
}
else
sub[0]='\0';
return snprintf(buffer, size, "SSL/%x.%x.%x%s",
(SSLEAY_VERSION_NUMBER>>12)&0xff,
(SSLEAY_VERSION_NUMBER>>8)&0xf,
(SSLEAY_VERSION_NUMBER>>4)&0xf, sub);
}
#endif
#endif
#endif
}
void Curl_ossl_random(struct SessionHandle *data, unsigned char *entropy,
size_t length)
{
Curl_ossl_seed(data);
RAND_bytes(entropy, curlx_uztosi(length));
}
void Curl_ossl_md5sum(unsigned char *tmp,
size_t tmplen,
unsigned char *md5sum ,
size_t unused)
{
MD5_CTX MD5pw;
(void)unused;
MD5_Init(&MD5pw);
MD5_Update(&MD5pw, tmp, tmplen);
MD5_Final(md5sum, &MD5pw);
}
#endif