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