links/https.c

905 lines
23 KiB
C

/* https.c
* HTTPS protocol client implementation
* (c) 2002 Mikulas Patocka
* This file is a part of the Links program, released under GPL.
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations
* including the two.
* You must obey the GNU General Public License in all respects
* for all of the code used other than OpenSSL. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you
* do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source
* files in the program, then also delete it here.
*/
#include "links.h"
#ifndef PATH_MAX
#define PATH_MAX 255
#endif
#ifdef HAVE_SSL
#ifndef LINKS_CRT_FILE
#define LINKS_CRT_FILE links.crt
#endif
#ifdef HAVE_BUILTIN_SSL_CERTIFICATES
#include "certs.inc"
#define N_SSL_CONTEXTS 2
#else
#define N_SSL_CONTEXTS 1
#endif
static int ssl_initialized = 0;
static SSL_CTX *contexts[N_SSL_CONTEXTS];
#ifdef HAVE_CRYPTO_SET_MEM_FUNCTIONS_1
#define file_line_arg
#define pass_file_line
#else
#define file_line_arg , const char *file, int line
#define pass_file_line , file, line
#endif
#ifdef HAVE_CRYPTO_SET_MEM_FUNCTIONS
static unsigned in_ssl_malloc_hook = 0;
static void *malloc_hook(size_t size file_line_arg)
{
void *p;
in_ssl_malloc_hook++;
#if defined(OS2_ADVANCED_HEAP)
if (!size) size = 1;
do p = os2_orig_malloc(size); while (!p && out_of_memory(0, NULL, 0));
#elif !defined(HAVE_OPENSSL_CLEANUP) || defined(HAVE_CRYPTO_SET_MEM_FUNCTIONS_1)
if (!size) size = 1;
do p = malloc(size); while (!p && out_of_memory(0, NULL, 0));
#elif defined(LEAK_DEBUG)
p = debug_mem_alloc(cast_uchar file, line, size, 1);
#else
p = mem_alloc_mayfail(size);
#endif
in_ssl_malloc_hook--;
return p;
}
static void *realloc_hook(void *ptr, size_t size file_line_arg)
{
void *p;
if (!ptr) return malloc_hook(size pass_file_line);
in_ssl_malloc_hook++;
#if !defined(HAVE_OPENSSL_CLEANUP) || defined(HAVE_CRYPTO_SET_MEM_FUNCTIONS_1) || defined(OS2_ADVANCED_HEAP)
if (!size) size = 1;
do p = realloc(ptr, size); while (!p && out_of_memory(0, NULL, 0));
#elif defined(LEAK_DEBUG)
p = debug_mem_realloc(cast_uchar file, line, ptr, size, 1);
#else
p = mem_realloc_mayfail(ptr, size);
#endif
in_ssl_malloc_hook--;
return p;
}
static void free_hook(void *ptr file_line_arg)
{
if (!ptr) return;
#if !defined(HAVE_OPENSSL_CLEANUP) || defined(HAVE_CRYPTO_SET_MEM_FUNCTIONS_1) || defined(OS2_ADVANCED_HEAP)
free(ptr);
#elif defined(LEAK_DEBUG)
debug_mem_free(cast_uchar file, line, ptr);
#else
mem_free(ptr);
#endif
}
#endif
#if defined(HAVE_SSL_CERTIFICATES) && (defined(DOS) || defined(OS2) || defined(WIN) || defined(OPENVMS))
static int ssl_set_private_paths(SSL_CTX *ctx)
{
unsigned char *path, *c;
int r;
#ifdef OPENVMS
path = stracpy(cast_uchar g_argv[0]);
#else
path = stracpy(path_to_exe);
#endif
for (c = path + strlen(cast_const_char path); c > path; c--) {
if (dir_sep(c[-1])
#ifdef OPENVMS
|| c[-1] == ']' || c[-1] == ':'
#endif
)
break;
}
c[0] = 0;
add_to_strn(&path, cast_uchar stringify(LINKS_CRT_FILE));
r = SSL_CTX_load_verify_locations(ctx, cast_const_char path, NULL);
mem_free(path);
clear_ssl_errors(__LINE__);
if (r != 1)
return -1;
return 0;
}
#else
#define ssl_set_private_paths(c) (-1)
#endif
#ifdef HAVE_BUILTIN_SSL_CERTIFICATES
static void ssl_load_private_certificates(SSL_CTX *ctx)
{
int i;
int errs = 0;
int succeeded = 0;
int total_certificates = (int)array_elements(certificates);
X509_STORE *store = SSL_CTX_get_cert_store(ctx);
if (!store)
errs |= 1;
else for (i = 0; i < total_certificates; i++) {
BIO *bio;
X509 *cert;
unsigned char *data = cast_uchar certificates[i].data;
int len = certificates[i].len;
unsigned char *b64;
if (data[len])
internal_error("invalid builtin certificate %u", i);
#if 1
b64 = base64_encode(data, len, cast_uchar "-----BEGIN CERTIFICATE-----\n", cast_uchar "-----END CERTIFICATE-----", 6);
bio = BIO_new_mem_buf(b64, (int)strlen(cast_const_char b64));
#else
{
static_const unsigned char base64_chars[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int l, x;
int col = 0;
b64 = init_str();
l = 0;
add_to_str(&b64, &l, cast_uchar "-----BEGIN CERTIFICATE-----\n");
for (x = 0; x < len; x += 3) {
unsigned char out[4];
out[0] = base64_chars[data[x] >> 2];
out[1] = base64_chars[((data[x] << 4) & 63) | (data[x + 1] >> 4)];
if (len - x > 1)
out[2] = base64_chars[((data[x + 1] << 2) & 63) | (data[x + 2] >> 6)];
else
out[2] = '=';
if (len - x > 2)
out[3] = base64_chars[data[x + 2] & 63];
else
out[3] = '=';
add_bytes_to_str(&b64, &l, out, 4);
if (!((col += 4) & 63))
add_chr_to_str(&b64, &l, '\n');
}
if (b64[l - 1] != '\n')
add_chr_to_str(&b64, &l, '\n');
add_to_str(&b64, &l, cast_uchar "-----END CERTIFICATE-----");
bio = BIO_new_mem_buf(b64, l);
}
#endif
/*fprintf(stderr, "%s\n", b64);*/
if (!bio) {
errs |= 2;
mem_free(b64);
continue;
}
cert = PEM_read_bio_X509(bio, NULL, 0, NULL);
if (cert) {
if (!X509_STORE_add_cert(store, cert)) {
errs |= 8;
} else {
succeeded++;
}
X509_free(cert);
} else {
errs |= 4;
}
mem_free(b64);
BIO_free(bio);
clear_ssl_errors(__LINE__);
}
if (errs) {
static_const char * const err_strings[4] = { "SSL_CTX_get_cert_store", "BIO_new_mem_buf", "PEM_read_bio_X509", "X509_STORE_add_cert" };
struct session *ses;
unsigned char *err_str = init_str();
int err_strl = 0;
unsigned char *numfail_str = init_str();
int numfail_strl = 0;
int e;
ses = get_download_ses(NULL);
for (e = 0; e < 4; e++) {
if (errs & (1 << e)) {
if (err_strl) add_to_str(&err_str, &err_strl, cast_uchar ", ");
add_to_str(&err_str, &err_strl, cast_uchar err_strings[e]);
}
}
add_num_to_str(&numfail_str, &numfail_strl, total_certificates - succeeded);
add_chr_to_str(&numfail_str, &numfail_strl, '/');
add_num_to_str(&numfail_str, &numfail_strl, total_certificates);
if (!ses) {
error("error initializing built-in certificates: %s, failed %s", err_str, numfail_str);
mem_free(err_str);
mem_free(numfail_str);
} else {
msg_box(ses->term, getml(err_str, numfail_str, NULL), TEXT_(T_SSL_ERROR), AL_CENTER, TEXT_(T_ERROR_INITIALIZING_BUILT_IN_CERTIFICATES), ": ", err_str, ", ", TEXT_(T_FAILED), " ", numfail_str, MSG_BOX_END, NULL, 1, TEXT_(T_CANCEL), msg_box_null, B_ENTER | B_ESC);
}
}
}
#endif
int ssl_asked_for_password;
static int ssl_password_callback(char *buf, int size, int rwflag, void *userdata)
{
ssl_asked_for_password = 1;
if (size > (int)strlen(cast_const_char ssl_options.client_cert_password))
size = (int)strlen(cast_const_char ssl_options.client_cert_password);
memcpy(buf, ssl_options.client_cert_password, size);
return size;
}
links_ssl *getSSL(void)
{
int idx;
links_ssl *ssl;
if (!ssl_initialized) {
memset(contexts, 0, sizeof contexts);
#ifdef HAVE_CRYPTO_SET_MEM_FUNCTIONS
CRYPTO_set_mem_functions(malloc_hook, realloc_hook, free_hook);
#endif
#if defined(HAVE_RAND_EGD) && defined(HAVE_RAND_FILE_NAME) && defined(HAVE_RAND_LOAD_FILE) && defined(HAVE_RAND_WRITE_FILE)
{
unsigned char f_randfile[PATH_MAX];
const unsigned char *f = (const unsigned char *)RAND_file_name(cast_char f_randfile, sizeof(f_randfile));
if (f && RAND_egd(cast_const_char f) < 0) {
/* Not an EGD, so read and write to it */
if (RAND_load_file(cast_const_char f_randfile, -1))
RAND_write_file(cast_const_char f_randfile);
}
}
#endif
#if defined(HAVE_RAND_ADD)
{
unsigned char *os_pool;
int os_pool_size;
os_seed_random(&os_pool, &os_pool_size);
if (os_pool_size) RAND_add(os_pool, os_pool_size, os_pool_size);
mem_free(os_pool);
}
#endif
#if defined(HAVE_OPENSSL_INIT_SSL)
OPENSSL_init_ssl(0, NULL);
#elif defined(OpenSSL_add_ssl_algorithms)
OpenSSL_add_ssl_algorithms();
#else
SSLeay_add_ssl_algorithms();
#endif
ssl_initialized = 1;
}
idx = 0;
#ifdef HAVE_BUILTIN_SSL_CERTIFICATES
if (ssl_options.built_in_certificates || proxies.only_proxies)
idx = 1;
#endif
if (!contexts[idx]) {
SSL_CTX *ctx;
const SSL_METHOD *m;
m = SSLv23_client_method();
if (!m) return NULL;
contexts[idx] = ctx = SSL_CTX_new((void *)m);
if (!ctx) return NULL;
#ifndef SSL_OP_NO_COMPRESSION
#define SSL_OP_NO_COMPRESSION 0
#endif
SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_COMPRESSION);
#ifdef SSL_MODE_ENABLE_PARTIAL_WRITE
SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
#endif
#ifdef SSL_CTX_set_min_proto_version
#if defined(SSL3_VERSION)
SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION);
#elif defined(TLS1_VERSION)
SSL_CTX_set_min_proto_version(ctx, TLS1_VERSION);
#elif defined(TLS1_1_VERSION)
SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION);
#elif defined(TLS1_2_VERSION)
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
#endif
#endif
if (!idx) {
if (ssl_set_private_paths(ctx))
SSL_CTX_set_default_verify_paths(ctx);
} else {
#ifdef HAVE_BUILTIN_SSL_CERTIFICATES
ssl_load_private_certificates(ctx);
#endif
}
#if defined(HAVE_X509_VERIFY_PARAM_SET_FLAGS) && defined(X509_V_FLAG_TRUSTED_FIRST)
#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10003000
X509_VERIFY_PARAM_set_flags(SSL_CTX_get_cert_store(ctx)->param, X509_V_FLAG_TRUSTED_FIRST);
#else
/*X509_VERIFY_PARAM_set_flags(X509_STORE_get0_param(SSL_CTX_get_cert_store(ctx)), X509_V_FLAG_TRUSTED_FIRST);*/
#endif
#endif
SSL_CTX_set_default_passwd_cb(ctx, ssl_password_callback);
}
ssl = mem_alloc_mayfail(sizeof(links_ssl));
if (!ssl)
return NULL;
ssl->ctx = contexts[idx];
ssl->ssl = SSL_new(ssl->ctx);
clear_ssl_errors(__LINE__);
if (!ssl->ssl) {
mem_free(ssl);
return NULL;
}
ssl->bytes_read = ssl->bytes_written = 0;
ssl->session_set = 0;
ssl->session_retrieved = 0;
ssl->ca = NULL;
return ssl;
}
void freeSSL(links_ssl *ssl)
{
if (!ssl || ssl == DUMMY)
return;
#ifdef SSL_SESSION_RESUME
{
int r;
SSL_set_quiet_shutdown(ssl->ssl, 1);
r = SSL_shutdown(ssl->ssl);
if (r < 0)
clear_ssl_errors(__LINE__);
}
#endif
SSL_free(ssl->ssl);
if (ssl->ca) mem_free(ssl->ca);
mem_free(ssl);
}
void ssl_finish(void)
{
int i;
for (i = 0; i < N_SSL_CONTEXTS; i++) {
if (contexts[i]) {
SSL_CTX_free(contexts[i]);
contexts[i] = NULL;
}
}
if (ssl_initialized) {
clear_ssl_errors(__LINE__);
#ifdef HAVE_OPENSSL_CLEANUP
OPENSSL_cleanup();
#endif
ssl_initialized = 0;
}
}
void https_func(struct connection *c)
{
c->ssl = DUMMY;
http_func(c);
}
#ifdef HAVE_SSL_CERTIFICATES
#ifdef SUPPORT_IPV6
static int is_numeric_ipv6_address(unsigned char *name, unsigned char address[16])
{
unsigned char *n;
int r;
size_t nl = strlen(cast_const_char name);
if (name[0] == '[' && name[nl - 1] == ']') {
n = memacpy(name + 1, nl - 2);
} else {
n = stracpy(name);
}
r = numeric_ipv6_address(n, address, NULL);
mem_free(n);
return r;
}
#endif
#if !(defined(HAVE_X509_CHECK_HOST) && defined(HAVE_X509_CHECK_IP))
static int check_host_name(const unsigned char *templ, const unsigned char *host)
{
int templ_len = (int)strlen(cast_const_char templ);
int host_len = (int)strlen(cast_const_char host);
unsigned char *wildcard;
if (templ_len > 0 && templ[templ_len - 1] == '.') templ_len--;
if (host_len > 0 && host[host_len - 1] == '.') host_len--;
wildcard = memchr(templ, '*', templ_len);
if (!wildcard) {
if (templ_len == host_len && !casecmp(templ, host, templ_len))
return 0;
return -1;
} else {
int prefix_len, suffix_len;
if (templ_len > host_len)
return -1;
prefix_len = (int)(wildcard - templ);
suffix_len = (int)(templ + templ_len - (wildcard + 1));
if (memchr(templ, '.', prefix_len))
return -1;
if (memchr(wildcard + 1, '*', suffix_len))
return -1;
if (casecmp(host, templ, prefix_len))
return -1;
if (memchr(host + prefix_len, '.', host_len - prefix_len - suffix_len))
return -1;
if (casecmp(host + host_len - suffix_len, wildcard + 1, suffix_len))
return -1;
return 0;
}
}
#ifdef HAVE_ASN1_STRING_GET0_DATA
#define asn_string_data ASN1_STRING_get0_data
#else
#define asn_string_data ASN1_STRING_data
#endif
/*
* This function is based on verifyhost in libcurl - I hope that it is correct.
*/
static int verify_ssl_host_name(X509 *server_cert, unsigned char *host)
{
unsigned char ipv4_address[4];
#ifdef SUPPORT_IPV6
unsigned char ipv6_address[16];
#endif
unsigned char *address = NULL;
int address_len = 0;
int type = GEN_DNS;
STACK_OF(GENERAL_NAME) *altnames;
if (!numeric_ip_address(host, ipv4_address)) {
address = ipv4_address;
address_len = 4;
type = GEN_IPADD;
}
#ifdef SUPPORT_IPV6
if (!is_numeric_ipv6_address(host, ipv6_address)) {
address = ipv6_address;
address_len = 16;
type = GEN_IPADD;
}
#endif
#if 1
altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL);
if (altnames) {
int retval = 1;
int i;
int n_altnames = sk_GENERAL_NAME_num(altnames);
for (i = 0; i < n_altnames; i++) {
const GENERAL_NAME *altname = sk_GENERAL_NAME_value(altnames, i);
const unsigned char *altname_ptr;
int altname_len;
if (altname->type != type) {
if (altname->type == GEN_IPADD || altname->type == GEN_DNS || altname->type == GEN_URI)
retval = S_INVALID_CERTIFICATE;
continue;
}
altname_ptr = asn_string_data(altname->d.ia5);
altname_len = ASN1_STRING_length(altname->d.ia5);
if (type == GEN_IPADD) {
if (altname_len == address_len && !memcmp(altname_ptr, address, address_len)) {
retval = 0;
break;
}
} else {
if (altname_len == (int)strlen(cast_const_char altname_ptr) && !check_host_name(altname_ptr, host)) {
retval = 0;
break;
}
}
retval = S_INVALID_CERTIFICATE;
}
GENERAL_NAMES_free(altnames);
if (retval != 1)
return retval;
}
#endif
{
unsigned char *nulstr = cast_uchar "";
unsigned char *peer_CN = nulstr;
X509_NAME *name;
int j, i = -1;
retval = 1;
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, asn_string_data(tmp), j);
peer_CN[j] = '\0';
}
}
} else {
j = ASN1_STRING_to_UTF8(&peer_CN, tmp);
}
if (peer_CN && (int)strlen(cast_const_char peer_CN) != j) {
retval = S_INVALID_CERTIFICATE;
}
}
}
if (peer_CN && peer_CN != nulstr) {
if (retval == 1 && !check_host_name(peer_CN, host))
retval = 0;
OPENSSL_free(peer_CN);
}
if (retval != 1)
return retval;
}
return S_INVALID_CERTIFICATE;
}
#else
static int verify_ssl_host_name(X509 *server_cert, unsigned char *host)
{
int v;
unsigned char ipv4_address[4];
#ifdef SUPPORT_IPV6
unsigned char ipv6_address[16];
#endif
if (!numeric_ip_address(host, ipv4_address)) {
v = X509_check_ip(server_cert, ipv4_address, 4, 0);
}
#ifdef SUPPORT_IPV6
else if (!is_numeric_ipv6_address(host, ipv6_address)) {
v = X509_check_ip(server_cert, ipv6_address, 16, 0);
}
#endif
else {
v = X509_check_host(server_cert, cast_const_char host, strlen(cast_const_char host), 0, NULL);
}
return v == 1 ? 0 : S_INVALID_CERTIFICATE;
}
#endif
static unsigned char *extract_field(unsigned char *str, unsigned char *field)
{
size_t len;
unsigned char *f = cast_uchar strstr(cast_const_char str, cast_const_char field);
if (!f)
return NULL;
f += strlen(cast_const_char field);
len = strcspn(cast_const_char f, "/");
return memacpy(f, len);
}
static unsigned char *extract_ca(unsigned char *str)
{
unsigned char *c, *o;
c = extract_field(str, cast_uchar "/C=");
o = extract_field(str, cast_uchar "/O=");
if (!o)
o = extract_field(str, cast_uchar "/CN=");
if (!o) {
if (c) mem_free(c), c = NULL;
o = stracpy(str);
}
if (c) {
add_to_strn(&o, cast_uchar ", ");
add_to_strn(&o, c);
mem_free(c);
}
return o;
}
int verify_ssl_certificate(links_ssl *ssl, unsigned char *host)
{
X509 *server_cert;
int ret;
if (ssl->ca != NULL)
mem_free(ssl->ca), ssl->ca = NULL;
if (SSL_get_verify_result(ssl->ssl) != X509_V_OK) {
clear_ssl_errors(__LINE__);
return S_INVALID_CERTIFICATE;
}
#ifdef HAVE_SSL_GET1_PEER_CERTIFICATE
server_cert = SSL_get1_peer_certificate(ssl->ssl);
#else
server_cert = SSL_get_peer_certificate(ssl->ssl);
#endif
if (!server_cert) {
clear_ssl_errors(__LINE__);
return S_INVALID_CERTIFICATE;
}
ret = verify_ssl_host_name(server_cert, host);
if (!ret) {
#ifdef SSL_GET0_VERIFIED_CHAIN
STACK_OF(X509) *certs = SSL_get0_verified_chain(ssl->ssl);
#else
STACK_OF(X509) *certs = SSL_get_peer_cert_chain(ssl->ssl);
#endif
if (certs) {
int num = sk_X509_num(certs);
int i;
unsigned char *last_ca = NULL;
unsigned char *cas = init_str();
int casl = 0;
for (i = num - 1; i >= 0; i--) {
unsigned char space[3072];
unsigned char *n;
X509 *cert = sk_X509_value(certs, i);
X509_NAME *name;
name = X509_get_issuer_name(cert);
n = cast_uchar X509_NAME_oneline(name, cast_char space, 3072);
if (n) {
unsigned char *ca = extract_ca(n);
if (!last_ca || strcmp(cast_const_char ca, cast_const_char last_ca)) {
if (casl)
add_to_str(&cas, &casl, CERT_RIGHT_ARROW);
add_to_str(&cas, &casl, ca);
if (last_ca)
mem_free(last_ca);
last_ca = ca;
} else {
mem_free(ca);
}
}
}
if (last_ca)
mem_free(last_ca);
if (casl)
ssl->ca = cas;
else
mem_free(cas);
}
}
X509_free(server_cert);
clear_ssl_errors(__LINE__);
return ret;
}
int verify_ssl_cipher(links_ssl *ssl)
{
unsigned char *method;
unsigned char *cipher;
method = cast_uchar SSL_get_version(ssl->ssl);
if (!strncmp(cast_const_char method, "SSL", 3))
return S_INSECURE_CIPHER;
if (SSL_get_cipher_bits(ssl->ssl, NULL) < 112)
return S_INSECURE_CIPHER;
cipher = cast_uchar SSL_get_cipher_name(ssl->ssl);
if (cipher) {
if (strstr(cast_const_char cipher, "RC4"))
return S_INSECURE_CIPHER;
if (strstr(cast_const_char cipher, "NULL"))
return S_INSECURE_CIPHER;
}
return 0;
}
#endif
int ssl_not_reusable(links_ssl *ssl)
{
unsigned char *cipher;
if (!ssl || ssl == DUMMY)
return 0;
ssl->bytes_read = (ssl->bytes_read + 4095) & ~4095;
ssl->bytes_written = (ssl->bytes_written + 4095) & ~4095;
cipher = cast_uchar SSL_get_cipher_name(ssl->ssl);
if (cipher) {
if (strstr(cast_const_char cipher, "RC4-") ||
strstr(cast_const_char cipher, "DES-") ||
strstr(cast_const_char cipher, "RC2-") ||
strstr(cast_const_char cipher, "IDEA-") ||
strstr(cast_const_char cipher, "GOST-")) {
return ssl->bytes_read + ssl->bytes_written >= 1 << 20;
}
}
return 0;
}
unsigned char *get_cipher_string(links_ssl *ssl)
{
unsigned char *version, *cipher;
unsigned char *s = init_str();
int l = 0;
add_num_to_str(&s, &l, SSL_get_cipher_bits(ssl->ssl, NULL));
add_to_str(&s, &l, cast_uchar "-bit");
version = cast_uchar SSL_get_version(ssl->ssl);
if (version) {
add_chr_to_str(&s, &l, ' ');
add_to_str(&s, &l, version);
}
cipher = cast_uchar SSL_get_cipher_name(ssl->ssl);
if (cipher) {
add_chr_to_str(&s, &l, ' ');
add_to_str(&s, &l, cipher);
}
#if defined(SSL_SESSION_RESUME) && 0
if (SSL_session_reused(ssl->ssl)) {
add_to_str(&s, &l, cast_uchar " (reused session)");
}
#endif
return s;
}
#ifdef SSL_SESSION_RESUME
struct session_cache_entry {
list_entry_1st
uttime absolute_time;
SSL_CTX *ctx;
SSL_SESSION *session;
int port;
list_entry_last
unsigned char host[1];
};
static struct list_head session_cache = { &session_cache, &session_cache };
static struct session_cache_entry *find_session_cache_entry(SSL_CTX *ctx, unsigned char *host, int port)
{
struct session_cache_entry *sce;
struct list_head *lsce;
foreach(struct session_cache_entry, sce, lsce, session_cache)
if (sce->ctx == ctx && !strcmp(cast_const_char sce->host, cast_const_char host))
return sce;
return NULL;
}
SSL_SESSION *get_session_cache_entry(SSL_CTX *ctx, unsigned char *host, int port)
{
struct session_cache_entry *sce = find_session_cache_entry(ctx, host, port);
if (!sce)
return NULL;
if (get_absolute_time() - sce->absolute_time > SESSION_TIMEOUT)
return NULL;
return sce->session;
}
static void set_session_cache_entry(SSL_CTX *ctx, unsigned char *host, int port, SSL_SESSION *s)
{
struct session_cache_entry *sce = find_session_cache_entry(ctx, host, port);
size_t sl;
if (sce) {
SSL_SESSION_free(sce->session);
if (s) {
sce->session = s;
} else {
del_from_list(sce);
mem_free(sce);
}
return;
}
if (!s)
return;
sl = strlen(cast_const_char host);
if (sl > MAXINT - sizeof(struct session_cache_entry)) return;
sce = mem_alloc(sizeof(struct session_cache_entry) + sl);
sce->absolute_time = get_absolute_time();
sce->ctx = ctx;
sce->session = s;
sce->port = port;
strcpy(cast_char sce->host, cast_const_char host);
add_to_list(session_cache, sce);
}
void retrieve_ssl_session(struct connection *c)
{
if (c->ssl && !c->ssl->session_retrieved && !proxies.only_proxies) {
SSL_SESSION *s;
unsigned char *orig_url, *h;
int p;
if (c->no_tls /*|| SSL_session_reused(c->ssl->ssl)*/) {
s = NULL;
c->ssl->session_retrieved = 1;
} else {
s = SSL_get1_session(c->ssl->ssl);
}
#ifdef HAVE_SSL_SESSION_IS_RESUMABLE
if (s && !SSL_SESSION_is_resumable(s)) {
SSL_SESSION_free(s);
s = NULL;
}
#endif
orig_url = remove_proxy_prefix(c->url);
h = get_host_name(orig_url);
p = get_port(orig_url);
if (s)
c->ssl->session_retrieved = 1;
set_session_cache_entry(c->ssl->ctx, h, p, s);
mem_free(h);
clear_ssl_errors(__LINE__);
}
}
static int shrink_session_cache(int u)
{
uttime now = get_absolute_time();
struct session_cache_entry *d;
struct list_head *ld;
int f = 0;
#ifdef HAVE_CRYPTO_SET_MEM_FUNCTIONS
if (in_ssl_malloc_hook++)
goto ret;
#endif
if (u == SH_FREE_SOMETHING && !list_empty(session_cache)) {
d = list_struct(session_cache.prev, struct session_cache_entry);
goto delete_last;
}
foreach(struct session_cache_entry, d, ld, session_cache) if (u == SH_FREE_ALL || now - d->absolute_time > SESSION_TIMEOUT) {
delete_last:
ld = d->list_entry.prev;
del_from_list(d);
SSL_SESSION_free(d->session);
mem_free(d);
f = ST_SOMETHING_FREED;
}
#ifdef HAVE_CRYPTO_SET_MEM_FUNCTIONS
ret:
in_ssl_malloc_hook--;
#endif
return f | (list_empty(session_cache) ? ST_CACHE_EMPTY : 0);
}
unsigned long session_info(int type)
{
switch (type) {
case CI_FILES:
return list_size(&session_cache);
default:
internal_error("session_info: bad request");
}
return 0;
}
void init_session_cache(void)
{
register_cache_upcall(shrink_session_cache, 0, cast_uchar "session");
}
#endif
#else
void https_func(struct connection *c)
{
setcstate(c, S_NO_SSL);
abort_connection(c);
}
#endif