links

lynx-like text mode web browser
git clone anongit@rnpnr.xyz:links.git
Log | Files | Refs | Feed | README | LICENSE

https.c (8831B)


      1 /* https.c
      2  * HTTPS protocol client implementation
      3  * (c) 2002 Mikulas Patocka
      4  * This file is a part of the Links program, released under GPL.
      5 
      6  * In addition, as a special exception, the copyright holders give
      7  * permission to link the code of portions of this program with the
      8  * OpenSSL library under certain conditions as described in each
      9  * individual source file, and distribute linked combinations
     10  * including the two.
     11  * You must obey the GNU General Public License in all respects
     12  * for all of the code used other than OpenSSL.  If you modify
     13  * file(s) with this exception, you may extend this exception to your
     14  * version of the file(s), but you are not obligated to do so.  If you
     15  * do not wish to do so, delete this exception statement from your
     16  * version.  If you delete this exception statement from all source
     17  * files in the program, then also delete it here.
     18  */
     19 
     20 #include <limits.h>
     21 #include <openssl/ssl.h>
     22 #include <openssl/x509v3.h>
     23 #include <string.h>
     24 
     25 #include "links.h"
     26 
     27 static SSL_CTX *contexts = NULL;
     28 
     29 struct session_cache_entry {
     30 	list_entry_1st;
     31 	uttime absolute_time;
     32 	SSL_CTX *ctx;
     33 	SSL_SESSION *session;
     34 	int port;
     35 	char host;
     36 };
     37 
     38 static struct list_head session_cache = { &session_cache, &session_cache };
     39 
     40 static int
     41 ssl_password_callback(char *buf, int size, int rwflag, void *userdata)
     42 {
     43 	const size_t sl = strlen((char *)ssl_options.client_cert_password);
     44 	if (size > sl)
     45 		size = sl;
     46 	memcpy(buf, ssl_options.client_cert_password, size);
     47 	return size;
     48 }
     49 
     50 links_ssl *
     51 getSSL(void)
     52 {
     53 	links_ssl *ssl;
     54 
     55 	if (!contexts) {
     56 		SSL_CTX *ctx;
     57 		const SSL_METHOD *m;
     58 
     59 		if (!(m = TLS_client_method()))
     60 			return NULL;
     61 		contexts = ctx = SSL_CTX_new(m);
     62 		if (!ctx)
     63 			return NULL;
     64 		SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
     65 		SSL_CTX_set_default_verify_paths(ctx);
     66 		SSL_CTX_set_default_passwd_cb(ctx, ssl_password_callback);
     67 	}
     68 	ssl = xmalloc(sizeof(links_ssl));
     69 	ssl->ctx = contexts;
     70 	ssl->ssl = SSL_new(ssl->ctx);
     71 	if (!ssl->ssl) {
     72 		free(ssl);
     73 		return NULL;
     74 	}
     75 	ssl->bytes_read = ssl->bytes_written = 0;
     76 	ssl->session_set = 0;
     77 	ssl->session_retrieved = 0;
     78 	ssl->ca = NULL;
     79 	return ssl;
     80 }
     81 
     82 void
     83 freeSSL(links_ssl *ssl)
     84 {
     85 	if (!ssl || ssl == DUMMY)
     86 		return;
     87 
     88 	SSL_shutdown(ssl->ssl);
     89 	SSL_free(ssl->ssl);
     90 	free(ssl->ca);
     91 	free(ssl);
     92 }
     93 
     94 void
     95 ssl_finish(void)
     96 {
     97 	SSL_CTX_free(contexts);
     98 	contexts = NULL;
     99 }
    100 
    101 void
    102 https_func(struct connection *c)
    103 {
    104 	c->ssl = DUMMY;
    105 	http_func(c);
    106 }
    107 
    108 static int
    109 verify_ssl_host_name(X509 *server_cert, char *host)
    110 {
    111 	int v;
    112 	char ipv4_address[4];
    113 	char ipv6_address[16];
    114 
    115 	if (!numeric_ip_address(host, ipv4_address))
    116 		v = X509_check_ip(server_cert, (unsigned char *)ipv4_address, 4,
    117 		                  0);
    118 	else if (!numeric_ipv6_address(host, ipv6_address, NULL))
    119 		v = X509_check_ip(server_cert, (unsigned char *)ipv6_address,
    120 		                  16, 0);
    121 	else
    122 		v = X509_check_host(server_cert, host, strlen(host), 0, NULL);
    123 
    124 	return v == 1 ? 0 : S_INVALID_CERTIFICATE;
    125 }
    126 
    127 static unsigned char *
    128 extract_field(const char *str, const char *field)
    129 {
    130 	size_t len;
    131 	char *f = strstr(str, field);
    132 	if (!f)
    133 		return NULL;
    134 	f += strlen(field);
    135 	len = strcspn(f, "/");
    136 	return memacpy(cast_uchar f, len);
    137 }
    138 
    139 static char *
    140 extract_ca(const char *str)
    141 {
    142 	unsigned char *c, *o;
    143 	c = extract_field(str, "/C=");
    144 	o = extract_field(str, "/O=");
    145 	if (!o)
    146 		o = extract_field(str, "/CN=");
    147 	if (!o) {
    148 		free(c);
    149 		c = NULL;
    150 		o = stracpy(cast_uchar str);
    151 	}
    152 	if (c) {
    153 		add_to_strn(&o, cast_uchar ", ");
    154 		add_to_strn(&o, c);
    155 		free(c);
    156 	}
    157 	return (char *)o;
    158 }
    159 
    160 int
    161 verify_ssl_certificate(links_ssl *ssl, unsigned char *host)
    162 {
    163 	X509 *server_cert;
    164 	int ret;
    165 
    166 	free(ssl->ca);
    167 	ssl->ca = NULL;
    168 
    169 	if (SSL_get_verify_result(ssl->ssl) != X509_V_OK)
    170 		return S_INVALID_CERTIFICATE;
    171 
    172 	server_cert = SSL_get_peer_certificate(ssl->ssl);
    173 	if (!server_cert)
    174 		return S_INVALID_CERTIFICATE;
    175 
    176 	ret = verify_ssl_host_name(server_cert, (char *)host);
    177 	if (!ret) {
    178 		STACK_OF(X509) *certs = SSL_get_peer_cert_chain(ssl->ssl);
    179 		if (certs) {
    180 			int num = sk_X509_num(certs);
    181 			int i;
    182 			char *last_ca = NULL;
    183 			unsigned char *cas = NULL;
    184 			size_t casl = 0;
    185 			for (i = num - 1; i >= 0; i--) {
    186 				char space[3072];
    187 				char *n;
    188 				X509 *cert = sk_X509_value(certs, i);
    189 				X509_NAME *name;
    190 				name = X509_get_issuer_name(cert);
    191 				n = X509_NAME_oneline(name, space,
    192 				                      sizeof(space));
    193 				if (n) {
    194 					char *ca = extract_ca(n);
    195 					if (!last_ca || strcmp(ca, last_ca)) {
    196 						if (casl)
    197 							casl = add_to_str(
    198 							    &cas, casl,
    199 							    CERT_RIGHT_ARROW);
    200 						casl = add_to_str(
    201 						    &cas, casl, cast_uchar ca);
    202 						free(last_ca);
    203 						last_ca = ca;
    204 					} else {
    205 						free(ca);
    206 					}
    207 				}
    208 			}
    209 			free(last_ca);
    210 			if (casl)
    211 				ssl->ca = cas;
    212 			else
    213 				free(cas);
    214 		}
    215 	}
    216 	X509_free(server_cert);
    217 	return ret;
    218 }
    219 
    220 int
    221 verify_ssl_cipher(links_ssl *ssl)
    222 {
    223 	const char *cipher;
    224 	if (SSL_get_cipher_bits(ssl->ssl, NULL) < 112)
    225 		return S_INSECURE_CIPHER;
    226 	if ((cipher = SSL_get_cipher_name(ssl->ssl)))
    227 		if (strstr(cipher, "RC4") || strstr(cipher, "NULL"))
    228 			return S_INSECURE_CIPHER;
    229 	return 0;
    230 }
    231 
    232 int
    233 ssl_not_reusable(links_ssl *ssl)
    234 {
    235 	const char *cipher;
    236 	if (!ssl || ssl == DUMMY)
    237 		return 0;
    238 	ssl->bytes_read = (ssl->bytes_read + 4095) & ~4095;
    239 	ssl->bytes_written = (ssl->bytes_written + 4095) & ~4095;
    240 	if ((cipher = SSL_get_cipher_name(ssl->ssl)))
    241 		if (strstr(cipher, "RC4-") || strstr(cipher, "DES-")
    242 		    || strstr(cipher, "RC2-") || strstr(cipher, "IDEA-")
    243 		    || strstr(cipher, "GOST-"))
    244 			return ssl->bytes_read + ssl->bytes_written >= 1 << 20;
    245 	return 0;
    246 }
    247 
    248 unsigned char *
    249 get_cipher_string(links_ssl *ssl)
    250 {
    251 	const char *version, *cipher;
    252 	unsigned char *s = NULL;
    253 	size_t l;
    254 
    255 	l = add_num_to_str(&s, 0, SSL_get_cipher_bits(ssl->ssl, NULL));
    256 	l = add_to_str(&s, l, (unsigned char *)"-bit");
    257 
    258 	if ((version = SSL_get_version(ssl->ssl))) {
    259 		l = add_chr_to_str(&s, l, ' ');
    260 		l = add_to_str(&s, l, (unsigned char *)version);
    261 	}
    262 	if ((cipher = SSL_get_cipher_name(ssl->ssl))) {
    263 		l = add_chr_to_str(&s, l, ' ');
    264 		l = add_to_str(&s, l, (unsigned char *)cipher);
    265 	}
    266 	return s;
    267 }
    268 
    269 static struct session_cache_entry *
    270 find_session_cache_entry(SSL_CTX *ctx, char *host, int port)
    271 {
    272 	struct session_cache_entry *sce = NULL;
    273 	struct list_head *lsce;
    274 	foreach (struct session_cache_entry, sce, lsce, session_cache)
    275 		if (sce->ctx == ctx && !strcmp(&sce->host, host))
    276 			return sce;
    277 	return NULL;
    278 }
    279 
    280 SSL_SESSION *
    281 get_session_cache_entry(SSL_CTX *ctx, unsigned char *host, int port)
    282 {
    283 	struct session_cache_entry *sce =
    284 	    find_session_cache_entry(ctx, (char *)host, port);
    285 	if (!sce)
    286 		return NULL;
    287 	if (get_absolute_time() - sce->absolute_time > SESSION_TIMEOUT)
    288 		return NULL;
    289 	return sce->session;
    290 }
    291 
    292 static void
    293 set_session_cache_entry(SSL_CTX *ctx, char *host, int port, SSL_SESSION *s)
    294 {
    295 	struct session_cache_entry *sce =
    296 	    find_session_cache_entry(ctx, host, port);
    297 	size_t sl;
    298 	if (sce) {
    299 		SSL_SESSION_free(sce->session);
    300 		if (s)
    301 			sce->session = s;
    302 		else {
    303 			del_from_list(sce);
    304 			free(sce);
    305 		}
    306 		return;
    307 	}
    308 	if (!s)
    309 		return;
    310 	sl = strlen(host);
    311 	if (sl > INT_MAX - sizeof(struct session_cache_entry))
    312 		return;
    313 	sce = xmalloc(sizeof(struct session_cache_entry) + sl);
    314 	sce->absolute_time = get_absolute_time();
    315 	sce->ctx = ctx;
    316 	sce->session = s;
    317 	sce->port = port;
    318 	strcpy(&sce->host, host);
    319 	add_to_list(session_cache, sce);
    320 }
    321 
    322 void
    323 retrieve_ssl_session(struct connection *c)
    324 {
    325 	if (c->ssl && !c->ssl->session_retrieved && !proxies.only_proxies) {
    326 		SSL_SESSION *s;
    327 		unsigned char *orig_url;
    328 		char *h;
    329 		int p;
    330 
    331 		if (c->no_tls) {
    332 			s = NULL;
    333 			c->ssl->session_retrieved = 1;
    334 		} else if ((s = SSL_get1_session(c->ssl->ssl)))
    335 			c->ssl->session_retrieved = 1;
    336 		orig_url = remove_proxy_prefix(c->url);
    337 		h = (char *)get_host_name(orig_url);
    338 		p = get_port(orig_url);
    339 		set_session_cache_entry(c->ssl->ctx, h, p, s);
    340 		free(h);
    341 	}
    342 }
    343 
    344 static int
    345 shrink_session_cache(int u)
    346 {
    347 	uttime now = get_absolute_time();
    348 	struct session_cache_entry *d = NULL;
    349 	struct list_head *ld;
    350 	int f = 0;
    351 	if (u == SH_FREE_SOMETHING && !list_empty(session_cache)) {
    352 		d = list_struct(session_cache.prev, struct session_cache_entry);
    353 		goto delete_last;
    354 	}
    355 	foreach (struct session_cache_entry, d, ld, session_cache)
    356 		if (u == SH_FREE_ALL
    357 		    || now - d->absolute_time > SESSION_TIMEOUT) {
    358 delete_last:
    359 			ld = d->list_entry.prev;
    360 			del_from_list(d);
    361 			SSL_SESSION_free(d->session);
    362 			free(d);
    363 			f = ST_SOMETHING_FREED;
    364 		}
    365 	return f | (list_empty(session_cache) ? ST_CACHE_EMPTY : 0);
    366 }
    367 
    368 unsigned long
    369 session_info(int type)
    370 {
    371 	switch (type) {
    372 	case CI_FILES:
    373 		return list_size(&session_cache);
    374 	default:
    375 		internal("session_info: bad request");
    376 	}
    377 	return 0;
    378 }
    379 
    380 void
    381 init_session_cache(void)
    382 {
    383 	register_cache_upcall(shrink_session_cache, 0,
    384 	                      (unsigned char *)"session");
    385 }