aboutsummaryrefslogtreecommitdiff
path: root/compat/libtls
diff options
context:
space:
mode:
authorOmar Polo <op@omarpolo.com>2024-01-07 18:52:51 +0000
committerOmar Polo <op@omarpolo.com>2024-01-07 18:52:51 +0000
commitebfc578491a0c59e41607c6cb58fc1c8e02f2ec3 (patch)
treed80e6637ada79e64e06afa5608622d291dec1cf2 /compat/libtls
parent12eb29d878ad5a96f030993f9331007f632233ab (diff)
sync libtls
Diffstat (limited to 'compat/libtls')
-rw-r--r--compat/libtls/Makefile1
-rw-r--r--compat/libtls/asn.c165
-rw-r--r--compat/libtls/openssl.c2
-rw-r--r--compat/libtls/tls.c46
-rw-r--r--compat/libtls/tls.h14
-rw-r--r--compat/libtls/tls_config.c17
-rw-r--r--compat/libtls/tls_conninfo.c6
-rw-r--r--compat/libtls/tls_internal.h31
-rw-r--r--compat/libtls/tls_ocsp.c7
-rw-r--r--compat/libtls/tls_signer.c443
-rw-r--r--compat/libtls/tls_verify.c101
11 files changed, 645 insertions, 188 deletions
diff --git a/compat/libtls/Makefile b/compat/libtls/Makefile
index 4af6e93..a585204 100644
--- a/compat/libtls/Makefile
+++ b/compat/libtls/Makefile
@@ -13,6 +13,7 @@ DISTFILES = Makefile \
tls_ocsp.c \
tls_peer.c \
tls_server.c \
+ tls_signer.c \
tls_util.c \
tls_verify.c
diff --git a/compat/libtls/asn.c b/compat/libtls/asn.c
index 4bc428e..db16392 100644
--- a/compat/libtls/asn.c
+++ b/compat/libtls/asn.c
@@ -26,38 +26,38 @@
#define GENTIME_LENGTH 15
#define UTCTIME_LENGTH 13
-#define V_ASN1_UTCTIME 23
-#define V_ASN1_GENERALIZEDTIME 24
+#define V_ASN1_UTCTIME 23
+#define V_ASN1_GENERALIZEDTIME 24
#ifndef HAVE_ASN1_TIME_TM_CMP
int
ASN1_time_tm_cmp(struct tm *tm1, struct tm *tm2)
{
- if (tm1->tm_year < tm2->tm_year)
- return (-1);
- if (tm1->tm_year > tm2->tm_year)
- return (1);
- if (tm1->tm_mon < tm2->tm_mon)
- return (-1);
- if (tm1->tm_mon > tm2->tm_mon)
- return (1);
- if (tm1->tm_mday < tm2->tm_mday)
- return (-1);
- if (tm1->tm_mday > tm2->tm_mday)
- return (1);
- if (tm1->tm_hour < tm2->tm_hour)
- return (-1);
- if (tm1->tm_hour > tm2->tm_hour)
- return (1);
- if (tm1->tm_min < tm2->tm_min)
- return (-1);
- if (tm1->tm_min > tm2->tm_min)
- return (1);
- if (tm1->tm_sec < tm2->tm_sec)
- return (-1);
- if (tm1->tm_sec > tm2->tm_sec)
- return (1);
- return 0;
+ if (tm1->tm_year < tm2->tm_year)
+ return (-1);
+ if (tm1->tm_year > tm2->tm_year)
+ return (1);
+ if (tm1->tm_mon < tm2->tm_mon)
+ return (-1);
+ if (tm1->tm_mon > tm2->tm_mon)
+ return (1);
+ if (tm1->tm_mday < tm2->tm_mday)
+ return (-1);
+ if (tm1->tm_mday > tm2->tm_mday)
+ return (1);
+ if (tm1->tm_hour < tm2->tm_hour)
+ return (-1);
+ if (tm1->tm_hour > tm2->tm_hour)
+ return (1);
+ if (tm1->tm_min < tm2->tm_min)
+ return (-1);
+ if (tm1->tm_min > tm2->tm_min)
+ return (1);
+ if (tm1->tm_sec < tm2->tm_sec)
+ return (-1);
+ if (tm1->tm_sec > tm2->tm_sec)
+ return (1);
+ return 0;
}
#endif
@@ -66,112 +66,15 @@ int
ASN1_time_tm_clamp_notafter(struct tm *tm)
{
#ifdef SMALL_TIME_T
- struct tm broken_os_epoch_tm;
- time_t broken_os_epoch_time = INT_MAX;
+ struct tm broken_os_epoch_tm;
+ time_t broken_os_epoch_time = INT_MAX;
- if (gmtime_r(&broken_os_epoch_time, &broken_os_epoch_tm) == NULL)
- return 0;
+ if (gmtime_r(&broken_os_epoch_time, &broken_os_epoch_tm) == NULL)
+ return 0;
- if (ASN1_time_tm_cmp(tm, &broken_os_epoch_tm) == 1)
- memcpy(tm, &broken_os_epoch_tm, sizeof(*tm));
-#endif
- return 1;
-}
+ if (ASN1_time_tm_cmp(tm, &broken_os_epoch_tm) == 1)
+ memcpy(tm, &broken_os_epoch_tm, sizeof(*tm));
#endif
-
-/*
- * Parse an RFC 5280 format ASN.1 time string.
- *
- * mode must be:
- * 0 if we expect to parse a time as specified in RFC 5280 for an X509 object.
- * V_ASN1_UTCTIME if we wish to parse an RFC5280 format UTC time.
- * V_ASN1_GENERALIZEDTIME if we wish to parse an RFC5280 format Generalized time.
- *
- * Returns:
- * -1 if the string was invalid.
- * V_ASN1_UTCTIME if the string validated as a UTC time string.
- * V_ASN1_GENERALIZEDTIME if the string validated as a Generalized time string.
- *
- * Fills in *tm with the corresponding time if tm is non NULL.
- */
-#ifndef HAVE_ASN1_TIME_PARSE
-#define ATOI2(ar) ((ar) += 2, ((ar)[-2] - '0') * 10 + ((ar)[-1] - '0'))
-int
-ASN1_time_parse(const char *bytes, size_t len, struct tm *tm, int mode)
-{
- size_t i;
- int type = 0;
- struct tm ltm;
- struct tm *lt;
- const char *p;
-
- if (bytes == NULL)
- return (-1);
-
- /* Constrain to valid lengths. */
- if (len != UTCTIME_LENGTH && len != GENTIME_LENGTH)
- return (-1);
-
- lt = tm;
- if (lt == NULL) {
- memset(&ltm, 0, sizeof(ltm));
- lt = &ltm;
- }
-
- /* Timezone is required and must be GMT (Zulu). */
- if (bytes[len - 1] != 'Z')
- return (-1);
-
- /* Make sure everything else is digits. */
- for (i = 0; i < len - 1; i++) {
- if (isdigit((unsigned char)bytes[i]))
- continue;
- return (-1);
- }
-
- /*
- * Validate and convert the time
- */
- p = bytes;
- switch (len) {
- case GENTIME_LENGTH:
- if (mode == V_ASN1_UTCTIME)
- return (-1);
- lt->tm_year = (ATOI2(p) * 100) - 1900; /* cc */
- type = V_ASN1_GENERALIZEDTIME;
- /* FALLTHROUGH */
- case UTCTIME_LENGTH:
- if (type == 0) {
- if (mode == V_ASN1_GENERALIZEDTIME)
- return (-1);
- type = V_ASN1_UTCTIME;
- }
- lt->tm_year += ATOI2(p); /* yy */
- if (type == V_ASN1_UTCTIME) {
- if (lt->tm_year < 50)
- lt->tm_year += 100;
- }
- lt->tm_mon = ATOI2(p) - 1; /* mm */
- if (lt->tm_mon < 0 || lt->tm_mon > 11)
- return (-1);
- lt->tm_mday = ATOI2(p); /* dd */
- if (lt->tm_mday < 1 || lt->tm_mday > 31)
- return (-1);
- lt->tm_hour = ATOI2(p); /* HH */
- if (lt->tm_hour < 0 || lt->tm_hour > 23)
- return (-1);
- lt->tm_min = ATOI2(p); /* MM */
- if (lt->tm_min < 0 || lt->tm_min > 59)
- return (-1);
- lt->tm_sec = ATOI2(p); /* SS */
- /* Leap second 60 is not accepted. Reconsider later? */
- if (lt->tm_sec < 0 || lt->tm_sec > 59)
- return (-1);
- break;
- default:
- return (-1);
- }
-
- return (type);
+ return 1;
}
#endif
diff --git a/compat/libtls/openssl.c b/compat/libtls/openssl.c
index 3528887..8c0b5ed 100644
--- a/compat/libtls/openssl.c
+++ b/compat/libtls/openssl.c
@@ -31,7 +31,7 @@
X509_LOOKUP_METHOD *
X509_LOOKUP_mem(void);
-static int
+int
X509_STORE_load_mem(X509_STORE *ctx, void *buf, int len)
{
X509_LOOKUP *lookup;
diff --git a/compat/libtls/tls.c b/compat/libtls/tls.c
index 0daabf5..75e1d2e 100644
--- a/compat/libtls/tls.c
+++ b/compat/libtls/tls.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls.c,v 1.97 2023/06/18 11:43:03 op Exp $ */
+/* $OpenBSD: tls.c,v 1.98 2023/07/02 06:37:27 beck Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
*
@@ -384,6 +384,8 @@ tls_keypair_to_pkey(struct tls *ctx, struct tls_keypair *keypair, EVP_PKEY **pke
static int
tls_keypair_setup_pkey(struct tls *ctx, struct tls_keypair *keypair, EVP_PKEY *pkey)
{
+ RSA_METHOD *rsa_method;
+ EC_KEY_METHOD *ecdsa_method;
RSA *rsa = NULL;
EC_KEY *eckey = NULL;
int ret = -1;
@@ -400,19 +402,45 @@ tls_keypair_setup_pkey(struct tls *ctx, struct tls_keypair *keypair, EVP_PKEY *p
switch (EVP_PKEY_id(pkey)) {
case EVP_PKEY_RSA:
if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL ||
- RSA_set_ex_data(rsa, 0, keypair->pubkey_hash) == 0 ||
- EVP_PKEY_set1_RSA(pkey, rsa) == 0) {
+ RSA_set_ex_data(rsa, 0, keypair->pubkey_hash) == 0) {
tls_set_errorx(ctx, "RSA key setup failure");
goto err;
}
+ if (ctx->config->sign_cb != NULL) {
+ rsa_method = tls_signer_rsa_method();
+ if (rsa_method == NULL ||
+ RSA_set_ex_data(rsa, 1, ctx->config) == 0 ||
+ RSA_set_method(rsa, rsa_method) == 0) {
+ tls_set_errorx(ctx, "failed to setup RSA key");
+ goto err;
+ }
+ }
+ /* Reset the key to work around caching in OpenSSL 3. */
+ if (EVP_PKEY_set1_RSA(pkey, rsa) == 0) {
+ tls_set_errorx(ctx, "failed to set RSA key");
+ goto err;
+ }
break;
case EVP_PKEY_EC:
if ((eckey = EVP_PKEY_get1_EC_KEY(pkey)) == NULL ||
- EC_KEY_set_ex_data(eckey, 0, keypair->pubkey_hash) == 0 ||
- EVP_PKEY_set1_EC_KEY(pkey, eckey) == 0) {
+ EC_KEY_set_ex_data(eckey, 0, keypair->pubkey_hash) == 0) {
tls_set_errorx(ctx, "EC key setup failure");
goto err;
}
+ if (ctx->config->sign_cb != NULL) {
+ ecdsa_method = tls_signer_ecdsa_method();
+ if (ecdsa_method == NULL ||
+ EC_KEY_set_ex_data(eckey, 1, ctx->config) == 0 ||
+ EC_KEY_set_method(eckey, ecdsa_method) == 0) {
+ tls_set_errorx(ctx, "failed to setup EC key");
+ goto err;
+ }
+ }
+ /* Reset the key to work around caching in OpenSSL 3. */
+ if (EVP_PKEY_set1_EC_KEY(pkey, eckey) == 0) {
+ tls_set_errorx(ctx, "failed to set EC key");
+ goto err;
+ }
break;
default:
tls_set_errorx(ctx, "incorrect key type");
@@ -488,16 +516,12 @@ tls_configure_ssl(struct tls *ctx, SSL_CTX *ssl_ctx)
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2);
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv3);
+ SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1);
+ SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_1);
- SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TLSv1);
- SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TLSv1_1);
SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TLSv1_2);
SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TLSv1_3);
- if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_0) == 0)
- SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1);
- if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_1) == 0)
- SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_1);
if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_2) == 0)
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_2);
if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_3) == 0)
diff --git a/compat/libtls/tls.h b/compat/libtls/tls.h
index b94a6fa..3418374 100644
--- a/compat/libtls/tls.h
+++ b/compat/libtls/tls.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls.h,v 1.62 2022/03/24 15:56:34 tb Exp $ */
+/* $OpenBSD: tls.h,v 1.63 2023/07/02 06:37:27 beck Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
*
@@ -29,14 +29,18 @@ extern "C" {
#define TLS_API 20200120
-#define TLS_PROTOCOL_TLSv1_0 (1 << 1)
-#define TLS_PROTOCOL_TLSv1_1 (1 << 2)
+/*
+ * Deprecated versions of TLS. Using these effectively selects
+ * the minimum supported version.
+ */
+#define TLS_PROTOCOL_TLSv1_0 (1 << 3)
+#define TLS_PROTOCOL_TLSv1_1 (1 << 3)
+/* Supported versions of TLS */
#define TLS_PROTOCOL_TLSv1_2 (1 << 3)
#define TLS_PROTOCOL_TLSv1_3 (1 << 4)
#define TLS_PROTOCOL_TLSv1 \
- (TLS_PROTOCOL_TLSv1_0|TLS_PROTOCOL_TLSv1_1|\
- TLS_PROTOCOL_TLSv1_2|TLS_PROTOCOL_TLSv1_3)
+ (TLS_PROTOCOL_TLSv1_2|TLS_PROTOCOL_TLSv1_3)
#define TLS_PROTOCOLS_ALL TLS_PROTOCOL_TLSv1
#define TLS_PROTOCOLS_DEFAULT (TLS_PROTOCOL_TLSv1_2|TLS_PROTOCOL_TLSv1_3)
diff --git a/compat/libtls/tls_config.c b/compat/libtls/tls_config.c
index 3f4306a..ffd443e 100644
--- a/compat/libtls/tls_config.c
+++ b/compat/libtls/tls_config.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls_config.c,v 1.66 2023/05/14 07:26:25 op Exp $ */
+/* $OpenBSD: tls_config.c,v 1.67 2023/07/02 06:37:27 beck Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
*
@@ -247,9 +247,9 @@ tls_config_parse_protocols(uint32_t *protocols, const char *protostr)
if (strcasecmp(p, "tlsv1") == 0)
proto = TLS_PROTOCOL_TLSv1;
else if (strcasecmp(p, "tlsv1.0") == 0)
- proto = TLS_PROTOCOL_TLSv1_0;
+ proto = TLS_PROTOCOL_TLSv1_2;
else if (strcasecmp(p, "tlsv1.1") == 0)
- proto = TLS_PROTOCOL_TLSv1_1;
+ proto = TLS_PROTOCOL_TLSv1_2;
else if (strcasecmp(p, "tlsv1.2") == 0)
proto = TLS_PROTOCOL_TLSv1_2;
else if (strcasecmp(p, "tlsv1.3") == 0)
@@ -735,6 +735,17 @@ tls_config_set_session_fd(struct tls_config *config, int session_fd)
}
int
+tls_config_set_sign_cb(struct tls_config *config, tls_sign_cb cb, void *cb_arg)
+{
+ config->use_fake_private_key = 1;
+ config->skip_private_key_check = 1;
+ config->sign_cb = cb;
+ config->sign_cb_arg = cb_arg;
+
+ return (0);
+}
+
+int
tls_config_set_verify_depth(struct tls_config *config, int verify_depth)
{
config->verify_depth = verify_depth;
diff --git a/compat/libtls/tls_conninfo.c b/compat/libtls/tls_conninfo.c
index ac6df2f..17a9040 100644
--- a/compat/libtls/tls_conninfo.c
+++ b/compat/libtls/tls_conninfo.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls_conninfo.c,v 1.23 2023/05/14 07:26:25 op Exp $ */
+/* $OpenBSD: tls_conninfo.c,v 1.24 2023/11/13 10:51:49 tb Exp $ */
/*
* Copyright (c) 2015 Joel Sing <jsing@openbsd.org>
* Copyright (c) 2015 Bob Beck <beck@openbsd.org>
@@ -119,9 +119,9 @@ tls_get_peer_cert_times(struct tls *ctx, time_t *notbefore,
goto err;
if ((after = X509_get_notAfter(ctx->ssl_peer_cert)) == NULL)
goto err;
- if (ASN1_time_parse(before->data, before->length, &before_tm, 0) == -1)
+ if (!ASN1_TIME_to_tm(before, &before_tm))
goto err;
- if (ASN1_time_parse(after->data, after->length, &after_tm, 0) == -1)
+ if (!ASN1_TIME_to_tm(after, &after_tm))
goto err;
if (!ASN1_time_tm_clamp_notafter(&after_tm))
goto err;
diff --git a/compat/libtls/tls_internal.h b/compat/libtls/tls_internal.h
index 4846a88..21645d3 100644
--- a/compat/libtls/tls_internal.h
+++ b/compat/libtls/tls_internal.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls_internal.h,v 1.82 2023/06/18 11:43:03 op Exp $ */
+/* $OpenBSD: tls_internal.h,v 1.83 2023/06/27 18:19:59 tb Exp $ */
/*
* Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org>
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
@@ -70,6 +70,10 @@ struct tls_ticket_key {
time_t time;
};
+typedef int (*tls_sign_cb)(void *_cb_arg, const char *_pubkey_hash,
+ const uint8_t *_input, size_t _input_len, int _padding_type,
+ uint8_t **_out_signature, size_t *_out_signature_len);
+
struct tls_config {
struct tls_error error;
@@ -103,6 +107,8 @@ struct tls_config {
int verify_time;
int skip_private_key_check;
int use_fake_private_key;
+ tls_sign_cb sign_cb;
+ void *sign_cb_arg;
};
struct tls_conninfo {
@@ -282,13 +288,30 @@ int tls_cert_pubkey_hash(X509 *_cert, char **_hash);
int tls_password_cb(char *_buf, int _size, int _rwflag, void *_u);
+RSA_METHOD *tls_signer_rsa_method(void);
+EC_KEY_METHOD *tls_signer_ecdsa_method(void);
+
+#define TLS_PADDING_NONE 0
+#define TLS_PADDING_RSA_PKCS1 1
+
+int tls_config_set_sign_cb(struct tls_config *_config, tls_sign_cb _cb,
+ void *_cb_arg);
+
+struct tls_signer* tls_signer_new(void);
+void tls_signer_free(struct tls_signer * _signer);
+const char *tls_signer_error(struct tls_signer * _signer);
+int tls_signer_add_keypair_file(struct tls_signer *_signer,
+ const char *_cert_file, const char *_key_file);
+int tls_signer_add_keypair_mem(struct tls_signer *_signer, const uint8_t *_cert,
+ size_t _cert_len, const uint8_t *_key, size_t _key_len);
+int tls_signer_sign(struct tls_signer *_signer, const char *_pubkey_hash,
+ const uint8_t *_input, size_t _input_len, int _padding_type,
+ uint8_t **_out_signature, size_t *_out_signature_len);
+
/* XXX this function is not fully hidden so relayd can use it */
void tls_config_skip_private_key_check(struct tls_config *config);
void tls_config_use_fake_private_key(struct tls_config *config);
-/* XXX prototypes brought for OpenSMTPD libtls wrapper to OpenSSL */
-int ASN1_time_parse(const char *bytes, size_t len, struct tm *tm, int mode);
-
#ifndef HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM
int SSL_CTX_use_certificate_chain_mem(SSL_CTX *, void *, int);
#endif
diff --git a/compat/libtls/tls_ocsp.c b/compat/libtls/tls_ocsp.c
index ada3362..b57d400 100644
--- a/compat/libtls/tls_ocsp.c
+++ b/compat/libtls/tls_ocsp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls_ocsp.c,v 1.23 2023/05/14 07:26:25 op Exp $ */
+/* $OpenBSD: tls_ocsp.c,v 1.24 2023/11/13 10:56:19 tb Exp $ */
/*
* Copyright (c) 2015 Marko Kreen <markokr@gmail.com>
* Copyright (c) 2016 Bob Beck <beck@openbsd.org>
@@ -66,8 +66,9 @@ tls_ocsp_asn1_parse_time(struct tls *ctx, ASN1_GENERALIZEDTIME *gt, time_t *gt_t
if (gt == NULL)
return -1;
/* RFC 6960 specifies that all times in OCSP must be GENERALIZEDTIME */
- if (ASN1_time_parse(gt->data, gt->length, &tm,
- V_ASN1_GENERALIZEDTIME) == -1)
+ if (!ASN1_GENERALIZEDTIME_check(gt))
+ return -1;
+ if (!ASN1_TIME_to_tm(gt, &tm))
return -1;
if ((*gt_time = timegm(&tm)) == -1)
return -1;
diff --git a/compat/libtls/tls_signer.c b/compat/libtls/tls_signer.c
new file mode 100644
index 0000000..a51c587
--- /dev/null
+++ b/compat/libtls/tls_signer.c
@@ -0,0 +1,443 @@
+/* $OpenBSD: tls_signer.c,v 1.9 2023/06/18 19:12:58 tb Exp $ */
+/*
+ * Copyright (c) 2021 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <limits.h>
+
+#include <openssl/ecdsa.h>
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+
+#include "tls.h"
+#include "tls_internal.h"
+
+struct tls_signer_key {
+ char *hash;
+ RSA *rsa;
+ EC_KEY *ecdsa;
+ struct tls_signer_key *next;
+};
+
+struct tls_signer {
+ struct tls_error error;
+ struct tls_signer_key *keys;
+};
+
+struct tls_signer *
+tls_signer_new(void)
+{
+ struct tls_signer *signer;
+
+ if ((signer = calloc(1, sizeof(*signer))) == NULL)
+ return (NULL);
+
+ return (signer);
+}
+
+void
+tls_signer_free(struct tls_signer *signer)
+{
+ struct tls_signer_key *skey;
+
+ if (signer == NULL)
+ return;
+
+ tls_error_clear(&signer->error);
+
+ while (signer->keys) {
+ skey = signer->keys;
+ signer->keys = skey->next;
+ RSA_free(skey->rsa);
+ EC_KEY_free(skey->ecdsa);
+ free(skey->hash);
+ free(skey);
+ }
+
+ free(signer);
+}
+
+const char *
+tls_signer_error(struct tls_signer *signer)
+{
+ return (signer->error.msg);
+}
+
+int
+tls_signer_add_keypair_mem(struct tls_signer *signer, const uint8_t *cert,
+ size_t cert_len, const uint8_t *key, size_t key_len)
+{
+ struct tls_signer_key *skey = NULL;
+ char *errstr = "unknown";
+ int ssl_err;
+ EVP_PKEY *pkey = NULL;
+ X509 *x509 = NULL;
+ BIO *bio = NULL;
+ char *hash = NULL;
+
+ /* Compute certificate hash */
+ if ((bio = BIO_new_mem_buf(cert, cert_len)) == NULL) {
+ tls_error_setx(&signer->error,
+ "failed to create certificate bio");
+ goto err;
+ }
+ if ((x509 = PEM_read_bio_X509(bio, NULL, tls_password_cb,
+ NULL)) == NULL) {
+ if ((ssl_err = ERR_peek_error()) != 0)
+ errstr = ERR_error_string(ssl_err, NULL);
+ tls_error_setx(&signer->error, "failed to load certificate: %s",
+ errstr);
+ goto err;
+ }
+ if (tls_cert_pubkey_hash(x509, &hash) == -1) {
+ tls_error_setx(&signer->error,
+ "failed to get certificate hash");
+ goto err;
+ }
+
+ X509_free(x509);
+ x509 = NULL;
+ BIO_free(bio);
+ bio = NULL;
+
+ /* Read private key */
+ if ((bio = BIO_new_mem_buf(key, key_len)) == NULL) {
+ tls_error_setx(&signer->error, "failed to create key bio");
+ goto err;
+ }
+ if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, tls_password_cb,
+ NULL)) == NULL) {
+ tls_error_setx(&signer->error, "failed to read private key");
+ goto err;
+ }
+
+ if ((skey = calloc(1, sizeof(*skey))) == NULL) {
+ tls_error_set(&signer->error, "failed to create key entry");
+ goto err;
+ }
+ skey->hash = hash;
+ if ((skey->rsa = EVP_PKEY_get1_RSA(pkey)) == NULL &&
+ (skey->ecdsa = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) {
+ tls_error_setx(&signer->error, "unknown key type");
+ goto err;
+ }
+
+ skey->next = signer->keys;
+ signer->keys = skey;
+ EVP_PKEY_free(pkey);
+ BIO_free(bio);
+
+ return (0);
+
+ err:
+ EVP_PKEY_free(pkey);
+ X509_free(x509);
+ BIO_free(bio);
+ free(hash);
+ free(skey);
+
+ return (-1);
+}
+
+int
+tls_signer_add_keypair_file(struct tls_signer *signer, const char *cert_file,
+ const char *key_file)
+{
+ char *cert = NULL, *key = NULL;
+ size_t cert_len, key_len;
+ int rv = -1;
+
+ if (tls_config_load_file(&signer->error, "certificate", cert_file,
+ &cert, &cert_len) == -1)
+ goto err;
+
+ if (tls_config_load_file(&signer->error, "key", key_file, &key,
+ &key_len) == -1)
+ goto err;
+
+ rv = tls_signer_add_keypair_mem(signer, cert, cert_len, key, key_len);
+
+ err:
+ free(cert);
+ free(key);
+
+ return (rv);
+}
+
+static int
+tls_sign_rsa(struct tls_signer *signer, struct tls_signer_key *skey,
+ const uint8_t *input, size_t input_len, int padding_type,
+ uint8_t **out_signature, size_t *out_signature_len)
+{
+ int rsa_padding, rsa_size, signature_len;
+ char *signature = NULL;
+
+ *out_signature = NULL;
+ *out_signature_len = 0;
+
+ if (padding_type == TLS_PADDING_NONE) {
+ rsa_padding = RSA_NO_PADDING;
+ } else if (padding_type == TLS_PADDING_RSA_PKCS1) {
+ rsa_padding = RSA_PKCS1_PADDING;
+ } else {
+ tls_error_setx(&signer->error, "invalid RSA padding type (%d)",
+ padding_type);
+ return (-1);
+ }
+
+ if (input_len > INT_MAX) {
+ tls_error_setx(&signer->error, "input too large");
+ return (-1);
+ }
+ if ((rsa_size = RSA_size(skey->rsa)) <= 0) {
+ tls_error_setx(&signer->error, "invalid RSA size: %d",
+ rsa_size);
+ return (-1);
+ }
+ if ((signature = calloc(1, rsa_size)) == NULL) {
+ tls_error_set(&signer->error, "RSA signature");
+ return (-1);
+ }
+
+ if ((signature_len = RSA_private_encrypt((int)input_len, input,
+ signature, skey->rsa, rsa_padding)) <= 0) {
+ /* XXX - include further details from libcrypto. */
+ tls_error_setx(&signer->error, "RSA signing failed");
+ free(signature);
+ return (-1);
+ }
+
+ *out_signature = signature;
+ *out_signature_len = (size_t)signature_len;
+
+ return (0);
+}
+
+static int
+tls_sign_ecdsa(struct tls_signer *signer, struct tls_signer_key *skey,
+ const uint8_t *input, size_t input_len, int padding_type,
+ uint8_t **out_signature, size_t *out_signature_len)
+{
+ unsigned char *signature;
+ int signature_len;
+
+ *out_signature = NULL;
+ *out_signature_len = 0;
+
+ if (padding_type != TLS_PADDING_NONE) {
+ tls_error_setx(&signer->error, "invalid ECDSA padding");
+ return (-1);
+ }
+
+ if (input_len > INT_MAX) {
+ tls_error_setx(&signer->error, "digest too large");
+ return (-1);
+ }
+ if ((signature_len = ECDSA_size(skey->ecdsa)) <= 0) {
+ tls_error_setx(&signer->error, "invalid ECDSA size: %d",
+ signature_len);
+ return (-1);
+ }
+ if ((signature = calloc(1, signature_len)) == NULL) {
+ tls_error_set(&signer->error, "ECDSA signature");
+ return (-1);
+ }
+
+ if (!ECDSA_sign(0, input, input_len, signature, &signature_len,
+ skey->ecdsa)) {
+ /* XXX - include further details from libcrypto. */
+ tls_error_setx(&signer->error, "ECDSA signing failed");
+ free(signature);
+ return (-1);
+ }
+
+ *out_signature = signature;
+ *out_signature_len = signature_len;
+
+ return (0);
+}
+
+int
+tls_signer_sign(struct tls_signer *signer, const char *pubkey_hash,
+ const uint8_t *input, size_t input_len, int padding_type,
+ uint8_t **out_signature, size_t *out_signature_len)
+{
+ struct tls_signer_key *skey;
+
+ *out_signature = NULL;
+ *out_signature_len = 0;
+
+ for (skey = signer->keys; skey; skey = skey->next)
+ if (!strcmp(pubkey_hash, skey->hash))
+ break;
+
+ if (skey == NULL) {
+ tls_error_setx(&signer->error, "key not found");
+ return (-1);
+ }
+
+ if (skey->rsa != NULL)
+ return tls_sign_rsa(signer, skey, input, input_len,
+ padding_type, out_signature, out_signature_len);
+
+ if (skey->ecdsa != NULL)
+ return tls_sign_ecdsa(signer, skey, input, input_len,
+ padding_type, out_signature, out_signature_len);
+
+ tls_error_setx(&signer->error, "unknown key type");
+
+ return (-1);
+}
+
+static int
+tls_rsa_priv_enc(int from_len, const unsigned char *from, unsigned char *to,
+ RSA *rsa, int rsa_padding)
+{
+ struct tls_config *config;
+ uint8_t *signature = NULL;
+ size_t signature_len = 0;
+ const char *pubkey_hash;
+ int padding_type;
+
+ /*
+ * This function is called via RSA_private_encrypt() and has to conform
+ * to its calling convention/signature. The caller is required to
+ * provide a 'to' buffer of at least RSA_size() bytes.
+ */
+
+ pubkey_hash = RSA_get_ex_data(rsa, 0);
+ config = RSA_get_ex_data(rsa, 1);
+
+ if (pubkey_hash == NULL || config == NULL)
+ goto err;
+
+ if (rsa_padding == RSA_NO_PADDING) {
+ padding_type = TLS_PADDING_NONE;
+ } else if (rsa_padding == RSA_PKCS1_PADDING) {
+ padding_type = TLS_PADDING_RSA_PKCS1;
+ } else {
+ goto err;
+ }
+
+ if (from_len < 0)
+ goto err;
+
+ if (config->sign_cb(config->sign_cb_arg, pubkey_hash, from, from_len,
+ padding_type, &signature, &signature_len) == -1)
+ goto err;
+
+ if (signature_len > INT_MAX || (int)signature_len > RSA_size(rsa))
+ goto err;
+
+ memcpy(to, signature, signature_len);
+ free(signature);
+
+ return ((int)signature_len);
+
+ err:
+ free(signature);
+
+ return (-1);
+}
+
+RSA_METHOD *
+tls_signer_rsa_method(void)
+{
+ static RSA_METHOD *rsa_method = NULL;
+
+ if (rsa_method != NULL)
+ goto out;
+
+ rsa_method = RSA_meth_new("libtls RSA method", 0);
+ if (rsa_method == NULL)
+ goto out;
+
+ RSA_meth_set_priv_enc(rsa_method, tls_rsa_priv_enc);
+
+ out:
+ return (rsa_method);
+}
+
+static ECDSA_SIG *
+tls_ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
+ const BIGNUM *rp, EC_KEY *eckey)
+{
+ struct tls_config *config;
+ ECDSA_SIG *ecdsa_sig = NULL;
+ uint8_t *signature = NULL;
+ size_t signature_len = 0;
+ const unsigned char *p;
+ const char *pubkey_hash;
+
+ /*
+ * This function is called via ECDSA_do_sign_ex() and has to conform
+ * to its calling convention/signature.
+ */
+
+ pubkey_hash = EC_KEY_get_ex_data(eckey, 0);
+ config = EC_KEY_get_ex_data(eckey, 1);
+
+ if (pubkey_hash == NULL || config == NULL)
+ goto err;
+
+ if (dgst_len < 0)
+ goto err;
+
+ if (config->sign_cb(config->sign_cb_arg, pubkey_hash, dgst, dgst_len,
+ TLS_PADDING_NONE, &signature, &signature_len) == -1)
+ goto err;
+
+ p = signature;
+ if ((ecdsa_sig = d2i_ECDSA_SIG(NULL, &p, signature_len)) == NULL)
+ goto err;
+
+ free(signature);
+
+ return (ecdsa_sig);
+
+ err:
+ free(signature);
+
+ return (NULL);
+}
+
+EC_KEY_METHOD *
+tls_signer_ecdsa_method(void)
+{
+ static EC_KEY_METHOD *ecdsa_method = NULL;
+ const EC_KEY_METHOD *default_method;
+ int (*sign)(int type, const unsigned char *dgst, int dlen,
+ unsigned char *sig, unsigned int *siglen,
+ const BIGNUM *kinv, const BIGNUM *r, EC_KEY *eckey);
+ int (*sign_setup)(EC_KEY *eckey, BN_CTX *ctx_in,
+ BIGNUM **kinvp, BIGNUM **rp);
+
+ if (ecdsa_method != NULL)
+ goto out;
+
+ default_method = EC_KEY_get_default_method();
+ ecdsa_method = EC_KEY_METHOD_new(default_method);
+ if (ecdsa_method == NULL)
+ goto out;
+
+ EC_KEY_METHOD_get_sign(default_method, &sign, &sign_setup, NULL);
+ EC_KEY_METHOD_set_sign(ecdsa_method, sign, sign_setup,
+ tls_ecdsa_do_sign);
+
+ out:
+ return (ecdsa_method);
+}
diff --git a/compat/libtls/tls_verify.c b/compat/libtls/tls_verify.c
index 053dc2f..7bf509d 100644
--- a/compat/libtls/tls_verify.c
+++ b/compat/libtls/tls_verify.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls_verify.c,v 1.23 2023/05/11 07:35:27 tb Exp $ */
+/* $OpenBSD: tls_verify.c,v 1.29 2023/11/22 18:23:09 op Exp $ */
/*
* Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org>
*
@@ -94,15 +94,21 @@ tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name,
union tls_addr addrbuf;
int addrlen, type;
int count, i;
- int rv = 0;
+ int critical = 0;
+ int rv = -1;
*alt_match = 0;
*alt_exists = 0;
- altname_stack = X509_get_ext_d2i(cert, NID_subject_alt_name,
- NULL, NULL);
- if (altname_stack == NULL)
- return 0;
+ altname_stack = X509_get_ext_d2i(cert, NID_subject_alt_name, &critical,
+ NULL);
+ if (altname_stack == NULL) {
+ if (critical != -1) {
+ tls_set_errorx(ctx, "error decoding subjectAltName");
+ goto err;
+ }
+ goto done;
+ }
if (inet_pton(AF_INET, name, &addrbuf) == 1) {
type = GEN_IPADD;
@@ -142,8 +148,7 @@ tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name,
"NUL byte in subjectAltName, "
"probably a malicious certificate",
name);
- rv = -1;
- break;
+ goto err;
}
/*
@@ -156,13 +161,12 @@ tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name,
"error verifying name '%s': "
"a dNSName of \" \" must not be "
"used", name);
- rv = -1;
- break;
+ goto err;
}
if (tls_match_name(data, name) == 0) {
*alt_match = 1;
- break;
+ goto done;
}
} else {
#ifdef DEBUG
@@ -183,8 +187,7 @@ tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name,
tls_set_errorx(ctx,
"Unexpected negative length for an "
"IP address: %d", datalen);
- rv = -1;
- break;
+ goto err;
}
/*
@@ -194,11 +197,15 @@ tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name,
if (datalen == addrlen &&
memcmp(data, &addrbuf, addrlen) == 0) {
*alt_match = 1;
- break;
+ goto done;
}
}
}
+ done:
+ rv = 0;
+
+ err:
sk_GENERAL_NAME_pop_free(altname_stack, GENERAL_NAME_free);
return rv;
}
@@ -207,10 +214,13 @@ static int
tls_check_common_name(struct tls *ctx, X509 *cert, const char *name,
int *cn_match)
{
+ unsigned char *utf8_bytes = NULL;
X509_NAME *subject_name;
char *common_name = NULL;
union tls_addr addrbuf;
int common_name_len;
+ ASN1_STRING *data;
+ int lastpos = -1;
int rv = -1;
*cn_match = 0;
@@ -219,29 +229,65 @@ tls_check_common_name(struct tls *ctx, X509 *cert, const char *name,
if (subject_name == NULL)
goto done;
- common_name_len = X509_NAME_get_text_by_NID(subject_name,
- NID_commonName, NULL, 0);
- if (common_name_len < 0)
+ lastpos = X509_NAME_get_index_by_NID(subject_name,
+ NID_commonName, lastpos);
+ if (lastpos == -1)
goto done;
-
- common_name = calloc(common_name_len + 1, 1);
- if (common_name == NULL) {
- tls_set_error(ctx, "out of memory");
+ if (lastpos < 0)
+ goto err;
+ if (X509_NAME_get_index_by_NID(subject_name, NID_commonName, lastpos)
+ != -1) {
+ /*
+ * Having multiple CN's is possible, and even happened back in
+ * the glory days of mullets and Hammer pants. In anything like
+ * a modern TLS cert, CN is as close to deprecated as it gets,
+ * and having more than one is bad. We therefore fail if we have
+ * more than one CN fed to us in the subject, treating the
+ * certificate as hostile.
+ */
+ tls_set_errorx(ctx, "error verifying name '%s': "
+ "Certificate subject contains multiple Common Name fields, "
+ "probably a malicious or malformed certificate", name);
goto err;
}
- X509_NAME_get_text_by_NID(subject_name, NID_commonName, common_name,
- common_name_len + 1);
-
- /* NUL bytes in CN? */
- if (common_name_len < 0 ||
- (size_t)common_name_len != strlen(common_name)) {
+ data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject_name,
+ lastpos));
+ /*
+ * Fail if we cannot encode the CN bytes as UTF-8.
+ */
+ if ((common_name_len = ASN1_STRING_to_UTF8(&utf8_bytes, data)) < 0) {
+ tls_set_errorx(ctx, "error verifying name '%s': "
+ "Common Name field cannot be encoded as a UTF-8 string, "
+ "probably a malicious certificate", name);
+ goto err;
+ }
+ /*
+ * Fail if the CN is of invalid length. RFC 5280 specifies that a CN
+ * must be between 1 and 64 bytes long.
+ */
+ if (common_name_len < 1 || common_name_len > 64) {
+ tls_set_errorx(ctx, "error verifying name '%s': "
+ "Common Name field has invalid length, "
+ "probably a malicious certificate", name);
+ goto err;
+ }
+ /*
+ * Fail if the resulting text contains a NUL byte.
+ */
+ if (memchr(utf8_bytes, 0, common_name_len) != NULL) {
tls_set_errorx(ctx, "error verifying name '%s': "
"NUL byte in Common Name field, "
"probably a malicious certificate", name);
goto err;
}
+ common_name = strndup(utf8_bytes, common_name_len);
+ if (common_name == NULL) {
+ tls_set_error(ctx, "out of memory");
+ goto err;
+ }
+
/*
* We don't want to attempt wildcard matching against IP addresses,
* so perform a simple comparison here.
@@ -260,6 +306,7 @@ tls_check_common_name(struct tls *ctx, X509 *cert, const char *name,
rv = 0;
err:
+ free(utf8_bytes);
free(common_name);
return rv;
}