openvpn/src/openvpn/ssl_verify_mbedtls.c
Max Fillinger 494fb71804
Some checks are pending
Build / Check code style with clang-format (push) Waiting to run
Build / Android - arm64-v8a (push) Waiting to run
Build / gcc-mingw - x64 - Debug - OSSL (push) Waiting to run
Build / gcc-mingw - x64 - Release - OSSL (push) Waiting to run
Build / gcc-mingw - x86 - Debug - OSSL (push) Waiting to run
Build / gcc-mingw - x86 - Release - OSSL (push) Waiting to run
Build / mingw unittest argv - x64 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest auth_token - x64 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest buffer - x64 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest crypto - x64 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest cryptoapi - x64 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest misc - x64 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest ncp - x64 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest options_parse - x64 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest packet_id - x64 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest pkt - x64 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest provider - x64 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest ssl - x64 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest tls_crypt - x64 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest user_pass - x64 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest argv - x64 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest auth_token - x64 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest buffer - x64 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest crypto - x64 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest cryptoapi - x64 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest misc - x64 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest ncp - x64 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest options_parse - x64 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest packet_id - x64 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest pkt - x64 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest provider - x64 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest ssl - x64 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest tls_crypt - x64 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest user_pass - x64 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest argv - x86 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest auth_token - x86 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest buffer - x86 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest crypto - x86 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest cryptoapi - x86 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest misc - x86 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest ncp - x86 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest options_parse - x86 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest packet_id - x86 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest pkt - x86 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest provider - x86 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest ssl - x86 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest tls_crypt - x86 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest user_pass - x86 - Debug - OSSL (push) Blocked by required conditions
Build / mingw unittest argv - x86 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest auth_token - x86 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest buffer - x86 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest crypto - x86 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest cryptoapi - x86 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest misc - x86 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest ncp - x86 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest options_parse - x86 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest packet_id - x86 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest pkt - x86 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest provider - x86 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest ssl - x86 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest tls_crypt - x86 - Release - OSSL (push) Blocked by required conditions
Build / mingw unittest user_pass - x86 - Release - OSSL (push) Blocked by required conditions
Build / gcc - ubuntu-24.04 - OpenSSL 3.0.13 --enable-pkcs11 (push) Waiting to run
Build / gcc - ubuntu-22.04 - OpenSSL 3.0.2 --enable-pkcs11 (push) Waiting to run
Build / clang-asan - ubuntu-22.04 - openssl (push) Waiting to run
Build / clang-asan - ubuntu-24.04 - openssl (push) Waiting to run
Build / macos-14 - libressl - asan (push) Waiting to run
Build / macos-14 - openssl@3 - asan (push) Waiting to run
Build / macos-15 - libressl - asan (push) Waiting to run
Build / macos-15 - openssl@3 - asan (push) Waiting to run
Build / macos-26 - libressl - asan (push) Waiting to run
Build / macos-26 - openssl@3 - asan (push) Waiting to run
Build / macos-14 - libressl - normal (push) Waiting to run
Build / macos-14 - openssl@3 - normal (push) Waiting to run
Build / macos-15 - libressl - normal (push) Waiting to run
Build / macos-15 - openssl@3 - normal (push) Waiting to run
Build / macos-26 - libressl - normal (push) Waiting to run
Build / macos-26 - openssl@3 - normal (push) Waiting to run
Build / msbuild - amd64 - openssl (push) Waiting to run
Build / msbuild - amd64-clang - openssl (push) Waiting to run
Build / msbuild - arm64 - openssl (push) Waiting to run
Build / msbuild - x86 - openssl (push) Waiting to run
Build / msbuild - x86-clang - openssl (push) Waiting to run
Build / clang asan - ubuntu-22.04 - libressl (push) Waiting to run
Build / gcc normal - ubuntu-22.04 - libressl (push) Waiting to run
Build / clang asan - ubuntu-22.04 - mbedtls3 (push) Waiting to run
Build / gcc normal - ubuntu-22.04 - mbedtls3 (push) Waiting to run
Build / clang asan - ubuntu-24.04 - awslc (push) Waiting to run
Build / gcc normal - ubuntu-24.04 - awslc (push) Waiting to run
Deploy Doxygen documentation to Pages / build (push) Waiting to run
Deploy Doxygen documentation to Pages / deploy (push) Blocked by required conditions
Add support for Mbed TLS 4
This commit adds support for Mbed TLS 4. This version comes with some
drastic changes. The crypto library has been completely redesigned, so
the contents of crypto_mbedtls.c are moved to crypto_mbedtls_legacy.c
and crypto_mbedtls.c handles the crypto for version 4.

