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