diff --git a/include/net/socket.h b/include/net/socket.h index 55d4c88184a6a..12529420a1654 100644 --- a/include/net/socket.h +++ b/include/net/socket.h @@ -43,6 +43,45 @@ struct zsock_pollfd { #define ZSOCK_MSG_PEEK 0x02 #define ZSOCK_MSG_DONTWAIT 0x40 +/* Protocol level for TLS. + * Here, the same socket protocol level for TLS as in Linux was used. + */ +#define SOL_TLS 282 + +/* Socket options for TLS */ + +/* Socket option to select TLS credentials to use. It accepts and returns an + * array of sec_tag_t that indicate which TLS credentials should be used with + * specific socket + */ +#define TLS_SEC_TAG_LIST 1 +/* Write-only socket option to set hostname. It accepts a string containing + * the hostname (may be NULL to disable hostname verification). By default, + * hostname check is enforced for TLS clients. + */ +#define TLS_HOSTNAME 2 +/* Socket option to select ciphersuites to use. It accepts and returns an array + * of integers with IANA assigned ciphersuite identifiers. + * If not set, socket will allow all ciphersuites available in the system + * (mebdTLS default behavior). + */ +#define TLS_CIPHERSUITE_LIST 3 +/* Read-only socket option to read a ciphersuite chosen during TLS handshake. + * It returns an integer containing an IANA assigned ciphersuite identifier + * of chosen ciphersuite. + */ +#define TLS_CIPHERSUITE_USED 4 +/* Write-only socket option to set peer verification level for TLS connection. + * This option accepts an integer with a peer verification level, compatible + * with mbedTLS values: + * 0 - none, + * 1 - optional + * 2 - required. + * If not set, socket will use mbedTLS defaults (none for servers, required + * for clients). + */ +#define TLS_PEER_VERIFY 5 + struct zsock_addrinfo { struct zsock_addrinfo *ai_next; int ai_flags; @@ -71,6 +110,10 @@ ssize_t zsock_recvfrom(int sock, void *buf, size_t max_len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); int zsock_fcntl(int sock, int cmd, int flags); int zsock_poll(struct zsock_pollfd *fds, int nfds, int timeout); +int zsock_getsockopt(int sock, int level, int optname, + void *optval, socklen_t *optlen); +int zsock_setsockopt(int sock, int level, int optname, + const void *optval, socklen_t optlen); int zsock_inet_pton(sa_family_t family, const char *src, void *dst); int zsock_getaddrinfo(const char *host, const char *service, const struct zsock_addrinfo *hints, @@ -92,6 +135,10 @@ ssize_t ztls_recvfrom(int sock, void *buf, size_t max_len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); int ztls_fcntl(int sock, int cmd, int flags); int ztls_poll(struct zsock_pollfd *fds, int nfds, int timeout); +int ztls_getsockopt(int sock, int level, int optname, + void *optval, socklen_t *optlen); +int ztls_setsockopt(int sock, int level, int optname, + const void *optval, socklen_t optlen); #endif /* defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) */ @@ -213,6 +260,26 @@ static inline int poll(struct zsock_pollfd *fds, int nfds, int timeout) #define MSG_PEEK ZSOCK_MSG_PEEK #define MSG_DONTWAIT ZSOCK_MSG_DONTWAIT +static inline int getsockopt(int sock, int level, int optname, + void *optval, socklen_t *optlen) +{ +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + return ztls_getsockopt(sock, level, optname, optval, optlen); +#else + return zsock_getsockopt(sock, level, optname, optval, optlen); +#endif +} + +static inline int setsockopt(int sock, int level, int optname, + const void *optval, socklen_t optlen) +{ +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + return ztls_setsockopt(sock, level, optname, optval, optlen); +#else + return zsock_setsockopt(sock, level, optname, optval, optlen); +#endif +} + static inline char *inet_ntop(sa_family_t family, const void *src, char *dst, size_t size) { diff --git a/include/net/tls_credentials.h b/include/net/tls_credentials.h new file mode 100644 index 0000000000000..d42d487c546ce --- /dev/null +++ b/include/net/tls_credentials.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** @file + * @brief TLS credentials management + * + * An API for applications to configure TLS credentials. + */ + +#ifndef __TLS_CREDENTIAL_H +#define __TLS_CREDENTIAL_H + +/** + * @brief TLS credentials management + * @defgroup tls_credentials TLS credentials management + * @ingroup networking + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** TLS credential types */ +enum tls_credential_type { + TLS_CREDENTIAL_NONE, + TLS_CREDENTIAL_CA_CERTIFICATE, + TLS_CREDENTIAL_SERVER_CERTIFICATE, + TLS_CREDENTIAL_PRIVATE_KEY, + TLS_CREDENTIAL_PSK, + TLS_CREDENTIAL_PSK_ID +}; + +/** Secure tag, a reference to TLS credential + * + * Secure tag can be used to reference credential after it was registered + * in the system. + * + * @note Some TLS credentials come in pairs: + * - TLS_CREDENTIAL_SERVER_CERTIFICATE with TLS_CREDENTIAL_PRIVATE_KEY, + * - TLS_CREDENTIAL_PSK with TLS_CREDENTIAL_PSK_ID. + * Such pairs of credentials must be assigned the same secure tag to be + * correctly handled in the system. + */ +typedef int sec_tag_t; + +/** + * @brief Add a TLS credential. + * + * @details This function adds a TLS credential, that can be used + * by TLS/DTLS for authentication. + * + * @param tag A security tag that credential will be referenced with. + * @param type A TLS/DTLS credential type. + * @param cred A TLS/DTLS credential. + * @param credlen A TLS/DTLS credential length. + * + * @retval 0 TLS credential successfully added. + * @retval -EACCES Access to the TLS credential subsystem was denied. + * @retval -ENOMEM Not enough memory to add new TLS credential. + * @retval -EEXIST TLS credential of specific tag and type already exists. + */ +int tls_credential_add(sec_tag_t tag, enum tls_credential_type type, + const void *cred, size_t credlen); + +/** + * @brief Get a TLS credential. + * + * @details This function gets an already registered TLS credential, + * referenced by @p tag secure tag of @p type. + * + * @param tag A security tag of requested credential. + * @param type A TLS/DTLS credential type of requested credential. + * @param cred A buffer for TLS/DTLS credential. + * @param credlen A buffer size on input. TLS/DTLS credential length on output. + * + * @retval 0 TLS credential successfully obtained. + * @retval -EACCES Access to the TLS credential subsystem was denied. + * @retval -ENOENT Requested TLS credential was not found. + * @retval -EFBIG Requested TLS credential does not fit in the buffer provided. + */ +int tls_credential_get(sec_tag_t tag, enum tls_credential_type type, + void *cred, size_t *credlen); + +/** + * @brief Delete a TLS credential. + * + * @details This function removes a TLS credential, referenced by @p tag + * secure tag of @p type. + * + * @param tag A security tag corresponding to removed credential. + * @param type A TLS/DTLS credential type of removed credential. + * + * @retval 0 TLS credential successfully deleted. + * @retval -EACCES Access to the TLS credential subsystem was denied. + * @retval -ENOENT Requested TLS credential was not found. + */ +int tls_credential_delete(sec_tag_t tag, enum tls_credential_type type); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* __TLS_CREDENTIAL_H */ diff --git a/samples/net/sockets/big_http_download/CMakeLists.txt b/samples/net/sockets/big_http_download/CMakeLists.txt index e6bf10afba6f5..f09f1f20f3299 100644 --- a/samples/net/sockets/big_http_download/CMakeLists.txt +++ b/samples/net/sockets/big_http_download/CMakeLists.txt @@ -5,3 +5,11 @@ FILE(GLOB app_sources src/*.c) target_sources(app PRIVATE ${app_sources}) include($ENV{ZEPHYR_BASE}/samples/net/common/common.cmake) + +set(gen_dir ${ZEPHYR_BINARY_DIR}/include/generated/) + +generate_inc_file_for_target( + app + src/lets_encrypt_x3.der + ${gen_dir}/lets_encrypt_x3.der.inc + ) diff --git a/samples/net/sockets/big_http_download/src/big_http_download.c b/samples/net/sockets/big_http_download/src/big_http_download.c index 2ba3ff3985ab3..e3bfe765c9551 100644 --- a/samples/net/sockets/big_http_download/src/big_http_download.c +++ b/samples/net/sockets/big_http_download/src/big_http_download.c @@ -25,6 +25,11 @@ #include #include +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) +#include +#include "ca_certificate.h" +#endif + #define sleep(x) k_sleep(x * 1000) #endif @@ -144,6 +149,19 @@ void download(struct addrinfo *ai, bool is_tls) CHECK(sock); printf("sock = %d\n", sock); +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + if (is_tls) { + sec_tag_t sec_tag_opt[] = { + CA_CERTIFICATE_TAG, + }; + CHECK(setsockopt(sock, SOL_TLS, TLS_SEC_TAG_LIST, + sec_tag_opt, sizeof(sec_tag_opt))); + + CHECK(setsockopt(sock, SOL_TLS, TLS_HOSTNAME, + host, strlen(host) + 1)); + } +#endif + CHECK(connect(sock, ai->ai_addr, ai->ai_addrlen)); sendall(sock, "GET /", SSTRLEN("GET /")); sendall(sock, uri_path, strlen(uri_path)); @@ -207,6 +225,11 @@ int main(void) int resolve_attempts = 10; bool is_tls = false; +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + tls_credential_add(CA_CERTIFICATE_TAG, TLS_CREDENTIAL_CA_CERTIFICATE, + ca_certificate, sizeof(ca_certificate)); +#endif + setbuf(stdout, NULL); if (strncmp(download_url, "http://", SSTRLEN("http://")) == 0) { diff --git a/samples/net/sockets/big_http_download/src/ca_certificate.h b/samples/net/sockets/big_http_download/src/ca_certificate.h new file mode 100644 index 0000000000000..00f7e65fb1675 --- /dev/null +++ b/samples/net/sockets/big_http_download/src/ca_certificate.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __CA_CERTIFICATE_H__ +#define __CA_CERTIFICATE_H__ + +#define CA_CERTIFICATE_TAG 1 + +/* By default only certificates in DER format are supported. If you want to use + * certificate in PEM format, you can enable support for it in Kconfig. + */ + +/* Let's Encrypt Authority X3 for https://www.7-zip.org */ +static const unsigned char ca_certificate[] = { +#include "lets_encrypt_x3.der.inc" +}; + +#endif /* __CA_CERTIFICATE_H__ */ diff --git a/samples/net/sockets/big_http_download/src/lets_encrypt_x3.der b/samples/net/sockets/big_http_download/src/lets_encrypt_x3.der new file mode 100644 index 0000000000000..e08466c5a7cc6 Binary files /dev/null and b/samples/net/sockets/big_http_download/src/lets_encrypt_x3.der differ diff --git a/samples/net/sockets/echo_client/CMakeLists.txt b/samples/net/sockets/echo_client/CMakeLists.txt index 5d3f141ee832a..48a38630d735a 100644 --- a/samples/net/sockets/echo_client/CMakeLists.txt +++ b/samples/net/sockets/echo_client/CMakeLists.txt @@ -14,3 +14,11 @@ target_sources_ifdef(CONFIG_NET_UDP app PRIVATE src/udp.c) target_sources_ifdef(CONFIG_NET_TCP app PRIVATE src/tcp.c) include($ENV{ZEPHYR_BASE}/samples/net/common/common.cmake) + +set(gen_dir ${ZEPHYR_BINARY_DIR}/include/generated/) + +generate_inc_file_for_target( + app + src/echo-apps-cert.der + ${gen_dir}/echo-apps-cert.der.inc + ) diff --git a/samples/net/sockets/echo_client/boards/qemu_x86_tls.conf b/samples/net/sockets/echo_client/boards/qemu_x86_tls.conf new file mode 100644 index 0000000000000..66ab7aab2d44f --- /dev/null +++ b/samples/net/sockets/echo_client/boards/qemu_x86_tls.conf @@ -0,0 +1,16 @@ +CONFIG_NET_UDP=n +CONFIG_NET_TCP=y +CONFIG_NET_IPV6=y +CONFIG_NET_IPV4=y + +CONFIG_MAIN_STACK_SIZE=4096 + +# TLS configuration +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_BUILTIN=y +CONFIG_MBEDTLS_ENABLE_HEAP=y +CONFIG_MBEDTLS_HEAP_SIZE=60000 +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=4096 + +CONFIG_NET_SOCKETS_SOCKOPT_TLS=y +CONFIG_NET_SOCKETS_TLS_MAX_CONTEXTS=2 diff --git a/samples/net/sockets/echo_client/src/ca_certificate.h b/samples/net/sockets/echo_client/src/ca_certificate.h new file mode 100644 index 0000000000000..de9feb4a0073a --- /dev/null +++ b/samples/net/sockets/echo_client/src/ca_certificate.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __CA_CERTIFICATE_H__ +#define __CA_CERTIFICATE_H__ + +#define CA_CERTIFICATE_TAG 1 + +#define TLS_PEER_HOSTNAME "localhost" + +/* This is the same cert as what is found in net-tools/echo-apps-cert.pem file + */ +static const unsigned char ca_certificate[] = { +#include "echo-apps-cert.der.inc" +}; + +#endif /* __CA_CERTIFICATE_H__ */ diff --git a/samples/net/sockets/echo_client/src/echo-apps-cert.der b/samples/net/sockets/echo_client/src/echo-apps-cert.der new file mode 100644 index 0000000000000..bfcb335e31c8c Binary files /dev/null and b/samples/net/sockets/echo_client/src/echo-apps-cert.der differ diff --git a/samples/net/sockets/echo_client/src/echo-client.c b/samples/net/sockets/echo_client/src/echo-client.c index 51620a7acba0c..decd04decd6be 100644 --- a/samples/net/sockets/echo_client/src/echo-client.c +++ b/samples/net/sockets/echo_client/src/echo-client.c @@ -28,38 +28,37 @@ #include #include +#include #include "common.h" +#include "ca_certificate.h" #define APP_BANNER "Run echo client" /* Generated by http://www.lipsum.com/ - * 3 paragraphs, 176 words, 1230 bytes of Lorem Ipsum + * 2 paragraphs, 171 words, 1210 bytes of Lorem Ipsum */ const char lorem_ipsum[] = - "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " - "Vestibulum id cursus felis, sit amet suscipit velit. Integer " - "facilisis malesuada porta. Nunc at accumsan mauris. Etiam vehicula, " - "arcu consequat feugiat venenatis, tellus velit gravida ligula, quis " - "posuere sem leo eget urna. Curabitur condimentum leo nec orci " - "mattis, nec faucibus dui rutrum. Ut mollis orci in iaculis " - "consequat. Nulla volutpat nibh eu velit sagittis, a iaculis dui " - "aliquam." + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse " + "eleifend odio sit amet scelerisque tincidunt. In orci ligula, egestas " + "ut neque sit amet, fringilla malesuada dolor. Fusce rhoncus nunc in " + "lacus tincidunt lobortis. Donec aliquam lectus gravida fermentum " + "egestas. Curabitur eu ex pretium, dapibus massa in, pretium tellus. " + "Suspendisse ac efficitur magna, ut convallis nisl. Duis sed sapien " + "odio. Aliquam efficitur sed tellus sit amet eleifend. Sed facilisis " + "ligula aliquam erat ornare hendrerit. Aenean tincidunt nunc a nulla " + "vestibulum mollis. Vivamus fringilla euismod nisi sit amet malesuada. " + "Vivamus consequat ultricies metus sed feugiat. Aenean malesuada " + "cursus sem." "\n" - "Quisque interdum consequat eros a eleifend. Fusce dapibus nisl " - "sit amet velit posuere imperdiet. Quisque accumsan tempor massa " - "sit amet tincidunt. Integer sollicitudin vehicula tristique. Nulla " - "sagittis massa turpis, ac ultricies neque posuere eu. Nulla et " - "imperdiet ex. Etiam venenatis sed lacus tincidunt hendrerit. In " - "libero nisl, congue id tellus vitae, tincidunt tristique mauris. " - "Nullam sed porta massa. Sed condimentum sem eu convallis euismod. " - "Suspendisse lobortis purus faucibus, gravida turpis id, mattis " - "velit. Maecenas eleifend sapien eu tincidunt lobortis. Sed elementum " - "sapien id enim laoreet consequat." - "\n" - "Aenean et neque aliquam, lobortis lectus in, consequat leo. Sed " - "quis egestas nulla. Quisque ac risus quis elit mollis finibus. " - "Phasellus efficitur imperdiet metus." + "Vestibulum tempor diam et aliquam tristique. Nullam condimentum felis " + "et convallis finibus. Aliquam erat volutpat. Nam et blandit tortor. " + "Nullam pharetra arcu aliquam, suscipit mi eu, faucibus lacus. " + "Pellentesque eleifend nulla sit amet purus maximus, et elementum " + "lectus fringilla. Praesent consectetur diam eget tellus molestie " + "tempor. Class aptent taciti sociosqu ad litora torquent per conubia " + "nostra, per inceptos himenaeos. Nam tincidunt urna augue, nec cursus " + "arcu eleifend nec. Donec semper tellus in leo nullam." "\n"; const int ipsum_len = sizeof(lorem_ipsum) - 1; @@ -113,11 +112,26 @@ static void wait(void) } } +static void init_app(void) +{ + NET_INFO(APP_BANNER); + +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + int err = tls_credential_add(CA_CERTIFICATE_TAG, + TLS_CREDENTIAL_CA_CERTIFICATE, + ca_certificate, + sizeof(ca_certificate)); + if (err < 0) { + NET_ERR("Failed to register public certificate: %d", err); + } +#endif +} + void main(void) { int ret; - NET_INFO(APP_BANNER); + init_app(); if (IS_ENABLED(CONFIG_NET_TCP)) { ret = start_tcp(); diff --git a/samples/net/sockets/echo_client/src/tcp.c b/samples/net/sockets/echo_client/src/tcp.c index 25ac665b4993f..9ef44cac3111f 100644 --- a/samples/net/sockets/echo_client/src/tcp.c +++ b/samples/net/sockets/echo_client/src/tcp.c @@ -18,8 +18,10 @@ #include #include +#include #include "common.h" +#include "ca_certificate.h" #define RECV_BUF_SIZE 128 @@ -81,13 +83,39 @@ static int start_tcp_proto(struct data *data, struct sockaddr *addr, { int ret; +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + data->tcp.sock = socket(addr->sa_family, SOCK_STREAM, IPPROTO_TLS_1_2); +#else data->tcp.sock = socket(addr->sa_family, SOCK_STREAM, IPPROTO_TCP); +#endif if (data->tcp.sock < 0) { NET_ERR("Failed to create TCP socket (%s): %d", data->proto, errno); return -errno; } +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + sec_tag_t sec_tag_list[] = { + CA_CERTIFICATE_TAG, + }; + + ret = setsockopt(data->tcp.sock, SOL_TLS, TLS_SEC_TAG_LIST, + sec_tag_list, sizeof(sec_tag_list)); + if (ret < 0) { + NET_ERR("Failed to set TLS_SEC_TAG_LIST option (%s): %d", + data->proto, errno); + ret = -errno; + } + + ret = setsockopt(data->tcp.sock, SOL_TLS, TLS_HOSTNAME, + TLS_PEER_HOSTNAME, sizeof(TLS_PEER_HOSTNAME)); + if (ret < 0) { + NET_ERR("Failed to set TLS_HOSTNAME option (%s): %d", + data->proto, errno); + ret = -errno; + } +#endif + ret = connect(data->tcp.sock, addr, addrlen); if (ret < 0) { NET_ERR("Cannot connect to TCP remote (%s): %d", data->proto, diff --git a/samples/net/sockets/echo_server/CMakeLists.txt b/samples/net/sockets/echo_server/CMakeLists.txt index 8462c3b9ad796..15db5bb84c3d0 100644 --- a/samples/net/sockets/echo_server/CMakeLists.txt +++ b/samples/net/sockets/echo_server/CMakeLists.txt @@ -14,3 +14,16 @@ target_sources_ifdef(CONFIG_NET_UDP app PRIVATE src/udp.c) target_sources_ifdef(CONFIG_NET_TCP app PRIVATE src/tcp.c) include($ENV{ZEPHYR_BASE}/samples/net/common/common.cmake) + +set(gen_dir ${ZEPHYR_BINARY_DIR}/include/generated/) + +foreach(inc_file + echo-apps-cert.der + echo-apps-key.der + ) + generate_inc_file_for_target( + app + src/${inc_file} + ${gen_dir}/${inc_file}.inc + ) +endforeach() diff --git a/samples/net/sockets/echo_server/boards/qemu_x86_tls.conf b/samples/net/sockets/echo_server/boards/qemu_x86_tls.conf new file mode 100644 index 0000000000000..7d2ead40d6715 --- /dev/null +++ b/samples/net/sockets/echo_server/boards/qemu_x86_tls.conf @@ -0,0 +1,16 @@ +CONFIG_NET_UDP=n +CONFIG_NET_TCP=y +CONFIG_NET_IPV6=y +CONFIG_NET_IPV4=y + +CONFIG_MAIN_STACK_SIZE=3072 + +# TLS configuration +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_BUILTIN=y +CONFIG_MBEDTLS_ENABLE_HEAP=y +CONFIG_MBEDTLS_HEAP_SIZE=60000 +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=4096 + +CONFIG_NET_SOCKETS_SOCKOPT_TLS=y +CONFIG_NET_SOCKETS_TLS_MAX_CONTEXTS=4 diff --git a/samples/net/sockets/echo_server/src/certificate.h b/samples/net/sockets/echo_server/src/certificate.h new file mode 100644 index 0000000000000..d0378ff259e0a --- /dev/null +++ b/samples/net/sockets/echo_server/src/certificate.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __CERTIFICATE_H__ +#define __CERTIFICATE_H__ + +#define SERVER_CERTIFICATE_TAG 1 + +static const unsigned char server_certificate[] = { +#include "echo-apps-cert.der.inc" +}; + +/* This is the private key in pkcs#8 format. */ +static const unsigned char private_key[] = { +#include "echo-apps-key.der.inc" +}; + +#endif /* __CERTIFICATE_H__ */ diff --git a/samples/net/sockets/echo_server/src/common.h b/samples/net/sockets/echo_server/src/common.h index 81b03ee5392a3..5def7acf7bac3 100644 --- a/samples/net/sockets/echo_server/src/common.h +++ b/samples/net/sockets/echo_server/src/common.h @@ -5,8 +5,13 @@ * SPDX-License-Identifier: Apache-2.0 */ + #define MY_PORT 4242 +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) +#define STACK_SIZE 4096 +#else #define STACK_SIZE 1024 +#endif #define THREAD_PRIORITY K_PRIO_COOP(8) #define RECV_BUFFER_SIZE 1280 diff --git a/samples/net/sockets/echo_server/src/echo-apps-cert.der b/samples/net/sockets/echo_server/src/echo-apps-cert.der new file mode 100644 index 0000000000000..bfcb335e31c8c Binary files /dev/null and b/samples/net/sockets/echo_server/src/echo-apps-cert.der differ diff --git a/samples/net/sockets/echo_server/src/echo-apps-key.der b/samples/net/sockets/echo_server/src/echo-apps-key.der new file mode 100644 index 0000000000000..5a4d67372ea41 Binary files /dev/null and b/samples/net/sockets/echo_server/src/echo-apps-key.der differ diff --git a/samples/net/sockets/echo_server/src/echo-server.c b/samples/net/sockets/echo_server/src/echo-server.c index 11c9308886922..9a3db61a80617 100644 --- a/samples/net/sockets/echo_server/src/echo-server.c +++ b/samples/net/sockets/echo_server/src/echo-server.c @@ -18,8 +18,10 @@ #include #include +#include #include "common.h" +#include "certificate.h" #define APP_BANNER "Run echo server" @@ -44,6 +46,24 @@ static void init_app(void) k_sem_init(&quit_lock, 0, UINT_MAX); NET_INFO(APP_BANNER); + +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + int err = tls_credential_add(SERVER_CERTIFICATE_TAG, + TLS_CREDENTIAL_SERVER_CERTIFICATE, + server_certificate, + sizeof(server_certificate)); + if (err < 0) { + NET_ERR("Failed to register public certificate: %d", err); + } + + + err = tls_credential_add(SERVER_CERTIFICATE_TAG, + TLS_CREDENTIAL_PRIVATE_KEY, + private_key, sizeof(private_key)); + if (err < 0) { + NET_ERR("Failed to register private key: %d", err); + } +#endif } void main(void) diff --git a/samples/net/sockets/echo_server/src/tcp.c b/samples/net/sockets/echo_server/src/tcp.c index 65c2b570e362e..eefc55c9be7fe 100644 --- a/samples/net/sockets/echo_server/src/tcp.c +++ b/samples/net/sockets/echo_server/src/tcp.c @@ -18,8 +18,10 @@ #include #include +#include #include "common.h" +#include "certificate.h" #define MAX_CLIENT_QUEUE 1 @@ -54,13 +56,32 @@ static int start_tcp_proto(struct data *data, struct sockaddr *bind_addr, { int ret; +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + data->tcp.sock = socket(bind_addr->sa_family, SOCK_STREAM, + IPPROTO_TLS_1_2); +#else data->tcp.sock = socket(bind_addr->sa_family, SOCK_STREAM, IPPROTO_TCP); +#endif if (data->tcp.sock < 0) { NET_ERR("Failed to create TCP socket (%s): %d", data->proto, errno); return -errno; } +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + sec_tag_t sec_tag_list[] = { + SERVER_CERTIFICATE_TAG, + }; + + ret = setsockopt(data->tcp.sock, SOL_TLS, TLS_SEC_TAG_LIST, + sec_tag_list, sizeof(sec_tag_list)); + if (ret < 0) { + NET_ERR("Failed to set TCP secure option (%s): %d", data->proto, + errno); + ret = -errno; + } +#endif + ret = bind(data->tcp.sock, bind_addr, bind_addrlen); if (ret < 0) { NET_ERR("Failed to bind TCP socket (%s): %d", data->proto, @@ -118,6 +139,7 @@ static int process_tcp(struct data *data) offset += received; +#if !defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) /* To prevent fragmentation of the response, reply only if * buffer is full or there is no more data to read */ @@ -126,6 +148,7 @@ static int process_tcp(struct data *data) sizeof(data->tcp.recv_buffer) - offset, MSG_PEEK | MSG_DONTWAIT) < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))) { +#endif ret = sendall(client, data->tcp.recv_buffer, offset); if (ret < 0) { NET_ERR("TCP (%s): Failed to send, " @@ -143,7 +166,9 @@ static int process_tcp(struct data *data) } offset = 0; +#if !defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) } +#endif } while (true); close(client); diff --git a/samples/net/sockets/http_get/CMakeLists.txt b/samples/net/sockets/http_get/CMakeLists.txt index e6bf10afba6f5..2579224865ddf 100644 --- a/samples/net/sockets/http_get/CMakeLists.txt +++ b/samples/net/sockets/http_get/CMakeLists.txt @@ -5,3 +5,11 @@ FILE(GLOB app_sources src/*.c) target_sources(app PRIVATE ${app_sources}) include($ENV{ZEPHYR_BASE}/samples/net/common/common.cmake) + +set(gen_dir ${ZEPHYR_BINARY_DIR}/include/generated/) + +generate_inc_file_for_target( + app + src/globalsign_r2.der + ${gen_dir}/globalsign_r2.der.inc + ) diff --git a/samples/net/sockets/http_get/src/ca_certificate.h b/samples/net/sockets/http_get/src/ca_certificate.h new file mode 100644 index 0000000000000..f44170c3c5ae2 --- /dev/null +++ b/samples/net/sockets/http_get/src/ca_certificate.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __CA_CERTIFICATE_H__ +#define __CA_CERTIFICATE_H__ + +#define CA_CERTIFICATE_TAG 1 + +/* By default only certificates in DER format are supported. If you want to use + * certificate in PEM format, you can enable support for it in Kconfig. + */ + +/* GlobalSign Root CA - R2 for https://google.com */ +static const unsigned char ca_certificate[] = { +#include "globalsign_r2.der.inc" +}; + +#endif /* __CA_CERTIFICATE_H__ */ diff --git a/samples/net/sockets/http_get/src/globalsign_r2.der b/samples/net/sockets/http_get/src/globalsign_r2.der new file mode 100644 index 0000000000000..4d937187ede7f Binary files /dev/null and b/samples/net/sockets/http_get/src/globalsign_r2.der differ diff --git a/samples/net/sockets/http_get/src/http_get.c b/samples/net/sockets/http_get/src/http_get.c index 22a2d6e1e5e8f..6f1b494e67755 100644 --- a/samples/net/sockets/http_get/src/http_get.c +++ b/samples/net/sockets/http_get/src/http_get.c @@ -21,6 +21,11 @@ #include #include +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) +#include +#include "ca_certificate.h" +#endif + #endif /* HTTP server to connect to */ @@ -57,6 +62,11 @@ int main(void) struct addrinfo *res; int st, sock; +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + tls_credential_add(CA_CERTIFICATE_TAG, TLS_CREDENTIAL_CA_CERTIFICATE, + ca_certificate, sizeof(ca_certificate)); +#endif + printf("Preparing HTTP GET request for http://" HTTP_HOST ":" HTTP_PORT HTTP_PATH "\n"); @@ -85,6 +95,18 @@ int main(void) #endif CHECK(sock); printf("sock = %d\n", sock); + +#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + sec_tag_t sec_tag_opt[] = { + CA_CERTIFICATE_TAG, + }; + CHECK(setsockopt(sock, SOL_TLS, TLS_SEC_TAG_LIST, + sec_tag_opt, sizeof(sec_tag_opt))); + + CHECK(setsockopt(sock, SOL_TLS, TLS_HOSTNAME, + HTTP_HOST, sizeof(HTTP_HOST))) +#endif + CHECK(connect(sock, res->ai_addr, res->ai_addrlen)); CHECK(send(sock, REQUEST, SSTRLEN(REQUEST), 0)); diff --git a/subsys/net/lib/CMakeLists.txt b/subsys/net/lib/CMakeLists.txt index f201fb3ed7225..b421cd5343c6b 100644 --- a/subsys/net/lib/CMakeLists.txt +++ b/subsys/net/lib/CMakeLists.txt @@ -6,6 +6,7 @@ add_subdirectory_ifdef(CONFIG_MQTT_LIB mqtt) add_subdirectory_ifdef(CONFIG_NET_APP app) add_subdirectory_ifdef(CONFIG_NET_APP_SETTINGS config) add_subdirectory_ifdef(CONFIG_NET_SOCKETS sockets) +add_subdirectory_ifdef(CONFIG_TLS_CREDENTIALS tls_credentials) add_subdirectory_ifdef(CONFIG_WEBSOCKET websocket) if(CONFIG_HTTP_PARSER_URL diff --git a/subsys/net/lib/Kconfig b/subsys/net/lib/Kconfig index 908959db47163..b49c5ea84e7bf 100644 --- a/subsys/net/lib/Kconfig +++ b/subsys/net/lib/Kconfig @@ -30,4 +30,6 @@ source "subsys/net/lib/app/Kconfig" source "subsys/net/lib/sockets/Kconfig" +source "subsys/net/lib/tls_credentials/Kconfig" + endmenu diff --git a/subsys/net/lib/sockets/Kconfig b/subsys/net/lib/sockets/Kconfig index 3486c3dcedc81..161a35eb10685 100644 --- a/subsys/net/lib/sockets/Kconfig +++ b/subsys/net/lib/sockets/Kconfig @@ -33,6 +33,7 @@ config NET_SOCKETS_POLL_MAX config NET_SOCKETS_SOCKOPT_TLS bool "Enable TCP TLS socket option support [EXPERIMENTAL]" default n + select TLS_CREDENTIALS help Enable TLS socket option support which automatically establishes a TLS connection to the remote host. @@ -45,6 +46,24 @@ config NET_SOCKETS_TLS_MAX_CONTEXTS "This variable specifies maximum number of TLS/DTLS contexts that can be allocated at the same time." +config NET_SOCKETS_TLS_MAX_CREDENTIALS + int "Maximum number of TLS/DTLS credentials per socket" + default 4 + depends on NET_SOCKETS_SOCKOPT_TLS + help + This variable sets maximum number of TLS/DTLS credentials that can be + used with a specific socket. + +config NET_SOCKETS_TLS_MAX_CIPHERSUITES + int "Maximum number of TLS/DTLS ciphersuites per socket" + default 4 + depends on NET_SOCKETS_SOCKOPT_TLS + help + This variable sets maximum number of TLS/DTLS ciphersuites that can + be used with specific socket, if set explicitly by socket option. + By default, all ciphersuites that are available in the system are + available to the socket. + config NET_DEBUG_SOCKETS bool "Debug BSD Sockets compatible API calls" default y if NET_LOG_GLOBAL diff --git a/subsys/net/lib/sockets/sockets.c b/subsys/net/lib/sockets/sockets.c index e8d9ace68159b..b4d45c8e37ddd 100644 --- a/subsys/net/lib/sockets/sockets.c +++ b/subsys/net/lib/sockets/sockets.c @@ -577,3 +577,19 @@ int zsock_inet_pton(sa_family_t family, const char *src, void *dst) return 0; } } + +int zsock_getsockopt(int sock, int level, int optname, + void *optval, socklen_t *optlen) +{ + SET_ERRNO(-ENOPROTOOPT); + + return 0; +} + +int zsock_setsockopt(int sock, int level, int optname, + const void *optval, socklen_t optlen) +{ + SET_ERRNO(-ENOPROTOOPT); + + return 0; +} diff --git a/subsys/net/lib/sockets/sockets_tls.c b/subsys/net/lib/sockets/sockets_tls.c index 9fe9713b36a22..e549c474ad411 100644 --- a/subsys/net/lib/sockets/sockets_tls.c +++ b/subsys/net/lib/sockets/sockets_tls.c @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -32,6 +33,17 @@ #include #endif /* CONFIG_MBEDTLS */ +#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; +}; + /** TLS context information. */ struct tls_context { /** Information whether TLS context is used. */ @@ -43,12 +55,40 @@ struct tls_context { /** Socket flags passed to a socket call. */ int flags; + /** 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; + } options; + #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 */ }; @@ -170,6 +210,7 @@ static struct tls_context *tls_alloc(void) 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; @@ -181,6 +222,11 @@ static struct tls_context *tls_alloc(void) if (tls) { mbedtls_ssl_init(&tls->ssl); mbedtls_ssl_config_init(&tls->config); +#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); @@ -204,6 +250,14 @@ static struct tls_context *tls_clone(struct tls_context *source_tls) 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; } @@ -222,6 +276,11 @@ static int tls_release(struct tls_context *tls) 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; @@ -266,14 +325,170 @@ static int tls_rx(void *ctx, unsigned char *buf, size_t len) return received; } -static int tls_mbedtls_set_credentials(struct tls_context *tls) +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) { - /* TODO Temporary solution to verify communication */ - mbedtls_ssl_conf_authmode(&tls->config, MBEDTLS_SSL_VERIFY_NONE); +#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_handshake(struct net_context *context) { int ret; @@ -321,6 +536,22 @@ static int tls_mbedtls_init(struct net_context *context, bool is_server) return -ENOMEM; } + /* 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); @@ -342,6 +573,166 @@ static int tls_mbedtls_init(struct net_context *context, bool is_server) 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; +} + int ztls_socket(int family, int type, int proto) { enum net_ip_protocol_secure tls_proto = 0; @@ -668,3 +1059,106 @@ int ztls_poll(struct zsock_pollfd *fds, int nfds, int timeout) 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; + + 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; + + default: + err = -ENOPROTOOPT; + break; + } + + if (err < 0) { + errno = -err; + return -1; + } + + return 0; +} diff --git a/subsys/net/lib/tls_credentials/CMakeLists.txt b/subsys/net/lib/tls_credentials/CMakeLists.txt new file mode 100644 index 0000000000000..ef3f7abdf1f7e --- /dev/null +++ b/subsys/net/lib/tls_credentials/CMakeLists.txt @@ -0,0 +1,4 @@ +zephyr_include_directories(.) +zephyr_sources( + tls_credentials.c +) diff --git a/subsys/net/lib/tls_credentials/Kconfig b/subsys/net/lib/tls_credentials/Kconfig new file mode 100644 index 0000000000000..1c93ddefe3712 --- /dev/null +++ b/subsys/net/lib/tls_credentials/Kconfig @@ -0,0 +1,21 @@ +# +# Copyright (c) 2018 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig TLS_CREDENTIALS + bool "TLS credentials management" + default n + help + Enable TLS credentials management subsystem. + +if TLS_CREDENTIALS + +config TLS_MAX_CREDENTIALS_NUMBER + int "Maximum number of TLS credentials" + default 4 + help + Maximum number of TLS credentials that can be registered. + +endif # TLS_CREDENTIALS diff --git a/subsys/net/lib/tls_credentials/tls_credentials.c b/subsys/net/lib/tls_credentials/tls_credentials.c new file mode 100644 index 0000000000000..daf8aca3733ea --- /dev/null +++ b/subsys/net/lib/tls_credentials/tls_credentials.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +#include +#include + +#include "tls_internal.h" + +/* Global pool of credentials shared among TLS contexts. */ +static struct tls_credential credentials[CONFIG_TLS_MAX_CREDENTIALS_NUMBER]; + +/* A mutex for protecting access to the credentials array. */ +static struct k_mutex credential_lock; + +static int credentials_init(struct device *unused) +{ + memset(credentials, 0, sizeof(credentials)); + + k_mutex_init(&credential_lock); + + return 0; +} +SYS_INIT(credentials_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); + +static struct tls_credential *unused_credential_get(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(credentials); i++) { + if (credentials[i].type == TLS_CREDENTIAL_NONE) { + return &credentials[i]; + } + } + + return NULL; +} + +struct tls_credential *credential_get(sec_tag_t tag, + enum tls_credential_type type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(credentials); i++) { + if (credentials[i].type == type && credentials[i].tag == tag) { + return &credentials[i]; + } + } + + return NULL; +} + +struct tls_credential *credential_next_get(sec_tag_t tag, + struct tls_credential *iter) +{ + int i; + + if (!iter) { + iter = credentials; + } else { + iter++; + } + + for (i = iter - credentials; i < ARRAY_SIZE(credentials); i++) { + if (credentials[i].type != TLS_CREDENTIAL_NONE && + credentials[i].tag == tag) { + return &credentials[i]; + } + } + + return NULL; +} + +void credentials_lock(void) +{ + k_mutex_lock(&credential_lock, K_FOREVER); +} + +void credentials_unlock(void) +{ + k_mutex_unlock(&credential_lock); +} + +int tls_credential_add(sec_tag_t tag, enum tls_credential_type type, + const void *cred, size_t credlen) +{ + struct tls_credential *credential; + int ret = 0; + + credentials_lock(); + + credential = credential_get(tag, type); + if (credential != NULL) { + ret = -EEXIST; + goto exit; + } + + credential = unused_credential_get(); + if (credential == NULL) { + ret = -ENOMEM; + goto exit; + } + + credential->tag = tag; + credential->type = type; + credential->buf = cred; + credential->len = credlen; + +exit: + credentials_unlock(); + + return ret; +} + +int tls_credential_get(sec_tag_t tag, enum tls_credential_type type, + void *cred, size_t *credlen) +{ + struct tls_credential *credential; + int ret = 0; + + credentials_lock(); + + credential = credential_get(tag, type); + if (credential == NULL) { + ret = -ENOENT; + goto exit; + } + + if (credential->len > *credlen) { + ret = -EFBIG; + goto exit; + } + + *credlen = credential->len; + memcpy(cred, credential->buf, credential->len); + +exit: + credentials_unlock(); + + return ret; +} + +int tls_credential_delete(sec_tag_t tag, enum tls_credential_type type) +{ + struct tls_credential *credential; + int ret = 0; + + credentials_lock(); + + credential = credential_get(tag, type); + + if (!credential) { + ret = -ENOENT; + goto exit; + } + + memset(credential, 0, sizeof(struct tls_credential)); + credential->type = TLS_CREDENTIAL_NONE; + +exit: + credentials_unlock(); + + return ret; +} diff --git a/subsys/net/lib/tls_credentials/tls_internal.h b/subsys/net/lib/tls_credentials/tls_internal.h new file mode 100644 index 0000000000000..5ecc1d3025c9f --- /dev/null +++ b/subsys/net/lib/tls_credentials/tls_internal.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** @file + * @brief Internal API for fetching TLS credentials + */ + +#ifndef __TLS_INTERNAL_H +#define __TLS_INTERNAL_H + +#include + +/* Internal structure representing TLS credential. */ +struct tls_credential { + /* TLS credential type. */ + enum tls_credential_type type; + + /* Secure tag that credential can be referenced with. */ + sec_tag_t tag; + + /* A pointer to the credential buffer. */ + const void *buf; + + /* Credential length. */ + size_t len; +}; + +/* Lock TLS credential access. */ +void credentials_lock(void); + +/* Unlock TLS credential access. */ +void credentials_unlock(void); + +/* Function for getting credential by tag and type. + * + * Note, that to assure thread safety, credential access should be locked with + * credentials_lock before calling this function. + */ +struct tls_credential *credential_get(sec_tag_t tag, + enum tls_credential_type type); + + +/* Function for iterating over credentials by tag. + * + * Note, that to assure thread safety, credential access should be locked with + * credentials_lock before calling this function. + */ +struct tls_credential *credential_next_get(sec_tag_t tag, + struct tls_credential *iter); + +#endif /* __TLS_INTERNAL_H */