diff options
Diffstat (limited to 'compat/libtls')
-rw-r--r-- | compat/libtls/Makefile | 1 | ||||
-rw-r--r-- | compat/libtls/asn.c | 165 | ||||
-rw-r--r-- | compat/libtls/openssl.c | 2 | ||||
-rw-r--r-- | compat/libtls/tls.c | 46 | ||||
-rw-r--r-- | compat/libtls/tls.h | 14 | ||||
-rw-r--r-- | compat/libtls/tls_config.c | 17 | ||||
-rw-r--r-- | compat/libtls/tls_conninfo.c | 6 | ||||
-rw-r--r-- | compat/libtls/tls_internal.h | 31 | ||||
-rw-r--r-- | compat/libtls/tls_ocsp.c | 7 | ||||
-rw-r--r-- | compat/libtls/tls_signer.c | 443 | ||||
-rw-r--r-- | compat/libtls/tls_verify.c | 101 |
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(<m, 0, sizeof(ltm)); - lt = <m; - } - - /* 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; } |