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