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