libcoap  4.2.0rc4
coap_gnutls.c
Go to the documentation of this file.
1 /*
2  * coap_gnutls.c -- GunTLS Datagram Transport Layer Support for libcoap
3  *
4  * Copyright (C) 2017 Dag Bjorklund <dag.bjorklund@comsel.fi>
5  * Copyright (C) 2018 Jon Shallow <supjps-libcoap@jpshallow.com>
6  *
7  * This file is part of the CoAP library libcoap. Please see README for terms
8  * of use.
9  */
10 
11 /*
12  * Naming used to prevent confusion between coap sessions, gnutls sessions etc.
13  * when reading the code.
14  *
15  * c_context A coap_context_t *
16  * c_session A coap_session_t *
17  * g_context A coap_gnutls_context_t * (held in c_context->dtls_context)
18  * g_session A gnutls_session_t (which has the * in the typedef)
19  * g_env A coap_gnutls_env_t * (held in c_session->tls)
20  */
21 
22 #include "coap_config.h"
23 
24 #ifdef HAVE_LIBGNUTLS
25 
26 #define MIN_GNUTLS_VERSION "3.3.0"
27 
28 #include "net.h"
29 #include "mem.h"
30 #include "coap_debug.h"
31 #include "prng.h"
32 #include <inttypes.h>
33 #include <stdio.h>
34 #include <errno.h>
35 #include <gnutls/gnutls.h>
36 #include <gnutls/x509.h>
37 #include <gnutls/dtls.h>
38 #include <unistd.h>
39 
40 #ifdef __GNUC__
41 #define UNUSED __attribute__((unused))
42 #else /* __GNUC__ */
43 #define UNUSED
44 #endif /* __GNUC__ */
45 
46 typedef struct coap_ssl_t {
47  const uint8_t *pdu;
48  unsigned pdu_len;
49  unsigned peekmode;
50  coap_tick_t timeout;
51 } coap_ssl_t;
52 
53 /*
54  * This structure encapsulates the GnuTLS session object.
55  * It handles both TLS and DTLS.
56  * c_session->tls points to this.
57  */
58 typedef struct coap_gnutls_env_t {
59  gnutls_session_t g_session;
60  gnutls_psk_client_credentials_t psk_cl_credentials;
61  gnutls_psk_server_credentials_t psk_sv_credentials;
62  gnutls_certificate_credentials_t pki_credentials;
63  coap_ssl_t coap_ssl_data;
64  /* If not set, need to do gnutls_handshake */
65  int established;
66  int seen_client_hello;
67 } coap_gnutls_env_t;
68 
69 #define IS_PSK (1 << 0)
70 #define IS_PKI (1 << 1)
71 #define IS_CLIENT (1 << 6)
72 #define IS_SERVER (1 << 7)
73 
74 typedef struct sni_entry {
75  char *sni;
76  coap_dtls_key_t pki_key;
77  gnutls_certificate_credentials_t pki_credentials;
78 } sni_entry;
79 
80 typedef struct coap_gnutls_context_t {
81  coap_dtls_pki_t setup_data;
82  int psk_pki_enabled;
83  size_t sni_count;
84  sni_entry *sni_entry_list;
85  gnutls_datum_t alpn_proto; /* Will be "coap", but that is a const */
86  char *root_ca_file;
87  char *root_ca_path;
88  gnutls_priority_t priority_cache;
89 } coap_gnutls_context_t;
90 
91 #if (GNUTLS_VERSION_NUMBER >= 0x030505)
92 #define VARIANTS "NORMAL:+ECDHE-PSK:+PSK:+ECDHE-ECDSA:+AES-128-CCM-8"
93 #else
94 #define VARIANTS "NORMAL:+ECDHE-PSK:+PSK"
95 #endif
96 
97 #define G_ACTION(xx) do { \
98  ret = (xx); \
99 } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
100 
101 #define G_CHECK(xx,func) do { \
102  if ((ret = (xx)) < 0) { \
103  coap_log(LOG_WARNING, "%s: '%s'\n", func, gnutls_strerror(ret)); \
104  goto fail; \
105  } \
106 } while (0)
107 
108 #define G_ACTION_CHECK(xx,func) do { \
109  G_ACTION(xx); \
110  G_CHECK(xx, func); \
111 } while 0
112 
113 static int dtls_log_level = 0;
114 
115 static int post_client_hello_gnutls_pki(gnutls_session_t g_session);
116 static int post_client_hello_gnutls_psk(gnutls_session_t g_session);
117 
118 /*
119  * return 0 failed
120  * 1 passed
121  */
122 int
124  if (gnutls_check_version(MIN_GNUTLS_VERSION) == NULL) {
125  coap_log(LOG_ERR, "GnuTLS " MIN_GNUTLS_VERSION " or later is required\n");
126  return 0;
127  }
128  return 1;
129 }
130 
131 /*
132  * return 0 failed
133  * 1 passed
134  */
135 int
136 coap_tls_is_supported(void) {
137  if (gnutls_check_version(MIN_GNUTLS_VERSION) == NULL) {
138  coap_log(LOG_ERR, "GnuTLS " MIN_GNUTLS_VERSION " or later is required\n");
139  return 0;
140  }
141  return 1;
142 }
143 
146  static coap_tls_version_t version;
147  const char *vers = gnutls_check_version(NULL);
148 
149  version.version = 0;
150  if (vers) {
151  int p1, p2, p3;
152 
153  sscanf (vers, "%d.%d.%d", &p1, &p2, &p3);
154  version.version = (p1 << 16) | (p2 << 8) | p3;
155  }
156  version.built_version = GNUTLS_VERSION_NUMBER;
157  version.type = COAP_TLS_LIBRARY_GNUTLS;
158  return &version;
159 }
160 
161 static void
162 coap_gnutls_audit_log_func(gnutls_session_t g_session, const char* text)
163 {
164  coap_session_t *c_session =
165  (coap_session_t *)gnutls_transport_get_ptr(g_session);
166  coap_log(LOG_WARNING, "** %s: %s", coap_session_str(c_session), text);
167 }
168 
169 static void
170 coap_gnutls_log_func(int level, const char* text)
171 {
172  /* debug logging in gnutls starts at 2 */
173  if (level > 2)
174  level = 2;
175  coap_log(LOG_DEBUG + level - 2, "%s", text);
176 }
177 
178 /*
179  * return 0 failed
180  * 1 passed
181  */
182 int
184  coap_dtls_pki_t* setup_data,
185  int role UNUSED)
186 {
187  coap_gnutls_context_t *g_context =
188  ((coap_gnutls_context_t *)c_context->dtls_context);
189 
190  if (!g_context || !setup_data)
191  return 0;
192 
193  g_context->setup_data = *setup_data;
194  g_context->psk_pki_enabled |= IS_PKI;
195  return 1;
196 }
197 
198 /*
199  * return 0 failed
200  * 1 passed
201  */
202 int
204  const char *ca_file,
205  const char *ca_path)
206 {
207  coap_gnutls_context_t *g_context =
208  ((coap_gnutls_context_t *)c_context->dtls_context);
209  if (!g_context) {
211  "coap_context_set_pki_root_cas: (D)TLS environment "
212  "not set up\n");
213  return 0;
214  }
215 
216  if (ca_file == NULL && ca_path == NULL) {
218  "coap_context_set_pki_root_cas: ca_file and/or ca_path "
219  "not defined\n");
220  return 0;
221  }
222  if (g_context->root_ca_file) {
223  gnutls_free(g_context->root_ca_file);
224  g_context->root_ca_file = NULL;
225  }
226  if (ca_file) {
227  g_context->root_ca_file = gnutls_strdup(ca_file);
228  }
229  if (g_context->root_ca_path) {
230  gnutls_free(g_context->root_ca_path);
231  g_context->root_ca_path = NULL;
232  }
233  if (ca_path) {
234 #if (GNUTLS_VERSION_NUMBER >= 0x030306)
235  g_context->root_ca_path = gnutls_strdup(ca_path);
236 #else
237  coap_log(LOG_ERR, "ca_path not supported in GnuTLS < 3.3.6\n");
238 #endif
239  }
240  return 1;
241 }
242 
243 /*
244  * return 0 failed
245  * 1 passed
246  */
247 int
249  const char *identity_hint UNUSED,
250  int role UNUSED
251 ) {
252  coap_gnutls_context_t *g_context =
253  ((coap_gnutls_context_t *)c_context->dtls_context);
254 
255  g_context->psk_pki_enabled |= IS_PSK;
256  return 1;
257 }
258 
259 /*
260  * return 0 failed
261  * 1 passed
262  */
263 int
265 {
266  coap_gnutls_context_t *g_context =
267  ((coap_gnutls_context_t *)c_context->dtls_context);
268  return g_context->psk_pki_enabled ? 1 : 0;
269 }
270 
271 void coap_dtls_startup(void) {
272  gnutls_global_set_audit_log_function(coap_gnutls_audit_log_func);
273  gnutls_global_set_log_function(coap_gnutls_log_func);
274 }
275 
276 void
277 coap_dtls_set_log_level(int level) {
278  dtls_log_level = level;
279  if (level - LOG_DEBUG >= -2) {
280  /* debug logging in gnutls starts at 2 */
281  gnutls_global_set_log_level(2 + level - LOG_DEBUG);
282  }
283  else {
284  gnutls_global_set_log_level(0);
285  }
286 }
287 
288 /*
289  * return current logging level
290  */
291 int
293  return dtls_log_level;
294 }
295 
296 /*
297  * return +ve new g_context
298  * NULL failure
299  */
300 void *
301 coap_dtls_new_context(struct coap_context_t *c_context UNUSED) {
302  const char *err;
303  int ret;
304  struct coap_gnutls_context_t *g_context =
305  (struct coap_gnutls_context_t *)
306  gnutls_malloc(sizeof(coap_gnutls_context_t));
307 
308  if (g_context) {
309  G_CHECK(gnutls_global_init(), "gnutls_global_init");
310  memset(g_context, 0, sizeof(struct coap_gnutls_context_t));
311  g_context->alpn_proto.data = gnutls_malloc(4);
312  if (g_context->alpn_proto.data) {
313  memcpy(g_context->alpn_proto.data, "coap", 4);
314  g_context->alpn_proto.size = 4;
315  }
316  G_CHECK(gnutls_priority_init(&g_context->priority_cache, VARIANTS, &err),
317  "gnutls_priority_init");
318  }
319  return g_context;
320 
321 fail:
322  if (g_context)
323  coap_dtls_free_context(g_context);
324  return NULL;
325 }
326 
327 void
328 coap_dtls_free_context(void *handle) {
329  size_t i;
330  coap_gnutls_context_t *g_context = (coap_gnutls_context_t *)handle;
331 
332  gnutls_free(g_context->alpn_proto.data);
333  gnutls_free(g_context->root_ca_file);
334  gnutls_free(g_context->root_ca_path);
335  for (i = 0; i < g_context->sni_count; i++) {
336  gnutls_free(g_context->sni_entry_list[i].sni);
337  if (g_context->psk_pki_enabled & IS_PKI) {
338  gnutls_certificate_free_credentials(
339  g_context->sni_entry_list[i].pki_credentials);
340  }
341  }
342  if (g_context->sni_count)
343  gnutls_free(g_context->sni_entry_list);
344 
345  gnutls_priority_deinit(g_context->priority_cache);
346 
347  gnutls_global_deinit();
348  gnutls_free(g_context);
349 }
350 
351 /*
352  * gnutls_psk_client_credentials_function return values
353  * (see gnutls_psk_set_client_credentials_function())
354  *
355  * return -1 failed
356  * 0 passed
357  */
358 static int
359 psk_client_callback(gnutls_session_t g_session,
360  char **username, gnutls_datum_t *key) {
361  coap_session_t *c_session =
362  (coap_session_t *)gnutls_transport_get_ptr(g_session);
363  uint8_t identity[64];
364  size_t identity_len;
365  uint8_t psk_key[64];
366  size_t psk_len;
367 
368  if (c_session == NULL || c_session->context == NULL ||
369  c_session->context->get_client_psk == NULL) {
370  return -1;
371  }
372 
373  psk_len = c_session->context->get_client_psk(c_session,
374  NULL,
375  0,
376  identity,
377  &identity_len,
378  sizeof (identity) - 1,
379  psk_key,
380  sizeof(psk_key));
381  if (identity_len < sizeof (identity))
382  identity[identity_len] = 0;
383 
384  *username = gnutls_malloc(identity_len+1);
385  memcpy(*username, identity, identity_len+1);
386 
387  key->data = gnutls_malloc(psk_len);
388  memcpy(key->data, psk_key, psk_len);
389  key->size = psk_len;
390 
391  return 0;
392 }
393 
394 /*
395  * return +ve SAN or CN derived from certificate
396  * NULL failed
397  */
398 static char* get_san_or_cn(gnutls_session_t g_session)
399 {
400  unsigned int cert_list_size = 0;
401  const gnutls_datum_t *cert_list;
402  gnutls_x509_crt_t cert;
403  char dn[256];
404  size_t size;
405  int n;
406  char *cn;
407  int ret;
408 
409  if (gnutls_certificate_type_get(g_session) != GNUTLS_CRT_X509)
410  return NULL;
411 
412  cert_list = gnutls_certificate_get_peers(g_session, &cert_list_size);
413  if (cert_list_size == 0) {
414  return NULL;
415  }
416 
417  G_CHECK(gnutls_x509_crt_init(&cert), "gnutls_x509_crt_init");
418 
419  /* Interested only in first cert in chain */
420  G_CHECK(gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER),
421  "gnutls_x509_crt_import");
422 
423  size = sizeof(dn) -1;
424  /* See if there is a Subject Alt Name first */
425  ret = gnutls_x509_crt_get_subject_alt_name(cert, 0, dn, &size, NULL);
426  if (ret >= 0) {
427  dn[size] = '\000';
428  gnutls_x509_crt_deinit(cert);
429  return gnutls_strdup(dn);
430  }
431 
432  size = sizeof(dn);
433  G_CHECK(gnutls_x509_crt_get_dn(cert, dn, &size), "gnutls_x509_crt_get_dn");
434 
435  gnutls_x509_crt_deinit(cert);
436 
437  /* Need to emulate strcasestr() here. Looking for CN= */
438  n = strlen(dn) - 3;
439  cn = dn;
440  while (n > 0) {
441  if (((cn[0] == 'C') || (cn[0] == 'c')) &&
442  ((cn[1] == 'N') || (cn[1] == 'n')) &&
443  (cn[2] == '=')) {
444  cn += 3;
445  break;
446  }
447  cn++;
448  n--;
449  }
450  if (n > 0) {
451  char *ecn = strchr(cn, ',');
452  if (ecn) {
453  cn[ecn-cn] = '\000';
454  }
455  return gnutls_strdup(cn);
456  }
457  return NULL;
458 
459 fail:
460  return NULL;
461 }
462 
463 /*
464  * return 0 failed
465  * 1 passed
466  */
467 static int cert_verify_gnutls(gnutls_session_t g_session)
468 {
469  unsigned int status = 0;
470  coap_session_t *c_session =
471  (coap_session_t *)gnutls_transport_get_ptr(g_session);
472  coap_gnutls_context_t *g_context =
473  (coap_gnutls_context_t *)c_session->context->dtls_context;
474  char *cn = NULL;
475  int alert = GNUTLS_A_BAD_CERTIFICATE;
476  int ret;
477 
478  G_CHECK(gnutls_certificate_verify_peers(g_session, NULL, 0, &status),
479  "gnutls_certificate_verify_peers");
480 
481  cn = get_san_or_cn(g_session);
482 
483  if (status) {
484  status &= ~(GNUTLS_CERT_INVALID);
485  if (status & (GNUTLS_CERT_NOT_ACTIVATED|GNUTLS_CERT_EXPIRED)) {
486  if (g_context->setup_data.allow_expired_certs) {
487  status &= ~(GNUTLS_CERT_NOT_ACTIVATED|GNUTLS_CERT_EXPIRED);
489  " %s: %s: overridden: '%s'\n",
490  coap_session_str(c_session),
491  "The certificate has an invalid usage date", cn ? cn : "?");
492  }
493  }
494  if (status & (GNUTLS_CERT_REVOCATION_DATA_SUPERSEDED|
495  GNUTLS_CERT_REVOCATION_DATA_ISSUED_IN_FUTURE)) {
496  if (g_context->setup_data.allow_expired_crl) {
497  status &= ~(GNUTLS_CERT_REVOCATION_DATA_SUPERSEDED|
498  GNUTLS_CERT_REVOCATION_DATA_ISSUED_IN_FUTURE);
500  " %s: %s: overridden: '%s'\n",
501  coap_session_str(c_session),
502  "The certificate's CRL entry has an invalid usage date",
503  cn ? cn : "?");
504  }
505  }
506 
507  if (status) {
509  " %s: status 0x%x: '%s'\n",
510  coap_session_str(c_session),
511  status, cn ? cn : "?");
512  }
513  }
514 
515  if (status)
516  goto fail;
517 
518  if (g_context->setup_data.validate_cn_call_back) {
519  unsigned int cert_list_size = 0;
520  const gnutls_datum_t *cert_list;
521  gnutls_x509_crt_t cert;
522  uint8_t der[2048];
523  size_t size;
524 
525  cert_list = gnutls_certificate_get_peers(g_session, &cert_list_size);
526  if (cert_list_size == 0) {
527  /* get_san_or_cn() should have caught this */
528  goto fail;
529  }
530 
531  G_CHECK(gnutls_x509_crt_init(&cert), "gnutls_x509_crt_init");
532 
533  /* Interested only in first cert in chain */
534  G_CHECK(gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER),
535  "gnutls_x509_crt_import");
536 
537  size = sizeof(der);
538  G_CHECK(gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_DER, der, &size),
539  "gnutls_x509_crt_export");
540  gnutls_x509_crt_deinit(cert);
541  if (!g_context->setup_data.validate_cn_call_back(cn,
542  der,
543  size,
544  c_session,
545  0,
546  status ? 0 : 1,
547  g_context->setup_data.cn_call_back_arg)) {
548  alert = GNUTLS_A_ACCESS_DENIED;
549  goto fail;
550  }
551  }
552 
553  if (g_context->setup_data.additional_tls_setup_call_back) {
554  /* Additional application setup wanted */
555  if (!g_context->setup_data.additional_tls_setup_call_back(g_session,
556  &g_context->setup_data)) {
557  goto fail;
558  }
559  }
560 
561  if (cn)
562  gnutls_free(cn);
563 
564  return 1;
565 
566 fail:
567  if (cn)
568  gnutls_free(cn);
569 
570  G_ACTION(gnutls_alert_send(g_session, GNUTLS_AL_FATAL, alert));
571  return 0;
572 }
573 
574 /*
575  * gnutls_certificate_verify_function return values
576  * (see gnutls_certificate_set_verify_function())
577  *
578  * return -1 failed
579  * 0 passed
580  */
581 static int cert_verify_callback_gnutls(gnutls_session_t g_session)
582 {
583  int ret;
584 
585  if (gnutls_auth_get_type(g_session) == GNUTLS_CRD_CERTIFICATE) {
586  if (cert_verify_gnutls(g_session) == 0) {
587  G_ACTION(gnutls_alert_send(g_session,
588  GNUTLS_AL_FATAL,
589  GNUTLS_A_ACCESS_DENIED));
590  return -1;
591  }
592  }
593  return 0;
594 }
595 
596 /*
597  * return 0 Success (GNUTLS_E_SUCCESS)
598  * neg GNUTLS_E_* error code
599  */
600 static int
601 setup_pki_credentials(gnutls_certificate_credentials_t *pki_credentials,
602  coap_gnutls_context_t *g_context,
603  coap_dtls_pki_t *setup_data)
604 {
605  int ret;
606 
607  switch (setup_data->pki_key.key_type) {
608  case COAP_PKI_KEY_PEM:
609  if (setup_data->pki_key.key.pem.public_cert &&
610  setup_data->pki_key.key.pem.public_cert[0] &&
611  setup_data->pki_key.key.pem.private_key &&
612  setup_data->pki_key.key.pem.private_key[0]) {
613  G_CHECK(gnutls_certificate_allocate_credentials(pki_credentials),
614  "gnutls_certificate_allocate_credentials");
615 
616  G_CHECK(gnutls_certificate_set_x509_key_file(*pki_credentials,
617  setup_data->pki_key.key.pem.public_cert,
618  setup_data->pki_key.key.pem.private_key,
619  GNUTLS_X509_FMT_PEM),
620  "gnutls_certificate_set_x509_key_file");
621  }
622  else {
624  "***setup_pki: (D)TLS: No Client Certificate + Private "
625  "Key defined\n");
626  return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
627  }
628  if (setup_data->pki_key.key.pem.ca_file &&
629  setup_data->pki_key.key.pem.ca_file[0]) {
630  G_CHECK(gnutls_certificate_set_x509_trust_file(*pki_credentials,
631  setup_data->pki_key.key.pem.ca_file,
632  GNUTLS_X509_FMT_PEM),
633  "gnutls_certificate_set_x509_trust_file");
634  }
635  break;
636 
637  case COAP_PKI_KEY_ASN1:
638  if (setup_data->pki_key.key.asn1.public_cert &&
639  setup_data->pki_key.key.asn1.public_cert_len &&
640  setup_data->pki_key.key.asn1.private_key &&
641  setup_data->pki_key.key.asn1.private_key_len > 0) {
642  gnutls_datum_t cert;
643  gnutls_datum_t key;
644 
645  /* Kludge to get around const parameters */
646  memcpy(&cert.data, &setup_data->pki_key.key.asn1.public_cert,
647  sizeof(cert.data));
648  cert.size = setup_data->pki_key.key.asn1.public_cert_len;
649  memcpy(&key.data, &setup_data->pki_key.key.asn1.private_key,
650  sizeof(key.data));
651  key.size = setup_data->pki_key.key.asn1.private_key_len;
652  G_CHECK(gnutls_certificate_set_x509_key_mem(*pki_credentials,
653  &cert,
654  &key,
655  GNUTLS_X509_FMT_DER),
656  "gnutls_certificate_set_x509_key_mem");
657  }
658  else {
660  "***setup_pki: (D)TLS: No Client Certificate + Private "
661  "Key defined\n");
662  return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
663  }
664  if (setup_data->pki_key.key.asn1.ca_cert &&
665  setup_data->pki_key.key.asn1.ca_cert_len > 0) {
666  gnutls_datum_t ca_cert;
667 
668  /* Kludge to get around const parameters */
669  memcpy(&ca_cert.data, &setup_data->pki_key.key.asn1.ca_cert,
670  sizeof(ca_cert.data));
671  ca_cert.size = setup_data->pki_key.key.asn1.ca_cert_len;
672  G_CHECK(gnutls_certificate_set_x509_trust_mem(*pki_credentials,
673  &ca_cert,
674  GNUTLS_X509_FMT_DER),
675  "gnutls_certificate_set_x509_trust_mem");
676  }
677  break;
678  default:
680  "***setup_pki: (D)TLS: Unknown key type %d\n",
681  setup_data->pki_key.key_type);
682  return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
683  }
684 
685  if (g_context->root_ca_file) {
686  G_CHECK(gnutls_certificate_set_x509_trust_file(*pki_credentials,
687  g_context->root_ca_file,
688  GNUTLS_X509_FMT_PEM),
689  "gnutls_certificate_set_x509_trust_file");
690  }
691  if (g_context->root_ca_path) {
692 #if (GNUTLS_VERSION_NUMBER >= 0x030306)
693  G_CHECK(gnutls_certificate_set_x509_trust_dir(*pki_credentials,
694  g_context->root_ca_path,
695  GNUTLS_X509_FMT_PEM),
696  "gnutls_certificate_set_x509_trust_dir");
697 #endif
698  }
699 
700  /* Verify Peer */
701  if (setup_data->verify_peer_cert) {
702  gnutls_certificate_set_verify_function(*pki_credentials,
703  cert_verify_callback_gnutls);
704  }
705 
706  /* Cert chain checking (can raise GNUTLS_E_CONSTRAINT_ERROR) */
707  if (setup_data->cert_chain_validation) {
708  gnutls_certificate_set_verify_limits(*pki_credentials,
709  0,
710  setup_data->cert_chain_verify_depth);
711  }
712 
713  /* Check for self signed */
714  gnutls_certificate_set_verify_flags(*pki_credentials,
715  GNUTLS_VERIFY_DO_NOT_ALLOW_SAME);
716 
717  /* CRL checking (can raise GNUTLS_CERT_MISSING_OCSP_STATUS) */
718  if (setup_data->check_cert_revocation == 0) {
719  gnutls_certificate_set_verify_flags(*pki_credentials,
720  GNUTLS_VERIFY_DO_NOT_ALLOW_SAME |
721  GNUTLS_VERIFY_DISABLE_CRL_CHECKS);
722  }
723 
724  return GNUTLS_E_SUCCESS;
725 
726 fail:
727  return ret;
728 }
729 
730 /*
731  * return 0 Success (GNUTLS_E_SUCCESS)
732  * neg GNUTLS_E_* error code
733  */
734 static int
735 post_client_hello_gnutls_pki(gnutls_session_t g_session)
736 {
737  coap_session_t *c_session =
738  (coap_session_t *)gnutls_transport_get_ptr(g_session);
739  coap_gnutls_context_t *g_context =
740  (coap_gnutls_context_t *)c_session->context->dtls_context;
741  coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
742  int ret = GNUTLS_E_SUCCESS;
743  char *name = NULL;
744 
745  g_env->seen_client_hello = 1;
746 
747  if (g_context->setup_data.validate_sni_call_back) {
748  /* DNS names (only type supported) may be at most 256 byte long */
749  size_t len = 256;
750  unsigned int type;
751  unsigned int i;
752  coap_dtls_pki_t sni_setup_data;
753 
754  name = gnutls_malloc(len);
755  if (name == NULL)
756  return GNUTLS_E_MEMORY_ERROR;
757 
758  for (i=0; ; ) {
759  ret = gnutls_server_name_get(g_session, name, &len, &type, i);
760  if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
761  char *new_name;
762  new_name = gnutls_realloc(name, len);
763  if (new_name == NULL) {
764  ret = GNUTLS_E_MEMORY_ERROR;
765  goto end;
766  }
767  name = new_name;
768  continue; /* retry call with same index */
769  }
770 
771  /* check if it is the last entry in list */
772  if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
773  break;
774  i++;
775  if (ret != GNUTLS_E_SUCCESS)
776  goto end;
777  /* unknown types need to be ignored */
778  if (type != GNUTLS_NAME_DNS)
779  continue;
780 
781  }
782  /* If no extension provided, make it a dummy entry */
783  if (i == 0) {
784  name[0] = '\000';
785  len = 0;
786  }
787 
788  /* Is this a cached entry? */
789  for (i = 0; i < g_context->sni_count; i++) {
790  if (strcmp(name, g_context->sni_entry_list[i].sni) == 0) {
791  break;
792  }
793  }
794  if (i == g_context->sni_count) {
795  /*
796  * New SNI request
797  */
798  coap_dtls_key_t *new_entry =
799  g_context->setup_data.validate_sni_call_back(name,
800  g_context->setup_data.sni_call_back_arg);
801  if (!new_entry) {
802  G_ACTION(gnutls_alert_send(g_session, GNUTLS_AL_FATAL,
803  GNUTLS_A_UNRECOGNIZED_NAME));
804  ret = GNUTLS_E_NO_CERTIFICATE_FOUND;
805  goto end;
806  }
807 
808  g_context->sni_entry_list = gnutls_realloc(g_context->sni_entry_list,
809  (i+1)*sizeof(sni_entry));
810  g_context->sni_entry_list[i].sni = gnutls_strdup(name);
811  g_context->sni_entry_list[i].pki_key = *new_entry;
812  sni_setup_data = g_context->setup_data;
813  sni_setup_data.pki_key = *new_entry;
814  if ((ret = setup_pki_credentials(
815  &g_context->sni_entry_list[i].pki_credentials,
816  g_context,
817  &sni_setup_data)) < 0) {
818  int keep_ret = ret;
819  G_ACTION(gnutls_alert_send(g_session, GNUTLS_AL_FATAL,
820  GNUTLS_A_BAD_CERTIFICATE));
821  ret = keep_ret;
822  goto end;
823  }
824  g_context->sni_count++;
825  }
826  G_CHECK(gnutls_credentials_set(g_env->g_session, GNUTLS_CRD_CERTIFICATE,
827  g_context->sni_entry_list[i].pki_credentials),
828  "gnutls_credentials_set");
829  }
830 
831 end:
832  free(name);
833  return ret;
834 
835 fail:
836  return ret;
837 }
838 
839 /*
840  * return 0 Success (GNUTLS_E_SUCCESS)
841  * neg GNUTLS_E_* error code
842  */
843 static int
844 post_client_hello_gnutls_psk(gnutls_session_t g_session)
845 {
846  coap_session_t *c_session =
847  (coap_session_t *)gnutls_transport_get_ptr(g_session);
848  coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
849 
850  g_env->seen_client_hello = 1;
851  return GNUTLS_E_SUCCESS;
852 }
853 
854 /*
855  * return 0 Success (GNUTLS_E_SUCCESS)
856  * neg GNUTLS_E_* error code
857  */
858 static int
859 setup_client_ssl_session(coap_session_t *c_session, coap_gnutls_env_t *g_env)
860 {
861  coap_gnutls_context_t *g_context =
862  (coap_gnutls_context_t *)c_session->context->dtls_context;
863  int ret;
864 
865  g_context->psk_pki_enabled |= IS_CLIENT;
866  if (g_context->psk_pki_enabled & IS_PSK) {
867  char *identity = NULL;
868  gnutls_datum_t psk_key;
869 
870  G_CHECK(gnutls_psk_allocate_client_credentials(&g_env->psk_cl_credentials),
871  "gnutls_psk_allocate_client_credentials");
872  psk_client_callback(g_env->g_session, &identity, &psk_key);
873  G_CHECK(gnutls_psk_set_client_credentials(g_env->psk_cl_credentials,
874  identity,
875  &psk_key,
876  GNUTLS_PSK_KEY_RAW),
877  "gnutls_psk_set_client_credentials");
878  G_CHECK(gnutls_credentials_set(g_env->g_session, GNUTLS_CRD_PSK,
879  g_env->psk_cl_credentials),
880  "gnutls_credentials_set");
881  gnutls_free(identity);
882  gnutls_free(psk_key.data);
883  }
884 
885  if (g_context->psk_pki_enabled & IS_PKI) {
886  coap_dtls_pki_t *setup_data = &g_context->setup_data;
887  G_CHECK(setup_pki_credentials(&g_env->pki_credentials, g_context,
888  setup_data),
889  "setup_pki_credentials");
890 
891  G_CHECK(gnutls_credentials_set(g_env->g_session, GNUTLS_CRD_CERTIFICATE,
892  g_env->pki_credentials),
893  "gnutls_credentials_set");
894 
895  if (c_session->proto == COAP_PROTO_TLS)
896  G_CHECK(gnutls_alpn_set_protocols(g_env->g_session,
897  &g_context->alpn_proto, 1, 0),
898  "gnutls_alpn_set_protocols");
899 
900  /* Issue SNI if requested */
901  if (setup_data->client_sni) {
902  G_CHECK(gnutls_server_name_set(g_env->g_session, GNUTLS_NAME_DNS,
903  setup_data->client_sni,
904  strlen(setup_data->client_sni)),
905  "gnutls_server_name_set");
906  }
907  }
908  return GNUTLS_E_SUCCESS;
909 
910 fail:
911  return ret;
912 }
913 
914 /*
915  * gnutls_psk_server_credentials_function return values
916  * (see gnutls_psk_set_server_credentials_function())
917  *
918  * return -1 failed
919  * 0 passed
920  */
921 static int
922 psk_server_callback(gnutls_session_t g_session,
923  const char *identity,
924  gnutls_datum_t *key)
925 {
926  coap_session_t *c_session =
927  (coap_session_t *)gnutls_transport_get_ptr(g_session);
928  size_t identity_len = 0;
929  uint8_t buf[64];
930  size_t psk_len;
931 
932  if (identity)
933  identity_len = strlen(identity);
934  else
935  identity = "";
936 
937  coap_log(LOG_DEBUG, "got psk_identity: '%.*s'\n",
938  (int)identity_len, identity);
939 
940  if (c_session == NULL || c_session->context == NULL ||
941  c_session->context->get_server_psk == NULL)
942  return -1;
943 
944  psk_len = c_session->context->get_server_psk(c_session,
945  (const uint8_t*)identity,
946  identity_len,
947  (uint8_t*)buf, sizeof(buf));
948  key->data = gnutls_malloc(psk_len);
949  memcpy(key->data, buf, psk_len);
950  key->size = psk_len;
951  return 0;
952 }
953 
954 /*
955  * return 0 Success (GNUTLS_E_SUCCESS)
956  * neg GNUTLS_E_* error code
957  */
958 static int
959 setup_server_ssl_session(coap_session_t *c_session, coap_gnutls_env_t *g_env)
960 {
961  coap_gnutls_context_t *g_context =
962  (coap_gnutls_context_t *)c_session->context->dtls_context;
963  int ret = GNUTLS_E_SUCCESS;
964 
965  g_context->psk_pki_enabled |= IS_SERVER;
966  if (g_context->psk_pki_enabled & IS_PSK) {
967  G_CHECK(gnutls_psk_allocate_server_credentials(&g_env->psk_sv_credentials),
968  "gnutls_psk_allocate_server_credentials");
969  gnutls_psk_set_server_credentials_function(g_env->psk_sv_credentials,
970  psk_server_callback);
971 
972  gnutls_handshake_set_post_client_hello_function(g_env->g_session,
973  post_client_hello_gnutls_psk);
974 
975  G_CHECK(gnutls_credentials_set(g_env->g_session,
976  GNUTLS_CRD_PSK,
977  g_env->psk_sv_credentials),
978  "gnutls_credentials_set\n");
979  }
980 
981  if (g_context->psk_pki_enabled & IS_PKI) {
982  coap_dtls_pki_t *setup_data = &g_context->setup_data;
983  G_CHECK(setup_pki_credentials(&g_env->pki_credentials, g_context,
984  setup_data),
985  "setup_pki_credentials");
986 
987  if (setup_data->require_peer_cert) {
988  gnutls_certificate_server_set_request(g_env->g_session,
989  GNUTLS_CERT_REQUIRE);
990  }
991  else {
992  gnutls_certificate_server_set_request(g_env->g_session, GNUTLS_CERT_IGNORE);
993  }
994 
995  gnutls_handshake_set_post_client_hello_function(g_env->g_session,
996  post_client_hello_gnutls_pki);
997 
998  G_CHECK(gnutls_credentials_set(g_env->g_session, GNUTLS_CRD_CERTIFICATE,
999  g_env->pki_credentials),
1000  "gnutls_credentials_set\n");
1001  }
1002  return GNUTLS_E_SUCCESS;
1003 
1004 fail:
1005  return ret;
1006 }
1007 
1008 /*
1009  * return +ve data amount
1010  * 0 no more
1011  * -1 error (error in errno)
1012  */
1013 static ssize_t
1014 coap_dgram_read(gnutls_transport_ptr_t context, void *out, size_t outl)
1015 {
1016  ssize_t ret = 0;
1017  coap_session_t *c_session = (struct coap_session_t *)context;
1018  coap_ssl_t *data = &((coap_gnutls_env_t *)c_session->tls)->coap_ssl_data;
1019 
1020  if (!c_session->tls) {
1021  errno = EAGAIN;
1022  return -1;
1023  }
1024 
1025  if (out != NULL) {
1026  if (data != NULL && data->pdu_len > 0) {
1027  if (outl < data->pdu_len) {
1028  memcpy(out, data->pdu, outl);
1029  ret = outl;
1030  data->pdu += outl;
1031  data->pdu_len -= outl;
1032  } else {
1033  memcpy(out, data->pdu, data->pdu_len);
1034  ret = data->pdu_len;
1035  if (!data->peekmode) {
1036  data->pdu_len = 0;
1037  data->pdu = NULL;
1038  }
1039  }
1040  }
1041  else {
1042  errno = EAGAIN;
1043  ret = -1;
1044  }
1045  }
1046  return ret;
1047 }
1048 
1049 /*
1050  * return +ve data amount
1051  * 0 no more
1052  * -1 error (error in errno)
1053  */
1054 /* callback function given to gnutls for sending data over socket */
1055 static ssize_t
1056 coap_dgram_write(gnutls_transport_ptr_t context, const void *send_buffer,
1057  size_t send_buffer_length) {
1058  ssize_t result = -1;
1059  coap_session_t *c_session = (struct coap_session_t *)context;
1060 
1061  if (c_session) {
1062  result = coap_session_send(c_session, send_buffer, send_buffer_length);
1063  if (result != (int)send_buffer_length) {
1064  coap_log(LOG_WARNING, "coap_network_send failed\n");
1065  result = 0;
1066  }
1067  } else {
1068  result = 0;
1069  }
1070  return result;
1071 }
1072 
1073 /*
1074  * return 1 fd has activity
1075  * 0 timeout
1076  * -1 error (error in errno)
1077  */
1078 static int
1079 receive_timeout(gnutls_transport_ptr_t context, unsigned int ms UNUSED) {
1080  coap_session_t *c_session = (struct coap_session_t *)context;
1081 
1082  if (c_session) {
1083  fd_set readfds, writefds, exceptfds;
1084  struct timeval tv;
1085  int nfds = c_session->sock.fd +1;
1086 
1087  FD_ZERO(&readfds);
1088  FD_ZERO(&writefds);
1089  FD_ZERO(&exceptfds);
1090  FD_SET (c_session->sock.fd, &readfds);
1091  FD_SET (c_session->sock.fd, &writefds);
1092  FD_SET (c_session->sock.fd, &exceptfds);
1093  /* Polling */
1094  tv.tv_sec = 0;
1095  tv.tv_usec = 0;
1096 
1097  return select(nfds, &readfds, &writefds, &exceptfds, &tv);
1098  }
1099  return 1;
1100 }
1101 
1102 static coap_gnutls_env_t *
1103 coap_dtls_new_gnutls_env(coap_session_t *c_session, int type)
1104 {
1105  coap_gnutls_context_t *g_context =
1106  ((coap_gnutls_context_t *)c_session->context->dtls_context);
1107  coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
1108  int flags = type | GNUTLS_DATAGRAM | GNUTLS_NONBLOCK;
1109  int ret;
1110 
1111  if (g_env)
1112  return g_env;
1113 
1114  g_env = gnutls_malloc(sizeof(coap_gnutls_env_t));
1115  if (!g_env)
1116  return NULL;
1117 
1118  memset(g_env, 0, sizeof(struct coap_gnutls_env_t));
1119 
1120  G_CHECK(gnutls_init(&g_env->g_session, flags), "gnutls_init");
1121 
1122  gnutls_transport_set_pull_function(g_env->g_session, coap_dgram_read);
1123  gnutls_transport_set_push_function(g_env->g_session, coap_dgram_write);
1124  gnutls_transport_set_pull_timeout_function(g_env->g_session, receive_timeout);
1125  /* So we can track the coap_session_t in callbacks */
1126  gnutls_transport_set_ptr(g_env->g_session, c_session);
1127 
1128  if (type == GNUTLS_SERVER) {
1129  G_CHECK(setup_server_ssl_session(c_session, g_env),
1130  "setup_server_ssl_session");
1131  }
1132  else {
1133  G_CHECK(setup_client_ssl_session(c_session, g_env),
1134  "setup_client_ssl_session");
1135  }
1136 
1137  G_CHECK(gnutls_priority_set(g_env->g_session, g_context->priority_cache),
1138  "gnutls_priority_set");
1139  gnutls_handshake_set_timeout(g_env->g_session,
1140  GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
1141 
1142  return g_env;
1143 
1144 fail:
1145  if (g_env)
1146  gnutls_free(g_env);
1147  return NULL;
1148 }
1149 
1150 static void
1151 coap_dtls_free_gnutls_env(coap_gnutls_context_t *g_context,
1152  coap_gnutls_env_t *g_env,
1153  int unreliable)
1154 {
1155  if (g_env) {
1156  /* It is suggested not to use GNUTLS_SHUT_RDWR in DTLS
1157  * connections because the peer's closure message might
1158  * be lost */
1159  gnutls_bye(g_env->g_session, unreliable ?
1160  GNUTLS_SHUT_WR : GNUTLS_SHUT_RDWR);
1161  gnutls_deinit(g_env->g_session);
1162  g_env->g_session = NULL;
1163  if (g_context->psk_pki_enabled & IS_PSK) {
1164  if (g_context->psk_pki_enabled & IS_CLIENT) {
1165  gnutls_psk_free_client_credentials(g_env->psk_cl_credentials);
1166  }
1167  else {
1168  gnutls_psk_free_server_credentials(g_env->psk_sv_credentials);
1169  }
1170  }
1171  if (g_context->psk_pki_enabled & IS_PKI) {
1172  gnutls_certificate_free_credentials(g_env->pki_credentials);
1173  }
1174  gnutls_free(g_env);
1175  }
1176 }
1177 
1178 void *coap_dtls_new_server_session(coap_session_t *c_session) {
1179  coap_gnutls_env_t *g_env =
1180  (coap_gnutls_env_t *)c_session->endpoint->hello.tls;
1181 
1182  gnutls_transport_set_ptr(g_env->g_session, c_session);
1183  /* For the next one */
1184  c_session->endpoint->hello.tls = NULL;
1185 
1186  return g_env;
1187 }
1188 
1189 static void log_last_alert(gnutls_session_t g_session) {
1190  int last_alert = gnutls_alert_get(g_session);
1191 
1192  coap_log(LOG_WARNING, "Received alert '%d': '%s'\n",
1193  last_alert, gnutls_alert_get_name(last_alert));
1194 }
1195 
1196 /*
1197  * return -1 failure
1198  * 0 not completed
1199  * 1 established
1200  */
1201 static int
1202 do_gnutls_handshake(coap_session_t *c_session, coap_gnutls_env_t *g_env) {
1203  int ret;
1204 
1205  ret = gnutls_handshake(g_env->g_session);
1206  switch (ret) {
1207  case GNUTLS_E_SUCCESS:
1208  g_env->established = 1;
1209  coap_log(LOG_DEBUG, "* %s: GnuTLS established\n",
1210  coap_session_str(c_session));
1211  ret = 1;
1212  break;
1213  case GNUTLS_E_INTERRUPTED:
1214  errno = EINTR;
1215  ret = 0;
1216  break;
1217  case GNUTLS_E_AGAIN:
1218  errno = EAGAIN;
1219  ret = 0;
1220  break;
1221  case GNUTLS_E_INSUFFICIENT_CREDENTIALS:
1223  "Insufficient credentials provided.\n");
1224  ret = -1;
1225  break;
1226  case GNUTLS_E_FATAL_ALERT_RECEIVED:
1227  log_last_alert(g_env->g_session);
1228  c_session->dtls_event = COAP_EVENT_DTLS_CLOSED;
1229  ret = -1;
1230  break;
1231  case GNUTLS_E_WARNING_ALERT_RECEIVED:
1232  log_last_alert(g_env->g_session);
1233  c_session->dtls_event = COAP_EVENT_DTLS_ERROR;
1234  ret = 0;
1235  break;
1236  case GNUTLS_E_DECRYPTION_FAILED:
1238  "do_gnutls_handshake: session establish "
1239  "returned %d: '%s'\n",
1240  ret, gnutls_strerror(ret));
1241  G_ACTION(gnutls_alert_send(g_env->g_session, GNUTLS_AL_FATAL,
1242  GNUTLS_A_DECRYPT_ERROR));
1243  c_session->dtls_event = COAP_EVENT_DTLS_CLOSED;
1244  ret = -1;
1245  break;
1246  case GNUTLS_E_UNKNOWN_CIPHER_SUITE:
1247  /* fall through */
1248  case GNUTLS_E_TIMEDOUT:
1249  c_session->dtls_event = COAP_EVENT_DTLS_CLOSED;
1250  ret = -1;
1251  break;
1252  default:
1254  "do_gnutls_handshake: session establish "
1255  "returned %d: '%s'\n",
1256  ret, gnutls_strerror(ret));
1257  ret = -1;
1258  break;
1259  }
1260  return ret;
1261 }
1262 
1263 void *coap_dtls_new_client_session(coap_session_t *c_session) {
1264  coap_gnutls_env_t *g_env = coap_dtls_new_gnutls_env(c_session, GNUTLS_CLIENT);
1265  int ret;
1266 
1267  if (g_env) {
1268  ret = do_gnutls_handshake(c_session, g_env);
1269  if (ret == -1) {
1270  coap_dtls_free_gnutls_env(c_session->context->dtls_context,
1271  g_env,
1272  COAP_PROTO_NOT_RELIABLE(c_session->proto));
1273  return NULL;
1274  }
1275  }
1276  return g_env;
1277 }
1278 
1279 void coap_dtls_free_session(coap_session_t *c_session) {
1280  if (c_session && c_session->context) {
1281  coap_dtls_free_gnutls_env(c_session->context->dtls_context,
1282  c_session->tls, COAP_PROTO_NOT_RELIABLE(c_session->proto));
1283  c_session->tls = NULL;
1284  }
1285 }
1286 
1288  coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
1289  int ret;
1290 
1291  if (g_env)
1292  G_CHECK(gnutls_dtls_set_data_mtu(g_env->g_session, c_session->mtu),
1293  "gnutls_dtls_set_data_mtu");
1294 fail:
1295  ;;
1296 }
1297 
1298 /*
1299  * return +ve data amount
1300  * 0 no more
1301  * -1 error
1302  */
1303 int coap_dtls_send(coap_session_t *c_session,
1304  const uint8_t *data, size_t data_len) {
1305  int ret;
1306  coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
1307 
1308  assert(g_env != NULL);
1309 
1310  c_session->dtls_event = -1;
1311  if (g_env->established) {
1312  ret = gnutls_record_send(g_env->g_session, data, data_len);
1313 
1314  if (ret <= 0) {
1315  switch (ret) {
1316  case GNUTLS_E_AGAIN:
1317  ret = 0;
1318  break;
1319  case GNUTLS_E_FATAL_ALERT_RECEIVED:
1320  log_last_alert(g_env->g_session);
1321  c_session->dtls_event = COAP_EVENT_DTLS_ERROR;
1322  ret = -1;
1323  break;
1324  default:
1325  ret = -1;
1326  break;
1327  }
1328  if (ret == -1) {
1329  coap_log(LOG_WARNING, "coap_dtls_send: cannot send PDU\n");
1330  }
1331  }
1332  }
1333  else {
1334  ret = do_gnutls_handshake(c_session, g_env);
1335  if (ret == 1) {
1336  /* Just connected, so send the data */
1337  return coap_dtls_send(c_session, data, data_len);
1338  }
1339  ret = -1;
1340  }
1341 
1342  if (c_session->dtls_event >= 0) {
1343  coap_handle_event(c_session->context, c_session->dtls_event, c_session);
1344  if (c_session->dtls_event == COAP_EVENT_DTLS_ERROR ||
1345  c_session->dtls_event == COAP_EVENT_DTLS_CLOSED) {
1347  ret = -1;
1348  }
1349  }
1350 
1351  return ret;
1352 }
1353 
1354 int coap_dtls_is_context_timeout(void) {
1355  return 1;
1356 }
1357 
1358 coap_tick_t coap_dtls_get_context_timeout(void *dtls_context UNUSED) {
1359  return 0;
1360 }
1361 
1363  return 0;
1364 }
1365 
1366 void coap_dtls_handle_timeout(coap_session_t *c_session UNUSED) {
1367 }
1368 
1369 /*
1370  * return +ve data amount
1371  * 0 no more
1372  * -1 error
1373  */
1374 int
1376  const uint8_t *data,
1377  size_t data_len
1378 ) {
1379  coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
1380  int ret = 0;
1381  coap_ssl_t *ssl_data = &g_env->coap_ssl_data;
1382 
1384 
1385  assert(g_env != NULL);
1386 
1387  if (ssl_data->pdu_len)
1388  coap_log(LOG_INFO, "** %s: Previous data not read %u bytes\n",
1389  coap_session_str(c_session), ssl_data->pdu_len);
1390  ssl_data->pdu = data;
1391  ssl_data->pdu_len = (unsigned)data_len;
1392 
1393  c_session->dtls_event = -1;
1394  if (g_env->established) {
1395  if (c_session->state == COAP_SESSION_STATE_HANDSHAKE) {
1397  c_session);
1398  gnutls_transport_set_ptr(g_env->g_session, c_session);
1399  coap_session_connected(c_session);
1400  }
1401  ret = gnutls_record_recv(g_env->g_session, pdu, (int)sizeof(pdu));
1402  if (ret > 0) {
1403  return coap_handle_dgram(c_session->context, c_session, pdu, (size_t)ret);
1404  }
1405  else if (ret == 0) {
1406  c_session->dtls_event = COAP_EVENT_DTLS_CLOSED;
1407  }
1408  else {
1410  "coap_dtls_receive: gnutls_record_recv returned %d\n", ret);
1411  ret = -1;
1412  }
1413  }
1414  else {
1415  ret = do_gnutls_handshake(c_session, g_env);
1416  if (ret == 1) {
1417  coap_session_connected(c_session);
1418  }
1419  else {
1420  ret = -1;
1421  }
1422  }
1423 
1424  if (c_session->dtls_event >= 0) {
1425  coap_handle_event(c_session->context, c_session->dtls_event, c_session);
1426  if (c_session->dtls_event == COAP_EVENT_DTLS_ERROR ||
1427  c_session->dtls_event == COAP_EVENT_DTLS_CLOSED) {
1429  ret = -1;
1430  }
1431  }
1432 
1433  return ret;
1434 }
1435 
1436 /*
1437  * return 0 failed
1438  * 1 passed
1439  */
1440 int
1441 coap_dtls_hello(coap_session_t *c_session,
1442  const uint8_t *data,
1443  size_t data_len
1444 ) {
1445  coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
1446  coap_ssl_t *ssl_data = g_env ? &g_env->coap_ssl_data : NULL;
1447  int ret;
1448 
1449  if (!g_env) {
1450  g_env = coap_dtls_new_gnutls_env(c_session, GNUTLS_SERVER);
1451  if (g_env) {
1452  c_session->tls = g_env;
1453  ssl_data = &g_env->coap_ssl_data;
1454  ssl_data->pdu = data;
1455  ssl_data->pdu_len = (unsigned)data_len;
1456  gnutls_dtls_set_data_mtu(g_env->g_session, c_session->mtu);
1457  ret = do_gnutls_handshake(c_session, g_env);
1458  if (ret == 1 || g_env->seen_client_hello) {
1459  /* The test for seen_client_hello gives the ability to setup a new
1460  coap_session to continue the gnutls_handshake past the client hello
1461  and safely allow updating of the g_env & g_session and separately
1462  letting a new session cleanly start up using endpoint->hello.
1463  */
1464  g_env->seen_client_hello = 0;
1465  return 1;
1466  }
1467  }
1468  return 0;
1469  }
1470 
1471  ssl_data->pdu = data;
1472  ssl_data->pdu_len = (unsigned)data_len;
1473 
1474  ret = do_gnutls_handshake(c_session, g_env);
1475  if (ret == 1 || g_env->seen_client_hello) {
1476  /* The test for seen_client_hello gives the ability to setup a new
1477  coap_session to continue the gnutls_handshake past the client hello
1478  and safely allow updating of the g_env & g_session and separately
1479  letting a new session cleanly start up using endpoint->hello.
1480  */
1481  g_env->seen_client_hello = 0;
1482  return 1;
1483  }
1484  return 0;
1485 }
1486 
1487 unsigned int coap_dtls_get_overhead(coap_session_t *c_session UNUSED) {
1488  return 37;
1489 }
1490 
1491 /*
1492  * return +ve data amount
1493  * 0 no more
1494  * -1 error (error in errno)
1495  */
1496 static ssize_t
1497 coap_sock_read(gnutls_transport_ptr_t context, void *out, size_t outl) {
1498  int ret = 0;
1499  coap_session_t *c_session = (struct coap_session_t *)context;
1500 
1501  if (out != NULL) {
1502  ret = (int)coap_socket_read(&c_session->sock, out, outl);
1503  if (ret == 0) {
1504  errno = EAGAIN;
1505  ret = -1;
1506  }
1507  }
1508  return ret;
1509 }
1510 
1511 /*
1512  * return +ve data amount
1513  * 0 no more
1514  * -1 error (error in errno)
1515  */
1516 static ssize_t
1517 coap_sock_write(gnutls_transport_ptr_t context, const void *in, size_t inl) {
1518  int ret = 0;
1519  coap_session_t *c_session = (struct coap_session_t *)context;
1520 
1521  ret = (int)coap_socket_write(&c_session->sock, in, inl);
1522  if (ret == 0) {
1523  errno = EAGAIN;
1524  ret = -1;
1525  }
1526  return ret;
1527 }
1528 
1529 void *coap_tls_new_client_session(coap_session_t *c_session, int *connected) {
1530  coap_gnutls_env_t *g_env = gnutls_malloc(sizeof(coap_gnutls_env_t));
1531  coap_gnutls_context_t *g_context =
1532  ((coap_gnutls_context_t *)c_session->context->dtls_context);
1533  int flags = GNUTLS_CLIENT;
1534  int ret;
1535 
1536  if (!g_env) {
1537  return NULL;
1538  }
1539  memset(g_env, 0, sizeof(struct coap_gnutls_env_t));
1540 
1541  *connected = 0;
1542  G_CHECK(gnutls_init(&g_env->g_session, flags), "gnutls_init");
1543 
1544  gnutls_transport_set_pull_function(g_env->g_session, coap_sock_read);
1545  gnutls_transport_set_push_function(g_env->g_session, coap_sock_write);
1546  gnutls_transport_set_pull_timeout_function(g_env->g_session, receive_timeout);
1547  /* So we can track the coap_session_t in callbacks */
1548  gnutls_transport_set_ptr(g_env->g_session, c_session);
1549 
1550  setup_client_ssl_session(c_session, g_env);
1551 
1552  gnutls_priority_set(g_env->g_session, g_context->priority_cache);
1553  gnutls_handshake_set_timeout(g_env->g_session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
1554 
1555  ret = do_gnutls_handshake(c_session, g_env);
1556  if (ret == 1) {
1557  *connected = 1;
1558  coap_handle_event(c_session->context, COAP_EVENT_DTLS_CONNECTED, c_session);
1559  coap_session_connected(c_session);
1560  }
1561  return g_env;
1562 
1563 fail:
1564  if (g_env)
1565  gnutls_free(g_env);
1566  return NULL;
1567 }
1568 
1569 void *coap_tls_new_server_session(coap_session_t *c_session, int *connected) {
1570  coap_gnutls_env_t *g_env = gnutls_malloc(sizeof(coap_gnutls_env_t));
1571  coap_gnutls_context_t *g_context =
1572  ((coap_gnutls_context_t *)c_session->context->dtls_context);
1573  int flags = GNUTLS_SERVER;
1574  int ret;
1575 
1576  if (!g_env)
1577  return NULL;
1578  memset(g_env, 0, sizeof(struct coap_gnutls_env_t));
1579 
1580  *connected = 0;
1581  G_CHECK(gnutls_init(&g_env->g_session, flags), "gnutls_init");
1582 
1583  gnutls_transport_set_pull_function(g_env->g_session, coap_sock_read);
1584  gnutls_transport_set_push_function(g_env->g_session, coap_sock_write);
1585  gnutls_transport_set_pull_timeout_function(g_env->g_session, receive_timeout);
1586  /* So we can track the coap_session_t in callbacks */
1587  gnutls_transport_set_ptr(g_env->g_session, c_session);
1588 
1589  setup_server_ssl_session(c_session, g_env);
1590 
1591  gnutls_priority_set(g_env->g_session, g_context->priority_cache);
1592  gnutls_handshake_set_timeout(g_env->g_session,
1593  GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
1594 
1595  c_session->tls = g_env;
1596  ret = do_gnutls_handshake(c_session, g_env);
1597  if (ret == 1) {
1598  *connected = 1;
1599  }
1600  return g_env;
1601 
1602 fail:
1603  return NULL;
1604 }
1605 
1606 void coap_tls_free_session(coap_session_t *c_session) {
1607  coap_dtls_free_session(c_session);
1608  return;
1609 }
1610 
1611 /*
1612  * return +ve data amount
1613  * 0 no more
1614  * -1 error (error in errno)
1615  */
1616 ssize_t coap_tls_write(coap_session_t *c_session,
1617  const uint8_t *data,
1618  size_t data_len
1619 ) {
1620  int ret;
1621  coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
1622 
1623  assert(g_env != NULL);
1624 
1625  c_session->dtls_event = -1;
1626  if (g_env->established) {
1627  ret = gnutls_record_send(g_env->g_session, data, data_len);
1628 
1629  if (ret <= 0) {
1630  switch (ret) {
1631  case GNUTLS_E_AGAIN:
1632  ret = 0;
1633  break;
1634  case GNUTLS_E_FATAL_ALERT_RECEIVED:
1635  log_last_alert(g_env->g_session);
1636  c_session->dtls_event = COAP_EVENT_DTLS_ERROR;
1637  ret = -1;
1638  break;
1639  default:
1641  "coap_tls_write: gnutls_record_send "
1642  "returned %d: '%s'\n",
1643  ret, gnutls_strerror(ret));
1644  ret = -1;
1645  break;
1646  }
1647  if (ret == -1) {
1648  coap_log(LOG_WARNING, "coap_dtls_send: cannot send PDU\n");
1649  }
1650  }
1651  }
1652  else {
1653  ret = do_gnutls_handshake(c_session, g_env);
1654  if (ret == 1) {
1656  c_session);
1657  coap_session_send_csm(c_session);
1658  }
1659  else {
1660  ret = -1;
1661  }
1662  }
1663 
1664  if (c_session->dtls_event >= 0) {
1665  coap_handle_event(c_session->context, c_session->dtls_event, c_session);
1666  if (c_session->dtls_event == COAP_EVENT_DTLS_ERROR ||
1667  c_session->dtls_event == COAP_EVENT_DTLS_CLOSED) {
1669  ret = -1;
1670  }
1671  }
1672 
1673  return ret;
1674 }
1675 
1676 /*
1677  * return +ve data amount
1678  * 0 no more
1679  * -1 error (error in errno)
1680  */
1681 ssize_t coap_tls_read(coap_session_t *c_session,
1682  uint8_t *data,
1683  size_t data_len
1684 ) {
1685  coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls;
1686  int ret;
1687 
1688  if (!g_env)
1689  return -1;
1690 
1691  c_session->dtls_event = -1;
1692  if (!g_env->established) {
1693  ret = do_gnutls_handshake(c_session, g_env);
1694  if (ret == 1) {
1696  c_session);
1697  coap_session_send_csm(c_session);
1698  }
1699  }
1700  if (g_env->established) {
1701  ret = gnutls_record_recv(g_env->g_session, data, (int)data_len);
1702  if (ret <= 0) {
1703  switch (ret) {
1704  case 0:
1705  c_session->dtls_event = COAP_EVENT_DTLS_CLOSED;
1706  break;
1707  case GNUTLS_E_AGAIN:
1708  errno = EAGAIN;
1709  ret = 0;
1710  break;
1711  default:
1713  "coap_tls_read: gnutls_record_recv "
1714  "returned %d: '%s'\n",
1715  ret, gnutls_strerror(ret));
1716  ret = -1;
1717  break;
1718  }
1719  }
1720  }
1721 
1722  if (c_session->dtls_event >= 0) {
1723  coap_handle_event(c_session->context, c_session->dtls_event, c_session);
1724  if (c_session->dtls_event == COAP_EVENT_DTLS_ERROR ||
1725  c_session->dtls_event == COAP_EVENT_DTLS_CLOSED) {
1727  ret = -1;
1728  }
1729  }
1730  return ret;
1731 }
1732 
1733 #else /* !HAVE_LIBGNUTLS */
1734 
1735 #ifdef __clang__
1736 /* Make compilers happy that do not like empty modules. As this function is
1737  * never used, we ignore -Wunused-function at the end of compiling this file
1738  */
1739 #pragma GCC diagnostic ignored "-Wunused-function"
1740 #endif
1741 static inline void dummy(void) {
1742 }
1743 
1744 #endif /* !HAVE_LIBGNUTLS */
unsigned mtu
path or CSM mtu
Definition: coap_session.h:63
void coap_dtls_set_log_level(int level)
Sets the (D)TLS logging level to the specified level.
Definition: coap_notls.c:76
static void dummy(void)
Definition: coap_gnutls.c:1741
void coap_session_send_csm(coap_session_t *session)
Notify session transport has just connected and CSM exchange can now start.
Definition: coap_session.c:290
int coap_dtls_hello(coap_session_t *session UNUSED, const uint8_t *data UNUSED, size_t data_len UNUSED)
Definition: coap_notls.c:140
#define COAP_RXBUFFER_SIZE
Definition: coap_io.h:19
void coap_tls_free_session(coap_session_t *coap_session UNUSED)
Definition: coap_notls.c:159
struct coap_context_t * context
session&#39;s context
Definition: coap_session.h:70
The PKI key type is ASN.1 (DER)
Definition: coap_dtls.h:133
void * tls
security parameters
Definition: coap_session.h:71
coap_pki_key_t key_type
key format type
Definition: coap_dtls.h:162
#define COAP_SESSION_STATE_HANDSHAKE
Definition: coap_session.h:52
int coap_dtls_receive(coap_session_t *session UNUSED, const uint8_t *data UNUSED, size_t data_len UNUSED)
Definition: coap_notls.c:132
coap_fd_t fd
Definition: coap_io.h:46
int coap_dtls_context_check_keys_enabled(coap_context_t *ctx UNUSED)
Definition: coap_notls.c:65
ssize_t coap_tls_read(coap_session_t *session UNUSED, uint8_t *data UNUSED, size_t data_len UNUSED)
Definition: coap_notls.c:169
int coap_dtls_get_log_level(void)
Get the current (D)TLS logging.
Definition: coap_notls.c:81
void * coap_dtls_new_client_session(coap_session_t *session UNUSED)
Definition: coap_notls.c:98
int coap_dtls_is_supported(void)
Check whether DTLS is available.
Definition: coap_notls.c:23
void coap_dtls_free_context(void *handle UNUSED)
Definition: coap_notls.c:91
void * coap_tls_new_server_session(coap_session_t *session UNUSED, int *connected UNUSED)
Definition: coap_notls.c:155
ssize_t coap_tls_write(coap_session_t *session UNUSED, const uint8_t *data UNUSED, size_t data_len UNUSED)
Definition: coap_notls.c:162
ssize_t coap_socket_read(coap_socket_t *sock, uint8_t *data, size_t data_len)
Definition: coap_io.c:665
ssize_t coap_session_send(coap_session_t *session, const uint8_t *data, size_t datalen)
Function interface for datagram data transmission.
Definition: coap_session.c:218
int dtls_event
Tracking any (D)TLS events on this sesison.
Definition: coap_session.h:93
uint8_t verify_peer_cert
Set to 1 to support this version of the struct.
Definition: coap_dtls.h:195
Debug.
Definition: coap_debug.h:49
uint64_t version
(D)TLS runtime Library Version
Definition: coap_dtls.h:48
size_t(* get_client_psk)(const coap_session_t *session, const uint8_t *hint, size_t hint_len, uint8_t *identity, size_t *identity_len, size_t max_identity_len, uint8_t *psk, size_t max_psk_len)
Definition: net.h:203
void * coap_tls_new_client_session(coap_session_t *session UNUSED, int *connected UNUSED)
Definition: coap_notls.c:151
const char * coap_session_str(const coap_session_t *session)
Get session description.
Definition: coap_session.c:943
int coap_tls_is_supported(void)
Check whether TLS is available.
Definition: coap_notls.c:28
int coap_dtls_context_set_pki(coap_context_t *ctx UNUSED, coap_dtls_pki_t *setup_data UNUSED, int server UNUSED)
Definition: coap_notls.c:41
coap_tls_version_t * coap_get_tls_library_version(void)
Determine the type and version of the underlying (D)TLS library.
Definition: coap_notls.c:33
const char * private_key
File location of Private Key in PEM format.
Definition: coap_dtls.h:142
int type
Library type.
Definition: coap_dtls.h:49
uint8_t require_peer_cert
1 if peer cert is required
Definition: coap_dtls.h:196
coap_proto_t proto
protocol used
Definition: coap_session.h:58
coap_dtls_key_t pki_key
PKI key definition.
Definition: coap_dtls.h:240
coap_pki_key_pem_t pem
for PEM keys
Definition: coap_dtls.h:164
char * client_sni
If not NULL, SNI to use in client TLS setup.
Definition: coap_dtls.h:236
Warning.
Definition: coap_debug.h:46
uint64_t coap_tick_t
This data type represents internal timer ticks with COAP_TICKS_PER_SECOND resolution.
Definition: coap_time.h:85
#define assert(...)
Definition: mem.c:18
coap_session_t hello
special session of DTLS hello messages
Definition: coap_session.h:309
int coap_dtls_context_set_psk(coap_context_t *ctx UNUSED, const char *hint UNUSED, int server UNUSED)
Definition: coap_notls.c:57
The structure that holds the PKI key information.
Definition: coap_dtls.h:161
unsigned int coap_dtls_get_overhead(coap_session_t *session UNUSED)
Definition: coap_notls.c:147
const uint8_t * public_cert
ASN1 (DER) Public Cert.
Definition: coap_dtls.h:150
const char * ca_file
File location of Common CA in PEM format.
Definition: coap_dtls.h:140
size_t ca_cert_len
ASN1 CA Cert length.
Definition: coap_dtls.h:152
The PKI key type is PEM.
Definition: coap_dtls.h:132
coap_socket_t sock
socket object for the session, if any
Definition: coap_session.h:68
ssize_t coap_socket_write(coap_socket_t *sock, const uint8_t *data, size_t data_len)
Definition: coap_io.c:635
static int dtls_log_level
Definition: coap_notls.c:70
The structure used for returning the underlying (D)TLS library information.
Definition: coap_dtls.h:47
#define COAP_EVENT_DTLS_CLOSED
(D)TLS events for COAP_PROTO_DTLS and COAP_PROTO_TLS
Definition: coap_event.h:33
int coap_handle_dgram(coap_context_t *ctx, coap_session_t *session, uint8_t *msg, size_t msg_len)
Parses and interprets a CoAP datagram with context ctx.
Definition: net.c:1369
#define COAP_PROTO_TLS
Definition: pdu.h:347
uint8_t cert_chain_validation
1 if to check cert_chain_verify_depth
Definition: coap_dtls.h:199
Error.
Definition: coap_debug.h:45
void * dtls_context
Definition: net.h:207
union coap_dtls_key_t::@1 key
coap_session_state_t state
current state of relationaship with peer
Definition: coap_session.h:60
const uint8_t * private_key
ASN1 (DER) Private Key.
Definition: coap_dtls.h:151
uint8_t check_cert_revocation
1 if revocation checks wanted
Definition: coap_dtls.h:201
#define COAP_TLS_LIBRARY_GNUTLS
Using GnuTLS library.
Definition: coap_dtls.h:41
void coap_dtls_free_session(coap_session_t *coap_session UNUSED)
Definition: coap_notls.c:102
#define COAP_EVENT_DTLS_ERROR
Definition: coap_event.h:36
#define COAP_EVENT_DTLS_CONNECTED
Definition: coap_event.h:34
int coap_handle_event(coap_context_t *context, coap_event_t event, coap_session_t *session)
Invokes the event handler of context for the given event and data.
Definition: net.c:2310
void * coap_dtls_new_server_session(coap_session_t *session UNUSED)
Definition: coap_notls.c:94
void coap_session_connected(coap_session_t *session)
Notify session that it has just connected or reconnected.
Definition: coap_session.c:326
The structure used for defining the PKI setup data to be used.
Definition: coap_dtls.h:191
int coap_dtls_context_set_pki_root_cas(struct coap_context_t *ctx UNUSED, const char *ca_file UNUSED, const char *ca_path UNUSED)
Definition: coap_notls.c:49
int coap_dtls_send(coap_session_t *session UNUSED, const uint8_t *data UNUSED, size_t data_len UNUSED)
Definition: coap_notls.c:109
uint8_t cert_chain_verify_depth
recommended depth is 3
Definition: coap_dtls.h:200
coap_tick_t coap_dtls_get_timeout(coap_session_t *session UNUSED)
Definition: coap_notls.c:124
size_t(* get_server_psk)(const coap_session_t *session, const uint8_t *identity, size_t identity_len, uint8_t *psk, size_t max_psk_len)
Definition: net.h:204
void coap_dtls_handle_timeout(coap_session_t *session UNUSED)
Definition: coap_notls.c:128
const char * public_cert
File location of Public Cert in PEM format.
Definition: coap_dtls.h:141
void coap_dtls_startup(void)
Initialize the underlying (D)TLS Library layer.
Definition: coap_notls.c:72
#define COAP_PROTO_NOT_RELIABLE(p)
Definition: coap_session.h:35
void coap_session_disconnected(coap_session_t *session, coap_nack_reason_t reason)
Notify session that it has failed.
Definition: coap_session.c:383
#define coap_log(level,...)
Logging function.
Definition: coap_debug.h:122
const uint8_t * ca_cert
ASN1 (DER) Common CA Cert.
Definition: coap_dtls.h:149
unsigned char uint8_t
Definition: uthash.h:79
Pseudo Random Numbers.
size_t public_cert_len
ASN1 Public Cert length.
Definition: coap_dtls.h:153
struct coap_endpoint_t * endpoint
session&#39;s endpoint
Definition: coap_session.h:69
#define UNUSED
Definition: coap_notls.c:19
coap_tick_t coap_dtls_get_context_timeout(void *dtls_context UNUSED)
Definition: coap_notls.c:120
uint64_t built_version
(D)TLS Built against Library Version
Definition: coap_dtls.h:50
void coap_dtls_session_update_mtu(coap_session_t *session UNUSED)
Definition: coap_notls.c:105
Information.
Definition: coap_debug.h:48
void * coap_dtls_new_context(struct coap_context_t *coap_context UNUSED)
Definition: coap_notls.c:86
The CoAP stack&#39;s global state is stored in a coap_context_t object.
Definition: net.h:148
int coap_dtls_is_context_timeout(void)
Check if timeout is handled per CoAP session or per CoAP context.
Definition: coap_notls.c:116
size_t private_key_len
ASN1 Private Key length.
Definition: coap_dtls.h:154
coap_pki_key_asn1_t asn1
for ASN.1 (DER) keys
Definition: coap_dtls.h:165