coap_openssl.c
Go to the documentation of this file.
/*
* coap_openssl.c -- Datagram Transport Layer Support for libcoap with openssl
*
* Copyright (C) 2017 Jean-Claude Michelou <jcm@spinetix.com>
* Copyright (C) 2018 Jon Shallow <supjps-libcoap@jpshallow.com>
*
* This file is part of the CoAP library libcoap. Please see README for terms
* of use.
*/
#include "coap_internal.h"
#ifdef HAVE_OPENSSL
/*
* OpenSSL 1.1.0 has support for making decisions during receipt of
* the Client Hello - the call back function is set up using
* SSL_CTX_set_tlsext_servername_callback() which is called later in the
* Client Hello processing - but called every Client Hello.
* Certificates and Preshared Keys have to be set up in the SSL CTX before
* SSL_Accept() is called, making the code messy to decide whether this is a
* PKI or PSK incoming request to handle things accordingly if both are
* defined. SNI has to create a new SSL CTX to handle different server names
* with different crtificates.
*
* OpenSSL 1.1.1 introduces a new function SSL_CTX_set_client_hello_cb().
* The call back is invoked early on in the Client Hello processing giving
* the ability to easily use different Preshared Keys, Certificates etc.
* Certificates do not have to be set up in the SSL CTX before SSL_Accept is
* called.
* Later in the Client Hello code, the callback for
* SSL_CTX_set_tlsext_servername_callback() is still called, but only if SNI
* is being used by the client, so cannot be used for doing things the
* OpenSSL 1.1.0 way.
*
* OpenSSL 1.1.1 supports TLS1.3.
*
* Consequently, this code has to have compile time options to include /
* exclude code based on whether compiled against 1.1.0 or 1.1.1, as well as
* have additional run time checks.
*
*/
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/hmac.h>
#include <openssl/x509v3.h>
#ifdef COAP_EPOLL_SUPPORT
# include <sys/epoll.h>
#endif /* COAP_EPOLL_SUPPORT */
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#error Must be compiled against OpenSSL 1.1.0 or later
#endif
#ifdef __GNUC__
#define UNUSED __attribute__((unused))
#else
#define UNUSED
#endif /* __GNUC__ */
/* RFC6091/RFC7250 */
#ifndef TLSEXT_TYPE_client_certificate_type
#define TLSEXT_TYPE_client_certificate_type 19
#endif
#ifndef TLSEXT_TYPE_server_certificate_type
#define TLSEXT_TYPE_server_certificate_type 20
#endif
#ifndef COAP_OPENSSL_CIPHERS
#if OPENSSL_VERSION_NUMBER >= 0x10101000L
#define COAP_OPENSSL_CIPHERS "TLSv1.3:TLSv1.2:!NULL"
#else /* OPENSSL_VERSION_NUMBER < 0x10101000L */
#define COAP_OPENSSL_CIPHERS "TLSv1.2:!NULL"
#endif /* OPENSSL_VERSION_NUMBER < 0x10101000L */
#endif /*COAP_OPENSSL_CIPHERS */
#ifndef COAP_OPENSSL_PSK_CIPHERS
#define COAP_OPENSSL_PSK_CIPHERS "PSK:!NULL"
#endif /*COAP_OPENSSL_PSK_CIPHERS */
/* This structure encapsulates the OpenSSL context object. */
typedef struct coap_dtls_context_t {
SSL_CTX *ctx;
SSL *ssl; /* OpenSSL object for listening to connection requests */
HMAC_CTX *cookie_hmac;
BIO_METHOD *meth;
BIO_ADDR *bio_addr;
} coap_dtls_context_t;
typedef struct coap_tls_context_t {
SSL_CTX *ctx;
BIO_METHOD *meth;
} coap_tls_context_t;
#define IS_PSK 0x1
#define IS_PKI 0x2
typedef struct sni_entry {
char *sni;
#if OPENSSL_VERSION_NUMBER < 0x10101000L
SSL_CTX *ctx;
#else /* OPENSSL_VERSION_NUMBER >= 0x10101000L */
coap_dtls_key_t pki_key;
#endif /* OPENSSL_VERSION_NUMBER >= 0x10101000L */
} sni_entry;
typedef struct coap_openssl_context_t {
coap_dtls_context_t dtls;
coap_tls_context_t tls;
coap_dtls_pki_t setup_data;
int psk_pki_enabled;
size_t sni_count;
sni_entry *sni_entry_list;
} coap_openssl_context_t;
int coap_dtls_is_supported(void) {
if (SSLeay() < 0x10100000L) {
coap_log(LOG_WARNING, "OpenSSL version 1.1.0 or later is required\n");
return 0;
}
#if OPENSSL_VERSION_NUMBER >= 0x10101000L
/*
* For 1.1.1, we need to use SSL_CTX_set_client_hello_cb()
* which is not in 1.1.0 instead of SSL_CTX_set_tlsext_servername_callback()
*
* However, there could be a runtime undefined external reference error
* as SSL_CTX_set_client_hello_cb() is not there in 1.1.0.
*/
if (SSLeay() < 0x10101000L) {
coap_log(LOG_WARNING, "OpenSSL version 1.1.1 or later is required\n");
return 0;
}
#endif /* OPENSSL_VERSION_NUMBER >= 0x10101000L */
return 1;
}
int coap_tls_is_supported(void) {
if (SSLeay() < 0x10100000L) {
coap_log(LOG_WARNING, "OpenSSL version 1.1.0 or later is required\n");
return 0;
}
#if OPENSSL_VERSION_NUMBER >= 0x10101000L
if (SSLeay() < 0x10101000L) {
coap_log(LOG_WARNING, "OpenSSL version 1.1.1 or later is required\n");
return 0;
}
#endif /* OPENSSL_VERSION_NUMBER >= 0x10101000L */
return 1;
}
coap_tls_version_t *
coap_get_tls_library_version(void) {
static coap_tls_version_t version;
version.version = SSLeay();
version.built_version = OPENSSL_VERSION_NUMBER;
version.type = COAP_TLS_LIBRARY_OPENSSL;
return &version;
}
void coap_dtls_startup(void) {
SSL_load_error_strings();
SSL_library_init();
}
static int dtls_log_level = 0;
void coap_dtls_set_log_level(int level) {
dtls_log_level = level;
}
int coap_dtls_get_log_level(void) {
return dtls_log_level;
}
typedef struct coap_ssl_st {
coap_session_t *session;
const void *pdu;
unsigned pdu_len;
unsigned peekmode;
coap_tick_t timeout;
} coap_ssl_data;
static int coap_dgram_create(BIO *a) {
coap_ssl_data *data = NULL;
data = malloc(sizeof(coap_ssl_data));
if (data == NULL)
return 0;
BIO_set_init(a, 1);
BIO_set_data(a, data);
memset(data, 0x00, sizeof(coap_ssl_data));
return 1;
}
static int coap_dgram_destroy(BIO *a) {
coap_ssl_data *data;
if (a == NULL)
return 0;
data = (coap_ssl_data *)BIO_get_data(a);
if (data != NULL)
free(data);
return 1;
}
static int coap_dgram_read(BIO *a, char *out, int outl) {
int ret = 0;
coap_ssl_data *data = (coap_ssl_data *)BIO_get_data(a);
if (out != NULL) {
if (data != NULL && data->pdu_len > 0) {
if (outl < (int)data->pdu_len) {
memcpy(out, data->pdu, outl);
ret = outl;
} else {
memcpy(out, data->pdu, data->pdu_len);
ret = (int)data->pdu_len;
}
if (!data->peekmode) {
data->pdu_len = 0;
data->pdu = NULL;
}
} else {
ret = -1;
}
BIO_clear_retry_flags(a);
if (ret < 0)
BIO_set_retry_read(a);
}
return ret;
}
static int coap_dgram_write(BIO *a, const char *in, int inl) {
int ret = 0;
coap_ssl_data *data = (coap_ssl_data *)BIO_get_data(a);
if (data->session) {
if (data->session->sock.flags == COAP_SOCKET_EMPTY && data->session->endpoint == NULL) {
/* socket was closed on client due to error */
BIO_clear_retry_flags(a);
return -1;
}
ret = (int)coap_session_send(data->session, (const uint8_t *)in, (size_t)inl);
BIO_clear_retry_flags(a);
if (ret <= 0)
BIO_set_retry_write(a);
} else {
BIO_clear_retry_flags(a);
ret = -1;
}
return ret;
}
static int coap_dgram_puts(BIO *a, const char *pstr) {
return coap_dgram_write(a, pstr, (int)strlen(pstr));
}
static long coap_dgram_ctrl(BIO *a, int cmd, long num, void *ptr) {
long ret = 1;
coap_ssl_data *data = BIO_get_data(a);
(void)ptr;
switch (cmd) {
case BIO_CTRL_GET_CLOSE:
ret = BIO_get_shutdown(a);
break;
case BIO_CTRL_SET_CLOSE:
BIO_set_shutdown(a, (int)num);
ret = 1;
break;
case BIO_CTRL_DGRAM_SET_PEEK_MODE:
data->peekmode = (unsigned)num;
break;
case BIO_CTRL_DGRAM_CONNECT:
case BIO_C_SET_FD:
case BIO_C_GET_FD:
case BIO_CTRL_DGRAM_SET_DONT_FRAG:
case BIO_CTRL_DGRAM_GET_MTU:
case BIO_CTRL_DGRAM_SET_MTU:
case BIO_CTRL_DGRAM_QUERY_MTU:
case BIO_CTRL_DGRAM_GET_FALLBACK_MTU:
ret = -1;
break;
case BIO_CTRL_DUP:
case BIO_CTRL_FLUSH:
case BIO_CTRL_DGRAM_MTU_DISCOVER:
case BIO_CTRL_DGRAM_SET_CONNECTED:
ret = 1;
break;
case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT:
data->timeout = coap_ticks_from_rt_us((uint64_t)((struct timeval*)ptr)->tv_sec * 1000000 + ((struct timeval*)ptr)->tv_usec);
ret = 1;
break;
case BIO_CTRL_RESET:
case BIO_C_FILE_SEEK:
case BIO_C_FILE_TELL:
case BIO_CTRL_INFO:
case BIO_CTRL_PENDING:
case BIO_CTRL_WPENDING:
case BIO_CTRL_DGRAM_GET_PEER:
case BIO_CTRL_DGRAM_SET_PEER:
case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT:
case BIO_CTRL_DGRAM_GET_RECV_TIMEOUT:
case BIO_CTRL_DGRAM_SET_SEND_TIMEOUT:
case BIO_CTRL_DGRAM_GET_SEND_TIMEOUT:
case BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP:
case BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP:
case BIO_CTRL_DGRAM_MTU_EXCEEDED:
case BIO_CTRL_DGRAM_GET_MTU_OVERHEAD:
default:
ret = 0;
break;
}
return ret;
}
static int coap_dtls_generate_cookie(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len) {
coap_dtls_context_t *dtls = (coap_dtls_context_t *)SSL_CTX_get_app_data(SSL_get_SSL_CTX(ssl));
coap_ssl_data *data = (coap_ssl_data*)BIO_get_data(SSL_get_rbio(ssl));
int r = HMAC_Init_ex(dtls->cookie_hmac, NULL, 0, NULL, NULL);
r &= HMAC_Update(dtls->cookie_hmac,
(const uint8_t*)&data->session->addr_info.local.addr,
(size_t)data->session->addr_info.local.size);
r &= HMAC_Update(dtls->cookie_hmac,
(const uint8_t*)&data->session->addr_info.remote.addr,
(size_t)data->session->addr_info.remote.size);
r &= HMAC_Final(dtls->cookie_hmac, cookie, cookie_len);
return r;
}
static int coap_dtls_verify_cookie(SSL *ssl, const uint8_t *cookie, unsigned int cookie_len) {
uint8_t hmac[32];
unsigned len = 32;
if (coap_dtls_generate_cookie(ssl, hmac, &len) && cookie_len == len && memcmp(cookie, hmac, len) == 0)
return 1;
else
return 0;
}
static unsigned coap_dtls_psk_client_callback(SSL *ssl, const char *hint, char *identity, unsigned int max_identity_len, unsigned char *buf, unsigned max_len) {
size_t hint_len = 0, identity_len = 0, psk_len;
coap_session_t *session = (coap_session_t*)SSL_get_app_data(ssl);
if (hint)
hint_len = strlen(hint);
else
hint = "";
coap_log(LOG_DEBUG, "got psk_identity_hint: '%.*s'\n", (int)hint_len, hint);
if (session == NULL || session->context == NULL || session->context->get_client_psk == NULL)
return 0;
psk_len = session->context->get_client_psk(session, (const uint8_t*)hint, hint_len, (uint8_t*)identity, &identity_len, max_identity_len - 1, (uint8_t*)buf, max_len);
if (identity_len < max_identity_len)
identity[identity_len] = 0;
return (unsigned)psk_len;
}
static unsigned coap_dtls_psk_server_callback(SSL *ssl, const char *identity, unsigned char *buf, unsigned max_len) {
size_t identity_len = 0;
coap_session_t *session = (coap_session_t*)SSL_get_app_data(ssl);
if (identity)
identity_len = strlen(identity);
else
identity = "";
coap_log(LOG_DEBUG, "got psk_identity: '%.*s'\n",
(int)identity_len, identity);
if (session == NULL || session->context == NULL || session->context->get_server_psk == NULL)
return 0;
return (unsigned)session->context->get_server_psk(session, (const uint8_t*)identity, identity_len, (uint8_t*)buf, max_len);
}
static void coap_dtls_info_callback(const SSL *ssl, int where, int ret) {
coap_session_t *session = (coap_session_t*)SSL_get_app_data(ssl);
const char *pstr;
int w = where &~SSL_ST_MASK;
if (w & SSL_ST_CONNECT)
pstr = "SSL_connect";
else if (w & SSL_ST_ACCEPT)
pstr = "SSL_accept";
else
pstr = "undefined";
if (where & SSL_CB_LOOP) {
if (dtls_log_level >= LOG_DEBUG)
coap_log(LOG_DEBUG, "* %s: %s:%s\n",
coap_session_str(session), pstr, SSL_state_string_long(ssl));
} else if (where & SSL_CB_ALERT) {
pstr = (where & SSL_CB_READ) ? "read" : "write";
if (dtls_log_level >= LOG_INFO)
coap_log(LOG_INFO, "* %s: SSL3 alert %s:%s:%s\n",
coap_session_str(session),
pstr,
SSL_alert_type_string_long(ret),
SSL_alert_desc_string_long(ret));
if ((where & (SSL_CB_WRITE|SSL_CB_READ)) && (ret >> 8) == SSL3_AL_FATAL)
session->dtls_event = COAP_EVENT_DTLS_ERROR;
} else if (where & SSL_CB_EXIT) {
if (ret == 0) {
if (dtls_log_level >= LOG_WARNING) {
unsigned long e;
coap_log(LOG_WARNING, "* %s: %s:failed in %s\n",
coap_session_str(session), pstr, SSL_state_string_long(ssl));
while ((e = ERR_get_error()))
coap_log(LOG_WARNING, "* %s: %s at %s:%s\n",
coap_session_str(session), ERR_reason_error_string(e),
ERR_lib_error_string(e), ERR_func_error_string(e));
}
} else if (ret < 0) {
if (dtls_log_level >= LOG_WARNING) {
int err = SSL_get_error(ssl, ret);
if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE && err != SSL_ERROR_WANT_CONNECT && err != SSL_ERROR_WANT_ACCEPT && err != SSL_ERROR_WANT_X509_LOOKUP) {
long e;
coap_log(LOG_WARNING, "* %s: %s:error in %s\n",
coap_session_str(session), pstr, SSL_state_string_long(ssl));
while ((e = ERR_get_error()))
coap_log(LOG_WARNING, "* %s: %s at %s:%s\n",
coap_session_str(session), ERR_reason_error_string(e),
ERR_lib_error_string(e), ERR_func_error_string(e));
}
}
}
}
if (where == SSL_CB_HANDSHAKE_START && SSL_get_state(ssl) == TLS_ST_OK)
session->dtls_event = COAP_EVENT_DTLS_RENEGOTIATE;
}
static int coap_sock_create(BIO *a) {
BIO_set_init(a, 1);
return 1;
}
static int coap_sock_destroy(BIO *a) {
(void)a;
return 1;
}
static int coap_sock_read(BIO *a, char *out, int outl) {
int ret = 0;
coap_session_t *session = (coap_session_t *)BIO_get_data(a);
if (out != NULL) {
ret = (int)coap_socket_read(&session->sock, (uint8_t*)out, (size_t)outl);
if (ret == 0) {
BIO_set_retry_read(a);
ret = -1;
} else {
BIO_clear_retry_flags(a);
}
}
return ret;
}
static int coap_sock_write(BIO *a, const char *in, int inl) {
int ret = 0;
coap_session_t *session = (coap_session_t *)BIO_get_data(a);
ret = (int)coap_socket_write(&session->sock, (const uint8_t*)in, (size_t)inl);
BIO_clear_retry_flags(a);
if (ret == 0) {
BIO_set_retry_read(a);
ret = -1;
} else {
BIO_clear_retry_flags(a);
}
return ret;
}
static int coap_sock_puts(BIO *a, const char *pstr) {
return coap_sock_write(a, pstr, (int)strlen(pstr));
}
static long coap_sock_ctrl(BIO *a, int cmd, long num, void *ptr) {
int r = 1;
(void)a;
(void)ptr;
(void)num;
switch (cmd) {
case BIO_C_SET_FD:
case BIO_C_GET_FD:
r = -1;
break;
case BIO_CTRL_SET_CLOSE:
case BIO_CTRL_DUP:
case BIO_CTRL_FLUSH:
r = 1;
break;
default:
case BIO_CTRL_GET_CLOSE:
r = 0;
break;
}
return r;
}
void *coap_dtls_new_context(struct coap_context_t *coap_context) {
coap_openssl_context_t *context;
(void)coap_context;
context = (coap_openssl_context_t *)coap_malloc(sizeof(coap_openssl_context_t));
if (context) {
uint8_t cookie_secret[32];
memset(context, 0, sizeof(coap_openssl_context_t));
/* Set up DTLS context */
context->dtls.ctx = SSL_CTX_new(DTLS_method());
if (!context->dtls.ctx)
goto error;
SSL_CTX_set_min_proto_version(context->dtls.ctx, DTLS1_2_VERSION);
SSL_CTX_set_app_data(context->dtls.ctx, &context->dtls);
SSL_CTX_set_read_ahead(context->dtls.ctx, 1);
SSL_CTX_set_cipher_list(context->dtls.ctx, COAP_OPENSSL_CIPHERS);
memset(cookie_secret, 0, sizeof(cookie_secret));
if (!RAND_bytes(cookie_secret, (int)sizeof(cookie_secret))) {
if (dtls_log_level >= LOG_WARNING)
coap_log(LOG_WARNING,
"Insufficient entropy for random cookie generation");
prng(cookie_secret, sizeof(cookie_secret));
}
context->dtls.cookie_hmac = HMAC_CTX_new();
if (!HMAC_Init_ex(context->dtls.cookie_hmac, cookie_secret, (int)sizeof(cookie_secret), EVP_sha256(), NULL))
goto error;
SSL_CTX_set_cookie_generate_cb(context->dtls.ctx, coap_dtls_generate_cookie);
SSL_CTX_set_cookie_verify_cb(context->dtls.ctx, coap_dtls_verify_cookie);
SSL_CTX_set_info_callback(context->dtls.ctx, coap_dtls_info_callback);
SSL_CTX_set_options(context->dtls.ctx, SSL_OP_NO_QUERY_MTU);
context->dtls.meth = BIO_meth_new(BIO_TYPE_DGRAM, "coapdgram");
if (!context->dtls.meth)
goto error;
context->dtls.bio_addr = BIO_ADDR_new();
if (!context->dtls.bio_addr)
goto error;
BIO_meth_set_write(context->dtls.meth, coap_dgram_write);
BIO_meth_set_read(context->dtls.meth, coap_dgram_read);
BIO_meth_set_puts(context->dtls.meth, coap_dgram_puts);
BIO_meth_set_ctrl(context->dtls.meth, coap_dgram_ctrl);
BIO_meth_set_create(context->dtls.meth, coap_dgram_create);
BIO_meth_set_destroy(context->dtls.meth, coap_dgram_destroy);
/* Set up TLS context */
context->tls.ctx = SSL_CTX_new(TLS_method());
if (!context->tls.ctx)
goto error;
SSL_CTX_set_app_data(context->tls.ctx, &context->tls);
SSL_CTX_set_min_proto_version(context->tls.ctx, TLS1_VERSION);
SSL_CTX_set_cipher_list(context->tls.ctx, COAP_OPENSSL_CIPHERS);
SSL_CTX_set_info_callback(context->tls.ctx, coap_dtls_info_callback);
context->tls.meth = BIO_meth_new(BIO_TYPE_SOCKET, "coapsock");
if (!context->tls.meth)
goto error;
BIO_meth_set_write(context->tls.meth, coap_sock_write);
BIO_meth_set_read(context->tls.meth, coap_sock_read);
BIO_meth_set_puts(context->tls.meth, coap_sock_puts);
BIO_meth_set_ctrl(context->tls.meth, coap_sock_ctrl);
BIO_meth_set_create(context->tls.meth, coap_sock_create);
BIO_meth_set_destroy(context->tls.meth, coap_sock_destroy);
}
return context;
error:
coap_dtls_free_context(context);
return NULL;
}
int
coap_dtls_context_set_psk(coap_context_t *ctx,
const char *identity_hint,
coap_dtls_role_t role
) {
coap_openssl_context_t *context = ((coap_openssl_context_t *)ctx->dtls_context);
BIO *bio;
if (role == COAP_DTLS_ROLE_SERVER) {
SSL_CTX_set_psk_server_callback(context->dtls.ctx, coap_dtls_psk_server_callback);
SSL_CTX_set_psk_server_callback(context->tls.ctx, coap_dtls_psk_server_callback);
SSL_CTX_use_psk_identity_hint(context->dtls.ctx, identity_hint ? identity_hint : "");
SSL_CTX_use_psk_identity_hint(context->tls.ctx, identity_hint ? identity_hint : "");
}
if (!context->dtls.ssl) {
/* This is set up to handle new incoming sessions to a server */
context->dtls.ssl = SSL_new(context->dtls.ctx);
if (!context->dtls.ssl)
return 0;
bio = BIO_new(context->dtls.meth);
if (!bio) {
SSL_free (context->dtls.ssl);
context->dtls.ssl = NULL;
return 0;
}
SSL_set_bio(context->dtls.ssl, bio, bio);
SSL_set_app_data(context->dtls.ssl, NULL);
SSL_set_options(context->dtls.ssl, SSL_OP_COOKIE_EXCHANGE);
SSL_set_mtu(context->dtls.ssl, COAP_DEFAULT_MTU);
}
context->psk_pki_enabled |= IS_PSK;
return 1;
}
static int
map_key_type(int asn1_private_key_type
) {
switch (asn1_private_key_type) {
case COAP_ASN1_PKEY_NONE: return EVP_PKEY_NONE;
case COAP_ASN1_PKEY_RSA: return EVP_PKEY_RSA;
case COAP_ASN1_PKEY_RSA2: return EVP_PKEY_RSA2;
case COAP_ASN1_PKEY_DSA: return EVP_PKEY_DSA;
case COAP_ASN1_PKEY_DSA1: return EVP_PKEY_DSA1;
case COAP_ASN1_PKEY_DSA2: return EVP_PKEY_DSA2;
case COAP_ASN1_PKEY_DSA3: return EVP_PKEY_DSA3;
case COAP_ASN1_PKEY_DSA4: return EVP_PKEY_DSA4;
case COAP_ASN1_PKEY_DH: return EVP_PKEY_DH;
case COAP_ASN1_PKEY_DHX: return EVP_PKEY_DHX;
case COAP_ASN1_PKEY_EC: return EVP_PKEY_EC;
case COAP_ASN1_PKEY_HMAC: return EVP_PKEY_HMAC;
case COAP_ASN1_PKEY_CMAC: return EVP_PKEY_CMAC;
case COAP_ASN1_PKEY_TLS1_PRF: return EVP_PKEY_TLS1_PRF;
case COAP_ASN1_PKEY_HKDF: return EVP_PKEY_HKDF;
default:
coap_log(LOG_WARNING,
"*** setup_pki: DTLS: Unknown Private Key type %d for ASN1\n",
asn1_private_key_type);
break;
}
return 0;
}
static uint8_t coap_alpn[] = { 4, 'c', 'o', 'a', 'p' };
static int
server_alpn_callback (SSL *ssl UNUSED,
const unsigned char **out,
unsigned char *outlen,
const unsigned char *in,
unsigned int inlen,
void *arg UNUSED
) {
unsigned char *tout = NULL;
int ret;
if (inlen == 0)
return SSL_TLSEXT_ERR_NOACK;
ret = SSL_select_next_proto(&tout,
outlen,
coap_alpn,
sizeof(coap_alpn),
in,
inlen);
*out = tout;
return (ret != OPENSSL_NPN_NEGOTIATED) ? SSL_TLSEXT_ERR_NOACK : SSL_TLSEXT_ERR_OK;
}
static void
add_ca_to_cert_store(X509_STORE *st, X509 *x509)
{
long e;
/* Flush out existing errors */
while ((e = ERR_get_error()) != 0) {
}
if (!X509_STORE_add_cert(st, x509)) {
while ((e = ERR_get_error()) != 0) {
int r = ERR_GET_REASON(e);
if (r != X509_R_CERT_ALREADY_IN_HASH_TABLE) {
/* Not already added */
coap_log(LOG_WARNING, "***setup_pki: (D)TLS: %s at %s:%s\n",
ERR_reason_error_string(e),
ERR_lib_error_string(e),
ERR_func_error_string(e));
}
}
}
}
#if OPENSSL_VERSION_NUMBER < 0x10101000L
static int
setup_pki_server(SSL_CTX *ctx,
coap_dtls_pki_t* setup_data
) {
switch (setup_data->pki_key.key_type) {
case COAP_PKI_KEY_PEM:
if (setup_data->pki_key.key.pem.public_cert &&
setup_data->pki_key.key.pem.public_cert[0]) {
if (!(SSL_CTX_use_certificate_file(ctx,
setup_data->pki_key.key.pem.public_cert,
SSL_FILETYPE_PEM))) {
coap_log(LOG_WARNING,
"*** setup_pki: (D)TLS: %s: Unable to configure "
"Server Certificate\n",
setup_data->pki_key.key.pem.public_cert);
return 0;
}
}
else {
coap_log(LOG_ERR,
"*** setup_pki: (D)TLS: No Server Certificate defined\n");
return 0;
}
if (setup_data->pki_key.key.pem.private_key &&
setup_data->pki_key.key.pem.private_key[0]) {
if (!(SSL_CTX_use_PrivateKey_file(ctx,
setup_data->pki_key.key.pem.private_key,
SSL_FILETYPE_PEM))) {
coap_log(LOG_WARNING,
"*** setup_pki: (D)TLS: %s: Unable to configure "
"Server Private Key\n",
setup_data->pki_key.key.pem.private_key);
return 0;
}
}
else {
coap_log(LOG_ERR,
"*** setup_pki: (D)TLS: No Server Private Key defined\n");
return 0;
}
if (setup_data->pki_key.key.pem.ca_file &&
setup_data->pki_key.key.pem.ca_file[0]) {
STACK_OF(X509_NAME) *cert_names;
X509_STORE *st;
BIO *in;
X509 *x = NULL;
char *rw_var = NULL;
cert_names = SSL_load_client_CA_file(setup_data->pki_key.key.pem.ca_file);
if (cert_names != NULL)
SSL_CTX_set_client_CA_list(ctx, cert_names);
else {
coap_log(LOG_WARNING,
"*** setup_pki: (D)TLS: %s: Unable to configure "
"client CA File\n",
setup_data->pki_key.key.pem.ca_file);
return 0;
}
st = SSL_CTX_get_cert_store(ctx);
in = BIO_new(BIO_s_file());
/* Need to do this to not get a compiler warning about const parameters */
memcpy(&rw_var, &setup_data->pki_key.key.pem.ca_file, sizeof (rw_var));
if (!BIO_read_filename(in, rw_var)) {
BIO_free(in);
X509_free(x);
break;
}
for (;;) {
if (PEM_read_bio_X509(in, &x, NULL, NULL) == NULL)
break;
add_ca_to_cert_store(st, x);
}
BIO_free(in);
X509_free(x);
}
break;
case COAP_PKI_KEY_ASN1:
if (setup_data->pki_key.key.asn1.public_cert &&
setup_data->pki_key.key.asn1.public_cert_len > 0) {
if (!(SSL_CTX_use_certificate_ASN1(ctx,
setup_data->pki_key.key.asn1.public_cert_len,
setup_data->pki_key.key.asn1.public_cert))) {
coap_log(LOG_WARNING,
"*** setup_pki: (D)TLS: %s: Unable to configure "
"Server Certificate\n",
"ASN1");
return 0;
}
}
else {
coap_log(LOG_ERR,
"*** setup_pki: (D)TLS: No Server Certificate defined\n");
return 0;
}
if (setup_data->pki_key.key.asn1.private_key &&
setup_data->pki_key.key.asn1.private_key_len > 0) {
int pkey_type = map_key_type(setup_data->pki_key.key.asn1.private_key_type);
if (!(SSL_CTX_use_PrivateKey_ASN1(pkey_type, ctx,
setup_data->pki_key.key.asn1.private_key,
setup_data->pki_key.key.asn1.private_key_len))) {
coap_log(LOG_WARNING,
"*** setup_pki: (D)TLS: %s: Unable to configure "
"Server Private Key\n",
"ASN1");
return 0;
}
}
else {
coap_log(LOG_ERR,
"*** setup_pki: (D)TLS: No Server Private Key defined\n");
return 0;
}
if (setup_data->pki_key.key.asn1.ca_cert &&
setup_data->pki_key.key.asn1.ca_cert_len > 0) {
/* Need to use a temp variable as it gets incremented*/
const uint8_t *p = setup_data->pki_key.key.asn1.ca_cert;
X509* x509 = d2i_X509(NULL, &p, setup_data->pki_key.key.asn1.ca_cert_len);
X509_STORE *st;
if (!x509 || !SSL_CTX_add_client_CA(ctx, x509)) {
coap_log(LOG_WARNING,
"*** setup_pki: (D)TLS: %s: Unable to configure "
"client CA File\n",
"ASN1");
X509_free(x509);
return 0;
}
st = SSL_CTX_get_cert_store(ctx);
add_ca_to_cert_store(st, x509);
X509_free(x509);
}
break;
default:
coap_log(LOG_ERR,
"*** setup_pki: (D)TLS: Unknown key type %d\n",
setup_data->pki_key.key_type);
return 0;
}
return 1;
}
#endif /* OPENSSL_VERSION_NUMBER < 0x10101000L */
static int
setup_pki_ssl(SSL *ssl,
coap_dtls_pki_t* setup_data, coap_dtls_role_t role
) {
switch (setup_data->pki_key.key_type) {
case COAP_PKI_KEY_PEM:
if (setup_data->pki_key.key.pem.public_cert &&
setup_data->pki_key.key.pem.public_cert[0]) {
if (!(SSL_use_certificate_file(ssl,
setup_data->pki_key.key.pem.public_cert,
SSL_FILETYPE_PEM))) {
coap_log(LOG_WARNING,
"*** setup_pki: (D)TLS: %s: Unable to configure "
"%s Certificate\n",
setup_data->pki_key.key.pem.public_cert,
role == COAP_DTLS_ROLE_SERVER ? "Server" : "Client");
return 0;
}
}
else if (role == COAP_DTLS_ROLE_SERVER ||
(setup_data->pki_key.key.pem.private_key &&
setup_data->pki_key.key.pem.private_key[0])) {
coap_log(LOG_ERR,
"*** setup_pki: (D)TLS: No %s Certificate defined\n",
role == COAP_DTLS_ROLE_SERVER ? "Server" : "Client");
return 0;
}
if (setup_data->pki_key.key.pem.private_key &&
setup_data->pki_key.key.pem.private_key[0]) {
if (!(SSL_use_PrivateKey_file(ssl,
setup_data->pki_key.key.pem.private_key,
SSL_FILETYPE_PEM))) {
coap_log(LOG_WARNING,
"*** setup_pki: (D)TLS: %s: Unable to configure "
"Client Private Key\n",
setup_data->pki_key.key.pem.private_key);
return 0;
}
}
else if (role == COAP_DTLS_ROLE_SERVER ||
(setup_data->pki_key.key.pem.public_cert &&
setup_data->pki_key.key.pem.public_cert[0])) {
coap_log(LOG_ERR,
"*** setup_pki: (D)TLS: No %s Private Key defined\n",
role == COAP_DTLS_ROLE_SERVER ? "Server" : "Client");
return 0;
}
if (setup_data->pki_key.key.pem.ca_file &&
setup_data->pki_key.key.pem.ca_file[0]) {
X509_STORE *st;
BIO *in;
X509 *x = NULL;
char *rw_var = NULL;
SSL_CTX *ctx = SSL_get_SSL_CTX(ssl);
if (role == COAP_DTLS_ROLE_SERVER) {
STACK_OF(X509_NAME) *cert_names = SSL_load_client_CA_file(setup_data->pki_key.key.pem.ca_file);
if (cert_names != NULL)
SSL_set_client_CA_list(ssl, cert_names);
else {
coap_log(LOG_WARNING,
"*** setup_pki: (D)TLS: %s: Unable to configure "
"%s CA File\n",
setup_data->pki_key.key.pem.ca_file,
role == COAP_DTLS_ROLE_SERVER ? "Server" : "Client");
return 0;
}
}
/* Add CA to the trusted root CA store */
in = BIO_new(BIO_s_file());
/* Need to do this to not get a compiler warning about const parameters */
memcpy(&rw_var, &setup_data->pki_key.key.pem.ca_file, sizeof (rw_var));
if (!BIO_read_filename(in, rw_var)) {
BIO_free(in);
X509_free(x);
break;
}
st = SSL_CTX_get_cert_store(ctx);
for (;;) {
if (PEM_read_bio_X509(in, &x, NULL, NULL) == NULL)
break;
add_ca_to_cert_store(st, x);
}
BIO_free(in);
X509_free(x);
}
break;
case COAP_PKI_KEY_ASN1:
if (setup_data->pki_key.key.asn1.public_cert &&
setup_data->pki_key.key.asn1.public_cert_len > 0) {
if (!(SSL_use_certificate_ASN1(ssl,
setup_data->pki_key.key.asn1.public_cert,
setup_data->pki_key.key.asn1.public_cert_len))) {
coap_log(LOG_WARNING,
"*** setup_pki: (D)TLS: %s: Unable to configure "
"%s Certificate\n",
role == COAP_DTLS_ROLE_SERVER ? "Server" : "Client",
"ASN1");
return 0;
}
}
else if (role == COAP_DTLS_ROLE_SERVER ||
(setup_data->pki_key.key.asn1.private_key &&
setup_data->pki_key.key.asn1.private_key[0])) {
coap_log(LOG_ERR,
"*** setup_pki: (D)TLS: No %s Certificate defined\n",
role == COAP_DTLS_ROLE_SERVER ? "Server" : "Client");
return 0;
}
if (setup_data->pki_key.key.asn1.private_key &&
setup_data->pki_key.key.asn1.private_key_len > 0) {
int pkey_type = map_key_type(setup_data->pki_key.key.asn1.private_key_type);
if (!(SSL_use_PrivateKey_ASN1(pkey_type, ssl,
setup_data->pki_key.key.asn1.private_key,
setup_data->pki_key.key.asn1.private_key_len))) {
coap_log(LOG_WARNING,
"*** setup_pki: (D)TLS: %s: Unable to configure "
"%s Private Key\n",
role == COAP_DTLS_ROLE_SERVER ? "Server" : "Client",
"ASN1");
return 0;
}
}
else if (role == COAP_DTLS_ROLE_SERVER ||
(setup_data->pki_key.key.asn1.public_cert &&
setup_data->pki_key.key.asn1.public_cert_len > 0)) {
coap_log(LOG_ERR,
"*** setup_pki: (D)TLS: No %s Private Key defined",
role == COAP_DTLS_ROLE_SERVER ? "Server" : "Client");
return 0;
}
if (setup_data->pki_key.key.asn1.ca_cert &&
setup_data->pki_key.key.asn1.ca_cert_len > 0) {
/* Need to use a temp variable as it gets incremented*/
const uint8_t *p = setup_data->pki_key.key.asn1.ca_cert;
X509* x509 = d2i_X509(NULL, &p, setup_data->pki_key.key.asn1.ca_cert_len);
X509_STORE *st;
SSL_CTX *ctx = SSL_get_SSL_CTX(ssl);
if (role == COAP_DTLS_ROLE_SERVER) {
if (!x509 || !SSL_add_client_CA(ssl, x509)) {
coap_log(LOG_WARNING,
"*** setup_pki: (D)TLS: %s: Unable to configure "
"client CA File\n",
"ASN1");
X509_free(x509);
return 0;
}
}
/* Add CA to the trusted root CA store */
st = SSL_CTX_get_cert_store(ctx);
add_ca_to_cert_store(st, x509);
X509_free(x509);
}
break;
default:
coap_log(LOG_ERR,
"*** setup_pki: (D)TLS: Unknown key type %d\n",
setup_data->pki_key.key_type);
return 0;
}
return 1;
}
static char*
get_san_or_cn_from_cert(X509* x509) {
if (x509) {
char *cn;
int n;
STACK_OF(GENERAL_NAME) *san_list;
char buffer[256];
san_list = X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL);
if (san_list) {
int san_count = sk_GENERAL_NAME_num(san_list);
for (n = 0; n < san_count; n++) {
const GENERAL_NAME * name = sk_GENERAL_NAME_value (san_list, n);
if (name->type == GEN_DNS) {
const char *dns_name = (const char *)ASN1_STRING_get0_data(name->d.dNSName);
/* Make sure that there is not an embedded NUL in the dns_name */
if (ASN1_STRING_length(name->d.dNSName) != (int)strlen (dns_name))
continue;
cn = OPENSSL_strdup(dns_name);
sk_GENERAL_NAME_pop_free(san_list, GENERAL_NAME_free);
return cn;
}
}
sk_GENERAL_NAME_pop_free(san_list, GENERAL_NAME_free);
}
/* Otherwise look for the CN= field */
X509_NAME_oneline(X509_get_subject_name(x509), buffer, sizeof(buffer));
/* Need to emulate strcasestr() here. Looking for CN= */
n = strlen(buffer) - 3;
cn = buffer;
while (n > 0) {
if (((cn[0] == 'C') || (cn[0] == 'c')) &&
((cn[1] == 'N') || (cn[1] == 'n')) &&
(cn[2] == '=')) {
cn += 3;
break;
}
cn++;
n--;
}
if (n > 0) {
char * ecn = strchr(cn, '/');
if (ecn) {
return OPENSSL_strndup(cn, ecn-cn);
}
else {
return OPENSSL_strdup(cn);
}
}
}
return NULL;
}
static int
tls_verify_call_back(int preverify_ok, X509_STORE_CTX *ctx) {
SSL *ssl = X509_STORE_CTX_get_ex_data(ctx,
SSL_get_ex_data_X509_STORE_CTX_idx());
coap_session_t *session = SSL_get_app_data(ssl);
coap_openssl_context_t *context =
((coap_openssl_context_t *)session->context->dtls_context);
coap_dtls_pki_t *setup_data = &context->setup_data;
int depth = X509_STORE_CTX_get_error_depth(ctx);
int err = X509_STORE_CTX_get_error(ctx);
X509 *x509 = X509_STORE_CTX_get_current_cert(ctx);
char *cn = get_san_or_cn_from_cert(x509);
int keep_preverify_ok = preverify_ok;
if (!preverify_ok) {
switch (err) {
case X509_V_ERR_CERT_NOT_YET_VALID:
case X509_V_ERR_CERT_HAS_EXPIRED:
if (setup_data->allow_expired_certs)
preverify_ok = 1;
break;
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
if (setup_data->allow_self_signed)
preverify_ok = 1;
break;
case X509_V_ERR_UNABLE_TO_GET_CRL:
if (setup_data->allow_no_crl)
preverify_ok = 1;
break;
case X509_V_ERR_CRL_NOT_YET_VALID:
case X509_V_ERR_CRL_HAS_EXPIRED:
if (setup_data->allow_expired_crl)
preverify_ok = 1;
break;
default:
break;
}
if (!preverify_ok) {
coap_log(LOG_WARNING,
" %s: %s: '%s' depth=%d\n",
coap_session_str(session),
X509_verify_cert_error_string(err), cn ? cn : "?", depth);
/* Invoke the CN callback function for this failure */
keep_preverify_ok = 1;
}
else {
coap_log(LOG_WARNING,
" %s: %s: overridden: '%s' depth=%d\n",
coap_session_str(session),
X509_verify_cert_error_string(err), cn ? cn : "?", depth);
}
}
/* Certificate - depth == 0 is the Client Cert */
if (setup_data->validate_cn_call_back && keep_preverify_ok) {
int length = i2d_X509(x509, NULL);
uint8_t *base_buf;
uint8_t *base_buf2 = base_buf = OPENSSL_malloc(length);
/* base_buf2 gets moved to the end */
i2d_X509(x509, &base_buf2);
if (!setup_data->validate_cn_call_back(cn, base_buf, length, session,
depth, preverify_ok,
setup_data->cn_call_back_arg)) {
if (depth == 0) {
X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REJECTED);
}
else {
X509_STORE_CTX_set_error(ctx, X509_V_ERR_INVALID_CA);
}
preverify_ok = 0;
}
OPENSSL_free(base_buf);
}
OPENSSL_free(cn);
return preverify_ok;
}
#if OPENSSL_VERSION_NUMBER < 0x10101000L
/*
* During the SSL/TLS initial negotiations, tls_secret_call_back() is called so
* it is possible to determine whether this is a PKI or PSK incoming
* request and adjust the ciphers if necessary
*
* Set up by SSL_set_session_secret_cb() in tls_server_name_call_back()
*/
static int
tls_secret_call_back(SSL *ssl,
void *secret UNUSED,
int *secretlen UNUSED,
STACK_OF(SSL_CIPHER) *peer_ciphers,
const SSL_CIPHER **cipher UNUSED,
void *arg
) {
int ii;
int psk_requested = 0;
coap_session_t *session = SSL_get_app_data(ssl);
coap_dtls_pki_t *setup_data = (coap_dtls_pki_t*)arg;
if (session && session->context->psk_key && session->context->psk_key_len) {
/* Is PSK being requested - if so, we need to change algorithms */
for (ii = 0; ii < sk_SSL_CIPHER_num (peer_ciphers); ii++) {
const SSL_CIPHER *peer_cipher = sk_SSL_CIPHER_value(peer_ciphers, ii);
coap_log(COAP_LOG_CIPHERS, "Client cipher: %s\n",
SSL_CIPHER_get_name(peer_cipher));
if (strstr (SSL_CIPHER_get_name (peer_cipher), "PSK")) {
psk_requested = 1;
break;
}
}
}
if (!psk_requested) {
if (session) {
coap_log(LOG_DEBUG, " %s: Using PKI ciphers\n",
coap_session_str(session));
}
else {
coap_log(LOG_DEBUG, "Using PKI ciphers\n");
}
if (setup_data->verify_peer_cert) {
if (setup_data->require_peer_cert) {
SSL_set_verify(ssl,
SSL_VERIFY_PEER |
SSL_VERIFY_CLIENT_ONCE |
SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
tls_verify_call_back);
}
else {
SSL_set_verify(ssl,
SSL_VERIFY_PEER |
SSL_VERIFY_CLIENT_ONCE,
tls_verify_call_back);
}
}
else {
SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
}
/* Check CA Chain */
if (setup_data->cert_chain_validation)
SSL_set_verify_depth(ssl, setup_data->cert_chain_verify_depth);
/* Certificate Revocation */
if (setup_data->check_cert_revocation) {
X509_VERIFY_PARAM *param;
param = X509_VERIFY_PARAM_new();
X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK);
SSL_set1_param(ssl, param);
X509_VERIFY_PARAM_free(param);
}
}
else {
if (session) {
if (session->context->psk_key && session->context->psk_key_len) {
memcpy(secret, session->context->psk_key, session->context->psk_key_len);
*secretlen = session->context->psk_key_len;
}
coap_log(LOG_DEBUG, " %s: Setting PSK ciphers\n",
coap_session_str(session));
}
else {
coap_log(LOG_DEBUG, "Setting PSK ciphers\n");
}
/*
* Force a PSK algorithm to be used, so we do PSK
*/
SSL_set_cipher_list (ssl, COAP_OPENSSL_PSK_CIPHERS);
SSL_set_psk_server_callback(ssl, coap_dtls_psk_server_callback);
}
if (setup_data->additional_tls_setup_call_back) {
/* Additional application setup wanted */
if (!setup_data->additional_tls_setup_call_back(ssl, setup_data))
return 0;
}
return 0;
}
/*
* During the SSL/TLS initial negotiations, tls_server_name_call_back() is called
* so it is possible to set up an extra callback to determine whether this is
* a PKI or PSK incoming request and adjust the ciphers if necessary
*
* Set up by SSL_CTX_set_tlsext_servername_callback() in coap_dtls_context_set_pki()
*/
static int
tls_server_name_call_back(SSL *ssl,
int *sd UNUSED,
void *arg
) {
coap_dtls_pki_t *setup_data = (coap_dtls_pki_t*)arg;
if (!ssl) {
return SSL_TLSEXT_ERR_NOACK;
}
if (setup_data->validate_sni_call_back) {
/* SNI checking requested */
coap_session_t *session = (coap_session_t*)SSL_get_app_data(ssl);
coap_openssl_context_t *context =
((coap_openssl_context_t *)session->context->dtls_context);
const char *sni = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
size_t i;
if (!sni || !sni[0]) {
sni = "";
}
for (i = 0; i < context->sni_count; i++) {
if (!strcmp(sni, context->sni_entry_list[i].sni)) {
break;
}
}
if (i == context->sni_count) {
SSL_CTX *ctx;
coap_dtls_pki_t sni_setup_data;
coap_dtls_key_t *new_entry = setup_data->validate_sni_call_back(sni,
setup_data->sni_call_back_arg);
if (!new_entry) {
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
/* Need to set up a new SSL_CTX to switch to */
if (session->proto == COAP_PROTO_DTLS) {
/* Set up DTLS context */
ctx = SSL_CTX_new(DTLS_method());
if (!ctx)
goto error;
SSL_CTX_set_min_proto_version(ctx, DTLS1_2_VERSION);
SSL_CTX_set_app_data(ctx, &context->dtls);
SSL_CTX_set_read_ahead(ctx, 1);
SSL_CTX_set_cipher_list(ctx, COAP_OPENSSL_CIPHERS);
SSL_CTX_set_cookie_generate_cb(ctx, coap_dtls_generate_cookie);
SSL_CTX_set_cookie_verify_cb(ctx, coap_dtls_verify_cookie);
SSL_CTX_set_info_callback(ctx, coap_dtls_info_callback);
SSL_CTX_set_options(ctx, SSL_OP_NO_QUERY_MTU);
}
else {
/* Set up TLS context */
ctx = SSL_CTX_new(TLS_method());
if (!ctx)
goto error;
SSL_CTX_set_app_data(ctx, &context->tls);
SSL_CTX_set_min_proto_version(ctx, TLS1_VERSION);
SSL_CTX_set_cipher_list(ctx, COAP_OPENSSL_CIPHERS);
SSL_CTX_set_info_callback(ctx, coap_dtls_info_callback);
SSL_CTX_set_alpn_select_cb(ctx, server_alpn_callback, NULL);
}
memset(&sni_setup_data, 0, sizeof(sni_setup_data));
sni_setup_data.pki_key.key_type = new_entry->key_type;
sni_setup_data.pki_key.key.pem = new_entry->key.pem;
sni_setup_data.pki_key.key.asn1 = new_entry->key.asn1;
setup_pki_server(ctx, &sni_setup_data);
context->sni_entry_list = OPENSSL_realloc(context->sni_entry_list,
(context->sni_count+1)*sizeof(sni_entry));
context->sni_entry_list[context->sni_count].sni = OPENSSL_strdup(sni);
context->sni_entry_list[context->sni_count].ctx = ctx;
context->sni_count++;
}
SSL_set_SSL_CTX (ssl, context->sni_entry_list[i].ctx);
SSL_clear_options (ssl, 0xFFFFFFFFL);
SSL_set_options (ssl, SSL_CTX_get_options (context->sni_entry_list[i].ctx));
}
/*
* Have to do extra call back next to get client algorithms
* SSL_get_client_ciphers() does not work this early on
*/
SSL_set_session_secret_cb(ssl, tls_secret_call_back, arg);
return SSL_TLSEXT_ERR_OK;
error:
return SSL_TLSEXT_ERR_ALERT_WARNING;
}
#else /* OPENSSL_VERSION_NUMBER >= 0x10101000L */
/*
* During the SSL/TLS initial negotiations, tls_client_hello_call_back() is
* called early in the Client Hello processing so it is possible to determine
* whether this is a PKI or PSK incoming request and adjust the ciphers if
* necessary.
*
* Set up by SSL_CTX_set_client_hello_cb().
*/
static int
tls_client_hello_call_back(SSL *ssl,
int *al,
void *arg UNUSED
) {
coap_session_t *session;
coap_openssl_context_t *dtls_context;
coap_dtls_pki_t *setup_data;
int psk_requested = 0;
const unsigned char *out;
size_t outlen;
if (!ssl) {
*al = SSL_AD_INTERNAL_ERROR;
return SSL_CLIENT_HELLO_ERROR;
}
session = (coap_session_t *)SSL_get_app_data(ssl);
assert(session != NULL);
assert(session->context != NULL);
assert(session->context->dtls_context != NULL);
dtls_context = (coap_openssl_context_t *)session->context->dtls_context;
setup_data = &dtls_context->setup_data;
/*
* See if PSK being requested
*/
if (session->context->psk_key && session->context->psk_key_len) {
int len = SSL_client_hello_get0_ciphers(ssl, &out);
STACK_OF(SSL_CIPHER) *peer_ciphers = NULL;
STACK_OF(SSL_CIPHER) *scsvc = NULL;
if (len && SSL_bytes_to_cipher_list(ssl, out, len,
SSL_client_hello_isv2(ssl),
&peer_ciphers, &scsvc)) {
int ii;
for (ii = 0; ii < sk_SSL_CIPHER_num (peer_ciphers); ii++) {
const SSL_CIPHER *peer_cipher = sk_SSL_CIPHER_value(peer_ciphers, ii);
coap_log(COAP_LOG_CIPHERS, "Client cipher: %s (%04x)\n",
SSL_CIPHER_get_name(peer_cipher),
SSL_CIPHER_get_protocol_id(peer_cipher));
if (strstr (SSL_CIPHER_get_name (peer_cipher), "PSK")) {
psk_requested = 1;
break;
}
}
}
sk_SSL_CIPHER_free(peer_ciphers);
sk_SSL_CIPHER_free(scsvc);
}
if (psk_requested) {
/*
* Client has requested PSK and it is supported
*/
if (session) {
coap_log(LOG_DEBUG, " %s: PSK request\n",
coap_session_str(session));
}
else {
coap_log(LOG_DEBUG, "PSK request\n");
}
SSL_set_psk_server_callback(ssl, coap_dtls_psk_server_callback);
if (setup_data->additional_tls_setup_call_back) {
/* Additional application setup wanted */
if (!setup_data->additional_tls_setup_call_back(ssl, setup_data))
return 0;
}
return SSL_CLIENT_HELLO_SUCCESS;
}
/*
* Handle Certificate requests
*/
/*
* Determine what type of certificate is being requested
*/
if (SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_client_certificate_type,
&out, &outlen)) {
size_t ii;
for (ii = 0; ii < outlen; ii++) {
switch (out[ii]) {
case 0:
/* RFC6091 X.509 */
if (outlen >= 2) {
/* X.509 cannot be the singular entry. RFC6091 3.1. Client Hello */
goto is_x509;
}
break;
case 2:
/* RFC7250 RPK - not yet supported */
break;
default:
break;
}
}
*al = SSL_AD_UNSUPPORTED_EXTENSION;
return SSL_CLIENT_HELLO_ERROR;
}
is_x509:
if (setup_data->validate_sni_call_back) {
/*
* SNI checking requested
*/
coap_dtls_pki_t sni_setup_data;
coap_openssl_context_t *context =
((coap_openssl_context_t *)session->context->dtls_context);
const char *sni = "";
char *sni_tmp = NULL;
size_t i;
if (SSL_client_hello_get0_ext (ssl, TLSEXT_TYPE_server_name, &out, &outlen) &&
outlen > 5 &&
(((out[0]<<8) + out[1] +2) == (int)outlen) &&
out[2] == TLSEXT_NAMETYPE_host_name &&
(((out[3]<<8) + out[4] +2 +3) == (int)outlen)) {
/* Skip over length, type and length */
out += 5;
outlen -= 5;
sni_tmp = OPENSSL_malloc(outlen+1);
sni_tmp[outlen] = '\000';
memcpy(sni_tmp, out, outlen);
sni = sni_tmp;
}
/* Is this a cached entry? */
for (i = 0; i < context->sni_count; i++) {
if (!strcmp(sni, context->sni_entry_list[i].sni)) {
break;
}
}
if (i == context->sni_count) {
/*
* New SNI request
*/
coap_dtls_key_t *new_entry = setup_data->validate_sni_call_back(sni,
setup_data->sni_call_back_arg);
if (!new_entry) {
*al = SSL_AD_UNRECOGNIZED_NAME;
return SSL_CLIENT_HELLO_ERROR;
}
context->sni_entry_list = OPENSSL_realloc(context->sni_entry_list,
(context->sni_count+1)*sizeof(sni_entry));
context->sni_entry_list[context->sni_count].sni = OPENSSL_strdup(sni);
context->sni_entry_list[context->sni_count].pki_key = *new_entry;
context->sni_count++;
}
if (sni_tmp) {
OPENSSL_free(sni_tmp);
}
memset(&sni_setup_data, 0, sizeof(sni_setup_data));
sni_setup_data.pki_key = context->sni_entry_list[i].pki_key;
setup_pki_ssl(ssl, &sni_setup_data, 1);
}
else {
setup_pki_ssl(ssl, setup_data, 1);
}
if (session) {
coap_log(LOG_DEBUG, " %s: Using PKI ciphers\n",
coap_session_str(session));
}
else {
coap_log(LOG_DEBUG, "Using PKI ciphers\n");
}
if (setup_data->verify_peer_cert) {
if (setup_data->require_peer_cert) {
SSL_set_verify(ssl,
SSL_VERIFY_PEER |
SSL_VERIFY_CLIENT_ONCE |
SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
tls_verify_call_back);
}
else {
SSL_set_verify(ssl,
SSL_VERIFY_PEER |
SSL_VERIFY_CLIENT_ONCE,
tls_verify_call_back);
}
}
else {
SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
}
/* Check CA Chain */
if (setup_data->cert_chain_validation)
SSL_set_verify_depth(ssl, setup_data->cert_chain_verify_depth);
/* Certificate Revocation */
if (setup_data->check_cert_revocation) {
X509_VERIFY_PARAM *param;
param = X509_VERIFY_PARAM_new();
X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK);
SSL_set1_param(ssl, param);
X509_VERIFY_PARAM_free(param);
}
if (setup_data->additional_tls_setup_call_back) {
/* Additional application setup wanted */
if (!setup_data->additional_tls_setup_call_back(ssl, setup_data))
return 0;
}
return SSL_CLIENT_HELLO_SUCCESS;
}
#endif /* OPENSSL_VERSION_NUMBER >= 0x10101000L */
int
coap_dtls_context_set_pki(coap_context_t *ctx,
coap_dtls_pki_t *setup_data,
coap_dtls_role_t role
) {
coap_openssl_context_t *context =
((coap_openssl_context_t *)ctx->dtls_context);
BIO *bio;
if (!setup_data)
return 0;
context->setup_data = *setup_data;
if (role == COAP_DTLS_ROLE_SERVER) {
if (context->dtls.ctx) {
/* SERVER DTLS */
#if OPENSSL_VERSION_NUMBER < 0x10101000L
if (!setup_pki_server(context->dtls.ctx, setup_data))
return 0;
#endif /* OPENSSL_VERSION_NUMBER < 0x10101000L */
/* libcoap is managing TLS connection based on setup_data options */
/* Need to set up logic to differentiate between a PSK or PKI session */
/*
* For OpenSSL 1.1.1, we need to use SSL_CTX_set_client_hello_cb()
* which is not in 1.1.0
*/
#if OPENSSL_VERSION_NUMBER < 0x10101000L
if (SSLeay() >= 0x10101000L) {
coap_log(LOG_WARNING,
"OpenSSL compiled with %lux, linked with %lux, so "
"no certificate checking\n",
OPENSSL_VERSION_NUMBER, SSLeay());
}
SSL_CTX_set_tlsext_servername_arg(context->dtls.ctx, &context->setup_data);
SSL_CTX_set_tlsext_servername_callback(context->dtls.ctx,
tls_server_name_call_back);
#else /* OPENSSL_VERSION_NUMBER >= 0x10101000L */
SSL_CTX_set_client_hello_cb(context->dtls.ctx,
tls_client_hello_call_back,
NULL);
#endif /* OPENSSL_VERSION_NUMBER >= 0x10101000L */
}
if (context->tls.ctx) {
/* SERVER TLS */
#if OPENSSL_VERSION_NUMBER < 0x10101000L
if (!setup_pki_server(context->tls.ctx, setup_data))
return 0;
#endif /* OPENSSL_VERSION_NUMBER < 0x10101000L */
/* libcoap is managing TLS connection based on setup_data options */
/* Need to set up logic to differentiate between a PSK or PKI session */
/*
* For OpenSSL 1.1.1, we need to use SSL_CTX_set_client_hello_cb()
* which is not in 1.1.0
*/
#if OPENSSL_VERSION_NUMBER < 0x10101000L
if (SSLeay() >= 0x10101000L) {
coap_log(LOG_WARNING,
"OpenSSL compiled with %lux, linked with %lux, so "
"no certificate checking\n",
OPENSSL_VERSION_NUMBER, SSLeay());
}
SSL_CTX_set_tlsext_servername_arg(context->tls.ctx, &context->setup_data);
SSL_CTX_set_tlsext_servername_callback(context->tls.ctx,
tls_server_name_call_back);
#else /* OPENSSL_VERSION_NUMBER >= 0x10101000L */
SSL_CTX_set_client_hello_cb(context->tls.ctx,
tls_client_hello_call_back,
NULL);
#endif /* OPENSSL_VERSION_NUMBER >= 0x10101000L */
/* TLS Only */
SSL_CTX_set_alpn_select_cb(context->tls.ctx, server_alpn_callback, NULL);
}
}
if (!context->dtls.ssl) {
/* This is set up to handle new incoming sessions to a server */
context->dtls.ssl = SSL_new(context->dtls.ctx);
if (!context->dtls.ssl)
return 0;
bio = BIO_new(context->dtls.meth);
if (!bio) {
SSL_free (context->dtls.ssl);
context->dtls.ssl = NULL;
return 0;
}
SSL_set_bio(context->dtls.ssl, bio, bio);
SSL_set_app_data(context->dtls.ssl, NULL);
SSL_set_options(context->dtls.ssl, SSL_OP_COOKIE_EXCHANGE);
SSL_set_mtu(context->dtls.ssl, COAP_DEFAULT_MTU);
}
context->psk_pki_enabled |= IS_PKI;
return 1;
}
int
coap_dtls_context_set_pki_root_cas(struct coap_context_t *ctx,
const char *ca_file,
const char *ca_dir
) {
coap_openssl_context_t *context =
((coap_openssl_context_t *)ctx->dtls_context);
if (context->dtls.ctx) {
if (!SSL_CTX_load_verify_locations(context->dtls.ctx, ca_file, ca_dir)) {
coap_log(LOG_WARNING, "Unable to install root CAs (%s/%s)\n",
ca_file ? ca_file : "NULL", ca_dir ? ca_dir : "NULL");
return 0;
}
}
if (context->tls.ctx) {
if (!SSL_CTX_load_verify_locations(context->tls.ctx, ca_file, ca_dir)) {
coap_log(LOG_WARNING, "Unable to install root CAs (%s/%s)\n",
ca_file ? ca_file : "NULL", ca_dir ? ca_dir : "NULL");
return 0;
}
}
return 1;
}
int
coap_dtls_context_check_keys_enabled(coap_context_t *ctx)
{
coap_openssl_context_t *context =
((coap_openssl_context_t *)ctx->dtls_context);
return context->psk_pki_enabled ? 1 : 0;
}
void coap_dtls_free_context(void *handle) {
size_t i;
coap_openssl_context_t *context = (coap_openssl_context_t *)handle;
if (context->dtls.ssl)
SSL_free(context->dtls.ssl);
if (context->dtls.ctx)
SSL_CTX_free(context->dtls.ctx);
if (context->dtls.cookie_hmac)
HMAC_CTX_free(context->dtls.cookie_hmac);
if (context->dtls.meth)
BIO_meth_free(context->dtls.meth);
if (context->dtls.bio_addr)
BIO_ADDR_free(context->dtls.bio_addr);
if ( context->tls.ctx )
SSL_CTX_free( context->tls.ctx );
if ( context->tls.meth )
BIO_meth_free( context->tls.meth );
for (i = 0; i < context->sni_count; i++) {
OPENSSL_free(context->sni_entry_list[i].sni);
#if OPENSSL_VERSION_NUMBER < 0x10101000L
SSL_CTX_free(context->sni_entry_list[i].ctx);
#endif /* OPENSSL_VERSION_NUMBER < 0x10101000L */
}
if (context->sni_count)
OPENSSL_free(context->sni_entry_list);
coap_free(context);
}
void * coap_dtls_new_server_session(coap_session_t *session) {
BIO *nbio = NULL;
SSL *nssl = NULL, *ssl = NULL;
coap_ssl_data *data;
coap_dtls_context_t *dtls = &((coap_openssl_context_t *)session->context->dtls_context)->dtls;
int r;
nssl = SSL_new(dtls->ctx);
if (!nssl)
goto error;
nbio = BIO_new(dtls->meth);
if (!nbio)
goto error;
SSL_set_bio(nssl, nbio, nbio);
SSL_set_app_data(nssl, NULL);
SSL_set_options(nssl, SSL_OP_COOKIE_EXCHANGE);
SSL_set_mtu(nssl, session->mtu);
ssl = dtls->ssl;
dtls->ssl = nssl;
nssl = NULL;
SSL_set_app_data(ssl, session);
data = (coap_ssl_data*)BIO_get_data(SSL_get_rbio(ssl));
data->session = session;
if (session->context->get_server_hint) {
char hint[128] = "";
size_t hint_len = session->context->get_server_hint(session, (uint8_t*)hint, sizeof(hint) - 1);
if (hint_len > 0 && hint_len < sizeof(hint)) {
hint[hint_len] = 0;
SSL_use_psk_identity_hint(ssl, hint);
}
}
r = SSL_accept(ssl);
if (r == -1) {
int err = SSL_get_error(ssl, r);
if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)
r = 0;
}
if (r == 0) {
SSL_free(ssl);
return NULL;
}
return ssl;
error:
if (nssl)
SSL_free(nssl);
return NULL;
}
static int
setup_client_ssl_session(coap_session_t *session, SSL *ssl
) {
coap_openssl_context_t *context = ((coap_openssl_context_t *)session->context->dtls_context);
if (context->psk_pki_enabled & IS_PSK) {
SSL_set_psk_client_callback(ssl, coap_dtls_psk_client_callback);
SSL_set_psk_server_callback(ssl, coap_dtls_psk_server_callback);
SSL_set_cipher_list(ssl, COAP_OPENSSL_PSK_CIPHERS);
}
if (context->psk_pki_enabled & IS_PKI) {
coap_dtls_pki_t *setup_data = &context->setup_data;
if (!setup_pki_ssl(ssl, setup_data, 0))
return 0;
/* libcoap is managing (D)TLS connection based on setup_data options */
if (session->proto == COAP_PROTO_TLS)
SSL_set_alpn_protos(ssl, coap_alpn, sizeof(coap_alpn));
/* Issue SNI if requested */
if (setup_data->client_sni &&
SSL_set_tlsext_host_name (ssl, setup_data->client_sni) != 1) {
coap_log(LOG_WARNING, "SSL_set_tlsext_host_name: set '%s' failed",
setup_data->client_sni);
}
/* Certificate Revocation */
if (setup_data->check_cert_revocation) {
X509_VERIFY_PARAM *param;
param = X509_VERIFY_PARAM_new();
X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK);
SSL_set1_param(ssl, param);
X509_VERIFY_PARAM_free(param);
}
/* Verify Peer */
if (setup_data->verify_peer_cert)
SSL_set_verify(ssl, SSL_VERIFY_PEER, tls_verify_call_back);
else
SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
/* Check CA Chain */
if (setup_data->cert_chain_validation)
SSL_set_verify_depth(ssl, setup_data->cert_chain_verify_depth);
}
return 1;
}
void *coap_dtls_new_client_session(coap_session_t *session) {
BIO *bio = NULL;
SSL *ssl = NULL;
coap_ssl_data *data;
int r;
coap_openssl_context_t *context = ((coap_openssl_context_t *)session->context->dtls_context);
coap_dtls_context_t *dtls = &context->dtls;
ssl = SSL_new(dtls->ctx);
if (!ssl)
goto error;
bio = BIO_new(dtls->meth);
if (!bio)
goto error;
data = (coap_ssl_data *)BIO_get_data(bio);
data->session = session;
SSL_set_bio(ssl, bio, bio);
SSL_set_app_data(ssl, session);
SSL_set_options(ssl, SSL_OP_COOKIE_EXCHANGE);
SSL_set_mtu(ssl, session->mtu);
if (!setup_client_ssl_session(session, ssl))
goto error;
session->dtls_timeout_count = 0;
r = SSL_connect(ssl);
if (r == -1) {
int ret = SSL_get_error(ssl, r);
if (ret != SSL_ERROR_WANT_READ && ret != SSL_ERROR_WANT_WRITE)
r = 0;
}
if (r == 0)
goto error;
return ssl;
error:
if (ssl)
SSL_free(ssl);
return NULL;
}
void coap_dtls_session_update_mtu(coap_session_t *session) {
SSL *ssl = (SSL *)session->tls;
if (ssl)
SSL_set_mtu(ssl, session->mtu);
}
void coap_dtls_free_session(coap_session_t *session) {
SSL *ssl = (SSL *)session->tls;
if (ssl) {
if (!SSL_in_init(ssl) && !(SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN)) {
int r = SSL_shutdown(ssl);
if (r == 0) r = SSL_shutdown(ssl);
}
SSL_free(ssl);
session->tls = NULL;
if (session->context)
coap_handle_event(session->context, COAP_EVENT_DTLS_CLOSED, session);
}
}
int coap_dtls_send(coap_session_t *session,
const uint8_t *data, size_t data_len) {
int r;
SSL *ssl = (SSL *)session->tls;
assert(ssl != NULL);
session->dtls_event = -1;
r = SSL_write(ssl, data, (int)data_len);
if (r <= 0) {
int err = SSL_get_error(ssl, r);
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
r = 0;
} else {
coap_log(LOG_WARNING, "coap_dtls_send: cannot send PDU\n");
if (err == SSL_ERROR_ZERO_RETURN)
session->dtls_event = COAP_EVENT_DTLS_CLOSED;
else if (err == SSL_ERROR_SSL)
session->dtls_event = COAP_EVENT_DTLS_ERROR;
r = -1;
}
}
if (session->dtls_event >= 0) {
/* COAP_EVENT_DTLS_CLOSED event reported in coap_session_disconnected() */
if (session->dtls_event != COAP_EVENT_DTLS_CLOSED)
coap_handle_event(session->context, session->dtls_event, session);
if (session->dtls_event == COAP_EVENT_DTLS_ERROR ||
session->dtls_event == COAP_EVENT_DTLS_CLOSED) {
coap_session_disconnected(session, COAP_NACK_TLS_FAILED);
r = -1;
}
}
return r;
}
int coap_dtls_is_context_timeout(void) {
return 0;
}
coap_tick_t coap_dtls_get_context_timeout(void *dtls_context) {
(void)dtls_context;
return 0;
}
coap_tick_t coap_dtls_get_timeout(coap_session_t *session, coap_tick_t now UNUSED) {
SSL *ssl = (SSL *)session->tls;
coap_ssl_data *ssl_data;
assert(ssl != NULL);
ssl_data = (coap_ssl_data*)BIO_get_data(SSL_get_rbio(ssl));
return ssl_data->timeout;
}
void coap_dtls_handle_timeout(coap_session_t *session) {
SSL *ssl = (SSL *)session->tls;
assert(ssl != NULL);
if (((session->state == COAP_SESSION_STATE_HANDSHAKE) &&
(++session->dtls_timeout_count > session->max_retransmit)) ||
(DTLSv1_handle_timeout(ssl) < 0)) {
/* Too many retries */
coap_session_disconnected(session, COAP_NACK_TLS_FAILED);
}
}
int coap_dtls_hello(coap_session_t *session,
const uint8_t *data, size_t data_len) {
coap_dtls_context_t *dtls = &((coap_openssl_context_t *)session->context->dtls_context)->dtls;
coap_ssl_data *ssl_data;
int r;
SSL_set_mtu(dtls->ssl, session->mtu);
ssl_data = (coap_ssl_data*)BIO_get_data(SSL_get_rbio(dtls->ssl));
ssl_data->session = session;
ssl_data->pdu = data;
ssl_data->pdu_len = (unsigned)data_len;
r = DTLSv1_listen(dtls->ssl, dtls->bio_addr);
if (r <= 0) {
int err = SSL_get_error(dtls->ssl, r);
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
/* Got a ClientHello, sent-out a VerifyRequest */
r = 0;
}
} else {
/* Got a valid answer to a VerifyRequest */
r = 1;
}
return r;
}
int coap_dtls_receive(coap_session_t *session,
const uint8_t *data, size_t data_len) {
coap_ssl_data *ssl_data;
SSL *ssl = (SSL *)session->tls;
int r;
assert(ssl != NULL);
int in_init = SSL_in_init(ssl);
uint8_t pdu[COAP_RXBUFFER_SIZE];
ssl_data = (coap_ssl_data*)BIO_get_data(SSL_get_rbio(ssl));
ssl_data->pdu = data;
ssl_data->pdu_len = (unsigned)data_len;
session->dtls_event = -1;
r = SSL_read(ssl, pdu, (int)sizeof(pdu));
if (r > 0) {
return coap_handle_dgram(session->context, session, pdu, (size_t)r);
} else {
int err = SSL_get_error(ssl, r);
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
if (in_init && SSL_is_init_finished(ssl)) {
coap_log(COAP_LOG_CIPHERS, "* %s: Using cipher: %s\n",
coap_session_str(session), SSL_get_cipher_name(ssl));
coap_handle_event(session->context, COAP_EVENT_DTLS_CONNECTED, session);
coap_session_connected(session);
}
r = 0;
} else {
if (err == SSL_ERROR_ZERO_RETURN) /* Got a close notify alert from the remote side */
session->dtls_event = COAP_EVENT_DTLS_CLOSED;
else if (err == SSL_ERROR_SSL)
session->dtls_event = COAP_EVENT_DTLS_ERROR;
r = -1;
}
if (session->dtls_event >= 0) {
/* COAP_EVENT_DTLS_CLOSED event reported in coap_session_disconnected() */
if (session->dtls_event != COAP_EVENT_DTLS_CLOSED)
coap_handle_event(session->context, session->dtls_event, session);
if (session->dtls_event == COAP_EVENT_DTLS_ERROR ||
session->dtls_event == COAP_EVENT_DTLS_CLOSED) {
coap_session_disconnected(session, COAP_NACK_TLS_FAILED);
r = -1;
}
}
}
return r;
}
unsigned int coap_dtls_get_overhead(coap_session_t *session) {
unsigned int overhead = 37;
const SSL_CIPHER *s_ciph = NULL;
if (session->tls != NULL)
s_ciph = SSL_get_current_cipher(session->tls);
if ( s_ciph ) {
unsigned int ivlen, maclen, blocksize = 1, pad = 0;
const EVP_CIPHER *e_ciph;
const EVP_MD *e_md;
char cipher[128];
e_ciph = EVP_get_cipherbynid(SSL_CIPHER_get_cipher_nid(s_ciph));
switch (EVP_CIPHER_mode(e_ciph)) {
case EVP_CIPH_GCM_MODE:
ivlen = EVP_GCM_TLS_EXPLICIT_IV_LEN;
maclen = EVP_GCM_TLS_TAG_LEN;
break;
case EVP_CIPH_CCM_MODE:
ivlen = EVP_CCM_TLS_EXPLICIT_IV_LEN;
SSL_CIPHER_description(s_ciph, cipher, sizeof(cipher));
if (strstr(cipher, "CCM8"))
maclen = 8;
else
maclen = 16;
break;
case EVP_CIPH_CBC_MODE:
e_md = EVP_get_digestbynid(SSL_CIPHER_get_digest_nid(s_ciph));
blocksize = EVP_CIPHER_block_size(e_ciph);
ivlen = EVP_CIPHER_iv_length(e_ciph);
pad = 1;
maclen = EVP_MD_size(e_md);
break;
case EVP_CIPH_STREAM_CIPHER:
/* Seen with PSK-CHACHA20-POLY1305 */
ivlen = 8;
maclen = 8;
break;
default:
SSL_CIPHER_description(s_ciph, cipher, sizeof(cipher));
coap_log(LOG_WARNING, "Unknown overhead for DTLS with cipher %s\n",
cipher);
ivlen = 8;
maclen = 16;
break;
}
overhead = DTLS1_RT_HEADER_LENGTH + ivlen + maclen + blocksize - 1 + pad;
}
return overhead;
}
void *coap_tls_new_client_session(coap_session_t *session, int *connected) {
BIO *bio = NULL;
SSL *ssl = NULL;
int r;
coap_openssl_context_t *context = ((coap_openssl_context_t *)session->context->dtls_context);
coap_tls_context_t *tls = &context->tls;
*connected = 0;
ssl = SSL_new(tls->ctx);
if (!ssl)
goto error;
bio = BIO_new(tls->meth);
if (!bio)
goto error;
BIO_set_data(bio, session);
SSL_set_bio(ssl, bio, bio);
SSL_set_app_data(ssl, session);
if (!setup_client_ssl_session(session, ssl))
return 0;
r = SSL_connect(ssl);
if (r == -1) {
int ret = SSL_get_error(ssl, r);
if (ret != SSL_ERROR_WANT_READ && ret != SSL_ERROR_WANT_WRITE)
r = 0;
if (ret == SSL_ERROR_WANT_READ)
session->sock.flags |= COAP_SOCKET_WANT_READ;
if (ret == SSL_ERROR_WANT_WRITE) {
session->sock.flags |= COAP_SOCKET_WANT_WRITE;
#ifdef COAP_EPOLL_SUPPORT
coap_epoll_ctl_mod(&session->sock,
EPOLLOUT |
((session->sock.flags & COAP_SOCKET_WANT_READ) ?
EPOLLIN : 0),
__func__);
#endif /* COAP_EPOLL_SUPPORT */
}
}
if (r == 0)
goto error;
*connected = SSL_is_init_finished(ssl);
return ssl;
error:
if (ssl)
SSL_free(ssl);
return NULL;
}
void *coap_tls_new_server_session(coap_session_t *session, int *connected) {
BIO *bio = NULL;
SSL *ssl = NULL;
coap_tls_context_t *tls = &((coap_openssl_context_t *)session->context->dtls_context)->tls;
int r;
*connected = 0;
ssl = SSL_new(tls->ctx);
if (!ssl)
goto error;
bio = BIO_new(tls->meth);
if (!bio)
goto error;
BIO_set_data(bio, session);
SSL_set_bio(ssl, bio, bio);
SSL_set_app_data(ssl, session);
if (session->context->get_server_hint) {
char hint[128] = "";
size_t hint_len = session->context->get_server_hint(session, (uint8_t*)hint, sizeof(hint) - 1);
if (hint_len > 0 && hint_len < sizeof(hint)) {
hint[hint_len] = 0;
SSL_use_psk_identity_hint(ssl, hint);
}
}
r = SSL_accept(ssl);
if (r == -1) {
int err = SSL_get_error(ssl, r);
if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)
r = 0;
if (err == SSL_ERROR_WANT_READ)
session->sock.flags |= COAP_SOCKET_WANT_READ;
if (err == SSL_ERROR_WANT_WRITE) {
session->sock.flags |= COAP_SOCKET_WANT_WRITE;
#ifdef COAP_EPOLL_SUPPORT
coap_epoll_ctl_mod(&session->sock,
EPOLLOUT |
((session->sock.flags & COAP_SOCKET_WANT_READ) ?
EPOLLIN : 0),
__func__);
#endif /* COAP_EPOLL_SUPPORT */
}
}
if (r == 0)
goto error;
*connected = SSL_is_init_finished(ssl);
return ssl;
error:
if (ssl)
SSL_free(ssl);
return NULL;
}
void coap_tls_free_session(coap_session_t *session) {
SSL *ssl = (SSL *)session->tls;
if (ssl) {
if (!SSL_in_init(ssl) && !(SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN)) {
int r = SSL_shutdown(ssl);
if (r == 0) r = SSL_shutdown(ssl);
}
SSL_free(ssl);
session->tls = NULL;
if (session->context)
coap_handle_event(session->context, COAP_EVENT_DTLS_CLOSED, session);
}
}
ssize_t coap_tls_write(coap_session_t *session,
const uint8_t *data,
size_t data_len
) {
SSL *ssl = (SSL *)session->tls;
int r, in_init;
if (ssl == NULL)
return -1;
in_init = !SSL_is_init_finished(ssl);
session->dtls_event = -1;
r = SSL_write(ssl, data, (int)data_len);
if (r <= 0) {
int err = SSL_get_error(ssl, r);
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
if (in_init && SSL_is_init_finished(ssl)) {
coap_log(COAP_LOG_CIPHERS, "* %s: Using cipher: %s\n",
coap_session_str(session), SSL_get_cipher_name(ssl));
coap_handle_event(session->context, COAP_EVENT_DTLS_CONNECTED, session);
coap_session_send_csm(session);
}
if (err == SSL_ERROR_WANT_READ)
session->sock.flags |= COAP_SOCKET_WANT_READ;
if (err == SSL_ERROR_WANT_WRITE) {
session->sock.flags |= COAP_SOCKET_WANT_WRITE;
#ifdef COAP_EPOLL_SUPPORT
coap_epoll_ctl_mod(&session->sock,
EPOLLOUT |
((session->sock.flags & COAP_SOCKET_WANT_READ) ?
EPOLLIN : 0),
__func__);
#endif /* COAP_EPOLL_SUPPORT */
}
r = 0;
} else {
coap_log(LOG_WARNING, "***%s: coap_tls_write: cannot send PDU\n",
coap_session_str(session));
if (err == SSL_ERROR_ZERO_RETURN)
session->dtls_event = COAP_EVENT_DTLS_CLOSED;
else if (err == SSL_ERROR_SSL)
session->dtls_event = COAP_EVENT_DTLS_ERROR;
r = -1;
}
} else if (in_init && SSL_is_init_finished(ssl)) {
coap_log(COAP_LOG_CIPHERS, "* %s: Using cipher: %s\n",
coap_session_str(session), SSL_get_cipher_name(ssl));
coap_handle_event(session->context, COAP_EVENT_DTLS_CONNECTED, session);
coap_session_send_csm(session);
}
if (session->dtls_event >= 0) {
/* COAP_EVENT_DTLS_CLOSED event reported in coap_session_disconnected() */
if (session->dtls_event != COAP_EVENT_DTLS_CLOSED)
coap_handle_event(session->context, session->dtls_event, session);
if (session->dtls_event == COAP_EVENT_DTLS_ERROR ||
session->dtls_event == COAP_EVENT_DTLS_CLOSED) {
coap_session_disconnected(session, COAP_NACK_TLS_FAILED);
r = -1;
}
}
return r;
}
ssize_t coap_tls_read(coap_session_t *session,
uint8_t *data,
size_t data_len
) {
SSL *ssl = (SSL *)session->tls;
int r, in_init;
if (ssl == NULL)
return -1;
in_init = !SSL_is_init_finished(ssl);
session->dtls_event = -1;
r = SSL_read(ssl, data, (int)data_len);
if (r <= 0) {
int err = SSL_get_error(ssl, r);
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
if (in_init && SSL_is_init_finished(ssl)) {
coap_log(COAP_LOG_CIPHERS, "* %s: Using cipher: %s\n",
coap_session_str(session), SSL_get_cipher_name(ssl));
coap_handle_event(session->context, COAP_EVENT_DTLS_CONNECTED, session);
coap_session_send_csm(session);
}
if (err == SSL_ERROR_WANT_READ)
session->sock.flags |= COAP_SOCKET_WANT_READ;
if (err == SSL_ERROR_WANT_WRITE) {
session->sock.flags |= COAP_SOCKET_WANT_WRITE;
#ifdef COAP_EPOLL_SUPPORT
coap_epoll_ctl_mod(&session->sock,
EPOLLOUT |
((session->sock.flags & COAP_SOCKET_WANT_READ) ?
EPOLLIN : 0),
__func__);
#endif /* COAP_EPOLL_SUPPORT */
}
r = 0;
} else {
if (err == SSL_ERROR_ZERO_RETURN) /* Got a close notify alert from the remote side */
session->dtls_event = COAP_EVENT_DTLS_CLOSED;
else if (err == SSL_ERROR_SSL)
session->dtls_event = COAP_EVENT_DTLS_ERROR;
r = -1;
}
} else if (in_init && SSL_is_init_finished(ssl)) {
coap_log(COAP_LOG_CIPHERS, "* %s: Using cipher: %s\n",
coap_session_str(session), SSL_get_cipher_name(ssl));
coap_handle_event(session->context, COAP_EVENT_DTLS_CONNECTED, session);
coap_session_send_csm(session);
}
if (session->dtls_event >= 0) {
/* COAP_EVENT_DTLS_CLOSED event reported in coap_session_disconnected() */
if (session->dtls_event != COAP_EVENT_DTLS_CLOSED)
coap_handle_event(session->context, session->dtls_event, session);
if (session->dtls_event == COAP_EVENT_DTLS_ERROR ||
session->dtls_event == COAP_EVENT_DTLS_CLOSED) {
coap_session_disconnected(session, COAP_NACK_TLS_FAILED);
r = -1;
}
}
return r;
}
#else /* !HAVE_OPENSSL */
#ifdef __clang__
/* Make compilers happy that do not like empty modules. As this function is
* never used, we ignore -Wunused-function at the end of compiling this file
*/
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
static inline void dummy(void) {
}
#endif /* HAVE_OPENSSL */
coap_session_t::mtu
unsigned mtu
path or CSM mtu
Definition: coap_session.h:65
coap_dtls_set_log_level
void coap_dtls_set_log_level(int level)
Sets the (D)TLS logging level to the specified level.
Definition: coap_notls.c:74
coap_session_send_csm
void coap_session_send_csm(coap_session_t *session)
Notify session transport has just connected and CSM exchange can now start.
Definition: coap_session.c:288
COAP_SOCKET_EMPTY
#define COAP_SOCKET_EMPTY
coap_socket_flags_t values
Definition: coap_io.h:76
coap_dtls_hello
int coap_dtls_hello(coap_session_t *session UNUSED, const uint8_t *data UNUSED, size_t data_len UNUSED)
Definition: coap_notls.c:139
COAP_RXBUFFER_SIZE
#define COAP_RXBUFFER_SIZE
Definition: coap_io.h:18
COAP_DEFAULT_MTU
#define COAP_DEFAULT_MTU
Definition: pdu.h:32
coap_session_t
Definition: coap_session.h:59
COAP_ASN1_PKEY_NONE
NONE.
Definition: coap_dtls.h:113
coap_dtls_pki_t::allow_self_signed
uint8_t allow_self_signed
1 if self signed certs are allowed
Definition: coap_dtls.h:199
dummy
static void dummy(void)
Definition: coap_openssl.c:2330
coap_tls_free_session
void coap_tls_free_session(coap_session_t *coap_session UNUSED)
Definition: coap_notls.c:158
coap_session_t::context
struct coap_context_t * context
session&#39;s context
Definition: coap_session.h:72
COAP_PKI_KEY_ASN1
The PKI key type is ASN.1 (DER)
Definition: coap_dtls.h:135
COAP_ASN1_PKEY_EC
EC type.
Definition: coap_dtls.h:123
coap_session_t::tls
void * tls
security parameters
Definition: coap_session.h:73
coap_dtls_key_t::key_type
coap_pki_key_t key_type
key format type
Definition: coap_dtls.h:164
COAP_SESSION_STATE_HANDSHAKE
#define COAP_SESSION_STATE_HANDSHAKE
Definition: coap_session.h:55
coap_dtls_receive
int coap_dtls_receive(coap_session_t *session UNUSED, const uint8_t *data UNUSED, size_t data_len UNUSED)
Definition: coap_notls.c:131
coap_dtls_context_check_keys_enabled
int coap_dtls_context_check_keys_enabled(coap_context_t *ctx UNUSED)
Definition: coap_notls.c:63
COAP_EVENT_DTLS_RENEGOTIATE
#define COAP_EVENT_DTLS_RENEGOTIATE
Definition: coap_event.h:35
coap_context_t::psk_key_len
size_t psk_key_len
Definition: net.h:203
coap_tls_read
ssize_t coap_tls_read(coap_session_t *session UNUSED, uint8_t *data UNUSED, size_t data_len UNUSED)
Definition: coap_notls.c:168
coap_epoll_ctl_mod
void coap_epoll_ctl_mod(coap_socket_t *sock, uint32_t events, const char *func)
coap_dtls_get_log_level
int coap_dtls_get_log_level(void)
Get the current (D)TLS logging.
Definition: coap_notls.c:79
COAP_PROTO_DTLS
#define COAP_PROTO_DTLS
Definition: pdu.h:345
coap_dtls_new_client_session
void * coap_dtls_new_client_session(coap_session_t *session UNUSED)
Definition: coap_notls.c:96
COAP_DTLS_ROLE_SERVER
Internal function invoked for server.
Definition: coap_dtls.h:268
COAP_ASN1_PKEY_HKDF
HKDF type.
Definition: coap_dtls.h:127
coap_dtls_is_supported
int coap_dtls_is_supported(void)
Check whether DTLS is available.
Definition: coap_notls.c:21
coap_dtls_free_context
void coap_dtls_free_context(void *handle UNUSED)
Definition: coap_notls.c:89
coap_dtls_context_set_pki
int coap_dtls_context_set_pki(coap_context_t *ctx UNUSED, coap_dtls_pki_t *setup_data UNUSED, coap_dtls_role_t role UNUSED)
Definition: coap_notls.c:39
coap_tls_new_server_session
void * coap_tls_new_server_session(coap_session_t *session UNUSED, int *connected UNUSED)
Definition: coap_notls.c:154
coap_tls_write
ssize_t coap_tls_write(coap_session_t *session UNUSED, const uint8_t *data UNUSED, size_t data_len UNUSED)
Definition: coap_notls.c:161
coap_dtls_pki_t::sni_call_back_arg
void * sni_call_back_arg
Passed in to the sni call-back function.
Definition: coap_dtls.h:229
coap_socket_read
ssize_t coap_socket_read(coap_socket_t *sock, uint8_t *data, size_t data_len)
Definition: coap_io.c:735
coap_session_send
ssize_t coap_session_send(coap_session_t *session, const uint8_t *data, size_t datalen)
Function interface for datagram data transmission.
Definition: coap_session.c:216
coap_session_t::dtls_event
int dtls_event
Tracking any (D)TLS events on this sesison.
Definition: coap_session.h:96
coap_dtls_pki_t::verify_peer_cert
uint8_t verify_peer_cert
Set to 1 to support this version of the struct.
Definition: coap_dtls.h:197
LOG_DEBUG
Debug.
Definition: coap_debug.h:55
coap_dtls_pki_t::allow_no_crl
uint8_t allow_no_crl
1 ignore if CRL not there
Definition: coap_dtls.h:204
coap_tls_version_t::version
uint64_t version
(D)TLS runtime Library Version
Definition: coap_dtls.h:52
coap_context_t::get_client_psk
size_t(* get_client_psk)(const coap_session_t *session, const uint8_t *hint, size_t hint_len, uint8_t *identity, size_t *identity_len, size_t max_identity_len, uint8_t *psk, size_t max_psk_len)
Definition: net.h:195
COAP_ASN1_PKEY_RSA2
RSA2 type.
Definition: coap_dtls.h:115
coap_dtls_pki_t::validate_sni_call_back
coap_dtls_sni_callback_t validate_sni_call_back
SNI check call-back function.
Definition: coap_dtls.h:228
COAP_ASN1_PKEY_HMAC
HMAC type.
Definition: coap_dtls.h:124
coap_tls_new_client_session
void * coap_tls_new_client_session(coap_session_t *session UNUSED, int *connected UNUSED)
Definition: coap_notls.c:150
COAP_ASN1_PKEY_DSA1
DSA1 type.
Definition: coap_dtls.h:117
coap_session_str
const char * coap_session_str(const coap_session_t *session)
Get session description.
Definition: coap_session.c:1049
COAP_ASN1_PKEY_DSA
DSA type.
Definition: coap_dtls.h:116
coap_session_t::max_retransmit
unsigned int max_retransmit
maximum re-transmit count (default 4)
Definition: coap_session.h:92
coap_malloc
COAP_STATIC_INLINE void * coap_malloc(size_t size)
Wrapper function to coap_malloc_type() for backwards compatibility.
Definition: mem.h:75
coap_dtls_pki_t::additional_tls_setup_call_back
coap_dtls_security_setup_t additional_tls_setup_call_back
Additional Security call-back handler that is invoked when libcoap has done the standerd, defined validation checks at the TLS level, If not NULL, called from within the TLS Client Hello connection setup.
Definition: coap_dtls.h:236
coap_tls_is_supported
int coap_tls_is_supported(void)
Check whether TLS is available.
Definition: coap_notls.c:26
coap_tls_version_t::type
coap_tls_library_t type
Library type.
Definition: coap_dtls.h:53
coap_get_tls_library_version
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:31
coap_pki_key_pem_t::private_key
const char * private_key
File location of Private Key in PEM format.
Definition: coap_dtls.h:144
coap_dtls_pki_t::require_peer_cert
uint8_t require_peer_cert
1 if peer cert is required
Definition: coap_dtls.h:198
coap_session_t::proto
coap_proto_t proto
protocol used
Definition: coap_session.h:60
coap_dtls_pki_t::pki_key
coap_dtls_key_t pki_key
PKI key definition.
Definition: coap_dtls.h:242
coap_dtls_key_t::pem
coap_pki_key_pem_t pem
for PEM keys
Definition: coap_dtls.h:166
coap_dtls_pki_t::client_sni
char * client_sni
If not NULL, SNI to use in client TLS setup.
Definition: coap_dtls.h:238
LOG_WARNING
Warning.
Definition: coap_debug.h:52
coap_tick_t
uint64_t coap_tick_t
This data type represents internal timer ticks with COAP_TICKS_PER_SECOND resolution.
Definition: coap_time.h:93
coap_dtls_key_t
The structure that holds the PKI key information.
Definition: coap_dtls.h:163
coap_dtls_get_overhead
unsigned int coap_dtls_get_overhead(coap_session_t *session UNUSED)
Definition: coap_notls.c:146
coap_pki_key_asn1_t::public_cert
const uint8_t * public_cert
ASN1 (DER) Public Cert.
Definition: coap_dtls.h:152
coap_pki_key_pem_t::ca_file
const char * ca_file
File location of Common CA in PEM format.
Definition: coap_dtls.h:142
coap_pki_key_asn1_t::ca_cert_len
size_t ca_cert_len
ASN1 CA Cert length.
Definition: coap_dtls.h:154
COAP_PKI_KEY_PEM
The PKI key type is PEM.
Definition: coap_dtls.h:134
COAP_ASN1_PKEY_RSA
RSA type.
Definition: coap_dtls.h:114
coap_session_t::sock
coap_socket_t sock
socket object for the session, if any
Definition: coap_session.h:70
coap_socket_write
ssize_t coap_socket_write(coap_socket_t *sock, const uint8_t *data, size_t data_len)
Definition: coap_io.c:690
dtls_log_level
static int dtls_log_level
Definition: coap_notls.c:68
coap_tls_version_t
The structure used for returning the underlying (D)TLS library information.
Definition: coap_dtls.h:51
COAP_EVENT_DTLS_CLOSED
#define COAP_EVENT_DTLS_CLOSED
(D)TLS events for COAP_PROTO_DTLS and COAP_PROTO_TLS
Definition: coap_event.h:33
COAP_ASN1_PKEY_DSA4
DSA4 type.
Definition: coap_dtls.h:120
coap_handle_dgram
int coap_handle_dgram(coap_context_t *ctx, coap_session_t *session, uint8_t *msg, size_t msg_len)
Parses and interprets a CoAP datagram with context ctx.
Definition: net.c:1557
coap_dtls_context_set_psk
int coap_dtls_context_set_psk(coap_context_t *ctx UNUSED, const char *hint UNUSED, coap_dtls_role_t role UNUSED)
Definition: coap_notls.c:55
COAP_PROTO_TLS
#define COAP_PROTO_TLS
Definition: pdu.h:347
coap_dtls_pki_t::cert_chain_validation
uint8_t cert_chain_validation
1 if to check cert_chain_verify_depth
Definition: coap_dtls.h:201
coap_free
COAP_STATIC_INLINE void coap_free(void *object)
Wrapper function to coap_free_type() for backwards compatibility.
Definition: mem.h:82
LOG_ERR
Error.
Definition: coap_debug.h:51
coap_context_t::dtls_context
void * dtls_context
Definition: net.h:199
COAP_TLS_LIBRARY_OPENSSL
Using OpenSSL library.
Definition: coap_dtls.h:43
coap_dtls_key_t::key
union coap_dtls_key_t::@1 key
coap_session_t::state
coap_session_state_t state
current state of relationaship with peer
Definition: coap_session.h:62
coap_pki_key_asn1_t::private_key
const uint8_t * private_key
ASN1 (DER) Private Key.
Definition: coap_dtls.h:153
COAP_ASN1_PKEY_DSA3
DSA3 type.
Definition: coap_dtls.h:119
coap_dtls_pki_t::validate_cn_call_back
coap_dtls_cn_callback_t validate_cn_call_back
CN check call-back function.
Definition: coap_dtls.h:220
coap_dtls_pki_t::allow_expired_certs
uint8_t allow_expired_certs
1 if expired certs are allowed
Definition: coap_dtls.h:200
coap_dtls_pki_t::check_cert_revocation
uint8_t check_cert_revocation
1 if revocation checks wanted
Definition: coap_dtls.h:203
coap_dtls_role_t
coap_dtls_role_t
Definition: coap_dtls.h:266
coap_context_t::get_server_hint
size_t(* get_server_hint)(const coap_session_t *session, uint8_t *hint, size_t max_hint_len)
Definition: net.h:197
coap_dtls_free_session
void coap_dtls_free_session(coap_session_t *coap_session UNUSED)
Definition: coap_notls.c:100
COAP_EVENT_DTLS_ERROR
#define COAP_EVENT_DTLS_ERROR
Definition: coap_event.h:36
COAP_EVENT_DTLS_CONNECTED
#define COAP_EVENT_DTLS_CONNECTED
Definition: coap_event.h:34
coap_handle_event
int coap_handle_event(coap_context_t *context, coap_event_t event, coap_session_t *session)
Invokes the event handler of context for the given event and data.
Definition: net.c:2542
coap_dtls_pki_t::cn_call_back_arg
void * cn_call_back_arg
Passed in to the CN call-back function.
Definition: coap_dtls.h:221
coap_dtls_new_server_session
void * coap_dtls_new_server_session(coap_session_t *session UNUSED)
Definition: coap_notls.c:92
coap_session_connected
void coap_session_connected(coap_session_t *session)
Notify session that it has just connected or reconnected.
Definition: coap_session.c:330
coap_dtls_pki_t::allow_expired_crl
uint8_t allow_expired_crl
1 if expired crl is allowed
Definition: coap_dtls.h:205
COAP_SOCKET_WANT_READ
#define COAP_SOCKET_WANT_READ
non blocking socket is waiting for reading
Definition: coap_io.h:80
coap_dtls_pki_t
The structure used for defining the PKI setup data to be used.
Definition: coap_dtls.h:193
coap_dtls_context_set_pki_root_cas
int coap_dtls_context_set_pki_root_cas(struct coap_context_t *ctx UNUSED, const char *ca_file UNUSED, const char *ca_path UNUSED)
Definition: coap_notls.c:47
coap_pki_key_asn1_t::private_key_type
coap_asn1_privatekey_type_t private_key_type
Private Key Type.
Definition: coap_dtls.h:157
coap_dtls_send
int coap_dtls_send(coap_session_t *session UNUSED, const uint8_t *data UNUSED, size_t data_len UNUSED)
Definition: coap_notls.c:107
coap_dtls_pki_t::cert_chain_verify_depth
uint8_t cert_chain_verify_depth
recommended depth is 3
Definition: coap_dtls.h:202
COAP_LOG_CIPHERS
CipherInfo.
Definition: coap_debug.h:56
coap_context_t::get_server_psk
size_t(* get_server_psk)(const coap_session_t *session, const uint8_t *identity, size_t identity_len, uint8_t *psk, size_t max_psk_len)
Definition: net.h:196
coap_ticks_from_rt_us
coap_tick_t coap_ticks_from_rt_us(uint64_t t)
Helper function that converts POSIX wallclock time in us to coap ticks.
coap_dtls_handle_timeout
void coap_dtls_handle_timeout(coap_session_t *session UNUSED)
Definition: coap_notls.c:127
coap_pki_key_pem_t::public_cert
const char * public_cert
File location of Public Cert in PEM format.
Definition: coap_dtls.h:143
coap_context_t::psk_key
uint8_t * psk_key
Definition: net.h:202
coap_dtls_startup
void coap_dtls_startup(void)
Initialize the underlying (D)TLS Library layer.
Definition: coap_notls.c:70
coap_dtls_get_timeout
coap_tick_t coap_dtls_get_timeout(coap_session_t *session UNUSED, coap_tick_t now UNUSED)
Definition: coap_notls.c:123
COAP_ASN1_PKEY_DH
DH type.
Definition: coap_dtls.h:121
COAP_NACK_TLS_FAILED
Definition: coap_io.h:230
COAP_ASN1_PKEY_DSA2
DSA2 type.
Definition: coap_dtls.h:118
coap_socket_t::flags
coap_socket_flags_t flags
Definition: coap_io.h:62
coap_session_disconnected
void coap_session_disconnected(coap_session_t *session, coap_nack_reason_t reason)
Notify session that it has failed.
Definition: coap_session.c:387
coap_log
#define coap_log(level,...)
Logging function.
Definition: coap_debug.h:129
coap_pki_key_asn1_t::ca_cert
const uint8_t * ca_cert
ASN1 (DER) Common CA Cert.
Definition: coap_dtls.h:151
uint8_t
unsigned char uint8_t
Definition: uthash.h:79
COAP_ASN1_PKEY_TLS1_PRF
TLS1_PRF type.
Definition: coap_dtls.h:126
coap_pki_key_asn1_t::public_cert_len
size_t public_cert_len
ASN1 Public Cert length.
Definition: coap_dtls.h:155
UNUSED
#define UNUSED
Definition: coap_notls.c:17
prng
#define prng(Buf, Length)
Fills Buf with Length bytes of random data.
Definition: prng.h:112
coap_dtls_get_context_timeout
coap_tick_t coap_dtls_get_context_timeout(void *dtls_context UNUSED)
Definition: coap_notls.c:118
COAP_ASN1_PKEY_DHX
DHX type.
Definition: coap_dtls.h:122
coap_tls_version_t::built_version
uint64_t built_version
(D)TLS Built against Library Version
Definition: coap_dtls.h:54
coap_dtls_session_update_mtu
void coap_dtls_session_update_mtu(coap_session_t *session UNUSED)
Definition: coap_notls.c:103
LOG_INFO
Information.
Definition: coap_debug.h:54
coap_dtls_new_context
void * coap_dtls_new_context(struct coap_context_t *coap_context UNUSED)
Definition: coap_notls.c:84
coap_session_t::dtls_timeout_count
unsigned int dtls_timeout_count
dtls setup retry counter
Definition: coap_session.h:95
COAP_ASN1_PKEY_CMAC
CMAC type.
Definition: coap_dtls.h:125
coap_context_t
The CoAP stack&#39;s global state is stored in a coap_context_t object.
Definition: net.h:147
coap_dtls_is_context_timeout
int coap_dtls_is_context_timeout(void)
Check if timeout is handled per CoAP session or per CoAP context.
Definition: coap_notls.c:114
coap_pki_key_asn1_t::private_key_len
size_t private_key_len
ASN1 Private Key length.
Definition: coap_dtls.h:156
coap_internal.h
Pulls together all the internal only header files.
coap_dtls_key_t::asn1
coap_pki_key_asn1_t asn1
for ASN.1 (DER) keys
Definition: coap_dtls.h:167
COAP_SOCKET_WANT_WRITE
#define COAP_SOCKET_WANT_WRITE
non blocking socket is waiting for writing
Definition: coap_io.h:81