xref: /lighttpd1.4/src/mod_gnutls.c (revision 66698469)
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