blob: 28c10789c60d954a83f1af6f7802218772f9cae9 [file] [log] [blame]
/**
* @file session_mbedtls.c
* @author Roman Janota <janota@cesnet.cz>
* @brief libnetconf2 - wrapped MbedTLS function calls for TLS/asymmetric cryptography support
*
* This file is a wrapper for MbedTLS function calls. The implementation is done
* in such a way that the original libnetconf2 code is not dependent on MbedTLS.
* This file is included in the build process only if MbedTLS is being used.
*
* @copyright
* Copyright (c) 2024 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <poll.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <curl/curl.h>
#include "compat.h"
#include "config.h"
#include "log_p.h"
#include "session.h"
#include "session_p.h"
#include "session_wrapper.h"
#include <mbedtls/base64.h>
#include <mbedtls/bignum.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/entropy.h>
#include <mbedtls/error.h>
#include <mbedtls/net_sockets.h>
#include <mbedtls/oid.h>
#include <mbedtls/pem.h>
#include <mbedtls/ssl.h>
#include <mbedtls/x509.h>
#include <mbedtls/x509_crl.h>
#include <mbedtls/x509_crt.h>
/* some mbedTLS functions may return 'high' and some 'low' level errors, try to handle both cases this way */
static const char *
nc_get_mbedtls_str_err(int err)
{
const char *err_str;
err_str = mbedtls_high_level_strerr(err);
if (err_str) {
return err_str;
}
err_str = mbedtls_low_level_strerr(err);
if (err_str) {
return err_str;
}
return "unknown error";
}
/**
* @brief Converts DN to a string.
*
* @param[in] dn Internal DN representation.
* @return DN string on success, NULL of fail.
*/
static char *
nc_server_tls_dn2str(const mbedtls_x509_name *dn)
{
char *str;
size_t len = 64;
int r;
str = malloc(len);
NC_CHECK_ERRMEM_RET(!str, NULL);
while ((r = mbedtls_x509_dn_gets(str, len, dn)) == MBEDTLS_ERR_X509_BUFFER_TOO_SMALL) {
len <<= 1;
str = nc_realloc(str, len);
NC_CHECK_ERRMEM_RET(!str, NULL);
}
if (r < 1) {
free(str);
ERR(NULL, "Failed to convert DN to string (%s).", nc_get_mbedtls_str_err(r));
return NULL;
}
return str;
}
/* creates a new rng context needed for PK operations and for ssl config */
static int
nc_tls_rng_new(mbedtls_ctr_drbg_context **ctr_drbg, mbedtls_entropy_context **entropy)
{
int rc;
*ctr_drbg = NULL;
*entropy = NULL;
*entropy = malloc(sizeof **entropy);
NC_CHECK_ERRMEM_GOTO(!*entropy, , fail);
*ctr_drbg = malloc(sizeof **ctr_drbg);
NC_CHECK_ERRMEM_GOTO(!*ctr_drbg, , fail);
mbedtls_entropy_init(*entropy);
mbedtls_ctr_drbg_init(*ctr_drbg);
rc = mbedtls_ctr_drbg_seed(*ctr_drbg, mbedtls_entropy_func, *entropy, NULL, 0);
if (rc) {
ERR(NULL, "Seeding ctr_drbg failed (%s).", nc_get_mbedtls_str_err(rc));
goto fail;
}
return 0;
fail:
mbedtls_ctr_drbg_free(*ctr_drbg);
free(*ctr_drbg);
if (*entropy) {
mbedtls_entropy_free(*entropy);
free(*entropy);
}
*ctr_drbg = NULL;
*entropy = NULL;
return 1;
}
static void
nc_tls_rng_destroy(mbedtls_ctr_drbg_context *ctr_drbg, mbedtls_entropy_context *entropy)
{
mbedtls_ctr_drbg_free(ctr_drbg);
free(ctr_drbg);
if (entropy) {
mbedtls_entropy_free(entropy);
free(entropy);
}
}
/* get verify err string, caller is responsible for freeing it, 256B should be more than enough */
static char *
nc_tls_get_verify_err_str(int err)
{
int ret;
char *err_buf = NULL;
err_buf = malloc(256);
NC_CHECK_ERRMEM_RET(!err_buf, NULL);
ret = mbedtls_x509_crt_verify_info(err_buf, 256, "", err);
if (ret < 0) {
free(err_buf);
return NULL;
}
/* strip the NL */
err_buf[ret - 1] = '\0';
return err_buf;
}
void *
nc_tls_session_new_wrap(void *tls_cfg)
{
int rc;
mbedtls_ssl_context *session;
session = malloc(sizeof *session);
NC_CHECK_ERRMEM_RET(!session, NULL);
mbedtls_ssl_init(session);
rc = mbedtls_ssl_setup(session, tls_cfg);
if (rc) {
ERR(NULL, "Setting up TLS session failed (%s).", nc_get_mbedtls_str_err(rc));
mbedtls_ssl_free(session);
free(session);
return NULL;
}
return session;
}
void
nc_tls_session_destroy_wrap(void *tls_session)
{
mbedtls_ssl_free(tls_session);
free(tls_session);
}
void *
nc_tls_config_new_wrap(int UNUSED(side))
{
mbedtls_ssl_config *tls_cfg;
tls_cfg = malloc(sizeof *tls_cfg);
NC_CHECK_ERRMEM_RET(!tls_cfg, NULL);
mbedtls_ssl_config_init(tls_cfg);
return tls_cfg;
}
void
nc_tls_config_destroy_wrap(void *tls_cfg)
{
if (!tls_cfg) {
return;
}
mbedtls_ssl_config_free(tls_cfg);
free(tls_cfg);
}
void *
nc_tls_cert_new_wrap(void)
{
mbedtls_x509_crt *cert;
cert = malloc(sizeof *cert);
NC_CHECK_ERRMEM_RET(!cert, NULL);
mbedtls_x509_crt_init(cert);
return cert;
}
void
nc_tls_cert_destroy_wrap(void *cert)
{
mbedtls_x509_crt_free(cert);
free(cert);
}
static void *
nc_tls_pkey_new_wrap(void)
{
mbedtls_pk_context *pkey;
pkey = malloc(sizeof *pkey);
NC_CHECK_ERRMEM_RET(!pkey, NULL);
mbedtls_pk_init(pkey);
return pkey;
}
void
nc_tls_privkey_destroy_wrap(void *pkey)
{
mbedtls_pk_free(pkey);
free(pkey);
}
void *
nc_tls_cert_store_new_wrap(void)
{
/* certificate is the same as a certificate store in MbedTLS */
return nc_tls_cert_new_wrap();
}
void
nc_tls_cert_store_destroy_wrap(void *cert_store)
{
/* certificate is the same as a certificate store in MbedTLS */
nc_tls_cert_destroy_wrap(cert_store);
}
void *
nc_tls_crl_store_new_wrap(void)
{
mbedtls_x509_crl *crl;
crl = malloc(sizeof *crl);
NC_CHECK_ERRMEM_RET(!crl, NULL);
mbedtls_x509_crl_init(crl);
return crl;
}
void
nc_tls_crl_store_destroy_wrap(void *crl_store)
{
mbedtls_x509_crl_free(crl_store);
free(crl_store);
}
void *
nc_tls_pem_to_cert_wrap(const char *cert_data)
{
int rc;
mbedtls_x509_crt *cert;
cert = nc_tls_cert_new_wrap();
if (!cert) {
return NULL;
}
rc = mbedtls_x509_crt_parse(cert, (const unsigned char *)cert_data, strlen(cert_data) + 1);
if (rc) {
ERR(NULL, "Parsing certificate data failed (%s).", nc_get_mbedtls_str_err(rc));
nc_tls_cert_destroy_wrap(cert);
return NULL;
}
return cert;
}
int
nc_tls_add_cert_to_store_wrap(void *cert, void *cert_store)
{
mbedtls_x509_crt *iter;
/* store is a linked list */
iter = cert_store;
while (iter->next) {
iter = iter->next;
}
iter->next = cert;
return 0;
}
void *
nc_tls_pem_to_privkey_wrap(const char *privkey_data)
{
int rc = 0;
mbedtls_pk_context *pkey = NULL;
mbedtls_ctr_drbg_context *ctr_drbg = NULL;
mbedtls_entropy_context *entropy = NULL;
rc = nc_tls_rng_new(&ctr_drbg, &entropy);
if (rc) {
goto cleanup;
}
pkey = nc_tls_pkey_new_wrap();
if (!pkey) {
rc = 1;
goto cleanup;
}
rc = mbedtls_pk_parse_key(pkey, (const unsigned char *)privkey_data, strlen(privkey_data) + 1, NULL, 0, mbedtls_ctr_drbg_random, ctr_drbg);
if (rc) {
ERR(NULL, "Parsing private key data failed (%s).", nc_get_mbedtls_str_err(rc));
goto cleanup;
}
cleanup:
if (rc) {
nc_tls_privkey_destroy_wrap(pkey);
pkey = NULL;
}
nc_tls_rng_destroy(ctr_drbg, entropy);
return pkey;
}
int
nc_tls_import_crl_path_wrap(const char *path, void *crl_store)
{
int rc;
rc = mbedtls_x509_crl_parse_file(crl_store, path);
if (rc) {
ERR(NULL, "Failed to import CRL from file \"%s\" (%s).", path, nc_get_mbedtls_str_err(rc));
return 1;
}
return 0;
}
int
nc_server_tls_add_crl_to_store_wrap(const unsigned char *crl_data, size_t size, void *crl_store)
{
int rc;
/* try DER first */
rc = mbedtls_x509_crl_parse_der(crl_store, crl_data, size);
if (!rc) {
/* success, it was DER */
return 0;
}
/* DER failed, try PEM */
rc = mbedtls_x509_crl_parse(crl_store, crl_data, size + 1);
if (!rc) {
/* success, it was PEM */
return 0;
}
/* failed to parse it */
ERR(NULL, "Reading downloaded CRL failed.");
return 1;
}
int
nc_server_tls_set_tls_versions_wrap(void *tls_cfg, unsigned int tls_versions)
{
if ((tls_versions & NC_TLS_VERSION_10) || ((tls_versions & NC_TLS_VERSION_11))) {
/* skip TLS versions 1.0 and 1.1 */
WRN(NULL, "mbedTLS does not support TLS1.0 and TLS1.1");
}
/* first set the minimum version */
if (tls_versions & NC_TLS_VERSION_12) {
mbedtls_ssl_conf_min_tls_version(tls_cfg, MBEDTLS_SSL_VERSION_TLS1_2);
} else if (tls_versions & NC_TLS_VERSION_13) {
mbedtls_ssl_conf_min_tls_version(tls_cfg, MBEDTLS_SSL_VERSION_TLS1_3);
}
/* then set the maximum version */
if (tls_versions & NC_TLS_VERSION_13) {
mbedtls_ssl_conf_max_tls_version(tls_cfg, MBEDTLS_SSL_VERSION_TLS1_3);
} else if (tls_versions & NC_TLS_VERSION_12) {
mbedtls_ssl_conf_max_tls_version(tls_cfg, MBEDTLS_SSL_VERSION_TLS1_2);
}
return 0;
}
static int
nc_server_tls_verify_cb(void *cb_data, mbedtls_x509_crt *cert, int depth, uint32_t *flags)
{
int ret = 0;
struct nc_tls_verify_cb_data *data = cb_data;
char *err;
if (!*flags) {
/* in-built verification was successful */
ret = nc_server_tls_verify_cert(cert, depth, 0, data);
} else {
/* in-built verification failed, but the client still may be authenticated if:
* 1) the peer cert matches any configured end-entity cert
* 2) the peer cert has a valid chain of trust to any configured certificate authority cert
* otherwise just continue until we reach the peer cert (depth = 0)
*/
if ((depth == 0) && (*flags == MBEDTLS_X509_BADCERT_NOT_TRUSTED)) {
/* not trusted self-signed peer certificate, case 1) */
ret = nc_server_tls_verify_cert(cert, depth, 1, data);
if (!ret) {
*flags &= ~MBEDTLS_X509_BADCERT_NOT_TRUSTED;
}
} else if (*flags == MBEDTLS_X509_BADCERT_MISSING) {
/* full chain of trust is invalid, but it may be valid partially, case 2) */
ret = nc_server_tls_verify_cert(cert, depth, 1, data);
if (!ret) {
*flags &= ~MBEDTLS_X509_BADCERT_MISSING;
}
} else {
err = nc_tls_get_verify_err_str(*flags);
ERR(data->session, "Cert verify: fail (%s).", err);
free(err);
ret = 1;
}
}
if (ret == -1) {
/* fatal error */
return MBEDTLS_ERR_X509_ALLOC_FAILED;
} else if (!ret) {
/* success */
return 0;
} else {
if (depth > 0) {
/* chain verify failed, but peer cert can still match */
return 0;
} else {
/* failed to verify peer cert, but return 0 so that we can propagate the error via the flags */
if (!*flags) {
*flags |= MBEDTLS_X509_BADCERT_OTHER;
}
return 0;
}
}
}
void
nc_server_tls_set_verify_wrap(void *tls_cfg, struct nc_tls_verify_cb_data *cb_data)
{
mbedtls_ssl_conf_authmode(tls_cfg, MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_verify(tls_cfg, nc_server_tls_verify_cb, cb_data);
}
void
nc_client_tls_set_verify_wrap(void *tls_cfg)
{
mbedtls_ssl_conf_authmode(tls_cfg, MBEDTLS_SSL_VERIFY_REQUIRED);
}
char *
nc_server_tls_get_subject_wrap(void *cert)
{
return nc_server_tls_dn2str(&(((mbedtls_x509_crt *)cert)->subject));
}
char *
nc_server_tls_get_issuer_wrap(void *cert)
{
return nc_server_tls_dn2str(&(((mbedtls_x509_crt *)cert)->issuer));
}
void *
nc_tls_get_sans_wrap(void *cert)
{
return &(((mbedtls_x509_crt *)cert)->subject_alt_names);
}
void
nc_tls_sans_destroy_wrap(void *UNUSED(sans))
{
return;
}
int
nc_tls_get_num_sans_wrap(void *sans)
{
mbedtls_x509_sequence *iter;
int n = 0;
/* sans are a linked list */
iter = sans;
while (iter) {
++n;
iter = iter->next;
}
return n;
}
int
nc_tls_get_san_value_type_wrap(void *sans, int idx, char **san_value, NC_TLS_CTN_MAPTYPE *san_type)
{
int i, rc, ret = 0;
mbedtls_x509_sequence *iter;
mbedtls_x509_subject_alternative_name san = {0};
const mbedtls_x509_buf *ip;
*san_value = NULL;
*san_type = NC_TLS_CTN_UNKNOWN;
/* find the SAN */
iter = sans;
for (i = 0; i < idx; i++) {
iter = iter->next;
}
/* parse it */
rc = mbedtls_x509_parse_subject_alt_name(&iter->buf, &san);
if (rc && (rc != MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE)) {
return -1;
}
/* get its type and value */
switch (san.type) {
case MBEDTLS_X509_SAN_DNS_NAME:
*san_type = NC_TLS_CTN_SAN_DNS_NAME;
*san_value = strndup((const char *)san.san.unstructured_name.p, san.san.unstructured_name.len);
NC_CHECK_ERRMEM_GOTO(!*san_value, ret = -1, cleanup);
break;
case MBEDTLS_X509_SAN_RFC822_NAME:
*san_type = NC_TLS_CTN_SAN_RFC822_NAME;
*san_value = strndup((const char *)san.san.unstructured_name.p, san.san.unstructured_name.len);
NC_CHECK_ERRMEM_GOTO(!*san_value, ret = -1, cleanup);
break;
case MBEDTLS_X509_SAN_IP_ADDRESS:
*san_type = NC_TLS_CTN_SAN_IP_ADDRESS;
ip = &san.san.unstructured_name;
if (ip->len == 4) {
rc = asprintf(san_value, "%d.%d.%d.%d", ip->p[0], ip->p[1], ip->p[2], ip->p[3]) == -1;
NC_CHECK_ERRMEM_GOTO(rc == -1, ret = -1, cleanup);
} else if (ip->len == 16) {
rc = asprintf(san_value, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
ip->p[0], ip->p[1], ip->p[2], ip->p[3], ip->p[4], ip->p[5],
ip->p[6], ip->p[7], ip->p[8], ip->p[9], ip->p[10], ip->p[11],
ip->p[12], ip->p[13], ip->p[14], ip->p[15]);
NC_CHECK_ERRMEM_GOTO(rc == -1, ret = -1, cleanup);
} else {
WRN(NULL, "SAN IP address in an unknown format (length is %d).", ip->len);
ret = 1;
}
break;
default:
/* we dont care about other types */
*san_type = NC_TLS_CTN_UNKNOWN;
ret = 1;
break;
}
cleanup:
mbedtls_x509_free_subject_alt_name(&san);
return ret;
}
int
nc_server_tls_certs_match_wrap(void *cert1, void *cert2)
{
mbedtls_x509_crt *c1 = cert1;
mbedtls_x509_crt *c2 = cert2;
if (!c1 || !c2) {
return 0;
}
/* compare raw DER encoded data */
if (!c1->raw.p || !c2->raw.p || (c1->raw.len != c2->raw.len) ||
memcmp(c1->raw.p, c2->raw.p, c1->raw.len)) {
return 0;
}
return 1;
}
int
nc_server_tls_md5_wrap(void *cert, unsigned char *buf)
{
int rc;
mbedtls_x509_crt *c = cert;
rc = mbedtls_md5(c->raw.p, c->raw.len, buf);
if (rc) {
ERR(NULL, "Calculating MD5 digest failed (%s).", nc_get_mbedtls_str_err(rc));
return 1;
}
return 0;
}
int
nc_server_tls_sha1_wrap(void *cert, unsigned char *buf)
{
int rc;
mbedtls_x509_crt *c = cert;
rc = mbedtls_sha1(c->raw.p, c->raw.len, buf);
if (rc) {
ERR(NULL, "Calculating SHA-1 digest failed (%s).", nc_get_mbedtls_str_err(rc));
return 1;
}
return 0;
}
int
nc_server_tls_sha224_wrap(void *cert, unsigned char *buf)
{
int rc;
mbedtls_x509_crt *c = cert;
rc = mbedtls_sha256(c->raw.p, c->raw.len, buf, 1);
if (rc) {
ERR(NULL, "Calculating SHA-224 digest failed (%s).", nc_get_mbedtls_str_err(rc));
return 1;
}
return 0;
}
int
nc_server_tls_sha256_wrap(void *cert, unsigned char *buf)
{
int rc;
mbedtls_x509_crt *c = cert;
rc = mbedtls_sha256(c->raw.p, c->raw.len, buf, 0);
if (rc) {
ERR(NULL, "Calculating SHA-256 digest failed (%s).", nc_get_mbedtls_str_err(rc));
return 1;
}
return 0;
}
int
nc_server_tls_sha384_wrap(void *cert, unsigned char *buf)
{
int rc;
mbedtls_x509_crt *c = cert;
rc = mbedtls_sha512(c->raw.p, c->raw.len, buf, 1);
if (rc) {
ERR(NULL, "Calculating SHA-384 digest failed (%s).", nc_get_mbedtls_str_err(rc));
return 1;
}
return 0;
}
int
nc_server_tls_sha512_wrap(void *cert, unsigned char *buf)
{
int rc;
mbedtls_x509_crt *c = cert;
rc = mbedtls_sha512(c->raw.p, c->raw.len, buf, 0);
if (rc) {
ERR(NULL, "Calculating SHA-512 digest failed (%s).", nc_get_mbedtls_str_err(rc));
return 1;
}
return 0;
}
static int
nc_server_tls_send(void *ctx, const unsigned char *buf, size_t len)
{
int sock, ret;
NC_CHECK_ARG_RET(NULL, ctx, MBEDTLS_ERR_NET_INVALID_CONTEXT);
sock = *(int *)ctx;
ret = send(sock, buf, len, MSG_NOSIGNAL);
if (ret < 0) {
if ((errno == EWOULDBLOCK) || (errno = EAGAIN) || (errno == EINTR)) {
return MBEDTLS_ERR_SSL_WANT_WRITE;
} else if ((errno == EPIPE) || (errno == ECONNRESET)) {
return MBEDTLS_ERR_NET_CONN_RESET;
} else {
return MBEDTLS_ERR_NET_SEND_FAILED;
}
}
return ret;
}
static int
nc_server_tls_recv(void *ctx, unsigned char *buf, size_t len)
{
int sock, ret;
NC_CHECK_ARG_RET(NULL, ctx, MBEDTLS_ERR_NET_INVALID_CONTEXT);
sock = *(int *)ctx;
ret = recv(sock, buf, len, 0);
if (ret < 0) {
if ((errno == EWOULDBLOCK) || (errno = EAGAIN) || (errno == EINTR)) {
return MBEDTLS_ERR_SSL_WANT_READ;
} else if ((errno == EPIPE) || (errno == ECONNRESET)) {
return MBEDTLS_ERR_NET_CONN_RESET;
} else {
return MBEDTLS_ERR_NET_RECV_FAILED;
}
}
return ret;
}
void
nc_server_tls_set_fd_wrap(void *tls_session, int UNUSED(sock), struct nc_tls_ctx *tls_ctx)
{
/* mbedtls sets a pointer to the sock, which is stored in tls_ctx */
mbedtls_ssl_set_bio(tls_session, tls_ctx->sock, nc_server_tls_send, nc_server_tls_recv, NULL);
}
int
nc_server_tls_handshake_step_wrap(void *tls_session)
{
int rc = 0;
rc = mbedtls_ssl_handshake(tls_session);
if (!rc) {
return 1;
} else if ((rc == MBEDTLS_ERR_SSL_WANT_READ) || (rc == MBEDTLS_ERR_SSL_WANT_WRITE)) {
return 0;
} else {
return rc;
}
}
int
nc_client_tls_handshake_step_wrap(void *tls_session, int sock)
{
int rc = 0;
struct pollfd pfd = {sock, 0, 0};
rc = mbedtls_ssl_handshake(tls_session);
if (!rc) {
return 1;
} else if ((rc == MBEDTLS_ERR_SSL_WANT_READ) || (rc == MBEDTLS_ERR_SSL_WANT_WRITE)) {
/* check for EPIPE */
if (poll(&pfd, 1, 0) < 0) {
return -1;
} else {
if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
return -1;
} else {
return 0;
}
}
} else {
return rc;
}
}
void
nc_tls_ctx_destroy_wrap(struct nc_tls_ctx *tls_ctx)
{
nc_tls_rng_destroy(tls_ctx->ctr_drbg, tls_ctx->entropy);
nc_tls_cert_destroy_wrap(tls_ctx->cert);
nc_tls_privkey_destroy_wrap(tls_ctx->pkey);
nc_tls_cert_store_destroy_wrap(tls_ctx->cert_store);
nc_tls_crl_store_destroy_wrap(tls_ctx->crl_store);
free(tls_ctx->sock);
}
void *
nc_tls_import_privkey_file_wrap(const char *privkey_path)
{
int rc;
mbedtls_pk_context *pkey;
mbedtls_ctr_drbg_context *ctr_drbg;
mbedtls_entropy_context *entropy;
if (nc_tls_rng_new(&ctr_drbg, &entropy)) {
return NULL;
}
pkey = nc_tls_pkey_new_wrap();
if (!pkey) {
nc_tls_rng_destroy(ctr_drbg, entropy);
return NULL;
}
rc = mbedtls_pk_parse_keyfile(pkey, privkey_path, NULL, mbedtls_ctr_drbg_random, ctr_drbg);
nc_tls_rng_destroy(ctr_drbg, entropy);
if (rc) {
ERR(NULL, "Parsing private key from file \"%s\" failed (%s).", privkey_path, nc_get_mbedtls_str_err(rc));
nc_tls_privkey_destroy_wrap(pkey);
return NULL;
}
return pkey;
}
int
nc_client_tls_load_cert_key_wrap(const char *cert_path, const char *key_path, void **cert, void **pkey)
{
int ret = 0;
mbedtls_x509_crt *c;
mbedtls_pk_context *pk;
NC_CHECK_ARG_RET(NULL, cert_path, key_path, cert, pkey, 1);
c = nc_tls_cert_new_wrap();
if (!c) {
return 1;
}
ret = mbedtls_x509_crt_parse_file(c, cert_path);
if (ret) {
ERR(NULL, "Parsing certificate from file \"%s\" failed (%s).", cert_path, nc_get_mbedtls_str_err(ret));
goto cleanup;
}
pk = nc_tls_import_privkey_file_wrap(key_path);
if (!pk) {
ret = 1;
goto cleanup;
}
*cert = c;
c = NULL;
*pkey = pk;
cleanup:
nc_tls_cert_destroy_wrap(c);
return ret;
}
int
nc_client_tls_load_trusted_certs_wrap(void *cert_store, const char *file_path, const char *dir_path)
{
int rc;
if (file_path && ((rc = mbedtls_x509_crt_parse_file(cert_store, file_path)) < 0)) {
ERR(NULL, "Loading CA certificate from file \"%s\" failed (%s).", file_path, nc_get_mbedtls_str_err(rc));
return 1;
}
if (dir_path && ((rc = mbedtls_x509_crt_parse_path(cert_store, dir_path)) < 0)) {
ERR(NULL, "Loading CA certificate from directory \"%s\" failed (%s).", dir_path, nc_get_mbedtls_str_err(rc));
return 1;
}
return 0;
}
int
nc_client_tls_load_crl_wrap(void *crl_store, const char *file_path, const char *dir_path)
{
int rc, ret = 0;
DIR *dir = NULL;
struct dirent *entry;
struct stat st = {0};
char *path = NULL;
if (file_path && (rc = mbedtls_x509_crl_parse_file(crl_store, file_path))) {
ERR(NULL, "Loading CRL from file \"%s\" failed (%s).", file_path, nc_get_mbedtls_str_err(rc));
return 1;
}
if (dir_path) {
/* parse the CRLs in the directory one by one */
dir = opendir(dir_path);
if (!dir) {
ERR(NULL, "Failed to open directory \"%s\" (%s).", dir_path, strerror(errno));
return 1;
}
while ((entry = readdir(dir))) {
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
/* skip current and parent directory */
continue;
}
rc = asprintf(&path, "%s/%s", dir_path, entry->d_name);
NC_CHECK_ERRMEM_GOTO(rc == -1, ret = 1; path = NULL, cleanup);
if (stat(path, &st) == -1) {
if (errno == ENOENT) {
/* broken symbolic link, ignore */
free(path);
path = NULL;
continue;
} else {
ERR(NULL, "Failed to get information about \"%s\" (%s).", path, strerror(errno));
ret = 1;
goto cleanup;
}
}
if (!S_ISREG(st.st_mode)) {
/* not a regular file, ignore */
free(path);
path = NULL;
continue;
}
rc = mbedtls_x509_crl_parse_file(crl_store, path);
if (rc) {
WRN(NULL, "Loading CRL from file \"%s\" failed (%s), skipping.", path, nc_get_mbedtls_str_err(rc));
}
free(path);
path = NULL;
}
}
cleanup:
free(path);
if (dir) {
closedir(dir);
}
return ret;
}
int
nc_client_tls_set_hostname_wrap(void *tls_session, const char *hostname)
{
int rc;
rc = mbedtls_ssl_set_hostname(tls_session, hostname);
if (rc) {
ERR(NULL, "Setting hostname failed (%s).", nc_get_mbedtls_str_err(rc));
return 1;
}
return 0;
}
int
nc_tls_init_ctx_wrap(int sock, void *cert, void *pkey, void *cert_store, void *crl_store, struct nc_tls_ctx *tls_ctx)
{
/* setup rng */
if (nc_tls_rng_new(&tls_ctx->ctr_drbg, &tls_ctx->entropy)) {
return 1;
}
/* fill the context */
tls_ctx->sock = malloc(sizeof *tls_ctx->sock);
NC_CHECK_ERRMEM_RET(!tls_ctx->sock, 1);
*tls_ctx->sock = sock;
tls_ctx->cert = cert;
tls_ctx->pkey = pkey;
tls_ctx->cert_store = cert_store;
tls_ctx->crl_store = crl_store;
return 0;
}
int
nc_tls_setup_config_from_ctx_wrap(struct nc_tls_ctx *tls_ctx, int side, void *tls_cfg)
{
int rc;
/* set default config data */
if (side == NC_SERVER) {
rc = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
} else {
rc = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
}
if (rc) {
ERR(NULL, "Setting default TLS config failed (%s).", nc_get_mbedtls_str_err(rc));
return 1;
}
/* set config's rng */
mbedtls_ssl_conf_rng(tls_cfg, mbedtls_ctr_drbg_random, tls_ctx->ctr_drbg);
/* set config's cert and key */
mbedtls_ssl_conf_own_cert(tls_cfg, tls_ctx->cert, tls_ctx->pkey);
/* set config's CA and CRL cert store */
mbedtls_ssl_conf_ca_chain(tls_cfg, tls_ctx->cert_store, tls_ctx->crl_store);
return 0;
}
uint32_t
nc_tls_get_verify_result_wrap(void *tls_session)
{
return mbedtls_ssl_get_verify_result(tls_session);
}
char *
nc_tls_verify_error_string_wrap(uint32_t err_code)
{
return nc_tls_get_verify_err_str(err_code);
}
void
nc_client_tls_print_connect_err_wrap(int connect_ret, const char *peername, void *UNUSED(tls_session))
{
const char *err = nc_get_mbedtls_str_err(connect_ret);
if (err) {
ERR(NULL, "TLS connection to \"%s\" failed (%s).", peername, err);
} else {
ERR(NULL, "TLS connection to \"%s\" failed.", peername);
}
}
void
nc_server_tls_print_accept_err_wrap(int accept_ret, void *UNUSED(tls_session))
{
const char *err = nc_get_mbedtls_str_err(accept_ret);
if (err) {
ERR(NULL, "TLS accept failed (%s).", err);
} else {
ERR(NULL, "TLS accept failed.");
}
}
int
nc_tls_is_der_subpubkey_wrap(unsigned char *der, long len)
{
int ret;
mbedtls_pk_context *pkey;
pkey = nc_tls_pkey_new_wrap();
if (!pkey) {
return -1;
}
ret = mbedtls_pk_parse_subpubkey(&der, der + len, pkey);
nc_tls_privkey_destroy_wrap(pkey);
if (!ret) {
/* success */
return 1;
} else {
/* fail */
return 0;
}
}
int
nc_base64_decode_wrap(const char *base64, unsigned char **bin)
{
size_t size;
int rc;
/* get the size of the decoded data */
rc = mbedtls_base64_decode(NULL, 0, &size, (const unsigned char *)base64, strlen(base64));
if (rc != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) {
ERR(NULL, "Base64 decoding failed (%s).", nc_get_mbedtls_str_err(rc));
return -1;
}
*bin = malloc(size);
NC_CHECK_ERRMEM_RET(!*bin, -1);
/* decode */
rc = mbedtls_base64_decode(*bin, size, &size, (const unsigned char *)base64, strlen(base64));
if (rc) {
ERR(NULL, "Base64 decoding failed (%s).", nc_get_mbedtls_str_err(rc));
free(*bin);
*bin = NULL;
return -1;
}
return size;
}
int
nc_base64_encode_wrap(const unsigned char *bin, size_t len, char **base64)
{
size_t size;
int rc;
rc = mbedtls_base64_encode(NULL, 0, &size, bin, len);
if (rc != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) {
ERR(NULL, "Base64 encoding failed (%s).", nc_get_mbedtls_str_err(rc));
return -1;
}
*base64 = malloc(size);
NC_CHECK_ERRMEM_RET(!*base64, -1);
rc = mbedtls_base64_encode((unsigned char *)*base64, size, &size, bin, len);
if (rc) {
ERR(NULL, "Base64 encoding failed (%s).", nc_get_mbedtls_str_err(rc));
free(*base64);
*base64 = NULL;
return -1;
}
return 0;
}
int
nc_tls_read_wrap(struct nc_session *session, unsigned char *buf, size_t size)
{
int rc;
mbedtls_ssl_context *tls_session = session->ti.tls.session;
rc = mbedtls_ssl_read(tls_session, buf, size);
if (rc <= 0) {
switch (rc) {
case MBEDTLS_ERR_SSL_WANT_READ:
case MBEDTLS_ERR_SSL_WANT_WRITE:
rc = 0;
break;
case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
ERR(session, "Communication socket unexpectedly closed (MbedTLS).");
session->status = NC_STATUS_INVALID;
session->term_reason = NC_SESSION_TERM_DROPPED;
rc = -1;
break;
default:
ERR(session, "TLS communication error occurred (%s).", nc_get_mbedtls_str_err(rc));
session->status = NC_STATUS_INVALID;
session->term_reason = NC_SESSION_TERM_OTHER;
rc = -1;
break;
}
}
return rc;
}
int
nc_tls_write_wrap(struct nc_session *session, const unsigned char *buf, size_t size)
{
int rc = 0;
mbedtls_ssl_context *tls_session = session->ti.tls.session;
rc = mbedtls_ssl_write(tls_session, buf, size);
if (rc < 0) {
switch (rc) {
case MBEDTLS_ERR_SSL_WANT_READ:
case MBEDTLS_ERR_SSL_WANT_WRITE:
rc = 0;
break;
case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
ERR(session, "TLS connection was properly closed.");
rc = -1;
break;
default:
ERR(session, "TLS communication error occurred (%s).", nc_get_mbedtls_str_err(rc));
rc = -1;
break;
}
}
return rc;
}
int
nc_tls_get_num_pending_bytes_wrap(void *tls_session)
{
return mbedtls_ssl_get_bytes_avail(tls_session);
}
int
nc_tls_get_fd_wrap(const struct nc_session *session)
{
return session->ti.tls.ctx.sock ? *session->ti.tls.ctx.sock : -1;
}
void
nc_tls_close_notify_wrap(void *tls_session)
{
int rc;
while ((rc = mbedtls_ssl_close_notify(tls_session))) {
if ((rc != MBEDTLS_ERR_SSL_WANT_READ) && (rc != MBEDTLS_ERR_SSL_WANT_WRITE)) {
/* some error occurred */
ERR(NULL, "Sending TLS close notify failed (%s).", nc_get_mbedtls_str_err(rc));
return;
}
}
}
void *
nc_tls_import_cert_file_wrap(const char *cert_path)
{
int rc;
mbedtls_x509_crt *c;
c = nc_tls_cert_new_wrap();
if (!c) {
return NULL;
}
rc = mbedtls_x509_crt_parse_file(c, cert_path);
if (rc) {
ERR(NULL, "Parsing certificate from file \"%s\" failed (%s).", cert_path, nc_get_mbedtls_str_err(rc));
nc_tls_cert_destroy_wrap(c);
return NULL;
}
return c;
}
char *
nc_tls_export_privkey_pem_wrap(void *pkey)
{
int rc;
char *pem;
size_t size = 128;
pem = malloc(size);
NC_CHECK_ERRMEM_RET(!pem, NULL);
while ((rc = mbedtls_pk_write_key_pem(pkey, (unsigned char *)pem, size)) == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) {
size <<= 1;
pem = nc_realloc(pem, size);
NC_CHECK_ERRMEM_RET(!pem, NULL);
}
if (rc < 0) {
ERR(NULL, "Exporting private key to PEM format failed (%s).", nc_get_mbedtls_str_err(rc));
free(pem);
return NULL;
}
return pem;
}
char *
nc_tls_export_cert_pem_wrap(void *cert)
{
char *b64 = NULL, *pem = NULL;
/* encode the certificate */
if (nc_base64_encode_wrap(((mbedtls_x509_crt *)cert)->raw.p, ((mbedtls_x509_crt *)cert)->raw.len, &b64)) {
goto cleanup;
}
if (asprintf(&pem, "-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----\n", b64) == -1) {
ERRMEM;
pem = NULL;
goto cleanup;
}
cleanup:
free(b64);
return pem;
}
char *
nc_tls_export_pubkey_pem_wrap(void *pkey)
{
int rc;
char *pem;
size_t size = 128;
pem = malloc(size);
NC_CHECK_ERRMEM_RET(!pem, NULL);
while ((rc = mbedtls_pk_write_pubkey_pem(pkey, (unsigned char *)pem, size)) == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) {
size <<= 1;
pem = nc_realloc(pem, size);
NC_CHECK_ERRMEM_RET(!pem, NULL);
}
if (rc < 0) {
ERR(NULL, "Exporting public key to PEM format failed (%s).", nc_get_mbedtls_str_err(rc));
free(pem);
return NULL;
}
return pem;
}
int
nc_tls_privkey_is_rsa_wrap(void *pkey)
{
return mbedtls_pk_get_type(pkey) == MBEDTLS_PK_RSA;
}
int
nc_tls_get_rsa_pubkey_params_wrap(void *pkey, void **e, void **n)
{
int rc;
mbedtls_mpi *exp = NULL, *mod = NULL;
exp = malloc(sizeof *exp);
mod = malloc(sizeof *mod);
if (!exp || !mod) {
ERRMEM;
goto fail;
}
mbedtls_mpi_init(exp);
mbedtls_mpi_init(mod);
if ((rc = mbedtls_rsa_export(mbedtls_pk_rsa(*(mbedtls_pk_context *)pkey), mod, NULL, NULL, NULL, exp))) {
ERR(NULL, "Failed to export RSA public key parameters (%s).", nc_get_mbedtls_str_err(rc));
goto fail;
}
*e = exp;
*n = mod;
return 0;
fail:
mbedtls_mpi_free(exp);
mbedtls_mpi_free(mod);
free(exp);
free(mod);
return 1;
}
void
nc_tls_destroy_mpi_wrap(void *mpi)
{
mbedtls_mpi_free(mpi);
free(mpi);
}
int
nc_tls_privkey_is_ec_wrap(void *pkey)
{
return mbedtls_pk_get_type(pkey) == MBEDTLS_PK_ECKEY;
}
char *
nc_tls_get_ec_group_wrap(void *pkey)
{
const mbedtls_ecp_curve_info *curve_info;
mbedtls_ecp_group_id group_id;
mbedtls_ecp_keypair *ec;
/* get the group ID from the EC key */
ec = mbedtls_pk_ec(*(mbedtls_pk_context *)pkey);
group_id = ec->private_grp.id;
/* get the group name based on the id */
curve_info = mbedtls_ecp_curve_info_from_grp_id(group_id);
return strdup(curve_info->name);
}
int
nc_tls_get_ec_pubkey_params_wrap(void *pkey, void **q, void **q_grp)
{
int ret;
mbedtls_ecp_group *grp = NULL;
mbedtls_ecp_point *p = NULL;
mbedtls_mpi *d = NULL;
/* init group, mpi and point */
grp = malloc(sizeof *grp);
d = malloc(sizeof *d);
p = malloc(sizeof *p);
if (!grp || !p || !d) {
ERRMEM;
ret = 1;
goto cleanup;
}
mbedtls_ecp_group_init(grp);
mbedtls_mpi_init(d);
mbedtls_ecp_point_init(p);
/* get the group and public key */
ret = mbedtls_ecp_export(mbedtls_pk_ec(*(mbedtls_pk_context *)pkey), grp, d, p);
if (ret) {
ERR(NULL, "Failed to export EC public key parameters (%s).", nc_get_mbedtls_str_err(ret));
ret = 1;
goto cleanup;
}
*q_grp = grp;
grp = NULL;
*q = p;
p = NULL;
cleanup:
mbedtls_ecp_group_free(grp);
free(grp);
mbedtls_mpi_free(d);
free(d);
mbedtls_ecp_point_free(p);
free(p);
return ret;
}
int
nc_tls_ec_point_to_bin_wrap(void *q, void *q_grp, unsigned char **bin, int *bin_len)
{
int rc;
unsigned char *buf;
size_t buf_len = 32, out_len;
buf = malloc(buf_len);
NC_CHECK_ERRMEM_RET(!buf, 1);
while ((rc = (mbedtls_ecp_point_write_binary(q_grp, q, MBEDTLS_ECP_PF_COMPRESSED, &out_len, buf, buf_len)))) {
if (rc != MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL) {
break;
}
buf_len <<= 1;
buf = nc_realloc(buf, buf_len);
NC_CHECK_ERRMEM_RET(!buf, 1);
}
if (rc) {
ERR(NULL, "Failed to write EC public key binary (%s).", nc_get_mbedtls_str_err(rc));
free(buf);
return 1;
}
*bin = buf;
*bin_len = out_len;
return 0;
}
void
nc_tls_ec_point_destroy_wrap(void *p)
{
mbedtls_ecp_point_free(p);
free(p);
}
void
nc_tls_ec_group_destroy_wrap(void *grp)
{
mbedtls_ecp_group_free(grp);
free(grp);
}
int
nc_tls_mpi2bin_wrap(void *mpi, unsigned char **bin, int *bin_len)
{
int rc;
unsigned char *buf;
int buf_len;
buf_len = mbedtls_mpi_size(mpi);
buf = malloc(buf_len);
NC_CHECK_ERRMEM_RET(!buf, 1);
rc = mbedtls_mpi_write_binary(mpi, buf, buf_len);
if (rc) {
ERR(NULL, "Failed to convert MPI to binary (%s).", nc_get_mbedtls_str_err(rc));
free(buf);
return 1;
}
*bin = buf;
*bin_len = buf_len;
return 0;
}
void *
nc_tls_import_pubkey_file_wrap(const char *pubkey_path)
{
int rc = 0;
mbedtls_pk_context *pk = NULL;
pk = nc_tls_pkey_new_wrap();
if (!pk) {
return NULL;
}
rc = mbedtls_pk_parse_public_keyfile(pk, pubkey_path);
if (rc) {
ERR(NULL, "Parsing public key from file \"%s\" failed (%s).", pubkey_path, nc_get_mbedtls_str_err(rc));
nc_tls_privkey_destroy_wrap(pk);
return NULL;
}
return pk;
}
int
nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *uri_count)
{
int ret = 0;
mbedtls_x509_crt *cert;
unsigned char *p, *end_v3_ext, *end_ext, *end_ext_octet, *end_crl_dist_points;
size_t len;
mbedtls_x509_buf ext_oid = {0};
int is_critical = 0;
mbedtls_x509_sequence general_names = {0};
mbedtls_x509_sequence *iter = NULL;
mbedtls_x509_subject_alternative_name san = {0};
void *tmp;
NC_CHECK_ARG_RET(NULL, cert_store, uris, uri_count, 1);
*uris = NULL;
*uri_count = 0;
/* iterate over all the CAs */
cert = cert_store;
while (cert) {
if (!cert->v3_ext.len) {
/* no extensions, skip this cert */
cert = cert->next;
continue;
}
/* go over all the extensions and try to find the CRL distribution points */
p = cert->v3_ext.p;
end_v3_ext = p + cert->v3_ext.len;
/*
* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
*/
ret = mbedtls_asn1_get_tag(&p, end_v3_ext, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
if (ret) {
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
goto cleanup;
}
while (p < end_v3_ext) {
/*
* Extension ::= SEQUENCE {
* extnID OBJECT IDENTIFIER,
* critical BOOLEAN DEFAULT FALSE,
* extnValue OCTET STRING }
*/
ret = mbedtls_asn1_get_tag(&p, end_v3_ext, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
if (ret) {
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
goto cleanup;
}
end_ext = p + len;
/* parse extnID */
ret = mbedtls_asn1_get_tag(&p, end_ext, &ext_oid.len, MBEDTLS_ASN1_OID);
if (ret) {
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
goto cleanup;
}
ext_oid.tag = MBEDTLS_ASN1_OID;
ext_oid.p = p;
if (memcmp(ext_oid.p, MBEDTLS_OID_CRL_DISTRIBUTION_POINTS, ext_oid.len)) {
/* not the extension we are looking for */
p = end_ext;
continue;
}
p += ext_oid.len;
/* parse optional critical */
ret = mbedtls_asn1_get_bool(&p, end_ext, &is_critical);
if (ret && (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG)) {
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
goto cleanup;
}
/* parse extnValue */
ret = mbedtls_asn1_get_tag(&p, end_ext, &len, MBEDTLS_ASN1_OCTET_STRING);
if (ret) {
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
goto cleanup;
}
end_ext_octet = p + len;
/*
* parse extnValue, that is CRLDistributionPoints
*
* CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
*/
ret = mbedtls_asn1_get_tag(&p, end_ext_octet, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
if (ret) {
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
goto cleanup;
}
if (p + len != end_ext_octet) {
/* length mismatch */
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
goto cleanup;
} else if (!len) {
/* empty sequence, but size is 1..max */
ERR(NULL, "Failed to parse CRL distribution points extension (empty sequence).");
goto cleanup;
}
end_crl_dist_points = p + len;
while (p < end_crl_dist_points) {
/*
* DistributionPoint ::= SEQUENCE {
* distributionPoint [0] DistributionPointName OPTIONAL,
* reasons [1] ReasonFlags OPTIONAL,
* cRLIssuer [2] GeneralNames OPTIONAL }
*/
ret = mbedtls_asn1_get_tag(&p, end_ext_octet, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
if (ret) {
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
goto cleanup;
}
if (!len) {
/* empty sequence */
continue;
}
/* parse distributionPoint */
ret = mbedtls_asn1_get_tag(&p, end_ext_octet, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0);
if (!ret) {
/*
* DistributionPointName ::= CHOICE {
* fullName [0] GeneralNames,
* nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
*/
ret = mbedtls_asn1_get_tag(&p, end_ext_octet, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0);
if (ret) {
if ((ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) && (*p == (MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 1))) {
/* it's nameRelativeToCRLIssuer, but we don't support it */
ERR(NULL, "Failed to parse CRL distribution points extension (nameRelativeToCRLIssuer not yet supported).");
goto cleanup;
} else {
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
goto cleanup;
}
}
/* parse GeneralNames, but thankfully there is an api for this */
ret = mbedtls_x509_get_subject_alt_name_ext(&p, p + len, &general_names);
if (ret) {
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
goto cleanup;
}
/* iterate over all the GeneralNames */
iter = &general_names;
while (iter) {
ret = mbedtls_x509_parse_subject_alt_name(&iter->buf, &san);
if (ret && (ret != MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE)) {
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
goto cleanup;
}
if (san.type == MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER) {
/* found an URI */
tmp = realloc(*uris, (*uri_count + 1) * sizeof **uris);
if (!tmp) {
ERRMEM;
ret = 1;
mbedtls_x509_free_subject_alt_name(&san);
goto cleanup;
}
*uris = tmp;
*uris[*uri_count] = strndup((const char *)san.san.unstructured_name.p, san.san.unstructured_name.len);
if (!*uris[*uri_count]) {
ERRMEM;
ret = 1;
mbedtls_x509_free_subject_alt_name(&san);
goto cleanup;
}
++(*uri_count);
}
mbedtls_x509_free_subject_alt_name(&san);
iter = iter->next;
}
} else if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) {
/* failed to parse it, but not because it's optional */
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
goto cleanup;
}
}
}
cert = cert->next;
}
cleanup:
return ret;
}
int
nc_tls_process_cipher_suite_wrap(const char *cipher, char **out)
{
const char *begin, *ptr;
/* check if it's a TLS 1.3 cipher suite */
if (!strcmp(cipher, "tls-aes-256-gcm-sha384") || !strcmp(cipher, "tls-aes-128-gcm-sha256") ||
!strcmp(cipher, "tls-chacha20-poly1305-sha256") || !strcmp(cipher, "tls-aes-128-ccm-sha256") ||
!strcmp(cipher, "tls-aes-128-ccm-8-sha256")) {
/* + 3 because mbedtls has "TLS1-3" prefix for 1.3 suites */
*out = malloc(strlen(cipher) + 3 + 1);
NC_CHECK_ERRMEM_RET(!*out, 1);
sprintf(*out, "TLS1-3");
begin = cipher + 4;
} else {
*out = malloc(strlen(cipher) + 1);
NC_CHECK_ERRMEM_RET(!*out, 1);
begin = cipher;
}
/* convert to uppercase */
for (ptr = begin; *ptr; ptr++) {
(*out)[ptr - begin] = toupper(*ptr);
}
(*out)[ptr - begin] = '\0';
return 0;
}
int
nc_tls_append_cipher_suite_wrap(struct nc_server_tls_opts *opts, const char *cipher_suite)
{
int cipher_id;
cipher_id = mbedtls_ssl_get_ciphersuite_id(cipher_suite);
if (!cipher_id) {
return 1;
}
/* append the cipher suite to a zero terminated array */
if (!opts->ciphers) {
/* first entry, account for terminating 0 */
opts->ciphers = malloc(2 * sizeof *opts->ciphers);
NC_CHECK_ERRMEM_RET(!opts->ciphers, 1);
((int *)opts->ciphers)[0] = cipher_id;
opts->cipher_count = 1;
} else {
/* +2 because of terminating 0 */
opts->ciphers = nc_realloc(opts->ciphers, (opts->cipher_count + 2) * sizeof *opts->ciphers);
NC_CHECK_ERRMEM_RET(!opts->ciphers, 1);
((int *)opts->ciphers)[opts->cipher_count] = cipher_id;
opts->cipher_count++;
}
/* terminate the array */
((int *)opts->ciphers)[opts->cipher_count] = 0;
return 0;
}
void
nc_server_tls_set_cipher_suites_wrap(void *tls_cfg, void *cipher_suites)
{
mbedtls_ssl_conf_ciphersuites(tls_cfg, cipher_suites);
}