mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-09-03 21:51:57 +00:00
Add support for DTLS send/sendto function. For DTLS clients, send function will try to establish DTLS connection before sending data. If DTLS handshake fails, it will return an error. For DTLS servers, send function requires to have DTLS connection already established. Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
1605 lines
35 KiB
C
1605 lines
35 KiB
C
/*
|
|
* Copyright (c) 2018 Intel Corporation
|
|
* Copyright (c) 2018 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#if defined(CONFIG_NET_DEBUG_SOCKETS)
|
|
#define SYS_LOG_DOMAIN "net/tls"
|
|
#define NET_LOG_ENABLED 1
|
|
#endif
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <init.h>
|
|
#include <misc/util.h>
|
|
#include <net/net_context.h>
|
|
#include <net/socket.h>
|
|
|
|
#if defined(CONFIG_MBEDTLS)
|
|
#if !defined(CONFIG_MBEDTLS_CFG_FILE)
|
|
#include "mbedtls/config.h"
|
|
#else
|
|
#include CONFIG_MBEDTLS_CFG_FILE
|
|
#endif /* CONFIG_MBEDTLS_CFG_FILE */
|
|
|
|
#include <mbedtls/ctr_drbg.h>
|
|
#include <mbedtls/net_sockets.h>
|
|
#include <mbedtls/x509.h>
|
|
#include <mbedtls/x509_crt.h>
|
|
#include <mbedtls/ssl.h>
|
|
#include <mbedtls/ssl_cookie.h>
|
|
#include <mbedtls/error.h>
|
|
#include <mbedtls/debug.h>
|
|
#endif /* CONFIG_MBEDTLS */
|
|
|
|
#include "sockets_internal.h"
|
|
#include "tls_internal.h"
|
|
|
|
/** A list of secure tags that TLS context should use. */
|
|
struct sec_tag_list {
|
|
/** An array of secure tags referencing TLS credentials. */
|
|
sec_tag_t sec_tags[CONFIG_NET_SOCKETS_TLS_MAX_CREDENTIALS];
|
|
|
|
/** Number of configured secure tags. */
|
|
int sec_tag_count;
|
|
};
|
|
|
|
/** Timer context for DTLS. */
|
|
struct dtls_timing_context {
|
|
u32_t snapshot;
|
|
u32_t int_ms;
|
|
u32_t fin_ms;
|
|
};
|
|
|
|
/** TLS context information. */
|
|
struct tls_context {
|
|
/** Information whether TLS context is used. */
|
|
bool is_used;
|
|
|
|
/** Secure protocol version running on TLS context. */
|
|
enum net_ip_protocol_secure tls_version;
|
|
|
|
/** Socket flags passed to a socket call. */
|
|
int flags;
|
|
|
|
/** Information whether TLS context was initialized. */
|
|
bool is_initialized;
|
|
|
|
/** Information whether TLS handshake is complete or not */
|
|
bool tls_established;
|
|
|
|
/** TLS specific option values. */
|
|
struct {
|
|
/** Select which credentials to use with TLS. */
|
|
struct sec_tag_list sec_tag_list;
|
|
|
|
/** 0-terminated list of allowed ciphersuites (mbedTLS format).
|
|
*/
|
|
int ciphersuites[CONFIG_NET_SOCKETS_TLS_MAX_CIPHERSUITES + 1];
|
|
|
|
/** Information if hostname was explicitly set on a socket. */
|
|
bool is_hostname_set;
|
|
|
|
/** Peer verification level. */
|
|
s8_t verify_level;
|
|
|
|
/** DTLS role, client by default. */
|
|
s8_t role;
|
|
} options;
|
|
|
|
#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS)
|
|
/** Context information for DTLS timing. */
|
|
struct dtls_timing_context dtls_timing;
|
|
|
|
/** mbedTLS cookie context for DTLS */
|
|
mbedtls_ssl_cookie_ctx cookie;
|
|
|
|
/** DTLS peer address. */
|
|
struct sockaddr dtls_peer_addr;
|
|
|
|
/** DTLS peer address length. */
|
|
socklen_t dtls_peer_addrlen;
|
|
#endif /* CONFIG_NET_SOCKETS_ENABLE_DTLS */
|
|
|
|
#if defined(CONFIG_MBEDTLS)
|
|
/** mbedTLS context. */
|
|
mbedtls_ssl_context ssl;
|
|
|
|
/** mbedTLS configuration. */
|
|
mbedtls_ssl_config config;
|
|
|
|
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
|
/** mbedTLS structure for CA chain. */
|
|
mbedtls_x509_crt ca_chain;
|
|
|
|
/** mbedTLS structure for own certificate. */
|
|
mbedtls_x509_crt own_cert;
|
|
|
|
/** mbedTLS structure for own private key. */
|
|
mbedtls_pk_context priv_key;
|
|
#endif /* MBEDTLS_X509_CRT_PARSE_C */
|
|
|
|
#endif /* CONFIG_MBEDTLS */
|
|
};
|
|
|
|
static mbedtls_ctr_drbg_context tls_ctr_drbg;
|
|
|
|
/* A global pool of TLS contexts. */
|
|
static struct tls_context tls_contexts[CONFIG_NET_SOCKETS_TLS_MAX_CONTEXTS];
|
|
|
|
/* A mutex for protecting TLS context allocation. */
|
|
static struct k_mutex context_lock;
|
|
|
|
#if defined(MBEDTLS_DEBUG_C) && defined(CONFIG_NET_DEBUG_SOCKETS)
|
|
static void tls_debug(void *ctx, int level, const char *file,
|
|
int line, const char *str)
|
|
{
|
|
const char *p, *basename;
|
|
|
|
ARG_UNUSED(ctx);
|
|
|
|
if (!file || !str) {
|
|
return;
|
|
}
|
|
|
|
/* Extract basename from file */
|
|
for (p = basename = file; *p != '\0'; p++) {
|
|
if (*p == '/' || *p == '\\') {
|
|
basename = p + 1;
|
|
}
|
|
}
|
|
|
|
NET_DBG("%s:%04d: |%d| %s", basename, line, level, str);
|
|
}
|
|
#endif /* defined(MBEDTLS_DEBUG_C) && defined(CONFIG_NET_TLS_DEBUG) */
|
|
|
|
#if defined(CONFIG_ENTROPY_HAS_DRIVER)
|
|
static int tls_entropy_func(void *ctx, unsigned char *buf, size_t len)
|
|
{
|
|
return entropy_get_entropy(ctx, buf, len);
|
|
}
|
|
#else
|
|
static int tls_entropy_func(void *ctx, unsigned char *buf, size_t len)
|
|
{
|
|
ARG_UNUSED(ctx);
|
|
|
|
size_t i = len / 4;
|
|
u32_t val;
|
|
|
|
while (i--) {
|
|
val = sys_rand32_get();
|
|
UNALIGNED_PUT(val, (u32_t *)buf);
|
|
buf += 4;
|
|
}
|
|
|
|
i = len & 0x3;
|
|
val = sys_rand32_get();
|
|
while (i--) {
|
|
*buf++ = val;
|
|
val >>= 8;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* defined(CONFIG_ENTROPY_HAS_DRIVER) */
|
|
|
|
#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS)
|
|
/* mbedTLS-defined function for setting timer. */
|
|
static void dtls_timing_set_delay(void *data, uint32_t int_ms, uint32_t fin_ms)
|
|
{
|
|
struct dtls_timing_context *ctx = data;
|
|
|
|
ctx->int_ms = int_ms;
|
|
ctx->fin_ms = fin_ms;
|
|
|
|
if (fin_ms != 0) {
|
|
ctx->snapshot = k_uptime_get_32();
|
|
}
|
|
}
|
|
|
|
/* mbedTLS-defined function for getting timer status.
|
|
* The return values are specified by mbedTLS. The callback must return:
|
|
* -1 if cancelled (fin_ms == 0),
|
|
* 0 if none of the delays have passed,
|
|
* 1 if only the intermediate delay has passed,
|
|
* 2 if the final delay has passed.
|
|
*/
|
|
static int dtls_timing_get_delay(void *data)
|
|
{
|
|
struct dtls_timing_context *timing = data;
|
|
unsigned long elapsed_ms;
|
|
|
|
NET_ASSERT(timing);
|
|
|
|
if (timing->fin_ms == 0) {
|
|
return -1;
|
|
}
|
|
|
|
elapsed_ms = k_uptime_get_32() - timing->snapshot;
|
|
|
|
if (elapsed_ms >= timing->fin_ms) {
|
|
return 2;
|
|
}
|
|
|
|
if (elapsed_ms >= timing->int_ms) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_NET_SOCKETS_ENABLE_DTLS */
|
|
|
|
/* Initialize TLS internals. */
|
|
static int tls_init(struct device *unused)
|
|
{
|
|
ARG_UNUSED(unused);
|
|
|
|
int ret;
|
|
static const unsigned char drbg_seed[] = "zephyr";
|
|
struct device *dev = NULL;
|
|
|
|
#if defined(CONFIG_ENTROPY_HAS_DRIVER)
|
|
dev = device_get_binding(CONFIG_ENTROPY_NAME);
|
|
|
|
if (!dev) {
|
|
NET_ERR("Failed to obtain entropy device");
|
|
return -ENODEV;
|
|
}
|
|
#else
|
|
NET_WARN("No entropy device on the system, "
|
|
"TLS communication may be insecure!");
|
|
#endif /* defined(CONFIG_ENTROPY_HAS_DRIVER) */
|
|
|
|
memset(tls_contexts, 0, sizeof(tls_contexts));
|
|
|
|
k_mutex_init(&context_lock);
|
|
|
|
mbedtls_ctr_drbg_init(&tls_ctr_drbg);
|
|
|
|
ret = mbedtls_ctr_drbg_seed(&tls_ctr_drbg, tls_entropy_func, dev,
|
|
drbg_seed, sizeof(drbg_seed));
|
|
if (ret != 0) {
|
|
mbedtls_ctr_drbg_free(&tls_ctr_drbg);
|
|
NET_ERR("TLS entropy source initialization failed");
|
|
return -EFAULT;
|
|
}
|
|
|
|
#if defined(MBEDTLS_DEBUG_C) && defined(CONFIG_NET_DEBUG_SOCKETS)
|
|
mbedtls_debug_set_threshold(CONFIG_MBEDTLS_DEBUG_LEVEL);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
SYS_INIT(tls_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|
|
|
|
/* Allocate TLS context. */
|
|
static struct tls_context *tls_alloc(void)
|
|
{
|
|
int i;
|
|
struct tls_context *tls = NULL;
|
|
|
|
k_mutex_lock(&context_lock, K_FOREVER);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(tls_contexts); i++) {
|
|
if (!tls_contexts[i].is_used) {
|
|
tls = &tls_contexts[i];
|
|
memset(tls, 0, sizeof(*tls));
|
|
tls->is_used = true;
|
|
tls->options.verify_level = -1;
|
|
|
|
NET_DBG("Allocated TLS context, %p", tls);
|
|
break;
|
|
}
|
|
}
|
|
|
|
k_mutex_unlock(&context_lock);
|
|
|
|
if (tls) {
|
|
mbedtls_ssl_init(&tls->ssl);
|
|
mbedtls_ssl_config_init(&tls->config);
|
|
#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS)
|
|
mbedtls_ssl_cookie_init(&tls->cookie);
|
|
#endif
|
|
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
|
mbedtls_x509_crt_init(&tls->ca_chain);
|
|
mbedtls_x509_crt_init(&tls->own_cert);
|
|
mbedtls_pk_init(&tls->priv_key);
|
|
#endif
|
|
|
|
#if defined(MBEDTLS_DEBUG_C) && defined(CONFIG_NET_DEBUG_SOCKETS)
|
|
mbedtls_ssl_conf_dbg(&tls->config, tls_debug, NULL);
|
|
#endif
|
|
} else {
|
|
NET_WARN("Failed to allocate TLS context");
|
|
}
|
|
|
|
return tls;
|
|
}
|
|
|
|
/* Allocate new TLS context and copy the content from the source context. */
|
|
static struct tls_context *tls_clone(struct tls_context *source_tls)
|
|
{
|
|
struct tls_context *target_tls;
|
|
|
|
target_tls = tls_alloc();
|
|
if (!target_tls) {
|
|
return NULL;
|
|
}
|
|
|
|
target_tls->tls_version = source_tls->tls_version;
|
|
|
|
memcpy(&target_tls->options, &source_tls->options,
|
|
sizeof(target_tls->options));
|
|
|
|
if (target_tls->options.is_hostname_set) {
|
|
mbedtls_ssl_set_hostname(&target_tls->ssl,
|
|
source_tls->ssl.hostname);
|
|
}
|
|
|
|
return target_tls;
|
|
}
|
|
|
|
/* Release TLS context. */
|
|
static int tls_release(struct tls_context *tls)
|
|
{
|
|
if (!PART_OF_ARRAY(tls_contexts, tls)) {
|
|
NET_ERR("Invalid TLS context");
|
|
return -EBADF;
|
|
}
|
|
|
|
if (!tls->is_used) {
|
|
NET_ERR("Deallocating unused TLS context");
|
|
return -EBADF;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS)
|
|
mbedtls_ssl_cookie_free(&tls->cookie);
|
|
#endif
|
|
mbedtls_ssl_config_free(&tls->config);
|
|
mbedtls_ssl_free(&tls->ssl);
|
|
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
|
mbedtls_x509_crt_free(&tls->ca_chain);
|
|
mbedtls_x509_crt_free(&tls->own_cert);
|
|
mbedtls_pk_free(&tls->priv_key);
|
|
#endif
|
|
|
|
tls->is_used = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS)
|
|
static bool dtls_is_peer_addr_valid(struct net_context *context,
|
|
const struct sockaddr *peer_addr,
|
|
socklen_t addrlen)
|
|
{
|
|
if (context->tls->dtls_peer_addrlen != addrlen ||
|
|
context->tls->dtls_peer_addr.sa_family != peer_addr->sa_family) {
|
|
return false;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_NET_IPV6) && peer_addr->sa_family == AF_INET6) {
|
|
struct sockaddr_in6 *addr1 = net_sin6(peer_addr);
|
|
struct sockaddr_in6 *addr2 =
|
|
net_sin6(&context->tls->dtls_peer_addr);
|
|
|
|
return (addr1->sin6_port == addr2->sin6_port) &&
|
|
net_ipv6_addr_cmp(&addr1->sin6_addr, &addr2->sin6_addr);
|
|
} else if (IS_ENABLED(CONFIG_NET_IPV4) &&
|
|
peer_addr->sa_family == AF_INET) {
|
|
struct sockaddr_in *addr1 = net_sin(peer_addr);
|
|
struct sockaddr_in *addr2 =
|
|
net_sin(&context->tls->dtls_peer_addr);
|
|
|
|
return (addr1->sin_port == addr2->sin_port) &&
|
|
net_ipv4_addr_cmp(&addr1->sin_addr, &addr2->sin_addr);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void dtls_peer_address_set(struct net_context *context,
|
|
const struct sockaddr *peer_addr,
|
|
socklen_t addrlen)
|
|
{
|
|
if (addrlen <= sizeof(context->tls->dtls_peer_addr)) {
|
|
memcpy(&context->tls->dtls_peer_addr, peer_addr, addrlen);
|
|
context->tls->dtls_peer_addrlen = addrlen;
|
|
}
|
|
}
|
|
|
|
static void dtls_peer_address_get(struct net_context *context,
|
|
struct sockaddr *peer_addr,
|
|
socklen_t *addrlen)
|
|
{
|
|
socklen_t len = min(context->tls->dtls_peer_addrlen, *addrlen);
|
|
|
|
memcpy(peer_addr, &context->tls->dtls_peer_addr, len);
|
|
*addrlen = len;
|
|
}
|
|
|
|
static inline int time_left(u32_t start, u32_t timeout)
|
|
{
|
|
u32_t elapsed = k_uptime_get_32() - start;
|
|
|
|
return timeout - elapsed;
|
|
}
|
|
|
|
static int dtls_tx(void *ctx, const unsigned char *buf, size_t len)
|
|
{
|
|
int sock = POINTER_TO_INT(ctx);
|
|
struct net_context *context = ctx;
|
|
ssize_t sent;
|
|
|
|
sent = zsock_sendto(sock, buf, len, context->tls->flags,
|
|
&context->tls->dtls_peer_addr,
|
|
context->tls->dtls_peer_addrlen);
|
|
if (sent < 0) {
|
|
if (errno == EAGAIN) {
|
|
return MBEDTLS_ERR_SSL_WANT_WRITE;
|
|
}
|
|
|
|
return MBEDTLS_ERR_NET_SEND_FAILED;
|
|
}
|
|
|
|
return sent;
|
|
}
|
|
|
|
static int dtls_rx(void *ctx, unsigned char *buf, size_t len, uint32_t timeout)
|
|
{
|
|
int sock = POINTER_TO_INT(ctx);
|
|
struct net_context *context = ctx;
|
|
bool is_block = !((context->tls->flags & ZSOCK_MSG_DONTWAIT) ||
|
|
sock_is_nonblock(context));
|
|
int remaining_time = (timeout == 0) ? K_FOREVER : timeout;
|
|
u32_t entry_time = k_uptime_get_32();
|
|
socklen_t addrlen = sizeof(struct sockaddr);
|
|
struct sockaddr addr;
|
|
int err;
|
|
ssize_t received;
|
|
struct pollfd fds;
|
|
bool retry;
|
|
|
|
do {
|
|
retry = false;
|
|
|
|
/* mbedtLS does not allow blocking rx for DTLS, therefore use
|
|
* poll for timeout functionality.
|
|
*/
|
|
if (is_block) {
|
|
fds.fd = sock;
|
|
fds.events = POLLIN;
|
|
if (zsock_poll(&fds, 1, remaining_time) == 0) {
|
|
return MBEDTLS_ERR_SSL_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
received = zsock_recvfrom(sock, buf, len, context->tls->flags,
|
|
&addr, &addrlen);
|
|
if (received < 0) {
|
|
if (errno == EAGAIN) {
|
|
return MBEDTLS_ERR_SSL_WANT_READ;
|
|
}
|
|
|
|
return MBEDTLS_ERR_NET_RECV_FAILED;
|
|
}
|
|
|
|
if (context->tls->dtls_peer_addrlen == 0) {
|
|
/* Only allow to store peer address for DTLS servers. */
|
|
if (context->tls->options.role
|
|
== MBEDTLS_SSL_IS_SERVER) {
|
|
dtls_peer_address_set(context, &addr, addrlen);
|
|
|
|
err = mbedtls_ssl_set_client_transport_id(
|
|
&context->tls->ssl,
|
|
(const unsigned char *)&addr, addrlen);
|
|
if (err < 0) {
|
|
return err;
|
|
}
|
|
} else {
|
|
/* For clients it's incorrect to receive when
|
|
* no peer has been set up.
|
|
*/
|
|
return MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED;
|
|
}
|
|
} else if (!dtls_is_peer_addr_valid(context, &addr, addrlen)) {
|
|
/* Received data from different peer, ignore it. */
|
|
retry = true;
|
|
|
|
if (remaining_time != K_FOREVER) {
|
|
/* Recalculate the timeout value. */
|
|
remaining_time = time_left(entry_time, timeout);
|
|
if (remaining_time <= 0) {
|
|
return MBEDTLS_ERR_SSL_TIMEOUT;
|
|
}
|
|
}
|
|
}
|
|
} while (retry);
|
|
|
|
return received;
|
|
}
|
|
#endif /* CONFIG_NET_SOCKETS_ENABLE_DTLS */
|
|
|
|
static int tls_tx(void *ctx, const unsigned char *buf, size_t len)
|
|
{
|
|
int sock = POINTER_TO_INT(ctx);
|
|
struct net_context *context = ctx;
|
|
ssize_t sent;
|
|
|
|
sent = zsock_send(sock, buf, len, context->tls->flags);
|
|
if (sent < 0) {
|
|
if (errno == EAGAIN) {
|
|
return MBEDTLS_ERR_SSL_WANT_WRITE;
|
|
}
|
|
|
|
return MBEDTLS_ERR_NET_SEND_FAILED;
|
|
}
|
|
|
|
return sent;
|
|
}
|
|
|
|
static int tls_rx(void *ctx, unsigned char *buf, size_t len)
|
|
{
|
|
int sock = POINTER_TO_INT(ctx);
|
|
struct net_context *context = ctx;
|
|
ssize_t received;
|
|
|
|
received = zsock_recv(sock, buf, len, context->tls->flags);
|
|
if (received < 0) {
|
|
if (errno == EAGAIN) {
|
|
return MBEDTLS_ERR_SSL_WANT_READ;
|
|
}
|
|
|
|
return MBEDTLS_ERR_NET_RECV_FAILED;
|
|
}
|
|
|
|
return received;
|
|
}
|
|
|
|
static int tls_add_ca_certificate(struct tls_context *tls,
|
|
struct tls_credential *ca_cert)
|
|
{
|
|
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
|
int err = mbedtls_x509_crt_parse(&tls->ca_chain,
|
|
ca_cert->buf, ca_cert->len);
|
|
if (err != 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
#endif /* MBEDTLS_X509_CRT_PARSE_C */
|
|
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
static void tls_set_ca_chain(struct tls_context *tls)
|
|
{
|
|
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
|
mbedtls_ssl_conf_ca_chain(&tls->config, &tls->ca_chain, NULL);
|
|
mbedtls_ssl_conf_cert_profile(&tls->config,
|
|
&mbedtls_x509_crt_profile_default);
|
|
#endif /* MBEDTLS_X509_CRT_PARSE_C */
|
|
}
|
|
|
|
static int tls_set_own_cert(struct tls_context *tls,
|
|
struct tls_credential *own_cert,
|
|
struct tls_credential *priv_key)
|
|
{
|
|
#if defined(MBEDTLS_X509_CRT_PARSE_C)
|
|
int err = mbedtls_x509_crt_parse(&tls->own_cert,
|
|
own_cert->buf, own_cert->len);
|
|
if (err != 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
err = mbedtls_pk_parse_key(&tls->priv_key, priv_key->buf,
|
|
priv_key->len, NULL, 0);
|
|
if (err != 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
err = mbedtls_ssl_conf_own_cert(&tls->config, &tls->own_cert,
|
|
&tls->priv_key);
|
|
if (err != 0) {
|
|
err = -ENOMEM;
|
|
}
|
|
|
|
return 0;
|
|
#endif /* MBEDTLS_X509_CRT_PARSE_C */
|
|
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
static int tls_set_psk(struct tls_context *tls,
|
|
struct tls_credential *psk,
|
|
struct tls_credential *psk_id)
|
|
{
|
|
#if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
|
|
int err = mbedtls_ssl_conf_psk(&tls->config,
|
|
psk->buf, psk->len,
|
|
(const unsigned char *)psk_id->buf,
|
|
psk_id->len - 1);
|
|
if (err != 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
#endif
|
|
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
static int tls_set_credential(struct tls_context *tls,
|
|
struct tls_credential *cred)
|
|
{
|
|
switch (cred->type) {
|
|
case TLS_CREDENTIAL_CA_CERTIFICATE:
|
|
return tls_add_ca_certificate(tls, cred);
|
|
|
|
case TLS_CREDENTIAL_SERVER_CERTIFICATE:
|
|
{
|
|
struct tls_credential *priv_key =
|
|
credential_get(cred->tag, TLS_CREDENTIAL_PRIVATE_KEY);
|
|
if (!priv_key) {
|
|
return -ENOENT;
|
|
}
|
|
|
|
return tls_set_own_cert(tls, cred, priv_key);
|
|
}
|
|
|
|
case TLS_CREDENTIAL_PRIVATE_KEY:
|
|
/* Ignore private key - it will be used together
|
|
* with public certificate
|
|
*/
|
|
break;
|
|
|
|
case TLS_CREDENTIAL_PSK:
|
|
{
|
|
struct tls_credential *psk_id =
|
|
credential_get(cred->tag, TLS_CREDENTIAL_PSK_ID);
|
|
if (!psk_id) {
|
|
return -ENOENT;
|
|
}
|
|
|
|
return tls_set_psk(tls, cred, psk_id);
|
|
}
|
|
|
|
case TLS_CREDENTIAL_PSK_ID:
|
|
/* Ignore PSK ID - it will be used together
|
|
* with PSK
|
|
*/
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tls_mbedtls_set_credentials(struct tls_context *tls)
|
|
{
|
|
struct tls_credential *cred;
|
|
sec_tag_t tag;
|
|
int i, err = 0;
|
|
bool tag_found, ca_cert_present = false;
|
|
|
|
credentials_lock();
|
|
|
|
for (i = 0; i < tls->options.sec_tag_list.sec_tag_count; i++) {
|
|
tag = tls->options.sec_tag_list.sec_tags[i];
|
|
cred = NULL;
|
|
tag_found = false;
|
|
|
|
while ((cred = credential_next_get(tag, cred)) != NULL) {
|
|
tag_found = true;
|
|
|
|
err = tls_set_credential(tls, cred);
|
|
if (err != 0) {
|
|
goto exit;
|
|
}
|
|
|
|
if (cred->type == TLS_CREDENTIAL_CA_CERTIFICATE) {
|
|
ca_cert_present = true;
|
|
}
|
|
}
|
|
|
|
if (!tag_found) {
|
|
err = -ENOENT;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
credentials_unlock();
|
|
|
|
if (err == 0 && ca_cert_present) {
|
|
tls_set_ca_chain(tls);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int tls_mbedtls_reset(struct net_context *context)
|
|
{
|
|
int ret;
|
|
|
|
ret = mbedtls_ssl_session_reset(&context->tls->ssl);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
context->tls->tls_established = false;
|
|
#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS)
|
|
memset(&context->tls->dtls_peer_addr, 0,
|
|
sizeof(context->tls->dtls_peer_addr));
|
|
context->tls->dtls_peer_addrlen = 0;
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tls_mbedtls_handshake(struct net_context *context)
|
|
{
|
|
int ret;
|
|
|
|
/* TODO For simplicity, TLS handshake blocks the socket even for
|
|
* non-blocking socket. Non-blocking behavior for handshake can
|
|
* be implemented later.
|
|
*/
|
|
while ((ret = mbedtls_ssl_handshake(&context->tls->ssl)) != 0) {
|
|
if (ret == MBEDTLS_ERR_SSL_WANT_READ ||
|
|
ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
|
|
continue;
|
|
} else if (ret == MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED) {
|
|
ret = tls_mbedtls_reset(context);
|
|
if (ret == 0) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
NET_ERR("TLS handshake error: -%x", -ret);
|
|
ret = -ECONNABORTED;
|
|
break;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
context->tls->tls_established = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tls_mbedtls_init(struct net_context *context, bool is_server)
|
|
{
|
|
int role, type, ret;
|
|
|
|
role = is_server ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT;
|
|
|
|
type = (net_context_get_type(context) == SOCK_STREAM) ?
|
|
MBEDTLS_SSL_TRANSPORT_STREAM :
|
|
MBEDTLS_SSL_TRANSPORT_DATAGRAM;
|
|
|
|
if (type == MBEDTLS_SSL_TRANSPORT_STREAM) {
|
|
mbedtls_ssl_set_bio(&context->tls->ssl, context,
|
|
tls_tx, tls_rx, NULL);
|
|
} else {
|
|
#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS)
|
|
mbedtls_ssl_set_bio(&context->tls->ssl, context,
|
|
dtls_tx, NULL, dtls_rx);
|
|
#else
|
|
return -ENOTSUP;
|
|
#endif /* CONFIG_NET_SOCKETS_ENABLE_DTLS */
|
|
}
|
|
|
|
ret = mbedtls_ssl_config_defaults(&context->tls->config, role, type,
|
|
MBEDTLS_SSL_PRESET_DEFAULT);
|
|
if (ret != 0) {
|
|
/* According to mbedTLS API documentation,
|
|
* mbedtls_ssl_config_defaults can fail due to memory
|
|
* allocation failure
|
|
*/
|
|
return -ENOMEM;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS)
|
|
if (type == MBEDTLS_SSL_TRANSPORT_DATAGRAM) {
|
|
/* DTLS requires timer callbacks to operate */
|
|
mbedtls_ssl_set_timer_cb(&context->tls->ssl,
|
|
&context->tls->dtls_timing,
|
|
dtls_timing_set_delay,
|
|
dtls_timing_get_delay);
|
|
|
|
/* Configure cookie for DTLS server */
|
|
if (role == MBEDTLS_SSL_IS_SERVER) {
|
|
ret = mbedtls_ssl_cookie_setup(&context->tls->cookie,
|
|
mbedtls_ctr_drbg_random,
|
|
&tls_ctr_drbg);
|
|
if (ret != 0) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
mbedtls_ssl_conf_dtls_cookies(&context->tls->config,
|
|
mbedtls_ssl_cookie_write,
|
|
mbedtls_ssl_cookie_check,
|
|
&context->tls->cookie);
|
|
}
|
|
}
|
|
#endif /* CONFIG_NET_SOCKETS_ENABLE_DTLS */
|
|
|
|
/* For TLS clients, set hostname to empty string to enforce it's
|
|
* verification - only if hostname option was not set. Otherwise
|
|
* depend on user configuration.
|
|
*/
|
|
if (!is_server && !context->tls->options.is_hostname_set) {
|
|
mbedtls_ssl_set_hostname(&context->tls->ssl, "");
|
|
}
|
|
|
|
/* If verification level was specified explicitly, set it. Otherwise,
|
|
* use mbedTLS default values (required for client, none for server)
|
|
*/
|
|
if (context->tls->options.verify_level != -1) {
|
|
mbedtls_ssl_conf_authmode(&context->tls->config,
|
|
context->tls->options.verify_level);
|
|
}
|
|
|
|
mbedtls_ssl_conf_rng(&context->tls->config,
|
|
mbedtls_ctr_drbg_random,
|
|
&tls_ctr_drbg);
|
|
|
|
ret = tls_mbedtls_set_credentials(context->tls);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
ret = mbedtls_ssl_setup(&context->tls->ssl,
|
|
&context->tls->config);
|
|
if (ret != 0) {
|
|
/* According to mbedTLS API documentation,
|
|
* mbedtls_ssl_setup can fail due to memory allocation failure
|
|
*/
|
|
return -ENOMEM;
|
|
}
|
|
|
|
context->tls->is_initialized = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tls_opt_sec_tag_list_set(struct net_context *context,
|
|
const void *optval, socklen_t optlen)
|
|
{
|
|
int sec_tag_cnt;
|
|
|
|
if (!optval) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
if (optlen % sizeof(sec_tag_t) != 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
sec_tag_cnt = optlen / sizeof(sec_tag_t);
|
|
if (sec_tag_cnt >
|
|
ARRAY_SIZE(context->tls->options.sec_tag_list.sec_tags)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
memcpy(context->tls->options.sec_tag_list.sec_tags, optval, optlen);
|
|
context->tls->options.sec_tag_list.sec_tag_count = sec_tag_cnt;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tls_opt_sec_tag_list_get(struct net_context *context,
|
|
void *optval, socklen_t *optlen)
|
|
{
|
|
int len;
|
|
|
|
if (*optlen % sizeof(sec_tag_t) != 0 || *optlen == 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
len = min(context->tls->options.sec_tag_list.sec_tag_count *
|
|
sizeof(sec_tag_t), *optlen);
|
|
|
|
memcpy(optval, context->tls->options.sec_tag_list.sec_tags, len);
|
|
*optlen = len;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tls_opt_hostname_set(struct net_context *context,
|
|
const void *optval, socklen_t optlen)
|
|
{
|
|
ARG_UNUSED(optlen);
|
|
|
|
if (mbedtls_ssl_set_hostname(&context->tls->ssl, optval) != 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
context->tls->options.is_hostname_set = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tls_opt_ciphersuite_list_set(struct net_context *context,
|
|
const void *optval, socklen_t optlen)
|
|
{
|
|
int cipher_cnt;
|
|
|
|
if (!optval) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
if (optlen % sizeof(int) != 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
cipher_cnt = optlen / sizeof(int);
|
|
|
|
/* + 1 for 0-termination. */
|
|
if (cipher_cnt + 1 > ARRAY_SIZE(context->tls->options.ciphersuites)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
memcpy(context->tls->options.ciphersuites, optval, optlen);
|
|
context->tls->options.ciphersuites[cipher_cnt] = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tls_opt_ciphersuite_list_get(struct net_context *context,
|
|
void *optval, socklen_t *optlen)
|
|
{
|
|
const int *selected_ciphers;
|
|
int cipher_cnt, i = 0;
|
|
int *ciphers = optval;
|
|
|
|
if (*optlen % sizeof(int) != 0 || *optlen == 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (context->tls->options.ciphersuites[0] == 0) {
|
|
/* No specific ciphersuites configured, return all available. */
|
|
selected_ciphers = mbedtls_ssl_list_ciphersuites();
|
|
} else {
|
|
selected_ciphers = context->tls->options.ciphersuites;
|
|
}
|
|
|
|
cipher_cnt = *optlen / sizeof(int);
|
|
while (selected_ciphers[i] != 0) {
|
|
ciphers[i] = selected_ciphers[i];
|
|
|
|
if (++i == cipher_cnt) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
*optlen = i * sizeof(int);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tls_opt_ciphersuite_used_get(struct net_context *context,
|
|
void *optval, socklen_t *optlen)
|
|
{
|
|
const char *ciph;
|
|
|
|
if (*optlen != sizeof(int)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
ciph = mbedtls_ssl_get_ciphersuite(&context->tls->ssl);
|
|
if (ciph == NULL) {
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
*(int *)optval = mbedtls_ssl_get_ciphersuite_id(ciph);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tls_opt_peer_verify_set(struct net_context *context,
|
|
const void *optval, socklen_t optlen)
|
|
{
|
|
int *peer_verify;
|
|
|
|
if (!optval) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
if (optlen != sizeof(int)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
peer_verify = (int *)optval;
|
|
|
|
if (*peer_verify != MBEDTLS_SSL_VERIFY_NONE &&
|
|
*peer_verify != MBEDTLS_SSL_VERIFY_OPTIONAL &&
|
|
*peer_verify != MBEDTLS_SSL_VERIFY_REQUIRED) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
context->tls->options.verify_level = *peer_verify;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tls_opt_role_set(struct net_context *context, const void *optval,
|
|
socklen_t optlen)
|
|
{
|
|
int *role;
|
|
|
|
if (!optval) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
if (optlen != sizeof(int)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
role = (int *)optval;
|
|
if (*role != MBEDTLS_SSL_IS_CLIENT &&
|
|
*role != MBEDTLS_SSL_IS_SERVER) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
context->tls->options.role = *role;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ztls_socket(int family, int type, int proto)
|
|
{
|
|
enum net_ip_protocol_secure tls_proto = 0;
|
|
int sock, ret, err;
|
|
|
|
if ((proto >= IPPROTO_TLS_1_0 && proto <= IPPROTO_TLS_1_2) ||
|
|
(proto >= IPPROTO_DTLS_1_0 && proto <= IPPROTO_DTLS_1_2)) {
|
|
#if !defined(CONFIG_NET_SOCKETS_ENABLE_DTLS)
|
|
if (type == SOCK_DGRAM) {
|
|
errno = ENOTSUP;
|
|
return -1;
|
|
}
|
|
#endif
|
|
tls_proto = proto;
|
|
proto = (type == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP;
|
|
}
|
|
|
|
sock = zsock_socket(family, type, proto);
|
|
if (sock < 0) {
|
|
/* errno will be propagated */
|
|
return -1;
|
|
}
|
|
|
|
if (tls_proto != 0) {
|
|
/* If TLS protocol is used, allocate TLS context */
|
|
struct net_context *context = INT_TO_POINTER(sock);
|
|
|
|
context->tls = tls_alloc();
|
|
|
|
if (!context->tls) {
|
|
ret = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
context->tls->tls_version = tls_proto;
|
|
}
|
|
|
|
return sock;
|
|
|
|
error:
|
|
err = zsock_close(sock);
|
|
__ASSERT(err == 0, "Socket close failed");
|
|
|
|
errno = -ret;
|
|
return -1;
|
|
}
|
|
|
|
int ztls_close(int sock)
|
|
{
|
|
struct net_context *context = INT_TO_POINTER(sock);
|
|
int ret, err = 0;
|
|
|
|
if (context->tls) {
|
|
/* Try to send close notification. */
|
|
context->tls->flags = 0;
|
|
(void)mbedtls_ssl_close_notify(&context->tls->ssl);
|
|
|
|
err = tls_release(context->tls);
|
|
}
|
|
|
|
ret = zsock_close(sock);
|
|
|
|
/* In case zsock_close fails, we propagate errno value set by
|
|
* zsock_close.
|
|
* In case zsock_close succeeds, but tls_release fails, set errno
|
|
* according to tls_release return value.
|
|
*/
|
|
if (ret == 0 && err < 0) {
|
|
errno = -err;
|
|
ret = -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ztls_bind(int sock, const struct sockaddr *addr, socklen_t addrlen)
|
|
{
|
|
/* No extra action needed here. */
|
|
return zsock_bind(sock, addr, addrlen);
|
|
}
|
|
|
|
int ztls_connect(int sock, const struct sockaddr *addr, socklen_t addrlen)
|
|
{
|
|
int ret;
|
|
struct net_context *context = INT_TO_POINTER(sock);
|
|
|
|
ret = zsock_connect(sock, addr, addrlen);
|
|
if (ret < 0) {
|
|
/* errno will be propagated */
|
|
return -1;
|
|
}
|
|
|
|
if (context->tls) {
|
|
if (net_context_get_type(context) == SOCK_STREAM) {
|
|
/* Do the handshake for TLS, not DTLS. */
|
|
ret = tls_mbedtls_init(context, false);
|
|
if (ret < 0) {
|
|
goto error;
|
|
}
|
|
|
|
/* Do not use any socket flags during the handshake. */
|
|
context->tls->flags = 0;
|
|
|
|
ret = tls_mbedtls_handshake(context);
|
|
if (ret < 0) {
|
|
goto error;
|
|
}
|
|
} else {
|
|
#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS)
|
|
/* Just store the address. */
|
|
dtls_peer_address_set(context, addr, addrlen);
|
|
#else
|
|
ret = -ENOTSUP;
|
|
goto error;
|
|
#endif /* CONFIG_NET_SOCKETS_ENABLE_DTLS */
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
errno = -ret;
|
|
return -1;
|
|
}
|
|
|
|
int ztls_listen(int sock, int backlog)
|
|
{
|
|
/* No extra action needed here. */
|
|
return zsock_listen(sock, backlog);
|
|
}
|
|
|
|
int ztls_accept(int sock, struct sockaddr *addr, socklen_t *addrlen)
|
|
{
|
|
int child_sock, ret, err;
|
|
struct net_context *parent_context = INT_TO_POINTER(sock);
|
|
struct net_context *child_context = NULL;
|
|
|
|
child_sock = zsock_accept(sock, addr, addrlen);
|
|
if (child_sock < 0) {
|
|
/* errno will be propagated */
|
|
return -1;
|
|
}
|
|
|
|
if (parent_context->tls) {
|
|
child_context = INT_TO_POINTER(child_sock);
|
|
|
|
child_context->tls = tls_clone(parent_context->tls);
|
|
if (!child_context->tls) {
|
|
ret = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
ret = tls_mbedtls_init(child_context, true);
|
|
if (ret < 0) {
|
|
goto error;
|
|
}
|
|
|
|
/* Do not use any socket flags during the handshake. */
|
|
child_context->tls->flags = 0;
|
|
|
|
ret = tls_mbedtls_handshake(child_context);
|
|
if (ret < 0) {
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
return child_sock;
|
|
|
|
error:
|
|
if (child_context && child_context->tls) {
|
|
err = tls_release(child_context->tls);
|
|
__ASSERT(err == 0, "TLS context release failed");
|
|
}
|
|
|
|
err = zsock_close(child_sock);
|
|
__ASSERT(err == 0, "Child socket close failed");
|
|
|
|
errno = -ret;
|
|
return -1;
|
|
}
|
|
|
|
ssize_t ztls_send(int sock, const void *buf, size_t len, int flags)
|
|
{
|
|
return ztls_sendto(sock, buf, len, flags, NULL, 0);
|
|
}
|
|
|
|
ssize_t ztls_recv(int sock, void *buf, size_t max_len, int flags)
|
|
{
|
|
return ztls_recvfrom(sock, buf, max_len, flags, NULL, 0);
|
|
}
|
|
|
|
static ssize_t sendto_tls(struct net_context *context, const void *buf,
|
|
size_t len, int flags)
|
|
{
|
|
int ret;
|
|
|
|
ret = mbedtls_ssl_write(&context->tls->ssl, buf, len);
|
|
if (ret >= 0) {
|
|
return ret;
|
|
}
|
|
|
|
if (ret == MBEDTLS_ERR_SSL_WANT_READ ||
|
|
ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
|
|
errno = EAGAIN;
|
|
} else {
|
|
errno = EIO;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS)
|
|
static ssize_t sendto_dtls_client(struct net_context *context, const void *buf,
|
|
size_t len, int flags,
|
|
const struct sockaddr *dest_addr,
|
|
socklen_t addrlen)
|
|
{
|
|
int ret;
|
|
|
|
if (!dest_addr) {
|
|
/* No address provided, check if we have stored one,
|
|
* otherwise return error.
|
|
*/
|
|
if (context->tls->dtls_peer_addrlen == 0) {
|
|
ret = -EDESTADDRREQ;
|
|
goto error;
|
|
}
|
|
} else if (context->tls->dtls_peer_addrlen == 0) {
|
|
/* Address provided and no peer address stored. */
|
|
dtls_peer_address_set(context, dest_addr, addrlen);
|
|
} else if (!dtls_is_peer_addr_valid(context, dest_addr, addrlen) != 0) {
|
|
/* Address provided but it does not match stored one */
|
|
ret = -EISCONN;
|
|
goto error;
|
|
}
|
|
|
|
if (!context->tls->is_initialized) {
|
|
ret = tls_mbedtls_init(context, false);
|
|
if (ret < 0) {
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if (!context->tls->tls_established) {
|
|
ret = tls_mbedtls_handshake(context);
|
|
if (ret < 0) {
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
return sendto_tls(context, buf, len, flags);
|
|
|
|
error:
|
|
errno = -ret;
|
|
return -1;
|
|
}
|
|
|
|
static ssize_t sendto_dtls_server(struct net_context *context, const void *buf,
|
|
size_t len, int flags,
|
|
const struct sockaddr *dest_addr,
|
|
socklen_t addrlen)
|
|
{
|
|
/* For DTLS server, require to have established DTLS connection
|
|
* in order to send data.
|
|
*/
|
|
if (!context->tls->tls_established) {
|
|
errno = ENOTCONN;
|
|
return -1;
|
|
}
|
|
|
|
/* Check if not trying to send to other peer, which we're
|
|
* not connected to.
|
|
*/
|
|
if (dest_addr &&
|
|
!dtls_is_peer_addr_valid(context, dest_addr, addrlen) != 0) {
|
|
errno = EISCONN;
|
|
return -1;
|
|
}
|
|
|
|
return sendto_tls(context, buf, len, flags);
|
|
}
|
|
#endif /* CONFIG_NET_SOCKETS_ENABLE_DTLS */
|
|
|
|
ssize_t ztls_sendto(int sock, const void *buf, size_t len, int flags,
|
|
const struct sockaddr *dest_addr, socklen_t addrlen)
|
|
{
|
|
struct net_context *context = INT_TO_POINTER(sock);
|
|
|
|
if (!context->tls) {
|
|
return zsock_sendto(sock, buf, len, flags, dest_addr, addrlen);
|
|
}
|
|
|
|
context->tls->flags = flags;
|
|
|
|
/* TLS */
|
|
if (net_context_get_type(context) == SOCK_STREAM) {
|
|
return sendto_tls(context, buf, len, flags);
|
|
}
|
|
|
|
#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS)
|
|
/* DTLS */
|
|
if (context->tls->options.role == MBEDTLS_SSL_IS_SERVER) {
|
|
return sendto_dtls_server(context, buf, len, flags,
|
|
dest_addr, addrlen);
|
|
}
|
|
|
|
return sendto_dtls_client(context, buf, len, flags, dest_addr, addrlen);
|
|
#else
|
|
errno = ENOTSUP;
|
|
return -1;
|
|
#endif /* CONFIG_NET_SOCKETS_ENABLE_DTLS */
|
|
}
|
|
|
|
ssize_t ztls_recvfrom(int sock, void *buf, size_t max_len, int flags,
|
|
struct sockaddr *src_addr, socklen_t *addrlen)
|
|
{
|
|
struct net_context *context = INT_TO_POINTER(sock);
|
|
int ret;
|
|
|
|
if (!context->tls) {
|
|
return zsock_recvfrom(sock, buf, max_len, flags,
|
|
src_addr, addrlen);
|
|
}
|
|
|
|
if (flags & ZSOCK_MSG_PEEK) {
|
|
/* TODO mbedTLS does not support 'peeking' This could be
|
|
* bypassed by having intermediate buffer for peeking
|
|
*/
|
|
errno = ENOTSUP;
|
|
return -1;
|
|
}
|
|
|
|
context->tls->flags = flags;
|
|
|
|
ret = mbedtls_ssl_read(&context->tls->ssl, buf, max_len);
|
|
if (ret >= 0) {
|
|
return ret;
|
|
}
|
|
|
|
if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
|
|
/* Peer notified that it's closing the connection. */
|
|
return 0;
|
|
}
|
|
|
|
if (ret == MBEDTLS_ERR_SSL_CLIENT_RECONNECT) {
|
|
/* Client reconnect on the same socket is not
|
|
* supported. See mbedtls_ssl_read API documentation.
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
if (ret == MBEDTLS_ERR_SSL_WANT_READ ||
|
|
ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
|
|
errno = EAGAIN;
|
|
return -1;
|
|
}
|
|
|
|
errno = EIO;
|
|
return -1;
|
|
}
|
|
|
|
int ztls_fcntl(int sock, int cmd, int flags)
|
|
{
|
|
/* No extra action needed here. */
|
|
return zsock_fcntl(sock, cmd, flags);
|
|
}
|
|
|
|
int ztls_poll(struct zsock_pollfd *fds, int nfds, int timeout)
|
|
{
|
|
bool has_mbedtls_data = false;
|
|
struct zsock_pollfd *pfd;
|
|
struct net_context *context;
|
|
int i, ret;
|
|
|
|
/* There might be some decrypted but unread data pending on mbedTLS,
|
|
* check for that.
|
|
*/
|
|
for (pfd = fds, i = nfds; i--; pfd++) {
|
|
/* Per POSIX, negative fd's are just ignored */
|
|
if (pfd->fd < 0) {
|
|
continue;
|
|
}
|
|
|
|
if (pfd->events & ZSOCK_POLLIN) {
|
|
context = INT_TO_POINTER(pfd->fd);
|
|
if (!context->tls) {
|
|
continue;
|
|
}
|
|
|
|
if (mbedtls_ssl_get_bytes_avail(
|
|
&context->tls->ssl) > 0) {
|
|
has_mbedtls_data = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If there is no data waiting on any of mbedTLS contexts,
|
|
* just do regular poll.
|
|
*/
|
|
if (!has_mbedtls_data) {
|
|
return zsock_poll(fds, nfds, timeout);
|
|
}
|
|
|
|
/* Otherwise, poll with no timeout, and update respective revents. */
|
|
ret = zsock_poll(fds, nfds, K_NO_WAIT);
|
|
if (ret < 0) {
|
|
/* errno will be propagated */
|
|
return -1;
|
|
}
|
|
|
|
/* Another pass, this time updating revents. */
|
|
for (pfd = fds, i = nfds; i--; pfd++) {
|
|
/* Per POSIX, negative fd's are just ignored */
|
|
if (pfd->fd < 0) {
|
|
continue;
|
|
}
|
|
|
|
if (pfd->events & ZSOCK_POLLIN) {
|
|
context = INT_TO_POINTER(pfd->fd);
|
|
if (!context->tls) {
|
|
continue;
|
|
}
|
|
|
|
if (mbedtls_ssl_get_bytes_avail(
|
|
&context->tls->ssl) > 0) {
|
|
if (pfd->revents == 0) {
|
|
ret++;
|
|
}
|
|
|
|
pfd->revents |= ZSOCK_POLLIN;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ztls_getsockopt(int sock, int level, int optname,
|
|
void *optval, socklen_t *optlen)
|
|
{
|
|
int err;
|
|
struct net_context *context = INT_TO_POINTER(sock);
|
|
|
|
if (level != SOL_TLS) {
|
|
return zsock_getsockopt(sock, level, optname, optval, optlen);
|
|
}
|
|
|
|
if (!context || !context->tls) {
|
|
return -EBADF;
|
|
}
|
|
|
|
if (!optval || !optlen) {
|
|
return -EFAULT;
|
|
}
|
|
|
|
switch (optname) {
|
|
case TLS_SEC_TAG_LIST:
|
|
err = tls_opt_sec_tag_list_get(context, optval, optlen);
|
|
break;
|
|
|
|
case TLS_HOSTNAME:
|
|
/* Write-only option. */
|
|
err = -ENOPROTOOPT;
|
|
break;
|
|
|
|
case TLS_CIPHERSUITE_LIST:
|
|
err = tls_opt_ciphersuite_list_get(context, optval, optlen);
|
|
break;
|
|
|
|
case TLS_CIPHERSUITE_USED:
|
|
err = tls_opt_ciphersuite_used_get(context, optval, optlen);
|
|
break;
|
|
|
|
case TLS_PEER_VERIFY:
|
|
/* Write-only option. */
|
|
err = -ENOPROTOOPT;
|
|
break;
|
|
|
|
case TLS_ROLE:
|
|
/* Write-only option. */
|
|
err = -ENOPROTOOPT;
|
|
break;
|
|
|
|
default:
|
|
err = -ENOPROTOOPT;
|
|
break;
|
|
}
|
|
|
|
if (err < 0) {
|
|
errno = -err;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ztls_setsockopt(int sock, int level, int optname,
|
|
const void *optval, socklen_t optlen)
|
|
{
|
|
int err;
|
|
struct net_context *context = INT_TO_POINTER(sock);
|
|
|
|
if (level != SOL_TLS) {
|
|
return zsock_setsockopt(sock, level, optname, optval, optlen);
|
|
}
|
|
|
|
if (!context || !context->tls) {
|
|
return -EBADF;
|
|
}
|
|
|
|
switch (optname) {
|
|
case TLS_SEC_TAG_LIST:
|
|
err = tls_opt_sec_tag_list_set(context, optval, optlen);
|
|
break;
|
|
|
|
case TLS_HOSTNAME:
|
|
err = tls_opt_hostname_set(context, optval, optlen);
|
|
break;
|
|
|
|
case TLS_CIPHERSUITE_LIST:
|
|
err = tls_opt_ciphersuite_list_set(context, optval, optlen);
|
|
break;
|
|
|
|
case TLS_CIPHERSUITE_USED:
|
|
/* Read-only option. */
|
|
err = -ENOPROTOOPT;
|
|
break;
|
|
|
|
case TLS_PEER_VERIFY:
|
|
err = tls_opt_peer_verify_set(context, optval, optlen);
|
|
break;
|
|
|
|
case TLS_ROLE:
|
|
err = tls_opt_role_set(context, optval, optlen);
|
|
break;
|
|
|
|
default:
|
|
err = -ENOPROTOOPT;
|
|
break;
|
|
}
|
|
|
|
if (err < 0) {
|
|
errno = -err;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|