/* 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