1 /* 2 * mod_gnutls - GnuTLS support for lighttpd 3 * 4 * Copyright(c) 2020 Glenn Strauss gstrauss()gluelogic.com All rights reserved 5 * License: BSD 3-clause (same as lighttpd) 6 */ 7 /* 8 * GnuTLS manual: https://www.gnutls.org/documentation.html 9 * 10 * Note: If session tickets are -not- disabled with 11 * ssl.openssl.ssl-conf-cmd = ("Options" => "-SessionTicket") 12 * mod_gnutls rotates server ticket encryption key (STEK) every 18 hours. 13 * (https://gnutls.org/manual/html_node/Session-resumption.html) 14 * This is fine for use with a single lighttpd instance, but with multiple 15 * lighttpd workers, no coordinated STEK (server ticket encryption key) 16 * rotation occurs unless ssl.stek-file is defined and maintained (preferred), 17 * or if some external job restarts lighttpd. Restarting lighttpd generates a 18 * new key that is shared by lighttpd workers for the lifetime of the new key. 19 * If the rotation period expires and lighttpd has not been restarted, and if 20 * ssl.stek-file is not in use, then lighttpd workers will generate new 21 * independent keys, making session tickets less effective for session 22 * resumption, since clients have a lower chance for future connections to 23 * reach the same lighttpd worker. However, things will still work, and a new 24 * session will be created if session resumption fails. Admins should plan to 25 * restart lighttpd at least every 18 hours if session tickets are enabled and 26 * multiple lighttpd workers are configured. Since that is likely disruptive, 27 * if multiple lighttpd workers are configured, ssl.stek-file should be 28 * defined and the file maintained externally. 29 * 30 * future possible enhancements to lighttpd mod_gnutls: 31 * - session cache (though session tickets are implemented) 32 * See gnutls_db_set_store_function() and gnutls_db_set_retrieve_function() 33 * (and do not enable unless server.feature-flags ssl.session-cache enabled) 34 */ 35 #include "first.h" 36 37 #include <sys/types.h> 38 #include <sys/stat.h> 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <stdarg.h> 42 #include <stdlib.h> 43 #include <stdio.h> /* vsnprintf() */ 44 #include <string.h> 45 46 #include <gnutls/gnutls.h> 47 #include <gnutls/ocsp.h> 48 #include <gnutls/x509.h> 49 #include <gnutls/x509-ext.h> 50 #include <gnutls/abstract.h> 51 52 #ifdef GNUTLS_SKIP_GLOBAL_INIT 53 GNUTLS_SKIP_GLOBAL_INIT 54 #endif 55 56 #include "base.h" 57 #include "fdevent.h" 58 #include "http_header.h" 59 #include "http_kv.h" 60 #include "log.h" 61 #include "plugin.h" 62 63 typedef struct { 64 /* SNI per host: with COMP_SERVER_SOCKET, COMP_HTTP_SCHEME, COMP_HTTP_HOST */ 65 gnutls_certificate_credentials_t ssl_cred; 66 char trust_inited; 67 char must_staple; 68 gnutls_datum_t *ssl_pemfile_x509; 69 gnutls_privkey_t ssl_pemfile_pkey; 70 const buffer *ssl_stapling_file; 71 unix_time64_t ssl_stapling_loadts; 72 unix_time64_t ssl_stapling_nextts; 73 } plugin_cert; 74 75 typedef struct { 76 int8_t ssl_session_ticket; 77 /*(preserved here for deinit at server shutdown)*/ 78 gnutls_priority_t priority_cache; 79 #if GNUTLS_VERSION_NUMBER < 0x030600 80 gnutls_dh_params_t dh_params; 81 #endif 82 } plugin_ssl_ctx; 83 84 typedef struct { 85 plugin_cert *pc; 86 87 /*(used only during startup; not patched)*/ 88 unsigned char ssl_enabled; /* only interesting for setting up listening sockets. don't use at runtime */ 89 unsigned char ssl_honor_cipher_order; /* determine SSL cipher in server-preferred order, not client-order */ 90 unsigned char ssl_empty_fragments; 91 unsigned char ssl_use_sslv2; 92 unsigned char ssl_use_sslv3; 93 const buffer *ssl_cipher_list; 94 const buffer *ssl_dh_file; 95 const buffer *ssl_ec_curve; 96 array *ssl_conf_cmd; 97 98 /*(copied from plugin_data for socket ssl_ctx config)*/ 99 gnutls_priority_t priority_cache; 100 unsigned char ssl_session_ticket; 101 unsigned char ssl_verifyclient; 102 unsigned char ssl_verifyclient_enforce; 103 unsigned char ssl_verifyclient_depth; 104 105 const char *priority_base; 106 buffer *priority_override; 107 buffer priority_str; 108 #if GNUTLS_VERSION_NUMBER < 0x030600 109 gnutls_dh_params_t dh_params; 110 #endif 111 } plugin_config_socket; /*(used at startup during configuration)*/ 112 113 typedef struct { 114 /* SNI per host: w/ COMP_SERVER_SOCKET, COMP_HTTP_SCHEME, COMP_HTTP_HOST */ 115 plugin_cert *pc; 116 gnutls_datum_t *ssl_ca_file; /* .data is (gnutls_x509_crt_t) */ 117 gnutls_datum_t *ssl_ca_dn_file; /* .data is (gnutls_x509_crt_t) */ 118 gnutls_datum_t *ssl_ca_crl_file;/* .data is (gnutls_x509_crl_t) */ 119 120 unsigned char ssl_verifyclient; 121 unsigned char ssl_verifyclient_enforce; 122 unsigned char ssl_verifyclient_depth; 123 unsigned char ssl_verifyclient_export_cert; 124 unsigned char ssl_read_ahead; 125 unsigned char ssl_log_noise; 126 unsigned char ssl_disable_client_renegotiation; 127 const buffer *ssl_verifyclient_username; 128 const buffer *ssl_acme_tls_1; 129 #if GNUTLS_VERSION_NUMBER < 0x030600 130 gnutls_dh_params_t dh_params; 131 #endif 132 } plugin_config; 133 134 typedef struct { 135 PLUGIN_DATA; 136 plugin_ssl_ctx *ssl_ctxs; 137 plugin_config defaults; 138 server *srv; 139 const char *ssl_stek_file; 140 } plugin_data; 141 142 static int ssl_is_init; 143 /* need assigned p->id for deep access of module handler_ctx for connection 144 * i.e. handler_ctx *hctx = con->plugin_ctx[plugin_data_singleton->id]; */ 145 static plugin_data *plugin_data_singleton; 146 #define LOCAL_SEND_BUFSIZE 16384 /* DEFAULT_MAX_RECORD_SIZE */ 147 static char *local_send_buffer; 148 149 typedef struct { 150 gnutls_session_t ssl; /* gnutls request/connection context */ 151 request_st *r; 152 connection *con; 153 int8_t close_notify; 154 uint8_t alpn; 155 int8_t ssl_session_ticket; 156 int handshake; 157 size_t pending_write; 158 plugin_config conf; 159 unsigned int verify_status; 160 buffer *tmp_buf; 161 log_error_st *errh; 162 gnutls_certificate_credentials_t acme_tls_1_cred; 163 } handler_ctx; 164 165 166 static handler_ctx * 167 handler_ctx_init (void) 168 { 169 handler_ctx *hctx = calloc(1, sizeof(*hctx)); 170 force_assert(hctx); 171 return hctx; 172 } 173 174 175 static void 176 handler_ctx_free (handler_ctx *hctx) 177 { 178 gnutls_deinit(hctx->ssl); 179 if (hctx->acme_tls_1_cred) 180 gnutls_certificate_free_credentials(hctx->acme_tls_1_cred); 181 free(hctx); 182 } 183 184 185 __attribute_cold__ 186 static void elog(log_error_st * const errh, 187 const char * const file, const int line, 188 const int rc, const char * const msg) 189 { 190 /* error logging convenience function that decodes gnutls result codes */ 191 log_error(errh, file, line, "GnuTLS: %s: (%s) %s", 192 msg, gnutls_strerror_name(rc), gnutls_strerror(rc)); 193 } 194 195 196 __attribute_cold__ 197 __attribute_format__((__printf__, 5, 6)) 198 static void elogf(log_error_st * const errh, 199 const char * const file, const int line, 200 const int rc, const char * const fmt, ...) 201 { 202 char msg[1024]; 203 va_list ap; 204 va_start(ap, fmt); 205 vsnprintf(msg, sizeof(msg), fmt, ap); 206 va_end(ap); 207 elog(errh, file, line, rc, msg); 208 } 209 210 211 /* gnutls func gnutls_load_file() loads file contents into a gnutls_datum_t. 212 * The data loaded could be sensitive, e.g. a private key included in a pemfile. 213 * However, gnutls_load_file() is not careful about zeroizing memory on error, 214 * might use realloc() (which does not guarantee to zeroize memory released), 215 * and silently continues on short read, so provide our own. Updates to 216 * gnutls 3.6.14 may be more careful with private key files, but do not 217 * extend the same care to pemfiles which might contain private keys. 218 * Related, see also mod_gnutls_datum_wipe() below. 219 * https://gitlab.com/gnutls/gnutls/-/issues/1001 220 * https://gitlab.com/gnutls/gnutls/-/issues/1002 221 * https://gitlab.com/gnutls/gnutls/-/merge_requests/1270 222 */ 223 static int 224 mod_gnutls_load_file (const char * const fn, gnutls_datum_t * const d, log_error_st *errh) 225 { 226 #if 0 227 int rc = gnutls_load_file(fn, d); 228 if (rc < 0) 229 elogf(errh, __FILE__, __LINE__, rc, "%s() %s", __func__, fn); 230 return rc; 231 #else 232 off_t dlen = 512*1024*1024;/*(arbitrary limit: 512 MB file; expect < 1 MB)*/ 233 char *data = fdevent_load_file(fn, &dlen, errh, gnutls_malloc, gnutls_free); 234 if (NULL == data) return GNUTLS_E_FILE_ERROR; 235 d->data = (unsigned char *)data; 236 d->size = (unsigned int)dlen; 237 return 0; 238 #endif 239 } 240 241 242 /* GnuTLS does not expose _gnutls_free_key_datum() so provide our own */ 243 static void 244 mod_gnutls_datum_wipe (gnutls_datum_t * const d) 245 { 246 if (NULL == d) return; 247 if (d->data) { 248 if (d->size) gnutls_memset(d->data, 0, d->size); 249 gnutls_free(d->data); 250 d->data = NULL; 251 } 252 d->size = 0; 253 } 254 255 256 /* session tickets 257 * 258 * gnutls expects 64 bytes of random data in gnutls_datum_t passed to 259 * gnutls_session_ticket_enable_server() 260 * 261 * (private) session ticket definitions from lib/gnutls_int.h 262 * #define TICKET_MASTER_KEY_SIZE (TICKET_KEY_NAME_SIZE+TICKET_CIPHER_KEY_SIZE+TICKET_MAC_SECRET_SIZE) 263 * #define TICKET_KEY_NAME_SIZE 16 264 * #define TICKET_CIPHER_KEY_SIZE 32 265 * #define TICKET_MAC_SECRET_SIZE 16 266 */ 267 #define TICKET_MASTER_KEY_SIZE 64 268 269 #define TLSEXT_KEYNAME_LENGTH 16 270 #define TLSEXT_TICK_KEY_LENGTH 32 271 272 /* construct our own session ticket encryption key structure 273 * to store keys that are not yet active 274 * (mirror from mod_openssl, even though not all bits are used here) */ 275 typedef struct tlsext_ticket_key_st { 276 unix_time64_t active_ts; /* tickets not issued w/ key until activation ts*/ 277 unix_time64_t expire_ts; /* key not valid after expiration timestamp */ 278 unsigned char tick_key_name[TLSEXT_KEYNAME_LENGTH]; 279 unsigned char tick_hmac_key[TLSEXT_TICK_KEY_LENGTH]; 280 unsigned char tick_aes_key[TLSEXT_TICK_KEY_LENGTH]; 281 } tlsext_ticket_key_t; 282 283 static tlsext_ticket_key_t session_ticket_keys[1]; /* temp store until active */ 284 static unix_time64_t stek_rotate_ts; 285 286 static gnutls_datum_t session_ticket_key; 287 288 289 static void 290 mod_gnutls_session_ticket_key_free (void) 291 { 292 mod_gnutls_datum_wipe(&session_ticket_key); 293 } 294 static void 295 mod_gnutls_session_ticket_key_init (server *srv) 296 { 297 int rc = gnutls_session_ticket_key_generate(&session_ticket_key); 298 if (rc < 0) { 299 /*(should not happen, but if it does, disable session ticket)*/ 300 session_ticket_key.size = 0; 301 elog(srv->errh, __FILE__, __LINE__, rc, 302 "gnutls_session_ticket_key_generate()"); 303 } 304 } 305 static void 306 mod_gnutls_session_ticket_key_rotate (server *srv) 307 { 308 mod_gnutls_session_ticket_key_free(); 309 mod_gnutls_session_ticket_key_init(srv); 310 } 311 312 313 static int 314 mod_gnutls_session_ticket_key_file (const char *fn) 315 { 316 /* session ticket encryption key (STEK) 317 * 318 * STEK file should be stored in non-persistent storage, 319 * e.g. /dev/shm/lighttpd/stek-file (in memory) 320 * with appropriate permissions set to keep stek-file from being 321 * read by other users. Where possible, systems should also be 322 * configured without swap. 323 * 324 * admin should schedule an independent job to periodically 325 * generate new STEK up to 3 times during key lifetime 326 * (lighttpd stores up to 3 keys) 327 * 328 * format of binary file is: 329 * 4-byte - format version (always 0; for use if format changes) 330 * 4-byte - activation timestamp 331 * 4-byte - expiration timestamp 332 * 16-byte - session ticket key name 333 * 32-byte - session ticket HMAC encrpytion key 334 * 32-byte - session ticket AES encrpytion key 335 * 336 * STEK file can be created with a command such as: 337 * dd if=/dev/random bs=1 count=80 status=none | \ 338 * perl -e 'print pack("iii",0,time()+300,time()+86400),<>' \ 339 * > STEK-file.$$ && mv STEK-file.$$ STEK-file 340 * 341 * The above delays activation time by 5 mins (+300 sec) to allow file to 342 * be propagated to other machines. (admin must handle this independently) 343 * If STEK generation is performed immediately prior to starting lighttpd, 344 * admin should activate keys immediately (without +300). 345 */ 346 int buf[23]; /* 92 bytes */ 347 int rc = 0; /*(will retry on next check interval upon any error)*/ 348 if (0 != fdevent_load_file_bytes((char *)buf,(off_t)sizeof(buf),0,fn,NULL)) 349 return rc; 350 if (buf[0] == 0) { /*(format version 0)*/ 351 session_ticket_keys[0].active_ts = TIME64_CAST(buf[1]); 352 session_ticket_keys[0].expire_ts = TIME64_CAST(buf[2]); 353 #ifndef __COVERITY__ 354 memcpy(&session_ticket_keys[0].tick_key_name, buf+3, 80); 355 #else 356 memcpy(&session_ticket_keys[0].tick_key_name, 357 buf+3, TLSEXT_KEYNAME_LENGTH); 358 memcpy(&session_ticket_keys[0].tick_hmac_key, 359 buf+7, TLSEXT_TICK_KEY_LENGTH); 360 memcpy(&session_ticket_keys[0].tick_aes_key, 361 buf+15, TLSEXT_TICK_KEY_LENGTH); 362 #endif 363 rc = 1; 364 } 365 366 gnutls_memset(buf, 0, sizeof(buf)); 367 return rc; 368 } 369 370 371 static void 372 mod_gnutls_session_ticket_key_check (server *srv, const plugin_data *p, const unix_time64_t cur_ts) 373 { 374 static unix_time64_t detect_retrograde_ts; 375 if (detect_retrograde_ts > cur_ts && detect_retrograde_ts - cur_ts > 28800) 376 stek_rotate_ts = 0; 377 detect_retrograde_ts = cur_ts; 378 379 if (p->ssl_stek_file) { 380 struct stat st; 381 if (0 == stat(p->ssl_stek_file, &st) 382 && TIME64_CAST(st.st_mtime) > stek_rotate_ts 383 && mod_gnutls_session_ticket_key_file(p->ssl_stek_file)) { 384 stek_rotate_ts = cur_ts; 385 } 386 387 tlsext_ticket_key_t *stek = session_ticket_keys; 388 if (stek->active_ts != 0 && stek->active_ts - 63 <= cur_ts) { 389 if (NULL == session_ticket_key.data) { 390 session_ticket_key.data = gnutls_malloc(TICKET_MASTER_KEY_SIZE); 391 if (NULL == session_ticket_key.data) return; 392 session_ticket_key.size = TICKET_MASTER_KEY_SIZE; 393 } 394 #ifndef __COVERITY__ 395 memcpy(session_ticket_key.data, 396 stek->tick_key_name, TICKET_MASTER_KEY_SIZE); 397 gnutls_memset(stek->tick_key_name, 0, TICKET_MASTER_KEY_SIZE); 398 #else 399 char * const data = (char *)session_ticket_key.data; 400 memcpy(data, 401 stek->tick_key_name, TLSEXT_KEYNAME_LENGTH); 402 memcpy(data+TLSEXT_KEYNAME_LENGTH, 403 stek->tick_hmac_key, TLSEXT_TICK_KEY_LENGTH); 404 memcpy(data+TLSEXT_KEYNAME_LENGTH+TLSEXT_TICK_KEY_LENGTH, 405 stek->tick_aes_key, 406 TICKET_MASTER_KEY_SIZE 407 - (TLSEXT_KEYNAME_LENGTH + TLSEXT_TICK_KEY_LENGTH)); 408 gnutls_memset(stek->tick_key_name, 0, TLSEXT_KEYNAME_LENGTH); 409 gnutls_memset(stek->tick_hmac_key, 0, TLSEXT_TICK_KEY_LENGTH); 410 gnutls_memset(stek->tick_aes_key, 0, TLSEXT_TICK_KEY_LENGTH); 411 #endif 412 } 413 if (stek->expire_ts < cur_ts) 414 mod_gnutls_session_ticket_key_free(); 415 } 416 else if (cur_ts - 86400 >= stek_rotate_ts /*(24 hours)*/ 417 || 0 == stek_rotate_ts) { 418 mod_gnutls_session_ticket_key_rotate(srv); 419 stek_rotate_ts = cur_ts; 420 } 421 } 422 423 424 INIT_FUNC(mod_gnutls_init) 425 { 426 plugin_data_singleton = (plugin_data *)calloc(1, sizeof(plugin_data)); 427 return plugin_data_singleton; 428 } 429 430 431 static int mod_gnutls_init_once_gnutls (void) 432 { 433 if (ssl_is_init) return 1; 434 ssl_is_init = 1; 435 436 /* Note: on systems with support for weak symbols, GNUTLS_SKIP_GLOBAL_INIT 437 * is set near top of this file to inhibit GnuTLS implicit initialization 438 * in a library constructor. On systems without support for weak symbols, 439 * set GNUTLS_NO_EXPLICIT_INIT=1 in the environment before starting lighttpd 440 * (GnuTLS 3.3.0 or later) */ 441 442 if (gnutls_global_init() != GNUTLS_E_SUCCESS) 443 return 0; 444 445 local_send_buffer = malloc(LOCAL_SEND_BUFSIZE); 446 force_assert(NULL != local_send_buffer); 447 448 return 1; 449 } 450 451 452 static void mod_gnutls_free_gnutls (void) 453 { 454 if (!ssl_is_init) return; 455 456 gnutls_memset(session_ticket_keys, 0, sizeof(session_ticket_keys)); 457 mod_gnutls_session_ticket_key_free(); 458 stek_rotate_ts = 0; 459 460 gnutls_global_deinit(); 461 462 free(local_send_buffer); 463 ssl_is_init = 0; 464 } 465 466 467 static void 468 mod_gnutls_free_config_crts (gnutls_datum_t *d) 469 { 470 if (NULL == d) return; 471 gnutls_x509_crt_t *crts = (gnutls_x509_crt_t *)(void *)d->data; 472 unsigned int u = d->size; 473 for (unsigned int i = 0; i < u; ++i) 474 gnutls_x509_crt_deinit(crts[i]); 475 gnutls_free(crts); 476 gnutls_free(d); 477 } 478 479 480 static void 481 mod_gnutls_free_config_crls (gnutls_datum_t *d) 482 { 483 if (NULL == d) return; 484 gnutls_x509_crl_t *crls = (gnutls_x509_crl_t *)(void *)d->data; 485 unsigned int u = d->size; 486 for (unsigned int i = 0; i < u; ++i) 487 gnutls_x509_crl_deinit(crls[i]); 488 gnutls_free(crls); 489 gnutls_free(d); 490 } 491 492 493 static int 494 mod_gnutls_cert_is_active (gnutls_x509_crt_t crt) 495 { 496 const time_t before = gnutls_x509_crt_get_activation_time(crt); 497 const time_t after = gnutls_x509_crt_get_expiration_time(crt); 498 const unix_time64_t now = log_epoch_secs; 499 return (before <= now && now <= after); 500 } 501 502 503 static gnutls_datum_t * 504 mod_gnutls_load_config_crts (const char *fn, log_error_st *errh) 505 { 506 /*(very similar to other mod_gnutls_load_config_*())*/ 507 if (!mod_gnutls_init_once_gnutls()) return NULL; 508 509 gnutls_datum_t f = { NULL, 0 }; 510 int rc = mod_gnutls_load_file(fn, &f, errh); 511 if (rc < 0) return NULL; 512 gnutls_datum_t *d = gnutls_malloc(sizeof(gnutls_datum_t)); 513 if (d == NULL) { 514 mod_gnutls_datum_wipe(&f); 515 return NULL; 516 } 517 d->data = NULL; 518 d->size = 0; 519 rc = gnutls_x509_crt_list_import2((gnutls_x509_crt_t **)&d->data, &d->size, 520 &f, GNUTLS_X509_FMT_PEM, 521 GNUTLS_X509_CRT_LIST_SORT); 522 mod_gnutls_datum_wipe(&f); 523 if (rc < 0) { 524 elogf(errh, __FILE__, __LINE__, rc, 525 "gnutls_x509_crt_list_import2() %s", fn); 526 mod_gnutls_free_config_crts(d); 527 return NULL; 528 } 529 else if ( 530 !mod_gnutls_cert_is_active(((gnutls_x509_crt_t *)(void *)d->data)[0])) { 531 log_error(errh, __FILE__, __LINE__, 532 "GnuTLS: inactive/expired X509 certificate '%s'", fn); 533 } 534 535 return d; 536 } 537 538 539 static gnutls_datum_t * 540 mod_gnutls_load_config_crls (const char *fn, log_error_st *errh) 541 { 542 /*(very similar to other mod_gnutls_load_config_*())*/ 543 if (!mod_gnutls_init_once_gnutls()) return NULL; 544 545 gnutls_datum_t f = { NULL, 0 }; 546 int rc = mod_gnutls_load_file(fn, &f, errh); 547 if (rc < 0) return NULL; 548 gnutls_datum_t *d = gnutls_malloc(sizeof(gnutls_datum_t)); 549 if (d == NULL) { 550 mod_gnutls_datum_wipe(&f); 551 return NULL; 552 } 553 d->data = NULL; 554 d->size = 0; 555 rc = gnutls_x509_crl_list_import2((gnutls_x509_crl_t **)&d->data, &d->size, 556 &f, GNUTLS_X509_FMT_PEM, 0); 557 mod_gnutls_datum_wipe(&f); 558 if (rc < 0) { 559 elogf(errh, __FILE__, __LINE__, rc, 560 "gnutls_x509_crl_list_import2() %s", fn); 561 mod_gnutls_free_config_crls(d); 562 return NULL; 563 } 564 565 return d; 566 } 567 568 569 static gnutls_privkey_t 570 mod_gnutls_load_config_pkey (const char *fn, log_error_st *errh) 571 { 572 /*(very similar to other mod_gnutls_load_config_*())*/ 573 if (!mod_gnutls_init_once_gnutls()) return NULL; 574 575 gnutls_datum_t f = { NULL, 0 }; 576 int rc = mod_gnutls_load_file(fn, &f, errh); 577 if (rc < 0) return NULL; 578 gnutls_privkey_t pkey; 579 rc = gnutls_privkey_init(&pkey); 580 if (rc < 0) { 581 mod_gnutls_datum_wipe(&f); 582 return NULL; 583 } 584 rc = gnutls_privkey_import_x509_raw(pkey, &f, GNUTLS_X509_FMT_PEM, NULL, 0); 585 mod_gnutls_datum_wipe(&f); 586 if (rc < 0) { 587 elogf(errh, __FILE__, __LINE__, rc, 588 "gnutls_privkey_import_x509_raw() %s", fn); 589 gnutls_privkey_deinit(pkey); 590 return NULL; 591 } 592 593 return pkey; 594 } 595 596 597 static void 598 mod_gnutls_free_config (server *srv, plugin_data * const p) 599 { 600 if (NULL != p->ssl_ctxs) { 601 gnutls_priority_t pcache_global_scope = p->ssl_ctxs->priority_cache; 602 /* free from $SERVER["socket"] (if not copy of global scope) */ 603 for (uint32_t i = 1; i < srv->config_context->used; ++i) { 604 plugin_ssl_ctx * const s = p->ssl_ctxs + i; 605 if (s->priority_cache && s->priority_cache != pcache_global_scope) { 606 if (s->priority_cache) 607 gnutls_priority_deinit(s->priority_cache); 608 #if GNUTLS_VERSION_NUMBER < 0x030600 609 if (s->dh_params) 610 gnutls_dh_params_deinit(s->dh_params); 611 #endif 612 } 613 } 614 /* free from global scope */ 615 if (pcache_global_scope) { 616 if (p->ssl_ctxs[0].priority_cache) 617 gnutls_priority_deinit(p->ssl_ctxs[0].priority_cache); 618 #if GNUTLS_VERSION_NUMBER < 0x030600 619 if (p->ssl_ctxs[0].dh_params) 620 gnutls_dh_params_deinit(p->ssl_ctxs[0].dh_params); 621 #endif 622 } 623 free(p->ssl_ctxs); 624 } 625 626 if (NULL == p->cvlist) return; 627 /* (init i to 0 if global context; to 1 to skip empty global context) */ 628 for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { 629 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 630 for (; -1 != cpv->k_id; ++cpv) { 631 switch (cpv->k_id) { 632 case 0: /* ssl.pemfile */ 633 if (cpv->vtype == T_CONFIG_LOCAL) { 634 plugin_cert *pc = cpv->v.v; 635 gnutls_certificate_free_credentials(pc->ssl_cred); 636 mod_gnutls_free_config_crts(pc->ssl_pemfile_x509); 637 gnutls_privkey_deinit(pc->ssl_pemfile_pkey); 638 free(pc); 639 } 640 break; 641 case 2: /* ssl.ca-file */ 642 case 3: /* ssl.ca-dn-file */ 643 if (cpv->vtype == T_CONFIG_LOCAL) 644 mod_gnutls_free_config_crts(cpv->v.v); 645 break; 646 case 4: /* ssl.ca-crl-file */ 647 if (cpv->vtype == T_CONFIG_LOCAL) 648 mod_gnutls_free_config_crls(cpv->v.v); 649 break; 650 default: 651 break; 652 } 653 } 654 } 655 } 656 657 658 FREE_FUNC(mod_gnutls_free) 659 { 660 plugin_data *p = p_d; 661 if (NULL == p->srv) return; 662 mod_gnutls_free_config(p->srv, p); 663 mod_gnutls_free_gnutls(); 664 } 665 666 667 static void 668 mod_gnutls_merge_config_cpv (plugin_config * const pconf, const config_plugin_value_t * const cpv) 669 { 670 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 671 case 0: /* ssl.pemfile */ 672 if (cpv->vtype == T_CONFIG_LOCAL) 673 pconf->pc = cpv->v.v; 674 break; 675 case 1: /* ssl.privkey */ 676 break; 677 case 2: /* ssl.ca-file */ 678 if (cpv->vtype == T_CONFIG_LOCAL) 679 pconf->ssl_ca_file = cpv->v.v; 680 break; 681 case 3: /* ssl.ca-dn-file */ 682 if (cpv->vtype == T_CONFIG_LOCAL) 683 pconf->ssl_ca_dn_file = cpv->v.v; 684 break; 685 case 4: /* ssl.ca-crl-file */ 686 if (cpv->vtype == T_CONFIG_LOCAL) 687 pconf->ssl_ca_crl_file = cpv->v.v; 688 break; 689 case 5: /* ssl.read-ahead */ 690 pconf->ssl_read_ahead = (0 != cpv->v.u); 691 break; 692 case 6: /* ssl.disable-client-renegotiation */ 693 pconf->ssl_disable_client_renegotiation = (0 != cpv->v.u); 694 break; 695 case 7: /* ssl.verifyclient.activate */ 696 pconf->ssl_verifyclient = (0 != cpv->v.u); 697 break; 698 case 8: /* ssl.verifyclient.enforce */ 699 pconf->ssl_verifyclient_enforce = (0 != cpv->v.u); 700 break; 701 case 9: /* ssl.verifyclient.depth */ 702 pconf->ssl_verifyclient_depth = (unsigned char)cpv->v.shrt; 703 break; 704 case 10:/* ssl.verifyclient.username */ 705 pconf->ssl_verifyclient_username = cpv->v.b; 706 break; 707 case 11:/* ssl.verifyclient.exportcert */ 708 pconf->ssl_verifyclient_export_cert = (0 != cpv->v.u); 709 break; 710 case 12:/* ssl.acme-tls-1 */ 711 pconf->ssl_acme_tls_1 = cpv->v.b; 712 break; 713 case 13:/* ssl.stapling-file */ 714 break; 715 case 14:/* debug.log-ssl-noise */ 716 pconf->ssl_log_noise = (unsigned char)cpv->v.shrt; 717 break; 718 #if 0 /*(cpk->k_id remapped in mod_gnutls_set_defaults())*/ 719 case 15:/* ssl.verifyclient.ca-file */ 720 case 16:/* ssl.verifyclient.ca-dn-file */ 721 case 17:/* ssl.verifyclient.ca-crl-file */ 722 break; 723 #endif 724 default:/* should not happen */ 725 return; 726 } 727 } 728 729 730 static void 731 mod_gnutls_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) 732 { 733 do { 734 mod_gnutls_merge_config_cpv(pconf, cpv); 735 } while ((++cpv)->k_id != -1); 736 } 737 738 739 static void 740 mod_gnutls_patch_config (request_st * const r, plugin_config * const pconf) 741 { 742 plugin_data * const p = plugin_data_singleton; 743 memcpy(pconf, &p->defaults, sizeof(plugin_config)); 744 for (int i = 1, used = p->nconfig; i < used; ++i) { 745 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 746 mod_gnutls_merge_config(pconf, p->cvlist + p->cvlist[i].v.u2[0]); 747 } 748 } 749 750 751 static int 752 mod_gnutls_verify_set_tlist (handler_ctx *hctx, int req) 753 { 754 /* XXX GnuTLS interfaces appear to be better for client-side use than for 755 * server-side use. If gnutls_x509_trust_list_t were available to attach 756 * to a gnutls_session_t (without copying), then there would not be race 757 * conditions swapping trust lists on the credential *shared* between 758 * connections when ssl.ca-dn-file and ssl.ca-file are both set. If both 759 * are set, current code attempts to set ssl.ca-dn-file right before sending 760 * client cert request, and sets ssl.ca-file right before client cert verify 761 * 762 * Architecture would be cleaner if the trust list for verifying client cert 763 * (gnutls_x509_trust_list_t) were available to attach to a gnutls_session_t 764 * instead of attaching to a gnutls_certificate_credentials_t. 765 */ 766 if (hctx->conf.pc->trust_inited) return GNUTLS_E_SUCCESS; 767 768 gnutls_datum_t *d; 769 int rc; 770 771 /* set trust list using ssl_ca_dn_file, if set, for client cert request 772 * (when req is true) (for CAs sent by server to client in cert request) 773 * (trust is later replaced by ssl_ca_file for client cert verification) */ 774 d = req && hctx->conf.ssl_ca_dn_file 775 ? hctx->conf.ssl_ca_dn_file 776 : hctx->conf.ssl_ca_file; 777 if (NULL == d) { 778 log_error(hctx->r->conf.errh, __FILE__, __LINE__, 779 "GnuTLS: can't verify client without ssl.verifyclient.ca-file " 780 "for TLS server name %s", 781 hctx->r->uri.authority.ptr); /*(might not be set yet if no SNI)*/ 782 return GNUTLS_E_INTERNAL_ERROR; 783 } 784 785 gnutls_x509_trust_list_t tlist = NULL; 786 rc = gnutls_x509_trust_list_init(&tlist, 0); 787 if (rc < 0) { 788 elog(hctx->r->conf.errh, __FILE__, __LINE__, rc, 789 "gnutls_x509_trust_list_init()"); 790 return rc; 791 } 792 793 gnutls_x509_crt_t *clist = (gnutls_x509_crt_t *)(void *)d->data; 794 rc = gnutls_x509_trust_list_add_cas(tlist, clist, d->size, 0); 795 if (rc < 0) { 796 elog(hctx->r->conf.errh, __FILE__, __LINE__, rc, 797 "gnutls_x509_trust_list_add_cas()"); 798 gnutls_x509_trust_list_deinit(tlist, 0); 799 return rc; 800 } 801 802 d = hctx->conf.ssl_ca_crl_file; 803 if (NULL != d && req && hctx->conf.ssl_ca_dn_file) { 804 /*(check req and ssl_ca_dn_file to see if tlist will be replaced later, 805 * and, if so, defer setting crls until later)*/ 806 gnutls_x509_crl_t *crl_list = (gnutls_x509_crl_t *)(void *)d->data; 807 rc = gnutls_x509_trust_list_add_crls(tlist, crl_list, d->size, 0, 0); 808 if (rc < 0) { 809 elog(hctx->r->conf.errh, __FILE__, __LINE__, rc, 810 "gnutls_x509_trust_list_add_crls()"); 811 gnutls_x509_trust_list_deinit(tlist, 0); 812 return rc; 813 } 814 } 815 816 /* gnutls limitation; wasteful to have to copy into each cred */ 817 /* (would be better to share list with session, instead of with cred) */ 818 gnutls_certificate_credentials_t ssl_cred = hctx->conf.pc->ssl_cred; 819 gnutls_certificate_set_trust_list(ssl_cred, tlist, 0); /* transfer tlist */ 820 821 /* (must flip trust lists back and forth b/w DN names and verify CAs) */ 822 if (NULL == hctx->conf.ssl_ca_dn_file) 823 hctx->conf.pc->trust_inited = 1; 824 825 return GNUTLS_E_SUCCESS; 826 } 827 828 829 static int 830 mod_gnutls_verify_cb (gnutls_session_t ssl) 831 { 832 handler_ctx * const hctx = gnutls_session_get_ptr(ssl); 833 if (!hctx->conf.ssl_verifyclient) return 0; 834 835 if (gnutls_auth_client_get_type(ssl) != GNUTLS_CRD_CERTIFICATE) 836 return GNUTLS_E_SUCCESS; 837 838 int rc; 839 840 /* gnutls limitation; wasteful to have to copy into each cred */ 841 /* (would be better to share list with session, instead of with cred) */ 842 if (hctx->conf.ssl_ca_dn_file) { 843 rc = mod_gnutls_verify_set_tlist(hctx, 0); /* for client cert verify */ 844 if (rc < 0) return rc; 845 } 846 847 /* gnutls_certificate_verify_peers2() includes processing OCSP staping, 848 * as well as certificate depth verification before getting internal 849 * flags and calling gnutls_x509_trust_list_verify_crt2() 850 * advanced reference: 851 * gnutls lib/cert-sessions.c:_gnutls_x509_cert_verify_peers() 852 * XXX: if GnuTLS provided a more advanced interface which permitted 853 * providing trust list, verify depth, and flags, we could avoid copying 854 * ca chain and crls into each credential, using 855 * gnutls_x509_trust_list_add_cas() 856 * gnutls_x509_trust_list_add_crls() 857 * gnutls_x509_trust_list_verify_crt2() 858 * See also GnuTLS manual Section 7.3.4 Advanced certificate verification 859 */ 860 861 rc = gnutls_certificate_verify_peers2(ssl, &hctx->verify_status); 862 if (rc < 0) return rc; 863 864 if (hctx->verify_status == 0 && hctx->conf.ssl_ca_dn_file) { 865 /* verify that client cert is issued by CA in ssl.ca-dn-file 866 * if both ssl.ca-dn-file and ssl.ca-file were configured */ 867 gnutls_x509_crt_t *CA_list = 868 (gnutls_x509_crt_t *)(void *)hctx->conf.ssl_ca_dn_file->data; 869 unsigned int len = hctx->conf.ssl_ca_dn_file->size; 870 unsigned int i; 871 gnutls_x509_dn_t issuer, subject; 872 unsigned int crt_size = 0; 873 const gnutls_datum_t *crts; 874 gnutls_x509_crt_t crt = NULL; 875 crts = gnutls_certificate_get_peers(ssl, &crt_size); 876 if (0 == crt_size 877 || gnutls_x509_crt_init(&crt) < 0 878 || gnutls_x509_crt_import(crt, &crts[0], GNUTLS_X509_FMT_DER) < 0 879 || gnutls_x509_crt_get_subject(crt, &issuer) < 0) 880 len = 0; /*(trigger failure path below)*/ 881 for (i = 0; i < len; ++i) { 882 if (gnutls_x509_crt_get_subject(CA_list[i], &subject) >= 0 883 && 0 == memcmp(&subject, &issuer, sizeof(issuer))) 884 break; 885 } 886 if (i == len) 887 hctx->verify_status |= GNUTLS_CERT_SIGNER_NOT_CA; 888 if (crt) gnutls_x509_crt_deinit(crt); 889 } 890 891 if (hctx->verify_status != 0 && hctx->conf.ssl_verifyclient_enforce) { 892 /* (not looping on GNUTLS_E_INTERRUPTED or GNUTLS_E_AGAIN 893 * since we do not want to block here (and not expecting to have to))*/ 894 (void)gnutls_alert_send(ssl, GNUTLS_AL_FATAL, GNUTLS_A_ACCESS_DENIED); 895 return GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR; 896 } 897 898 return GNUTLS_E_SUCCESS; 899 } 900 901 902 #if GNUTLS_VERSION_NUMBER < 0x030603 903 static time_t 904 mod_gnutls_ocsp_next_update (plugin_cert *pc, log_error_st *errh) 905 { 906 gnutls_datum_t f = { NULL, 0 }; 907 int rc = mod_gnutls_load_file(pc->ssl_stapling_file->ptr, &f, errh); 908 if (rc < 0) return (time_t)-1; 909 910 gnutls_ocsp_resp_t resp = NULL; 911 time_t nextupd; 912 if ( gnutls_ocsp_resp_init(&resp) < 0 913 || gnutls_ocsp_resp_import(resp, &f) < 0 914 || gnutls_ocsp_resp_get_single(resp, 0, NULL, NULL, NULL, NULL, NULL, 915 NULL, &nextupd, NULL, NULL) < 0) 916 nextupd = (time_t)-1; 917 gnutls_ocsp_resp_deinit(resp); 918 mod_gnutls_datum_wipe(&f); 919 return nextupd; 920 } 921 #endif 922 923 924 __attribute_cold__ 925 static void 926 mod_gnutls_expire_stapling_file (server *srv, plugin_cert *pc) 927 { 928 #if 0 929 /* discard expired OCSP stapling response */ 930 /* Does GnuTLS detect expired OCSP response? */ 931 /* or must we rebuild gnutls_certificate_credentials_t ? */ 932 #endif 933 if (pc->must_staple) 934 log_error(srv->errh, __FILE__, __LINE__, 935 "certificate marked OCSP Must-Staple, " 936 "but OCSP response expired from ssl.stapling-file %s", 937 pc->ssl_stapling_file->ptr); 938 } 939 940 941 static int 942 mod_gnutls_reload_stapling_file (server *srv, plugin_cert *pc, const unix_time64_t cur_ts) 943 { 944 #if GNUTLS_VERSION_NUMBER < 0x030603 945 /* load file into gnutls_ocsp_resp_t before loading into 946 * gnutls_certificate_credentials_t for safety. Still ToC-ToU since file 947 * is loaded twice, but unlikely condition. (GnuTLS limitation does not 948 * expose access to OCSP response from gnutls_certificate_credentials_t 949 * before GnuTLS 3.6.3) */ 950 time_t nextupd = mod_gnutls_ocsp_next_update(pc, srv->errh); 951 #else 952 UNUSED(srv); 953 #endif 954 955 /* GnuTLS 3.5.6 added the ability to include multiple OCSP responses for 956 * certificate chain as allowed in TLSv1.3, but that is not utilized here. 957 * If implemented, it will probably operate on a new directive, 958 * e.g. ssl.stapling-pemfile 959 * GnuTLS 3.6.3 added gnutls_certificate_set_ocsp_status_request_file2() 960 * GnuTLS 3.6.3 added gnutls_certificate_set_ocsp_status_request_mem() 961 * GnuTLS 3.6.3 added gnutls_certificate_get_ocsp_expiration() */ 962 963 /* gnutls_certificate_get_ocsp_expiration() code comments: 964 * Note that the credentials structure should be read-only when in 965 * use, thus when reloading, either the credentials structure must not 966 * be in use by any sessions, or a new credentials structure should be 967 * allocated for new sessions. 968 * XXX: lighttpd is not threaded, so this is probably not an issue (?) 969 */ 970 971 #if 0 972 gnutls_certificate_set_flags(pc->ssl_cred, 973 GNUTLS_CERTIFICATE_SKIP_OCSP_RESPONSE_CHECK); 974 #endif 975 976 const char *fn = pc->ssl_stapling_file->ptr; 977 int rc = gnutls_certificate_set_ocsp_status_request_file(pc->ssl_cred,fn,0); 978 if (rc < 0) 979 return rc; 980 981 #if GNUTLS_VERSION_NUMBER >= 0x030603 982 time_t nextupd = 983 gnutls_certificate_get_ocsp_expiration(pc->ssl_cred, 0, 0, 0); 984 if (nextupd == (time_t)-2) nextupd = (time_t)-1; 985 #endif 986 987 pc->ssl_stapling_loadts = cur_ts; 988 pc->ssl_stapling_nextts = nextupd; 989 if (pc->ssl_stapling_nextts == -1) { 990 /* "Next Update" might not be provided by OCSP responder 991 * Use 3600 sec (1 hour) in that case. */ 992 /* retry in 1 hour if unable to determine Next Update */ 993 pc->ssl_stapling_nextts = cur_ts + 3600; 994 pc->ssl_stapling_loadts = 0; 995 } 996 else if (pc->ssl_stapling_nextts < cur_ts) 997 mod_gnutls_expire_stapling_file(srv, pc); 998 999 return 0; 1000 } 1001 1002 1003 static int 1004 mod_gnutls_refresh_stapling_file (server *srv, plugin_cert *pc, const unix_time64_t cur_ts) 1005 { 1006 if (pc->ssl_stapling_nextts > cur_ts + 256) 1007 return 0; /* skip check for refresh unless close to expire */ 1008 struct stat st; 1009 if (0 != stat(pc->ssl_stapling_file->ptr, &st) 1010 || TIME64_CAST(st.st_mtime) <= pc->ssl_stapling_loadts) { 1011 if (pc->ssl_stapling_nextts < cur_ts) 1012 mod_gnutls_expire_stapling_file(srv, pc); 1013 return 0; 1014 } 1015 return mod_gnutls_reload_stapling_file(srv, pc, cur_ts); 1016 } 1017 1018 1019 static void 1020 mod_gnutls_refresh_stapling_files (server *srv, const plugin_data *p, const unix_time64_t cur_ts) 1021 { 1022 /* future: might construct array of (plugin_cert *) at startup 1023 * to avoid the need to search for them here */ 1024 /* (init i to 0 if global context; to 1 to skip empty global context) */ 1025 if (NULL == p->cvlist) return; 1026 for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { 1027 const config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 1028 for (; cpv->k_id != -1; ++cpv) { 1029 if (cpv->k_id != 0) continue; /* k_id == 0 for ssl.pemfile */ 1030 if (cpv->vtype != T_CONFIG_LOCAL) continue; 1031 plugin_cert *pc = cpv->v.v; 1032 if (pc->ssl_stapling_file) 1033 mod_gnutls_refresh_stapling_file(srv, pc, cur_ts); 1034 } 1035 } 1036 } 1037 1038 1039 static int 1040 mod_gnutls_crt_must_staple (gnutls_x509_crt_t crt) 1041 { 1042 /* Look for TLS features X.509 extension with value 5 1043 * RFC 7633 https://tools.ietf.org/html/rfc7633#appendix-A 1044 * 5 = OCSP Must-Staple (security mechanism) */ 1045 1046 int rc; 1047 1048 #if GNUTLS_VERSION_NUMBER < 0x030501 1049 1050 unsigned int i; 1051 char oid[128]; 1052 size_t oidsz; 1053 for (i = 0; ; ++i) { 1054 oidsz = sizeof(oid); 1055 rc = gnutls_x509_crt_get_extension_info(crt, i, oid, &oidsz, NULL); 1056 if (rc < 0 || 0 == strcmp(oid, GNUTLS_X509EXT_OID_TLSFEATURES)) break; 1057 } 1058 /* ext not found if (rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) */ 1059 if (rc < 0) return rc; 1060 1061 gnutls_datum_t der = { NULL, 0 }; 1062 rc = gnutls_x509_crt_get_extension_data2(crt, i, &der); 1063 if (rc < 0) return rc; 1064 1065 /* DER encoding (Tag, Length, Value (TLV)) expecting: 30:03:02:01:05 1066 * [TL[TLV]] (30=Sequence, Len=03, (02=Integer, Len=01, Value=05)) 1067 * XXX: This is not future-proof if TLS feature list values are extended */ 1068 /*const char must_staple[] = { 0x30, 0x03, 0x02, 0x01, 0x05 };*/ 1069 /*rc = (der.size == 5 && 0 == memcmp(der.data, must_staple, 5))*/ 1070 rc = (der.size >= 5 && der.data[0] == 0x30 && der.data[1] >= 0x03 1071 && der.data[2] == 0x2 && der.data[3] == 0x1 && der.data[4] == 0x5) 1072 ? 1 1073 : 0; 1074 1075 gnutls_free(der.data); 1076 1077 #else 1078 1079 gnutls_x509_tlsfeatures_t f; 1080 rc = gnutls_x509_tlsfeatures_init(&f); 1081 if (rc < 0) return rc; 1082 rc = gnutls_x509_tlsfeatures_add(f, 5); /* 5 = OCSP Must-Staple */ 1083 if (rc < 0) return rc; 1084 rc = (0 != gnutls_x509_tlsfeatures_check_crt(f, crt)); 1085 gnutls_x509_tlsfeatures_deinit(f); 1086 1087 #endif 1088 1089 return rc; /* 1 if OCSP Must-Staple found; 0 if not */ 1090 } 1091 1092 1093 static int 1094 mod_gnutls_construct_crt_chain (plugin_cert *pc, gnutls_datum_t *d, log_error_st *errh) 1095 { 1096 /* Historically, openssl will use the cert chain in (SSL_CTX *) if a cert 1097 * does not have a chain configured in (SSL *). Attempt to provide 1098 * compatible behavior here. This may be called after startup since 1099 * ssl.pemfile might be defined in a config scope without ssl.ca-file, 1100 * and at runtime is when ssl.ca-file (e.g. from matching $SERVER["socket"]) 1101 * would be known along with ssl.pemfile (e.g. from $HTTP["host"]) */ 1102 1103 gnutls_certificate_credentials_t ssl_cred; 1104 int rc = gnutls_certificate_allocate_credentials(&ssl_cred); 1105 if (rc < 0) return rc; 1106 unsigned int ncrt = (d ? d->size : 0); 1107 unsigned int off = (d == pc->ssl_pemfile_x509) ? 0 : 1; 1108 gnutls_pcert_st * const pcert_list = 1109 gnutls_malloc(sizeof(gnutls_pcert_st) * (off+ncrt)); 1110 if (NULL == pcert_list) { 1111 gnutls_certificate_free_credentials(ssl_cred); 1112 return GNUTLS_E_MEMORY_ERROR; 1113 } 1114 memset(pcert_list, 0, sizeof(gnutls_pcert_st) * (off+ncrt)); 1115 rc = 0; 1116 1117 if (off) { /*(add crt to chain if different from d)*/ 1118 /*assert(pc->ssl_pemfile_x509->size == 1)*/ 1119 gnutls_x509_crt_t *crts = 1120 (gnutls_x509_crt_t *)(void *)pc->ssl_pemfile_x509->data; 1121 rc = gnutls_pcert_import_x509(pcert_list, crts[0], 0); 1122 } 1123 1124 if (0 == rc && ncrt) { 1125 gnutls_x509_crt_t *crts = (gnutls_x509_crt_t *)(void *)d->data; 1126 #if GNUTLS_VERSION_NUMBER < 0x030400 1127 /*(GNUTLS_X509_CRT_LIST_SORT not needed; crts sorted when file read)*/ 1128 rc = gnutls_pcert_import_x509_list(pcert_list+off, crts, &ncrt, 0); 1129 #else /* manually import list, but note that sorting is not implemented */ 1130 rc = 0; 1131 for (unsigned int i = 0; i < ncrt; ++i) { 1132 rc = gnutls_pcert_import_x509(pcert_list+off+i, crts[i], 0); 1133 if (rc < 0) break; 1134 } 1135 #endif 1136 } 1137 ncrt += off; 1138 if (0 == rc) 1139 rc = gnutls_certificate_set_key(ssl_cred, NULL, 0, pcert_list, ncrt, 1140 pc->ssl_pemfile_pkey); 1141 if (rc < 0) { 1142 for (unsigned int i = 0; i < ncrt; ++i) 1143 gnutls_pcert_deinit(pcert_list+i); 1144 gnutls_free(pcert_list); 1145 gnutls_certificate_free_credentials(ssl_cred); 1146 elog(errh, __FILE__, __LINE__, rc, "gnutls_certificate_set_key()"); 1147 return rc; 1148 } 1149 /* XXX: gnutls_certificate_set_key() has an inconsistent implementation. 1150 * On success, key ownership is transferred, and so should not be freed, 1151 * but pcert_list is a shallow memcpy(), so gnutls_pcert_deinit() should 1152 * not be run on gnutls_pcert_st in list, though top-level list storage 1153 * should be freed. On failure, ownership is not transferred for either. */ 1154 gnutls_free(pcert_list); 1155 pc->ssl_pemfile_pkey = NULL; 1156 pc->ssl_cred = ssl_cred; 1157 1158 /* release lists used to configure pc->ssl_cred */ 1159 mod_gnutls_free_config_crts(pc->ssl_pemfile_x509); 1160 pc->ssl_pemfile_x509 = NULL; 1161 1162 return 0; 1163 } 1164 1165 1166 static void * 1167 network_gnutls_load_pemfile (server *srv, const buffer *pemfile, const buffer *privkey, const buffer *ssl_stapling_file) 1168 { 1169 #if 0 /* see comments in mod_gnutls_construct_crt_chain() above */ 1170 1171 gnutls_certificate_credentials_t ssl_cred = NULL; 1172 int rc; 1173 1174 rc = gnutls_certificate_allocate_credentials(&ssl_cred); 1175 if (rc < 0) return NULL; 1176 1177 rc = gnutls_certificate_set_x509_key_file2(ssl_cred, 1178 pemfile->ptr, privkey->ptr, 1179 GNUTLS_X509_FMT_PEM, NULL, 0); 1180 if (rc < 0) { 1181 elogf(srv->errh, __FILE__, __LINE__, rc, 1182 "gnutls_certificate_set_x509_key_file2(%s, %s)", 1183 pemfile->ptr, privkey->ptr); 1184 gnutls_certificate_free_credentials(ssl_cred); 1185 return NULL; 1186 } 1187 1188 plugin_cert *pc = malloc(sizeof(plugin_cert)); 1189 force_assert(pc); 1190 pc->ssl_cred = ssl_cred; 1191 pc->trust_inited = 0; 1192 1193 return pc; 1194 1195 #else 1196 1197 gnutls_datum_t *d = mod_gnutls_load_config_crts(pemfile->ptr, srv->errh); 1198 if (NULL == d) return NULL; 1199 if (0 == d->size) { 1200 mod_gnutls_free_config_crts(d); 1201 return NULL; 1202 } 1203 1204 gnutls_privkey_t pkey = mod_gnutls_load_config_pkey(privkey->ptr,srv->errh); 1205 if (NULL == pkey) { 1206 mod_gnutls_free_config_crts(d); 1207 return NULL; 1208 } 1209 1210 plugin_cert *pc = malloc(sizeof(plugin_cert)); 1211 force_assert(pc); 1212 pc->ssl_cred = NULL; 1213 pc->trust_inited = 0; 1214 pc->ssl_pemfile_x509 = d; 1215 pc->ssl_pemfile_pkey = pkey; 1216 pc->ssl_stapling_file= ssl_stapling_file; 1217 pc->ssl_stapling_loadts = 0; 1218 pc->ssl_stapling_nextts = 0; 1219 pc->must_staple = 1220 mod_gnutls_crt_must_staple(((gnutls_x509_crt_t *)(void *)d->data)[0]); 1221 1222 if (d->size > 1) { /*(certificate chain provided)*/ 1223 int rc = mod_gnutls_construct_crt_chain(pc, d, srv->errh); 1224 if (rc < 0) { 1225 mod_gnutls_free_config_crts(d); 1226 gnutls_privkey_deinit(pkey); 1227 free(pc); 1228 return NULL; 1229 } 1230 } 1231 1232 if (pc->ssl_stapling_file) { 1233 if (mod_gnutls_reload_stapling_file(srv, pc, log_epoch_secs) < 0) { 1234 /* continue without OCSP response if there is an error */ 1235 } 1236 } 1237 else if (pc->must_staple) { 1238 log_error(srv->errh, __FILE__, __LINE__, 1239 "certificate %s marked OCSP Must-Staple, " 1240 "but ssl.stapling-file not provided", pemfile->ptr); 1241 } 1242 1243 #if 0 1244 pc->notAfter = TIME64_CAST(gnutls_x509_crt_get_expiration_time( 1245 ((gnutls_x509_crt_t *)(void *)d->data)[0])); 1246 #endif 1247 1248 return pc; 1249 1250 #endif 1251 } 1252 1253 1254 #if GNUTLS_VERSION_NUMBER >= 0x030200 1255 1256 static int 1257 mod_gnutls_acme_tls_1 (handler_ctx *hctx) 1258 { 1259 buffer * const b = hctx->tmp_buf; 1260 const buffer * const name = &hctx->r->uri.authority; 1261 log_error_st * const errh = hctx->r->conf.errh; 1262 int rc = GNUTLS_E_INVALID_REQUEST; 1263 1264 /* check if acme-tls/1 protocol is enabled (path to dir of cert(s) is set)*/ 1265 if (!hctx->conf.ssl_acme_tls_1) 1266 return 0; 1267 1268 /* check if SNI set server name (required for acme-tls/1 protocol) 1269 * and perform simple path checks for no '/' 1270 * and no leading '.' (e.g. ignore "." or ".." or anything beginning '.') */ 1271 if (buffer_is_blank(name)) return rc; 1272 if (NULL != strchr(name->ptr, '/')) return rc; 1273 if (name->ptr[0] == '.') return rc; 1274 #if 0 1275 if (0 != http_request_host_policy(name, hctx->r->conf.http_parseopts, 443)) 1276 return rc; 1277 #endif 1278 buffer_copy_path_len2(b, BUF_PTR_LEN(hctx->conf.ssl_acme_tls_1), 1279 BUF_PTR_LEN(name)); 1280 1281 #if 0 1282 1283 buffer * const privkey = buffer_init(); 1284 buffer_append_str2(b, BUF_PTR_LEN(b), CONST_STR_LEN(".crt.pem")); 1285 buffer_append_string_len(privkey, CONST_STR_LEN(".key.pem")); 1286 1287 /*(similar to network_gnutls_load_pemfile() but propagates rc)*/ 1288 gnutls_certificate_credentials_t ssl_cred = NULL; 1289 rc = gnutls_certificate_allocate_credentials(&ssl_cred); 1290 if (rc < 0) { buffer_free(privkey); return rc; } 1291 rc = gnutls_certificate_set_x509_key_file2(ssl_cred, 1292 b->ptr, privkey->ptr, 1293 GNUTLS_X509_FMT_PEM, NULL, 0); 1294 if (rc < 0) { 1295 elogf(errh, __FILE__, __LINE__, rc, 1296 "failed to load acme-tls/1 cert (%s, %s)", 1297 b->ptr, privkey->ptr); 1298 buffer_free(privkey); 1299 gnutls_certificate_free_credentials(ssl_cred); 1300 return rc; 1301 } 1302 buffer_free(privkey); 1303 1304 #else 1305 1306 /* gnutls_certificate_set_x509_key_file2() does not securely wipe 1307 * sensitive data from memory, so take a few extra steps */ 1308 1309 /* similar to network_gnutls_load_pemfile() */ 1310 1311 uint32_t len = buffer_clen(b); 1312 buffer_append_string_len(b, CONST_STR_LEN(".crt.pem")); 1313 1314 gnutls_datum_t *d = mod_gnutls_load_config_crts(b->ptr, errh); 1315 if (NULL == d) return GNUTLS_E_FILE_ERROR; 1316 if (0 == d->size) { 1317 mod_gnutls_free_config_crts(d); 1318 return GNUTLS_E_FILE_ERROR; 1319 } 1320 1321 buffer_truncate(b, len); 1322 buffer_append_string_len(b, CONST_STR_LEN(".key.pem")); 1323 1324 gnutls_privkey_t pkey = mod_gnutls_load_config_pkey(b->ptr, errh); 1325 if (NULL == pkey) { 1326 mod_gnutls_free_config_crts(d); 1327 return GNUTLS_E_FILE_ERROR; 1328 } 1329 1330 plugin_cert pc; 1331 pc.ssl_cred = NULL; 1332 pc.trust_inited = 0; 1333 pc.ssl_pemfile_x509 = d; 1334 pc.ssl_pemfile_pkey = pkey; 1335 1336 rc = mod_gnutls_construct_crt_chain(&pc, d, errh); 1337 if (rc < 0) { 1338 mod_gnutls_free_config_crts(d); 1339 gnutls_privkey_deinit(pkey); 1340 return rc; 1341 } 1342 1343 gnutls_certificate_credentials_t ssl_cred = pc.ssl_cred; 1344 1345 #endif 1346 1347 hctx->acme_tls_1_cred = ssl_cred; /*(save ptr and free later)*/ 1348 1349 gnutls_credentials_clear(hctx->ssl); 1350 rc = gnutls_credentials_set(hctx->ssl, GNUTLS_CRD_CERTIFICATE, ssl_cred); 1351 if (rc < 0) { 1352 elogf(hctx->r->conf.errh, __FILE__, __LINE__, rc, 1353 "failed to set acme-tls/1 certificate for TLS server name %s", 1354 hctx->r->uri.authority.ptr); 1355 return rc; 1356 } 1357 1358 /*(acme-tls/1 is separate from certificate auth access to website)*/ 1359 gnutls_certificate_server_set_request(hctx->ssl, GNUTLS_CERT_IGNORE); 1360 1361 return GNUTLS_E_SUCCESS; /* 0 */ 1362 } 1363 1364 1365 static int 1366 mod_gnutls_alpn_h2_policy (handler_ctx * const hctx) 1367 { 1368 /*(currently called after handshake has completed)*/ 1369 #if 0 /* SNI omitted by client when connecting to IP instead of to name */ 1370 if (buffer_is_blank(&hctx->r->uri.authority)) { 1371 log_error(hctx->errh, __FILE__, __LINE__, 1372 "SSL: error ALPN h2 without SNI"); 1373 return -1; 1374 } 1375 #endif 1376 if (gnutls_protocol_get_version(hctx->ssl) < GNUTLS_TLS1_2) { 1377 /*(future: if DTLS supported by lighttpd, add DTLS condition)*/ 1378 log_error(hctx->errh, __FILE__, __LINE__, 1379 "SSL: error ALPN h2 requires TLSv1.2 or later"); 1380 return -1; 1381 } 1382 1383 return 0; 1384 } 1385 1386 1387 enum { 1388 MOD_GNUTLS_ALPN_HTTP11 = 1 1389 ,MOD_GNUTLS_ALPN_HTTP10 = 2 1390 ,MOD_GNUTLS_ALPN_H2 = 3 1391 ,MOD_GNUTLS_ALPN_ACME_TLS_1 = 4 1392 }; 1393 1394 1395 /* https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids */ 1396 static int 1397 mod_gnutls_ALPN (handler_ctx * const hctx, const unsigned char * const in, const unsigned int inlen) 1398 { 1399 /*(skip first two bytes which should match inlen-2)*/ 1400 for (unsigned int i = 2, n; i < inlen; i += n) { 1401 n = in[i++]; 1402 if (i+n > inlen || 0 == n) break; 1403 1404 switch (n) { 1405 case 2: /* "h2" */ 1406 if (in[i] == 'h' && in[i+1] == '2') { 1407 if (!hctx->r->conf.h2proto) continue; 1408 hctx->alpn = MOD_GNUTLS_ALPN_H2; 1409 if (hctx->r->handler_module == NULL)/*(e.g. not mod_sockproxy)*/ 1410 hctx->r->http_version = HTTP_VERSION_2; 1411 return GNUTLS_E_SUCCESS; 1412 } 1413 continue; 1414 case 8: /* "http/1.1" "http/1.0" */ 1415 if (0 == memcmp(in+i, "http/1.", 7)) { 1416 if (in[i+7] == '1') { 1417 hctx->alpn = MOD_GNUTLS_ALPN_HTTP11; 1418 return GNUTLS_E_SUCCESS; 1419 } 1420 if (in[i+7] == '0') { 1421 hctx->alpn = MOD_GNUTLS_ALPN_HTTP10; 1422 return GNUTLS_E_SUCCESS; 1423 } 1424 } 1425 continue; 1426 case 10: /* "acme-tls/1" */ 1427 if (0 == memcmp(in+i, "acme-tls/1", 10)) { 1428 int rc = mod_gnutls_acme_tls_1(hctx); 1429 if (0 == rc) { 1430 hctx->alpn = MOD_GNUTLS_ALPN_ACME_TLS_1; 1431 return GNUTLS_E_SUCCESS; 1432 } 1433 return rc; 1434 } 1435 continue; 1436 default: 1437 continue; 1438 } 1439 } 1440 1441 return GNUTLS_E_SUCCESS; 1442 } 1443 1444 #endif /* GNUTLS_VERSION_NUMBER >= 0x030200 */ 1445 1446 1447 static int 1448 mod_gnutls_SNI(handler_ctx * const hctx, 1449 const unsigned char *servername, unsigned int len) 1450 { 1451 /* https://www.gnutls.org/manual/gnutls.html#Virtual-hosts-and-credentials 1452 * figure the advertized name - the following hack relies on the fact that 1453 * this extension only supports DNS names, and due to a protocol bug cannot 1454 * be extended to support anything else. */ 1455 if (len < 5) return 0; 1456 len -= 5; 1457 servername += 5; 1458 request_st * const r = hctx->r; 1459 buffer_copy_string_len(&r->uri.scheme, CONST_STR_LEN("https")); 1460 1461 if (len >= 1024) { /*(expecting < 256; TLSEXT_MAXLEN_host_name is 255)*/ 1462 log_error(r->conf.errh, __FILE__, __LINE__, 1463 "GnuTLS: SNI name too long %.*s", (int)len, servername); 1464 return GNUTLS_E_INVALID_REQUEST; 1465 } 1466 1467 /* use SNI to patch mod_gnutls config and then reset COMP_HTTP_HOST */ 1468 buffer_copy_string_len_lc(&r->uri.authority, (const char *)servername, len); 1469 #if 0 1470 /*(r->uri.authority used below for configuration before request read; 1471 * revisit for h2)*/ 1472 if (0 != http_request_host_policy(&r->uri.authority, 1473 r->conf.http_parseopts, 443)) 1474 return GNUTLS_E_INVALID_REQUEST; 1475 #endif 1476 1477 r->conditional_is_valid |= (1 << COMP_HTTP_SCHEME) 1478 | (1 << COMP_HTTP_HOST); 1479 1480 mod_gnutls_patch_config(r, &hctx->conf); 1481 /* reset COMP_HTTP_HOST so that conditions re-run after request hdrs read */ 1482 /*(done in configfile-glue.c:config_cond_cache_reset() after request hdrs read)*/ 1483 /*config_cond_cache_reset_item(r, COMP_HTTP_HOST);*/ 1484 /*buffer_clear(&r->uri.authority);*/ 1485 1486 return 0; 1487 } 1488 1489 1490 static int 1491 mod_gnutls_client_hello_ext_cb(void *ctx, unsigned int tls_id, 1492 const unsigned char *data, unsigned int dlen) 1493 { 1494 switch (tls_id) { 1495 case 0: /* Server Name */ 1496 return mod_gnutls_SNI((handler_ctx *)ctx, data, dlen); 1497 #if GNUTLS_VERSION_NUMBER >= 0x030200 1498 case 16: /* ALPN */ 1499 return mod_gnutls_ALPN((handler_ctx *)ctx, data, dlen); 1500 #endif 1501 /*case 35:*/ /* Session Ticket */ 1502 default: 1503 break; 1504 } 1505 1506 return GNUTLS_E_SUCCESS; /* 0 */ 1507 } 1508 1509 1510 static int 1511 mod_gnutls_client_hello_hook(gnutls_session_t ssl, unsigned int htype, 1512 unsigned when, unsigned int incoming, 1513 const gnutls_datum_t *msg) 1514 { 1515 /*assert(htype == GNUTLS_HANDSHAKE_CLIENT_HELLO);*/ 1516 /*assert(when == GNUTLS_HOOK_PRE);*/ 1517 UNUSED(htype); 1518 UNUSED(when); 1519 UNUSED(incoming); 1520 1521 handler_ctx * const hctx = gnutls_session_get_ptr(ssl); 1522 #if GNUTLS_VERSION_NUMBER >= 0x030200 1523 /*(do not repeat if acme-tls/1 creds have been set 1524 * and still in handshake (hctx->alpn not unset yet))*/ 1525 if (hctx->alpn == MOD_GNUTLS_ALPN_ACME_TLS_1) 1526 return GNUTLS_E_SUCCESS; /* 0 */ 1527 #endif 1528 /* ??? why might this be called more than once ??? renegotiation? */ 1529 void *existing_cred = NULL; 1530 if (0 == gnutls_credentials_get(ssl, GNUTLS_CRD_CERTIFICATE, &existing_cred) 1531 && existing_cred) 1532 return GNUTLS_E_SUCCESS; /* 0 */ 1533 1534 int rc = gnutls_ext_raw_parse(hctx, mod_gnutls_client_hello_ext_cb, msg, 1535 GNUTLS_EXT_RAW_FLAG_TLS_CLIENT_HELLO); 1536 if (rc < 0) { 1537 log_error_st *errh = hctx->r->conf.errh; 1538 elog(errh, __FILE__, __LINE__, rc, "gnutls_ext_raw_parse()"); 1539 return rc; 1540 } 1541 1542 #if GNUTLS_VERSION_NUMBER >= 0x030200 1543 /* https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids */ 1544 static const gnutls_datum_t alpn_protos_http_acme[] = { 1545 { (unsigned char *)CONST_STR_LEN("h2") } 1546 ,{ (unsigned char *)CONST_STR_LEN("http/1.1") } 1547 ,{ (unsigned char *)CONST_STR_LEN("http/1.0") } 1548 ,{ (unsigned char *)CONST_STR_LEN("acme-tls/1") } 1549 }; 1550 unsigned int n = hctx->conf.ssl_acme_tls_1 ? 4 : 3; 1551 const gnutls_datum_t *alpn_protos = alpn_protos_http_acme; 1552 if (!hctx->r->conf.h2proto) { 1553 ++alpn_protos; 1554 --n; 1555 } 1556 /*unsigned int flags = GNUTLS_ALPN_SERVER_PRECEDENCE;*/ 1557 rc = gnutls_alpn_set_protocols(hctx->ssl, alpn_protos, n, 0); 1558 if (rc < 0) { 1559 log_error_st *errh = hctx->r->conf.errh; 1560 elog(errh, __FILE__, __LINE__, rc, "gnutls_alpn_set_protocols()"); 1561 return rc; 1562 } 1563 /*(skip below if creds already set for acme-tls/1 1564 * via mod_gnutls_client_hello_ext_cb())*/ 1565 if (hctx->alpn == MOD_GNUTLS_ALPN_ACME_TLS_1) 1566 return GNUTLS_E_SUCCESS; /* 0 */ 1567 #endif 1568 1569 #if 0 /* must enable before GnuTLS client hello hook */ 1570 /* GnuTLS returns an error here if TLSv1.3 (? key already set ?) */ 1571 /* see mod_gnutls_handle_con_accept() */ 1572 /* future: ? handle in mod_gnutls_client_hello_ext_cb() */ 1573 if (hctx->ssl_session_ticket && session_ticket_key.size) { 1574 /* XXX: NOT done: parse client hello for session ticket extension 1575 * and choose from among multiple keys */ 1576 rc = gnutls_session_ticket_enable_server(ssl, &session_ticket_key); 1577 if (rc < 0) { 1578 elog(hctx->r->conf.errh, __FILE__, __LINE__, rc, 1579 "gnutls_session_ticket_enable_server()"); 1580 return rc; 1581 } 1582 } 1583 #endif 1584 1585 if (NULL == hctx->conf.pc->ssl_cred) { 1586 rc = mod_gnutls_construct_crt_chain(hctx->conf.pc, 1587 hctx->conf.ssl_ca_file, 1588 hctx->r->conf.errh); 1589 if (rc < 0) return rc; 1590 } 1591 1592 gnutls_certificate_credentials_t ssl_cred = hctx->conf.pc->ssl_cred; 1593 1594 hctx->verify_status = ~0u; 1595 gnutls_certificate_request_t req = GNUTLS_CERT_IGNORE; 1596 if (hctx->conf.ssl_verifyclient) { 1597 /*(cred shared; settings should be consistent across site using cred)*/ 1598 /*(i.e. settings for client certs must not differ under this cred)*/ 1599 req = hctx->conf.ssl_verifyclient_enforce 1600 ? GNUTLS_CERT_REQUIRE 1601 : GNUTLS_CERT_REQUEST; 1602 gnutls_certificate_set_verify_function(ssl_cred, mod_gnutls_verify_cb); 1603 gnutls_certificate_set_verify_limits(ssl_cred, 8200 /*(default)*/, 1604 hctx->conf.ssl_verifyclient_depth); 1605 rc = mod_gnutls_verify_set_tlist(hctx, 1); /* for client cert request */ 1606 if (rc < 0) return rc; 1607 } 1608 gnutls_certificate_server_set_request(ssl, req); 1609 1610 #if GNUTLS_VERSION_NUMBER < 0x030600 1611 if (hctx->conf.dh_params) 1612 gnutls_certificate_set_dh_params(ssl_cred, hctx->conf.dh_params); 1613 #if GNUTLS_VERSION_NUMBER >= 0x030506 1614 else 1615 gnutls_certificate_set_known_dh_params(ssl_cred, GNUTLS_SEC_PARAM_HIGH); 1616 #endif 1617 #endif 1618 1619 rc = gnutls_credentials_set(ssl, GNUTLS_CRD_CERTIFICATE, ssl_cred); 1620 if (rc < 0) { 1621 elogf(hctx->r->conf.errh, __FILE__, __LINE__, rc, 1622 "failed to set SNI certificate for TLS server name %s", 1623 hctx->r->uri.authority.ptr); 1624 return rc; 1625 } 1626 1627 return GNUTLS_E_SUCCESS; /* 0 */ 1628 } 1629 1630 1631 static int 1632 mod_gnutls_ssl_conf_ciphersuites (server *srv, plugin_config_socket *s, buffer *ciphersuites, const buffer *cipherstring); 1633 1634 1635 static int 1636 mod_gnutls_ssl_conf_curves(server *srv, plugin_config_socket *s, const buffer *curvelist); 1637 1638 1639 static void 1640 mod_gnutls_ssl_conf_proto (server *srv, plugin_config_socket *s, const buffer *minb, const buffer *maxb); 1641 1642 1643 static int 1644 mod_gnutls_ssl_conf_cmd (server *srv, plugin_config_socket *s) 1645 { 1646 /* reference: 1647 * https://www.openssl.org/docs/man1.1.1/man3/SSL_CONF_cmd.html */ 1648 int rc = 0; 1649 buffer *cipherstring = NULL; 1650 buffer *ciphersuites = NULL; 1651 buffer *minb = NULL; 1652 buffer *maxb = NULL; 1653 buffer *curves = NULL; 1654 1655 for (size_t i = 0; i < s->ssl_conf_cmd->used; ++i) { 1656 data_string *ds = (data_string *)s->ssl_conf_cmd->data[i]; 1657 if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("CipherString"))) 1658 cipherstring = &ds->value; 1659 else if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("Ciphersuites"))) 1660 ciphersuites = &ds->value; 1661 else if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("Curves")) 1662 || buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("Groups"))) 1663 curves = &ds->value; 1664 else if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("MaxProtocol"))) 1665 maxb = &ds->value; 1666 else if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("MinProtocol"))) 1667 minb = &ds->value; 1668 else if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("Protocol"))) { 1669 /* openssl config for Protocol=... is complex and deprecated */ 1670 log_error(srv->errh, __FILE__, __LINE__, 1671 "GnuTLS: ssl.openssl.ssl-conf-cmd %s ignored; " 1672 "use MinProtocol=... and MaxProtocol=... instead", 1673 ds->key.ptr); 1674 } 1675 else if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("Options"))) { 1676 for (char *v = ds->value.ptr, *e; *v; v = e) { 1677 while (*v == ' ' || *v == '\t' || *v == ',') ++v; 1678 int flag = 1; 1679 if (*v == '-') { 1680 flag = 0; 1681 ++v; 1682 } 1683 for (e = v; light_isalpha(*e); ++e) ; 1684 switch ((int)(e-v)) { 1685 case 11: 1686 if (buffer_eq_icase_ssn(v, "Compression", 11)) { 1687 /* GnuTLS 3.6.0+ no longer implements 1688 * any support for compression */ 1689 if (!flag) continue; 1690 } 1691 break; 1692 case 13: 1693 if (buffer_eq_icase_ssn(v, "SessionTicket", 13)) { 1694 /*(translates to "%NO_TICKETS" priority str if !flag)*/ 1695 s->ssl_session_ticket = flag; 1696 continue; 1697 } 1698 break; 1699 case 16: 1700 if (buffer_eq_icase_ssn(v, "ServerPreference", 16)) { 1701 /*(translates to "%SERVER_PRECEDENCE" priority string)*/ 1702 s->ssl_honor_cipher_order = flag; 1703 continue; 1704 } 1705 break; 1706 default: 1707 break; 1708 } 1709 /* warn if not explicitly handled or ignored above */ 1710 if (!flag) --v; 1711 log_error(srv->errh, __FILE__, __LINE__, 1712 "GnuTLS: ssl.openssl.ssl-conf-cmd Options %.*s " 1713 "ignored", (int)(e-v), v); 1714 } 1715 } 1716 else if (buffer_eq_icase_slen(&ds->key, 1717 CONST_STR_LEN("gnutls-override"))) { 1718 s->priority_override = &ds->value; 1719 } 1720 #if 0 1721 else if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("..."))) { 1722 } 1723 #endif 1724 else { 1725 /* warn if not explicitly handled or ignored above */ 1726 log_error(srv->errh, __FILE__, __LINE__, 1727 "GnuTLS: ssl.openssl.ssl-conf-cmd %s ignored", 1728 ds->key.ptr); 1729 } 1730 } 1731 1732 if (minb || maxb) /*(if at least one was set)*/ 1733 mod_gnutls_ssl_conf_proto(srv, s, minb, maxb); 1734 1735 if (!mod_gnutls_ssl_conf_ciphersuites(srv, s, ciphersuites, cipherstring)) 1736 rc = -1; 1737 1738 if (curves) { 1739 if (!s->ssl_ec_curve) 1740 buffer_append_string_len(&s->priority_str, 1741 CONST_STR_LEN("-CURVE-ALL:")); 1742 if (!mod_gnutls_ssl_conf_curves(srv, s, curves)) 1743 rc = -1; 1744 } 1745 1746 return rc; 1747 } 1748 1749 1750 static int 1751 network_init_ssl (server *srv, plugin_config_socket *s, plugin_data *p) 1752 { 1753 int rc; 1754 UNUSED(p); 1755 1756 /* construct GnuTLS "priority" string 1757 * 1758 * default: NORMAL (since GnuTLS 3.3.0, could also use NULL for defaults) 1759 * SUITEB128 and SUITEB192 are stricter than NORMAL 1760 * (and are attempted to be supported in mod_gnutls_ssl_conf_ciphersuites()) 1761 * 1762 * gnutls defaults to %PARTIAL_RENEGOTIATION (see manual) 1763 * mod_gnutls does not attempt to support 1764 * hctx->conf.ssl_disable_client_renegotiation == 0 1765 * though possible with %UNSAFE_RENEGOTATION 1766 */ 1767 1768 #if GNUTLS_VERSION_NUMBER < 0x030600 1769 /* GnuTLS 3.6.0+ no longer implements any support for compression, 1770 * but we still explicitly disable for earlier versions */ 1771 buffer_append_string_len(&s->priority_str, 1772 CONST_STR_LEN("-COMP_ALL:+COMP-NULL:")); 1773 #endif 1774 1775 if (s->ssl_cipher_list) { 1776 if (!mod_gnutls_ssl_conf_ciphersuites(srv,s,NULL,s->ssl_cipher_list)) 1777 return -1; 1778 } 1779 1780 #if GNUTLS_VERSION_NUMBER < 0x030600 1781 if (s->ssl_dh_file) { 1782 /* "Prior to GnuTLS 3.6.0 for the ephemeral or anonymous Diffie-Hellman 1783 * (DH) TLS ciphersuites the application was required to generate or 1784 * provide DH parameters. That is no longer necessary as GnuTLS utilizes 1785 * DH parameters and negotiation from [RFC7919]." 1786 */ 1787 /* In other words, the following should not be used in 3.6.0 or later: 1788 * gnutls_certificate_set_dh_params() 1789 * gnutls_certificate_set_known_dh_params() 1790 * However, if support is implemented in mod_gnutls, must also free in 1791 * mod_gnutls_free_config() as gnutls_certificate_free_credentials() 1792 * does not free RSA or DH params manually associated with credential */ 1793 gnutls_datum_t f = { NULL, 0 }; 1794 rc = gnutls_dh_params_init(&s->dh_params); 1795 if (rc < 0) return -1; 1796 rc = mod_gnutls_load_file(s->ssl_dh_file->ptr, &f, srv->errh); 1797 if (rc < 0) return -1; 1798 rc = gnutls_dh_params_import_pkcs3(s->dh_params,&f,GNUTLS_X509_FMT_PEM); 1799 mod_gnutls_datum_wipe(&f); 1800 if (rc < 0) { 1801 elogf(srv->errh, __FILE__, __LINE__, rc, 1802 "gnutls_dh_params_import_pkcs3() %s", s->ssl_dh_file->ptr); 1803 return -1; 1804 } 1805 } 1806 else { 1807 #if GNUTLS_VERSION_NUMBER < 0x030506 1808 /* (this might take a while; you should upgrade to newer gnutls) */ 1809 rc = gnutls_dh_params_init(&s->dh_params); 1810 if (rc < 0) return -1; 1811 unsigned int bits = 1812 gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_HIGH); 1813 rc = gnutls_dh_params_generate2(s->dh_params, bits); 1814 if (rc < 0) { 1815 elogf(srv->errh, __FILE__, __LINE__, rc, 1816 "gnutls_dh_params_generate2()"); 1817 return -1; 1818 } 1819 #endif 1820 } 1821 #endif /* GNUTLS_VERSION_NUMBER < 0x030600 */ 1822 1823 if (s->ssl_ec_curve) { 1824 buffer_append_string_len(&s->priority_str, 1825 CONST_STR_LEN("-CURVE-ALL:")); 1826 if (!mod_gnutls_ssl_conf_curves(srv, s, s->ssl_ec_curve)) 1827 return -1; 1828 } 1829 1830 if (s->ssl_conf_cmd && s->ssl_conf_cmd->used) { 1831 if (0 != mod_gnutls_ssl_conf_cmd(srv, s)) return -1; 1832 } 1833 1834 if (s->ssl_honor_cipher_order) 1835 buffer_append_string_len(&s->priority_str, 1836 CONST_STR_LEN("%SERVER_PRECEDENCE:")); 1837 1838 if (!s->ssl_session_ticket) 1839 buffer_append_string_len(&s->priority_str, 1840 CONST_STR_LEN("%NO_TICKETS:")); 1841 1842 if (!s->ssl_use_sslv3 && !s->ssl_use_sslv2 1843 && NULL == strstr(s->priority_str.ptr, "-VERS-ALL:")) 1844 mod_gnutls_ssl_conf_proto(srv, s, NULL, NULL); 1845 1846 /* explicitly disable SSLv3 unless enabled in config 1847 * (gnutls library would also need to be compiled with legacy support) */ 1848 if (!s->ssl_use_sslv3) 1849 buffer_append_string_len(&s->priority_str, 1850 CONST_STR_LEN("-VERS-SSL3.0:")); 1851 1852 /* gnutls_priority_init2() is available since GnuTLS 3.6.3 and could be 1853 * called once with s->priority_base, and a second time with s->priority_str 1854 * and GNUTLS_PRIORITY_INIT_DEF_APPEND, but preserve compat with earlier 1855 * GnuTLS by concatenating into a single priority string */ 1856 1857 buffer *b = srv->tmp_buf; 1858 if (NULL == s->priority_base) s->priority_base = "NORMAL"; 1859 buffer_copy_string_len(b, s->priority_base, strlen(s->priority_base)); 1860 if (!buffer_is_blank(&s->priority_str)) { 1861 buffer_append_char(b, ':'); 1862 uint32_t len = buffer_clen(&s->priority_str); 1863 if (s->priority_str.ptr[len-1] == ':') 1864 --len; /* remove trailing ':' */ 1865 buffer_append_string_len(b, s->priority_str.ptr, len); 1866 } 1867 1868 if (s->priority_override && !buffer_is_blank(s->priority_override)) { 1869 b = s->priority_override; 1870 s->ssl_session_ticket = (NULL == strstr(b->ptr, "%NO_TICKET")); 1871 } 1872 1873 if (p->defaults.ssl_log_noise) 1874 log_error(srv->errh, __FILE__, __LINE__, 1875 "debug: GnuTLS priority string: %s", b->ptr); 1876 1877 const char *err_pos; 1878 rc = gnutls_priority_init(&s->priority_cache, b->ptr, &err_pos); 1879 if (rc < 0) { 1880 elogf(srv->errh, __FILE__, __LINE__, rc, 1881 "gnutls_priority_init() error near %s", err_pos); 1882 return -1; 1883 } 1884 1885 return 0; 1886 } 1887 1888 1889 static int 1890 mod_gnutls_set_defaults_sockets(server *srv, plugin_data *p) 1891 { 1892 static const config_plugin_keys_t cpk[] = { 1893 { CONST_STR_LEN("ssl.engine"), 1894 T_CONFIG_BOOL, 1895 T_CONFIG_SCOPE_SOCKET } 1896 ,{ CONST_STR_LEN("ssl.cipher-list"), 1897 T_CONFIG_STRING, 1898 T_CONFIG_SCOPE_SOCKET } 1899 ,{ CONST_STR_LEN("ssl.honor-cipher-order"), 1900 T_CONFIG_BOOL, 1901 T_CONFIG_SCOPE_SOCKET } 1902 ,{ CONST_STR_LEN("ssl.dh-file"), 1903 T_CONFIG_STRING, 1904 T_CONFIG_SCOPE_SOCKET } 1905 ,{ CONST_STR_LEN("ssl.ec-curve"), 1906 T_CONFIG_STRING, 1907 T_CONFIG_SCOPE_SOCKET } 1908 ,{ CONST_STR_LEN("ssl.openssl.ssl-conf-cmd"), 1909 T_CONFIG_ARRAY_KVSTRING, 1910 T_CONFIG_SCOPE_SOCKET } 1911 ,{ CONST_STR_LEN("ssl.pemfile"), /* included to process global scope */ 1912 T_CONFIG_STRING, 1913 T_CONFIG_SCOPE_CONNECTION } 1914 ,{ CONST_STR_LEN("ssl.empty-fragments"), 1915 T_CONFIG_BOOL, 1916 T_CONFIG_SCOPE_SOCKET } 1917 ,{ CONST_STR_LEN("ssl.use-sslv2"), 1918 T_CONFIG_BOOL, 1919 T_CONFIG_SCOPE_SOCKET } 1920 ,{ CONST_STR_LEN("ssl.use-sslv3"), 1921 T_CONFIG_BOOL, 1922 T_CONFIG_SCOPE_SOCKET } 1923 ,{ CONST_STR_LEN("ssl.stek-file"), 1924 T_CONFIG_STRING, 1925 T_CONFIG_SCOPE_SERVER } 1926 ,{ NULL, 0, 1927 T_CONFIG_UNSET, 1928 T_CONFIG_SCOPE_UNSET } 1929 }; 1930 static const buffer default_ssl_cipher_list = { CONST_STR_LEN("HIGH"), 0 }; 1931 1932 p->ssl_ctxs = calloc(srv->config_context->used, sizeof(plugin_ssl_ctx)); 1933 force_assert(p->ssl_ctxs); 1934 1935 int rc = HANDLER_GO_ON; 1936 plugin_data_base srvplug; 1937 memset(&srvplug, 0, sizeof(srvplug)); 1938 plugin_data_base * const ps = &srvplug; 1939 if (!config_plugin_values_init(srv, ps, cpk, "mod_gnutls")) 1940 return HANDLER_ERROR; 1941 1942 plugin_config_socket defaults; 1943 memset(&defaults, 0, sizeof(defaults)); 1944 defaults.ssl_honor_cipher_order = 1; /* default server preference for PFS */ 1945 defaults.ssl_session_ticket = 1; /* enabled by default */ 1946 defaults.ssl_cipher_list = &default_ssl_cipher_list; 1947 1948 /* process and validate config directives for global and $SERVER["socket"] 1949 * (init i to 0 if global context; to 1 to skip empty global context) */ 1950 for (int i = !ps->cvlist[0].v.u2[1]; i < ps->nconfig; ++i) { 1951 config_cond_info cfginfo; 1952 config_get_config_cond_info(&cfginfo, (uint32_t)ps->cvlist[i].k_id); 1953 int is_socket_scope = (0 == i || cfginfo.comp == COMP_SERVER_SOCKET); 1954 int count_not_engine = 0; 1955 1956 plugin_config_socket conf; 1957 memcpy(&conf, &defaults, sizeof(conf)); 1958 1959 /*(preserve prior behavior; not inherited)*/ 1960 /*(forcing inheritance might break existing configs where SSL is enabled 1961 * by default in the global scope, but not $SERVER["socket"]=="*:80") */ 1962 conf.ssl_enabled = 0; 1963 1964 config_plugin_value_t *cpv = ps->cvlist + ps->cvlist[i].v.u2[0]; 1965 for (; -1 != cpv->k_id; ++cpv) { 1966 /* ignore ssl.pemfile (k_id=6); included to process global scope */ 1967 if (!is_socket_scope && cpv->k_id != 6) { 1968 log_error(srv->errh, __FILE__, __LINE__, 1969 "GnuTLS: %s is valid only in global scope or " 1970 "$SERVER[\"socket\"] condition", cpk[cpv->k_id].k); 1971 continue; 1972 } 1973 ++count_not_engine; 1974 switch (cpv->k_id) { 1975 case 0: /* ssl.engine */ 1976 conf.ssl_enabled = (0 != cpv->v.u); 1977 --count_not_engine; 1978 break; 1979 case 1: /* ssl.cipher-list */ 1980 if (!buffer_is_blank(cpv->v.b)) 1981 conf.ssl_cipher_list = cpv->v.b; 1982 break; 1983 case 2: /* ssl.honor-cipher-order */ 1984 conf.ssl_honor_cipher_order = (0 != cpv->v.u); 1985 break; 1986 case 3: /* ssl.dh-file */ 1987 #if GNUTLS_VERSION_NUMBER < 0x030600 1988 if (!buffer_is_blank(cpv->v.b)) 1989 conf.ssl_dh_file = cpv->v.b; 1990 #else 1991 log_error(srv->errh, __FILE__, __LINE__, 1992 "GnuTLS: ignoring ssl.dh-file; " 1993 "obsoleted in GnuTLS 3.6.0 and later implementing RFC7919"); 1994 #endif 1995 break; 1996 case 4: /* ssl.ec-curve */ 1997 if (!buffer_is_blank(cpv->v.b)) 1998 conf.ssl_ec_curve = cpv->v.b; 1999 break; 2000 case 5: /* ssl.openssl.ssl-conf-cmd */ 2001 *(const array **)&conf.ssl_conf_cmd = cpv->v.a; 2002 break; 2003 case 6: /* ssl.pemfile */ 2004 /* ignore here; included to process global scope when 2005 * ssl.pemfile is set, but ssl.engine is not "enable" */ 2006 break; 2007 case 7: /* ssl.empty-fragments */ 2008 conf.ssl_empty_fragments = (0 != cpv->v.u); 2009 log_error(srv->errh, __FILE__, __LINE__, 2010 "GnuTLS: ignoring ssl.empty-fragments; openssl-specific " 2011 "counter-measure against a SSL 3.0/TLS 1.0 protocol " 2012 "vulnerability affecting CBC ciphers, which cannot be handled" 2013 " by some broken (Microsoft) SSL implementations."); 2014 break; 2015 case 8: /* ssl.use-sslv2 */ 2016 conf.ssl_use_sslv2 = (0 != cpv->v.u); 2017 log_error(srv->errh, __FILE__, __LINE__, "GnuTLS: " 2018 "ssl.use-sslv2 is deprecated and will soon be removed. " 2019 "Many modern TLS libraries no longer support SSLv2."); 2020 break; 2021 case 9: /* ssl.use-sslv3 */ 2022 conf.ssl_use_sslv3 = (0 != cpv->v.u); 2023 log_error(srv->errh, __FILE__, __LINE__, "GnuTLS: " 2024 "ssl.use-sslv3 is deprecated and will soon be removed. " 2025 "Many modern TLS libraries no longer support SSLv3. " 2026 "If needed, use: " 2027 "ssl.openssl.ssl-conf-cmd = (\"MinProtocol\" => \"SSLv3\")"); 2028 break; 2029 case 10:/* ssl.stek-file */ 2030 if (!buffer_is_blank(cpv->v.b)) 2031 p->ssl_stek_file = cpv->v.b->ptr; 2032 break; 2033 default:/* should not happen */ 2034 break; 2035 } 2036 } 2037 if (HANDLER_GO_ON != rc) break; 2038 if (0 == i) memcpy(&defaults, &conf, sizeof(conf)); 2039 2040 if (0 != i && !conf.ssl_enabled) continue; 2041 2042 /* fill plugin_config_socket with global context then $SERVER["socket"] 2043 * only for directives directly in current $SERVER["socket"] condition*/ 2044 2045 /*conf.pc = p->defaults.pc;*/ 2046 conf.ssl_verifyclient = p->defaults.ssl_verifyclient; 2047 conf.ssl_verifyclient_enforce = p->defaults.ssl_verifyclient_enforce; 2048 conf.ssl_verifyclient_depth = p->defaults.ssl_verifyclient_depth; 2049 2050 int sidx = ps->cvlist[i].k_id; 2051 for (int j = !p->cvlist[0].v.u2[1]; j < p->nconfig; ++j) { 2052 if (p->cvlist[j].k_id != sidx) continue; 2053 /*if (0 == sidx) break;*//*(repeat to get ssl_pemfile,ssl_privkey)*/ 2054 cpv = p->cvlist + p->cvlist[j].v.u2[0]; 2055 for (; -1 != cpv->k_id; ++cpv) { 2056 ++count_not_engine; 2057 switch (cpv->k_id) { 2058 case 0: /* ssl.pemfile */ 2059 if (cpv->vtype == T_CONFIG_LOCAL) 2060 conf.pc = cpv->v.v; 2061 break; 2062 case 7: /* ssl.verifyclient.activate */ 2063 conf.ssl_verifyclient = (0 != cpv->v.u); 2064 break; 2065 case 8: /* ssl.verifyclient.enforce */ 2066 conf.ssl_verifyclient_enforce = (0 != cpv->v.u); 2067 break; 2068 case 9: /* ssl.verifyclient.depth */ 2069 conf.ssl_verifyclient_depth = (unsigned char)cpv->v.shrt; 2070 break; 2071 default: 2072 break; 2073 } 2074 } 2075 break; 2076 } 2077 2078 if (NULL == conf.pc) { 2079 if (0 == i && !conf.ssl_enabled) continue; 2080 if (0 != i) { 2081 /* inherit ssl settings from global scope 2082 * (if only ssl.engine = "enable" and no other ssl.* settings) 2083 * (This is for convenience when defining both IPv4 and IPv6 2084 * and desiring to inherit the ssl config from global context 2085 * without having to duplicate the directives)*/ 2086 if (count_not_engine 2087 || (conf.ssl_enabled 2088 && NULL == p->ssl_ctxs[0].priority_cache)) { 2089 log_error(srv->errh, __FILE__, __LINE__, 2090 "GnuTLS: ssl.pemfile has to be set in same " 2091 "$SERVER[\"socket\"] scope as other ssl.* directives, " 2092 "unless only ssl.engine is set, inheriting ssl.* from " 2093 "global scope"); 2094 rc = HANDLER_ERROR; 2095 continue; 2096 } 2097 plugin_ssl_ctx * const s = p->ssl_ctxs + sidx; 2098 *s = *p->ssl_ctxs;/*(copy struct of ssl_ctx from global scope)*/ 2099 continue; 2100 } 2101 /* PEM file is required */ 2102 log_error(srv->errh, __FILE__, __LINE__, 2103 "GnuTLS: ssl.pemfile has to be set when ssl.engine = \"enable\""); 2104 rc = HANDLER_ERROR; 2105 continue; 2106 } 2107 2108 /* (initialize once if module enabled) */ 2109 if (!mod_gnutls_init_once_gnutls()) { 2110 rc = HANDLER_ERROR; 2111 break; 2112 } 2113 2114 /* configure ssl_ctx for socket */ 2115 2116 /*conf.ssl_ctx = NULL;*//*(filled by network_init_ssl() even on error)*/ 2117 if (0 == network_init_ssl(srv, &conf, p)) { 2118 plugin_ssl_ctx * const s = p->ssl_ctxs + sidx; 2119 s->ssl_session_ticket = conf.ssl_session_ticket; 2120 s->priority_cache = conf.priority_cache; 2121 #if GNUTLS_VERSION_NUMBER < 0x030600 2122 s->dh_params = conf.dh_params; 2123 #endif 2124 } 2125 else { 2126 gnutls_priority_deinit(conf.priority_cache); 2127 #if GNUTLS_VERSION_NUMBER < 0x030600 2128 gnutls_dh_params_deinit(conf.dh_params); 2129 #endif 2130 rc = HANDLER_ERROR; 2131 } 2132 free(conf.priority_str.ptr); 2133 } 2134 2135 if (rc == HANDLER_GO_ON && ssl_is_init) 2136 mod_gnutls_session_ticket_key_check(srv, p, log_epoch_secs); 2137 2138 free(srvplug.cvlist); 2139 return rc; 2140 } 2141 2142 2143 SETDEFAULTS_FUNC(mod_gnutls_set_defaults) 2144 { 2145 static const config_plugin_keys_t cpk[] = { 2146 { CONST_STR_LEN("ssl.pemfile"), 2147 T_CONFIG_STRING, 2148 T_CONFIG_SCOPE_CONNECTION } 2149 ,{ CONST_STR_LEN("ssl.privkey"), 2150 T_CONFIG_STRING, 2151 T_CONFIG_SCOPE_CONNECTION } 2152 ,{ CONST_STR_LEN("ssl.ca-file"), 2153 T_CONFIG_STRING, 2154 T_CONFIG_SCOPE_CONNECTION } 2155 ,{ CONST_STR_LEN("ssl.ca-dn-file"), 2156 T_CONFIG_STRING, 2157 T_CONFIG_SCOPE_CONNECTION } 2158 ,{ CONST_STR_LEN("ssl.ca-crl-file"), 2159 T_CONFIG_STRING, 2160 T_CONFIG_SCOPE_CONNECTION } 2161 ,{ CONST_STR_LEN("ssl.read-ahead"), 2162 T_CONFIG_BOOL, 2163 T_CONFIG_SCOPE_CONNECTION } 2164 ,{ CONST_STR_LEN("ssl.disable-client-renegotiation"), 2165 T_CONFIG_BOOL, 2166 T_CONFIG_SCOPE_CONNECTION } 2167 ,{ CONST_STR_LEN("ssl.verifyclient.activate"), 2168 T_CONFIG_BOOL, 2169 T_CONFIG_SCOPE_CONNECTION } 2170 ,{ CONST_STR_LEN("ssl.verifyclient.enforce"), 2171 T_CONFIG_BOOL, 2172 T_CONFIG_SCOPE_CONNECTION } 2173 ,{ CONST_STR_LEN("ssl.verifyclient.depth"), 2174 T_CONFIG_SHORT, 2175 T_CONFIG_SCOPE_CONNECTION } 2176 ,{ CONST_STR_LEN("ssl.verifyclient.username"), 2177 T_CONFIG_STRING, 2178 T_CONFIG_SCOPE_CONNECTION } 2179 ,{ CONST_STR_LEN("ssl.verifyclient.exportcert"), 2180 T_CONFIG_BOOL, 2181 T_CONFIG_SCOPE_CONNECTION } 2182 ,{ CONST_STR_LEN("ssl.acme-tls-1"), 2183 T_CONFIG_STRING, 2184 T_CONFIG_SCOPE_CONNECTION } 2185 ,{ CONST_STR_LEN("ssl.stapling-file"), 2186 T_CONFIG_STRING, 2187 T_CONFIG_SCOPE_CONNECTION } 2188 ,{ CONST_STR_LEN("debug.log-ssl-noise"), 2189 T_CONFIG_BOOL, 2190 T_CONFIG_SCOPE_CONNECTION } 2191 ,{ CONST_STR_LEN("ssl.verifyclient.ca-file"), 2192 T_CONFIG_STRING, 2193 T_CONFIG_SCOPE_CONNECTION } 2194 ,{ CONST_STR_LEN("ssl.verifyclient.ca-dn-file"), 2195 T_CONFIG_STRING, 2196 T_CONFIG_SCOPE_CONNECTION } 2197 ,{ CONST_STR_LEN("ssl.verifyclient.ca-crl-file"), 2198 T_CONFIG_STRING, 2199 T_CONFIG_SCOPE_CONNECTION } 2200 ,{ NULL, 0, 2201 T_CONFIG_UNSET, 2202 T_CONFIG_SCOPE_UNSET } 2203 }; 2204 2205 plugin_data * const p = p_d; 2206 p->srv = srv; 2207 if (!config_plugin_values_init(srv, p, cpk, "mod_gnutls")) 2208 return HANDLER_ERROR; 2209 2210 /* process and validate config directives 2211 * (init i to 0 if global context; to 1 to skip empty global context) */ 2212 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 2213 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 2214 config_plugin_value_t *pemfile = NULL; 2215 config_plugin_value_t *privkey = NULL; 2216 const buffer *ssl_stapling_file = NULL; 2217 for (; -1 != cpv->k_id; ++cpv) { 2218 switch (cpv->k_id) { 2219 case 0: /* ssl.pemfile */ 2220 if (!buffer_is_blank(cpv->v.b)) pemfile = cpv; 2221 break; 2222 case 1: /* ssl.privkey */ 2223 if (!buffer_is_blank(cpv->v.b)) privkey = cpv; 2224 break; 2225 case 15:/* ssl.verifyclient.ca-file */ 2226 if (cpv->k_id == 15) cpv->k_id = 2; 2227 __attribute_fallthrough__ 2228 case 16:/* ssl.verifyclient.ca-dn-file */ 2229 if (cpv->k_id == 16) cpv->k_id = 3; 2230 __attribute_fallthrough__ 2231 case 2: /* ssl.ca-file */ 2232 case 3: /* ssl.ca-dn-file */ 2233 if (!buffer_is_blank(cpv->v.b)) { 2234 gnutls_datum_t *d = 2235 mod_gnutls_load_config_crts(cpv->v.b->ptr, srv->errh); 2236 if (d != NULL) { 2237 cpv->vtype = T_CONFIG_LOCAL; 2238 cpv->v.v = d; 2239 } 2240 else { 2241 log_error(srv->errh, __FILE__, __LINE__, 2242 "%s = %s", cpk[cpv->k_id].k, cpv->v.b->ptr); 2243 return HANDLER_ERROR; 2244 } 2245 } 2246 break; 2247 case 17:/* ssl.verifyclient.ca-crl-file */ 2248 cpv->k_id = 4; 2249 __attribute_fallthrough__ 2250 case 4: /* ssl.ca-crl-file */ 2251 if (!buffer_is_blank(cpv->v.b)) { 2252 gnutls_datum_t *d = 2253 mod_gnutls_load_config_crls(cpv->v.b->ptr, srv->errh); 2254 if (d != NULL) { 2255 cpv->vtype = T_CONFIG_LOCAL; 2256 cpv->v.v = d; 2257 } 2258 else { 2259 log_error(srv->errh, __FILE__, __LINE__, 2260 "%s = %s", cpk[cpv->k_id].k, cpv->v.b->ptr); 2261 return HANDLER_ERROR; 2262 } 2263 } 2264 break; 2265 case 5: /* ssl.read-ahead */ 2266 break; 2267 case 6: /* ssl.disable-client-renegotiation */ 2268 /* (force disabled, the default, if HTTP/2 enabled in server) */ 2269 if (srv->srvconf.h2proto) 2270 cpv->v.u = 1; /* disable client renegotiation */ 2271 break; 2272 case 7: /* ssl.verifyclient.activate */ 2273 case 8: /* ssl.verifyclient.enforce */ 2274 break; 2275 case 9: /* ssl.verifyclient.depth */ 2276 if (cpv->v.shrt > 255) { 2277 log_error(srv->errh, __FILE__, __LINE__, 2278 "GnuTLS: %s is absurdly large (%hu); limiting to 255", 2279 cpk[cpv->k_id].k, cpv->v.shrt); 2280 cpv->v.shrt = 255; 2281 } 2282 break; 2283 case 10:/* ssl.verifyclient.username */ 2284 if (buffer_is_blank(cpv->v.b)) 2285 cpv->v.b = NULL; 2286 break; 2287 case 11:/* ssl.verifyclient.exportcert */ 2288 break; 2289 case 12:/* ssl.acme-tls-1 */ 2290 if (buffer_is_blank(cpv->v.b)) 2291 cpv->v.b = NULL; 2292 break; 2293 case 13:/* ssl.stapling-file */ 2294 if (!buffer_is_blank(cpv->v.b)) 2295 ssl_stapling_file = cpv->v.b; 2296 break; 2297 case 14:/* debug.log-ssl-noise */ 2298 #if 0 /*(handled further above)*/ 2299 case 15:/* ssl.verifyclient.ca-file */ 2300 case 16:/* ssl.verifyclient.ca-dn-file */ 2301 case 17:/* ssl.verifyclient.ca-crl-file */ 2302 #endif 2303 break; 2304 default:/* should not happen */ 2305 break; 2306 } 2307 } 2308 2309 if (pemfile) { 2310 if (NULL == privkey) privkey = pemfile; 2311 pemfile->v.v = 2312 network_gnutls_load_pemfile(srv, pemfile->v.b, privkey->v.b, 2313 ssl_stapling_file); 2314 if (pemfile->v.v) 2315 pemfile->vtype = T_CONFIG_LOCAL; 2316 else 2317 return HANDLER_ERROR; 2318 } 2319 } 2320 2321 p->defaults.ssl_verifyclient = 0; 2322 p->defaults.ssl_verifyclient_enforce = 1; 2323 p->defaults.ssl_verifyclient_depth = 9; 2324 p->defaults.ssl_verifyclient_export_cert = 0; 2325 p->defaults.ssl_disable_client_renegotiation = 1; 2326 p->defaults.ssl_read_ahead = 0; 2327 2328 /* initialize p->defaults from global config context */ 2329 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 2330 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 2331 if (-1 != cpv->k_id) 2332 mod_gnutls_merge_config(&p->defaults, cpv); 2333 } 2334 2335 return mod_gnutls_set_defaults_sockets(srv, p); 2336 } 2337 2338 2339 /* local_send_buffer is a static buffer of size (LOCAL_SEND_BUFSIZE) 2340 * 2341 * buffer is allocated once, is NOT realloced (note: not thread-safe) 2342 * */ 2343 2344 /* copy small mem chunks into single large buffer 2345 * before gnutls_record_send() to reduce number times 2346 * write() called underneath gnutls_record_send() and 2347 * potentially reduce number of packets generated if TCP_NODELAY 2348 * Alternatively, GnuTLS provides gnutls_record_cork() and 2349 * gnutls_record_uncork(), not currently used by mod_gnutls */ 2350 2351 2352 __attribute_cold__ 2353 static int 2354 mod_gnutls_write_err(connection *con, handler_ctx *hctx, int wr, size_t wr_len) 2355 { 2356 switch (wr) { 2357 case GNUTLS_E_AGAIN: 2358 case GNUTLS_E_INTERRUPTED: 2359 if (gnutls_record_get_direction(hctx->ssl)) 2360 con->is_writable = -1; 2361 else 2362 con->is_readable = -1; 2363 break; /* try again later */ 2364 default: 2365 #if 0 2366 /* ??? how to check for EPIPE or ECONNRESET and skip logging ??? */ 2367 if (hctx->conf.ssl_log_noise) 2368 elog(hctx->r->conf.errh, __FILE__, __LINE__, wr, __func__); 2369 #endif 2370 elog(hctx->r->conf.errh, __FILE__, __LINE__, wr, __func__); 2371 return -1; 2372 } 2373 2374 /* partial write; save attempted wr_len */ 2375 hctx->pending_write = wr_len; 2376 2377 return 0; /* try again later */ 2378 } 2379 2380 2381 __attribute_cold__ 2382 static int 2383 mod_gnutls_read_err(connection *con, handler_ctx *hctx, int rc) 2384 { 2385 switch (rc) { 2386 case GNUTLS_E_AGAIN: 2387 case GNUTLS_E_INTERRUPTED: 2388 if (gnutls_record_get_direction(hctx->ssl)) 2389 con->is_writable = -1; 2390 con->is_readable = 0; 2391 return 0; 2392 #if 0 2393 case GNUTLS_E_SESSION_EOF: /*(not exposed by library)*/ 2394 /* XXX: future: save state to avoid future read after response? */ 2395 con->is_readable = 0; 2396 r->keep_alive = 0; 2397 return (hctx->handshake ? -2 : -1); /*(-1 error if during handshake)*/ 2398 #endif 2399 case GNUTLS_E_REHANDSHAKE: 2400 if (!hctx->handshake) return -1; /*(not expected during handshake)*/ 2401 if (hctx->conf.ssl_disable_client_renegotiation)/*(mod_gnutls default)*/ 2402 return -1; 2403 #if 0 2404 if (gnutls_safe_renegotiation_status(hctx->ssl)) { 2405 hctx->handshake = 0; 2406 return con->network_read(con, cq, max_bytes); 2407 } 2408 #else 2409 return 0; /*(ignore client renegotiation request; generally unsafe)*/ 2410 #endif 2411 case GNUTLS_E_WARNING_ALERT_RECEIVED: 2412 case GNUTLS_E_FATAL_ALERT_RECEIVED: 2413 { 2414 const char *str; 2415 gnutls_alert_description_t alert = gnutls_alert_get(hctx->ssl); 2416 switch (alert) { 2417 case GNUTLS_A_NO_RENEGOTIATION: 2418 return 0; /*(ignore non-fatal alert from client)*/ 2419 case GNUTLS_A_HANDSHAKE_FAILURE: 2420 case GNUTLS_A_CLOSE_NOTIFY: /*(not exposed by library)*/ 2421 case GNUTLS_A_UNKNOWN_CA: 2422 case GNUTLS_A_CERTIFICATE_UNKNOWN: 2423 case GNUTLS_A_BAD_CERTIFICATE: 2424 if (!hctx->conf.ssl_log_noise) return -1; 2425 __attribute_fallthrough__ 2426 default: 2427 str = gnutls_alert_get_name(alert); 2428 elogf(hctx->r->conf.errh, __FILE__, __LINE__, rc, 2429 "%s(): alert %s", __func__, str ? str : "(unknown)"); 2430 return -1; 2431 } 2432 } 2433 case GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET: 2434 case GNUTLS_E_UNKNOWN_CIPHER_SUITE: /* GNUTLS_A_HANDSHAKE_FAILURE */ 2435 case GNUTLS_E_PREMATURE_TERMINATION: 2436 if (!hctx->conf.ssl_log_noise) return -1; 2437 __attribute_fallthrough__ 2438 case GNUTLS_E_GOT_APPLICATION_DATA: /*(not expected with current use)*/ 2439 /*if (hctx->handshake) return -1;*//*(accept only during handshake)*/ 2440 default: 2441 elog(hctx->r->conf.errh, __FILE__, __LINE__, rc, __func__); 2442 return -1; 2443 } 2444 } 2445 2446 2447 static int 2448 mod_gnutls_close_notify(handler_ctx *hctx); 2449 2450 2451 static int 2452 connection_write_cq_ssl (connection * const con, chunkqueue * const cq, off_t max_bytes) 2453 { 2454 handler_ctx * const hctx = con->plugin_ctx[plugin_data_singleton->id]; 2455 gnutls_session_t const ssl = hctx->ssl; 2456 if (!hctx->handshake) return 0; 2457 2458 if (hctx->pending_write) { 2459 int wr = gnutls_record_send(ssl, NULL, 0); 2460 if (wr <= 0) 2461 return mod_gnutls_write_err(con, hctx, wr, hctx->pending_write); 2462 max_bytes -= wr; 2463 hctx->pending_write = 0; 2464 chunkqueue_mark_written(cq, wr); 2465 } 2466 2467 if (__builtin_expect( (0 != hctx->close_notify), 0)) 2468 return mod_gnutls_close_notify(hctx); 2469 2470 const size_t lim = gnutls_record_get_max_size(ssl); 2471 2472 /* future: for efficiency/performance might consider using GnuTLS corking 2473 * gnutls_record_cork() 2474 * gnutls_record_uncork() 2475 * gnutls_record_check_corked() 2476 * though chunkqueue_peek_data() already will read a mix of MEM_CHUNK and 2477 * FILE_CHUNK into the buffer before sending, e.g. to send header response 2478 * headers and beginning of files, but does so for LOCAL_SEND_BUFSIZE (16k) 2479 * More might be possible to send before uncorking. 2480 */ 2481 2482 log_error_st * const errh = hctx->errh; 2483 while (max_bytes > 0 && !chunkqueue_is_empty(cq)) { 2484 char *data = local_send_buffer; 2485 uint32_t data_len = LOCAL_SEND_BUFSIZE < max_bytes 2486 ? LOCAL_SEND_BUFSIZE 2487 : (uint32_t)max_bytes; 2488 int wr; 2489 2490 if (0 != chunkqueue_peek_data(cq, &data, &data_len, errh)) return -1; 2491 if (__builtin_expect( (0 == data_len), 0)) { 2492 chunkqueue_remove_finished_chunks(cq); 2493 continue; 2494 } 2495 2496 /* gnutls_record_send() copies the data, up to max record size, but if 2497 * (temporarily) unable to write the entire record, it is documented 2498 * that the caller must call gnutls_record_send() again, later, with the 2499 * same arguments, or with NULL ptr and 0 data_len. The func may return 2500 * GNUTLS_E_AGAIN or GNUTLS_E_INTERRUPTED to indicate that caller should 2501 * wait for fd to be readable/writable before calling the func again, 2502 * which is why those (temporary) errors are returned instead of telling 2503 * the caller that the data was successfully copied. 2504 * Additionally, to be accurate, the size must fit into a record which 2505 * is why we restrict ourselves to sending max out record payload each 2506 * iteration. 2507 * XXX: above comments modified from mod_mbedtls; should be verified 2508 */ 2509 2510 int wr_total = 0; 2511 do { 2512 size_t wr_len = (data_len > lim) ? lim : data_len; 2513 wr = gnutls_record_send(ssl, data, wr_len); 2514 if (wr <= 0) { 2515 if (wr_total) chunkqueue_mark_written(cq, wr_total); 2516 return mod_gnutls_write_err(con, hctx, wr, wr_len); 2517 } 2518 wr_total += wr; 2519 data += wr; 2520 } while ((data_len -= wr)); 2521 chunkqueue_mark_written(cq, wr_total); 2522 max_bytes -= wr_total; 2523 } 2524 2525 return 0; 2526 } 2527 2528 2529 static int 2530 mod_gnutls_ssl_handshake (handler_ctx *hctx) 2531 { 2532 int rc = gnutls_handshake(hctx->ssl); 2533 if (rc < 0) 2534 return mod_gnutls_read_err(hctx->con, hctx, rc); 2535 2536 /*(rc == GNUTLS_E_SUCCESS)*/ 2537 2538 hctx->handshake = 1; 2539 #if GNUTLS_VERSION_NUMBER >= 0x030200 2540 if (hctx->alpn == MOD_GNUTLS_ALPN_H2) { 2541 if (0 != mod_gnutls_alpn_h2_policy(hctx)) 2542 return -1; 2543 } 2544 else if (hctx->alpn == MOD_GNUTLS_ALPN_ACME_TLS_1) { 2545 /* Once TLS handshake is complete, return -1 to result in 2546 * CON_STATE_ERROR so that socket connection is quickly closed */ 2547 return -1; 2548 } 2549 hctx->alpn = 0; 2550 #endif 2551 return 1; /* continue reading */ 2552 } 2553 2554 2555 static int 2556 connection_read_cq_ssl (connection * const con, chunkqueue * const cq, off_t max_bytes) 2557 { 2558 handler_ctx * const hctx = con->plugin_ctx[plugin_data_singleton->id]; 2559 2560 UNUSED(max_bytes); 2561 2562 if (__builtin_expect( (0 != hctx->close_notify), 0)) 2563 return mod_gnutls_close_notify(hctx); 2564 2565 if (!hctx->handshake) { 2566 int rc = mod_gnutls_ssl_handshake(hctx); 2567 if (1 != rc) return rc; /* !hctx->handshake; not done, or error */ 2568 } 2569 2570 gnutls_session_t ssl = hctx->ssl; 2571 ssize_t len; 2572 char *mem = NULL; 2573 size_t mem_len = 0; 2574 size_t pend = gnutls_record_check_pending(ssl); 2575 do { 2576 mem_len = pend < 2048 ? 2048 : pend; 2577 chunk * const ckpt = cq->last; 2578 mem = chunkqueue_get_memory(cq, &mem_len); 2579 2580 len = gnutls_record_recv(ssl, mem, mem_len); 2581 chunkqueue_use_memory(cq, ckpt, len > 0 ? len : 0); 2582 } while (len > 0 && (pend = gnutls_record_check_pending(ssl))); 2583 2584 if (len < 0) { 2585 return mod_gnutls_read_err(con, hctx, (int)len); 2586 } else if (len == 0) { 2587 con->is_readable = 0; 2588 /* the other end closed the connection -> KEEP-ALIVE */ 2589 2590 return -2; 2591 } else { 2592 return 0; 2593 } 2594 } 2595 2596 2597 static void 2598 mod_gnutls_debug_cb(int level, const char *str) 2599 { 2600 UNUSED(level); 2601 log_error_st *errh = plugin_data_singleton->srv->errh; 2602 log_error(errh, __FILE__, __LINE__, "GnuTLS: %s", str); 2603 } 2604 2605 2606 CONNECTION_FUNC(mod_gnutls_handle_con_accept) 2607 { 2608 const server_socket *srv_sock = con->srv_socket; 2609 if (!srv_sock->is_ssl) return HANDLER_GO_ON; 2610 2611 plugin_data *p = p_d; 2612 handler_ctx * const hctx = handler_ctx_init(); 2613 request_st * const r = &con->request; 2614 hctx->r = r; 2615 hctx->con = con; 2616 hctx->tmp_buf = con->srv->tmp_buf; 2617 hctx->errh = r->conf.errh; 2618 con->plugin_ctx[p->id] = hctx; 2619 buffer_blank(&r->uri.authority); 2620 2621 plugin_ssl_ctx * const s = p->ssl_ctxs + srv_sock->sidx; 2622 hctx->ssl_session_ticket = s->ssl_session_ticket; 2623 int flags = GNUTLS_SERVER | GNUTLS_NO_SIGNAL | GNUTLS_NONBLOCK; 2624 /* ??? add feature: GNUTLS_ENABLE_EARLY_START ??? */ 2625 int rc = gnutls_init(&hctx->ssl, flags); 2626 if (rc < 0) { 2627 elog(r->conf.errh, __FILE__, __LINE__, rc, "gnutls_init()"); 2628 return HANDLER_ERROR; 2629 } 2630 2631 rc = gnutls_priority_set(hctx->ssl, s->priority_cache); 2632 if (rc < 0) { 2633 elog(r->conf.errh, __FILE__, __LINE__, rc, "gnutls_priority_set()"); 2634 return HANDLER_ERROR; 2635 } 2636 2637 /* generic func replaces gnutls_handshake_set_post_client_hello_function()*/ 2638 gnutls_handshake_set_hook_function(hctx->ssl, GNUTLS_HANDSHAKE_CLIENT_HELLO, 2639 GNUTLS_HOOK_PRE, 2640 mod_gnutls_client_hello_hook); 2641 2642 gnutls_session_set_ptr(hctx->ssl, hctx); 2643 gnutls_transport_set_int(hctx->ssl, con->fd); 2644 2645 con->network_read = connection_read_cq_ssl; 2646 con->network_write = connection_write_cq_ssl; 2647 con->proto_default_port = 443; /* "https" */ 2648 mod_gnutls_patch_config(r, &hctx->conf); 2649 2650 #if GNUTLS_VERSION_NUMBER < 0x030600 2651 hctx->conf.dh_params = s->dh_params; 2652 #endif 2653 2654 /* debug logging is global. Once enabled, debug hook will remain so, though 2655 * different connection might overwrite level, if configured differently. 2656 * If GNUTLS_DEBUG_LEVEL is set in environment (and ssl_log_noise not set), 2657 * then debugging will go to stderr */ 2658 if (hctx->conf.ssl_log_noise) {/* volume level for debug message callback */ 2659 gnutls_global_set_log_function(mod_gnutls_debug_cb); 2660 gnutls_global_set_log_level(hctx->conf.ssl_log_noise); 2661 } 2662 2663 /* GnuTLS limitation: must set session ticket encryption key before GnuTLS 2664 * client hello hook runs if TLSv1.3 (? key already set by then ?) */ 2665 if (hctx->ssl_session_ticket && session_ticket_key.size) { 2666 rc = gnutls_session_ticket_enable_server(hctx->ssl,&session_ticket_key); 2667 if (rc < 0) { 2668 elog(r->conf.errh, __FILE__, __LINE__, rc, 2669 "gnutls_session_ticket_enable_server()"); 2670 return HANDLER_ERROR; 2671 } 2672 } 2673 2674 return HANDLER_GO_ON; 2675 } 2676 2677 2678 static void 2679 mod_gnutls_detach(handler_ctx *hctx) 2680 { 2681 /* step aside from further SSL processing 2682 * (used after handle_connection_shut_wr hook) */ 2683 /* future: might restore prior network_read and network_write fn ptrs */ 2684 hctx->con->is_ssl_sock = 0; 2685 /* if called after handle_connection_shut_wr hook, shutdown SHUT_WR */ 2686 if (-1 == hctx->close_notify) shutdown(hctx->con->fd, SHUT_WR); 2687 hctx->close_notify = 1; 2688 } 2689 2690 2691 CONNECTION_FUNC(mod_gnutls_handle_con_shut_wr) 2692 { 2693 plugin_data *p = p_d; 2694 handler_ctx *hctx = con->plugin_ctx[p->id]; 2695 if (NULL == hctx) return HANDLER_GO_ON; 2696 2697 hctx->close_notify = -2; 2698 if (hctx->handshake) { 2699 mod_gnutls_close_notify(hctx); 2700 } 2701 else { 2702 mod_gnutls_detach(hctx); 2703 } 2704 2705 return HANDLER_GO_ON; 2706 } 2707 2708 2709 static int 2710 mod_gnutls_close_notify (handler_ctx *hctx) 2711 { 2712 if (1 == hctx->close_notify) return -2; 2713 2714 int rc = gnutls_bye(hctx->ssl, GNUTLS_SHUT_WR); 2715 switch (rc) { 2716 case GNUTLS_E_SUCCESS: 2717 mod_gnutls_detach(hctx); 2718 return -2; 2719 case GNUTLS_E_AGAIN: 2720 case GNUTLS_E_INTERRUPTED: 2721 return 0; 2722 default: 2723 elog(hctx->r->conf.errh, __FILE__, __LINE__, rc, 2724 "mod_gnutls_close_notify()"); 2725 __attribute_fallthrough__ 2726 case GNUTLS_E_PUSH_ERROR: /*(noisy; probably connection reset)*/ 2727 mod_gnutls_detach(hctx); 2728 return -1; 2729 } 2730 } 2731 2732 2733 CONNECTION_FUNC(mod_gnutls_handle_con_close) 2734 { 2735 plugin_data *p = p_d; 2736 handler_ctx *hctx = con->plugin_ctx[p->id]; 2737 if (NULL != hctx) { 2738 con->plugin_ctx[p->id] = NULL; 2739 if (1 != hctx->close_notify) 2740 mod_gnutls_close_notify(hctx); /*(one final try)*/ 2741 handler_ctx_free(hctx); 2742 } 2743 2744 return HANDLER_GO_ON; 2745 } 2746 2747 2748 __attribute_noinline__ 2749 static void 2750 https_add_ssl_client_cert (request_st * const r, const gnutls_x509_crt_t peer) 2751 { 2752 gnutls_datum_t d; 2753 if (gnutls_x509_crt_export2(peer, GNUTLS_X509_FMT_PEM, &d) >= 0) 2754 http_header_env_set(r, 2755 CONST_STR_LEN("SSL_CLIENT_CERT"), 2756 (char *)d.data, d.size); 2757 if (d.data) gnutls_free(d.data); 2758 } 2759 2760 2761 /* modified from gnutls tests/dn.c:print_dn() */ 2762 static void 2763 https_add_ssl_client_subject (request_st * const r, gnutls_x509_dn_t dn) 2764 { 2765 int irdn = 0, i, rc; 2766 gnutls_x509_ava_st ava; 2767 const size_t prelen = sizeof("SSL_CLIENT_S_DN_")-1; 2768 char key[64] = "SSL_CLIENT_S_DN_"; 2769 char buf[512]; /*(expecting element value len <= 256)*/ 2770 2771 /* add components of client Subject DN */ 2772 2773 /* man gnutls_x509_dn_get_rdn_ava() 2774 * The X.509 distinguished name is a sequence of sequences of strings and 2775 * this is what the irdn and iava indexes model. 2776 * This is a low-level function that requires the caller to do the value 2777 * conversions when necessary (e.g. from UCS-2). 2778 * XXX: value conversions not done below; unprintable chars replaced w/ '?' 2779 */ 2780 2781 do { 2782 for (i=0; (rc = gnutls_x509_dn_get_rdn_ava(dn,irdn,i,&ava)) == 0; ++i) { 2783 const char *name = 2784 gnutls_x509_dn_oid_name((char *)ava.oid.data, 2785 GNUTLS_X509_DN_OID_RETURN_OID); 2786 const size_t len = strlen(name); 2787 if (prelen+len >= sizeof(key)) continue; 2788 memcpy(key+prelen, name, len); /*(not '\0'-terminated)*/ 2789 2790 unsigned int n, vlen = ava.value.size; 2791 if (vlen > sizeof(buf)-1) 2792 vlen = sizeof(buf)-1; 2793 for (n = 0; n < vlen; ++n) { 2794 unsigned char c = ava.value.data[n]; 2795 buf[n] = (c >= 32 && c != 127) ? c : '?'; 2796 } 2797 2798 http_header_env_set(r, key, prelen+len, buf, n); 2799 } 2800 ++irdn; 2801 } while (rc == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND && i > 0); 2802 2803 if (rc != GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) 2804 elog(r->conf.errh, __FILE__, __LINE__, rc, 2805 "gnutls_x509_dn_get_rdn_ava()"); 2806 } 2807 2808 2809 __attribute_cold__ 2810 static void 2811 https_add_ssl_client_verify_err (buffer * const b, unsigned int status) 2812 { 2813 #if GNUTLS_VERSION_NUMBER >= 0x030104 2814 /* get failure string and translate newline to ':', removing last one */ 2815 /* (preserving behavior from mod_openssl) */ 2816 gnutls_datum_t msg = { NULL, 0 }; 2817 if (gnutls_certificate_verification_status_print(status, GNUTLS_CRT_X509, 2818 &msg, 0) >= 0) { 2819 size_t sz = msg.size-1; /* '\0'-terminated string */ 2820 for (char *nl=(char *)msg.data; NULL != (nl=strchr(nl, '\n')); ++nl) 2821 nl[0] = ('\0' == nl[1] ? (--sz, '\0') : ':'); 2822 buffer_append_string_len(b, (char *)msg.data, sz); 2823 } 2824 if (msg.data) gnutls_free(msg.data); 2825 #else 2826 UNUSED(b); 2827 UNUSED(status); 2828 #endif 2829 } 2830 2831 2832 __attribute_noinline__ 2833 static void 2834 https_add_ssl_client_entries (request_st * const r, handler_ctx * const hctx) 2835 { 2836 gnutls_session_t ssl = hctx->ssl; 2837 unsigned int crt_size = 0; 2838 const gnutls_datum_t *crts = NULL; 2839 buffer *vb = http_header_env_set_ptr(r, CONST_STR_LEN("SSL_CLIENT_VERIFY")); 2840 2841 if (hctx->verify_status != ~0u) 2842 crts = gnutls_certificate_get_peers(ssl, &crt_size); 2843 if (0 == crt_size) { /* || hctx->verify_status == ~0u) */ 2844 /*(e.g. no cert, or verify result not available)*/ 2845 buffer_copy_string_len(vb, CONST_STR_LEN("NONE")); 2846 return; 2847 } 2848 else if (0 != hctx->verify_status) { 2849 buffer_copy_string_len(vb, CONST_STR_LEN("FAILED:")); 2850 https_add_ssl_client_verify_err(vb, hctx->verify_status); 2851 return; 2852 } 2853 else { 2854 buffer_copy_string_len(vb, CONST_STR_LEN("SUCCESS")); 2855 } 2856 2857 gnutls_x509_crt_t crt; 2858 if (gnutls_x509_crt_init(&crt) < 0) 2859 return; 2860 if (crts && gnutls_x509_crt_import(crt, &crts[0], GNUTLS_X509_FMT_DER) < 0){ 2861 gnutls_x509_crt_deinit(crt); 2862 return; 2863 } 2864 2865 int rc; 2866 gnutls_datum_t d = { NULL, 0 }; 2867 char buf[512]; 2868 /*rc = gnutls_x509_crt_print(cert, GNUTLS_CRT_PRINT_ONELINE, &d);*//* ??? */ 2869 #if GNUTLS_VERSION_NUMBER < 0x030507 2870 d.data = buf; 2871 d.size = sizeof(buf); 2872 rc = gnutls_x509_crt_get_dn(crt, buf, &d.size); 2873 #else 2874 rc = gnutls_x509_crt_get_dn3(crt, &d, 0); 2875 #endif 2876 if (rc >= 0) 2877 http_header_env_set(r, 2878 CONST_STR_LEN("SSL_CLIENT_S_DN"), 2879 (char *)d.data, d.size); 2880 if (d.data && d.data != (void *)buf) gnutls_free(d.data); 2881 2882 gnutls_x509_dn_t dn; 2883 rc = gnutls_x509_crt_get_subject(crt, &dn); 2884 if (rc >= 0) 2885 https_add_ssl_client_subject(r, dn); 2886 2887 size_t sz = sizeof(buf); 2888 if (gnutls_x509_crt_get_serial(crt, buf, &sz) >= 0) 2889 buffer_append_string_encoded_hex_uc( 2890 http_header_env_set_ptr(r, CONST_STR_LEN("SSL_CLIENT_M_SERIAL")), 2891 buf, sz); 2892 2893 if (hctx->conf.ssl_verifyclient_username) { 2894 /* pick one of the exported values as "REMOTE_USER", for example 2895 * ssl.verifyclient.username = "SSL_CLIENT_S_DN_UID" 2896 * or 2897 * ssl.verifyclient.username = "SSL_CLIENT_S_DN_emailAddress" 2898 */ 2899 const buffer *varname = hctx->conf.ssl_verifyclient_username; 2900 vb = http_header_env_get(r, BUF_PTR_LEN(varname)); 2901 if (vb) { /* same as mod_auth_api.c:http_auth_setenv() */ 2902 http_header_env_set(r, 2903 CONST_STR_LEN("REMOTE_USER"), 2904 BUF_PTR_LEN(vb)); 2905 http_header_env_set(r, 2906 CONST_STR_LEN("AUTH_TYPE"), 2907 CONST_STR_LEN("SSL_CLIENT_VERIFY")); 2908 } 2909 } 2910 2911 /* if (NULL != crt) (e.g. not PSK-based ciphersuite) */ 2912 if (hctx->conf.ssl_verifyclient_export_cert && NULL != crt) 2913 https_add_ssl_client_cert(r, crt); 2914 2915 gnutls_x509_crt_deinit(crt); 2916 } 2917 2918 2919 static void 2920 http_cgi_ssl_env (request_st * const r, handler_ctx * const hctx) 2921 { 2922 gnutls_session_t ssl = hctx->ssl; 2923 gnutls_protocol_t version = gnutls_protocol_get_version(ssl); 2924 gnutls_cipher_algorithm_t cipher = gnutls_cipher_get(ssl); 2925 gnutls_kx_algorithm_t kx = gnutls_kx_get(ssl); 2926 gnutls_mac_algorithm_t mac = gnutls_mac_get(ssl); 2927 const char *s; 2928 2929 s = gnutls_protocol_get_name(version); 2930 if (s) http_header_env_set(r, CONST_STR_LEN("SSL_PROTOCOL"), s, strlen(s)); 2931 2932 s = gnutls_cipher_suite_get_name(kx, cipher, mac); 2933 if (s) http_header_env_set(r, CONST_STR_LEN("SSL_CIPHER"), s, strlen(s)); 2934 2935 /* SSL_CIPHER_ALGKEYSIZE - Number of cipher bits (possible) */ 2936 /* SSL_CIPHER_USEKEYSIZE - Number of cipher bits (actually used) */ 2937 /* (always the same in extra/gnutls_openssl.c:SSL_CIPHER_get_bits()) 2938 * (The values are the same except for very old, weak ciphers, i.e. if you 2939 * care about this, then you instead ought to be using stronger ciphers)*/ 2940 /* ??? gnutls_x509_crt_get_pk_algorithm(crt, &usekeysize); ??? */ 2941 size_t algkeysize = 8 * gnutls_cipher_get_key_size(cipher); 2942 size_t usekeysize = algkeysize; 2943 char buf[LI_ITOSTRING_LENGTH]; 2944 http_header_env_set(r, CONST_STR_LEN("SSL_CIPHER_USEKEYSIZE"), 2945 buf, li_utostrn(buf, sizeof(buf), usekeysize)); 2946 http_header_env_set(r, CONST_STR_LEN("SSL_CIPHER_ALGKEYSIZE"), 2947 buf, li_utostrn(buf, sizeof(buf), algkeysize)); 2948 } 2949 2950 2951 REQUEST_FUNC(mod_gnutls_handle_request_env) 2952 { 2953 plugin_data *p = p_d; 2954 /* simple flag for request_env_patched */ 2955 if (r->plugin_ctx[p->id]) return HANDLER_GO_ON; 2956 handler_ctx *hctx = r->con->plugin_ctx[p->id]; 2957 if (NULL == hctx) return HANDLER_GO_ON; 2958 r->plugin_ctx[p->id] = (void *)(uintptr_t)1u; 2959 2960 http_cgi_ssl_env(r, hctx); 2961 if (hctx->conf.ssl_verifyclient) { 2962 https_add_ssl_client_entries(r, hctx); 2963 } 2964 2965 return HANDLER_GO_ON; 2966 } 2967 2968 2969 REQUEST_FUNC(mod_gnutls_handle_uri_raw) 2970 { 2971 /* mod_gnutls must be loaded prior to mod_auth 2972 * if mod_gnutls is configured to set REMOTE_USER based on client cert */ 2973 /* mod_gnutls must be loaded after mod_extforward 2974 * if mod_gnutls config is based on lighttpd.conf remote IP conditional 2975 * using remote IP address set by mod_extforward, *unless* PROXY protocol 2976 * is enabled with extforward.hap-PROXY = "enable", in which case the 2977 * reverse is true: mod_extforward must be loaded after mod_gnutls */ 2978 plugin_data *p = p_d; 2979 handler_ctx *hctx = r->con->plugin_ctx[p->id]; 2980 if (NULL == hctx) return HANDLER_GO_ON; 2981 2982 mod_gnutls_patch_config(r, &hctx->conf); 2983 if (hctx->conf.ssl_verifyclient) { 2984 mod_gnutls_handle_request_env(r, p); 2985 } 2986 2987 return HANDLER_GO_ON; 2988 } 2989 2990 2991 REQUEST_FUNC(mod_gnutls_handle_request_reset) 2992 { 2993 plugin_data *p = p_d; 2994 r->plugin_ctx[p->id] = NULL; /* simple flag for request_env_patched */ 2995 return HANDLER_GO_ON; 2996 } 2997 2998 2999 TRIGGER_FUNC(mod_gnutls_handle_trigger) { 3000 const plugin_data * const p = p_d; 3001 const unix_time64_t cur_ts = log_epoch_secs; 3002 if (cur_ts & 0x3f) return HANDLER_GO_ON; /*(continue once each 64 sec)*/ 3003 3004 mod_gnutls_session_ticket_key_check(srv, p, cur_ts); 3005 mod_gnutls_refresh_stapling_files(srv, p, cur_ts); 3006 3007 return HANDLER_GO_ON; 3008 } 3009 3010 3011 int mod_gnutls_plugin_init (plugin *p); 3012 int mod_gnutls_plugin_init (plugin *p) 3013 { 3014 p->version = LIGHTTPD_VERSION_ID; 3015 p->name = "gnutls"; 3016 p->init = mod_gnutls_init; 3017 p->cleanup = mod_gnutls_free; 3018 p->priv_defaults= mod_gnutls_set_defaults; 3019 3020 p->handle_connection_accept = mod_gnutls_handle_con_accept; 3021 p->handle_connection_shut_wr = mod_gnutls_handle_con_shut_wr; 3022 p->handle_connection_close = mod_gnutls_handle_con_close; 3023 p->handle_uri_raw = mod_gnutls_handle_uri_raw; 3024 p->handle_request_env = mod_gnutls_handle_request_env; 3025 p->handle_request_reset = mod_gnutls_handle_request_reset; 3026 p->handle_trigger = mod_gnutls_handle_trigger; 3027 3028 return 0; 3029 } 3030 3031 3032 /* cipher suites 3033 * 3034 * (extremely coarse (and very possibly incorrect) mapping to openssl labels) 3035 */ 3036 3037 /* TLSv1.3 cipher list (supported in gnutls) */ 3038 static const char suite_TLSv13[] = 3039 "+CHACHA20-POLY1305:" 3040 "+AES-256-GCM:" 3041 "+AES-256-CCM:" 3042 "+AES-256-CCM-8:" 3043 "+AES-128-GCM:" 3044 "+AES-128-CCM:" 3045 "+AES-128-CCM-8:" 3046 ; 3047 3048 /* TLSv1.2 cipher list (supported in gnutls) */ 3049 static const char suite_TLSv12[] = 3050 "+CHACHA20-POLY1305:" 3051 "+AES-256-GCM:" 3052 "+AES-256-CCM:" 3053 "+AES-256-CBC:" 3054 "+AES-256-CCM-8:" 3055 "+CAMELLIA-256-GCM:" 3056 "+CAMELLIA-256-CBC:" 3057 "+AES-128-GCM:" 3058 "+AES-128-CCM:" 3059 "+AES-128-CBC:" 3060 "+AES-128-CCM-8:" 3061 "+CAMELLIA-128-GCM:" 3062 "+CAMELLIA-128-CBC:" 3063 ; 3064 3065 /* TLSv1.0 cipher list (supported in gnutls) */ 3066 /* XXX: intentionally not including overlapping eNULL ciphers */ 3067 static const char suite_TLSv10[] = 3068 "+AES-256-CBC:" 3069 "+AES-128-CBC:" 3070 "+CAMELLIA-256-CBC:" 3071 "+CAMELLIA-128-CBC:" 3072 ; 3073 3074 /* SSLv3 cipher list (supported in gnutls) */ 3075 /* XXX: intentionally not including overlapping eNULL ciphers */ 3076 static const char suite_SSLv3[] = 3077 "+AES-256-CBC:" 3078 "+AES-128-CBC:" 3079 "+CAMELLIA-256-CBC:" 3080 "+CAMELLIA-128-CBC:" 3081 "+3DES-CBC:" 3082 "+DES-CBC:" 3083 ; 3084 3085 /* HIGH cipher list (mapped from openssl list to gnutls) */ 3086 static const char suite_HIGH[] = 3087 "+CHACHA20-POLY1305:" 3088 "+AES-256-GCM:" 3089 "+AES-256-CCM:" 3090 "+AES-256-CBC:" 3091 "+AES-256-CCM-8:" 3092 "+CAMELLIA-256-CBC:" 3093 "+AES-128-GCM:" 3094 "+AES-128-CCM:" 3095 "+AES-128-CBC:" 3096 "+AES-128-CCM-8:" 3097 "+CAMELLIA-128-CBC:" 3098 ; 3099 3100 3101 static int 3102 mod_gnutls_ssl_conf_ciphersuites (server *srv, plugin_config_socket *s, buffer *ciphersuites, const buffer *cipherstring) 3103 { 3104 /* reference: https://www.openssl.org/docs/man1.1.1/man1/ciphers.html 3105 * Attempt to parse *some* keywords from Ciphersuites and CipherString 3106 * !!! openssl uses a *different* naming scheme than does GnuTLS !!! 3107 * Ciphersuites in openssl takes only TLSv1.3 suites. 3108 * Note that CipherString does allow cipher suites to be listed, 3109 * and this code does not currently attempt to provide mapping */ 3110 3111 buffer * const plist = &s->priority_str; 3112 char n[128]; /*(most ciphersuite names are about 40 chars)*/ 3113 3114 if (ciphersuites) { 3115 buffer *b = ciphersuites; 3116 buffer_to_upper(b); /*(ciphersuites are all uppercase (currently))*/ 3117 for (const char *e = b->ptr-1; e; ) { 3118 const char * const p = e+1; 3119 e = strchr(p, ':'); 3120 size_t len = e ? (size_t)(e - p) : strlen(p); 3121 3122 if (buffer_eq_icase_ss(p, len, 3123 CONST_STR_LEN("TLS_CHACHA20_POLY1305_SHA256"))) 3124 buffer_append_string_len(plist, 3125 CONST_STR_LEN("+CHACHA20-POLY1305:")); 3126 else if (buffer_eq_icase_ss(p, len, 3127 CONST_STR_LEN("TLS_AES_256_GCM_SHA384"))) 3128 buffer_append_string_len(plist, 3129 CONST_STR_LEN("+AES-256-GCM:")); 3130 else if (buffer_eq_icase_ss(p, len, 3131 CONST_STR_LEN("TLS_AES_128_GCM_SHA256"))) 3132 buffer_append_string_len(plist, 3133 CONST_STR_LEN("+AES-128-GCM:")); 3134 else if (buffer_eq_icase_ss(p, len, 3135 CONST_STR_LEN("TLS_AES_128_CCM_SHA256"))) 3136 buffer_append_string_len(plist, 3137 CONST_STR_LEN("+AES-128-CCM:")); 3138 else if (buffer_eq_icase_ss(p, len, 3139 CONST_STR_LEN("TLS_AES_128_CCM_8_SHA256"))) 3140 buffer_append_string_len(plist, 3141 CONST_STR_LEN("+AES-128-CCM-8:")); 3142 else 3143 log_error(srv->errh, __FILE__, __LINE__, 3144 "GnuTLS: skipped ciphersuite; not recognized: %.*s", 3145 (int)len, p); 3146 } 3147 } 3148 3149 /* XXX: openssl config for CipherString=... is excessively complex. 3150 * If there is a need to enable specific ciphersuites, then that 3151 * can be accomplished with mod_gnutls by specifying the list in 3152 * Ciphersuites=... in the ssl.openssl.ssl-conf-cmd directive. 3153 * 3154 * The tokens parsed here are a quick attempt to handle a few cases 3155 * 3156 * XXX: not done: could make a list of ciphers with bitflag of attributes 3157 * to make future combining easier */ 3158 if (cipherstring && !buffer_is_blank(cipherstring)) { 3159 const buffer *b = cipherstring; 3160 const char *e = b->ptr; 3161 3162 /* XXX: not done: no checking for duplication of ciphersuites 3163 * even if tokens overlap or are repeated */ 3164 3165 /* XXX: not done: might walk string and build up exclude list of !xxxxx 3166 * ciphersuites and walk string again, excluding as result list built */ 3167 3168 /* manually handle first token, since one-offs apply */ 3169 /* (openssl syntax NOT fully supported) */ 3170 #define strncmp_const(s,cs) strncmp((s),(cs),sizeof(cs)-1) 3171 if (0 == strncmp_const(e, "!ALL") || 0 == strncmp_const(e, "-ALL")) { 3172 /* "!ALL" excluding all ciphers is empty list */ 3173 e += sizeof("!ALL")-1; /* same as sizeof("-ALL")-1 */ 3174 buffer_append_string_len(plist, CONST_STR_LEN("-CIPHER-ALL:")); 3175 } 3176 else if (0 == strncmp_const(e, "!DEFAULT") 3177 || 0 == strncmp_const(e, "-DEFAULT")) { 3178 /* "!DEFAULT" excluding default ciphers is empty list */ 3179 e += sizeof("!DEFAULT")-1; /* same as sizeof("-DEFAULT")-1 */ 3180 buffer_append_string_len(plist, CONST_STR_LEN("-CIPHER-ALL:")); 3181 } 3182 else if (0 == strncmp_const(e, "DEFAULT")) { 3183 e += sizeof("DEFAULT")-1; 3184 s->priority_base = "NORMAL"; 3185 } 3186 else if (0 == /* effectively the same as "DEFAULT" */ 3187 strncmp_const(e, "ALL:!COMPLEMENTOFDEFAULT:!eNULL")) { 3188 e += sizeof("ALL:!COMPLEMENTOFDEFAULT:!eNULL")-1; 3189 s->priority_base = "NORMAL"; 3190 } 3191 else if (0 == strncmp_const(e, "SUITEB128") 3192 || 0 == strncmp_const(e, "SUITEB128ONLY") 3193 || 0 == strncmp_const(e, "SUITEB192")) { 3194 s->priority_base = (0 == strncmp_const(e, "SUITEB192")) 3195 ? "SUITEB192" 3196 : "SUITEB128"; 3197 e += (0 == strncmp_const(e, "SUITEB128ONLY")) 3198 ? sizeof("SUITEB128ONLY")-1 3199 : sizeof("SUITEB128")-1; 3200 if (*e) 3201 log_error(srv->errh, __FILE__, __LINE__, 3202 "GnuTLS: ignoring cipher string after SUITEB: %s", e); 3203 return 1; 3204 } 3205 else if (0 == strncmp_const(e, 3206 "ECDHE+AESGCM:ECDHE+AES256:CHACHA20:!SHA1:!SHA256:!SHA384") 3207 || 0 == strncmp_const(e, 3208 "EECDH+AESGCM:AES256+EECDH:CHACHA20:!SHA1:!SHA256:!SHA384")) { 3209 e += sizeof( 3210 "EECDH+AESGCM:AES256+EECDH:CHACHA20:!SHA1:!SHA256:!SHA384")-1; 3211 buffer_append_string_len(plist, 3212 CONST_STR_LEN("+AES-256-GCM:+AES-128-GCM:+AES-256-CCM:+AES-256-CCM-8:+CHACHA20-POLY1305:")); 3213 } 3214 3215 if (e != b->ptr && *e != ':' && *e != '\0') { 3216 log_error(srv->errh, __FILE__, __LINE__, 3217 "GnuTLS: error: missing support for cipher list: %s", b->ptr); 3218 return 0; 3219 } 3220 3221 /* not handled: "ALL" is "DEFAULT" and "RC4" */ 3222 /* not handled: "COMPLEMENTOFALL" is "eNULL" */ 3223 3224 int rc = 1; 3225 if (e == b->ptr || *e == '\0') --e; /*initial condition for loop below*/ 3226 do { 3227 const char * const p = e+1; 3228 e = strchr(p, ':'); 3229 size_t len = e ? (size_t)(e - p) : strlen(p); 3230 if (0 == len) continue; 3231 if (len >= sizeof(n)) { 3232 log_error(srv->errh, __FILE__, __LINE__, 3233 "GnuTLS: skipped ciphersuite; too long: %.*s", 3234 (int)len, p); 3235 continue; 3236 } 3237 char c = (*p == '!' || *p == '-' || *p == '+') ? *p : 0; 3238 size_t nlen = c ? len-1 : len; 3239 memcpy(n, c ? p+1 : p, nlen); 3240 n[nlen] = '\0'; 3241 3242 /* not handled: !xxxxx -xxxxx and most +xxxxx */ 3243 if (c) { 3244 log_error(srv->errh, __FILE__, __LINE__, 3245 "GnuTLS: error: missing support for cipher list: %s", b->ptr); 3246 } 3247 3248 /* ignore @STRENGTH sorting and ignore @SECLEVEL=n */ 3249 char *a = strchr(n, '@'); 3250 if (a) { 3251 log_error(srv->errh, __FILE__, __LINE__, 3252 "GnuTLS: ignored %s in %.*s", a, (int)len, p); 3253 *a = '\0'; 3254 nlen = (size_t)(a - n); 3255 } 3256 3257 if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("TLSv1.3"))) { 3258 buffer_append_string_len(plist, 3259 CONST_STR_LEN(suite_TLSv13)); 3260 continue; 3261 } 3262 3263 if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("TLSv1.2"))) { 3264 buffer_append_string_len(plist, 3265 CONST_STR_LEN(suite_TLSv12)); 3266 continue; 3267 } 3268 3269 if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("TLSv1.0"))) { 3270 buffer_append_string_len(plist, 3271 CONST_STR_LEN(suite_TLSv10)); 3272 continue; 3273 } 3274 3275 if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("SSLv3"))) { 3276 buffer_append_string_len(plist, 3277 CONST_STR_LEN(suite_SSLv3)); 3278 continue; 3279 } 3280 3281 /* handle a popular recommendations 3282 * ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM" 3283 * ssl.cipher-list = "AES256+EECDH:AES256+EDH" 3284 * which uses AES hardware acceleration built into popular CPUs */ 3285 if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("ECDHE+AESGCM")) 3286 || buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("EECDH+AESGCM"))) { 3287 buffer_append_string_len(plist, 3288 CONST_STR_LEN("+AES-256-GCM:+AES-128-GCM:")); 3289 continue; 3290 } 3291 if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("DHE+AESGCM")) 3292 || buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("EDH+AESGCM"))) { 3293 buffer_append_string_len(plist, 3294 CONST_STR_LEN("+AES-256-GCM:+AES-128-GCM:")); 3295 continue; 3296 } 3297 if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("AES256+EECDH"))) { 3298 buffer_append_string_len(plist, 3299 CONST_STR_LEN("+AES-256-GCM:+AES-256-CCM:+AES-256-CBC:+AES-256-CCM-8:")); 3300 continue; 3301 } 3302 if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("AES256+EDH"))) { 3303 buffer_append_string_len(plist, 3304 CONST_STR_LEN("+AES-256-GCM:+AES-256-CCM:+AES-256-CBC:+AES-256-CCM-8:")); 3305 continue; 3306 } 3307 3308 if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("HIGH"))) { 3309 buffer_append_string_len(plist, 3310 CONST_STR_LEN(suite_HIGH)); 3311 continue; 3312 } 3313 3314 if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("AES256")) 3315 || buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("AES"))) { 3316 buffer_append_string_len(plist, 3317 CONST_STR_LEN("+AES-256-GCM:+AES-256-CCM:+AES-256-CBC:+AES-256-CCM-8:")); 3318 if (nlen == sizeof("AES256")-1) continue; 3319 } 3320 3321 if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("AES128")) 3322 || buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("AES"))) { 3323 buffer_append_string_len(plist, 3324 CONST_STR_LEN("+AES-128-GCM:+AES-128-CCM:+AES-128-CBC:+AES-128-CCM-8:")); 3325 continue; 3326 } 3327 3328 if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("CAMELLIA256")) 3329 || buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("CAMELLIA"))) { 3330 buffer_append_string_len(plist, 3331 CONST_STR_LEN("+CAMELLIA-256-GCM:+CAMELLIA-256-CBC:")); 3332 if (nlen == sizeof("CAMELLIA256")-1) continue; 3333 } 3334 3335 if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("CAMELLIA128")) 3336 || buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("CAMELLIA"))) { 3337 buffer_append_string_len(plist, 3338 CONST_STR_LEN("+CAMELLIA-128-GCM:+CAMELLIA-128-CBC:")); 3339 continue; 3340 } 3341 3342 if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("CHACHA20"))) { 3343 buffer_append_string_len(plist, 3344 CONST_STR_LEN("+CHACHA20-POLY1305:")); 3345 continue; 3346 } 3347 3348 if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("3DES"))) { 3349 buffer_append_string_len(plist, 3350 CONST_STR_LEN("+3DES-CBC:")); 3351 continue; 3352 } 3353 3354 /* not recommended, but permitted if explicitly requested */ 3355 if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("NULL")) 3356 || buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("eNULL"))) { 3357 buffer_append_string_len(plist, 3358 CONST_STR_LEN("+NULL:")); 3359 continue; 3360 } 3361 3362 { 3363 log_error(srv->errh, __FILE__, __LINE__, 3364 "GnuTLS: error: missing support for cipher list: %.*s", 3365 (int)len, p); 3366 rc = 0; 3367 continue; 3368 } 3369 } while (e); 3370 if (0 == rc) return 0; 3371 } 3372 3373 return 1; 3374 } 3375 3376 3377 static int 3378 mod_gnutls_ssl_conf_curves(server *srv, plugin_config_socket *s, const buffer *curvelist) 3379 { 3380 /* map NIST name (e.g. P-256) or OpenSSL OID name (e.g. prime256v1) 3381 * to GnuTLS name of supported curves/groups */ 3382 static const char *names[] = { 3383 "P-192", "CURVE-SECP192R1", 3384 "P-224", "CURVE-SECP224R1", 3385 "P-256", "GROUP-SECP256R1", /* CURVE-SECP256R1 */ 3386 "P-384", "GROUP-SECP384R1", /* CURVE-SECP384R1 */ 3387 "P-521", "GROUP-SECP521R1", /* CURVE-SECP521R1 */ 3388 "X25519", "GROUP-X25519", /* CURVE-X25519 */ 3389 "X448", "GROUP-X448", /* CURVE-X448 */ 3390 "prime192v1", "CURVE-SECP192R1", 3391 "secp224r1", "CURVE-SECP224R1", 3392 "prime256v1", "GROUP-SECP256R1", /* CURVE-SECP256R1 */ 3393 "secp384r1", "GROUP-SECP384R1", /* CURVE-SECP384R1 */ 3394 "secp521r1", "GROUP-SECP521R1", /* CURVE-SECP521R1 */ 3395 "ffdhe2048", "GROUP-FFDHE2048", 3396 "ffdhe3072", "GROUP-FFDHE3072", 3397 "ffdhe4096", "GROUP-FFDHE4096", 3398 "ffdhe6144", "GROUP-FFDHE6144", 3399 "ffdhe8192", "GROUP-FFDHE8192", 3400 }; 3401 3402 buffer * const plist = &s->priority_str; 3403 const buffer * const b = curvelist; 3404 for (const char *e = b->ptr-1; e; ) { 3405 const char * const n = e+1; 3406 e = strchr(n, ':'); 3407 size_t len = e ? (size_t)(e - n) : strlen(n); 3408 uint32_t i; 3409 for (i = 0; i < sizeof(names)/sizeof(*names)/2; i += 2) { 3410 if (0 == strncmp(names[i], n, len) && names[i][len] == '\0') 3411 break; 3412 } 3413 if (i == sizeof(names)/sizeof(*names)/2) { 3414 log_error(srv->errh, __FILE__, __LINE__, 3415 "GnuTLS: unrecognized curve: %.*s; ignored", (int)len, n); 3416 continue; 3417 } 3418 3419 buffer_append_char(plist, '+'); 3420 buffer_append_string_len(plist, names[i+1], strlen(names[i+1])); 3421 buffer_append_char(plist, ':'); 3422 } 3423 3424 return 1; 3425 } 3426 3427 3428 static int 3429 mod_gnutls_ssl_conf_proto_val (server *srv, plugin_config_socket *s, const buffer *b, int max) 3430 { 3431 if (NULL == b) /* default: min TLSv1.2, max TLSv1.3 */ 3432 return max ? GNUTLS_TLS1_3 : GNUTLS_TLS1_2; 3433 else if (buffer_eq_icase_slen(b, CONST_STR_LEN("None"))) /*"disable" limit*/ 3434 return max 3435 ? GNUTLS_TLS1_3 3436 : (s->ssl_use_sslv3 ? GNUTLS_SSL3 : GNUTLS_TLS1_0); 3437 else if (buffer_eq_icase_slen(b, CONST_STR_LEN("SSLv3"))) 3438 return GNUTLS_SSL3; 3439 else if (buffer_eq_icase_slen(b, CONST_STR_LEN("TLSv1.0"))) 3440 return GNUTLS_TLS1_0; 3441 else if (buffer_eq_icase_slen(b, CONST_STR_LEN("TLSv1.1"))) 3442 return GNUTLS_TLS1_1; 3443 else if (buffer_eq_icase_slen(b, CONST_STR_LEN("TLSv1.2"))) 3444 return GNUTLS_TLS1_2; 3445 else if (buffer_eq_icase_slen(b, CONST_STR_LEN("TLSv1.3"))) 3446 return GNUTLS_TLS1_3; 3447 else { 3448 if (buffer_eq_icase_slen(b, CONST_STR_LEN("DTLSv1")) 3449 || buffer_eq_icase_slen(b, CONST_STR_LEN("DTLSv1.2"))) 3450 log_error(srv->errh, __FILE__, __LINE__, 3451 "GnuTLS: ssl.openssl.ssl-conf-cmd %s %s ignored", 3452 max ? "MaxProtocol" : "MinProtocol", b->ptr); 3453 else 3454 log_error(srv->errh, __FILE__, __LINE__, 3455 "GnuTLS: ssl.openssl.ssl-conf-cmd %s %s invalid; ignored", 3456 max ? "MaxProtocol" : "MinProtocol", b->ptr); 3457 } 3458 return max ? GNUTLS_TLS1_3 : GNUTLS_TLS1_2; 3459 } 3460 3461 3462 static void 3463 mod_gnutls_ssl_conf_proto (server *srv, plugin_config_socket *s, const buffer *minb, const buffer *maxb) 3464 { 3465 /* use of SSL v3 should be avoided, and SSL v2 is not supported */ 3466 int n = mod_gnutls_ssl_conf_proto_val(srv, s, minb, 0); 3467 int x = mod_gnutls_ssl_conf_proto_val(srv, s, maxb, 1); 3468 if (n < 0) return; 3469 if (x < 0) return; 3470 buffer * const b = &s->priority_str; 3471 buffer_append_string_len(b, CONST_STR_LEN("-VERS-ALL:")); 3472 switch (n) { 3473 case GNUTLS_SSL3: 3474 buffer_append_string_len(b, CONST_STR_LEN("+VERS-SSL3.0:")); 3475 __attribute_fallthrough__ 3476 case GNUTLS_TLS1_0: 3477 if (x < GNUTLS_TLS1_0) break; 3478 buffer_append_string_len(b, CONST_STR_LEN("+VERS-TLS1.0:")); 3479 __attribute_fallthrough__ 3480 case GNUTLS_TLS1_1: 3481 if (x < GNUTLS_TLS1_1) break; 3482 buffer_append_string_len(b, CONST_STR_LEN("+VERS-TLS1.1:")); 3483 __attribute_fallthrough__ 3484 case GNUTLS_TLS1_2: 3485 if (x < GNUTLS_TLS1_2) break; 3486 buffer_append_string_len(b, CONST_STR_LEN("+VERS-TLS1.2:")); 3487 __attribute_fallthrough__ 3488 case GNUTLS_TLS1_3: 3489 if (x < GNUTLS_TLS1_3) break; 3490 buffer_append_string_len(b, CONST_STR_LEN("+VERS-TLS1.3:")); 3491 break; 3492 } 3493 } 3494