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