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 }