From d2a397bcf8fd50c642cd6b69dc18a5cb219a6f9f Mon Sep 17 00:00:00 2001 From: Robert Lubos Date: Thu, 6 Sep 2018 10:28:55 +0200 Subject: [PATCH] net: mqtt: Add TLS socket transport Add TLS transport to socket MQTT implementation. Signed-off-by: Robert Lubos --- include/net/mqtt.h | 41 +++++ subsys/net/lib/mqtt_sock/CMakeLists.txt | 4 + subsys/net/lib/mqtt_sock/Kconfig | 5 + subsys/net/lib/mqtt_sock/mqtt_transport.c | 18 ++ .../lib/mqtt_sock/mqtt_transport_socket_tls.c | 161 ++++++++++++++++++ 5 files changed, 229 insertions(+) create mode 100644 subsys/net/lib/mqtt_sock/mqtt_transport_socket_tls.c diff --git a/include/net/mqtt.h b/include/net/mqtt.h index b6de19014dd..2aec0ed71dd 100644 --- a/include/net/mqtt.h +++ b/include/net/mqtt.h @@ -26,6 +26,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -312,13 +313,40 @@ struct mqtt_client; typedef void (*mqtt_evt_cb_t)(struct mqtt_client *client, const struct mqtt_evt *evt); +/** @brief TLS configuration for secure MQTT transports. */ +struct mqtt_sec_config { + /** Indicates the preference for peer verification. */ + int peer_verify; + + /** Indicates the number of entries in the cipher list. */ + u32_t cipher_count; + + /** Indicates the list of ciphers to be used for the session. + * May be NULL to use the default ciphers. + */ + int *cipher_list; + + /** Indicates the number of entries in the sec tag list. */ + u32_t sec_tag_count; + + /** Indicates the list of security tags to be used for the session. */ + sec_tag_t *seg_tag_list; + + /** Peer hostname for ceritificate verification. + * May be NULL to skip hostname verification. + */ + char *hostname; +}; + /** @brief MQTT transport type. */ enum mqtt_transport_type { /** Use non secure TCP transport for MQTT connection. */ MQTT_TRANSPORT_NON_SECURE = 0x00, +#if defined(CONFIG_MQTT_LIB_TLS) /** Use secure TCP transport (TLS) for MQTT connection. */ MQTT_TRANSPORT_SECURE = 0x01, +#endif /* CONFIG_MQTT_LIB_TLS */ /** Shall not be used as a transport type. * Indicator of maximum transport types possible. @@ -340,6 +368,19 @@ struct mqtt_transport { /** Socket descriptor. */ int sock; } tcp; + +#if defined(CONFIG_MQTT_LIB_TLS) + /* TLS socket transport for MQTT */ + struct { + /** Socket descriptor. */ + int sock; + + /** TLS configuration. See @ref mqtt_sec_config for + * details. + */ + struct mqtt_sec_config config; + } tls; +#endif /* CONFIG_MQTT_LIB_TLS */ }; }; diff --git a/subsys/net/lib/mqtt_sock/CMakeLists.txt b/subsys/net/lib/mqtt_sock/CMakeLists.txt index e5ac7db0d71..b6cf9e26842 100644 --- a/subsys/net/lib/mqtt_sock/CMakeLists.txt +++ b/subsys/net/lib/mqtt_sock/CMakeLists.txt @@ -8,3 +8,7 @@ zephyr_library_sources( mqtt_transport.c mqtt.c ) + +zephyr_library_sources_ifdef(CONFIG_MQTT_LIB_TLS + mqtt_transport_socket_tls.c + ) diff --git a/subsys/net/lib/mqtt_sock/Kconfig b/subsys/net/lib/mqtt_sock/Kconfig index 3b1342ed184..b53e00dadcc 100644 --- a/subsys/net/lib/mqtt_sock/Kconfig +++ b/subsys/net/lib/mqtt_sock/Kconfig @@ -27,4 +27,9 @@ config MQTT_KEEPALIVE Keep alive time for MQTT (in seconds). Sending of Ping Requests to keep the connection alive are governed by this value. +config MQTT_LIB_TLS + bool "TLS support for socket MQTT Library" + help + Enable TLS support for socket MQTT Library + endif # MQTT_LIB diff --git a/subsys/net/lib/mqtt_sock/mqtt_transport.c b/subsys/net/lib/mqtt_sock/mqtt_transport.c index dc28f4ba1ff..6d7cc7183c1 100644 --- a/subsys/net/lib/mqtt_sock/mqtt_transport.c +++ b/subsys/net/lib/mqtt_sock/mqtt_transport.c @@ -19,6 +19,16 @@ extern int mqtt_client_tcp_read(struct mqtt_client *client, u8_t *data, u32_t buflen); extern int mqtt_client_tcp_disconnect(struct mqtt_client *client); +#if defined(CONFIG_MQTT_LIB_TLS) +/* Transport handler functions for TLS socket transport. */ +extern int mqtt_client_tls_connect(struct mqtt_client *client); +extern int mqtt_client_tls_write(struct mqtt_client *client, const u8_t *data, + u32_t datalen); +extern int mqtt_client_tls_read(struct mqtt_client *client, u8_t *data, + u32_t buflen); +extern int mqtt_client_tls_disconnect(struct mqtt_client *client); +#endif /* CONFIG_MQTT_LIB_TLS */ + /**@brief Function pointer array for TCP/TLS transport handlers. */ const struct transport_procedure transport_fn[MQTT_TRANSPORT_NUM] = { { @@ -26,7 +36,15 @@ const struct transport_procedure transport_fn[MQTT_TRANSPORT_NUM] = { mqtt_client_tcp_write, mqtt_client_tcp_read, mqtt_client_tcp_disconnect, + }, +#if defined(CONFIG_MQTT_LIB_TLS) + { + mqtt_client_tls_connect, + mqtt_client_tls_write, + mqtt_client_tls_read, + mqtt_client_tls_disconnect, } +#endif /* CONFIG_MQTT_LIB_TLS */ }; int mqtt_transport_connect(struct mqtt_client *client) diff --git a/subsys/net/lib/mqtt_sock/mqtt_transport_socket_tls.c b/subsys/net/lib/mqtt_sock/mqtt_transport_socket_tls.c new file mode 100644 index 00000000000..862495e9df1 --- /dev/null +++ b/subsys/net/lib/mqtt_sock/mqtt_transport_socket_tls.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** @file mqtt_transport_socket_tls.h + * + * @brief Internal functions to handle transport over TLS socket. + */ + +#define LOG_MODULE_NAME net_mqtt_sock_tls +#define NET_LOG_LEVEL CONFIG_MQTT_LOG_LEVEL + +#include +#include +#include + +#include "mqtt_os.h" + +/**@brief Handles connect request for TLS socket transport. + * + * @param[in] client Identifies the client on which the procedure is requested. + * + * @retval 0 or an error code indicating reason for failure. + */ +int mqtt_client_tls_connect(struct mqtt_client *client) +{ + const struct sockaddr *broker = client->broker; + struct mqtt_sec_config *tls_config = &client->transport.tls.config; + int ret; + + client->transport.tls.sock = socket(broker->sa_family, + SOCK_STREAM, IPPROTO_TLS_1_2); + if (client->transport.tls.sock < 0) { + return -errno; + } + + MQTT_TRC("Created socket %d", client->transport.tls.sock); + + /* Set secure socket options. */ + ret = setsockopt(client->transport.tls.sock, SOL_TLS, TLS_PEER_VERIFY, + &tls_config->peer_verify, + sizeof(tls_config->peer_verify)); + if (ret < 0) { + goto error; + } + + if (tls_config->cipher_list != NULL && tls_config->cipher_count > 0) { + ret = setsockopt(client->transport.tls.sock, SOL_TLS, + TLS_CIPHERSUITE_LIST, tls_config->cipher_list, + sizeof(int) * tls_config->cipher_count); + if (ret < 0) { + goto error; + } + } + + if (tls_config->seg_tag_list != NULL && tls_config->sec_tag_count > 0) { + ret = setsockopt(client->transport.tls.sock, SOL_TLS, + TLS_SEC_TAG_LIST, tls_config->seg_tag_list, + sizeof(sec_tag_t) * tls_config->sec_tag_count); + if (ret < 0) { + goto error; + } + } + + if (tls_config->hostname) { + ret = setsockopt(client->transport.tls.sock, SOL_TLS, + TLS_HOSTNAME, tls_config->hostname, + strlen(tls_config->hostname)); + if (ret < 0) { + goto error; + } + } + + size_t peer_addr_size = sizeof(struct sockaddr_in6); + + if (broker->sa_family == AF_INET) { + peer_addr_size = sizeof(struct sockaddr_in); + } + + ret = connect(client->transport.tls.sock, client->broker, + peer_addr_size); + if (ret < 0) { + goto error; + } + + MQTT_TRC("Connect completed"); + return 0; + +error: + (void)close(client->transport.tls.sock); + return -errno; +} + +/**@brief Handles write requests on TLS socket transport. + * + * @param[in] client Identifies the client on which the procedure is requested. + * @param[in] data Data to be written on the transport. + * @param[in] datalen Length of data to be written on the transport. + * + * @retval 0 or an error code indicating reason for failure. + */ +int mqtt_client_tls_write(struct mqtt_client *client, const u8_t *data, + u32_t datalen) +{ + u32_t offset = 0; + int ret; + + while (offset < datalen) { + ret = send(client->transport.tls.sock, data + offset, + datalen - offset, 0); + if (ret < 0) { + return -errno; + } + + offset += ret; + } + + return 0; +} + +/**@brief Handles read requests on TLS socket transport. + * + * @param[in] client Identifies the client on which the procedure is requested. + * @param[in] data Pointer where read data is to be fetched. + * @param[in] buflen Size of memory provided for the operation. + * + * @retval Number of bytes read or an error code indicating reason for failure. + * 0 if connection was closed. + */ +int mqtt_client_tls_read(struct mqtt_client *client, u8_t *data, u32_t buflen) +{ + int ret; + + ret = recv(client->transport.tls.sock, data, buflen, MSG_DONTWAIT); + if (ret < 0) { + return -errno; + } + + return ret; +} + +/**@brief Handles transport disconnection requests on TLS socket transport. + * + * @param[in] client Identifies the client on which the procedure is requested. + * + * @retval 0 or an error code indicating reason for failure. + */ +int mqtt_client_tls_disconnect(struct mqtt_client *client) +{ + int ret; + + MQTT_TRC("Closing socket %d", client->transport.tls.sock); + ret = close(client->transport.tls.sock); + if (ret < 0) { + return -errno; + } + + return 0; +}