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