From d57141bfd34650cf560e85d2b45a1275e069bf54 Mon Sep 17 00:00:00 2001 From: roman Date: Thu, 8 Jun 2023 11:06:42 +0200 Subject: [PATCH] config UPDATE support for TLS verions and ciphers --- src/config_new_tls.c | 370 ++++++++++++++++++++++++++++----------- src/server_config.c | 185 +++++++++++++++++++- src/server_config.h | 35 ++++ src/session.h | 10 ++ src/session_p.h | 3 + src/session_server_tls.c | 24 +++ tests/test_tls.c | 9 + 7 files changed, 530 insertions(+), 106 deletions(-) diff --git a/src/config_new_tls.c b/src/config_new_tls.c index 7dafe87a..1d2939b1 100644 --- a/src/config_new_tls.c +++ b/src/config_new_tls.c @@ -15,6 +15,7 @@ #define _GNU_SOURCE +#include #include #include #include @@ -30,108 +31,6 @@ #include "session.h" #include "session_p.h" -API int -nc_server_config_new_tls_ctn(const struct ly_ctx *ctx, const char *endpt_name, uint32_t id, const char *fingerprint, - NC_TLS_CTN_MAPTYPE map_type, const char *name, struct lyd_node **config) -{ - int ret = 0; - char *tree_path = NULL; - struct lyd_node *new_tree; - - NC_CHECK_ARG_RET(NULL, ctx, endpt_name, id, map_type, name, 1); - NC_CHECK_ARG_RET(NULL, config, 1); - - /* prepare path for instertion of leaves later */ - asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/" - "netconf-server-parameters/client-identity-mappings/cert-to-name[id='%d']", endpt_name, id); - if (!tree_path) { - ERRMEM; - ret = 1; - goto cleanup; - } - - /* create all the nodes in the path */ - ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree); - if (ret) { - goto cleanup; - } - if (!*config) { - *config = new_tree; - } - - if (!new_tree) { - /* no new nodes were created */ - ret = lyd_find_path(*config, tree_path, 0, &new_tree); - } else { - /* config was NULL */ - ret = lyd_find_path(new_tree, tree_path, 0, &new_tree); - } - if (ret) { - ERR(NULL, "Unable to find netconf-server-parameters container."); - goto cleanup; - } - - /* not mandatory */ - if (fingerprint) { - ret = lyd_new_term(new_tree, NULL, "fingerprint", fingerprint, 0, NULL); - if (ret) { - goto cleanup; - } - } - - /* insert map-type */ - switch (map_type) { - case NC_TLS_CTN_SPECIFIED: - ret = lyd_new_term(new_tree, NULL, "map-type", "ietf-x509-cert-to-name:specified", 0, NULL); - break; - case NC_TLS_CTN_SAN_RFC822_NAME: - ret = lyd_new_term(new_tree, NULL, "map-type", "ietf-x509-cert-to-name:san-rfc822-name", 0, NULL); - break; - case NC_TLS_CTN_SAN_DNS_NAME: - ret = lyd_new_term(new_tree, NULL, "map-type", "ietf-x509-cert-to-name:san-dns-name", 0, NULL); - break; - case NC_TLS_CTN_SAN_IP_ADDRESS: - ret = lyd_new_term(new_tree, NULL, "map-type", "ietf-x509-cert-to-name:san-ip-address", 0, NULL); - break; - case NC_TLS_CTN_SAN_ANY: - ret = lyd_new_term(new_tree, NULL, "map-type", "ietf-x509-cert-to-name:san-any", 0, NULL); - break; - case NC_TLS_CTN_COMMON_NAME: - ret = lyd_new_term(new_tree, NULL, "map-type", "ietf-x509-cert-to-name:common-name", 0, NULL); - break; - case NC_TLS_CTN_UNKNOWN: - default: - ERR(NULL, "Unknown map_type."); - ret = 1; - break; - } - if (ret) { - goto cleanup; - } - - /* insert name */ - ret = lyd_new_term(new_tree, NULL, "name", name, 0, NULL); - if (ret) { - goto cleanup; - } - - /* check if top-level container has operation and if not, add it */ - ret = nc_config_new_check_add_operation(ctx, *config); - if (ret) { - goto cleanup; - } - - /* Add all default nodes */ - ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL); - if (ret) { - goto cleanup; - } - -cleanup: - free(tree_path); - return ret; -} - API int nc_server_config_new_tls_server_certificate(const struct ly_ctx *ctx, const char *endpt_name, const char *pubkey_path, const char *privkey_path, const char *certificate_path, struct lyd_node **config) @@ -404,3 +303,270 @@ nc_server_config_new_tls_client_ca(const struct ly_ctx *ctx, const char *endpt_n free(tree_path); return ret; } + +API int +nc_server_config_new_tls_ctn(const struct ly_ctx *ctx, const char *endpt_name, uint32_t id, const char *fingerprint, + NC_TLS_CTN_MAPTYPE map_type, const char *name, struct lyd_node **config) +{ + int ret = 0; + char *tree_path = NULL; + struct lyd_node *new_tree; + + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, id, map_type, name, 1); + NC_CHECK_ARG_RET(NULL, config, 1); + + /* prepare path for instertion of leaves later */ + asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/" + "netconf-server-parameters/client-identity-mappings/cert-to-name[id='%d']", endpt_name, id); + if (!tree_path) { + ERRMEM; + ret = 1; + goto cleanup; + } + + /* create all the nodes in the path */ + ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree); + if (ret) { + goto cleanup; + } + if (!*config) { + *config = new_tree; + } + + if (!new_tree) { + /* no new nodes were created */ + ret = lyd_find_path(*config, tree_path, 0, &new_tree); + } else { + /* config was NULL */ + ret = lyd_find_path(new_tree, tree_path, 0, &new_tree); + } + if (ret) { + ERR(NULL, "Unable to find netconf-server-parameters container."); + goto cleanup; + } + + /* not mandatory */ + if (fingerprint) { + ret = lyd_new_term(new_tree, NULL, "fingerprint", fingerprint, 0, NULL); + if (ret) { + goto cleanup; + } + } + + /* insert map-type */ + switch (map_type) { + case NC_TLS_CTN_SPECIFIED: + ret = lyd_new_term(new_tree, NULL, "map-type", "ietf-x509-cert-to-name:specified", 0, NULL); + break; + case NC_TLS_CTN_SAN_RFC822_NAME: + ret = lyd_new_term(new_tree, NULL, "map-type", "ietf-x509-cert-to-name:san-rfc822-name", 0, NULL); + break; + case NC_TLS_CTN_SAN_DNS_NAME: + ret = lyd_new_term(new_tree, NULL, "map-type", "ietf-x509-cert-to-name:san-dns-name", 0, NULL); + break; + case NC_TLS_CTN_SAN_IP_ADDRESS: + ret = lyd_new_term(new_tree, NULL, "map-type", "ietf-x509-cert-to-name:san-ip-address", 0, NULL); + break; + case NC_TLS_CTN_SAN_ANY: + ret = lyd_new_term(new_tree, NULL, "map-type", "ietf-x509-cert-to-name:san-any", 0, NULL); + break; + case NC_TLS_CTN_COMMON_NAME: + ret = lyd_new_term(new_tree, NULL, "map-type", "ietf-x509-cert-to-name:common-name", 0, NULL); + break; + case NC_TLS_CTN_UNKNOWN: + default: + ERR(NULL, "Unknown map_type."); + ret = 1; + break; + } + if (ret) { + goto cleanup; + } + + /* insert name */ + ret = lyd_new_term(new_tree, NULL, "name", name, 0, NULL); + if (ret) { + goto cleanup; + } + + /* check if top-level container has operation and if not, add it */ + ret = nc_config_new_check_add_operation(ctx, *config); + if (ret) { + goto cleanup; + } + + /* Add all default nodes */ + ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL); + if (ret) { + goto cleanup; + } + +cleanup: + free(tree_path); + return ret; +} + +API int +nc_server_config_new_tls_version(const struct ly_ctx *ctx, const char *endpt_name, + NC_TLS_VERSION tls_version, struct lyd_node **config) +{ + int ret = 0; + struct lyd_node *new_tree; + char *tree_path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, config, 1); + + /* prepare path for instertion of leaves later */ + asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tls-server-parameters/" + "hello-params/tls-versions", endpt_name); + if (!tree_path) { + ERRMEM; + ret = 1; + goto cleanup; + } + + /* create all the nodes in the path */ + ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree); + if (ret) { + goto cleanup; + } + if (!*config) { + *config = new_tree; + } + + if (!new_tree) { + /* no new nodes were created */ + ret = lyd_find_path(*config, tree_path, 0, &new_tree); + } else { + /* config was NULL */ + ret = lyd_find_path(new_tree, tree_path, 0, &new_tree); + } + if (ret) { + goto cleanup; + } + + switch (tls_version) { + case NC_TLS_VERSION_10: + ret = lyd_new_term(new_tree, NULL, "tls-version", "ietf-tls-common:tls10", 0, NULL); + break; + case NC_TLS_VERSION_11: + ret = lyd_new_term(new_tree, NULL, "tls-version", "ietf-tls-common:tls11", 0, NULL); + break; + case NC_TLS_VERSION_12: + ret = lyd_new_term(new_tree, NULL, "tls-version", "ietf-tls-common:tls12", 0, NULL); + break; + case NC_TLS_VERSION_13: + ret = lyd_new_term(new_tree, NULL, "tls-version", "ietf-tls-common:tls13", 0, NULL); + break; + default: + ERR(NULL, "Unknown TLS version."); + ret = 1; + break; + } + if (ret) { + ERR(NULL, "Creating new tls-version node failed."); + goto cleanup; + } + + /* check if top-level container has operation and if not, add it */ + ret = nc_config_new_check_add_operation(ctx, *config); + if (ret) { + goto cleanup; + } + + /* Add all default nodes */ + ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL); + if (ret) { + goto cleanup; + } + +cleanup: + free(tree_path); + return ret; +} + +API int +nc_server_config_new_tls_ciphers(const struct ly_ctx *ctx, const char *endpt_name, struct lyd_node **config, + uint16_t cipher_count, ...) +{ + int ret = 0; + struct lyd_node *new_tree = NULL, *old = NULL; + va_list ap; + char *tree_path = NULL, *cipher = NULL, *cipher_ident = NULL; + uint16_t i; + + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, config, 1); + + /* prepare path */ + asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/" + "tls/tls-server-parameters/hello-params", endpt_name); + if (!tree_path) { + ERRMEM; + ret = 1; + goto cleanup; + } + + /* create all the nodes in the path */ + ret = lyd_new_path(*config, ctx, tree_path, NULL, LYD_NEW_PATH_UPDATE, &new_tree); + if (ret) { + goto cleanup; + } + if (!*config) { + *config = new_tree; + } + + if (!new_tree) { + /* no new nodes were created */ + ret = lyd_find_path(*config, tree_path, 0, &new_tree); + } else { + /* config was NULL */ + ret = lyd_find_path(new_tree, tree_path, 0, &new_tree); + } + if (ret) { + goto cleanup; + } + + /* delete all older algorithms (if any) se they can be replaced by the new ones */ + lyd_find_path(new_tree, "cipher-suites", 0, &old); + if (old) { + lyd_free_tree(old); + } + + va_start(ap, cipher_count); + for (i = 0; i < cipher_count; i++) { + cipher = va_arg(ap, char *); + + asprintf(&cipher_ident, "iana-tls-cipher-suite-algs:%s", cipher); + if (!cipher_ident) { + ERRMEM; + ret = 1; + goto cleanup; + } + + /* create the leaf list */ + ret = lyd_new_path(new_tree, ctx, "cipher-suites/cipher-suite", cipher_ident, 0, NULL); + free(cipher_ident); + + if (ret) { + ERR(NULL, "Creating new cipher-suites leaf-list failed."); + goto cleanup; + } + } + + /* check if top-level container has operation and if not, add it */ + ret = nc_config_new_check_add_operation(ctx, *config); + if (ret) { + goto cleanup; + } + + /* Add all default nodes */ + ret = lyd_new_implicit_tree(*config, LYD_IMPLICIT_NO_STATE, NULL); + if (ret) { + goto cleanup; + } + +cleanup: + va_end(ap); + free(tree_path); + return ret; +} diff --git a/src/server_config.c b/src/server_config.c index 4182cbd7..a143839e 100644 --- a/src/server_config.c +++ b/src/server_config.c @@ -16,6 +16,7 @@ #define _GNU_SOURCE #include +#include #include #include #include @@ -668,6 +669,13 @@ nc_server_config_del_endpt_unix_socket(struct nc_endpt *endpt, struct nc_bind *b #ifdef NC_ENABLED_SSH_TLS +static void +nc_server_config_tls_del_ciphers(struct nc_server_tls_opts *opts) +{ + free(opts->ciphers); + opts->ciphers = NULL; +} + static void nc_server_config_tls_del_public_key(struct nc_server_tls_opts *opts) { @@ -764,7 +772,7 @@ nc_server_config_del_ctn(struct nc_server_tls_opts *opts, struct nc_ctn *ctn) } static void -nc_server_config_del_ctns(struct nc_server_tls_opts *opts) +nc_server_config_tls_del_ctns(struct nc_server_tls_opts *opts) { struct nc_ctn *cur, *next; @@ -796,7 +804,8 @@ nc_server_config_del_tls(struct nc_bind *bind, struct nc_server_tls_opts *opts) nc_server_config_tls_del_certs(&opts->ca_certs); nc_server_config_tls_del_certs(&opts->ee_certs); - nc_server_config_del_ctns(opts); + nc_server_config_tls_del_ctns(opts); + nc_server_config_tls_del_ciphers(opts); free(opts); } @@ -2999,6 +3008,163 @@ nc_server_config_fingerprint(const struct lyd_node *node, NC_OPERATION op) return ret; } +static void +nc_server_config_set_tls_version(struct nc_server_tls_opts *opts, NC_TLS_VERSION version, NC_OPERATION op) +{ + if (op == NC_OP_CREATE) { + /* add the version if it isn't there already */ + opts->tls_versions |= version; + } else if ((op == NC_OP_DELETE) && (opts->tls_versions & version)) { + /* delete the version if it is there */ + opts->tls_versions -= version; + } +} + +static int +nc_server_config_tls_version(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_endpt *endpt; + const char *version = NULL; + + assert(!strcmp(LYD_NAME(node), "tls-version")); + + if (nc_server_config_get_endpt(node, &endpt, NULL)) { + ret = 1; + goto cleanup; + } + + version = ((struct lyd_node_term *)node)->value.ident->name; + if (!strcmp(version, "tls10")) { + nc_server_config_set_tls_version(endpt->opts.tls, NC_TLS_VERSION_10, op); + } else if (!strcmp(version, "tls11")) { + nc_server_config_set_tls_version(endpt->opts.tls, NC_TLS_VERSION_11, op); + } else if (!strcmp(version, "tls12")) { + nc_server_config_set_tls_version(endpt->opts.tls, NC_TLS_VERSION_12, op); + } else if (!strcmp(version, "tls13")) { + nc_server_config_set_tls_version(endpt->opts.tls, NC_TLS_VERSION_13, op); + } else { + ERR(NULL, "TLS version \"%s\" not supported.", version); + ret = 1; + goto cleanup; + } + +cleanup: + return ret; +} + +static int +nc_server_config_create_cipher_suite(struct nc_server_tls_opts *opts, const char *cipher) +{ + int ret = 0; + char *ssl_cipher = NULL; + uint16_t i; + + ssl_cipher = malloc(strlen(cipher) + 1); + if (!ssl_cipher) { + ERRMEM; + ret = 1; + goto cleanup; + } + + for (i = 0; cipher[i]; i++) { + if (cipher[i] == '-') { + /* OpenSSL requires _ instead of - in cipher names */ + ssl_cipher[i] = '_'; + } else { + /* and requires uppercase unlike the identities */ + ssl_cipher[i] = toupper(cipher[i]); + } + } + ssl_cipher[i] = '\0'; + + if (!opts->ciphers) { + /* first entry */ + opts->ciphers = strdup(ssl_cipher); + if (!opts->ciphers) { + ERRMEM; + ret = 1; + goto cleanup; + } + } else { + /* + 1 because of : between entries */ + opts->ciphers = nc_realloc(opts->ciphers, strlen(opts->ciphers) + strlen(ssl_cipher) + 1 + 1); + if (!opts->ciphers) { + ERRMEM; + ret = 1; + goto cleanup; + } + sprintf(opts->ciphers, "%s:%s", opts->ciphers, ssl_cipher); + } + +cleanup: + free(ssl_cipher); + return ret; +} + +static int +nc_server_config_del_concrete_cipher_suite(struct nc_server_tls_opts *opts, const char *cipher) +{ + int cipher_found = 0; + char *haystack, *substr; + size_t cipher_len = strlen(cipher); + + /* delete */ + haystack = opts->ciphers; + while ((substr = strstr(haystack, cipher))) { + /* iterate over all the substrings */ + if (((substr == haystack) && (*(substr + cipher_len) == ':')) || + ((substr != haystack) && (*(substr - 1) == ':') && (*(substr + cipher_len) == ':'))) { + /* either the first element of the string or somewhere in the middle */ + memmove(substr, substr + cipher_len + 1, strlen(substr + cipher_len + 1)); + cipher_found = 1; + break; + } else if ((*(substr - 1) == ':') && (*(substr + cipher_len) == '\0')) { + /* the last element of the string */ + *(substr - 1) = '\0'; + cipher_found = 1; + break; + } + haystack++; + } + + if (!cipher_found) { + ERR(NULL, "Unable to delete a cipher (%s), which was not previously added.", cipher); + return 1; + } + + return 0; +} + +static int +nc_server_config_cipher_suite(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_endpt *endpt; + const char *cipher = NULL; + + if (nc_server_config_get_endpt(node, &endpt, NULL)) { + ret = 1; + goto cleanup; + } + + cipher = ((struct lyd_node_term *)node)->value.ident->name; + if (op == NC_OP_CREATE) { + ret = nc_server_config_create_cipher_suite(endpt->opts.tls, cipher); + if (ret) { + goto cleanup; + } + } else if (op == NC_OP_DELETE) { + ret = nc_server_config_del_concrete_cipher_suite(endpt->opts.tls, cipher); + if (ret) { + goto cleanup; + } + } + +cleanup: + return ret; +} + #endif /* NC_ENABLED_SSH_TLS */ static int @@ -3152,6 +3318,14 @@ nc_server_config_parse_netconf_server(const struct lyd_node *node, NC_OPERATION if (nc_server_config_fingerprint(node, op)) { goto error; } + } else if (!strcmp(name, "tls-version")) { + if (nc_server_config_tls_version(node, op)) { + goto error; + } + } else if (!strcmp(name, "cipher-suite")) { + if (nc_server_config_cipher_suite(node, op)) { + goto error; + } } #endif /* NC_ENABLED_SSH_TLS */ @@ -3286,6 +3460,7 @@ nc_server_config_load_modules(struct ly_ctx **ctx) "server-ident-tls13-epsk", "client-auth-supported", "client-auth-x509-cert", "client-auth-raw-public-key", "client-auth-tls12-psk", "client-auth-tls13-epsk", NULL }; + const char *iana_tls_cipher_suite_algs[] = {NULL}; /* all features */ const char *libnetconf2_netconf_server[] = {NULL}; @@ -3293,14 +3468,16 @@ nc_server_config_load_modules(struct ly_ctx **ctx) "ietf-netconf-server", "ietf-x509-cert-to-name", "ietf-crypto-types", "ietf-tcp-common", "ietf-tcp-server", "ietf-tcp-client", "ietf-ssh-common", "ietf-ssh-server", "iana-ssh-encryption-algs", "iana-ssh-key-exchange-algs", "iana-ssh-mac-algs", "iana-ssh-public-key-algs", "iana-crypt-hash", - "ietf-keystore", "ietf-truststore", "ietf-tls-common", "ietf-tls-server", "libnetconf2-netconf-server", NULL + "ietf-keystore", "ietf-truststore", "ietf-tls-common", "ietf-tls-server", "iana-tls-cipher-suite-algs", + "libnetconf2-netconf-server", NULL }; const char **module_features[] = { ietf_nectonf_server, ietf_x509_cert_to_name, ietf_crypto_types, ietf_tcp_common, ietf_tcp_server, ietf_tcp_client, ietf_ssh_common, ietf_ssh_server, iana_ssh_encryption_algs, iana_ssh_key_exchange_algs, iana_ssh_mac_algs, iana_ssh_public_key_algs, iana_crypt_hash, - ietf_keystore, ietf_truststore, ietf_tls_common, ietf_tls_server, libnetconf2_netconf_server, NULL + ietf_keystore, ietf_truststore, ietf_tls_common, ietf_tls_server, iana_tls_cipher_suite_algs, + libnetconf2_netconf_server, NULL }; for (i = 0; module_names[i] != NULL; i++) { diff --git a/src/server_config.h b/src/server_config.h index ebebf180..2a97dca3 100644 --- a/src/server_config.h +++ b/src/server_config.h @@ -328,6 +328,41 @@ int nc_server_config_new_tls_client_ca(const struct ly_ctx *ctx, const char *end int nc_server_config_new_tls_ctn(const struct ly_ctx *ctx, const char *endpt_name, uint32_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name, struct lyd_node **config); +/** + * @brief Creates new YANG configuration data nodes for a TLS version. + * + * @param[in] ctx libyang context. + * @param[in] endpt_name Arbitrary identifier of the endpoint. + * If an endpoint with this identifier already exists, it's contents will be changed. + * @param[in] tls_version TLS version to be used. Call this multiple times to set + * the accepted versions of the TLS protocol and let the client and server negotiate + * the given version. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_new_tls_version(const struct ly_ctx *ctx, const char *endpt_name, + NC_TLS_VERSION tls_version, struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for a TLS cipher. + * + * @param[in] ctx libyang context. + * @param[in] endpt_name Arbitrary identifier of the endpoint. + * If an endpoint with this identifier already exists, it's contents will be changed. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @param[in] cipher_count Number of ciphers. + * @param[in] ... TLS ciphers. These ciphers MUST be in the format as listed in the + * iana-tls-cipher-suite-algs YANG model (lowercase and separated by dashes). Regardless + * of the TLS protocol version used, all of these ciphers will be tried and some of them + * might not be set (TLS handshake might fail then). For the list of supported ciphers see + * the OpenSSL documentation. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_new_tls_ciphers(const struct ly_ctx *ctx, const char *endpt_name, struct lyd_node **config, + uint16_t cipher_count, ...); + #endif /* NC_ENABLED_SSH_TLS */ #ifdef __cplusplus diff --git a/src/session.h b/src/session.h index 5f256ec2..52626e8a 100644 --- a/src/session.h +++ b/src/session.h @@ -58,6 +58,16 @@ typedef enum { NC_TLS_CTN_COMMON_NAME /**< common name as username */ } NC_TLS_CTN_MAPTYPE; +/** + * @brief Enumeration of TLS versions. + */ +typedef enum { + NC_TLS_VERSION_10 = 1, /**< TLS1.0 */ + NC_TLS_VERSION_11 = 2, /**< TLS1.1 */ + NC_TLS_VERSION_12 = 4, /**< TLS1.2 */ + NC_TLS_VERSION_13 = 8 /**< TLS1.3 */ +} NC_TLS_VERSION; + #endif /* NC_ENABLED_SSH_TLS */ /** diff --git a/src/session_p.h b/src/session_p.h index 62ce5971..4d8b058f 100644 --- a/src/session_p.h +++ b/src/session_p.h @@ -262,6 +262,9 @@ struct nc_server_tls_opts { struct nc_cert_grouping ca_certs; /**< Client certificate authorities */ struct nc_cert_grouping ee_certs; /**< Client end-entity certificates */ + unsigned int tls_versions; /**< TLS versions */ + char *ciphers; /**< TLS ciphers */ + struct nc_ctn *ctn; /**< Cert-to-name entries */ }; diff --git a/src/session_server_tls.c b/src/session_server_tls.c index e02456df..0af28883 100644 --- a/src/session_server_tls.c +++ b/src/session_server_tls.c @@ -1022,6 +1022,30 @@ nc_accept_tls_session(struct nc_session *session, struct nc_server_tls_opts *opt goto error; } + /* set TLS versions for the current SSL session */ + if (opts->tls_versions) { + if (!(opts->tls_versions & NC_TLS_VERSION_10)) { + SSL_set_options(session->ti.tls, SSL_OP_NO_TLSv1); + } + if (!(opts->tls_versions & NC_TLS_VERSION_11)) { + SSL_set_options(session->ti.tls, SSL_OP_NO_TLSv1_1); + } + if (!(opts->tls_versions & NC_TLS_VERSION_12)) { + SSL_set_options(session->ti.tls, SSL_OP_NO_TLSv1_2); + } + if (!(opts->tls_versions & NC_TLS_VERSION_13)) { + SSL_set_options(session->ti.tls, SSL_OP_NO_TLSv1_3); + } + } + + /* set TLS cipher suites */ + if (opts->ciphers) { + /* set for TLS1.2 and lower */ + SSL_set_cipher_list(session->ti.tls, opts->ciphers); + /* set for TLS1.3 */ + SSL_set_ciphersuites(session->ti.tls, opts->ciphers); + } + SSL_set_fd(session->ti.tls, sock); sock = -1; SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY); diff --git a/tests/test_tls.c b/tests/test_tls.c index bb3867e1..9b0789d0 100644 --- a/tests/test_tls.c +++ b/tests/test_tls.c @@ -153,11 +153,20 @@ setup_f(void **state) ret = nc_server_config_new_tls_client_ca(ctx, "endpt", "client_ca", TESTS_DIR "/data/serverca.pem", &tree); assert_int_equal(ret, 0); + /* create new cert-to-name */ ret = nc_server_config_new_tls_ctn(ctx, "endpt", 1, "04:85:6B:75:D1:1A:86:E0:D8:FE:5B:BD:72:F5:73:1D:07:EA:32:BF:09:11:21:6A:6E:23:78:8E:B6:D5:73:C3:2D", NC_TLS_CTN_SPECIFIED, "client", &tree); assert_int_equal(ret, 0); + /* limit TLS version to 1.3 */ + ret = nc_server_config_new_tls_version(ctx, "endpt", NC_TLS_VERSION_13, &tree); + assert_int_equal(ret, 0); + + /* set the TLS cipher */ + ret = nc_server_config_new_tls_ciphers(ctx, "endpt", &tree, 1, "tls-aes-128-ccm-sha256"); + assert_int_equal(ret, 0); + /* configure the server based on the data */ ret = nc_server_config_setup_diff(tree); assert_int_equal(ret, 0);