mirror of
https://github.com/OpenVPN/openvpn.git
synced 2026-02-03 20:39:40 -05:00
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
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>
737 lines
20 KiB
C
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) */
|