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