• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * coap_gnutls.c -- GnuTLS Datagram Transport Layer Support for libcoap
3  *
4  * Copyright (C) 2017 Dag Bjorklund <dag.bjorklund@comsel.fi>
5  * Copyright (C) 2018-2023 Jon Shallow <supjps-libcoap@jpshallow.com>
6  *
7  * SPDX-License-Identifier: BSD-2-Clause
8  *
9  * This file is part of the CoAP library libcoap. Please see README for terms
10  * of use.
11  */
12 
13 /**
14  * @file coap_gnutls.c
15  * @brief GnuTLS interafe functions
16  */
17 
18 /*
19  * Naming used to prevent confusion between coap sessions, gnutls sessions etc.
20  * when reading the code.
21  *
22  * c_context  A coap_context_t *
23  * c_session  A coap_session_t *
24  * g_context  A coap_gnutls_context_t * (held in c_context->dtls_context)
25  * g_session  A gnutls_session_t (which has the * in the typedef)
26  * g_env      A coap_gnutls_env_t * (held in c_session->tls)
27  */
28 
29 /*
30  * Notes
31  *
32  * There is a memory leak in GnuTLS prior to 3.3.26 when hint is not freed off
33  * when server psk credentials are freed off.
34  *
35  * ca_path in coap_dtls_context_set_pki_root_cas() is not supported until 3.3.6
36  *
37  * Identity Hint is not provided if using DH and versions prior to 3.4.4
38  *
39  * 3.5.5 or later is required to interoperate with TinyDTLS as CCM algorithm
40  * support is required.
41  *
42  * TLS 1.3 is properly supported from 3.6.5 onwards
43  * (but is not enabled by default in 3.6.4)
44  *
45  * Starting with 3.6.3, fixed in 3.6.13, Client Hellos may fail with some
46  * server implementations (e.g. Californium) as random value is all zeros
47  * - CVE-2020-11501 - a security weakness.
48  * 3.6.6 or later is required to support Raw Public Key(RPK)
49  */
50 
51 #include "coap3/coap_internal.h"
52 
53 #ifdef COAP_WITH_LIBGNUTLS
54 
55 #define MIN_GNUTLS_VERSION "3.3.0"
56 
57 #include <stdio.h>
58 #include <gnutls/gnutls.h>
59 #include <gnutls/x509.h>
60 #include <gnutls/dtls.h>
61 #include <gnutls/pkcs11.h>
62 #include <gnutls/crypto.h>
63 #include <gnutls/abstract.h>
64 #include <unistd.h>
65 #if (GNUTLS_VERSION_NUMBER >= 0x030606)
66 #define COAP_GNUTLS_KEY_RPK GNUTLS_KEY_DIGITAL_SIGNATURE | \
67   GNUTLS_KEY_NON_REPUDIATION | \
68   GNUTLS_KEY_KEY_ENCIPHERMENT | \
69   GNUTLS_KEY_DATA_ENCIPHERMENT | \
70   GNUTLS_KEY_KEY_AGREEMENT | \
71   GNUTLS_KEY_KEY_CERT_SIGN
72 #endif /* GNUTLS_VERSION_NUMBER >= 0x030606 */
73 
74 #ifndef GNUTLS_CRT_RAW
75 #define GNUTLS_CRT_RAW GNUTLS_CRT_RAWPK
76 #endif /* GNUTLS_CRT_RAW */
77 
78 #ifdef _WIN32
79 #define strcasecmp _stricmp
80 #endif
81 
82 typedef struct coap_ssl_t {
83   const uint8_t *pdu;
84   unsigned pdu_len;
85   unsigned peekmode;
86   gnutls_datum_t cookie_key;
87 } coap_ssl_t;
88 
89 /*
90  * This structure encapsulates the GnuTLS session object.
91  * It handles both TLS and DTLS.
92  * c_session->tls points to this.
93  */
94 typedef struct coap_gnutls_env_t {
95   gnutls_session_t g_session;
96   gnutls_psk_client_credentials_t psk_cl_credentials;
97   gnutls_psk_server_credentials_t psk_sv_credentials;
98   gnutls_certificate_credentials_t pki_credentials;
99   coap_ssl_t coap_ssl_data;
100   /* If not set, need to do gnutls_handshake */
101   int established;
102   int doing_dtls_timeout;
103   coap_tick_t last_timeout;
104   int sent_alert;
105 } coap_gnutls_env_t;
106 
107 #define IS_PSK (1 << 0)
108 #define IS_PKI (1 << 1)
109 #define IS_CLIENT (1 << 6)
110 #define IS_SERVER (1 << 7)
111 
112 typedef struct pki_sni_entry {
113   char *sni;
114   coap_dtls_key_t pki_key;
115   gnutls_certificate_credentials_t pki_credentials;
116 } pki_sni_entry;
117 
118 typedef struct psk_sni_entry {
119   char *sni;
120   coap_dtls_spsk_info_t psk_info;
121   gnutls_psk_server_credentials_t psk_credentials;
122 } psk_sni_entry;
123 
124 typedef struct coap_gnutls_context_t {
125   coap_dtls_pki_t setup_data;
126   int psk_pki_enabled;
127   size_t pki_sni_count;
128   pki_sni_entry *pki_sni_entry_list;
129   size_t psk_sni_count;
130   psk_sni_entry *psk_sni_entry_list;
131   gnutls_datum_t alpn_proto;    /* Will be "coap", but that is a const */
132   char *root_ca_file;
133   char *root_ca_path;
134   gnutls_priority_t priority_cache;
135 } coap_gnutls_context_t;
136 
137 typedef enum coap_free_bye_t {
138   COAP_FREE_BYE_AS_TCP,  /**< call gnutls_bye() with GNUTLS_SHUT_RDWR */
139   COAP_FREE_BYE_AS_UDP,  /**< call gnutls_bye() with GNUTLS_SHUT_WR */
140   COAP_FREE_BYE_NONE     /**< do not call gnutls_bye() */
141 } coap_free_bye_t;
142 
143 #define VARIANTS_3_6_6 "NORMAL:+ECDHE-PSK:+PSK:+ECDHE-ECDSA:+AES-128-CCM-8:+CTYPE-CLI-ALL:+CTYPE-SRV-ALL:+SHA256"
144 #define VARIANTS_3_5_5 "NORMAL:+ECDHE-PSK:+PSK:+ECDHE-ECDSA:+AES-128-CCM-8"
145 #define VARIANTS_BASE "NORMAL:+ECDHE-PSK:+PSK"
146 
147 #define VARIANTS_NO_TLS13_3_6_6 VARIANTS_3_6_6 ":-VERS-TLS1.3"
148 #define VARIANTS_NO_TLS13_3_6_4 VARIANTS_3_5_5 ":-VERS-TLS1.3"
149 
150 #define G_ACTION(xx) do { \
151     ret = (xx); \
152   } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
153 
154 #define G_CHECK(xx,func) do { \
155     if ((ret = (xx)) < 0) { \
156       coap_log_warn("%s: '%s'\n", func, gnutls_strerror(ret)); \
157       goto fail; \
158     } \
159   } while (0)
160 
161 #define G_ACTION_CHECK(xx,func) do { \
162     G_ACTION(xx); \
163     G_CHECK(xx, func); \
164   } while 0
165 
166 static coap_log_t dtls_log_level = COAP_LOG_EMERG;
167 
168 #if COAP_SERVER_SUPPORT
169 static int post_client_hello_gnutls_pki(gnutls_session_t g_session);
170 static int post_client_hello_gnutls_psk(gnutls_session_t g_session);
171 static int psk_server_callback(gnutls_session_t g_session,
172                                const char *identity,
173                                gnutls_datum_t *key);
174 #endif /* COAP_SERVER_SUPPORT */
175 
176 /*
177  * return 0 failed
178  *        1 passed
179  */
180 int
coap_dtls_is_supported(void)181 coap_dtls_is_supported(void) {
182   if (gnutls_check_version(MIN_GNUTLS_VERSION) == NULL) {
183     coap_log_err("GnuTLS " MIN_GNUTLS_VERSION " or later is required\n");
184     return 0;
185   }
186   return 1;
187 }
188 
189 /*
190  * return 0 failed
191  *        1 passed
192  */
193 int
coap_tls_is_supported(void)194 coap_tls_is_supported(void) {
195 #if !COAP_DISABLE_TCP
196   if (gnutls_check_version(MIN_GNUTLS_VERSION) == NULL) {
197     coap_log_err("GnuTLS " MIN_GNUTLS_VERSION " or later is required\n");
198     return 0;
199   }
200   return 1;
201 #else /* COAP_DISABLE_TCP */
202   return 0;
203 #endif /* COAP_DISABLE_TCP */
204 }
205 
206 /*
207  * return 0 failed
208  *        1 passed
209  */
210 int
coap_dtls_psk_is_supported(void)211 coap_dtls_psk_is_supported(void) {
212   return 1;
213 }
214 
215 /*
216  * return 0 failed
217  *        1 passed
218  */
219 int
coap_dtls_pki_is_supported(void)220 coap_dtls_pki_is_supported(void) {
221   return 1;
222 }
223 
224 /*
225  * return 0 failed
226  *        1 passed
227  */
228 int
coap_dtls_pkcs11_is_supported(void)229 coap_dtls_pkcs11_is_supported(void) {
230   return 1;
231 }
232 
233 /*
234  * return 0 failed
235  *        1 passed
236  */
237 int
coap_dtls_rpk_is_supported(void)238 coap_dtls_rpk_is_supported(void) {
239 #if (GNUTLS_VERSION_NUMBER >= 0x030606)
240   return 1;
241 #else /* GNUTLS_VERSION_NUMBER < 0x030606 */
242   return 0;
243 #endif /* GNUTLS_VERSION_NUMBER < 0x030606 */
244 }
245 
246 coap_tls_version_t *
coap_get_tls_library_version(void)247 coap_get_tls_library_version(void) {
248   static coap_tls_version_t version;
249   const char *vers = gnutls_check_version(NULL);
250 
251   version.version = 0;
252   if (vers) {
253     int p1, p2, p3;
254 
255     sscanf(vers, "%d.%d.%d", &p1, &p2, &p3);
256     version.version = (p1 << 16) | (p2 << 8) | p3;
257   }
258   version.built_version = GNUTLS_VERSION_NUMBER;
259   version.type = COAP_TLS_LIBRARY_GNUTLS;
260   return &version;
261 }
262 
263 static void
coap_gnutls_audit_log_func(gnutls_session_t g_session,const char * text)264 coap_gnutls_audit_log_func(gnutls_session_t g_session, const char *text) {
265 #if COAP_MAX_LOGGING_LEVEL > 0
266   if (g_session) {
267     coap_session_t *c_session =
268         (coap_session_t *)gnutls_transport_get_ptr(g_session);
269     coap_log_warn("** %s: %s",
270                   coap_session_str(c_session), text);
271   } else {
272     coap_log_warn("** (null): %s", text);
273   }
274 #else /* COAP_MAX_LOGGING_LEVEL ==  0 */
275   (void)g_session;
276   (void)text;
277 #endif /* COAP_MAX_LOGGING_LEVEL ==  0 */
278 }
279 
280 static void
coap_gnutls_log_func(int level,const char * text)281 coap_gnutls_log_func(int level, const char *text) {
282   /* Things get noisy, even at level 1 */
283   if (level > 0)
284     level += COAP_LOG_WARN;
285   if (level > COAP_LOG_DEBUG)
286     level = COAP_LOG_DEBUG;
287   coap_dtls_log(level, "%s", text);
288 }
289 
290 /*
291  * return 0 failed
292  *        1 passed
293  */
294 int
coap_dtls_context_set_pki(coap_context_t * c_context,const coap_dtls_pki_t * setup_data,const coap_dtls_role_t role COAP_UNUSED)295 coap_dtls_context_set_pki(coap_context_t *c_context,
296                           const coap_dtls_pki_t *setup_data,
297                           const coap_dtls_role_t role COAP_UNUSED) {
298   coap_gnutls_context_t *g_context =
299       ((coap_gnutls_context_t *)c_context->dtls_context);
300 
301   if (!g_context || !setup_data)
302     return 0;
303 
304   g_context->setup_data = *setup_data;
305   if (!g_context->setup_data.verify_peer_cert) {
306     /* Needs to be clear so that no CA DNs are transmitted */
307     g_context->setup_data.check_common_ca = 0;
308     if (g_context->setup_data.is_rpk_not_cert) {
309       /* Disable all of these as they cannot be checked */
310       g_context->setup_data.allow_self_signed = 0;
311       g_context->setup_data.allow_expired_certs = 0;
312       g_context->setup_data.cert_chain_validation = 0;
313       g_context->setup_data.cert_chain_verify_depth = 0;
314       g_context->setup_data.check_cert_revocation = 0;
315       g_context->setup_data.allow_no_crl = 0;
316       g_context->setup_data.allow_expired_crl = 0;
317       g_context->setup_data.allow_bad_md_hash = 0;
318       g_context->setup_data.allow_short_rsa_length = 0;
319     } else {
320       /* Allow all of these but warn if issue */
321       g_context->setup_data.allow_self_signed = 1;
322       g_context->setup_data.allow_expired_certs = 1;
323       g_context->setup_data.cert_chain_validation = 1;
324       g_context->setup_data.cert_chain_verify_depth = 10;
325       g_context->setup_data.check_cert_revocation = 1;
326       g_context->setup_data.allow_no_crl = 1;
327       g_context->setup_data.allow_expired_crl = 1;
328       g_context->setup_data.allow_bad_md_hash = 1;
329       g_context->setup_data.allow_short_rsa_length = 1;
330     }
331   }
332   g_context->psk_pki_enabled |= IS_PKI;
333   return 1;
334 }
335 
336 /*
337  * return 0 failed
338  *        1 passed
339  */
340 int
coap_dtls_context_set_pki_root_cas(coap_context_t * c_context,const char * ca_file,const char * ca_path)341 coap_dtls_context_set_pki_root_cas(coap_context_t *c_context,
342                                    const char *ca_file,
343                                    const char *ca_path) {
344   coap_gnutls_context_t *g_context =
345       ((coap_gnutls_context_t *)c_context->dtls_context);
346   if (!g_context) {
347     coap_log_warn("coap_context_set_pki_root_cas: (D)TLS environment "
348                   "not set up\n");
349     return 0;
350   }
351 
352   if (ca_file == NULL && ca_path == NULL) {
353     coap_log_warn("coap_context_set_pki_root_cas: ca_file and/or ca_path "
354                   "not defined\n");
355     return 0;
356   }
357   if (g_context->root_ca_file) {
358     gnutls_free(g_context->root_ca_file);
359     g_context->root_ca_file = NULL;
360   }
361   if (ca_file) {
362     g_context->root_ca_file = gnutls_strdup(ca_file);
363   }
364   if (g_context->root_ca_path) {
365     gnutls_free(g_context->root_ca_path);
366     g_context->root_ca_path = NULL;
367   }
368   if (ca_path) {
369 #if (GNUTLS_VERSION_NUMBER >= 0x030306)
370     g_context->root_ca_path = gnutls_strdup(ca_path);
371 #else
372     coap_log_err("ca_path not supported in GnuTLS < 3.3.6\n");
373 #endif
374   }
375   return 1;
376 }
377 
378 #if COAP_SERVER_SUPPORT
379 /*
380  * return 0 failed
381  *        1 passed
382  */
383 int
coap_dtls_context_set_spsk(coap_context_t * c_context,coap_dtls_spsk_t * setup_data)384 coap_dtls_context_set_spsk(coap_context_t *c_context,
385                            coap_dtls_spsk_t *setup_data
386                           ) {
387   coap_gnutls_context_t *g_context =
388       ((coap_gnutls_context_t *)c_context->dtls_context);
389 
390   if (!g_context || !setup_data)
391     return 0;
392 
393   g_context->psk_pki_enabled |= IS_PSK;
394   return 1;
395 }
396 #endif /* COAP_SERVER_SUPPORT */
397 
398 #if COAP_CLIENT_SUPPORT
399 /*
400  * return 0 failed
401  *        1 passed
402  */
403 int
coap_dtls_context_set_cpsk(coap_context_t * c_context,coap_dtls_cpsk_t * setup_data)404 coap_dtls_context_set_cpsk(coap_context_t *c_context,
405                            coap_dtls_cpsk_t *setup_data
406                           ) {
407   coap_gnutls_context_t *g_context =
408       ((coap_gnutls_context_t *)c_context->dtls_context);
409 
410   if (!g_context || !setup_data)
411     return 0;
412 
413   g_context->psk_pki_enabled |= IS_PSK;
414   return 1;
415 }
416 #endif /* COAP_CLIENT_SUPPORT */
417 
418 /*
419  * return 0 failed
420  *        1 passed
421  */
422 int
coap_dtls_context_check_keys_enabled(coap_context_t * c_context)423 coap_dtls_context_check_keys_enabled(coap_context_t *c_context) {
424   coap_gnutls_context_t *g_context =
425       ((coap_gnutls_context_t *)c_context->dtls_context);
426   return g_context->psk_pki_enabled ? 1 : 0;
427 }
428 
429 void
coap_dtls_startup(void)430 coap_dtls_startup(void) {
431   gnutls_global_set_audit_log_function(coap_gnutls_audit_log_func);
432   gnutls_global_set_log_function(coap_gnutls_log_func);
433 }
434 
435 void
coap_dtls_shutdown(void)436 coap_dtls_shutdown(void) {
437   coap_dtls_set_log_level(COAP_LOG_EMERG);
438 }
439 
440 void *
coap_dtls_get_tls(const coap_session_t * c_session,coap_tls_library_t * tls_lib)441 coap_dtls_get_tls(const coap_session_t *c_session,
442                   coap_tls_library_t *tls_lib) {
443   if (tls_lib)
444     *tls_lib = COAP_TLS_LIBRARY_GNUTLS;
445   if (c_session && c_session->tls) {
446     const coap_gnutls_env_t *g_env = (const coap_gnutls_env_t *)c_session->tls;
447 
448     return g_env->g_session;
449   }
450   return NULL;
451 }
452 
453 void
coap_dtls_set_log_level(coap_log_t level)454 coap_dtls_set_log_level(coap_log_t level) {
455   dtls_log_level = level;
456   gnutls_global_set_log_level(dtls_log_level);
457 }
458 
459 /*
460  * return current logging level
461  */
462 coap_log_t
coap_dtls_get_log_level(void)463 coap_dtls_get_log_level(void) {
464   return dtls_log_level;
465 }
466 
467 /*
468  * return +ve  new g_context
469  *        NULL failure
470  */
471 void *
coap_dtls_new_context(coap_context_t * c_context COAP_UNUSED)472 coap_dtls_new_context(coap_context_t *c_context COAP_UNUSED) {
473   const char *err;
474   int ret;
475   coap_gnutls_context_t *g_context =
476       (coap_gnutls_context_t *)
477       gnutls_malloc(sizeof(coap_gnutls_context_t));
478 
479   if (g_context) {
480     coap_tls_version_t *tls_version = coap_get_tls_library_version();
481     const char *priority;
482 
483     G_CHECK(gnutls_global_init(), "gnutls_global_init");
484     memset(g_context, 0, sizeof(coap_gnutls_context_t));
485     g_context->alpn_proto.data = gnutls_malloc(4);
486     if (g_context->alpn_proto.data) {
487       memcpy(g_context->alpn_proto.data, "coap", 4);
488       g_context->alpn_proto.size = 4;
489     }
490 
491     if (tls_version->version >= 0x030606) {
492       priority = VARIANTS_3_6_6;
493     } else if (tls_version->version >= 0x030505) {
494       priority = VARIANTS_3_5_5;
495     } else {
496       priority = VARIANTS_BASE;
497     }
498     ret = gnutls_priority_init(&g_context->priority_cache, priority, &err);
499     if (ret != GNUTLS_E_SUCCESS) {
500       if (ret == GNUTLS_E_INVALID_REQUEST)
501         coap_log_warn("gnutls_priority_init: Syntax error at: %s\n", err);
502       else
503         coap_log_warn("gnutls_priority_init: %s\n", gnutls_strerror(ret));
504       goto fail;
505     }
506   }
507   return g_context;
508 
509 fail:
510   if (g_context)
511     coap_dtls_free_context(g_context);
512   return NULL;
513 }
514 
515 void
coap_dtls_free_context(void * handle)516 coap_dtls_free_context(void *handle) {
517   size_t i;
518   coap_gnutls_context_t *g_context = (coap_gnutls_context_t *)handle;
519 
520   gnutls_free(g_context->alpn_proto.data);
521   gnutls_free(g_context->root_ca_file);
522   gnutls_free(g_context->root_ca_path);
523   for (i = 0; i < g_context->pki_sni_count; i++) {
524     gnutls_free(g_context->pki_sni_entry_list[i].sni);
525     gnutls_certificate_free_credentials(
526         g_context->pki_sni_entry_list[i].pki_credentials);
527   }
528   if (g_context->pki_sni_entry_list)
529     gnutls_free(g_context->pki_sni_entry_list);
530 
531   for (i = 0; i < g_context->psk_sni_count; i++) {
532     gnutls_free(g_context->psk_sni_entry_list[i].sni);
533     /* YUK - A memory leak in 3.3.0 (fixed by 3.3.26) of hint */
534     gnutls_psk_free_server_credentials(
535         g_context->psk_sni_entry_list[i].psk_credentials);
536   }
537   if (g_context->psk_sni_entry_list)
538     gnutls_free(g_context->psk_sni_entry_list);
539 
540   gnutls_priority_deinit(g_context->priority_cache);
541 
542   gnutls_global_deinit();
543   gnutls_free(g_context);
544 }
545 
546 #if COAP_CLIENT_SUPPORT
547 /*
548  * gnutls_psk_client_credentials_function return values
549  * (see gnutls_psk_set_client_credentials_function())
550  *
551  * return -1 failed
552  *         0 passed
553  */
554 static int
psk_client_callback(gnutls_session_t g_session,char ** username,gnutls_datum_t * key)555 psk_client_callback(gnutls_session_t g_session,
556                     char **username, gnutls_datum_t *key) {
557   coap_session_t *c_session =
558       (coap_session_t *)gnutls_transport_get_ptr(g_session);
559   coap_gnutls_context_t *g_context;
560   coap_dtls_cpsk_t *setup_data;
561   const char *hint = gnutls_psk_client_get_hint(g_session);
562   coap_bin_const_t temp;
563   const coap_bin_const_t *psk_key;
564   const coap_bin_const_t *psk_identity;
565   const coap_dtls_cpsk_info_t *cpsk_info;
566 
567   /* Initialize result parameters. */
568   *username = NULL;
569   key->data = NULL;
570 
571   if (c_session == NULL)
572     return -1;
573 
574   g_context = (coap_gnutls_context_t *)c_session->context->dtls_context;
575   if (g_context == NULL)
576     return -1;
577 
578   setup_data = &c_session->cpsk_setup_data;
579 
580   temp.s = hint ? (const uint8_t *)hint : (const uint8_t *)"";
581   temp.length = strlen((const char *)temp.s);
582   coap_session_refresh_psk_hint(c_session, &temp);
583 
584   coap_log_debug("got psk_identity_hint: '%.*s'\n", (int)temp.length,
585                  (const char *)temp.s);
586 
587   if (setup_data->validate_ih_call_back) {
588     coap_str_const_t lhint;
589 
590     lhint.length = temp.length;
591     lhint.s = temp.s;
592     cpsk_info =
593         setup_data->validate_ih_call_back(&lhint,
594                                           c_session,
595                                           setup_data->ih_call_back_arg);
596 
597     if (cpsk_info == NULL)
598       return -1;
599 
600     coap_session_refresh_psk_identity(c_session, &cpsk_info->identity);
601     coap_session_refresh_psk_key(c_session, &cpsk_info->key);
602     psk_identity = &cpsk_info->identity;
603     psk_key = &cpsk_info->key;
604   } else {
605     psk_identity = coap_get_session_client_psk_identity(c_session);
606     psk_key = coap_get_session_client_psk_key(c_session);
607   }
608 
609   if (psk_identity == NULL || psk_key == NULL) {
610     coap_log_warn("no PSK available\n");
611     return -1;
612   }
613 
614   *username = gnutls_malloc(psk_identity->length+1);
615   if (*username == NULL)
616     return -1;
617   memcpy(*username, psk_identity->s, psk_identity->length);
618   (*username)[psk_identity->length] = '\000';
619 
620   key->data = gnutls_malloc(psk_key->length);
621   if (key->data == NULL) {
622     gnutls_free(*username);
623     *username = NULL;
624     return -1;
625   }
626   memcpy(key->data, psk_key->s, psk_key->length);
627   key->size = psk_key->length;
628   return 0;
629 }
630 #endif /* COAP_CLIENT_SUPPORT */
631 
632 typedef struct {
633   gnutls_certificate_type_t certificate_type;
634   char *san_or_cn;
635   const gnutls_datum_t *cert_list;
636   unsigned int cert_list_size;
637   int self_signed; /* 1 if cert self-signed, 0 otherwise */
638 } coap_gnutls_certificate_info_t;
639 
640 /*
641  * return Type of certificate and SAN or CN if appropriate derived from
642  *        certificate. GNUTLS_CRT_UNKNOWN if failure.
643  */
644 static gnutls_certificate_type_t
get_san_or_cn(gnutls_session_t g_session,coap_gnutls_certificate_info_t * cert_info)645 get_san_or_cn(gnutls_session_t g_session,
646               coap_gnutls_certificate_info_t *cert_info) {
647   gnutls_x509_crt_t cert;
648   char dn[256];
649   size_t size;
650   int n;
651   char *cn;
652   int ret;
653 
654 #if (GNUTLS_VERSION_NUMBER >= 0x030606)
655   cert_info->certificate_type = gnutls_certificate_type_get2(g_session,
656                                                              GNUTLS_CTYPE_PEERS);
657 #else /* < 3.6.6 */
658   cert_info->certificate_type = gnutls_certificate_type_get(g_session);
659 #endif /* < 3.6.6 */
660 
661   cert_info->san_or_cn = NULL;
662 
663   cert_info->cert_list = gnutls_certificate_get_peers(g_session,
664                                                       &cert_info->cert_list_size);
665   if (cert_info->cert_list_size == 0) {
666     return GNUTLS_CRT_UNKNOWN;
667   }
668 
669   if (cert_info->certificate_type != GNUTLS_CRT_X509)
670     return cert_info->certificate_type;
671 
672   G_CHECK(gnutls_x509_crt_init(&cert), "gnutls_x509_crt_init");
673 
674   /* Interested only in first cert in chain */
675   G_CHECK(gnutls_x509_crt_import(cert, &cert_info->cert_list[0],
676                                  GNUTLS_X509_FMT_DER), "gnutls_x509_crt_import");
677 
678   cert_info->self_signed = gnutls_x509_crt_check_issuer(cert, cert);
679 
680   size = sizeof(dn) -1;
681   /* See if there is a Subject Alt Name first */
682   ret = gnutls_x509_crt_get_subject_alt_name(cert, 0, dn, &size, NULL);
683   if (ret >= 0) {
684     dn[size] = '\000';
685     gnutls_x509_crt_deinit(cert);
686     cert_info->san_or_cn = gnutls_strdup(dn);
687     return cert_info->certificate_type;
688   }
689 
690   size = sizeof(dn);
691   G_CHECK(gnutls_x509_crt_get_dn(cert, dn, &size), "gnutls_x509_crt_get_dn");
692 
693   gnutls_x509_crt_deinit(cert);
694 
695   /* Need to emulate strcasestr() here.  Looking for CN= */
696   n = strlen(dn) - 3;
697   cn = dn;
698   while (n > 0) {
699     if (((cn[0] == 'C') || (cn[0] == 'c')) &&
700         ((cn[1] == 'N') || (cn[1] == 'n')) &&
701         (cn[2] == '=')) {
702       cn += 3;
703       break;
704     }
705     cn++;
706     n--;
707   }
708   if (n > 0) {
709     char *ecn = strchr(cn, ',');
710     if (ecn) {
711       cn[ecn-cn] = '\000';
712     }
713     cert_info->san_or_cn = gnutls_strdup(cn);
714     return cert_info->certificate_type;
715   }
716   return GNUTLS_CRT_UNKNOWN;
717 
718 fail:
719   return GNUTLS_CRT_UNKNOWN;
720 }
721 
722 #if (GNUTLS_VERSION_NUMBER >= 0x030606)
723 #define OUTPUT_CERT_NAME (cert_type == GNUTLS_CRT_X509 ? \
724                           cert_info.san_or_cn : \
725                           cert_type == GNUTLS_CRT_RAW ? \
726                           COAP_DTLS_RPK_CERT_CN : "?")
727 #else /* GNUTLS_VERSION_NUMBER < 0x030606 */
728 #define OUTPUT_CERT_NAME (cert_type == GNUTLS_CRT_X509 ? \
729                           cert_info.san_or_cn : "?")
730 #endif /* GNUTLS_VERSION_NUMBER < 0x030606 */
731 
732 #if (GNUTLS_VERSION_NUMBER >= 0x030606)
733 static int
check_rpk_cert(coap_gnutls_context_t * g_context,coap_gnutls_certificate_info_t * cert_info,coap_session_t * c_session)734 check_rpk_cert(coap_gnutls_context_t *g_context,
735                coap_gnutls_certificate_info_t *cert_info,
736                coap_session_t *c_session) {
737   int ret;
738 
739   if (g_context->setup_data.validate_cn_call_back) {
740     gnutls_pcert_st pcert;
741     uint8_t der[2048];
742     size_t size;
743 
744     G_CHECK(gnutls_pcert_import_rawpk_raw(&pcert, &cert_info->cert_list[0],
745                                           GNUTLS_X509_FMT_DER, 0, 0),
746             "gnutls_pcert_import_rawpk_raw");
747 
748     size = sizeof(der);
749     G_CHECK(gnutls_pubkey_export(pcert.pubkey, GNUTLS_X509_FMT_DER, der, &size),
750             "gnutls_pubkey_export");
751     gnutls_pcert_deinit(&pcert);
752     if (!g_context->setup_data.validate_cn_call_back(COAP_DTLS_RPK_CERT_CN,
753                                                      der,
754                                                      size,
755                                                      c_session,
756                                                      0,
757                                                      1,
758                                                      g_context->setup_data.cn_call_back_arg)) {
759       return 0;
760     }
761   }
762   return 1;
763 fail:
764   return 0;
765 }
766 #endif /* >= 3.6.6 */
767 
768 /*
769  * return 0 failed
770  *        1 passed
771  */
772 static int
cert_verify_gnutls(gnutls_session_t g_session)773 cert_verify_gnutls(gnutls_session_t g_session) {
774   unsigned int status = 0;
775   unsigned int fail = 0;
776   coap_session_t *c_session =
777       (coap_session_t *)gnutls_transport_get_ptr(g_session);
778   coap_gnutls_context_t *g_context =
779       (coap_gnutls_context_t *)c_session->context->dtls_context;
780   coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
781   int alert = GNUTLS_A_BAD_CERTIFICATE;
782   int ret;
783   coap_gnutls_certificate_info_t cert_info;
784   gnutls_certificate_type_t cert_type;
785 
786   memset(&cert_info, 0, sizeof(cert_info));
787   cert_type = get_san_or_cn(g_session, &cert_info);
788 #if (GNUTLS_VERSION_NUMBER >= 0x030606)
789   if (cert_type == GNUTLS_CRT_RAW) {
790     if (!check_rpk_cert(g_context, &cert_info, c_session)) {
791       alert = GNUTLS_A_ACCESS_DENIED;
792       goto fail;
793     }
794     goto ok;
795   }
796 #endif /* >= 3.6.6 */
797 
798   if (cert_info.cert_list_size == 0 && !g_context->setup_data.verify_peer_cert)
799     goto ok;
800 
801   G_CHECK(gnutls_certificate_verify_peers(g_session, NULL, 0, &status),
802           "gnutls_certificate_verify_peers");
803 
804   if (status) {
805     status &= ~(GNUTLS_CERT_INVALID);
806     if (status & (GNUTLS_CERT_NOT_ACTIVATED|GNUTLS_CERT_EXPIRED)) {
807       status &= ~(GNUTLS_CERT_NOT_ACTIVATED|GNUTLS_CERT_EXPIRED);
808       if (g_context->setup_data.allow_expired_certs) {
809         coap_log_info("   %s: %s: overridden: '%s'\n",
810                       coap_session_str(c_session),
811                       "The certificate has an invalid usage date",
812                       OUTPUT_CERT_NAME);
813       } else {
814         fail = 1;
815         coap_log_warn("   %s: %s: '%s'\n",
816                       coap_session_str(c_session),
817                       "The certificate has an invalid usage date",
818                       OUTPUT_CERT_NAME);
819       }
820     }
821     if (status & (GNUTLS_CERT_REVOCATION_DATA_SUPERSEDED|
822                   GNUTLS_CERT_REVOCATION_DATA_ISSUED_IN_FUTURE)) {
823       status &= ~(GNUTLS_CERT_REVOCATION_DATA_SUPERSEDED|
824                   GNUTLS_CERT_REVOCATION_DATA_ISSUED_IN_FUTURE);
825       if (g_context->setup_data.allow_expired_crl) {
826         coap_log_info("   %s: %s: overridden: '%s'\n",
827                       coap_session_str(c_session),
828                       "The certificate's CRL entry has an invalid usage date",
829                       OUTPUT_CERT_NAME);
830       } else {
831         fail = 1;
832         coap_log_warn("   %s: %s: '%s'\n",
833                       coap_session_str(c_session),
834                       "The certificate's CRL entry has an invalid usage date",
835                       OUTPUT_CERT_NAME);
836       }
837     }
838     if (status & (GNUTLS_CERT_SIGNER_NOT_FOUND)) {
839       status &= ~(GNUTLS_CERT_SIGNER_NOT_FOUND);
840       if (cert_info.self_signed) {
841         if (g_context->setup_data.allow_self_signed &&
842             !g_context->setup_data.check_common_ca) {
843           coap_log_info("   %s: %s: overridden: '%s'\n",
844                         coap_session_str(c_session),
845                         "Self-signed",
846                         OUTPUT_CERT_NAME);
847         } else {
848           fail = 1;
849           alert = GNUTLS_A_UNKNOWN_CA;
850           coap_log_warn("   %s: %s: '%s'\n",
851                         coap_session_str(c_session),
852                         "Self-signed",
853                         OUTPUT_CERT_NAME);
854         }
855       } else {
856         if (!g_context->setup_data.verify_peer_cert) {
857           coap_log_info("   %s: %s: overridden: '%s'\n",
858                         coap_session_str(c_session),
859                         "The peer certificate's CA is unknown",
860                         OUTPUT_CERT_NAME);
861         } else {
862           fail = 1;
863           alert = GNUTLS_A_UNKNOWN_CA;
864           coap_log_warn("   %s: %s: '%s'\n",
865                         coap_session_str(c_session),
866                         "The peer certificate's CA is unknown",
867                         OUTPUT_CERT_NAME);
868         }
869       }
870     }
871 
872     if (status) {
873       fail = 1;
874       coap_log_warn("   %s: gnutls_certificate_verify_peers() status 0x%x: '%s'\n",
875                     coap_session_str(c_session),
876                     status, OUTPUT_CERT_NAME);
877     }
878   }
879 
880   if (fail)
881     goto fail;
882 
883   if (g_context->setup_data.validate_cn_call_back) {
884     gnutls_x509_crt_t cert;
885     uint8_t der[2048];
886     size_t size;
887     /* status == 0 indicates that the certificate passed to
888      *  setup_data.validate_cn_call_back has been validated. */
889     const int cert_is_trusted = !status;
890 
891     G_CHECK(gnutls_x509_crt_init(&cert), "gnutls_x509_crt_init");
892 
893     /* Interested only in first cert in chain */
894     G_CHECK(gnutls_x509_crt_import(cert, &cert_info.cert_list[0],
895                                    GNUTLS_X509_FMT_DER), "gnutls_x509_crt_import");
896 
897     size = sizeof(der);
898     G_CHECK(gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_DER, der, &size),
899             "gnutls_x509_crt_export");
900     gnutls_x509_crt_deinit(cert);
901     if (!g_context->setup_data.validate_cn_call_back(OUTPUT_CERT_NAME,
902                                                      der,
903                                                      size,
904                                                      c_session,
905                                                      0,
906                                                      cert_is_trusted,
907                                                      g_context->setup_data.cn_call_back_arg)) {
908       alert = GNUTLS_A_ACCESS_DENIED;
909       goto fail;
910     }
911   }
912 
913   if (g_context->setup_data.additional_tls_setup_call_back) {
914     /* Additional application setup wanted */
915     if (!g_context->setup_data.additional_tls_setup_call_back(g_session,
916                                                               &g_context->setup_data)) {
917       goto fail;
918     }
919   }
920 
921 ok:
922   if (cert_info.san_or_cn)
923     gnutls_free(cert_info.san_or_cn);
924 
925   return 1;
926 
927 fail:
928   if (cert_info.san_or_cn)
929     gnutls_free(cert_info.san_or_cn);
930 
931   if (!g_env->sent_alert) {
932     G_ACTION(gnutls_alert_send(g_session, GNUTLS_AL_FATAL, alert));
933     g_env->sent_alert = 1;
934   }
935   c_session->dtls_event = COAP_EVENT_DTLS_CLOSED;
936   return 0;
937 }
938 
939 /*
940  * gnutls_certificate_verify_function return values
941  * (see gnutls_certificate_set_verify_function())
942  *
943  * return -1 failed
944  *         0 passed
945  */
946 static int
cert_verify_callback_gnutls(gnutls_session_t g_session)947 cert_verify_callback_gnutls(gnutls_session_t g_session) {
948   if (gnutls_auth_get_type(g_session) == GNUTLS_CRD_CERTIFICATE) {
949     if (cert_verify_gnutls(g_session) == 0) {
950       return -1;
951     }
952   }
953   return 0;
954 }
955 
956 #ifndef min
957 #define min(a,b) ((a) < (b) ? (a) : (b))
958 #endif
959 
960 static int
pin_callback(void * user_data,int attempt,const char * token_url COAP_UNUSED,const char * token_label COAP_UNUSED,unsigned int flags COAP_UNUSED,char * pin,size_t pin_max)961 pin_callback(void *user_data, int attempt,
962              const char *token_url COAP_UNUSED,
963              const char *token_label COAP_UNUSED,
964              unsigned int flags COAP_UNUSED,
965              char *pin,
966              size_t pin_max) {
967   coap_dtls_pki_t *setup_data = (coap_dtls_pki_t *)user_data;
968 
969   /* Only do this on first attempt to prevent token lockout */
970   if (attempt == 0 && setup_data && setup_data->pki_key.key.pkcs11.user_pin) {
971     int len = min(pin_max - 1, strlen(setup_data->pki_key.key.pkcs11.user_pin));
972     memcpy(pin, setup_data->pki_key.key.pkcs11.user_pin, len);
973     pin[len] = 0;
974     return 0;
975   }
976   return -1;
977 }
978 #if (GNUTLS_VERSION_NUMBER >= 0x030606)
979 /* first part of Raw public key, this is the start of the Subject Public Key */
980 static const unsigned char cert_asn1_header1[] = {
981   0x30, 0x59, /* SEQUENCE, length 89 bytes */
982   0x30, 0x13, /* SEQUENCE, length 19 bytes */
983   0x06, 0x07, /* OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1) */
984   0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01,
985 };
986 /* PrimeX will get inserted */
987 #if 0
988 0x06, 0x08, /* OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7) */
989       0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07,
990 #endif
991 static const unsigned char cert_asn1_header2[] = {
992   0x03, 0x42, /* BIT STRING, length 66 bytes */
993   /* Note: 0 bits (0x00) and no compression (0x04) are already in the certificate */
994 };
995 
996 static gnutls_datum_t *
get_asn1_spki(const uint8_t * data,size_t size)997 get_asn1_spki(const uint8_t *data, size_t size) {
998   coap_binary_t *pub_key = get_asn1_tag(COAP_ASN1_BITSTRING, data, size, NULL);
999   coap_binary_t *prime = get_asn1_tag(COAP_ASN1_IDENTIFIER, data, size, NULL);
1000   gnutls_datum_t *spki = NULL;
1001 
1002   if (pub_key && prime) {
1003     size_t header_size = sizeof(cert_asn1_header1) +
1004                          2 +
1005                          prime->length +
1006                          sizeof(cert_asn1_header2);
1007     uint8_t *tmp = gnutls_malloc(sizeof(gnutls_datum_t) +
1008                                  header_size +
1009                                  pub_key->length);
1010 
1011     if (tmp) {
1012       spki = (gnutls_datum_t *)tmp;
1013       spki->data = &tmp[sizeof(gnutls_datum_t)];
1014       memcpy(&spki->data[header_size], pub_key->s, pub_key->length);
1015       memcpy(spki->data, cert_asn1_header1, sizeof(cert_asn1_header1));
1016       spki->data[sizeof(cert_asn1_header1)] = COAP_ASN1_IDENTIFIER;
1017       spki->data[sizeof(cert_asn1_header1)+1] = prime->length;
1018       memcpy(&spki->data[sizeof(cert_asn1_header1)+2],
1019              prime->s, prime->length);
1020       memcpy(&spki->data[sizeof(cert_asn1_header1)+2+prime->length],
1021              cert_asn1_header2, sizeof(cert_asn1_header2));
1022       spki->size = header_size + pub_key->length;
1023     }
1024   }
1025   if (pub_key)
1026     coap_delete_binary(pub_key);
1027   if (prime)
1028     coap_delete_binary(prime);
1029   return spki;
1030 }
1031 #endif /* GNUTLS_VERSION_NUMBER >= 0x030606 */
1032 
1033 /*
1034  * return 0   Success (GNUTLS_E_SUCCESS)
1035  *        neg GNUTLS_E_* error code
1036  */
1037 static int
setup_pki_credentials(gnutls_certificate_credentials_t * pki_credentials,gnutls_session_t g_session,coap_gnutls_context_t * g_context,coap_dtls_pki_t * setup_data,coap_dtls_role_t role)1038 setup_pki_credentials(gnutls_certificate_credentials_t *pki_credentials,
1039                       gnutls_session_t g_session,
1040                       coap_gnutls_context_t *g_context,
1041                       coap_dtls_pki_t *setup_data, coap_dtls_role_t role) {
1042   int ret;
1043 
1044   G_CHECK(gnutls_certificate_allocate_credentials(pki_credentials),
1045           "gnutls_certificate_allocate_credentials");
1046 
1047   switch (setup_data->pki_key.key_type) {
1048   case COAP_PKI_KEY_PEM:
1049     if (setup_data->pki_key.key.pem.public_cert &&
1050         setup_data->pki_key.key.pem.public_cert[0] &&
1051         setup_data->pki_key.key.pem.private_key &&
1052         setup_data->pki_key.key.pem.private_key[0]) {
1053       if (setup_data->is_rpk_not_cert) {
1054         coap_log_warn("RPK keys cannot be in COAP_PKI_KEY_PEM format\n");
1055         return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
1056       } else {
1057         G_CHECK(gnutls_certificate_set_x509_key_file(*pki_credentials,
1058                                                      setup_data->pki_key.key.pem.public_cert,
1059                                                      setup_data->pki_key.key.pem.private_key,
1060                                                      GNUTLS_X509_FMT_PEM),
1061                 "gnutls_certificate_set_x509_key_file");
1062       }
1063     } else if (role == COAP_DTLS_ROLE_SERVER) {
1064       coap_log_err("***setup_pki: (D)TLS: No %s Certificate + Private "
1065                    "Key defined\n",
1066                    role == COAP_DTLS_ROLE_SERVER ? "Server" : "Client");
1067       return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
1068     }
1069     if (setup_data->pki_key.key.pem.ca_file &&
1070         setup_data->pki_key.key.pem.ca_file[0]) {
1071       ret = gnutls_certificate_set_x509_trust_file(*pki_credentials,
1072                                                    setup_data->pki_key.key.pem.ca_file,
1073                                                    GNUTLS_X509_FMT_PEM);
1074       if (ret == 0) {
1075         coap_log_warn("gnutls_certificate_set_x509_trust_file: No certificates found\n");
1076       } else if (ret < 0) {
1077         coap_log_warn("%s: '%s'\n",
1078                       "gnutls_certificate_set_x509_trust_file",
1079                       gnutls_strerror(ret));
1080         goto fail;
1081       }
1082     }
1083     break;
1084 
1085   case COAP_PKI_KEY_PEM_BUF:
1086     if (setup_data->pki_key.key.pem_buf.public_cert &&
1087         setup_data->pki_key.key.pem_buf.public_cert_len &&
1088         setup_data->pki_key.key.pem_buf.private_key &&
1089         setup_data->pki_key.key.pem_buf.private_key_len) {
1090       gnutls_datum_t cert;
1091       gnutls_datum_t key;
1092       int alloced_cert_memory = 0;
1093       int alloced_key_memory = 0;
1094 
1095       cert.size = setup_data->pki_key.key.pem_buf.public_cert_len;
1096       if (setup_data->pki_key.key.pem_buf.public_cert[cert.size-1] != '\000') {
1097         /* Need to allocate memory, rather than just copying pointers across */
1098         alloced_cert_memory = 1;
1099         cert.data = gnutls_malloc(cert.size + 1);
1100         if (!cert.data) {
1101           coap_log_err("gnutls_malloc failure\n");
1102           return GNUTLS_E_MEMORY_ERROR;
1103         }
1104         memcpy(cert.data, setup_data->pki_key.key.pem_buf.public_cert,
1105                cert.size);
1106         cert.data[cert.size] = '\000';
1107         cert.size++;
1108       } else {
1109         /* To get around const issue */
1110         memcpy(&cert.data,
1111                &setup_data->pki_key.key.pem_buf.public_cert, sizeof(cert.data));
1112       }
1113       key.size = setup_data->pki_key.key.pem_buf.private_key_len;
1114       if (setup_data->pki_key.key.pem_buf.private_key[key.size-1] != '\000') {
1115         /* Need to allocate memory, rather than just copying pointers across */
1116         alloced_key_memory = 1;
1117         key.data = gnutls_malloc(key.size + 1);
1118         if (!key.data) {
1119           coap_log_err("gnutls_malloc failure\n");
1120           if (alloced_cert_memory)
1121             gnutls_free(cert.data);
1122           return GNUTLS_E_MEMORY_ERROR;
1123         }
1124         memcpy(key.data, setup_data->pki_key.key.pem_buf.private_key, key.size);
1125         key.data[key.size] = '\000';
1126         key.size++;
1127       } else {
1128         /* To get around const issue */
1129         memcpy(&key.data,
1130                &setup_data->pki_key.key.pem_buf.private_key, sizeof(key.data));
1131       }
1132       if (setup_data->is_rpk_not_cert) {
1133 #if (GNUTLS_VERSION_NUMBER >= 0x030606)
1134         int have_done_key = 0;
1135         if (strstr((char *)key.data, "-----BEGIN EC PRIVATE KEY-----")) {
1136           gnutls_datum_t der_private;
1137 
1138           if (gnutls_pem_base64_decode2("EC PRIVATE KEY", &key,
1139                                         &der_private) == 0) {
1140             gnutls_datum_t *spki = get_asn1_spki(der_private.data,
1141                                                  der_private.size);
1142 
1143             if (spki) {
1144               ret = gnutls_certificate_set_rawpk_key_mem(*pki_credentials,
1145                                                          spki,
1146                                                          &der_private,
1147                                                          GNUTLS_X509_FMT_DER, NULL,
1148                                                          COAP_GNUTLS_KEY_RPK,
1149                                                          NULL, 0, 0);
1150               if (ret >= 0) {
1151                 have_done_key = 1;
1152               }
1153               gnutls_free(spki);
1154             }
1155             gnutls_free(der_private.data);
1156           }
1157         }
1158         if (!have_done_key) {
1159           G_CHECK(gnutls_certificate_set_rawpk_key_mem(*pki_credentials,
1160                                                        &cert,
1161                                                        &key,
1162                                                        GNUTLS_X509_FMT_PEM, NULL,
1163                                                        COAP_GNUTLS_KEY_RPK,
1164                                                        NULL, 0, 0),
1165                   "gnutls_certificate_set_rawpk_key_mem");
1166         }
1167 #else /* GNUTLS_VERSION_NUMBER < 0x030606 */
1168         coap_log_err("RPK Support not available (needs gnutls 3.6.6 or later)\n");
1169         if (alloced_cert_memory)
1170           gnutls_free(cert.data);
1171         if (alloced_key_memory)
1172           gnutls_free(key.data);
1173         return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
1174 #endif /* GNUTLS_VERSION_NUMBER < 0x030606 */
1175       } else {
1176         G_CHECK(gnutls_certificate_set_x509_key_mem(*pki_credentials,
1177                                                     &cert,
1178                                                     &key,
1179                                                     GNUTLS_X509_FMT_PEM),
1180                 "gnutls_certificate_set_x509_key_mem");
1181       }
1182       if (alloced_cert_memory)
1183         gnutls_free(cert.data);
1184       if (alloced_key_memory)
1185         gnutls_free(key.data);
1186     } else if (role == COAP_DTLS_ROLE_SERVER) {
1187       coap_log_err("***setup_pki: (D)TLS: No Server Certificate + Private "
1188                    "Key defined\n");
1189       return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
1190     }
1191     if (setup_data->pki_key.key.pem_buf.ca_cert &&
1192         setup_data->pki_key.key.pem_buf.ca_cert_len) {
1193       gnutls_datum_t ca;
1194       int alloced_ca_memory = 0;
1195 
1196       ca.size = setup_data->pki_key.key.pem_buf.ca_cert_len;
1197       if (setup_data->pki_key.key.pem_buf.ca_cert[ca.size-1] != '\000') {
1198         /* Need to allocate memory, rather than just copying pointers across */
1199         alloced_ca_memory = 1;
1200         ca.data = gnutls_malloc(ca.size + 1);
1201         if (!ca.data) {
1202           coap_log_err("gnutls_malloc failure\n");
1203           return GNUTLS_E_MEMORY_ERROR;
1204         }
1205         memcpy(ca.data, setup_data->pki_key.key.pem_buf.ca_cert, ca.size);
1206         ca.data[ca.size] = '\000';
1207         ca.size++;
1208       } else {
1209         /* To get around const issue */
1210         memcpy(&ca.data,
1211                &setup_data->pki_key.key.pem_buf.ca_cert, sizeof(ca.data));
1212       }
1213       ret = gnutls_certificate_set_x509_trust_mem(*pki_credentials,
1214                                                   &ca,
1215                                                   GNUTLS_X509_FMT_PEM);
1216       if (ret == 0) {
1217         coap_log_warn("gnutls_certificate_set_x509_trust_mem: No certificates found\n");
1218       } else if (ret < 0) {
1219         coap_log_warn("%s: '%s'\n",
1220                       "gnutls_certificate_set_x509_trust_mem",
1221                       gnutls_strerror(ret));
1222         if (alloced_ca_memory)
1223           gnutls_free(ca.data);
1224         goto fail;
1225       }
1226       if (alloced_ca_memory)
1227         gnutls_free(ca.data);
1228     }
1229     break;
1230 
1231   case COAP_PKI_KEY_ASN1:
1232     if (setup_data->pki_key.key.asn1.public_cert &&
1233         setup_data->pki_key.key.asn1.public_cert_len &&
1234         setup_data->pki_key.key.asn1.private_key &&
1235         setup_data->pki_key.key.asn1.private_key_len > 0) {
1236       gnutls_datum_t cert;
1237       gnutls_datum_t key;
1238 
1239       /* Kludge to get around const parameters */
1240       memcpy(&cert.data, &setup_data->pki_key.key.asn1.public_cert,
1241              sizeof(cert.data));
1242       cert.size = setup_data->pki_key.key.asn1.public_cert_len;
1243       memcpy(&key.data, &setup_data->pki_key.key.asn1.private_key,
1244              sizeof(key.data));
1245       key.size = setup_data->pki_key.key.asn1.private_key_len;
1246       if (setup_data->is_rpk_not_cert) {
1247 #if (GNUTLS_VERSION_NUMBER >= 0x030606)
1248         int have_done_key = 0;
1249         if (setup_data->pki_key.key.asn1.private_key_type ==
1250             COAP_ASN1_PKEY_EC) {
1251           gnutls_datum_t *spki = get_asn1_spki(key.data,
1252                                                key.size);
1253 
1254           if (spki) {
1255             ret = gnutls_certificate_set_rawpk_key_mem(*pki_credentials,
1256                                                        spki,
1257                                                        &key,
1258                                                        GNUTLS_X509_FMT_DER, NULL,
1259                                                        COAP_GNUTLS_KEY_RPK,
1260                                                        NULL, 0, 0);
1261             if (ret >= 0) {
1262               have_done_key = 1;
1263             }
1264             gnutls_free(spki);
1265           }
1266         }
1267         if (!have_done_key) {
1268           G_CHECK(gnutls_certificate_set_rawpk_key_mem(*pki_credentials,
1269                                                        &cert,
1270                                                        &key,
1271                                                        GNUTLS_X509_FMT_DER, NULL,
1272                                                        COAP_GNUTLS_KEY_RPK,
1273                                                        NULL, 0, 0),
1274                   "gnutls_certificate_set_rawpk_key_mem");
1275         }
1276 #else /* GNUTLS_VERSION_NUMBER < 0x030606 */
1277         coap_log_err("RPK Support not available (needs gnutls 3.6.6 or later)\n");
1278         return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
1279 #endif /* GNUTLS_VERSION_NUMBER < 0x030606 */
1280       } else {
1281         G_CHECK(gnutls_certificate_set_x509_key_mem(*pki_credentials,
1282                                                     &cert,
1283                                                     &key,
1284                                                     GNUTLS_X509_FMT_DER),
1285                 "gnutls_certificate_set_x509_key_mem");
1286       }
1287     } else if (role == COAP_DTLS_ROLE_SERVER) {
1288       coap_log_err("***setup_pki: (D)TLS: No %s Certificate + Private "
1289                    "Key defined\n",
1290                    role == COAP_DTLS_ROLE_SERVER ? "Server" : "Client");
1291       return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
1292     }
1293     if (setup_data->pki_key.key.asn1.ca_cert &&
1294         setup_data->pki_key.key.asn1.ca_cert_len > 0) {
1295       gnutls_datum_t ca_cert;
1296 
1297       /* Kludge to get around const parameters */
1298       memcpy(&ca_cert.data, &setup_data->pki_key.key.asn1.ca_cert,
1299              sizeof(ca_cert.data));
1300       ca_cert.size = setup_data->pki_key.key.asn1.ca_cert_len;
1301       ret = gnutls_certificate_set_x509_trust_mem(*pki_credentials,
1302                                                   &ca_cert,
1303                                                   GNUTLS_X509_FMT_DER);
1304       if (ret == 0) {
1305         coap_log_warn("gnutls_certificate_set_x509_trust_mem: No certificates found\n");
1306       } else if (ret < 0) {
1307         coap_log_warn("%s: '%s'\n",
1308                       "gnutls_certificate_set_x509_trust_mem",
1309                       gnutls_strerror(ret));
1310         goto fail;
1311       }
1312     }
1313     break;
1314 
1315   case COAP_PKI_KEY_PKCS11:
1316     if (setup_data->pki_key.key.pkcs11.public_cert &&
1317         setup_data->pki_key.key.pkcs11.public_cert[0] &&
1318         setup_data->pki_key.key.pkcs11.private_key &&
1319         setup_data->pki_key.key.pkcs11.private_key[0]) {
1320 
1321       gnutls_pkcs11_set_pin_function(pin_callback, setup_data);
1322       if (setup_data->is_rpk_not_cert) {
1323 #if (GNUTLS_VERSION_NUMBER >= 0x030606)
1324         G_CHECK(gnutls_certificate_set_rawpk_key_file(*pki_credentials,
1325                                                       setup_data->pki_key.key.pkcs11.public_cert,
1326                                                       setup_data->pki_key.key.pkcs11.private_key,
1327                                                       GNUTLS_X509_FMT_PEM, NULL,
1328                                                       COAP_GNUTLS_KEY_RPK,
1329                                                       NULL, 0, GNUTLS_PKCS_PLAIN, 0),
1330                 "gnutls_certificate_set_rawpk_key_file");
1331 #else /* GNUTLS_VERSION_NUMBER < 0x030606 */
1332         coap_log_err("RPK Support not available (needs gnutls 3.6.6 or later)\n");
1333         return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
1334 #endif /* GNUTLS_VERSION_NUMBER < 0x030606 */
1335       } else {
1336         G_CHECK(gnutls_certificate_set_x509_key_file(*pki_credentials,
1337                                                      setup_data->pki_key.key.pkcs11.public_cert,
1338                                                      setup_data->pki_key.key.pkcs11.private_key,
1339                                                      GNUTLS_X509_FMT_DER),
1340                 "gnutls_certificate_set_x509_key_file");
1341       }
1342     } else if (role == COAP_DTLS_ROLE_SERVER) {
1343       coap_log_err("***setup_pki: (D)TLS: No %s Certificate + Private "
1344                    "Key defined\n",
1345                    role == COAP_DTLS_ROLE_SERVER ? "Server" : "Client");
1346       return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
1347     }
1348     if (setup_data->pki_key.key.pkcs11.ca &&
1349         setup_data->pki_key.key.pkcs11.ca[0]) {
1350       ret = gnutls_certificate_set_x509_trust_file(*pki_credentials,
1351                                                    setup_data->pki_key.key.pkcs11.ca,
1352                                                    GNUTLS_X509_FMT_DER);
1353       if (ret == 0) {
1354         coap_log_warn("gnutls_certificate_set_x509_trust_file: No certificates found\n");
1355       } else if (ret < 0) {
1356         coap_log_warn("%s: '%s'\n",
1357                       "gnutls_certificate_set_x509_trust_file",
1358                       gnutls_strerror(ret));
1359         goto fail;
1360       }
1361     }
1362     break;
1363 
1364   default:
1365     coap_log_err("***setup_pki: (D)TLS: Unknown key type %d\n",
1366                  setup_data->pki_key.key_type);
1367     return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
1368   }
1369 
1370   if (g_context->root_ca_file) {
1371     ret = gnutls_certificate_set_x509_trust_file(*pki_credentials,
1372                                                  g_context->root_ca_file,
1373                                                  GNUTLS_X509_FMT_PEM);
1374     if (ret == 0) {
1375       coap_log_warn("gnutls_certificate_set_x509_trust_file: Root CA: No certificates found\n");
1376     }
1377   }
1378   if (g_context->root_ca_path) {
1379 #if (GNUTLS_VERSION_NUMBER >= 0x030306)
1380     G_CHECK(gnutls_certificate_set_x509_trust_dir(*pki_credentials,
1381                                                   g_context->root_ca_path,
1382                                                   GNUTLS_X509_FMT_PEM),
1383             "gnutls_certificate_set_x509_trust_dir");
1384 #endif
1385   }
1386   gnutls_certificate_send_x509_rdn_sequence(g_session,
1387                                             setup_data->check_common_ca ? 0 : 1);
1388   if (!(g_context->psk_pki_enabled & IS_PKI)) {
1389     /* No PKI defined at all - still need a trust set up for 3.6.0 or later */
1390     G_CHECK(gnutls_certificate_set_x509_system_trust(*pki_credentials),
1391             "gnutls_certificate_set_x509_system_trust");
1392   }
1393 
1394   /* Verify Peer */
1395   gnutls_certificate_set_verify_function(*pki_credentials,
1396                                          cert_verify_callback_gnutls);
1397 
1398   /* Cert chain checking (can raise GNUTLS_E_CONSTRAINT_ERROR) */
1399   if (setup_data->cert_chain_validation) {
1400     gnutls_certificate_set_verify_limits(*pki_credentials,
1401                                          0,
1402                                          setup_data->cert_chain_verify_depth + 2);
1403   }
1404 
1405   /*
1406    * Check for self signed
1407    *           CRL checking (can raise GNUTLS_CERT_MISSING_OCSP_STATUS)
1408    */
1409   gnutls_certificate_set_verify_flags(*pki_credentials,
1410                                       (setup_data->check_cert_revocation == 0 ?
1411                                        GNUTLS_VERIFY_DISABLE_CRL_CHECKS : 0)
1412                                      );
1413 
1414   return GNUTLS_E_SUCCESS;
1415 
1416 fail:
1417   return ret;
1418 }
1419 
1420 #if COAP_SERVER_SUPPORT
1421 /*
1422  * return 0   Success (GNUTLS_E_SUCCESS)
1423  *        neg GNUTLS_E_* error code
1424  */
1425 static int
setup_psk_credentials(gnutls_psk_server_credentials_t * psk_credentials,coap_gnutls_context_t * g_context COAP_UNUSED,coap_dtls_spsk_t * setup_data)1426 setup_psk_credentials(gnutls_psk_server_credentials_t *psk_credentials,
1427                       coap_gnutls_context_t *g_context COAP_UNUSED,
1428                       coap_dtls_spsk_t *setup_data) {
1429   int ret;
1430   char hint[COAP_DTLS_HINT_LENGTH];
1431 
1432   G_CHECK(gnutls_psk_allocate_server_credentials(psk_credentials),
1433           "gnutls_psk_allocate_server_credentials");
1434   gnutls_psk_set_server_credentials_function(*psk_credentials,
1435                                              psk_server_callback);
1436   if (setup_data->psk_info.hint.s) {
1437     snprintf(hint, sizeof(hint), "%.*s", (int)setup_data->psk_info.hint.length,
1438              setup_data->psk_info.hint.s);
1439     G_CHECK(gnutls_psk_set_server_credentials_hint(*psk_credentials, hint),
1440             "gnutls_psk_set_server_credentials_hint");
1441   }
1442 
1443   return GNUTLS_E_SUCCESS;
1444 
1445 fail:
1446   return ret;
1447 }
1448 
1449 /*
1450  * return 0   Success (GNUTLS_E_SUCCESS)
1451  *        neg GNUTLS_E_* error code
1452  */
1453 static int
post_client_hello_gnutls_psk(gnutls_session_t g_session)1454 post_client_hello_gnutls_psk(gnutls_session_t g_session) {
1455   coap_session_t *c_session =
1456       (coap_session_t *)gnutls_transport_get_ptr(g_session);
1457   coap_gnutls_context_t *g_context =
1458       (coap_gnutls_context_t *)c_session->context->dtls_context;
1459   coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
1460   int ret = GNUTLS_E_SUCCESS;
1461   char *name = NULL;
1462 
1463   if (c_session->context->spsk_setup_data.validate_sni_call_back) {
1464     coap_dtls_spsk_t sni_setup_data;
1465     /* DNS names (only type supported) may be at most 256 byte long */
1466     size_t len = 256;
1467     unsigned int type;
1468     unsigned int i;
1469 
1470     name = gnutls_malloc(len);
1471     if (name == NULL)
1472       return GNUTLS_E_MEMORY_ERROR;
1473 
1474     for (i=0; ;) {
1475       ret = gnutls_server_name_get(g_session, name, &len, &type, i);
1476       if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
1477         char *new_name;
1478         new_name = gnutls_realloc(name, len);
1479         if (new_name == NULL) {
1480           ret = GNUTLS_E_MEMORY_ERROR;
1481           goto end;
1482         }
1483         name = new_name;
1484         continue; /* retry call with same index */
1485       }
1486 
1487       /* check if it is the last entry in list */
1488       if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
1489         break;
1490       i++;
1491       if (ret != GNUTLS_E_SUCCESS)
1492         goto end;
1493       /* unknown types need to be ignored */
1494       if (type != GNUTLS_NAME_DNS)
1495         continue;
1496 
1497     }
1498     /* If no extension provided, make it a dummy entry */
1499     if (i == 0) {
1500       name[0] = '\000';
1501       len = 0;
1502     }
1503 
1504     /* Is this a cached entry? */
1505     for (i = 0; i < g_context->psk_sni_count; i++) {
1506       if (strcasecmp(name, g_context->psk_sni_entry_list[i].sni) == 0) {
1507         break;
1508       }
1509     }
1510     if (i == g_context->psk_sni_count) {
1511       /*
1512        * New SNI request
1513        */
1514       const coap_dtls_spsk_info_t *new_entry =
1515           c_session->context->spsk_setup_data.validate_sni_call_back(name,
1516               c_session,
1517               c_session->context->spsk_setup_data.sni_call_back_arg);
1518       if (!new_entry) {
1519         G_ACTION(gnutls_alert_send(g_session, GNUTLS_AL_FATAL,
1520                                    GNUTLS_A_UNRECOGNIZED_NAME));
1521         g_env->sent_alert = 1;
1522         ret = GNUTLS_E_NO_CERTIFICATE_FOUND;
1523         goto end;
1524       }
1525 
1526       g_context->psk_sni_entry_list =
1527           gnutls_realloc(g_context->psk_sni_entry_list,
1528                          (i+1)*sizeof(psk_sni_entry));
1529       g_context->psk_sni_entry_list[i].sni = gnutls_strdup(name);
1530       g_context->psk_sni_entry_list[i].psk_info = *new_entry;
1531       sni_setup_data = c_session->context->spsk_setup_data;
1532       sni_setup_data.psk_info = *new_entry;
1533       if ((ret = setup_psk_credentials(
1534                      &g_context->psk_sni_entry_list[i].psk_credentials,
1535                      g_context,
1536                      &sni_setup_data)) < 0) {
1537         int keep_ret = ret;
1538         G_ACTION(gnutls_alert_send(g_session, GNUTLS_AL_FATAL,
1539                                    GNUTLS_A_BAD_CERTIFICATE));
1540         g_env->sent_alert = 1;
1541         ret = keep_ret;
1542         goto end;
1543       }
1544       g_context->psk_sni_count++;
1545     }
1546     G_CHECK(gnutls_credentials_set(g_env->g_session, GNUTLS_CRD_PSK,
1547                                    g_context->psk_sni_entry_list[i].psk_credentials),
1548             "gnutls_credentials_set");
1549     coap_session_refresh_psk_hint(c_session,
1550                                   &g_context->psk_sni_entry_list[i].psk_info.hint);
1551     coap_session_refresh_psk_key(c_session,
1552                                  &g_context->psk_sni_entry_list[i].psk_info.key);
1553   }
1554 
1555 end:
1556   free(name);
1557   return ret;
1558 
1559 fail:
1560   return ret;
1561 }
1562 
1563 /*
1564  * return 0   Success (GNUTLS_E_SUCCESS)
1565  *        neg GNUTLS_E_* error code
1566  */
1567 static int
post_client_hello_gnutls_pki(gnutls_session_t g_session)1568 post_client_hello_gnutls_pki(gnutls_session_t g_session) {
1569   coap_session_t *c_session =
1570       (coap_session_t *)gnutls_transport_get_ptr(g_session);
1571   coap_gnutls_context_t *g_context =
1572       (coap_gnutls_context_t *)c_session->context->dtls_context;
1573   coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
1574   int ret = GNUTLS_E_SUCCESS;
1575   char *name = NULL;
1576 
1577   if (g_context->setup_data.validate_sni_call_back) {
1578     /* DNS names (only type supported) may be at most 256 byte long */
1579     size_t len = 256;
1580     unsigned int type;
1581     unsigned int i;
1582     coap_dtls_pki_t sni_setup_data;
1583 
1584     name = gnutls_malloc(len);
1585     if (name == NULL)
1586       return GNUTLS_E_MEMORY_ERROR;
1587 
1588     for (i=0; ;) {
1589       ret = gnutls_server_name_get(g_session, name, &len, &type, i);
1590       if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
1591         char *new_name;
1592         new_name = gnutls_realloc(name, len);
1593         if (new_name == NULL) {
1594           ret = GNUTLS_E_MEMORY_ERROR;
1595           goto end;
1596         }
1597         name = new_name;
1598         continue; /* retry call with same index */
1599       }
1600 
1601       /* check if it is the last entry in list */
1602       if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
1603         break;
1604       i++;
1605       if (ret != GNUTLS_E_SUCCESS)
1606         goto end;
1607       /* unknown types need to be ignored */
1608       if (type != GNUTLS_NAME_DNS)
1609         continue;
1610 
1611     }
1612     /* If no extension provided, make it a dummy entry */
1613     if (i == 0) {
1614       name[0] = '\000';
1615       len = 0;
1616     }
1617 
1618     /* Is this a cached entry? */
1619     for (i = 0; i < g_context->pki_sni_count; i++) {
1620       if (strcasecmp(name, g_context->pki_sni_entry_list[i].sni) == 0) {
1621         break;
1622       }
1623     }
1624     if (i == g_context->pki_sni_count) {
1625       /*
1626        * New SNI request
1627        */
1628       coap_dtls_key_t *new_entry =
1629           g_context->setup_data.validate_sni_call_back(name,
1630                                                        g_context->setup_data.sni_call_back_arg);
1631       if (!new_entry) {
1632         G_ACTION(gnutls_alert_send(g_session, GNUTLS_AL_FATAL,
1633                                    GNUTLS_A_UNRECOGNIZED_NAME));
1634         g_env->sent_alert = 1;
1635         ret = GNUTLS_E_NO_CERTIFICATE_FOUND;
1636         goto end;
1637       }
1638 
1639       g_context->pki_sni_entry_list = gnutls_realloc(
1640                                           g_context->pki_sni_entry_list,
1641                                           (i+1)*sizeof(pki_sni_entry));
1642       g_context->pki_sni_entry_list[i].sni = gnutls_strdup(name);
1643       g_context->pki_sni_entry_list[i].pki_key = *new_entry;
1644       sni_setup_data = g_context->setup_data;
1645       sni_setup_data.pki_key = *new_entry;
1646       if ((ret = setup_pki_credentials(
1647                      &g_context->pki_sni_entry_list[i].pki_credentials,
1648                      g_session,
1649                      g_context,
1650                      &sni_setup_data, COAP_DTLS_ROLE_SERVER)) < 0) {
1651         int keep_ret = ret;
1652         G_ACTION(gnutls_alert_send(g_session, GNUTLS_AL_FATAL,
1653                                    GNUTLS_A_BAD_CERTIFICATE));
1654         g_env->sent_alert = 1;
1655         ret = keep_ret;
1656         goto end;
1657       }
1658       g_context->pki_sni_count++;
1659     }
1660     G_CHECK(gnutls_credentials_set(g_env->g_session, GNUTLS_CRD_CERTIFICATE,
1661                                    g_context->pki_sni_entry_list[i].pki_credentials),
1662             "gnutls_credentials_set");
1663   }
1664 
1665 end:
1666   free(name);
1667   return ret;
1668 
1669 fail:
1670   return ret;
1671 }
1672 #endif /* COAP_SERVER_SUPPORT */
1673 
1674 #if COAP_CLIENT_SUPPORT
1675 /*
1676  * return 0   Success (GNUTLS_E_SUCCESS)
1677  *        neg GNUTLS_E_* error code
1678  */
1679 static int
setup_client_ssl_session(coap_session_t * c_session,coap_gnutls_env_t * g_env)1680 setup_client_ssl_session(coap_session_t *c_session, coap_gnutls_env_t *g_env) {
1681   coap_gnutls_context_t *g_context =
1682       (coap_gnutls_context_t *)c_session->context->dtls_context;
1683   int ret;
1684 
1685   g_context->psk_pki_enabled |= IS_CLIENT;
1686   if (g_context->psk_pki_enabled & IS_PSK) {
1687     coap_dtls_cpsk_t *setup_data = &c_session->cpsk_setup_data;
1688     G_CHECK(gnutls_psk_allocate_client_credentials(&g_env->psk_cl_credentials),
1689             "gnutls_psk_allocate_client_credentials");
1690     gnutls_psk_set_client_credentials_function(g_env->psk_cl_credentials,
1691                                                psk_client_callback);
1692     G_CHECK(gnutls_credentials_set(g_env->g_session, GNUTLS_CRD_PSK,
1693                                    g_env->psk_cl_credentials),
1694             "gnutls_credentials_set");
1695     /* Issue SNI if requested */
1696     if (setup_data->client_sni) {
1697       G_CHECK(gnutls_server_name_set(g_env->g_session, GNUTLS_NAME_DNS,
1698                                      setup_data->client_sni,
1699                                      strlen(setup_data->client_sni)),
1700               "gnutls_server_name_set");
1701     }
1702     if (setup_data->validate_ih_call_back) {
1703       const char *err;
1704       coap_tls_version_t *tls_version = coap_get_tls_library_version();
1705 
1706       if (tls_version->version >= 0x030604) {
1707         /* Disable TLS1.3 if Identity Hint Callback set */
1708         const char *priority;
1709 
1710         if (tls_version->version >= 0x030606) {
1711           priority = VARIANTS_NO_TLS13_3_6_6;
1712         } else {
1713           priority = VARIANTS_NO_TLS13_3_6_4;
1714         }
1715         ret = gnutls_priority_set_direct(g_env->g_session,
1716                                          priority, &err);
1717         if (ret < 0) {
1718           if (ret == GNUTLS_E_INVALID_REQUEST)
1719             coap_log_warn("gnutls_priority_set_direct: Syntax error at: %s\n", err);
1720           else
1721             coap_log_warn("gnutls_priority_set_direct: %s\n", gnutls_strerror(ret));
1722           goto fail;
1723         }
1724       }
1725     }
1726   }
1727 
1728   if ((g_context->psk_pki_enabled & IS_PKI) ||
1729       (g_context->psk_pki_enabled & (IS_PSK | IS_PKI)) == 0) {
1730     /*
1731      * If neither PSK or PKI have been set up, use PKI basics.
1732      * This works providing COAP_PKI_KEY_PEM has a value of 0.
1733      */
1734     coap_dtls_pki_t *setup_data = &g_context->setup_data;
1735     G_CHECK(setup_pki_credentials(&g_env->pki_credentials, g_env->g_session,
1736                                   g_context, setup_data,
1737                                   COAP_DTLS_ROLE_CLIENT),
1738             "setup_pki_credentials");
1739 
1740     G_CHECK(gnutls_credentials_set(g_env->g_session, GNUTLS_CRD_CERTIFICATE,
1741                                    g_env->pki_credentials),
1742             "gnutls_credentials_set");
1743 
1744     if (c_session->proto == COAP_PROTO_TLS)
1745       G_CHECK(gnutls_alpn_set_protocols(g_env->g_session,
1746                                         &g_context->alpn_proto, 1, 0),
1747               "gnutls_alpn_set_protocols");
1748 
1749     /* Issue SNI if requested (only happens if PKI defined) */
1750     if (setup_data->client_sni) {
1751       G_CHECK(gnutls_server_name_set(g_env->g_session, GNUTLS_NAME_DNS,
1752                                      setup_data->client_sni,
1753                                      strlen(setup_data->client_sni)),
1754               "gnutls_server_name_set");
1755     }
1756   }
1757   return GNUTLS_E_SUCCESS;
1758 
1759 fail:
1760   return ret;
1761 }
1762 #endif /* COAP_CLIENT_SUPPORT */
1763 
1764 #if COAP_SERVER_SUPPORT
1765 /*
1766  * gnutls_psk_server_credentials_function return values
1767  * (see gnutls_psk_set_server_credentials_function())
1768  *
1769  * return -1 failed
1770  *         0 passed
1771  */
1772 static int
psk_server_callback(gnutls_session_t g_session,const char * identity,gnutls_datum_t * key)1773 psk_server_callback(gnutls_session_t g_session,
1774                     const char *identity,
1775                     gnutls_datum_t *key) {
1776   coap_session_t *c_session =
1777       (coap_session_t *)gnutls_transport_get_ptr(g_session);
1778   coap_gnutls_context_t *g_context;
1779   coap_dtls_spsk_t *setup_data;
1780   coap_bin_const_t lidentity;
1781   const coap_bin_const_t *psk_key;
1782 
1783   if (c_session == NULL)
1784     return -1;
1785 
1786   g_context = (coap_gnutls_context_t *)c_session->context->dtls_context;
1787   if (g_context == NULL)
1788     return -1;
1789   setup_data = &c_session->context->spsk_setup_data;
1790 
1791 
1792   /* Track the Identity being used */
1793   lidentity.s = identity ? (const uint8_t *)identity : (const uint8_t *)"";
1794   lidentity.length = strlen((const char *)lidentity.s);
1795   coap_session_refresh_psk_identity(c_session, &lidentity);
1796 
1797   coap_log_debug("got psk_identity: '%.*s'\n",
1798                  (int)lidentity.length, (const char *)lidentity.s);
1799 
1800   if (setup_data->validate_id_call_back) {
1801     psk_key = setup_data->validate_id_call_back(&lidentity,
1802                                                 c_session,
1803                                                 setup_data->id_call_back_arg);
1804 
1805     coap_session_refresh_psk_key(c_session, psk_key);
1806   } else {
1807     psk_key = coap_get_session_server_psk_key(c_session);
1808   }
1809 
1810   if (psk_key == NULL)
1811     return -1;
1812 
1813   key->data = gnutls_malloc(psk_key->length);
1814   if (key->data == NULL)
1815     return -1;
1816   memcpy(key->data, psk_key->s, psk_key->length);
1817   key->size = psk_key->length;
1818   return 0;
1819 }
1820 
1821 /*
1822  * return 0   Success (GNUTLS_E_SUCCESS)
1823  *        neg GNUTLS_E_* error code
1824  */
1825 static int
setup_server_ssl_session(coap_session_t * c_session,coap_gnutls_env_t * g_env)1826 setup_server_ssl_session(coap_session_t *c_session, coap_gnutls_env_t *g_env) {
1827   coap_gnutls_context_t *g_context =
1828       (coap_gnutls_context_t *)c_session->context->dtls_context;
1829   int ret = GNUTLS_E_SUCCESS;
1830 
1831   g_context->psk_pki_enabled |= IS_SERVER;
1832   if (g_context->psk_pki_enabled & IS_PSK) {
1833     G_CHECK(setup_psk_credentials(
1834                 &g_env->psk_sv_credentials,
1835                 g_context,
1836                 &c_session->context->spsk_setup_data),
1837             "setup_psk_credentials\n");
1838     G_CHECK(gnutls_credentials_set(g_env->g_session,
1839                                    GNUTLS_CRD_PSK,
1840                                    g_env->psk_sv_credentials),
1841             "gnutls_credentials_set\n");
1842     gnutls_handshake_set_post_client_hello_function(g_env->g_session,
1843                                                     post_client_hello_gnutls_psk);
1844   }
1845 
1846   if (g_context->psk_pki_enabled & IS_PKI) {
1847     coap_dtls_pki_t *setup_data = &g_context->setup_data;
1848     G_CHECK(setup_pki_credentials(&g_env->pki_credentials, g_env->g_session,
1849                                   g_context, setup_data,
1850                                   COAP_DTLS_ROLE_SERVER),
1851             "setup_pki_credentials");
1852 
1853     if (setup_data->verify_peer_cert) {
1854       gnutls_certificate_server_set_request(g_env->g_session,
1855                                             GNUTLS_CERT_REQUIRE);
1856     } else if (setup_data->is_rpk_not_cert) {
1857       gnutls_certificate_server_set_request(g_env->g_session,
1858                                             GNUTLS_CERT_REQUEST);
1859     } else {
1860       gnutls_certificate_server_set_request(g_env->g_session,
1861                                             GNUTLS_CERT_IGNORE);
1862     }
1863 
1864     gnutls_handshake_set_post_client_hello_function(g_env->g_session,
1865                                                     post_client_hello_gnutls_pki);
1866 
1867     G_CHECK(gnutls_credentials_set(g_env->g_session, GNUTLS_CRD_CERTIFICATE,
1868                                    g_env->pki_credentials),
1869             "gnutls_credentials_set\n");
1870   }
1871   return GNUTLS_E_SUCCESS;
1872 
1873 fail:
1874   return ret;
1875 }
1876 #endif /* COAP_SERVER_SUPPORT */
1877 
1878 /*
1879  * return +ve data amount
1880  *        0   no more
1881  *        -1  error (error in errno)
1882  */
1883 static ssize_t
coap_dgram_read(gnutls_transport_ptr_t context,void * out,size_t outl)1884 coap_dgram_read(gnutls_transport_ptr_t context, void *out, size_t outl) {
1885   ssize_t ret = 0;
1886   coap_session_t *c_session = (coap_session_t *)context;
1887   coap_ssl_t *data;
1888 
1889   if (!c_session->tls) {
1890     errno = EAGAIN;
1891     return -1;
1892   }
1893   data = &((coap_gnutls_env_t *)c_session->tls)->coap_ssl_data;
1894 
1895   if (out != NULL) {
1896     if (data != NULL && data->pdu_len > 0) {
1897       if (outl < data->pdu_len) {
1898         memcpy(out, data->pdu, outl);
1899         ret = outl;
1900         if (!data->peekmode) {
1901           data->pdu += outl;
1902           data->pdu_len -= outl;
1903         }
1904       } else {
1905         memcpy(out, data->pdu, data->pdu_len);
1906         ret = data->pdu_len;
1907         if (!data->peekmode) {
1908           data->pdu_len = 0;
1909           data->pdu = NULL;
1910         }
1911       }
1912     } else {
1913       errno = EAGAIN;
1914       ret = -1;
1915     }
1916   }
1917   return ret;
1918 }
1919 
1920 /*
1921  * return +ve data amount
1922  *        0   no more
1923  *        -1  error (error in errno)
1924  */
1925 /* callback function given to gnutls for sending data over socket */
1926 static ssize_t
coap_dgram_write(gnutls_transport_ptr_t context,const void * send_buffer,size_t send_buffer_length)1927 coap_dgram_write(gnutls_transport_ptr_t context, const void *send_buffer,
1928                  size_t send_buffer_length) {
1929   ssize_t result = -1;
1930   coap_session_t *c_session = (coap_session_t *)context;
1931 
1932   if (c_session) {
1933     if (!coap_netif_available(c_session)
1934 #if COAP_SERVER_SUPPORT
1935         && c_session->endpoint == NULL
1936 #endif /* COAP_SERVER_SUPPORT */
1937        ) {
1938       /* socket was closed on client due to error */
1939       errno = ECONNRESET;
1940       return -1;
1941     }
1942     result = c_session->sock.lfunc[COAP_LAYER_TLS].l_write(c_session,
1943                                                            send_buffer, send_buffer_length);
1944     if (result != (int)send_buffer_length) {
1945       coap_log_warn("coap_netif_dgrm_write failed (%zd != %zu)\n",
1946                     result, send_buffer_length);
1947       result = 0;
1948     }
1949   } else {
1950     result = 0;
1951   }
1952   return result;
1953 }
1954 
1955 /*
1956  * return 1  fd has activity
1957  *        0  timeout
1958  *        -1 error (error in errno)
1959  */
1960 static int
receive_timeout(gnutls_transport_ptr_t context,unsigned int ms COAP_UNUSED)1961 receive_timeout(gnutls_transport_ptr_t context, unsigned int ms COAP_UNUSED) {
1962   coap_session_t *c_session = (coap_session_t *)context;
1963 
1964   if (c_session) {
1965     fd_set readfds, writefds, exceptfds;
1966     struct timeval tv;
1967     int nfds = c_session->sock.fd +1;
1968     coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
1969 
1970     /* If data has been read in by libcoap ahead of GnuTLS, say it is there */
1971     if (c_session->proto == COAP_PROTO_DTLS && g_env &&
1972         g_env->coap_ssl_data.pdu_len > 0) {
1973       return 1;
1974     }
1975 
1976     FD_ZERO(&readfds);
1977     FD_ZERO(&writefds);
1978     FD_ZERO(&exceptfds);
1979     FD_SET(c_session->sock.fd, &readfds);
1980     if (!(g_env && g_env->doing_dtls_timeout)) {
1981       FD_SET(c_session->sock.fd, &writefds);
1982       FD_SET(c_session->sock.fd, &exceptfds);
1983     }
1984     /* Polling */
1985     tv.tv_sec = 0;
1986     tv.tv_usec = 0;
1987 
1988     return select(nfds, &readfds, &writefds, &exceptfds, &tv);
1989   }
1990   return 1;
1991 }
1992 
1993 static coap_gnutls_env_t *
coap_dtls_new_gnutls_env(coap_session_t * c_session,int type)1994 coap_dtls_new_gnutls_env(coap_session_t *c_session, int type) {
1995   coap_gnutls_context_t *g_context =
1996       ((coap_gnutls_context_t *)c_session->context->dtls_context);
1997   coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
1998 #if (GNUTLS_VERSION_NUMBER >= 0x030606)
1999   int flags = type | GNUTLS_DATAGRAM | GNUTLS_NONBLOCK | GNUTLS_ENABLE_RAWPK;
2000 #else /* < 3.6.6 */
2001   int flags = type | GNUTLS_DATAGRAM | GNUTLS_NONBLOCK;
2002 #endif /* < 3.6.6 */
2003   int ret;
2004 
2005   if (g_env)
2006     return g_env;
2007 
2008   g_env = gnutls_malloc(sizeof(coap_gnutls_env_t));
2009   if (!g_env)
2010     return NULL;
2011 
2012   memset(g_env, 0, sizeof(coap_gnutls_env_t));
2013 
2014   G_CHECK(gnutls_init(&g_env->g_session, flags), "gnutls_init");
2015 
2016   gnutls_transport_set_pull_function(g_env->g_session, coap_dgram_read);
2017   gnutls_transport_set_push_function(g_env->g_session, coap_dgram_write);
2018   gnutls_transport_set_pull_timeout_function(g_env->g_session, receive_timeout);
2019   /* So we can track the coap_session_t in callbacks */
2020   gnutls_transport_set_ptr(g_env->g_session, c_session);
2021 
2022   G_CHECK(gnutls_priority_set(g_env->g_session, g_context->priority_cache),
2023           "gnutls_priority_set");
2024 
2025   if (type == GNUTLS_SERVER) {
2026 #if COAP_SERVER_SUPPORT
2027     G_CHECK(setup_server_ssl_session(c_session, g_env),
2028             "setup_server_ssl_session");
2029 #else /* ! COAP_SERVER_SUPPORT */
2030     goto fail;
2031 #endif /* ! COAP_SERVER_SUPPORT */
2032   } else {
2033 #if COAP_CLIENT_SUPPORT
2034     G_CHECK(setup_client_ssl_session(c_session, g_env),
2035             "setup_client_ssl_session");
2036 #else /* COAP_CLIENT_SUPPORT */
2037     goto fail;
2038 #endif /* COAP_CLIENT_SUPPORT */
2039   }
2040 
2041   gnutls_handshake_set_timeout(g_env->g_session,
2042                                GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
2043   gnutls_dtls_set_timeouts(g_env->g_session, COAP_DTLS_RETRANSMIT_MS,
2044                            COAP_DTLS_RETRANSMIT_TOTAL_MS);
2045 
2046   return g_env;
2047 
2048 fail:
2049   if (g_env)
2050     gnutls_free(g_env);
2051   return NULL;
2052 }
2053 
2054 static void
coap_dtls_free_gnutls_env(coap_gnutls_context_t * g_context,coap_gnutls_env_t * g_env,coap_free_bye_t free_bye)2055 coap_dtls_free_gnutls_env(coap_gnutls_context_t *g_context,
2056                           coap_gnutls_env_t *g_env,
2057                           coap_free_bye_t free_bye) {
2058   if (g_env) {
2059     /* It is suggested not to use GNUTLS_SHUT_RDWR in DTLS
2060      * connections because the peer's closure message might
2061      * be lost */
2062     if (free_bye != COAP_FREE_BYE_NONE && !g_env->sent_alert) {
2063       /* Only do this if appropriate */
2064       gnutls_bye(g_env->g_session, free_bye == COAP_FREE_BYE_AS_UDP ?
2065                  GNUTLS_SHUT_WR : GNUTLS_SHUT_RDWR);
2066     }
2067     gnutls_deinit(g_env->g_session);
2068     g_env->g_session = NULL;
2069     if (g_context->psk_pki_enabled & IS_PSK) {
2070       if ((g_context->psk_pki_enabled & IS_CLIENT) &&
2071           g_env->psk_cl_credentials != NULL) {
2072         gnutls_psk_free_client_credentials(g_env->psk_cl_credentials);
2073         g_env->psk_cl_credentials = NULL;
2074       } else {
2075         /* YUK - A memory leak in 3.3.0 (fixed by 3.3.26) of hint */
2076         if (g_env->psk_sv_credentials != NULL)
2077           gnutls_psk_free_server_credentials(g_env->psk_sv_credentials);
2078         g_env->psk_sv_credentials = NULL;
2079       }
2080     }
2081     if ((g_context->psk_pki_enabled & IS_PKI) ||
2082         (g_context->psk_pki_enabled &
2083          (IS_PSK | IS_PKI | IS_CLIENT)) == IS_CLIENT) {
2084       gnutls_certificate_free_credentials(g_env->pki_credentials);
2085       g_env->pki_credentials = NULL;
2086     }
2087     gnutls_free(g_env->coap_ssl_data.cookie_key.data);
2088     gnutls_free(g_env);
2089   }
2090 }
2091 
2092 #if COAP_SERVER_SUPPORT
2093 void *
coap_dtls_new_server_session(coap_session_t * c_session)2094 coap_dtls_new_server_session(coap_session_t *c_session) {
2095   coap_gnutls_env_t *g_env =
2096       (coap_gnutls_env_t *)c_session->tls;
2097 
2098   gnutls_transport_set_ptr(g_env->g_session, c_session);
2099 
2100   return g_env;
2101 }
2102 #endif /* COAP_SERVER_SUPPORT */
2103 
2104 static void
log_last_alert(coap_session_t * c_session,gnutls_session_t g_session)2105 log_last_alert(coap_session_t *c_session,
2106                gnutls_session_t g_session) {
2107 #if COAP_MAX_LOGGING_LEVEL > 0
2108   int last_alert = gnutls_alert_get(g_session);
2109 
2110   if (last_alert == GNUTLS_A_CLOSE_NOTIFY)
2111     coap_log_debug("***%s: Alert '%d': %s\n",
2112                    coap_session_str(c_session),
2113                    last_alert, gnutls_alert_get_name(last_alert));
2114   else
2115     coap_log_warn("***%s: Alert '%d': %s\n",
2116                   coap_session_str(c_session),
2117                   last_alert, gnutls_alert_get_name(last_alert));
2118 #else /* COAP_MAX_LOGGING_LEVEL ==  0 */
2119   (void)c_session;
2120   (void)g_session;
2121 #endif /* COAP_MAX_LOGGING_LEVEL ==  0 */
2122 }
2123 
2124 /*
2125  * return -1  failure
2126  *         0  not completed
2127  *         1  established
2128  */
2129 static int
do_gnutls_handshake(coap_session_t * c_session,coap_gnutls_env_t * g_env)2130 do_gnutls_handshake(coap_session_t *c_session, coap_gnutls_env_t *g_env) {
2131   int ret;
2132 
2133   ret = gnutls_handshake(g_env->g_session);
2134   switch (ret) {
2135   case GNUTLS_E_SUCCESS:
2136     g_env->established = 1;
2137     coap_log_debug("*  %s: GnuTLS established\n",
2138                    coap_session_str(c_session));
2139     ret = 1;
2140     break;
2141   case GNUTLS_E_INTERRUPTED:
2142     errno = EINTR;
2143     ret = 0;
2144     break;
2145   case GNUTLS_E_AGAIN:
2146     errno = EAGAIN;
2147     ret = 0;
2148     break;
2149   case GNUTLS_E_INSUFFICIENT_CREDENTIALS:
2150     coap_log_warn("Insufficient credentials provided.\n");
2151     ret = -1;
2152     break;
2153   case GNUTLS_E_FATAL_ALERT_RECEIVED:
2154     /* Stop the sending of an alert on closedown */
2155     g_env->sent_alert = 1;
2156     log_last_alert(c_session, g_env->g_session);
2157   /* Fall through */
2158   case GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET:
2159   case GNUTLS_E_UNEXPECTED_PACKET:
2160     c_session->dtls_event = COAP_EVENT_DTLS_CLOSED;
2161     ret = -1;
2162     break;
2163   case GNUTLS_E_WARNING_ALERT_RECEIVED:
2164     log_last_alert(c_session, g_env->g_session);
2165     c_session->dtls_event = COAP_EVENT_DTLS_ERROR;
2166     ret = 0;
2167     break;
2168   case GNUTLS_E_NO_CERTIFICATE_FOUND:
2169 #if (GNUTLS_VERSION_NUMBER > 0x030606)
2170   case GNUTLS_E_CERTIFICATE_REQUIRED:
2171 #endif /* GNUTLS_VERSION_NUMBER > 0x030606 */
2172     coap_log_warn("Client Certificate requested and required, but not provided\n"
2173                  );
2174     G_ACTION(gnutls_alert_send(g_env->g_session, GNUTLS_AL_FATAL,
2175                                GNUTLS_A_BAD_CERTIFICATE));
2176     g_env->sent_alert = 1;
2177     c_session->dtls_event = COAP_EVENT_DTLS_CLOSED;
2178     ret = -1;
2179     break;
2180   case GNUTLS_E_DECRYPTION_FAILED:
2181     coap_log_warn("do_gnutls_handshake: session establish "
2182                   "returned '%s'\n",
2183                   gnutls_strerror(ret));
2184     G_ACTION(gnutls_alert_send(g_env->g_session, GNUTLS_AL_FATAL,
2185                                GNUTLS_A_DECRYPT_ERROR));
2186     g_env->sent_alert = 1;
2187     c_session->dtls_event = COAP_EVENT_DTLS_CLOSED;
2188     ret = -1;
2189     break;
2190   case GNUTLS_E_CERTIFICATE_ERROR:
2191     if (g_env->sent_alert) {
2192       c_session->dtls_event = COAP_EVENT_DTLS_CLOSED;
2193       ret = -1;
2194       break;
2195     }
2196   /* Fall through */
2197   case GNUTLS_E_UNKNOWN_CIPHER_SUITE:
2198   case GNUTLS_E_NO_CIPHER_SUITES:
2199   case GNUTLS_E_INVALID_SESSION:
2200     coap_log_warn("do_gnutls_handshake: session establish "
2201                   "returned '%s'\n",
2202                   gnutls_strerror(ret));
2203     if (!g_env->sent_alert) {
2204       G_ACTION(gnutls_alert_send(g_env->g_session, GNUTLS_AL_FATAL,
2205                                  GNUTLS_A_HANDSHAKE_FAILURE));
2206       g_env->sent_alert = 1;
2207     }
2208     c_session->dtls_event = COAP_EVENT_DTLS_CLOSED;
2209     ret = -1;
2210     break;
2211   case GNUTLS_E_SESSION_EOF:
2212   case GNUTLS_E_PREMATURE_TERMINATION:
2213   case GNUTLS_E_TIMEDOUT:
2214   case GNUTLS_E_PULL_ERROR:
2215   case GNUTLS_E_PUSH_ERROR:
2216     c_session->dtls_event = COAP_EVENT_DTLS_CLOSED;
2217     ret = -1;
2218     break;
2219   default:
2220     coap_log_warn("do_gnutls_handshake: session establish "
2221                   "returned %d: '%s'\n",
2222                   ret, gnutls_strerror(ret));
2223     ret = -1;
2224     break;
2225   }
2226   return ret;
2227 }
2228 
2229 #if COAP_CLIENT_SUPPORT
2230 void *
coap_dtls_new_client_session(coap_session_t * c_session)2231 coap_dtls_new_client_session(coap_session_t *c_session) {
2232   coap_gnutls_env_t *g_env = coap_dtls_new_gnutls_env(c_session, GNUTLS_CLIENT);
2233   int ret;
2234 
2235   if (g_env) {
2236     ret = do_gnutls_handshake(c_session, g_env);
2237     if (ret == -1) {
2238       coap_dtls_free_gnutls_env(c_session->context->dtls_context,
2239                                 g_env,
2240                                 COAP_PROTO_NOT_RELIABLE(c_session->proto) ?
2241                                 COAP_FREE_BYE_AS_UDP : COAP_FREE_BYE_AS_TCP);
2242       return NULL;
2243     }
2244   }
2245   return g_env;
2246 }
2247 #endif /* COAP_CLIENT_SUPPORT */
2248 
2249 void
coap_dtls_free_session(coap_session_t * c_session)2250 coap_dtls_free_session(coap_session_t *c_session) {
2251   if (c_session && c_session->context && c_session->tls) {
2252     coap_dtls_free_gnutls_env(c_session->context->dtls_context,
2253                               c_session->tls,
2254                               COAP_PROTO_NOT_RELIABLE(c_session->proto) ?
2255                               COAP_FREE_BYE_AS_UDP : COAP_FREE_BYE_AS_TCP);
2256     c_session->tls = NULL;
2257     coap_handle_event(c_session->context, COAP_EVENT_DTLS_CLOSED, c_session);
2258   }
2259 }
2260 
2261 void
coap_dtls_session_update_mtu(coap_session_t * c_session)2262 coap_dtls_session_update_mtu(coap_session_t *c_session) {
2263   coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
2264   int ret;
2265 
2266   if (g_env)
2267     G_CHECK(gnutls_dtls_set_data_mtu(g_env->g_session,
2268                                      (unsigned int)c_session->mtu),
2269             "gnutls_dtls_set_data_mtu");
2270 fail:
2271   ;;
2272 }
2273 
2274 /*
2275  * return +ve data amount
2276  *        0   no more
2277  *        -1  error
2278  */
2279 ssize_t
coap_dtls_send(coap_session_t * c_session,const uint8_t * data,size_t data_len)2280 coap_dtls_send(coap_session_t *c_session,
2281                const uint8_t *data, size_t data_len) {
2282   int ret;
2283   coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
2284 
2285   assert(g_env != NULL);
2286 
2287   c_session->dtls_event = -1;
2288   if (g_env->established) {
2289     ret = gnutls_record_send(g_env->g_session, data, data_len);
2290 
2291     if (ret <= 0) {
2292       switch (ret) {
2293       case GNUTLS_E_AGAIN:
2294         ret = 0;
2295         break;
2296       case GNUTLS_E_FATAL_ALERT_RECEIVED:
2297         /* Stop the sending of an alert on closedown */
2298         g_env->sent_alert = 1;
2299         log_last_alert(c_session, g_env->g_session);
2300         c_session->dtls_event = COAP_EVENT_DTLS_CLOSED;
2301         ret = -1;
2302         break;
2303       default:
2304         coap_log_debug("coap_dtls_send: gnutls_record_send "
2305                        "returned %d: '%s'\n",
2306                        ret, gnutls_strerror(ret));
2307         ret = -1;
2308         break;
2309       }
2310       if (ret == -1) {
2311         coap_log_warn("coap_dtls_send: cannot send PDU\n");
2312       }
2313     }
2314   } else {
2315     ret = do_gnutls_handshake(c_session, g_env);
2316     if (ret == 1) {
2317       /* Just connected, so send the data */
2318       return coap_dtls_send(c_session, data, data_len);
2319     }
2320     ret = -1;
2321   }
2322 
2323   if (c_session->dtls_event >= 0) {
2324     coap_handle_event(c_session->context, c_session->dtls_event, c_session);
2325     if (c_session->dtls_event == COAP_EVENT_DTLS_ERROR ||
2326         c_session->dtls_event == COAP_EVENT_DTLS_CLOSED) {
2327       coap_session_disconnected(c_session, COAP_NACK_TLS_FAILED);
2328       ret = -1;
2329     }
2330   }
2331 
2332   if (ret > 0) {
2333     if (ret == (ssize_t)data_len)
2334       coap_log_debug("*  %s: dtls:  sent %4d bytes\n",
2335                      coap_session_str(c_session), ret);
2336     else
2337       coap_log_debug("*  %s: dtls:  sent %4d of %4zd bytes\n",
2338                      coap_session_str(c_session), ret, data_len);
2339   }
2340   return ret;
2341 }
2342 
2343 int
coap_dtls_is_context_timeout(void)2344 coap_dtls_is_context_timeout(void) {
2345   return 0;
2346 }
2347 
2348 coap_tick_t
coap_dtls_get_context_timeout(void * dtls_context COAP_UNUSED)2349 coap_dtls_get_context_timeout(void *dtls_context COAP_UNUSED) {
2350   return 0;
2351 }
2352 
2353 coap_tick_t
coap_dtls_get_timeout(coap_session_t * c_session,coap_tick_t now)2354 coap_dtls_get_timeout(coap_session_t *c_session, coap_tick_t now) {
2355   coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
2356 
2357   assert(c_session->state == COAP_SESSION_STATE_HANDSHAKE);
2358   if (g_env && g_env->g_session) {
2359     unsigned int rem_ms = gnutls_dtls_get_timeout(g_env->g_session);
2360 
2361     if (rem_ms == 0) {
2362       /*
2363        * Need to make sure that we do not do this too frequently as some
2364        * versions of gnutls reset retransmit if a spurious packet is received
2365        * (e.g. duplicate Client Hello), but last_transmit does not get updated
2366        * when gnutls_handshake() is called and there is 'nothing' to resend.
2367        */
2368       if (g_env->last_timeout + COAP_DTLS_RETRANSMIT_COAP_TICKS > now)
2369         return g_env->last_timeout + COAP_DTLS_RETRANSMIT_COAP_TICKS;
2370     }
2371     /* Reset for the next time */
2372     g_env->last_timeout = now;
2373     return now + rem_ms;
2374   }
2375 
2376   return 0;
2377 }
2378 
2379 /*
2380  * return 1 timed out
2381  *        0 still timing out
2382  */
2383 int
coap_dtls_handle_timeout(coap_session_t * c_session)2384 coap_dtls_handle_timeout(coap_session_t *c_session) {
2385   coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
2386 
2387   assert(g_env != NULL && c_session->state == COAP_SESSION_STATE_HANDSHAKE);
2388   g_env->doing_dtls_timeout = 1;
2389   if ((++c_session->dtls_timeout_count > c_session->max_retransmit) ||
2390       (do_gnutls_handshake(c_session, g_env) < 0)) {
2391     /* Too many retries */
2392     g_env->doing_dtls_timeout = 0;
2393     coap_session_disconnected(c_session, COAP_NACK_TLS_FAILED);
2394     return 1;
2395   } else {
2396     g_env->doing_dtls_timeout = 0;
2397     return 0;
2398   }
2399 }
2400 
2401 /*
2402  * return +ve data amount
2403  *        0   no more
2404  *        -1  error
2405  */
2406 int
coap_dtls_receive(coap_session_t * c_session,const uint8_t * data,size_t data_len)2407 coap_dtls_receive(coap_session_t *c_session, const uint8_t *data,
2408                   size_t data_len) {
2409   coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
2410   int ret = 0;
2411   coap_ssl_t *ssl_data = &g_env->coap_ssl_data;
2412 
2413   uint8_t pdu[COAP_RXBUFFER_SIZE];
2414 
2415   assert(g_env != NULL);
2416 
2417   if (ssl_data->pdu_len)
2418     coap_log_err("** %s: Previous data not read %u bytes\n",
2419                  coap_session_str(c_session), ssl_data->pdu_len);
2420   ssl_data->pdu = data;
2421   ssl_data->pdu_len = data_len;
2422 
2423   c_session->dtls_event = -1;
2424   if (g_env->established) {
2425     if (c_session->state == COAP_SESSION_STATE_HANDSHAKE) {
2426       coap_handle_event(c_session->context, COAP_EVENT_DTLS_CONNECTED,
2427                         c_session);
2428       gnutls_transport_set_ptr(g_env->g_session, c_session);
2429       c_session->sock.lfunc[COAP_LAYER_TLS].l_establish(c_session);
2430     }
2431     ret = gnutls_record_recv(g_env->g_session, pdu, (int)sizeof(pdu));
2432     if (ret > 0) {
2433       return coap_handle_dgram(c_session->context, c_session, pdu, (size_t)ret);
2434     } else if (ret == 0) {
2435       c_session->dtls_event = COAP_EVENT_DTLS_CLOSED;
2436     } else {
2437       switch (ret) {
2438       case GNUTLS_E_FATAL_ALERT_RECEIVED:
2439         /* Stop the sending of an alert on closedown */
2440         g_env->sent_alert = 1;
2441         log_last_alert(c_session, g_env->g_session);
2442         c_session->dtls_event = COAP_EVENT_DTLS_CLOSED;
2443         ret = -1;
2444         break;
2445       case GNUTLS_E_WARNING_ALERT_RECEIVED:
2446         log_last_alert(c_session, g_env->g_session);
2447         c_session->dtls_event = COAP_EVENT_DTLS_ERROR;
2448         ret = 0;
2449         break;
2450       default:
2451         coap_log_warn("coap_dtls_receive: gnutls_record_recv returned %d\n", ret);
2452         ret = -1;
2453         break;
2454       }
2455     }
2456   } else {
2457     ret = do_gnutls_handshake(c_session, g_env);
2458     if (ret == 1) {
2459       coap_session_connected(c_session);
2460     } else {
2461       ret = -1;
2462       if (ssl_data->pdu_len && !g_env->sent_alert) {
2463         /* Do the handshake again incase of internal timeout */
2464         ret = do_gnutls_handshake(c_session, g_env);
2465         if (ret == 1) {
2466           /* Just connected, so send the data */
2467           coap_session_connected(c_session);
2468         }
2469       }
2470     }
2471   }
2472 
2473   if (c_session->dtls_event >= 0) {
2474     /* COAP_EVENT_DTLS_CLOSED event reported in coap_session_disconnected() */
2475     if (c_session->dtls_event != COAP_EVENT_DTLS_CLOSED)
2476       coap_handle_event(c_session->context, c_session->dtls_event, c_session);
2477     if (c_session->dtls_event == COAP_EVENT_DTLS_ERROR ||
2478         c_session->dtls_event == COAP_EVENT_DTLS_CLOSED) {
2479       coap_session_disconnected(c_session, COAP_NACK_TLS_FAILED);
2480       ssl_data = NULL;
2481       ret = -1;
2482     }
2483   }
2484   if (ssl_data && ssl_data->pdu_len) {
2485     /* pdu data is held on stack which will not stay there */
2486     coap_log_debug("coap_dtls_receive: ret %d: remaining data %u\n", ret, ssl_data->pdu_len);
2487     ssl_data->pdu_len = 0;
2488     ssl_data->pdu = NULL;
2489   }
2490   if (ret > 0) {
2491     coap_log_debug("*  %s: dtls:  recv %4d bytes\n",
2492                    coap_session_str(c_session), ret);
2493   }
2494   return ret;
2495 }
2496 
2497 #if COAP_SERVER_SUPPORT
2498 /*
2499  * return -1  failure
2500  *         0  not completed
2501  *         1  client hello seen
2502  */
2503 int
coap_dtls_hello(coap_session_t * c_session,const uint8_t * data,size_t data_len)2504 coap_dtls_hello(coap_session_t *c_session,
2505                 const uint8_t *data,
2506                 size_t data_len
2507                ) {
2508   coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
2509   coap_ssl_t *ssl_data;
2510   int ret;
2511 
2512   if (!g_env) {
2513     g_env = coap_dtls_new_gnutls_env(c_session, GNUTLS_SERVER);
2514     if (g_env) {
2515       c_session->tls = g_env;
2516       gnutls_key_generate(&g_env->coap_ssl_data.cookie_key,
2517                           GNUTLS_COOKIE_KEY_SIZE);
2518     } else {
2519       /* error should have already been reported */
2520       return -1;
2521     }
2522   }
2523   if (data_len > 0) {
2524     gnutls_dtls_prestate_st prestate;
2525     uint8_t *data_rw;
2526 
2527     memset(&prestate, 0, sizeof(prestate));
2528     /* Need to do this to not get a compiler warning about const parameters */
2529     memcpy(&data_rw, &data, sizeof(data_rw));
2530     ret = gnutls_dtls_cookie_verify(&g_env->coap_ssl_data.cookie_key,
2531                                     &c_session->addr_info,
2532                                     sizeof(c_session->addr_info),
2533                                     data_rw, data_len,
2534                                     &prestate);
2535     if (ret < 0) {  /* cookie not valid */
2536       coap_log_debug("Invalid Cookie - sending Hello Verify\n");
2537       gnutls_dtls_cookie_send(&g_env->coap_ssl_data.cookie_key,
2538                               &c_session->addr_info,
2539                               sizeof(c_session->addr_info),
2540                               &prestate,
2541                               c_session,
2542                               coap_dgram_write);
2543       return 0;
2544     }
2545     gnutls_dtls_prestate_set(g_env->g_session, &prestate);
2546   }
2547 
2548   ssl_data = &g_env->coap_ssl_data;
2549   ssl_data->pdu = data;
2550   ssl_data->pdu_len = data_len;
2551 
2552   ret = do_gnutls_handshake(c_session, g_env);
2553   if (ret < 0) {
2554     /*
2555      * as the above failed, need to remove g_env to clean up any
2556      * pollution of the information
2557      */
2558     coap_dtls_free_gnutls_env(((coap_gnutls_context_t *)c_session->context->dtls_context),
2559                               g_env, COAP_FREE_BYE_NONE);
2560     c_session->tls = NULL;
2561     ssl_data = NULL;
2562     ret = -1;
2563   } else {
2564     /* Client Hello has been seen */
2565     ret = 1;
2566   }
2567 
2568   if (ssl_data && ssl_data->pdu_len) {
2569     /* pdu data is held on stack which will not stay there */
2570     coap_log_debug("coap_dtls_hello: ret %d: remaining data %u\n", ret, ssl_data->pdu_len);
2571     ssl_data->pdu_len = 0;
2572     ssl_data->pdu = NULL;
2573   }
2574   return ret;
2575 }
2576 #endif /* COAP_SERVER_SUPPORT */
2577 
2578 unsigned int
coap_dtls_get_overhead(coap_session_t * c_session COAP_UNUSED)2579 coap_dtls_get_overhead(coap_session_t *c_session COAP_UNUSED) {
2580   return 37;
2581 }
2582 
2583 #if !COAP_DISABLE_TCP
2584 /*
2585  * strm
2586  * return +ve data amount
2587  *        0   connection closed
2588  *        -1  error (error in errno)
2589  */
2590 static ssize_t
coap_sock_read(gnutls_transport_ptr_t context,void * out,size_t outl)2591 coap_sock_read(gnutls_transport_ptr_t context, void *out, size_t outl) {
2592   int ret = 0;
2593   coap_session_t *c_session = (coap_session_t *)context;
2594 
2595   if (out != NULL) {
2596     ret = (int)c_session->sock.lfunc[COAP_LAYER_TLS].l_read(c_session, out, outl);
2597     /* Translate layer returns into what GnuTLS expects */
2598     if (ret == 0) {
2599       errno = EAGAIN;
2600       ret = -1;
2601     }
2602   }
2603   return ret;
2604 }
2605 
2606 /*
2607  * strm
2608  * return +ve data amount
2609  *        0   no more
2610  *        -1  error (error in errno)
2611  */
2612 static ssize_t
coap_sock_write(gnutls_transport_ptr_t context,const void * in,size_t inl)2613 coap_sock_write(gnutls_transport_ptr_t context, const void *in, size_t inl) {
2614   int ret = 0;
2615   coap_session_t *c_session = (coap_session_t *)context;
2616 
2617   ret = (int)c_session->sock.lfunc[COAP_LAYER_TLS].l_write(c_session, in, inl);
2618   /* Translate layer what returns into what GnuTLS expects */
2619   if (ret < 0) {
2620     if ((c_session->state == COAP_SESSION_STATE_CSM ||
2621          c_session->state == COAP_SESSION_STATE_HANDSHAKE) &&
2622         (errno == EPIPE || errno == ECONNRESET)) {
2623       /*
2624        * Need to handle a TCP timing window where an agent continues with
2625        * the sending of the next handshake or a CSM.
2626        * However, the peer does not like a certificate and so sends a
2627        * fatal alert and closes the TCP session.
2628        * The sending of the next handshake or CSM may get terminated because
2629        * of the closed TCP session, but there is still an outstanding alert
2630        * to be read in and reported on.
2631        * In this case, pretend that sending the info was fine so that the
2632        * alert can be read (which effectively is what happens with DTLS).
2633        */
2634       ret = inl;
2635     } else {
2636       coap_log_debug("*  %s: failed to send %zd bytes (%s) state %d\n",
2637                      coap_session_str(c_session), inl, coap_socket_strerror(),
2638                      c_session->state);
2639     }
2640   }
2641   if (ret == 0) {
2642     errno = EAGAIN;
2643     ret = -1;
2644   }
2645   return ret;
2646 }
2647 
2648 #if COAP_CLIENT_SUPPORT
2649 void *
coap_tls_new_client_session(coap_session_t * c_session)2650 coap_tls_new_client_session(coap_session_t *c_session) {
2651   coap_gnutls_env_t *g_env = gnutls_malloc(sizeof(coap_gnutls_env_t));
2652   coap_gnutls_context_t *g_context =
2653       ((coap_gnutls_context_t *)c_session->context->dtls_context);
2654 #if (GNUTLS_VERSION_NUMBER >= 0x030606)
2655   int flags = GNUTLS_CLIENT | GNUTLS_NONBLOCK | GNUTLS_ENABLE_RAWPK;
2656 #else /* < 3.6.6 */
2657   int flags = GNUTLS_CLIENT | GNUTLS_NONBLOCK;
2658 #endif /* < 3.6.6 */
2659   int ret;
2660 
2661   if (!g_env) {
2662     return NULL;
2663   }
2664   memset(g_env, 0, sizeof(coap_gnutls_env_t));
2665 
2666   G_CHECK(gnutls_init(&g_env->g_session, flags), "gnutls_init");
2667 
2668   gnutls_transport_set_pull_function(g_env->g_session, coap_sock_read);
2669   gnutls_transport_set_push_function(g_env->g_session, coap_sock_write);
2670   gnutls_transport_set_pull_timeout_function(g_env->g_session, receive_timeout);
2671   /* So we can track the coap_session_t in callbacks */
2672   gnutls_transport_set_ptr(g_env->g_session, c_session);
2673 
2674   gnutls_priority_set(g_env->g_session, g_context->priority_cache);
2675   setup_client_ssl_session(c_session, g_env);
2676 
2677   gnutls_handshake_set_timeout(g_env->g_session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
2678 
2679   c_session->tls = g_env;
2680   ret = do_gnutls_handshake(c_session, g_env);
2681   if (ret == 1) {
2682     coap_handle_event(c_session->context, COAP_EVENT_DTLS_CONNECTED, c_session);
2683     c_session->sock.lfunc[COAP_LAYER_TLS].l_establish(c_session);
2684   }
2685   return g_env;
2686 
2687 fail:
2688   if (g_env)
2689     gnutls_free(g_env);
2690   return NULL;
2691 }
2692 #endif /* COAP_CLIENT_SUPPORT */
2693 
2694 #if COAP_SERVER_SUPPORT
2695 void *
coap_tls_new_server_session(coap_session_t * c_session)2696 coap_tls_new_server_session(coap_session_t *c_session) {
2697   coap_gnutls_env_t *g_env = gnutls_malloc(sizeof(coap_gnutls_env_t));
2698   coap_gnutls_context_t *g_context =
2699       ((coap_gnutls_context_t *)c_session->context->dtls_context);
2700 #if (GNUTLS_VERSION_NUMBER >= 0x030606)
2701   int flags = GNUTLS_SERVER | GNUTLS_NONBLOCK | GNUTLS_ENABLE_RAWPK;
2702 #else /* < 3.6.6 */
2703   int flags = GNUTLS_SERVER | GNUTLS_NONBLOCK;
2704 #endif /* < 3.6.6 */
2705   int ret;
2706 
2707   if (!g_env)
2708     return NULL;
2709   memset(g_env, 0, sizeof(coap_gnutls_env_t));
2710 
2711   G_CHECK(gnutls_init(&g_env->g_session, flags), "gnutls_init");
2712 
2713   gnutls_transport_set_pull_function(g_env->g_session, coap_sock_read);
2714   gnutls_transport_set_push_function(g_env->g_session, coap_sock_write);
2715   gnutls_transport_set_pull_timeout_function(g_env->g_session, receive_timeout);
2716   /* So we can track the coap_session_t in callbacks */
2717   gnutls_transport_set_ptr(g_env->g_session, c_session);
2718 
2719   setup_server_ssl_session(c_session, g_env);
2720 
2721   gnutls_priority_set(g_env->g_session, g_context->priority_cache);
2722   gnutls_handshake_set_timeout(g_env->g_session,
2723                                GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
2724 
2725   c_session->tls = g_env;
2726   ret = do_gnutls_handshake(c_session, g_env);
2727   if (ret == 1) {
2728     coap_handle_event(c_session->context, COAP_EVENT_DTLS_CONNECTED, c_session);
2729     c_session->sock.lfunc[COAP_LAYER_TLS].l_establish(c_session);
2730   }
2731   return g_env;
2732 
2733 fail:
2734   return NULL;
2735 }
2736 #endif /* COAP_SERVER_SUPPORT */
2737 
2738 void
coap_tls_free_session(coap_session_t * c_session)2739 coap_tls_free_session(coap_session_t *c_session) {
2740   coap_dtls_free_session(c_session);
2741   return;
2742 }
2743 
2744 /*
2745  * strm
2746  * return +ve Number of bytes written.
2747  *         -1 Error (error in errno).
2748  */
2749 ssize_t
coap_tls_write(coap_session_t * c_session,const uint8_t * data,size_t data_len)2750 coap_tls_write(coap_session_t *c_session, const uint8_t *data,
2751                size_t data_len) {
2752   int ret;
2753   coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
2754 
2755   assert(g_env != NULL);
2756 
2757   c_session->dtls_event = -1;
2758   if (g_env->established) {
2759     ret = gnutls_record_send(g_env->g_session, data, data_len);
2760 
2761     if (ret <= 0) {
2762       switch (ret) {
2763       case GNUTLS_E_AGAIN:
2764         ret = 0;
2765         break;
2766       case GNUTLS_E_PUSH_ERROR:
2767       case GNUTLS_E_PULL_ERROR:
2768       case GNUTLS_E_PREMATURE_TERMINATION:
2769         c_session->dtls_event = COAP_EVENT_DTLS_CLOSED;
2770         break;
2771       case GNUTLS_E_FATAL_ALERT_RECEIVED:
2772         /* Stop the sending of an alert on closedown */
2773         g_env->sent_alert = 1;
2774         log_last_alert(c_session, g_env->g_session);
2775         c_session->dtls_event = COAP_EVENT_DTLS_CLOSED;
2776         break;
2777       default:
2778         coap_log_warn("coap_tls_write: gnutls_record_send "
2779                       "returned %d: '%s'\n",
2780                       ret, gnutls_strerror(ret));
2781         ret = -1;
2782         break;
2783       }
2784       if (ret == -1) {
2785         coap_log_info("coap_tls_write: cannot send PDU\n");
2786       }
2787     }
2788   } else {
2789     ret = do_gnutls_handshake(c_session, g_env);
2790     if (ret == 1) {
2791       coap_handle_event(c_session->context, COAP_EVENT_DTLS_CONNECTED,
2792                         c_session);
2793       c_session->sock.lfunc[COAP_LAYER_TLS].l_establish(c_session);
2794       ret = 0;
2795     } else {
2796       ret = -1;
2797     }
2798   }
2799 
2800   if (c_session->dtls_event >= 0) {
2801     /* COAP_EVENT_DTLS_CLOSED event reported in coap_session_disconnected() */
2802     if (c_session->dtls_event != COAP_EVENT_DTLS_CLOSED)
2803       coap_handle_event(c_session->context, c_session->dtls_event, c_session);
2804     if (c_session->dtls_event == COAP_EVENT_DTLS_ERROR ||
2805         c_session->dtls_event == COAP_EVENT_DTLS_CLOSED) {
2806       coap_session_disconnected(c_session, COAP_NACK_TLS_FAILED);
2807       ret = -1;
2808     }
2809   }
2810 
2811   if (ret > 0) {
2812     if (ret == (ssize_t)data_len)
2813       coap_log_debug("*  %s: tls:   sent %4d bytes\n",
2814                      coap_session_str(c_session), ret);
2815     else
2816       coap_log_debug("*  %s: tls:   sent %4d of %4zd bytes\n",
2817                      coap_session_str(c_session), ret, data_len);
2818   }
2819   return ret;
2820 }
2821 
2822 /*
2823  * strm
2824  * return >=0 Number of bytes read.
2825  *         -1 Error (error in errno).
2826  */
2827 ssize_t
coap_tls_read(coap_session_t * c_session,uint8_t * data,size_t data_len)2828 coap_tls_read(coap_session_t *c_session, uint8_t *data, size_t data_len) {
2829   coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
2830   int ret = -1;
2831 
2832   if (!g_env) {
2833     errno = ENXIO;
2834     return -1;
2835   }
2836 
2837   c_session->dtls_event = -1;
2838   if (!g_env->established && !g_env->sent_alert) {
2839     ret = do_gnutls_handshake(c_session, g_env);
2840     if (ret == 1) {
2841       coap_handle_event(c_session->context, COAP_EVENT_DTLS_CONNECTED,
2842                         c_session);
2843       c_session->sock.lfunc[COAP_LAYER_TLS].l_establish(c_session);
2844       ret = 0;
2845     }
2846   }
2847   if (c_session->state != COAP_SESSION_STATE_NONE && g_env->established) {
2848     ret = gnutls_record_recv(g_env->g_session, data, (int)data_len);
2849     if (ret <= 0) {
2850       switch (ret) {
2851       case 0:
2852         c_session->dtls_event = COAP_EVENT_DTLS_CLOSED;
2853         break;
2854       case GNUTLS_E_AGAIN:
2855         errno = EAGAIN;
2856         ret = 0;
2857         break;
2858       case GNUTLS_E_PULL_ERROR:
2859         c_session->dtls_event = COAP_EVENT_DTLS_ERROR;
2860         break;
2861       case GNUTLS_E_FATAL_ALERT_RECEIVED:
2862         /* Stop the sending of an alert on closedown */
2863         g_env->sent_alert = 1;
2864         log_last_alert(c_session, g_env->g_session);
2865         c_session->dtls_event = COAP_EVENT_DTLS_CLOSED;
2866         break;
2867       case GNUTLS_E_WARNING_ALERT_RECEIVED:
2868         log_last_alert(c_session, g_env->g_session);
2869         c_session->dtls_event = COAP_EVENT_DTLS_ERROR;
2870         break;
2871       default:
2872         coap_log_warn("coap_tls_read: gnutls_record_recv "
2873                       "returned %d: '%s'\n",
2874                       ret, gnutls_strerror(ret));
2875         ret = -1;
2876         break;
2877       }
2878     }
2879   }
2880 
2881   if (c_session->dtls_event >= 0) {
2882     /* COAP_EVENT_DTLS_CLOSED event reported in coap_session_disconnected() */
2883     if (c_session->dtls_event != COAP_EVENT_DTLS_CLOSED)
2884       coap_handle_event(c_session->context, c_session->dtls_event, c_session);
2885     if (c_session->dtls_event == COAP_EVENT_DTLS_ERROR ||
2886         c_session->dtls_event == COAP_EVENT_DTLS_CLOSED) {
2887       coap_session_disconnected(c_session, COAP_NACK_TLS_FAILED);
2888       ret = -1;
2889     }
2890   }
2891   if (ret > 0) {
2892     coap_log_debug("*  %s: tls:   recv %4d bytes\n",
2893                    coap_session_str(c_session), ret);
2894   }
2895   return ret;
2896 }
2897 #endif /* !COAP_DISABLE_TCP */
2898 
2899 #if COAP_SERVER_SUPPORT
2900 coap_digest_ctx_t *
coap_digest_setup(void)2901 coap_digest_setup(void) {
2902   gnutls_hash_hd_t digest_ctx;
2903 
2904   if (gnutls_hash_init(&digest_ctx, GNUTLS_DIG_SHA256)) {
2905     return NULL;
2906   }
2907   return digest_ctx;
2908 }
2909 
2910 void
coap_digest_free(coap_digest_ctx_t * digest_ctx)2911 coap_digest_free(coap_digest_ctx_t *digest_ctx) {
2912   gnutls_hash_deinit(digest_ctx, NULL);
2913 }
2914 
2915 int
coap_digest_update(coap_digest_ctx_t * digest_ctx,const uint8_t * data,size_t data_len)2916 coap_digest_update(coap_digest_ctx_t *digest_ctx,
2917                    const uint8_t *data,
2918                    size_t data_len) {
2919   int ret = gnutls_hash(digest_ctx, data, data_len);
2920 
2921   return ret == 0;
2922 }
2923 
2924 int
coap_digest_final(coap_digest_ctx_t * digest_ctx,coap_digest_t * digest_buffer)2925 coap_digest_final(coap_digest_ctx_t *digest_ctx,
2926                   coap_digest_t *digest_buffer) {
2927   gnutls_hash_output(digest_ctx, (uint8_t *)digest_buffer);
2928 
2929   coap_digest_free(digest_ctx);
2930   return 1;
2931 }
2932 #endif /* COAP_SERVER_SUPPORT */
2933 
2934 #if COAP_WS_SUPPORT
2935 /*
2936  * The struct hash_algs and the function get_hash_alg() are used to
2937  * determine which hash type to use for creating the required hash object.
2938  */
2939 static struct hash_algs {
2940   cose_alg_t alg;
2941   gnutls_digest_algorithm_t dig_type;
2942   size_t dig_size;
2943 } hashs[] = {
2944   {COSE_ALGORITHM_SHA_1,       GNUTLS_DIG_SHA1,   20},
2945   {COSE_ALGORITHM_SHA_256_256, GNUTLS_DIG_SHA256, 32},
2946   {COSE_ALGORITHM_SHA_512,     GNUTLS_DIG_SHA512, 64},
2947 };
2948 
2949 static gnutls_digest_algorithm_t
get_hash_alg(cose_alg_t alg,size_t * hash_len)2950 get_hash_alg(cose_alg_t alg, size_t *hash_len) {
2951   size_t idx;
2952 
2953   for (idx = 0; idx < sizeof(hashs) / sizeof(struct hash_algs); idx++) {
2954     if (hashs[idx].alg == alg) {
2955       *hash_len = hashs[idx].dig_size;
2956       return hashs[idx].dig_type;
2957     }
2958   }
2959   coap_log_debug("get_hash_alg: COSE hash %d not supported\n", alg);
2960   return GNUTLS_DIG_UNKNOWN;
2961 }
2962 
2963 int
coap_crypto_hash(cose_alg_t alg,const coap_bin_const_t * data,coap_bin_const_t ** hash)2964 coap_crypto_hash(cose_alg_t alg,
2965                  const coap_bin_const_t *data,
2966                  coap_bin_const_t **hash) {
2967   size_t hash_length;
2968   gnutls_digest_algorithm_t dig_type = get_hash_alg(alg, &hash_length);
2969   gnutls_hash_hd_t digest_ctx;
2970   coap_binary_t *dummy = NULL;
2971   int ret;
2972 
2973   if (dig_type == GNUTLS_DIG_UNKNOWN) {
2974     coap_log_debug("coap_crypto_hash: algorithm %d not supported\n", alg);
2975     return 0;
2976   }
2977 
2978   if (gnutls_hash_init(&digest_ctx, dig_type)) {
2979     return 0;
2980   }
2981   ret = gnutls_hash(digest_ctx, data->s, data->length);
2982   if (ret != 0)
2983     goto error;
2984 
2985   dummy = coap_new_binary(hash_length);
2986   if (!dummy)
2987     goto error;
2988   gnutls_hash_output(digest_ctx, dummy->s);
2989 
2990   *hash = (coap_bin_const_t *)(dummy);
2991   gnutls_hash_deinit(digest_ctx, NULL);
2992   return 1;
2993 
2994 error:
2995   coap_delete_binary(dummy);
2996   gnutls_hash_deinit(digest_ctx, NULL);
2997   return 0;
2998 }
2999 #endif /* COAP_WS_SUPPORT */
3000 
3001 #if COAP_OSCORE_SUPPORT
3002 int
coap_oscore_is_supported(void)3003 coap_oscore_is_supported(void) {
3004   return 1;
3005 }
3006 
3007 /*
3008  * The struct cipher_algs and the function get_cipher_alg() are used to
3009  * determine which cipher type to use for creating the required cipher
3010  * suite object.
3011  */
3012 static struct cipher_algs {
3013   cose_alg_t alg;
3014   gnutls_cipher_algorithm_t cipher_type;
3015 } ciphers[] = {{COSE_ALGORITHM_AES_CCM_16_64_128, GNUTLS_CIPHER_AES_128_CCM_8},
3016   {COSE_ALGORITHM_AES_CCM_16_64_256, GNUTLS_CIPHER_AES_256_CCM_8}
3017 };
3018 
3019 static gnutls_cipher_algorithm_t
get_cipher_alg(cose_alg_t alg)3020 get_cipher_alg(cose_alg_t alg) {
3021   size_t idx;
3022 
3023   for (idx = 0; idx < sizeof(ciphers) / sizeof(struct cipher_algs); idx++) {
3024     if (ciphers[idx].alg == alg)
3025       return ciphers[idx].cipher_type;
3026   }
3027   coap_log_debug("get_cipher_alg: COSE cipher %d not supported\n", alg);
3028   return 0;
3029 }
3030 
3031 /*
3032  * The struct hmac_algs and the function get_hmac_alg() are used to
3033  * determine which hmac type to use for creating the required hmac
3034  * suite object.
3035  */
3036 static struct hmac_algs {
3037   cose_hmac_alg_t hmac_alg;
3038   gnutls_mac_algorithm_t hmac_type;
3039 } hmacs[] = {
3040   {COSE_HMAC_ALG_HMAC256_256, GNUTLS_MAC_SHA256},
3041   {COSE_HMAC_ALG_HMAC512_512, GNUTLS_MAC_SHA512},
3042 };
3043 
3044 static gnutls_mac_algorithm_t
get_hmac_alg(cose_hmac_alg_t hmac_alg)3045 get_hmac_alg(cose_hmac_alg_t hmac_alg) {
3046   size_t idx;
3047 
3048   for (idx = 0; idx < sizeof(hmacs) / sizeof(struct hmac_algs); idx++) {
3049     if (hmacs[idx].hmac_alg == hmac_alg)
3050       return hmacs[idx].hmac_type;
3051   }
3052   coap_log_debug("get_hmac_alg: COSE HMAC %d not supported\n", hmac_alg);
3053   return 0;
3054 }
3055 
3056 int
coap_crypto_check_cipher_alg(cose_alg_t alg)3057 coap_crypto_check_cipher_alg(cose_alg_t alg) {
3058   return get_cipher_alg(alg);
3059 }
3060 
3061 int
coap_crypto_check_hkdf_alg(cose_hkdf_alg_t hkdf_alg)3062 coap_crypto_check_hkdf_alg(cose_hkdf_alg_t hkdf_alg) {
3063   cose_hmac_alg_t hmac_alg;
3064 
3065   if (!cose_get_hmac_alg_for_hkdf(hkdf_alg, &hmac_alg))
3066     return 0;
3067   return get_hmac_alg(hmac_alg);
3068 }
3069 
3070 int
coap_crypto_aead_encrypt(const coap_crypto_param_t * params,coap_bin_const_t * data,coap_bin_const_t * aad,uint8_t * result,size_t * max_result_len)3071 coap_crypto_aead_encrypt(const coap_crypto_param_t *params,
3072                          coap_bin_const_t *data,
3073                          coap_bin_const_t *aad,
3074                          uint8_t *result,
3075                          size_t *max_result_len) {
3076   gnutls_aead_cipher_hd_t ctx;
3077   gnutls_datum_t key;
3078   const coap_crypto_aes_ccm_t *ccm;
3079   int ret = 0;
3080   size_t result_len = *max_result_len;
3081   gnutls_cipher_algorithm_t algo;
3082   unsigned tag_size;
3083   uint8_t *key_data_rw;
3084   coap_bin_const_t laad;
3085 
3086   if (data == NULL)
3087     return 0;
3088 
3089   assert(params != NULL);
3090   if (!params) {
3091     return 0;
3092   }
3093   if ((algo = get_cipher_alg(params->alg)) == 0) {
3094     coap_log_debug("coap_crypto_encrypt: algorithm %d not supported\n",
3095                    params->alg);
3096     return 0;
3097   }
3098   tag_size = gnutls_cipher_get_tag_size(algo);
3099   ccm = &params->params.aes;
3100 
3101   /* Get a RW copy of data */
3102   memcpy(&key_data_rw, &ccm->key.s, sizeof(key_data_rw));
3103   key.data = key_data_rw;
3104   key.size = ccm->key.length;
3105 
3106   if (aad) {
3107     laad = *aad;
3108   } else {
3109     laad.s = NULL;
3110     laad.length = 0;
3111   }
3112 
3113   G_CHECK(gnutls_aead_cipher_init(&ctx, algo, &key), "gnutls_aead_cipher_init");
3114 
3115   G_CHECK(gnutls_aead_cipher_encrypt(ctx,
3116                                      ccm->nonce,
3117                                      15 - ccm->l, /* iv */
3118                                      laad.s,
3119                                      laad.length, /* ad */
3120                                      tag_size,
3121                                      data->s,
3122                                      data->length, /* input */
3123                                      result,
3124                                      &result_len), /* output */
3125           "gnutls_aead_cipher_encrypt");
3126   *max_result_len = result_len;
3127   ret = 1;
3128 fail:
3129   gnutls_aead_cipher_deinit(ctx);
3130   return ret == 1 ? 1 : 0;
3131 }
3132 
3133 int
coap_crypto_aead_decrypt(const coap_crypto_param_t * params,coap_bin_const_t * data,coap_bin_const_t * aad,uint8_t * result,size_t * max_result_len)3134 coap_crypto_aead_decrypt(const coap_crypto_param_t *params,
3135                          coap_bin_const_t *data,
3136                          coap_bin_const_t *aad,
3137                          uint8_t *result,
3138                          size_t *max_result_len) {
3139   gnutls_aead_cipher_hd_t ctx;
3140   gnutls_datum_t key;
3141   const coap_crypto_aes_ccm_t *ccm;
3142   int ret = 0;
3143   size_t result_len = *max_result_len;
3144   gnutls_cipher_algorithm_t algo;
3145   unsigned tag_size;
3146   uint8_t *key_data_rw;
3147   coap_bin_const_t laad;
3148 
3149   if (data == NULL)
3150     return 0;
3151 
3152   assert(params != NULL);
3153 
3154   if (!params) {
3155     return 0;
3156   }
3157   if ((algo = get_cipher_alg(params->alg)) == 0) {
3158     coap_log_debug("coap_crypto_decrypt: algorithm %d not supported\n",
3159                    params->alg);
3160     return 0;
3161   }
3162   tag_size = gnutls_cipher_get_tag_size(algo);
3163   ccm = &params->params.aes;
3164 
3165   /* Get a RW copy of data */
3166   memcpy(&key_data_rw, &ccm->key.s, sizeof(key_data_rw));
3167   key.data = key_data_rw;
3168   key.size = ccm->key.length;
3169 
3170   if (aad) {
3171     laad = *aad;
3172   } else {
3173     laad.s = NULL;
3174     laad.length = 0;
3175   }
3176 
3177   G_CHECK(gnutls_aead_cipher_init(&ctx, algo, &key), "gnutls_aead_cipher_init");
3178 
3179   G_CHECK(gnutls_aead_cipher_decrypt(ctx,
3180                                      ccm->nonce,
3181                                      15 - ccm->l, /* iv */
3182                                      laad.s,
3183                                      laad.length, /* ad */
3184                                      tag_size,
3185                                      data->s,
3186                                      data->length, /* input */
3187                                      result,
3188                                      &result_len), /* output */
3189           "gnutls_aead_cipher_decrypt");
3190   *max_result_len = result_len;
3191   ret = 1;
3192 fail:
3193   gnutls_aead_cipher_deinit(ctx);
3194   return ret == 1 ? 1 : 0;
3195 }
3196 
3197 int
coap_crypto_hmac(cose_hmac_alg_t hmac_alg,coap_bin_const_t * key,coap_bin_const_t * data,coap_bin_const_t ** hmac)3198 coap_crypto_hmac(cose_hmac_alg_t hmac_alg,
3199                  coap_bin_const_t *key,
3200                  coap_bin_const_t *data,
3201                  coap_bin_const_t **hmac) {
3202   gnutls_hmac_hd_t ctx;
3203   int ret = 0;
3204   unsigned len;
3205   gnutls_mac_algorithm_t mac_algo;
3206   coap_binary_t *dummy = NULL;
3207 
3208   if (data == NULL)
3209     return 0;
3210 
3211   if ((mac_algo = get_hmac_alg(hmac_alg)) == 0) {
3212     coap_log_debug("coap_crypto_hmac: algorithm %d not supported\n", hmac_alg);
3213     return 0;
3214   }
3215   len = gnutls_hmac_get_len(mac_algo);
3216   if (len == 0)
3217     return 0;
3218 
3219   dummy = coap_new_binary(len);
3220   if (dummy == NULL)
3221     return 0;
3222   G_CHECK(gnutls_hmac_init(&ctx, mac_algo, key->s, key->length),
3223           "gnutls_hmac_init");
3224   G_CHECK(gnutls_hmac(ctx, data->s, data->length), "gnutls_hmac");
3225   gnutls_hmac_output(ctx, dummy->s);
3226   *hmac = (coap_bin_const_t *)dummy;
3227   dummy = NULL;
3228   ret = 1;
3229 fail:
3230   coap_delete_binary(dummy);
3231   gnutls_hmac_deinit(ctx, NULL);
3232   return ret == 1 ? 1 : 0;
3233 }
3234 
3235 #endif /* COAP_OSCORE_SUPPORT */
3236 
3237 #else /* !COAP_WITH_LIBGNUTLS */
3238 
3239 #ifdef __clang__
3240 /* Make compilers happy that do not like empty modules. As this function is
3241  * never used, we ignore -Wunused-function at the end of compiling this file
3242  */
3243 #pragma GCC diagnostic ignored "-Wunused-function"
3244 #endif
3245 static inline void
dummy(void)3246 dummy(void) {
3247 }
3248 
3249 #endif /* !COAP_WITH_LIBGNUTLS */
3250