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