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