Mbed TLS 4 also removed the feature for looking up a crypto algorithm by
name, so we need to translate algorithm names to Mbed TLS numbers in
OpenVPN. The tables are not yet complete. For symmetric algorithms, I
have added AES and Chacha-Poly which should be enough for most use
cases.

Change-Id: Ib251d546d993b96ed3bd8cb9111bcc627cdb0fae
Signed-off-by: Max Fillinger <maximilian.fillinger@sentyron.com>
Acked-by: Arne Schwabe <arne-openvpn@rfc2549.org>
Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1441
Message-Id: <20260123164746.7333-1-gert@greenie.muc.de>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg35401.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>
2026-01-24 18:49:44 +01:00

737 lines
20 KiB
C

/*
* OpenVPN -- An application to securely tunnel IP networks
* over a single TCP/UDP port, with support for SSL/TLS-based
* session authentication and key exchange,
* packet encryption, packet authentication, and
* packet compression.
*
* Copyright (C) 2002-2026 OpenVPN Inc <sales@openvpn.net>
* Copyright (C) 2010-2026 Sentyron B.V. <openvpn@sentyron.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <https://www.gnu.org/licenses/>.
*/
/**
* @file
* Control Channel Verification Module mbed TLS backend
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "syshead.h"
#if defined(ENABLE_CRYPTO_MBEDTLS)
#include <mbedtls/version.h>
#if MBEDTLS_VERSION_NUMBER < 0x04000000
#include "crypto_mbedtls_legacy.h"
#include <mbedtls/bignum.h>
#include <mbedtls/sha1.h>
#else
#include "crypto_mbedtls.h"
#endif
#include "mbedtls_compat.h"
#include "ssl_verify.h"
#include <mbedtls/asn1.h>
#include <mbedtls/error.h>
#include <mbedtls/oid.h>
#define MAX_SUBJECT_LENGTH 256
int
verify_callback(void *session_obj, mbedtls_x509_crt *cert, int cert_depth, uint32_t *flags)
{
struct tls_session *session = (struct tls_session *)session_obj;
struct gc_arena gc = gc_new();
ASSERT(cert);
ASSERT(session);
session->verified = false;
/* Remember certificate hash */
struct buffer cert_fingerprint = x509_get_sha256_fingerprint(cert, &gc);
cert_hash_remember(session, cert_depth, &cert_fingerprint);
if (session->opt->verify_hash_no_ca)
{
/*
* If we decide to verify the peer certificate based on the fingerprint
* we ignore wrong dates and the certificate not being trusted.
* Any other problem with the certificate (wrong key, bad cert,...)
* will still trigger an error.
* Clearing these flags relies on verify_cert will later rejecting a
* certificate that has no matching fingerprint.
*/
uint32_t flags_ignore = MBEDTLS_X509_BADCERT_NOT_TRUSTED | MBEDTLS_X509_BADCERT_EXPIRED
| MBEDTLS_X509_BADCERT_FUTURE;
*flags = *flags & ~flags_ignore;
}
/* did peer present cert which was signed by our root cert? */
if (*flags != 0)
{
int ret = 0;
char errstr[512] = { 0 };
char *subject = x509_get_subject(cert, &gc);
char *serial = backend_x509_get_serial(cert, &gc);
ret = mbedtls_x509_crt_verify_info(errstr, sizeof(errstr) - 1, "", *flags);
if (ret <= 0
&& snprintf(errstr, sizeof(errstr), "Could not retrieve error string, flags=%" PRIx32,
*flags)
>= sizeof(errstr))
{
errstr[0] = '\0';
}
else
{
chomp(errstr);
}
if (subject)
{
msg(D_TLS_ERRORS, "VERIFY ERROR: depth=%d, subject=%s, serial=%s: %s", cert_depth,
subject, serial ? serial : "<not available>", errstr);
}
else
{
msg(D_TLS_ERRORS,
"VERIFY ERROR: depth=%d, (could not extract X509 "
"subject string from certificate): %s",
cert_depth, errstr);
}
/* Leave flags set to non-zero to indicate that the cert is not ok */
}
else if (SUCCESS != verify_cert(session, cert, cert_depth))
{
*flags |= MBEDTLS_X509_BADCERT_OTHER;
}
gc_free(&gc);
/*
* PolarSSL/mbed TLS-1.2.0+ expects 0 on anything except fatal errors.
*/
return 0;
}
/* not supported for mbedTLS yet */
bool
x509_username_field_ext_supported(const char *fieldname)
{
return false;
}
result_t
backend_x509_get_username(char *cn, size_t cn_len, char *x509_username_field, mbedtls_x509_crt *cert)
{
mbedtls_x509_name *name;
ASSERT(cn != NULL);
name = &cert->subject;
/* Find common name */
while (name != NULL)
{
if (0 == memcmp(name->oid.p, MBEDTLS_OID_AT_CN, MBEDTLS_OID_SIZE(MBEDTLS_OID_AT_CN)))
{
break;
}
name = name->next;
}
/* Not found, return an error if this is the peer's certificate */
if (name == NULL)
{
return FAILURE;
}
/* Found, extract CN */
if (cn_len > name->val.len)
{
memcpy(cn, name->val.p, name->val.len);
cn[name->val.len] = '\0';
}
else
{
memcpy(cn, name->val.p, cn_len);
cn[cn_len - 1] = '\0';
}
return SUCCESS;
}
#if MBEDTLS_VERSION_NUMBER >= 0x04000000
/* Mbed TLS 4 has no function to print the certificate serial number and does
* not expose the bignum functions anymore. So in order to write the serial
* number as a decimal string, we implement bignum % 10 and bignum / 10. */
static char
bignum_mod_10(const uint8_t *bignum, size_t bignum_length)
{
int result = 0;
for (size_t i = 0; i < bignum_length; i++)
{
result = (result * 256) % 10;
result = (result + bignum[i]) % 10;
}
return (char)result;
}
/* Divide bignum by 10 rounded down, in place. */
static void
bignum_div_10(uint8_t *bignum, size_t *bignum_length)
{
/*
* Some intuition for the algorithm below:
*
* We want to calculate
*
* (bignum[0] * 256^n + bignum[1] * 256^(n-1) + ... + bignum[n]) / 10.
*
* Let remainder = bignum[0] % 10 and carry = remainder * 256.
* Then we can write the above as
*
* (bignum[0] / 10) * 256^n
* + ((carry + bignum[1]) * 256^(n-1) + ... + bignum[n]) / 10.
*
* So now we have the first byte of our result. The second byte will be
* (carry + bignum[1]) / 10. Note that this fits into one byte because
* 0 <= remainder < 10. We calculate the next remainder and carry as
* remainder = (carry + bignum[1]) % 10 and carry = remainder * 256 and
* move on to the next byte until we are done.
*/
size_t new_length = 0;
int carry = 0;
for (size_t i = 0; i < *bignum_length; i++)
{
uint8_t next_byte = (uint8_t)((bignum[i] + carry) / 10);
int remainder = (bignum[i] + carry) % 10;
carry = remainder * 256;
/* Write the byte unless it's a leading zero. */
if (new_length != 0 || next_byte != 0)
{
bignum[new_length++] = next_byte;
}
}
*bignum_length = new_length;
}
/* Write the decimal representation of bignum to out, if enough space is available.
* Returns the number of bytes needed in out, or 0 on error. To calculate the
* necessary buffer size, the function can be called with out = NULL. */
static size_t
write_bignum(char *out, size_t out_size, const uint8_t *bignum, size_t bignum_length)
{
if (bignum_length == 0)
{
/* We want out to be "0". */
if (out != NULL)
{
if (out_size >= 2)
{
out[0] = '0';
out[1] = '\0';
}
else if (out_size > 0)
{
out[0] = '\0';
}
}
return 2;
}
uint8_t *bignum_copy = malloc(bignum_length);
if (bignum_copy == NULL)
{
return 0;
}
memcpy(bignum_copy, bignum, bignum_length);
size_t bytes_needed = 0;
size_t bytes_written = 0;
while (bignum_length > 0)
{
/* We're writing the digits in reverse order. We put them in the right order later. */
char digit = bignum_mod_10(bignum_copy, bignum_length);
if (out != NULL && bytes_written < out_size - 1)
{
out[bytes_written++] = '0' + (char)digit;
}
bytes_needed += 1;
bignum_div_10(bignum_copy, &bignum_length);
}
if (out != NULL)
{
if (bytes_written == bytes_needed)
{
/* We had space for all digits. Now reverse them. */
for (size_t i = 0; i < bytes_written / 2; i++)
{
char tmp = out[i];
out[i] = out[bytes_written - 1 - i];
out[bytes_written - 1 - i] = tmp;
}
out[bytes_written] = '\0';
}
else if (out_size > 0)
{
out[0] = '\0';
}
}
bytes_needed += 1;
free(bignum_copy);
return bytes_needed;
}
#endif /* MBEDTLS_VERSION_NUMBER >= 0x04000000 */
char *
backend_x509_get_serial(mbedtls_x509_crt *cert, struct gc_arena *gc)
{
char *buf = NULL;
size_t buflen = 0;
#if MBEDTLS_VERSION_NUMBER < 0x04000000
mbedtls_mpi serial_mpi = { 0 };
/* Transform asn1 integer serial into mbed TLS MPI */
mbedtls_mpi_init(&serial_mpi);
if (!mbed_ok(mbedtls_mpi_read_binary(&serial_mpi, cert->serial.p, cert->serial.len)))
{
msg(M_WARN, "Failed to retrieve serial from certificate.");
goto end;
}
/* Determine decimal representation length, allocate buffer */
mbedtls_mpi_write_string(&serial_mpi, 10, NULL, 0, &buflen);
buf = gc_malloc(buflen, true, gc);
/* Write MPI serial as decimal string into buffer */
if (!mbed_ok(mbedtls_mpi_write_string(&serial_mpi, 10, buf, buflen, &buflen)))
{
msg(M_WARN, "Failed to write serial to string.");
buf = NULL;
goto end;
}
end:
mbedtls_mpi_free(&serial_mpi);
return buf;
#else
buflen = write_bignum(NULL, 0, cert->serial.p, cert->serial.len);
if (buflen == 0)
{
msg(M_WARN, "Failed to write serial to string.");
return NULL;
}
buf = gc_malloc(buflen, true, gc);
if (write_bignum(buf, buflen, cert->serial.p, cert->serial.len) != buflen)
{
msg(M_WARN, "Failed to write serial to string.");
return NULL;
}
return buf;
#endif /* MBEDTLS_VERSION_NUMBER < 0x04000000 */
}
char *
backend_x509_get_serial_hex(mbedtls_x509_crt *cert, struct gc_arena *gc)
{
char *buf = NULL;
size_t len = cert->serial.len * 3 + 1;
buf = gc_malloc(len, true, gc);
if (mbedtls_x509_serial_gets(buf, len - 1, &cert->serial) < 0)
{
buf = NULL;
}
return buf;
}
result_t
backend_x509_write_pem(openvpn_x509_cert_t *cert, const char *filename)
{
/* mbed TLS does not make it easy to write a certificate in PEM format.
* The only way is to directly access the DER encoded raw certificate
* and PEM encode it ourselves */
struct gc_arena gc = gc_new();
/* just do a very loose upper bound for the base64 based PEM encoding
* using 3 times the space for the base64 and 100 bytes for the
* headers and footer */
struct buffer pem = alloc_buf_gc(cert->raw.len * 3 + 100, &gc);
struct buffer der = { 0 };
buf_set_read(&der, cert->raw.p, cert->raw.len);
if (!crypto_pem_encode("CERTIFICATE", &pem, &der, &gc))
{
goto err;
}
if (!buffer_write_file(filename, &pem))
{
goto err;
}
gc_free(&gc);
return SUCCESS;
err:
msg(D_TLS_DEBUG_LOW, "Error writing X509 certificate to file %s", filename);
gc_free(&gc);
return FAILURE;
}
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#endif
static struct buffer
x509_get_fingerprint(const mbedtls_md_info_t *md_info, mbedtls_x509_crt *cert, struct gc_arena *gc)
{
const size_t md_size = mbedtls_md_get_size(md_info);
struct buffer fingerprint = alloc_buf_gc(md_size, gc);
mbedtls_md(md_info, cert->raw.p, cert->raw.len, BPTR(&fingerprint));
ASSERT(buf_inc_len(&fingerprint, md_size));
return fingerprint;
}
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
struct buffer
x509_get_sha1_fingerprint(mbedtls_x509_crt *cert, struct gc_arena *gc)
{
return x509_get_fingerprint(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), cert, gc);
}
struct buffer
x509_get_sha256_fingerprint(mbedtls_x509_crt *cert, struct gc_arena *gc)
{
return x509_get_fingerprint(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), cert, gc);
}
char *
x509_get_subject(mbedtls_x509_crt *cert, struct gc_arena *gc)
{
char tmp_subject[MAX_SUBJECT_LENGTH] = { 0 };
char *subject = NULL;
int ret = 0;
ret = mbedtls_x509_dn_gets(tmp_subject, MAX_SUBJECT_LENGTH - 1, &cert->subject);
if (ret > 0)
{
/* Allocate the required space for the subject */
subject = string_alloc(tmp_subject, gc);
}
return subject;
}
static void
do_setenv_x509(struct env_set *es, const char *name, char *value, int depth)
{
char *name_expand;
size_t name_expand_size;
string_mod(value, CC_ANY, CC_CRLF, '?');
msg(D_X509_ATTR, "X509 ATTRIBUTE name='%s' value='%s' depth=%d", name, value, depth);
name_expand_size = 64 + strlen(name);
name_expand = (char *)malloc(name_expand_size);
check_malloc_return(name_expand);
snprintf(name_expand, name_expand_size, "X509_%d_%s", depth, name);
setenv_str(es, name_expand, value);
free(name_expand);
}
static char *
asn1_buf_to_c_string(const mbedtls_asn1_buf *orig, struct gc_arena *gc)
{
size_t i;
char *val;
if (!(orig->tag == MBEDTLS_ASN1_UTF8_STRING || orig->tag == MBEDTLS_ASN1_PRINTABLE_STRING
|| orig->tag == MBEDTLS_ASN1_IA5_STRING))
{
/* Only support C-string compatible types */
return string_alloc("ERROR: unsupported ASN.1 string type", gc);
}
for (i = 0; i < orig->len; ++i)
{
if (orig->p[i] == '\0')
{
return string_alloc("ERROR: embedded null value", gc);
}
}
val = gc_malloc(orig->len + 1, false, gc);
memcpy(val, orig->p, orig->len);
val[orig->len] = '\0';
return val;
}
static void
do_setenv_name(struct env_set *es, const struct x509_track *xt, const mbedtls_x509_crt *cert,
int depth, struct gc_arena *gc)
{
const mbedtls_x509_name *xn;
for (xn = &cert->subject; xn != NULL; xn = xn->next)
{
const char *xn_short_name = NULL;
if (0 == mbedtls_oid_get_attr_short_name(&xn->oid, &xn_short_name)
&& 0 == strcmp(xt->name, xn_short_name))
{
char *val_str = asn1_buf_to_c_string(&xn->val, gc);
do_setenv_x509(es, xt->name, val_str, depth);
}
}
}
void
x509_track_add(const struct x509_track **ll_head, const char *name, msglvl_t msglevel,
struct gc_arena *gc)
{
struct x509_track *xt;
ALLOC_OBJ_CLEAR_GC(xt, struct x509_track, gc);
if (*name == '+')
{
xt->flags |= XT_FULL_CHAIN;
++name;
}
xt->name = name;
xt->next = *ll_head;
*ll_head = xt;
}
void
x509_setenv_track(const struct x509_track *xt, struct env_set *es, const int depth,
mbedtls_x509_crt *cert)
{
struct gc_arena gc = gc_new();
while (xt)
{
if (depth == 0 || (xt->flags & XT_FULL_CHAIN))
{
if (0 == strcmp(xt->name, "SHA1") || 0 == strcmp(xt->name, "SHA256"))
{
/* Fingerprint is not part of X509 structure */
struct buffer cert_hash;
char *fingerprint;
if (0 == strcmp(xt->name, "SHA1"))
{
cert_hash = x509_get_sha1_fingerprint(cert, &gc);
}
else
{
cert_hash = x509_get_sha256_fingerprint(cert, &gc);
}
fingerprint =
format_hex_ex(BPTR(&cert_hash), BLEN(&cert_hash), 0, 1 | FHE_CAPS, ":", &gc);
do_setenv_x509(es, xt->name, fingerprint, depth);
}
else
{
do_setenv_name(es, xt, cert, depth, &gc);
}
}
xt = xt->next;
}
gc_free(&gc);
}
/*
* Save X509 fields to environment, using the naming convention:
*
* X509_{cert_depth}_{name}={value}
*/
void
x509_setenv(struct env_set *es, int cert_depth, mbedtls_x509_crt *cert)
{
int i;
unsigned char c;
const mbedtls_x509_name *name;
char s[128] = { 0 };
name = &cert->subject;
while (name != NULL)
{
char name_expand[64 + 8];
const char *shortname;
if (0 == mbedtls_oid_get_attr_short_name(&name->oid, &shortname))
{
snprintf(name_expand, sizeof(name_expand), "X509_%d_%s", cert_depth, shortname);
}
else
{
snprintf(name_expand, sizeof(name_expand), "X509_%d_\?\?", cert_depth);
}
for (i = 0; i < name->val.len; i++)
{
if (i >= (int)sizeof(s) - 1)
{
break;
}
c = name->val.p[i];
if (c < 32 || c == 127 || (c > 128 && c < 160))
{
s[i] = '?';
}
else
{
s[i] = c;
}
}
s[i] = '\0';
/* Check both strings, set environment variable */
string_mod(name_expand, CC_PRINT, CC_CRLF, '_');
string_mod((char *)s, CC_PRINT, CC_CRLF, '_');
setenv_str_incr(es, name_expand, (char *)s);
name = name->next;
}
}
/* Dummy function because Netscape certificate types are not supported in OpenVPN with mbedtls.
* Returns SUCCESS if usage is NS_CERT_CHECK_NONE, FAILURE otherwise. */
result_t
x509_verify_ns_cert_type(mbedtls_x509_crt *cert, const int usage)
{
if (usage == NS_CERT_CHECK_NONE)
{
return SUCCESS;
}
return FAILURE;
}
result_t
x509_verify_cert_ku(mbedtls_x509_crt *cert, const unsigned int *const expected_ku, size_t expected_len)
{
msg(D_HANDSHAKE, "Validating certificate key usage");
if (!mbedtls_x509_crt_has_ext_type(cert, MBEDTLS_X509_EXT_KEY_USAGE))
{
msg(D_TLS_ERRORS, "ERROR: Certificate does not have key usage extension");
return FAILURE;
}
if (expected_ku[0] == OPENVPN_KU_REQUIRED)
{
/* Extension required, value checked by TLS library */
return SUCCESS;
}
result_t fFound = FAILURE;
for (size_t i = 0; SUCCESS != fFound && i < expected_len; i++)
{
if (expected_ku[i] != 0 && 0 == mbedtls_x509_crt_check_key_usage(cert, expected_ku[i]))
{
fFound = SUCCESS;
}
}
if (fFound != SUCCESS)
{
msg(D_TLS_ERRORS, "ERROR: Certificate has invalid key usage, expected one of:");
for (size_t i = 0; i < expected_len && expected_ku[i]; i++)
{
msg(D_TLS_ERRORS, " * %04x", expected_ku[i]);
}
}
return fFound;
}
result_t
x509_verify_cert_eku(mbedtls_x509_crt *cert, const char *const expected_oid)
{
result_t fFound = FAILURE;
if (!mbedtls_x509_crt_has_ext_type(cert, MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE))
{
msg(D_HANDSHAKE, "Certificate does not have extended key usage extension");
}
else
{
mbedtls_x509_sequence *oid_seq = &(cert->ext_key_usage);
msg(D_HANDSHAKE, "Validating certificate extended key usage");
while (oid_seq != NULL)
{
mbedtls_x509_buf *oid = &oid_seq->buf;
char oid_num_str[1024];
const char *oid_str;
if (0 == mbedtls_oid_get_extended_key_usage(oid, &oid_str))
{
msg(D_HANDSHAKE, "++ Certificate has EKU (str) %s, expects %s", oid_str,
expected_oid);
if (!strcmp(expected_oid, oid_str))
{
fFound = SUCCESS;
break;
}
}
if (0 < mbedtls_oid_get_numeric_string(oid_num_str, sizeof(oid_num_str), oid))
{
msg(D_HANDSHAKE, "++ Certificate has EKU (oid) %s, expects %s", oid_num_str,
expected_oid);
if (!strcmp(expected_oid, oid_num_str))
{
fFound = SUCCESS;
break;
}
}
oid_seq = oid_seq->next;
}
}
return fFound;
}
bool
tls_verify_crl_missing(const struct tls_options *opt)
{
if (opt->crl_file && !(opt->ssl_flags & SSLF_CRL_VERIFY_DIR)
&& (opt->ssl_ctx->crl == NULL || opt->ssl_ctx->crl->version == 0))
{
return true;
}
return false;
}
#endif /* #if defined(ENABLE_CRYPTO_MBEDTLS) */