sync libtls

This commit is contained in:
Omar Polo 2024-01-07 18:52:51 +00:00
parent 12eb29d878
commit ebfc578491
11 changed files with 645 additions and 188 deletions

View File

@ -13,6 +13,7 @@ DISTFILES = Makefile \
tls_ocsp.c \
tls_peer.c \
tls_server.c \
tls_signer.c \
tls_util.c \
tls_verify.c

View File

@ -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));
if (ASN1_time_tm_cmp(tm, &broken_os_epoch_tm) == 1)
memcpy(tm, &broken_os_epoch_tm, sizeof(*tm));
#endif
return 1;
}
#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

View File

@ -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;

View File

@ -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)

View File

@ -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)

View File

@ -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)
@ -734,6 +734,17 @@ tls_config_set_session_fd(struct tls_config *config, int session_fd)
return (0);
}
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)
{

View File

@ -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;

View File

@ -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

View File

@ -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;

443
compat/libtls/tls_signer.c Normal file
View File

@ -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);
}

View File

@ -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;
}