check in v3.8.2 source

This commit is contained in:
2023-11-06 23:46:37 -08:00
parent e2db88c634
commit 2b68369a2b
1216 changed files with 563118 additions and 0 deletions

0
tls/empty.c Normal file
View File

931
tls/tls.c Normal file
View File

@@ -0,0 +1,931 @@
/* $OpenBSD: tls.c,v 1.98 2023/07/02 06:37:27 beck Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@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 <sys/socket.h>
#include <errno.h>
#include <limits.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/safestack.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <tls.h>
#include "tls_internal.h"
static struct tls_config *tls_config_default;
static int tls_init_rv = -1;
static void
tls_do_init(void)
{
OPENSSL_init_ssl(OPENSSL_INIT_NO_LOAD_CONFIG, NULL);
if (BIO_sock_init() != 1)
return;
if ((tls_config_default = tls_config_new_internal()) == NULL)
return;
tls_config_default->refcount++;
tls_init_rv = 0;
}
int
tls_init(void)
{
static pthread_once_t once = PTHREAD_ONCE_INIT;
if (pthread_once(&once, tls_do_init) != 0)
return -1;
return tls_init_rv;
}
const char *
tls_error(struct tls *ctx)
{
return ctx->error.msg;
}
void
tls_error_clear(struct tls_error *error)
{
free(error->msg);
error->msg = NULL;
error->num = 0;
error->tls = 0;
}
static int
tls_error_vset(struct tls_error *error, int errnum, const char *fmt, va_list ap)
{
char *errmsg = NULL;
int rv = -1;
tls_error_clear(error);
error->num = errnum;
error->tls = 1;
if (vasprintf(&errmsg, fmt, ap) == -1) {
errmsg = NULL;
goto err;
}
if (errnum == -1) {
error->msg = errmsg;
return (0);
}
if (asprintf(&error->msg, "%s: %s", errmsg, strerror(errnum)) == -1) {
error->msg = NULL;
goto err;
}
rv = 0;
err:
free(errmsg);
return (rv);
}
int
tls_error_set(struct tls_error *error, const char *fmt, ...)
{
va_list ap;
int errnum, rv;
errnum = errno;
va_start(ap, fmt);
rv = tls_error_vset(error, errnum, fmt, ap);
va_end(ap);
return (rv);
}
int
tls_error_setx(struct tls_error *error, const char *fmt, ...)
{
va_list ap;
int rv;
va_start(ap, fmt);
rv = tls_error_vset(error, -1, fmt, ap);
va_end(ap);
return (rv);
}
int
tls_config_set_error(struct tls_config *config, const char *fmt, ...)
{
va_list ap;
int errnum, rv;
errnum = errno;
va_start(ap, fmt);
rv = tls_error_vset(&config->error, errnum, fmt, ap);
va_end(ap);
return (rv);
}
int
tls_config_set_errorx(struct tls_config *config, const char *fmt, ...)
{
va_list ap;
int rv;
va_start(ap, fmt);
rv = tls_error_vset(&config->error, -1, fmt, ap);
va_end(ap);
return (rv);
}
int
tls_set_error(struct tls *ctx, const char *fmt, ...)
{
va_list ap;
int errnum, rv;
errnum = errno;
va_start(ap, fmt);
rv = tls_error_vset(&ctx->error, errnum, fmt, ap);
va_end(ap);
return (rv);
}
int
tls_set_errorx(struct tls *ctx, const char *fmt, ...)
{
va_list ap;
int rv;
va_start(ap, fmt);
rv = tls_error_vset(&ctx->error, -1, fmt, ap);
va_end(ap);
return (rv);
}
int
tls_set_ssl_errorx(struct tls *ctx, const char *fmt, ...)
{
va_list ap;
int rv;
/* Only set an error if a more specific one does not already exist. */
if (ctx->error.tls != 0)
return (0);
va_start(ap, fmt);
rv = tls_error_vset(&ctx->error, -1, fmt, ap);
va_end(ap);
return (rv);
}
struct tls_sni_ctx *
tls_sni_ctx_new(void)
{
return (calloc(1, sizeof(struct tls_sni_ctx)));
}
void
tls_sni_ctx_free(struct tls_sni_ctx *sni_ctx)
{
if (sni_ctx == NULL)
return;
SSL_CTX_free(sni_ctx->ssl_ctx);
X509_free(sni_ctx->ssl_cert);
free(sni_ctx);
}
struct tls *
tls_new(void)
{
struct tls *ctx;
if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
return (NULL);
tls_reset(ctx);
if (tls_configure(ctx, tls_config_default) == -1) {
free(ctx);
return NULL;
}
return (ctx);
}
int
tls_configure(struct tls *ctx, struct tls_config *config)
{
if (config == NULL)
config = tls_config_default;
pthread_mutex_lock(&config->mutex);
config->refcount++;
pthread_mutex_unlock(&config->mutex);
tls_config_free(ctx->config);
ctx->config = config;
ctx->keypair = config->keypair;
if ((ctx->flags & TLS_SERVER) != 0)
return (tls_configure_server(ctx));
return (0);
}
int
tls_cert_hash(X509 *cert, char **hash)
{
char d[EVP_MAX_MD_SIZE], *dhex = NULL;
int dlen, rv = -1;
free(*hash);
*hash = NULL;
if (X509_digest(cert, EVP_sha256(), d, &dlen) != 1)
goto err;
if (tls_hex_string(d, dlen, &dhex, NULL) != 0)
goto err;
if (asprintf(hash, "SHA256:%s", dhex) == -1) {
*hash = NULL;
goto err;
}
rv = 0;
err:
free(dhex);
return (rv);
}
int
tls_cert_pubkey_hash(X509 *cert, char **hash)
{
char d[EVP_MAX_MD_SIZE], *dhex = NULL;
int dlen, rv = -1;
free(*hash);
*hash = NULL;
if (X509_pubkey_digest(cert, EVP_sha256(), d, &dlen) != 1)
goto err;
if (tls_hex_string(d, dlen, &dhex, NULL) != 0)
goto err;
if (asprintf(hash, "SHA256:%s", dhex) == -1) {
*hash = NULL;
goto err;
}
rv = 0;
err:
free(dhex);
return (rv);
}
static int
tls_keypair_to_pkey(struct tls *ctx, struct tls_keypair *keypair, EVP_PKEY **pkey)
{
BIO *bio = NULL;
X509 *x509 = NULL;
char *mem;
size_t len;
int ret = -1;
*pkey = NULL;
if (ctx->config->use_fake_private_key) {
mem = keypair->cert_mem;
len = keypair->cert_len;
} else {
mem = keypair->key_mem;
len = keypair->key_len;
}
if (mem == NULL)
return (0);
if (len > INT_MAX) {
tls_set_errorx(ctx, ctx->config->use_fake_private_key ?
"cert too long" : "key too long");
goto err;
}
if ((bio = BIO_new_mem_buf(mem, len)) == NULL) {
tls_set_errorx(ctx, "failed to create buffer");
goto err;
}
if (ctx->config->use_fake_private_key) {
if ((x509 = PEM_read_bio_X509(bio, NULL, tls_password_cb,
NULL)) == NULL) {
tls_set_errorx(ctx, "failed to read X509 certificate");
goto err;
}
if ((*pkey = X509_get_pubkey(x509)) == NULL) {
tls_set_errorx(ctx, "failed to retrieve pubkey");
goto err;
}
} else {
if ((*pkey = PEM_read_bio_PrivateKey(bio, NULL, tls_password_cb,
NULL)) == NULL) {
tls_set_errorx(ctx, "failed to read private key");
goto err;
}
}
ret = 0;
err:
BIO_free(bio);
X509_free(x509);
return (ret);
}
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;
/* Only install the pubkey hash if fake private keys are used. */
if (!ctx->config->skip_private_key_check)
return (0);
if (keypair->pubkey_hash == NULL) {
tls_set_errorx(ctx, "public key hash not set");
goto err;
}
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) {
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) {
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");
goto err;
}
ret = 0;
err:
RSA_free(rsa);
EC_KEY_free(eckey);
return (ret);
}
int
tls_configure_ssl_keypair(struct tls *ctx, SSL_CTX *ssl_ctx,
struct tls_keypair *keypair, int required)
{
EVP_PKEY *pkey = NULL;
if (!required &&
keypair->cert_mem == NULL &&
keypair->key_mem == NULL)
return(0);
if (keypair->cert_mem != NULL) {
if (keypair->cert_len > INT_MAX) {
tls_set_errorx(ctx, "certificate too long");
goto err;
}
if (SSL_CTX_use_certificate_chain_mem(ssl_ctx,
keypair->cert_mem, keypair->cert_len) != 1) {
tls_set_errorx(ctx, "failed to load certificate");
goto err;
}
}
if (tls_keypair_to_pkey(ctx, keypair, &pkey) == -1)
goto err;
if (pkey != NULL) {
if (tls_keypair_setup_pkey(ctx, keypair, pkey) == -1)
goto err;
if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1) {
tls_set_errorx(ctx, "failed to load private key");
goto err;
}
EVP_PKEY_free(pkey);
pkey = NULL;
}
if (!ctx->config->skip_private_key_check &&
SSL_CTX_check_private_key(ssl_ctx) != 1) {
tls_set_errorx(ctx, "private/public key mismatch");
goto err;
}
return (0);
err:
EVP_PKEY_free(pkey);
return (-1);
}
int
tls_configure_ssl(struct tls *ctx, SSL_CTX *ssl_ctx)
{
SSL_CTX_clear_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
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_2);
SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TLSv1_3);
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)
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_3);
if (ctx->config->alpn != NULL) {
if (SSL_CTX_set_alpn_protos(ssl_ctx, ctx->config->alpn,
ctx->config->alpn_len) != 0) {
tls_set_errorx(ctx, "failed to set alpn");
goto err;
}
}
if (ctx->config->ciphers != NULL) {
if (SSL_CTX_set_cipher_list(ssl_ctx,
ctx->config->ciphers) != 1) {
tls_set_errorx(ctx, "failed to set ciphers");
goto err;
}
}
if (ctx->config->verify_time == 0) {
X509_VERIFY_PARAM_set_flags(SSL_CTX_get0_param(ssl_ctx),
X509_V_FLAG_NO_CHECK_TIME);
}
/* Disable any form of session caching by default */
SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_OFF);
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET);
return (0);
err:
return (-1);
}
static int
tls_ssl_cert_verify_cb(X509_STORE_CTX *x509_ctx, void *arg)
{
struct tls *ctx = arg;
int x509_err;
if (ctx->config->verify_cert == 0)
return (1);
if ((X509_verify_cert(x509_ctx)) < 0) {
tls_set_errorx(ctx, "X509 verify cert failed");
return (0);
}
x509_err = X509_STORE_CTX_get_error(x509_ctx);
if (x509_err == X509_V_OK)
return (1);
tls_set_errorx(ctx, "certificate verification failed: %s",
X509_verify_cert_error_string(x509_err));
return (0);
}
int
tls_configure_ssl_verify(struct tls *ctx, SSL_CTX *ssl_ctx, int verify)
{
size_t ca_len = ctx->config->ca_len;
char *ca_mem = ctx->config->ca_mem;
char *crl_mem = ctx->config->crl_mem;
size_t crl_len = ctx->config->crl_len;
char *ca_free = NULL;
STACK_OF(X509_INFO) *xis = NULL;
X509_STORE *store;
X509_INFO *xi;
BIO *bio = NULL;
int rv = -1;
int i;
SSL_CTX_set_verify(ssl_ctx, verify, NULL);
SSL_CTX_set_cert_verify_callback(ssl_ctx, tls_ssl_cert_verify_cb, ctx);
if (ctx->config->verify_depth >= 0)
SSL_CTX_set_verify_depth(ssl_ctx, ctx->config->verify_depth);
if (ctx->config->verify_cert == 0)
goto done;
/* If no CA has been specified, attempt to load the default. */
if (ctx->config->ca_mem == NULL && ctx->config->ca_path == NULL) {
if (tls_config_load_file(&ctx->error, "CA", tls_default_ca_cert_file(),
&ca_mem, &ca_len) != 0)
goto err;
ca_free = ca_mem;
}
if (ca_mem != NULL) {
if (ca_len > INT_MAX) {
tls_set_errorx(ctx, "ca too long");
goto err;
}
if (SSL_CTX_load_verify_mem(ssl_ctx, ca_mem, ca_len) != 1) {
tls_set_errorx(ctx, "ssl verify memory setup failure");
goto err;
}
} else if (SSL_CTX_load_verify_locations(ssl_ctx, NULL,
ctx->config->ca_path) != 1) {
tls_set_errorx(ctx, "ssl verify locations failure");
goto err;
}
if (crl_mem != NULL) {
if (crl_len > INT_MAX) {
tls_set_errorx(ctx, "crl too long");
goto err;
}
if ((bio = BIO_new_mem_buf(crl_mem, crl_len)) == NULL) {
tls_set_errorx(ctx, "failed to create buffer");
goto err;
}
if ((xis = PEM_X509_INFO_read_bio(bio, NULL, tls_password_cb,
NULL)) == NULL) {
tls_set_errorx(ctx, "failed to parse crl");
goto err;
}
store = SSL_CTX_get_cert_store(ssl_ctx);
for (i = 0; i < sk_X509_INFO_num(xis); i++) {
xi = sk_X509_INFO_value(xis, i);
if (xi->crl == NULL)
continue;
if (!X509_STORE_add_crl(store, xi->crl)) {
tls_set_error(ctx, "failed to add crl");
goto err;
}
}
X509_STORE_set_flags(store,
X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
}
done:
rv = 0;
err:
sk_X509_INFO_pop_free(xis, X509_INFO_free);
BIO_free(bio);
free(ca_free);
return (rv);
}
void
tls_free(struct tls *ctx)
{
if (ctx == NULL)
return;
tls_reset(ctx);
free(ctx);
}
void
tls_reset(struct tls *ctx)
{
struct tls_sni_ctx *sni, *nsni;
tls_config_free(ctx->config);
ctx->config = NULL;
SSL_CTX_free(ctx->ssl_ctx);
SSL_free(ctx->ssl_conn);
X509_free(ctx->ssl_peer_cert);
ctx->ssl_conn = NULL;
ctx->ssl_ctx = NULL;
ctx->ssl_peer_cert = NULL;
/* X509 objects in chain are freed with the SSL */
ctx->ssl_peer_chain = NULL;
ctx->socket = -1;
ctx->state = 0;
free(ctx->servername);
ctx->servername = NULL;
free(ctx->error.msg);
ctx->error.msg = NULL;
ctx->error.num = -1;
tls_conninfo_free(ctx->conninfo);
ctx->conninfo = NULL;
tls_ocsp_free(ctx->ocsp);
ctx->ocsp = NULL;
for (sni = ctx->sni_ctx; sni != NULL; sni = nsni) {
nsni = sni->next;
tls_sni_ctx_free(sni);
}
ctx->sni_ctx = NULL;
ctx->read_cb = NULL;
ctx->write_cb = NULL;
ctx->cb_arg = NULL;
}
int
tls_ssl_error(struct tls *ctx, SSL *ssl_conn, int ssl_ret, const char *prefix)
{
const char *errstr = "unknown error";
unsigned long err;
int ssl_err;
ssl_err = SSL_get_error(ssl_conn, ssl_ret);
switch (ssl_err) {
case SSL_ERROR_NONE:
case SSL_ERROR_ZERO_RETURN:
return (0);
case SSL_ERROR_WANT_READ:
return (TLS_WANT_POLLIN);
case SSL_ERROR_WANT_WRITE:
return (TLS_WANT_POLLOUT);
case SSL_ERROR_SYSCALL:
if ((err = ERR_peek_error()) != 0) {
errstr = ERR_error_string(err, NULL);
} else if (ssl_ret == 0) {
if ((ctx->state & TLS_HANDSHAKE_COMPLETE) != 0) {
ctx->state |= TLS_EOF_NO_CLOSE_NOTIFY;
return (0);
}
errstr = "unexpected EOF";
} else if (ssl_ret == -1) {
errstr = strerror(errno);
}
tls_set_ssl_errorx(ctx, "%s failed: %s", prefix, errstr);
return (-1);
case SSL_ERROR_SSL:
if ((err = ERR_peek_error()) != 0) {
errstr = ERR_error_string(err, NULL);
}
tls_set_ssl_errorx(ctx, "%s failed: %s", prefix, errstr);
return (-1);
case SSL_ERROR_WANT_CONNECT:
case SSL_ERROR_WANT_ACCEPT:
case SSL_ERROR_WANT_X509_LOOKUP:
default:
tls_set_ssl_errorx(ctx, "%s failed (%d)", prefix, ssl_err);
return (-1);
}
}
int
tls_handshake(struct tls *ctx)
{
int rv = -1;
tls_error_clear(&ctx->error);
if ((ctx->flags & (TLS_CLIENT | TLS_SERVER_CONN)) == 0) {
tls_set_errorx(ctx, "invalid operation for context");
goto out;
}
if ((ctx->state & TLS_HANDSHAKE_COMPLETE) != 0) {
tls_set_errorx(ctx, "handshake already completed");
goto out;
}
if ((ctx->flags & TLS_CLIENT) != 0)
rv = tls_handshake_client(ctx);
else if ((ctx->flags & TLS_SERVER_CONN) != 0)
rv = tls_handshake_server(ctx);
if (rv == 0) {
ctx->ssl_peer_cert = SSL_get_peer_certificate(ctx->ssl_conn);
ctx->ssl_peer_chain = SSL_get_peer_cert_chain(ctx->ssl_conn);
if (tls_conninfo_populate(ctx) == -1)
rv = -1;
if (ctx->ocsp == NULL)
ctx->ocsp = tls_ocsp_setup_from_peer(ctx);
}
out:
/* Prevent callers from performing incorrect error handling */
errno = 0;
return (rv);
}
ssize_t
tls_read(struct tls *ctx, void *buf, size_t buflen)
{
ssize_t rv = -1;
int ssl_ret;
tls_error_clear(&ctx->error);
if ((ctx->state & TLS_HANDSHAKE_COMPLETE) == 0) {
if ((rv = tls_handshake(ctx)) != 0)
goto out;
}
if (buflen > INT_MAX) {
tls_set_errorx(ctx, "buflen too long");
goto out;
}
ERR_clear_error();
if ((ssl_ret = SSL_read(ctx->ssl_conn, buf, buflen)) > 0) {
rv = (ssize_t)ssl_ret;
goto out;
}
rv = (ssize_t)tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "read");
out:
/* Prevent callers from performing incorrect error handling */
errno = 0;
return (rv);
}
ssize_t
tls_write(struct tls *ctx, const void *buf, size_t buflen)
{
ssize_t rv = -1;
int ssl_ret;
tls_error_clear(&ctx->error);
if ((ctx->state & TLS_HANDSHAKE_COMPLETE) == 0) {
if ((rv = tls_handshake(ctx)) != 0)
goto out;
}
if (buflen > INT_MAX) {
tls_set_errorx(ctx, "buflen too long");
goto out;
}
ERR_clear_error();
if ((ssl_ret = SSL_write(ctx->ssl_conn, buf, buflen)) > 0) {
rv = (ssize_t)ssl_ret;
goto out;
}
rv = (ssize_t)tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "write");
out:
/* Prevent callers from performing incorrect error handling */
errno = 0;
return (rv);
}
int
tls_close(struct tls *ctx)
{
int ssl_ret;
int rv = 0;
tls_error_clear(&ctx->error);
if ((ctx->flags & (TLS_CLIENT | TLS_SERVER_CONN)) == 0) {
tls_set_errorx(ctx, "invalid operation for context");
rv = -1;
goto out;
}
if (ctx->state & TLS_SSL_NEEDS_SHUTDOWN) {
ERR_clear_error();
ssl_ret = SSL_shutdown(ctx->ssl_conn);
if (ssl_ret < 0) {
rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret,
"shutdown");
if (rv == TLS_WANT_POLLIN || rv == TLS_WANT_POLLOUT)
goto out;
}
ctx->state &= ~TLS_SSL_NEEDS_SHUTDOWN;
}
if (ctx->socket != -1) {
if (shutdown(ctx->socket, SHUT_RDWR) != 0) {
if (rv == 0 &&
errno != ENOTCONN && errno != ECONNRESET) {
tls_set_error(ctx, "shutdown");
rv = -1;
}
}
if (close(ctx->socket) != 0) {
if (rv == 0) {
tls_set_error(ctx, "close");
rv = -1;
}
}
ctx->socket = -1;
}
if ((ctx->state & TLS_EOF_NO_CLOSE_NOTIFY) != 0) {
tls_set_errorx(ctx, "EOF without close notify");
rv = -1;
}
out:
/* Prevent callers from performing incorrect error handling */
errno = 0;
return (rv);
}

171
tls/tls_bio_cb.c Normal file
View File

@@ -0,0 +1,171 @@
/* $OpenBSD: tls_bio_cb.c,v 1.21 2023/05/14 07:26:25 op Exp $ */
/*
* Copyright (c) 2016 Tobias Pape <tobias@netshed.de>
*
* 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 <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/bio.h>
#include <tls.h>
#include "tls_internal.h"
static int bio_cb_write(BIO *bio, const char *buf, int num);
static int bio_cb_read(BIO *bio, char *buf, int size);
static int bio_cb_puts(BIO *bio, const char *str);
static long bio_cb_ctrl(BIO *bio, int cmd, long num, void *ptr);
static BIO_METHOD *bio_cb_method;
static pthread_mutex_t bio_cb_method_lock = PTHREAD_MUTEX_INITIALIZER;
static void
bio_cb_method_init(void)
{
BIO_METHOD *bio_method;
if (bio_cb_method != NULL)
return;
bio_method = BIO_meth_new(BIO_TYPE_MEM, "libtls_callbacks");
if (bio_method == NULL)
return;
BIO_meth_set_write(bio_method, bio_cb_write);
BIO_meth_set_read(bio_method, bio_cb_read);
BIO_meth_set_puts(bio_method, bio_cb_puts);
BIO_meth_set_ctrl(bio_method, bio_cb_ctrl);
bio_cb_method = bio_method;
}
static BIO_METHOD *
bio_s_cb(void)
{
if (bio_cb_method != NULL)
return (bio_cb_method);
pthread_mutex_lock(&bio_cb_method_lock);
bio_cb_method_init();
pthread_mutex_unlock(&bio_cb_method_lock);
return (bio_cb_method);
}
static int
bio_cb_puts(BIO *bio, const char *str)
{
return (bio_cb_write(bio, str, strlen(str)));
}
static long
bio_cb_ctrl(BIO *bio, int cmd, long num, void *ptr)
{
long ret = 1;
switch (cmd) {
case BIO_CTRL_GET_CLOSE:
ret = (long)BIO_get_shutdown(bio);
break;
case BIO_CTRL_SET_CLOSE:
BIO_set_shutdown(bio, (int)num);
break;
case BIO_CTRL_DUP:
case BIO_CTRL_FLUSH:
break;
case BIO_CTRL_INFO:
case BIO_CTRL_GET:
case BIO_CTRL_SET:
default:
ret = BIO_ctrl(BIO_next(bio), cmd, num, ptr);
}
return (ret);
}
static int
bio_cb_write(BIO *bio, const char *buf, int num)
{
struct tls *ctx = BIO_get_data(bio);
int rv;
BIO_clear_retry_flags(bio);
rv = (ctx->write_cb)(ctx, buf, num, ctx->cb_arg);
if (rv == TLS_WANT_POLLIN) {
BIO_set_retry_read(bio);
rv = -1;
} else if (rv == TLS_WANT_POLLOUT) {
BIO_set_retry_write(bio);
rv = -1;
}
return (rv);
}
static int
bio_cb_read(BIO *bio, char *buf, int size)
{
struct tls *ctx = BIO_get_data(bio);
int rv;
BIO_clear_retry_flags(bio);
rv = (ctx->read_cb)(ctx, buf, size, ctx->cb_arg);
if (rv == TLS_WANT_POLLIN) {
BIO_set_retry_read(bio);
rv = -1;
} else if (rv == TLS_WANT_POLLOUT) {
BIO_set_retry_write(bio);
rv = -1;
}
return (rv);
}
int
tls_set_cbs(struct tls *ctx, tls_read_cb read_cb, tls_write_cb write_cb,
void *cb_arg)
{
const BIO_METHOD *bio_cb;
BIO *bio;
int rv = -1;
if (read_cb == NULL || write_cb == NULL) {
tls_set_errorx(ctx, "no callbacks provided");
goto err;
}
ctx->read_cb = read_cb;
ctx->write_cb = write_cb;
ctx->cb_arg = cb_arg;
if ((bio_cb = bio_s_cb()) == NULL) {
tls_set_errorx(ctx, "failed to create callback method");
goto err;
}
if ((bio = BIO_new(bio_cb)) == NULL) {
tls_set_errorx(ctx, "failed to create callback i/o");
goto err;
}
BIO_set_data(bio, ctx);
BIO_set_init(bio, 1);
SSL_set_bio(ctx->ssl_conn, bio, bio);
rv = 0;
err:
return (rv);
}

485
tls/tls_client.c Normal file
View File

@@ -0,0 +1,485 @@
/* $OpenBSD: tls_client.c,v 1.49 2023/05/14 07:26:25 op Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@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 <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <limits.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/err.h>
#include <openssl/x509.h>
#include <tls.h>
#include "tls_internal.h"
struct tls *
tls_client(void)
{
struct tls *ctx;
if (tls_init() == -1)
return (NULL);
if ((ctx = tls_new()) == NULL)
return (NULL);
ctx->flags |= TLS_CLIENT;
return (ctx);
}
int
tls_connect(struct tls *ctx, const char *host, const char *port)
{
return tls_connect_servername(ctx, host, port, NULL);
}
int
tls_connect_servername(struct tls *ctx, const char *host, const char *port,
const char *servername)
{
struct addrinfo hints, *res, *res0;
const char *h = NULL, *p = NULL;
char *hs = NULL, *ps = NULL;
int rv = -1, s = -1, ret;
if ((ctx->flags & TLS_CLIENT) == 0) {
tls_set_errorx(ctx, "not a client context");
goto err;
}
if (host == NULL) {
tls_set_errorx(ctx, "host not specified");
goto err;
}
/* If port is NULL, try to extract a port from the specified host. */
if (port == NULL) {
ret = tls_host_port(host, &hs, &ps);
if (ret == -1) {
tls_set_errorx(ctx, "memory allocation failure");
goto err;
}
if (ret != 0) {
tls_set_errorx(ctx, "no port provided");
goto err;
}
}
h = (hs != NULL) ? hs : host;
p = (ps != NULL) ? ps : port;
/*
* First check if the host is specified as a numeric IP address,
* either IPv4 or IPv6, before trying to resolve the host.
* The AI_ADDRCONFIG resolver option will not return IPv4 or IPv6
* records if it is not configured on an interface; not considering
* loopback addresses. Checking the numeric addresses first makes
* sure that connection attempts to numeric addresses and especially
* 127.0.0.1 or ::1 loopback addresses are always possible.
*/
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
/* try as an IPv4 literal */
hints.ai_family = AF_INET;
hints.ai_flags = AI_NUMERICHOST;
if (getaddrinfo(h, p, &hints, &res0) != 0) {
/* try again as an IPv6 literal */
hints.ai_family = AF_INET6;
if (getaddrinfo(h, p, &hints, &res0) != 0) {
/* last try, with name resolution and save the error */
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_ADDRCONFIG;
if ((s = getaddrinfo(h, p, &hints, &res0)) != 0) {
tls_set_error(ctx, "%s", gai_strerror(s));
goto err;
}
}
}
/* It was resolved somehow; now try connecting to what we got */
s = -1;
for (res = res0; res; res = res->ai_next) {
s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (s == -1) {
tls_set_error(ctx, "socket");
continue;
}
if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
tls_set_error(ctx, "connect");
close(s);
s = -1;
continue;
}
break; /* Connected. */
}
freeaddrinfo(res0);
if (s == -1)
goto err;
if (servername == NULL)
servername = h;
if (tls_connect_socket(ctx, s, servername) != 0) {
close(s);
goto err;
}
ctx->socket = s;
rv = 0;
err:
free(hs);
free(ps);
return (rv);
}
static int
tls_client_read_session(struct tls *ctx)
{
int sfd = ctx->config->session_fd;
uint8_t *session = NULL;
size_t session_len = 0;
SSL_SESSION *ss = NULL;
BIO *bio = NULL;
struct stat sb;
ssize_t n;
int rv = -1;
if (fstat(sfd, &sb) == -1) {
tls_set_error(ctx, "failed to stat session file");
goto err;
}
if (sb.st_size < 0 || sb.st_size > INT_MAX) {
tls_set_errorx(ctx, "invalid session file size");
goto err;
}
session_len = (size_t)sb.st_size;
/* A zero size file means that we do not yet have a valid session. */
if (session_len == 0)
goto done;
if ((session = malloc(session_len)) == NULL)
goto err;
n = pread(sfd, session, session_len, 0);
if (n < 0 || (size_t)n != session_len) {
tls_set_error(ctx, "failed to read session file");
goto err;
}
if ((bio = BIO_new_mem_buf(session, session_len)) == NULL)
goto err;
if ((ss = PEM_read_bio_SSL_SESSION(bio, NULL, tls_password_cb,
NULL)) == NULL) {
tls_set_errorx(ctx, "failed to parse session");
goto err;
}
if (SSL_set_session(ctx->ssl_conn, ss) != 1) {
tls_set_errorx(ctx, "failed to set session");
goto err;
}
done:
rv = 0;
err:
freezero(session, session_len);
SSL_SESSION_free(ss);
BIO_free(bio);
return rv;
}
static int
tls_client_write_session(struct tls *ctx)
{
int sfd = ctx->config->session_fd;
SSL_SESSION *ss = NULL;
BIO *bio = NULL;
long data_len;
char *data;
off_t offset;
size_t len;
ssize_t n;
int rv = -1;
if ((ss = SSL_get1_session(ctx->ssl_conn)) == NULL) {
if (ftruncate(sfd, 0) == -1) {
tls_set_error(ctx, "failed to truncate session file");
goto err;
}
goto done;
}
if ((bio = BIO_new(BIO_s_mem())) == NULL)
goto err;
if (PEM_write_bio_SSL_SESSION(bio, ss) == 0)
goto err;
if ((data_len = BIO_get_mem_data(bio, &data)) <= 0)
goto err;
len = (size_t)data_len;
offset = 0;
if (ftruncate(sfd, len) == -1) {
tls_set_error(ctx, "failed to truncate session file");
goto err;
}
while (len > 0) {
if ((n = pwrite(sfd, data + offset, len, offset)) == -1) {
tls_set_error(ctx, "failed to write session file");
goto err;
}
offset += n;
len -= n;
}
done:
rv = 0;
err:
SSL_SESSION_free(ss);
BIO_free_all(bio);
return (rv);
}
static int
tls_connect_common(struct tls *ctx, const char *servername)
{
union tls_addr addrbuf;
size_t servername_len;
int rv = -1;
if ((ctx->flags & TLS_CLIENT) == 0) {
tls_set_errorx(ctx, "not a client context");
goto err;
}
if (servername != NULL) {
if ((ctx->servername = strdup(servername)) == NULL) {
tls_set_errorx(ctx, "out of memory");
goto err;
}
/*
* If there's a trailing dot, remove it. While an FQDN includes
* the terminating dot representing the zero-length label of
* the root (RFC 8499, section 2), the SNI explicitly does not
* include it (RFC 6066, section 3).
*/
servername_len = strlen(ctx->servername);
if (servername_len > 0 &&
ctx->servername[servername_len - 1] == '.')
ctx->servername[servername_len - 1] = '\0';
}
if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) {
tls_set_errorx(ctx, "ssl context failure");
goto err;
}
if (tls_configure_ssl(ctx, ctx->ssl_ctx) != 0)
goto err;
if (tls_configure_ssl_keypair(ctx, ctx->ssl_ctx,
ctx->config->keypair, 0) != 0)
goto err;
if (ctx->config->verify_name) {
if (ctx->servername == NULL) {
tls_set_errorx(ctx, "server name not specified");
goto err;
}
}
if (tls_configure_ssl_verify(ctx, ctx->ssl_ctx, SSL_VERIFY_PEER) == -1)
goto err;
if (ctx->config->ecdhecurves != NULL) {
if (SSL_CTX_set1_groups(ctx->ssl_ctx, ctx->config->ecdhecurves,
ctx->config->ecdhecurves_len) != 1) {
tls_set_errorx(ctx, "failed to set ecdhe curves");
goto err;
}
}
if (SSL_CTX_set_tlsext_status_cb(ctx->ssl_ctx, tls_ocsp_verify_cb) != 1) {
tls_set_errorx(ctx, "ssl OCSP verification setup failure");
goto err;
}
if ((ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) {
tls_set_errorx(ctx, "ssl connection failure");
goto err;
}
if (SSL_set_app_data(ctx->ssl_conn, ctx) != 1) {
tls_set_errorx(ctx, "ssl application data failure");
goto err;
}
if (ctx->config->session_fd != -1) {
SSL_clear_options(ctx->ssl_conn, SSL_OP_NO_TICKET);
if (tls_client_read_session(ctx) == -1)
goto err;
}
if (SSL_set_tlsext_status_type(ctx->ssl_conn, TLSEXT_STATUSTYPE_ocsp) != 1) {
tls_set_errorx(ctx, "ssl OCSP extension setup failure");
goto err;
}
/*
* RFC 6066 (SNI): Literal IPv4 and IPv6 addresses are not
* permitted in "HostName".
*/
if (ctx->servername != NULL &&
inet_pton(AF_INET, ctx->servername, &addrbuf) != 1 &&
inet_pton(AF_INET6, ctx->servername, &addrbuf) != 1) {
if (SSL_set_tlsext_host_name(ctx->ssl_conn,
ctx->servername) == 0) {
tls_set_errorx(ctx, "server name indication failure");
goto err;
}
}
ctx->state |= TLS_CONNECTED;
rv = 0;
err:
return (rv);
}
int
tls_connect_socket(struct tls *ctx, int s, const char *servername)
{
return tls_connect_fds(ctx, s, s, servername);
}
int
tls_connect_fds(struct tls *ctx, int fd_read, int fd_write,
const char *servername)
{
int rv = -1;
if (fd_read < 0 || fd_write < 0) {
tls_set_errorx(ctx, "invalid file descriptors");
goto err;
}
if (tls_connect_common(ctx, servername) != 0)
goto err;
if (SSL_set_rfd(ctx->ssl_conn, fd_read) != 1 ||
SSL_set_wfd(ctx->ssl_conn, fd_write) != 1) {
tls_set_errorx(ctx, "ssl file descriptor failure");
goto err;
}
rv = 0;
err:
return (rv);
}
int
tls_connect_cbs(struct tls *ctx, tls_read_cb read_cb,
tls_write_cb write_cb, void *cb_arg, const char *servername)
{
int rv = -1;
if (tls_connect_common(ctx, servername) != 0)
goto err;
if (tls_set_cbs(ctx, read_cb, write_cb, cb_arg) != 0)
goto err;
rv = 0;
err:
return (rv);
}
int
tls_handshake_client(struct tls *ctx)
{
X509 *cert = NULL;
int match, ssl_ret;
int rv = -1;
if ((ctx->flags & TLS_CLIENT) == 0) {
tls_set_errorx(ctx, "not a client context");
goto err;
}
if ((ctx->state & TLS_CONNECTED) == 0) {
tls_set_errorx(ctx, "context not connected");
goto err;
}
ctx->state |= TLS_SSL_NEEDS_SHUTDOWN;
ERR_clear_error();
if ((ssl_ret = SSL_connect(ctx->ssl_conn)) != 1) {
rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "handshake");
goto err;
}
if (ctx->config->verify_name) {
cert = SSL_get_peer_certificate(ctx->ssl_conn);
if (cert == NULL) {
tls_set_errorx(ctx, "no server certificate");
goto err;
}
if (tls_check_name(ctx, cert, ctx->servername, &match) == -1)
goto err;
if (!match) {
tls_set_errorx(ctx, "name `%s' not present in"
" server certificate", ctx->servername);
goto err;
}
}
ctx->state |= TLS_HANDSHAKE_COMPLETE;
if (ctx->config->session_fd != -1) {
if (tls_client_write_session(ctx) == -1)
goto err;
}
rv = 0;
err:
X509_free(cert);
return (rv);
}

930
tls/tls_config.c Normal file
View File

@@ -0,0 +1,930 @@
/* $OpenBSD: tls_config.c,v 1.67 2023/07/02 06:37:27 beck Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@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 <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <tls.h>
#include "tls_internal.h"
static const char default_ca_file[] = TLS_DEFAULT_CA_FILE;
const char *
tls_default_ca_cert_file(void)
{
return default_ca_file;
}
int
tls_config_load_file(struct tls_error *error, const char *filetype,
const char *filename, char **buf, size_t *len)
{
struct stat st;
int fd = -1;
ssize_t n;
free(*buf);
*buf = NULL;
*len = 0;
if ((fd = open(filename, O_RDONLY)) == -1) {
tls_error_set(error, "failed to open %s file '%s'",
filetype, filename);
goto err;
}
if (fstat(fd, &st) != 0) {
tls_error_set(error, "failed to stat %s file '%s'",
filetype, filename);
goto err;
}
if (st.st_size < 0)
goto err;
*len = (size_t)st.st_size;
if ((*buf = malloc(*len)) == NULL) {
tls_error_set(error, "failed to allocate buffer for "
"%s file", filetype);
goto err;
}
n = read(fd, *buf, *len);
if (n < 0 || (size_t)n != *len) {
tls_error_set(error, "failed to read %s file '%s'",
filetype, filename);
goto err;
}
close(fd);
return 0;
err:
if (fd != -1)
close(fd);
freezero(*buf, *len);
*buf = NULL;
*len = 0;
return -1;
}
struct tls_config *
tls_config_new_internal(void)
{
struct tls_config *config;
unsigned char sid[TLS_MAX_SESSION_ID_LENGTH];
if ((config = calloc(1, sizeof(*config))) == NULL)
return (NULL);
if (pthread_mutex_init(&config->mutex, NULL) != 0)
goto err;
config->refcount = 1;
config->session_fd = -1;
if ((config->keypair = tls_keypair_new()) == NULL)
goto err;
/*
* Default configuration.
*/
if (tls_config_set_dheparams(config, "none") != 0)
goto err;
if (tls_config_set_ecdhecurves(config, "default") != 0)
goto err;
if (tls_config_set_ciphers(config, "secure") != 0)
goto err;
if (tls_config_set_protocols(config, TLS_PROTOCOLS_DEFAULT) != 0)
goto err;
if (tls_config_set_verify_depth(config, 6) != 0)
goto err;
/*
* Set session ID context to a random value. For the simple case
* of a single process server this is good enough. For multiprocess
* servers the session ID needs to be set by the caller.
*/
arc4random_buf(sid, sizeof(sid));
if (tls_config_set_session_id(config, sid, sizeof(sid)) != 0)
goto err;
config->ticket_keyrev = arc4random();
config->ticket_autorekey = 1;
tls_config_prefer_ciphers_server(config);
tls_config_verify(config);
return (config);
err:
tls_config_free(config);
return (NULL);
}
struct tls_config *
tls_config_new(void)
{
if (tls_init() == -1)
return (NULL);
return tls_config_new_internal();
}
void
tls_config_free(struct tls_config *config)
{
struct tls_keypair *kp, *nkp;
int refcount;
if (config == NULL)
return;
pthread_mutex_lock(&config->mutex);
refcount = --config->refcount;
pthread_mutex_unlock(&config->mutex);
if (refcount > 0)
return;
for (kp = config->keypair; kp != NULL; kp = nkp) {
nkp = kp->next;
tls_keypair_free(kp);
}
free(config->error.msg);
free(config->alpn);
free((char *)config->ca_mem);
free((char *)config->ca_path);
free((char *)config->ciphers);
free((char *)config->crl_mem);
free(config->ecdhecurves);
pthread_mutex_destroy(&config->mutex);
free(config);
}
static void
tls_config_keypair_add(struct tls_config *config, struct tls_keypair *keypair)
{
struct tls_keypair *kp;
kp = config->keypair;
while (kp->next != NULL)
kp = kp->next;
kp->next = keypair;
}
const char *
tls_config_error(struct tls_config *config)
{
return config->error.msg;
}
void
tls_config_clear_keys(struct tls_config *config)
{
struct tls_keypair *kp;
for (kp = config->keypair; kp != NULL; kp = kp->next)
tls_keypair_clear_key(kp);
}
int
tls_config_parse_protocols(uint32_t *protocols, const char *protostr)
{
uint32_t proto, protos = 0;
char *s, *p, *q;
int negate;
if (protostr == NULL) {
*protocols = TLS_PROTOCOLS_DEFAULT;
return (0);
}
if ((s = strdup(protostr)) == NULL)
return (-1);
q = s;
while ((p = strsep(&q, ",:")) != NULL) {
while (*p == ' ' || *p == '\t')
p++;
negate = 0;
if (*p == '!') {
negate = 1;
p++;
}
if (negate && protos == 0)
protos = TLS_PROTOCOLS_ALL;
proto = 0;
if (strcasecmp(p, "all") == 0 ||
strcasecmp(p, "legacy") == 0)
proto = TLS_PROTOCOLS_ALL;
else if (strcasecmp(p, "default") == 0 ||
strcasecmp(p, "secure") == 0)
proto = TLS_PROTOCOLS_DEFAULT;
if (strcasecmp(p, "tlsv1") == 0)
proto = TLS_PROTOCOL_TLSv1;
else if (strcasecmp(p, "tlsv1.0") == 0)
proto = TLS_PROTOCOL_TLSv1_2;
else if (strcasecmp(p, "tlsv1.1") == 0)
proto = TLS_PROTOCOL_TLSv1_2;
else if (strcasecmp(p, "tlsv1.2") == 0)
proto = TLS_PROTOCOL_TLSv1_2;
else if (strcasecmp(p, "tlsv1.3") == 0)
proto = TLS_PROTOCOL_TLSv1_3;
if (proto == 0) {
free(s);
return (-1);
}
if (negate)
protos &= ~proto;
else
protos |= proto;
}
*protocols = protos;
free(s);
return (0);
}
static int
tls_config_parse_alpn(struct tls_config *config, const char *alpn,
char **alpn_data, size_t *alpn_len)
{
size_t buf_len, i, len;
char *buf = NULL;
char *s = NULL;
char *p, *q;
free(*alpn_data);
*alpn_data = NULL;
*alpn_len = 0;
if ((buf_len = strlen(alpn) + 1) > 65535) {
tls_config_set_errorx(config, "alpn too large");
goto err;
}
if ((buf = malloc(buf_len)) == NULL) {
tls_config_set_errorx(config, "out of memory");
goto err;
}
if ((s = strdup(alpn)) == NULL) {
tls_config_set_errorx(config, "out of memory");
goto err;
}
i = 0;
q = s;
while ((p = strsep(&q, ",")) != NULL) {
if ((len = strlen(p)) == 0) {
tls_config_set_errorx(config,
"alpn protocol with zero length");
goto err;
}
if (len > 255) {
tls_config_set_errorx(config,
"alpn protocol too long");
goto err;
}
buf[i++] = len & 0xff;
memcpy(&buf[i], p, len);
i += len;
}
free(s);
*alpn_data = buf;
*alpn_len = buf_len;
return (0);
err:
free(buf);
free(s);
return (-1);
}
int
tls_config_set_alpn(struct tls_config *config, const char *alpn)
{
return tls_config_parse_alpn(config, alpn, &config->alpn,
&config->alpn_len);
}
static int
tls_config_add_keypair_file_internal(struct tls_config *config,
const char *cert_file, const char *key_file, const char *ocsp_file)
{
struct tls_keypair *keypair;
if ((keypair = tls_keypair_new()) == NULL)
return (-1);
if (tls_keypair_set_cert_file(keypair, &config->error, cert_file) != 0)
goto err;
if (key_file != NULL &&
tls_keypair_set_key_file(keypair, &config->error, key_file) != 0)
goto err;
if (ocsp_file != NULL &&
tls_keypair_set_ocsp_staple_file(keypair, &config->error,
ocsp_file) != 0)
goto err;
tls_config_keypair_add(config, keypair);
return (0);
err:
tls_keypair_free(keypair);
return (-1);
}
static int
tls_config_add_keypair_mem_internal(struct tls_config *config, const uint8_t *cert,
size_t cert_len, const uint8_t *key, size_t key_len,
const uint8_t *staple, size_t staple_len)
{
struct tls_keypair *keypair;
if ((keypair = tls_keypair_new()) == NULL)
return (-1);
if (tls_keypair_set_cert_mem(keypair, &config->error, cert, cert_len) != 0)
goto err;
if (key != NULL &&
tls_keypair_set_key_mem(keypair, &config->error, key, key_len) != 0)
goto err;
if (staple != NULL &&
tls_keypair_set_ocsp_staple_mem(keypair, &config->error, staple,
staple_len) != 0)
goto err;
tls_config_keypair_add(config, keypair);
return (0);
err:
tls_keypair_free(keypair);
return (-1);
}
int
tls_config_add_keypair_mem(struct tls_config *config, const uint8_t *cert,
size_t cert_len, const uint8_t *key, size_t key_len)
{
return tls_config_add_keypair_mem_internal(config, cert, cert_len, key,
key_len, NULL, 0);
}
int
tls_config_add_keypair_file(struct tls_config *config,
const char *cert_file, const char *key_file)
{
return tls_config_add_keypair_file_internal(config, cert_file,
key_file, NULL);
}
int
tls_config_add_keypair_ocsp_mem(struct tls_config *config, const uint8_t *cert,
size_t cert_len, const uint8_t *key, size_t key_len, const uint8_t *staple,
size_t staple_len)
{
return tls_config_add_keypair_mem_internal(config, cert, cert_len, key,
key_len, staple, staple_len);
}
int
tls_config_add_keypair_ocsp_file(struct tls_config *config,
const char *cert_file, const char *key_file, const char *ocsp_file)
{
return tls_config_add_keypair_file_internal(config, cert_file,
key_file, ocsp_file);
}
int
tls_config_set_ca_file(struct tls_config *config, const char *ca_file)
{
return tls_config_load_file(&config->error, "CA", ca_file,
&config->ca_mem, &config->ca_len);
}
int
tls_config_set_ca_path(struct tls_config *config, const char *ca_path)
{
return tls_set_string(&config->ca_path, ca_path);
}
int
tls_config_set_ca_mem(struct tls_config *config, const uint8_t *ca, size_t len)
{
return tls_set_mem(&config->ca_mem, &config->ca_len, ca, len);
}
int
tls_config_set_cert_file(struct tls_config *config, const char *cert_file)
{
return tls_keypair_set_cert_file(config->keypair, &config->error,
cert_file);
}
int
tls_config_set_cert_mem(struct tls_config *config, const uint8_t *cert,
size_t len)
{
return tls_keypair_set_cert_mem(config->keypair, &config->error,
cert, len);
}
int
tls_config_set_ciphers(struct tls_config *config, const char *ciphers)
{
SSL_CTX *ssl_ctx = NULL;
if (ciphers == NULL ||
strcasecmp(ciphers, "default") == 0 ||
strcasecmp(ciphers, "secure") == 0)
ciphers = TLS_CIPHERS_DEFAULT;
else if (strcasecmp(ciphers, "compat") == 0)
ciphers = TLS_CIPHERS_COMPAT;
else if (strcasecmp(ciphers, "legacy") == 0)
ciphers = TLS_CIPHERS_LEGACY;
else if (strcasecmp(ciphers, "all") == 0 ||
strcasecmp(ciphers, "insecure") == 0)
ciphers = TLS_CIPHERS_ALL;
if ((ssl_ctx = SSL_CTX_new(SSLv23_method())) == NULL) {
tls_config_set_errorx(config, "out of memory");
goto err;
}
if (SSL_CTX_set_cipher_list(ssl_ctx, ciphers) != 1) {
tls_config_set_errorx(config, "no ciphers for '%s'", ciphers);
goto err;
}
SSL_CTX_free(ssl_ctx);
return tls_set_string(&config->ciphers, ciphers);
err:
SSL_CTX_free(ssl_ctx);
return -1;
}
int
tls_config_set_crl_file(struct tls_config *config, const char *crl_file)
{
return tls_config_load_file(&config->error, "CRL", crl_file,
&config->crl_mem, &config->crl_len);
}
int
tls_config_set_crl_mem(struct tls_config *config, const uint8_t *crl,
size_t len)
{
return tls_set_mem(&config->crl_mem, &config->crl_len, crl, len);
}
int
tls_config_set_dheparams(struct tls_config *config, const char *params)
{
int keylen;
if (params == NULL || strcasecmp(params, "none") == 0)
keylen = 0;
else if (strcasecmp(params, "auto") == 0)
keylen = -1;
else if (strcasecmp(params, "legacy") == 0)
keylen = 1024;
else {
tls_config_set_errorx(config, "invalid dhe param '%s'", params);
return (-1);
}
config->dheparams = keylen;
return (0);
}
int
tls_config_set_ecdhecurve(struct tls_config *config, const char *curve)
{
if (curve == NULL ||
strcasecmp(curve, "none") == 0 ||
strcasecmp(curve, "auto") == 0) {
curve = TLS_ECDHE_CURVES;
} else if (strchr(curve, ',') != NULL || strchr(curve, ':') != NULL) {
tls_config_set_errorx(config, "invalid ecdhe curve '%s'",
curve);
return (-1);
}
return tls_config_set_ecdhecurves(config, curve);
}
int
tls_config_set_ecdhecurves(struct tls_config *config, const char *curves)
{
int *curves_list = NULL, *curves_new;
size_t curves_num = 0;
char *cs = NULL;
char *p, *q;
int rv = -1;
int nid;
free(config->ecdhecurves);
config->ecdhecurves = NULL;
config->ecdhecurves_len = 0;
if (curves == NULL || strcasecmp(curves, "default") == 0)
curves = TLS_ECDHE_CURVES;
if ((cs = strdup(curves)) == NULL) {
tls_config_set_errorx(config, "out of memory");
goto err;
}
q = cs;
while ((p = strsep(&q, ",:")) != NULL) {
while (*p == ' ' || *p == '\t')
p++;
nid = OBJ_sn2nid(p);
if (nid == NID_undef)
nid = OBJ_ln2nid(p);
if (nid == NID_undef)
nid = EC_curve_nist2nid(p);
if (nid == NID_undef) {
tls_config_set_errorx(config,
"invalid ecdhe curve '%s'", p);
goto err;
}
if ((curves_new = reallocarray(curves_list, curves_num + 1,
sizeof(int))) == NULL) {
tls_config_set_errorx(config, "out of memory");
goto err;
}
curves_list = curves_new;
curves_list[curves_num] = nid;
curves_num++;
}
config->ecdhecurves = curves_list;
config->ecdhecurves_len = curves_num;
curves_list = NULL;
rv = 0;
err:
free(cs);
free(curves_list);
return (rv);
}
int
tls_config_set_key_file(struct tls_config *config, const char *key_file)
{
return tls_keypair_set_key_file(config->keypair, &config->error,
key_file);
}
int
tls_config_set_key_mem(struct tls_config *config, const uint8_t *key,
size_t len)
{
return tls_keypair_set_key_mem(config->keypair, &config->error,
key, len);
}
static int
tls_config_set_keypair_file_internal(struct tls_config *config,
const char *cert_file, const char *key_file, const char *ocsp_file)
{
if (tls_config_set_cert_file(config, cert_file) != 0)
return (-1);
if (tls_config_set_key_file(config, key_file) != 0)
return (-1);
if (ocsp_file != NULL &&
tls_config_set_ocsp_staple_file(config, ocsp_file) != 0)
return (-1);
return (0);
}
static int
tls_config_set_keypair_mem_internal(struct tls_config *config, const uint8_t *cert,
size_t cert_len, const uint8_t *key, size_t key_len,
const uint8_t *staple, size_t staple_len)
{
if (tls_config_set_cert_mem(config, cert, cert_len) != 0)
return (-1);
if (tls_config_set_key_mem(config, key, key_len) != 0)
return (-1);
if ((staple != NULL) &&
(tls_config_set_ocsp_staple_mem(config, staple, staple_len) != 0))
return (-1);
return (0);
}
int
tls_config_set_keypair_file(struct tls_config *config,
const char *cert_file, const char *key_file)
{
return tls_config_set_keypair_file_internal(config, cert_file, key_file,
NULL);
}
int
tls_config_set_keypair_mem(struct tls_config *config, const uint8_t *cert,
size_t cert_len, const uint8_t *key, size_t key_len)
{
return tls_config_set_keypair_mem_internal(config, cert, cert_len,
key, key_len, NULL, 0);
}
int
tls_config_set_keypair_ocsp_file(struct tls_config *config,
const char *cert_file, const char *key_file, const char *ocsp_file)
{
return tls_config_set_keypair_file_internal(config, cert_file, key_file,
ocsp_file);
}
int
tls_config_set_keypair_ocsp_mem(struct tls_config *config, const uint8_t *cert,
size_t cert_len, const uint8_t *key, size_t key_len,
const uint8_t *staple, size_t staple_len)
{
return tls_config_set_keypair_mem_internal(config, cert, cert_len,
key, key_len, staple, staple_len);
}
int
tls_config_set_protocols(struct tls_config *config, uint32_t protocols)
{
config->protocols = protocols;
return (0);
}
int
tls_config_set_session_fd(struct tls_config *config, int session_fd)
{
struct stat sb;
mode_t mugo;
if (session_fd == -1) {
config->session_fd = session_fd;
return (0);
}
if (fstat(session_fd, &sb) == -1) {
tls_config_set_error(config, "failed to stat session file");
return (-1);
}
if (!S_ISREG(sb.st_mode)) {
tls_config_set_errorx(config,
"session file is not a regular file");
return (-1);
}
if (sb.st_uid != getuid()) {
tls_config_set_errorx(config, "session file has incorrect "
"owner (uid %u != %u)", sb.st_uid, getuid());
return (-1);
}
mugo = sb.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
if (mugo != (S_IRUSR|S_IWUSR)) {
tls_config_set_errorx(config, "session file has incorrect "
"permissions (%o != 600)", mugo);
return (-1);
}
config->session_fd = 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)
{
config->verify_depth = verify_depth;
return (0);
}
void
tls_config_prefer_ciphers_client(struct tls_config *config)
{
config->ciphers_server = 0;
}
void
tls_config_prefer_ciphers_server(struct tls_config *config)
{
config->ciphers_server = 1;
}
void
tls_config_insecure_noverifycert(struct tls_config *config)
{
config->verify_cert = 0;
}
void
tls_config_insecure_noverifyname(struct tls_config *config)
{
config->verify_name = 0;
}
void
tls_config_insecure_noverifytime(struct tls_config *config)
{
config->verify_time = 0;
}
void
tls_config_verify(struct tls_config *config)
{
config->verify_cert = 1;
config->verify_name = 1;
config->verify_time = 1;
}
void
tls_config_ocsp_require_stapling(struct tls_config *config)
{
config->ocsp_require_stapling = 1;
}
void
tls_config_verify_client(struct tls_config *config)
{
config->verify_client = 1;
}
void
tls_config_verify_client_optional(struct tls_config *config)
{
config->verify_client = 2;
}
void
tls_config_skip_private_key_check(struct tls_config *config)
{
config->skip_private_key_check = 1;
}
void
tls_config_use_fake_private_key(struct tls_config *config)
{
config->use_fake_private_key = 1;
config->skip_private_key_check = 1;
}
int
tls_config_set_ocsp_staple_file(struct tls_config *config, const char *staple_file)
{
return tls_keypair_set_ocsp_staple_file(config->keypair, &config->error,
staple_file);
}
int
tls_config_set_ocsp_staple_mem(struct tls_config *config, const uint8_t *staple,
size_t len)
{
return tls_keypair_set_ocsp_staple_mem(config->keypair, &config->error,
staple, len);
}
int
tls_config_set_session_id(struct tls_config *config,
const unsigned char *session_id, size_t len)
{
if (len > TLS_MAX_SESSION_ID_LENGTH) {
tls_config_set_errorx(config, "session ID too large");
return (-1);
}
memset(config->session_id, 0, sizeof(config->session_id));
memcpy(config->session_id, session_id, len);
return (0);
}
int
tls_config_set_session_lifetime(struct tls_config *config, int lifetime)
{
if (lifetime > TLS_MAX_SESSION_TIMEOUT) {
tls_config_set_errorx(config, "session lifetime too large");
return (-1);
}
if (lifetime != 0 && lifetime < TLS_MIN_SESSION_TIMEOUT) {
tls_config_set_errorx(config, "session lifetime too small");
return (-1);
}
config->session_lifetime = lifetime;
return (0);
}
int
tls_config_add_ticket_key(struct tls_config *config, uint32_t keyrev,
unsigned char *key, size_t keylen)
{
struct tls_ticket_key newkey;
int i;
if (TLS_TICKET_KEY_SIZE != keylen ||
sizeof(newkey.aes_key) + sizeof(newkey.hmac_key) > keylen) {
tls_config_set_errorx(config,
"wrong amount of ticket key data");
return (-1);
}
keyrev = htonl(keyrev);
memset(&newkey, 0, sizeof(newkey));
memcpy(newkey.key_name, &keyrev, sizeof(keyrev));
memcpy(newkey.aes_key, key, sizeof(newkey.aes_key));
memcpy(newkey.hmac_key, key + sizeof(newkey.aes_key),
sizeof(newkey.hmac_key));
newkey.time = time(NULL);
for (i = 0; i < TLS_NUM_TICKETS; i++) {
struct tls_ticket_key *tk = &config->ticket_keys[i];
if (memcmp(newkey.key_name, tk->key_name,
sizeof(tk->key_name)) != 0)
continue;
/* allow re-entry of most recent key */
if (i == 0 && memcmp(newkey.aes_key, tk->aes_key,
sizeof(tk->aes_key)) == 0 && memcmp(newkey.hmac_key,
tk->hmac_key, sizeof(tk->hmac_key)) == 0)
return (0);
tls_config_set_errorx(config, "ticket key already present");
return (-1);
}
memmove(&config->ticket_keys[1], &config->ticket_keys[0],
sizeof(config->ticket_keys) - sizeof(config->ticket_keys[0]));
config->ticket_keys[0] = newkey;
config->ticket_autorekey = 0;
return (0);
}
int
tls_config_ticket_autorekey(struct tls_config *config)
{
unsigned char key[TLS_TICKET_KEY_SIZE];
int rv;
arc4random_buf(key, sizeof(key));
rv = tls_config_add_ticket_key(config, config->ticket_keyrev++, key,
sizeof(key));
config->ticket_autorekey = 1;
return (rv);
}

344
tls/tls_conninfo.c Normal file
View File

@@ -0,0 +1,344 @@
/* $OpenBSD: tls_conninfo.c,v 1.23 2023/05/14 07:26:25 op Exp $ */
/*
* Copyright (c) 2015 Joel Sing <jsing@openbsd.org>
* Copyright (c) 2015 Bob Beck <beck@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 <stdio.h>
#include <string.h>
#include <openssl/x509.h>
#include <tls.h>
#include "tls_internal.h"
int ASN1_time_tm_clamp_notafter(struct tm *tm);
int
tls_hex_string(const unsigned char *in, size_t inlen, char **out,
size_t *outlen)
{
static const char hex[] = "0123456789abcdef";
size_t i, len;
char *p;
if (outlen != NULL)
*outlen = 0;
if (inlen >= SIZE_MAX)
return (-1);
if ((*out = reallocarray(NULL, inlen + 1, 2)) == NULL)
return (-1);
p = *out;
len = 0;
for (i = 0; i < inlen; i++) {
p[len++] = hex[(in[i] >> 4) & 0x0f];
p[len++] = hex[in[i] & 0x0f];
}
p[len++] = 0;
if (outlen != NULL)
*outlen = len;
return (0);
}
static int
tls_get_peer_cert_hash(struct tls *ctx, char **hash)
{
*hash = NULL;
if (ctx->ssl_peer_cert == NULL)
return (0);
if (tls_cert_hash(ctx->ssl_peer_cert, hash) == -1) {
tls_set_errorx(ctx, "unable to compute peer certificate hash - out of memory");
*hash = NULL;
return -1;
}
return 0;
}
static int
tls_get_peer_cert_issuer(struct tls *ctx, char **issuer)
{
X509_NAME *name = NULL;
*issuer = NULL;
if (ctx->ssl_peer_cert == NULL)
return (-1);
if ((name = X509_get_issuer_name(ctx->ssl_peer_cert)) == NULL)
return (-1);
*issuer = X509_NAME_oneline(name, 0, 0);
if (*issuer == NULL)
return (-1);
return (0);
}
static int
tls_get_peer_cert_subject(struct tls *ctx, char **subject)
{
X509_NAME *name = NULL;
*subject = NULL;
if (ctx->ssl_peer_cert == NULL)
return (-1);
if ((name = X509_get_subject_name(ctx->ssl_peer_cert)) == NULL)
return (-1);
*subject = X509_NAME_oneline(name, 0, 0);
if (*subject == NULL)
return (-1);
return (0);
}
static int
tls_get_peer_cert_times(struct tls *ctx, time_t *notbefore,
time_t *notafter)
{
struct tm before_tm, after_tm;
ASN1_TIME *before, *after;
if (ctx->ssl_peer_cert == NULL)
return (-1);
if ((before = X509_get_notBefore(ctx->ssl_peer_cert)) == NULL)
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)
goto err;
if (ASN1_time_parse(after->data, after->length, &after_tm, 0) == -1)
goto err;
if (!ASN1_time_tm_clamp_notafter(&after_tm))
goto err;
if ((*notbefore = timegm(&before_tm)) == -1)
goto err;
if ((*notafter = timegm(&after_tm)) == -1)
goto err;
return (0);
err:
return (-1);
}
static int
tls_get_peer_cert_info(struct tls *ctx)
{
if (ctx->ssl_peer_cert == NULL)
return (0);
if (tls_get_peer_cert_hash(ctx, &ctx->conninfo->hash) == -1)
goto err;
if (tls_get_peer_cert_subject(ctx, &ctx->conninfo->subject) == -1)
goto err;
if (tls_get_peer_cert_issuer(ctx, &ctx->conninfo->issuer) == -1)
goto err;
if (tls_get_peer_cert_times(ctx, &ctx->conninfo->notbefore,
&ctx->conninfo->notafter) == -1)
goto err;
return (0);
err:
return (-1);
}
static int
tls_conninfo_alpn_proto(struct tls *ctx)
{
const unsigned char *p;
unsigned int len;
free(ctx->conninfo->alpn);
ctx->conninfo->alpn = NULL;
SSL_get0_alpn_selected(ctx->ssl_conn, &p, &len);
if (len > 0) {
if ((ctx->conninfo->alpn = malloc(len + 1)) == NULL)
return (-1);
memcpy(ctx->conninfo->alpn, p, len);
ctx->conninfo->alpn[len] = '\0';
}
return (0);
}
static int
tls_conninfo_cert_pem(struct tls *ctx)
{
int i, rv = -1;
BIO *membio = NULL;
BUF_MEM *bptr = NULL;
if (ctx->ssl_peer_cert == NULL)
return 0;
if ((membio = BIO_new(BIO_s_mem()))== NULL)
goto err;
/*
* We have to write the peer cert out separately, because
* the certificate chain may or may not contain it.
*/
if (!PEM_write_bio_X509(membio, ctx->ssl_peer_cert))
goto err;
for (i = 0; i < sk_X509_num(ctx->ssl_peer_chain); i++) {
X509 *chaincert = sk_X509_value(ctx->ssl_peer_chain, i);
if (chaincert != ctx->ssl_peer_cert &&
!PEM_write_bio_X509(membio, chaincert))
goto err;
}
BIO_get_mem_ptr(membio, &bptr);
free(ctx->conninfo->peer_cert);
ctx->conninfo->peer_cert_len = 0;
if ((ctx->conninfo->peer_cert = malloc(bptr->length)) == NULL)
goto err;
ctx->conninfo->peer_cert_len = bptr->length;
memcpy(ctx->conninfo->peer_cert, bptr->data,
ctx->conninfo->peer_cert_len);
/* BIO_free() will kill BUF_MEM - because we have not set BIO_NOCLOSE */
rv = 0;
err:
BIO_free(membio);
return rv;
}
static int
tls_conninfo_session(struct tls *ctx)
{
ctx->conninfo->session_resumed = SSL_session_reused(ctx->ssl_conn);
return 0;
}
int
tls_conninfo_populate(struct tls *ctx)
{
const char *tmp;
tls_conninfo_free(ctx->conninfo);
if ((ctx->conninfo = calloc(1, sizeof(struct tls_conninfo))) == NULL) {
tls_set_errorx(ctx, "out of memory");
goto err;
}
if (tls_conninfo_alpn_proto(ctx) == -1)
goto err;
if ((tmp = SSL_get_cipher(ctx->ssl_conn)) == NULL)
goto err;
if ((ctx->conninfo->cipher = strdup(tmp)) == NULL)
goto err;
ctx->conninfo->cipher_strength = SSL_get_cipher_bits(ctx->ssl_conn, NULL);
if (ctx->servername != NULL) {
if ((ctx->conninfo->servername =
strdup(ctx->servername)) == NULL)
goto err;
}
if ((tmp = SSL_get_version(ctx->ssl_conn)) == NULL)
goto err;
if ((ctx->conninfo->version = strdup(tmp)) == NULL)
goto err;
if (tls_get_peer_cert_info(ctx) == -1)
goto err;
if (tls_conninfo_cert_pem(ctx) == -1)
goto err;
if (tls_conninfo_session(ctx) == -1)
goto err;
return (0);
err:
tls_conninfo_free(ctx->conninfo);
ctx->conninfo = NULL;
return (-1);
}
void
tls_conninfo_free(struct tls_conninfo *conninfo)
{
if (conninfo == NULL)
return;
free(conninfo->alpn);
free(conninfo->cipher);
free(conninfo->servername);
free(conninfo->version);
free(conninfo->hash);
free(conninfo->issuer);
free(conninfo->subject);
free(conninfo->peer_cert);
free(conninfo);
}
const char *
tls_conn_alpn_selected(struct tls *ctx)
{
if (ctx->conninfo == NULL)
return (NULL);
return (ctx->conninfo->alpn);
}
const char *
tls_conn_cipher(struct tls *ctx)
{
if (ctx->conninfo == NULL)
return (NULL);
return (ctx->conninfo->cipher);
}
int
tls_conn_cipher_strength(struct tls *ctx)
{
if (ctx->conninfo == NULL)
return (0);
return (ctx->conninfo->cipher_strength);
}
const char *
tls_conn_servername(struct tls *ctx)
{
if (ctx->conninfo == NULL)
return (NULL);
return (ctx->conninfo->servername);
}
int
tls_conn_session_resumed(struct tls *ctx)
{
if (ctx->conninfo == NULL)
return (0);
return (ctx->conninfo->session_resumed);
}
const char *
tls_conn_version(struct tls *ctx)
{
if (ctx->conninfo == NULL)
return (NULL);
return (ctx->conninfo->version);
}

326
tls/tls_internal.h Normal file
View File

@@ -0,0 +1,326 @@
/* $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>
*
* 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.
*/
#ifndef HEADER_TLS_INTERNAL_H
#define HEADER_TLS_INTERNAL_H
#include <pthread.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <openssl/ssl.h>
__BEGIN_HIDDEN_DECLS
#ifndef TLS_DEFAULT_CA_FILE
#define TLS_DEFAULT_CA_FILE "/etc/ssl/cert.pem"
#endif
#define TLS_CIPHERS_DEFAULT "TLSv1.3:TLSv1.2+AEAD+ECDHE:TLSv1.2+AEAD+DHE"
#define TLS_CIPHERS_COMPAT "HIGH:!aNULL"
#define TLS_CIPHERS_LEGACY "HIGH:MEDIUM:!aNULL"
#define TLS_CIPHERS_ALL "ALL:!aNULL:!eNULL"
#define TLS_ECDHE_CURVES "X25519,P-256,P-384"
union tls_addr {
struct in_addr ip4;
struct in6_addr ip6;
};
struct tls_error {
char *msg;
int num;
int tls;
};
struct tls_keypair {
struct tls_keypair *next;
char *cert_mem;
size_t cert_len;
char *key_mem;
size_t key_len;
char *ocsp_staple;
size_t ocsp_staple_len;
char *pubkey_hash;
};
#define TLS_MIN_SESSION_TIMEOUT (4)
#define TLS_MAX_SESSION_TIMEOUT (24 * 60 * 60)
#define TLS_NUM_TICKETS 4
#define TLS_TICKET_NAME_SIZE 16
#define TLS_TICKET_AES_SIZE 32
#define TLS_TICKET_HMAC_SIZE 16
struct tls_ticket_key {
/* The key_name must be 16 bytes according to -lssl */
unsigned char key_name[TLS_TICKET_NAME_SIZE];
unsigned char aes_key[TLS_TICKET_AES_SIZE];
unsigned char hmac_key[TLS_TICKET_HMAC_SIZE];
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;
pthread_mutex_t mutex;
int refcount;
char *alpn;
size_t alpn_len;
const char *ca_path;
char *ca_mem;
size_t ca_len;
const char *ciphers;
int ciphers_server;
char *crl_mem;
size_t crl_len;
int dheparams;
int *ecdhecurves;
size_t ecdhecurves_len;
struct tls_keypair *keypair;
int ocsp_require_stapling;
uint32_t protocols;
unsigned char session_id[TLS_MAX_SESSION_ID_LENGTH];
int session_fd;
int session_lifetime;
struct tls_ticket_key ticket_keys[TLS_NUM_TICKETS];
uint32_t ticket_keyrev;
int ticket_autorekey;
int verify_cert;
int verify_client;
int verify_depth;
int verify_name;
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 {
char *alpn;
char *cipher;
int cipher_strength;
char *servername;
int session_resumed;
char *version;
char *hash;
char *issuer;
char *subject;
uint8_t *peer_cert;
size_t peer_cert_len;
time_t notbefore;
time_t notafter;
};
#define TLS_CLIENT (1 << 0)
#define TLS_SERVER (1 << 1)
#define TLS_SERVER_CONN (1 << 2)
#define TLS_EOF_NO_CLOSE_NOTIFY (1 << 0)
#define TLS_CONNECTED (1 << 1)
#define TLS_HANDSHAKE_COMPLETE (1 << 2)
#define TLS_SSL_NEEDS_SHUTDOWN (1 << 3)
struct tls_ocsp_result {
const char *result_msg;
int response_status;
int cert_status;
int crl_reason;
time_t this_update;
time_t next_update;
time_t revocation_time;
};
struct tls_ocsp {
/* responder location */
char *ocsp_url;
/* cert data, this struct does not own these */
X509 *main_cert;
STACK_OF(X509) *extra_certs;
struct tls_ocsp_result *ocsp_result;
};
struct tls_sni_ctx {
struct tls_sni_ctx *next;
struct tls_keypair *keypair;
SSL_CTX *ssl_ctx;
X509 *ssl_cert;
};
struct tls {
struct tls_config *config;
struct tls_keypair *keypair;
struct tls_error error;
uint32_t flags;
uint32_t state;
char *servername;
int socket;
SSL *ssl_conn;
SSL_CTX *ssl_ctx;
struct tls_sni_ctx *sni_ctx;
X509 *ssl_peer_cert;
STACK_OF(X509) *ssl_peer_chain;
struct tls_conninfo *conninfo;
struct tls_ocsp *ocsp;
tls_read_cb read_cb;
tls_write_cb write_cb;
void *cb_arg;
};
int tls_set_mem(char **_dest, size_t *_destlen, const void *_src,
size_t _srclen);
int tls_set_string(const char **_dest, const char *_src);
struct tls_keypair *tls_keypair_new(void);
void tls_keypair_clear_key(struct tls_keypair *_keypair);
void tls_keypair_free(struct tls_keypair *_keypair);
int tls_keypair_set_cert_file(struct tls_keypair *_keypair,
struct tls_error *_error, const char *_cert_file);
int tls_keypair_set_cert_mem(struct tls_keypair *_keypair,
struct tls_error *_error, const uint8_t *_cert, size_t _len);
int tls_keypair_set_key_file(struct tls_keypair *_keypair,
struct tls_error *_error, const char *_key_file);
int tls_keypair_set_key_mem(struct tls_keypair *_keypair,
struct tls_error *_error, const uint8_t *_key, size_t _len);
int tls_keypair_set_ocsp_staple_file(struct tls_keypair *_keypair,
struct tls_error *_error, const char *_ocsp_file);
int tls_keypair_set_ocsp_staple_mem(struct tls_keypair *_keypair,
struct tls_error *_error, const uint8_t *_staple, size_t _len);
int tls_keypair_load_cert(struct tls_keypair *_keypair,
struct tls_error *_error, X509 **_cert);
struct tls_sni_ctx *tls_sni_ctx_new(void);
void tls_sni_ctx_free(struct tls_sni_ctx *sni_ctx);
struct tls_config *tls_config_new_internal(void);
struct tls *tls_new(void);
struct tls *tls_server_conn(struct tls *ctx);
int tls_check_name(struct tls *ctx, X509 *cert, const char *servername,
int *match);
int tls_configure_server(struct tls *ctx);
int tls_configure_ssl(struct tls *ctx, SSL_CTX *ssl_ctx);
int tls_configure_ssl_keypair(struct tls *ctx, SSL_CTX *ssl_ctx,
struct tls_keypair *keypair, int required);
int tls_configure_ssl_verify(struct tls *ctx, SSL_CTX *ssl_ctx, int verify);
int tls_handshake_client(struct tls *ctx);
int tls_handshake_server(struct tls *ctx);
int tls_config_load_file(struct tls_error *error, const char *filetype,
const char *filename, char **buf, size_t *len);
int tls_config_ticket_autorekey(struct tls_config *config);
int tls_host_port(const char *hostport, char **host, char **port);
int tls_set_cbs(struct tls *ctx,
tls_read_cb read_cb, tls_write_cb write_cb, void *cb_arg);
void tls_error_clear(struct tls_error *error);
int tls_error_set(struct tls_error *error, const char *fmt, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
int tls_error_setx(struct tls_error *error, const char *fmt, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
int tls_config_set_error(struct tls_config *cfg, const char *fmt, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
int tls_config_set_errorx(struct tls_config *cfg, const char *fmt, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
int tls_set_error(struct tls *ctx, const char *fmt, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
int tls_set_errorx(struct tls *ctx, const char *fmt, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
int tls_set_ssl_errorx(struct tls *ctx, const char *fmt, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
int tls_ssl_error(struct tls *ctx, SSL *ssl_conn, int ssl_ret,
const char *prefix);
int tls_conninfo_populate(struct tls *ctx);
void tls_conninfo_free(struct tls_conninfo *conninfo);
int tls_ocsp_verify_cb(SSL *ssl, void *arg);
int tls_ocsp_stapling_cb(SSL *ssl, void *arg);
void tls_ocsp_free(struct tls_ocsp *ctx);
struct tls_ocsp *tls_ocsp_setup_from_peer(struct tls *ctx);
int tls_hex_string(const unsigned char *_in, size_t _inlen, char **_out,
size_t *_outlen);
int tls_cert_hash(X509 *_cert, char **_hash);
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);
__END_HIDDEN_DECLS
/* 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);
#endif /* HEADER_TLS_INTERNAL_H */

169
tls/tls_keypair.c Normal file
View File

@@ -0,0 +1,169 @@
/* $OpenBSD: tls_keypair.c,v 1.8 2021/01/05 17:37:12 jsing Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@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 <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <tls.h>
#include "tls_internal.h"
struct tls_keypair *
tls_keypair_new(void)
{
return calloc(1, sizeof(struct tls_keypair));
}
static int
tls_keypair_pubkey_hash(struct tls_keypair *keypair, struct tls_error *error)
{
X509 *cert = NULL;
int rv = -1;
free(keypair->pubkey_hash);
keypair->pubkey_hash = NULL;
if (keypair->cert_mem == NULL) {
rv = 0;
goto done;
}
if (tls_keypair_load_cert(keypair, error, &cert) == -1)
goto err;
if (tls_cert_pubkey_hash(cert, &keypair->pubkey_hash) == -1)
goto err;
rv = 0;
err:
X509_free(cert);
done:
return (rv);
}
void
tls_keypair_clear_key(struct tls_keypair *keypair)
{
freezero(keypair->key_mem, keypair->key_len);
keypair->key_mem = NULL;
keypair->key_len = 0;
}
int
tls_keypair_set_cert_file(struct tls_keypair *keypair, struct tls_error *error,
const char *cert_file)
{
if (tls_config_load_file(error, "certificate", cert_file,
&keypair->cert_mem, &keypair->cert_len) == -1)
return -1;
return tls_keypair_pubkey_hash(keypair, error);
}
int
tls_keypair_set_cert_mem(struct tls_keypair *keypair, struct tls_error *error,
const uint8_t *cert, size_t len)
{
if (tls_set_mem(&keypair->cert_mem, &keypair->cert_len, cert, len) == -1)
return -1;
return tls_keypair_pubkey_hash(keypair, error);
}
int
tls_keypair_set_key_file(struct tls_keypair *keypair, struct tls_error *error,
const char *key_file)
{
tls_keypair_clear_key(keypair);
return tls_config_load_file(error, "key", key_file,
&keypair->key_mem, &keypair->key_len);
}
int
tls_keypair_set_key_mem(struct tls_keypair *keypair, struct tls_error *error,
const uint8_t *key, size_t len)
{
tls_keypair_clear_key(keypair);
return tls_set_mem(&keypair->key_mem, &keypair->key_len, key, len);
}
int
tls_keypair_set_ocsp_staple_file(struct tls_keypair *keypair,
struct tls_error *error, const char *ocsp_file)
{
return tls_config_load_file(error, "ocsp", ocsp_file,
&keypair->ocsp_staple, &keypair->ocsp_staple_len);
}
int
tls_keypair_set_ocsp_staple_mem(struct tls_keypair *keypair,
struct tls_error *error, const uint8_t *staple, size_t len)
{
return tls_set_mem(&keypair->ocsp_staple, &keypair->ocsp_staple_len,
staple, len);
}
void
tls_keypair_free(struct tls_keypair *keypair)
{
if (keypair == NULL)
return;
tls_keypair_clear_key(keypair);
free(keypair->cert_mem);
free(keypair->ocsp_staple);
free(keypair->pubkey_hash);
free(keypair);
}
int
tls_keypair_load_cert(struct tls_keypair *keypair, struct tls_error *error,
X509 **cert)
{
char *errstr = "unknown";
BIO *cert_bio = NULL;
unsigned long ssl_err;
int rv = -1;
X509_free(*cert);
*cert = NULL;
if (keypair->cert_mem == NULL) {
tls_error_set(error, "keypair has no certificate");
goto err;
}
if ((cert_bio = BIO_new_mem_buf(keypair->cert_mem,
keypair->cert_len)) == NULL) {
tls_error_set(error, "failed to create certificate bio");
goto err;
}
if ((*cert = PEM_read_bio_X509(cert_bio, NULL, tls_password_cb,
NULL)) == NULL) {
if ((ssl_err = ERR_peek_error()) != 0)
errstr = ERR_error_string(ssl_err, NULL);
tls_error_set(error, "failed to load certificate: %s", errstr);
goto err;
}
rv = 0;
err:
BIO_free(cert_bio);
return (rv);
}

463
tls/tls_ocsp.c Normal file
View File

@@ -0,0 +1,463 @@
/* $OpenBSD: tls_ocsp.c,v 1.23 2023/05/14 07:26:25 op Exp $ */
/*
* Copyright (c) 2015 Marko Kreen <markokr@gmail.com>
* Copyright (c) 2016 Bob Beck <beck@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 <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <openssl/err.h>
#include <openssl/ocsp.h>
#include <openssl/x509.h>
#include <tls.h>
#include "tls_internal.h"
#define MAXAGE_SEC (14*24*60*60)
#define JITTER_SEC (60)
/*
* State for request.
*/
static struct tls_ocsp *
tls_ocsp_new(void)
{
return (calloc(1, sizeof(struct tls_ocsp)));
}
void
tls_ocsp_free(struct tls_ocsp *ocsp)
{
if (ocsp == NULL)
return;
X509_free(ocsp->main_cert);
free(ocsp->ocsp_result);
free(ocsp->ocsp_url);
free(ocsp);
}
static int
tls_ocsp_asn1_parse_time(struct tls *ctx, ASN1_GENERALIZEDTIME *gt, time_t *gt_time)
{
struct tm tm;
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)
return -1;
if ((*gt_time = timegm(&tm)) == -1)
return -1;
return 0;
}
static int
tls_ocsp_fill_info(struct tls *ctx, int response_status, int cert_status,
int crl_reason, ASN1_GENERALIZEDTIME *revtime,
ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd)
{
struct tls_ocsp_result *info = NULL;
free(ctx->ocsp->ocsp_result);
ctx->ocsp->ocsp_result = NULL;
if ((info = calloc(1, sizeof (struct tls_ocsp_result))) == NULL) {
tls_set_error(ctx, "calloc");
return -1;
}
info->response_status = response_status;
info->cert_status = cert_status;
info->crl_reason = crl_reason;
if (info->response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
info->result_msg =
OCSP_response_status_str(info->response_status);
} else if (info->cert_status != V_OCSP_CERTSTATUS_REVOKED) {
info->result_msg = OCSP_cert_status_str(info->cert_status);
} else {
info->result_msg = OCSP_crl_reason_str(info->crl_reason);
}
info->revocation_time = info->this_update = info->next_update = -1;
if (revtime != NULL &&
tls_ocsp_asn1_parse_time(ctx, revtime, &info->revocation_time) != 0) {
tls_set_error(ctx,
"unable to parse revocation time in OCSP reply");
goto err;
}
if (thisupd != NULL &&
tls_ocsp_asn1_parse_time(ctx, thisupd, &info->this_update) != 0) {
tls_set_error(ctx,
"unable to parse this update time in OCSP reply");
goto err;
}
if (nextupd != NULL &&
tls_ocsp_asn1_parse_time(ctx, nextupd, &info->next_update) != 0) {
tls_set_error(ctx,
"unable to parse next update time in OCSP reply");
goto err;
}
ctx->ocsp->ocsp_result = info;
return 0;
err:
free(info);
return -1;
}
static OCSP_CERTID *
tls_ocsp_get_certid(X509 *main_cert, STACK_OF(X509) *extra_certs,
SSL_CTX *ssl_ctx)
{
X509_NAME *issuer_name;
X509 *issuer;
X509_STORE_CTX *storectx = NULL;
X509_OBJECT *obj = NULL;
OCSP_CERTID *cid = NULL;
X509_STORE *store;
if ((issuer_name = X509_get_issuer_name(main_cert)) == NULL)
goto out;
if (extra_certs != NULL) {
issuer = X509_find_by_subject(extra_certs, issuer_name);
if (issuer != NULL) {
cid = OCSP_cert_to_id(NULL, main_cert, issuer);
goto out;
}
}
if ((store = SSL_CTX_get_cert_store(ssl_ctx)) == NULL)
goto out;
if ((storectx = X509_STORE_CTX_new()) == NULL)
goto out;
if (X509_STORE_CTX_init(storectx, store, main_cert, extra_certs) != 1)
goto out;
if ((obj = X509_STORE_CTX_get_obj_by_subject(storectx, X509_LU_X509,
issuer_name)) == NULL)
goto out;
cid = OCSP_cert_to_id(NULL, main_cert, X509_OBJECT_get0_X509(obj));
out:
X509_STORE_CTX_free(storectx);
X509_OBJECT_free(obj);
return cid;
}
struct tls_ocsp *
tls_ocsp_setup_from_peer(struct tls *ctx)
{
struct tls_ocsp *ocsp = NULL;
STACK_OF(OPENSSL_STRING) *ocsp_urls = NULL;
if ((ocsp = tls_ocsp_new()) == NULL)
goto err;
/* steal state from ctx struct */
ocsp->main_cert = SSL_get_peer_certificate(ctx->ssl_conn);
ocsp->extra_certs = SSL_get_peer_cert_chain(ctx->ssl_conn);
if (ocsp->main_cert == NULL) {
tls_set_errorx(ctx, "no peer certificate for OCSP");
goto err;
}
ocsp_urls = X509_get1_ocsp(ocsp->main_cert);
if (ocsp_urls == NULL) {
tls_set_errorx(ctx, "no OCSP URLs in peer certificate");
goto err;
}
ocsp->ocsp_url = strdup(sk_OPENSSL_STRING_value(ocsp_urls, 0));
if (ocsp->ocsp_url == NULL) {
tls_set_errorx(ctx, "out of memory");
goto err;
}
X509_email_free(ocsp_urls);
return ocsp;
err:
tls_ocsp_free(ocsp);
X509_email_free(ocsp_urls);
return NULL;
}
static int
tls_ocsp_verify_response(struct tls *ctx, OCSP_RESPONSE *resp)
{
OCSP_BASICRESP *br = NULL;
ASN1_GENERALIZEDTIME *revtime = NULL, *thisupd = NULL, *nextupd = NULL;
OCSP_CERTID *cid = NULL;
STACK_OF(X509) *combined = NULL;
int response_status=0, cert_status=0, crl_reason=0;
int ret = -1;
unsigned long flags;
if ((br = OCSP_response_get1_basic(resp)) == NULL) {
tls_set_errorx(ctx, "cannot load ocsp reply");
goto err;
}
/*
* Skip validation of 'extra_certs' as this should be done
* already as part of main handshake.
*/
flags = OCSP_TRUSTOTHER;
/* now verify */
if (OCSP_basic_verify(br, ctx->ocsp->extra_certs,
SSL_CTX_get_cert_store(ctx->ssl_ctx), flags) != 1) {
tls_set_errorx(ctx, "ocsp verify failed");
goto err;
}
/* signature OK, look inside */
response_status = OCSP_response_status(resp);
if (response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
tls_set_errorx(ctx, "ocsp verify failed: response - %s",
OCSP_response_status_str(response_status));
goto err;
}
cid = tls_ocsp_get_certid(ctx->ocsp->main_cert,
ctx->ocsp->extra_certs, ctx->ssl_ctx);
if (cid == NULL) {
tls_set_errorx(ctx, "ocsp verify failed: no issuer cert");
goto err;
}
if (OCSP_resp_find_status(br, cid, &cert_status, &crl_reason,
&revtime, &thisupd, &nextupd) != 1) {
tls_set_errorx(ctx, "ocsp verify failed: no result for cert");
goto err;
}
if (OCSP_check_validity(thisupd, nextupd, JITTER_SEC,
MAXAGE_SEC) != 1) {
tls_set_errorx(ctx,
"ocsp verify failed: ocsp response not current");
goto err;
}
if (tls_ocsp_fill_info(ctx, response_status, cert_status,
crl_reason, revtime, thisupd, nextupd) != 0)
goto err;
/* finally can look at status */
if (cert_status != V_OCSP_CERTSTATUS_GOOD && cert_status !=
V_OCSP_CERTSTATUS_UNKNOWN) {
tls_set_errorx(ctx, "ocsp verify failed: revoked cert - %s",
OCSP_crl_reason_str(crl_reason));
goto err;
}
ret = 0;
err:
sk_X509_free(combined);
OCSP_CERTID_free(cid);
OCSP_BASICRESP_free(br);
return ret;
}
/*
* Process a raw OCSP response from an OCSP server request.
* OCSP details can then be retrieved with tls_peer_ocsp_* functions.
* returns 0 if certificate ok, -1 otherwise.
*/
static int
tls_ocsp_process_response_internal(struct tls *ctx, const unsigned char *response,
size_t size)
{
int ret;
OCSP_RESPONSE *resp;
resp = d2i_OCSP_RESPONSE(NULL, &response, size);
if (resp == NULL) {
tls_ocsp_free(ctx->ocsp);
ctx->ocsp = NULL;
tls_set_error(ctx, "unable to parse OCSP response");
return -1;
}
ret = tls_ocsp_verify_response(ctx, resp);
OCSP_RESPONSE_free(resp);
return ret;
}
/* TLS handshake verification callback for stapled requests */
int
tls_ocsp_verify_cb(SSL *ssl, void *arg)
{
const unsigned char *raw = NULL;
int size, res = -1;
struct tls *ctx;
if ((ctx = SSL_get_app_data(ssl)) == NULL)
return -1;
size = SSL_get_tlsext_status_ocsp_resp(ssl, &raw);
if (size <= 0) {
if (ctx->config->ocsp_require_stapling) {
tls_set_errorx(ctx, "no stapled OCSP response provided");
return 0;
}
return 1;
}
tls_ocsp_free(ctx->ocsp);
if ((ctx->ocsp = tls_ocsp_setup_from_peer(ctx)) == NULL)
return 0;
if (ctx->config->verify_cert == 0 || ctx->config->verify_time == 0)
return 1;
res = tls_ocsp_process_response_internal(ctx, raw, size);
return (res == 0) ? 1 : 0;
}
/* Staple the OCSP information in ctx->ocsp to the server handshake. */
int
tls_ocsp_stapling_cb(SSL *ssl, void *arg)
{
int ret = SSL_TLSEXT_ERR_ALERT_FATAL;
unsigned char *ocsp_staple = NULL;
struct tls *ctx;
if ((ctx = SSL_get_app_data(ssl)) == NULL)
goto err;
if (ctx->keypair == NULL || ctx->keypair->ocsp_staple == NULL ||
ctx->keypair->ocsp_staple_len == 0)
return SSL_TLSEXT_ERR_NOACK;
if ((ocsp_staple = malloc(ctx->keypair->ocsp_staple_len)) == NULL)
goto err;
memcpy(ocsp_staple, ctx->keypair->ocsp_staple,
ctx->keypair->ocsp_staple_len);
if (SSL_set_tlsext_status_ocsp_resp(ctx->ssl_conn, ocsp_staple,
ctx->keypair->ocsp_staple_len) != 1)
goto err;
ret = SSL_TLSEXT_ERR_OK;
err:
if (ret != SSL_TLSEXT_ERR_OK)
free(ocsp_staple);
return ret;
}
/*
* Public API
*/
/* Retrieve OCSP URL from peer certificate, if present. */
const char *
tls_peer_ocsp_url(struct tls *ctx)
{
if (ctx->ocsp == NULL)
return NULL;
return ctx->ocsp->ocsp_url;
}
const char *
tls_peer_ocsp_result(struct tls *ctx)
{
if (ctx->ocsp == NULL)
return NULL;
if (ctx->ocsp->ocsp_result == NULL)
return NULL;
return ctx->ocsp->ocsp_result->result_msg;
}
int
tls_peer_ocsp_response_status(struct tls *ctx)
{
if (ctx->ocsp == NULL)
return -1;
if (ctx->ocsp->ocsp_result == NULL)
return -1;
return ctx->ocsp->ocsp_result->response_status;
}
int
tls_peer_ocsp_cert_status(struct tls *ctx)
{
if (ctx->ocsp == NULL)
return -1;
if (ctx->ocsp->ocsp_result == NULL)
return -1;
return ctx->ocsp->ocsp_result->cert_status;
}
int
tls_peer_ocsp_crl_reason(struct tls *ctx)
{
if (ctx->ocsp == NULL)
return -1;
if (ctx->ocsp->ocsp_result == NULL)
return -1;
return ctx->ocsp->ocsp_result->crl_reason;
}
time_t
tls_peer_ocsp_this_update(struct tls *ctx)
{
if (ctx->ocsp == NULL)
return -1;
if (ctx->ocsp->ocsp_result == NULL)
return -1;
return ctx->ocsp->ocsp_result->this_update;
}
time_t
tls_peer_ocsp_next_update(struct tls *ctx)
{
if (ctx->ocsp == NULL)
return -1;
if (ctx->ocsp->ocsp_result == NULL)
return -1;
return ctx->ocsp->ocsp_result->next_update;
}
time_t
tls_peer_ocsp_revocation_time(struct tls *ctx)
{
if (ctx->ocsp == NULL)
return -1;
if (ctx->ocsp->ocsp_result == NULL)
return -1;
return ctx->ocsp->ocsp_result->revocation_time;
}
int
tls_ocsp_process_response(struct tls *ctx, const unsigned char *response,
size_t size)
{
if ((ctx->state & TLS_HANDSHAKE_COMPLETE) == 0)
return -1;
return tls_ocsp_process_response_internal(ctx, response, size);
}

99
tls/tls_peer.c Normal file
View File

@@ -0,0 +1,99 @@
/* $OpenBSD: tls_peer.c,v 1.8 2017/04/10 17:11:13 jsing Exp $ */
/*
* Copyright (c) 2015 Joel Sing <jsing@openbsd.org>
* Copyright (c) 2015 Bob Beck <beck@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 <stdio.h>
#include <openssl/x509.h>
#include <tls.h>
#include "tls_internal.h"
const char *
tls_peer_cert_hash(struct tls *ctx)
{
if (ctx->conninfo == NULL)
return (NULL);
return (ctx->conninfo->hash);
}
const char *
tls_peer_cert_issuer(struct tls *ctx)
{
if (ctx->conninfo == NULL)
return (NULL);
return (ctx->conninfo->issuer);
}
const char *
tls_peer_cert_subject(struct tls *ctx)
{
if (ctx->conninfo == NULL)
return (NULL);
return (ctx->conninfo->subject);
}
int
tls_peer_cert_provided(struct tls *ctx)
{
return (ctx->ssl_peer_cert != NULL);
}
int
tls_peer_cert_contains_name(struct tls *ctx, const char *name)
{
int match;
if (ctx->ssl_peer_cert == NULL)
return (0);
if (tls_check_name(ctx, ctx->ssl_peer_cert, name, &match) == -1)
return (0);
return (match);
}
time_t
tls_peer_cert_notbefore(struct tls *ctx)
{
if (ctx->ssl_peer_cert == NULL)
return (-1);
if (ctx->conninfo == NULL)
return (-1);
return (ctx->conninfo->notbefore);
}
time_t
tls_peer_cert_notafter(struct tls *ctx)
{
if (ctx->ssl_peer_cert == NULL)
return (-1);
if (ctx->conninfo == NULL)
return (-1);
return (ctx->conninfo->notafter);
}
const uint8_t *
tls_peer_cert_chain_pem(struct tls *ctx, size_t *size)
{
if (ctx->ssl_peer_cert == NULL)
return (NULL);
if (ctx->conninfo == NULL)
return (NULL);
*size = ctx->conninfo->peer_cert_len;
return (ctx->conninfo->peer_cert);
}

468
tls/tls_server.c Normal file
View File

@@ -0,0 +1,468 @@
/* $OpenBSD: tls_server.c,v 1.49 2023/05/14 07:26:25 op Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@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 <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <openssl/ec.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <tls.h>
#include "tls_internal.h"
struct tls *
tls_server(void)
{
struct tls *ctx;
if (tls_init() == -1)
return (NULL);
if ((ctx = tls_new()) == NULL)
return (NULL);
ctx->flags |= TLS_SERVER;
return (ctx);
}
struct tls *
tls_server_conn(struct tls *ctx)
{
struct tls *conn_ctx;
if ((conn_ctx = tls_new()) == NULL)
return (NULL);
conn_ctx->flags |= TLS_SERVER_CONN;
pthread_mutex_lock(&ctx->config->mutex);
ctx->config->refcount++;
pthread_mutex_unlock(&ctx->config->mutex);
conn_ctx->config = ctx->config;
conn_ctx->keypair = ctx->config->keypair;
return (conn_ctx);
}
static int
tls_server_alpn_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen, void *arg)
{
struct tls *ctx = arg;
if (SSL_select_next_proto((unsigned char**)out, outlen,
ctx->config->alpn, ctx->config->alpn_len, in, inlen) ==
OPENSSL_NPN_NEGOTIATED)
return (SSL_TLSEXT_ERR_OK);
return (SSL_TLSEXT_ERR_NOACK);
}
static int
tls_servername_cb(SSL *ssl, int *al, void *arg)
{
struct tls *ctx = (struct tls *)arg;
struct tls_sni_ctx *sni_ctx;
union tls_addr addrbuf;
struct tls *conn_ctx;
const char *name;
int match;
if ((conn_ctx = SSL_get_app_data(ssl)) == NULL)
goto err;
if ((name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name)) ==
NULL) {
/*
* The servername callback gets called even when there is no
* TLS servername extension provided by the client. Sigh!
*/
return (SSL_TLSEXT_ERR_NOACK);
}
/*
* Per RFC 6066 section 3: ensure that name is not an IP literal.
*
* While we should treat this as an error, a number of clients
* (Python, Ruby and Safari) are not RFC compliant. To avoid handshake
* failures, pretend that we did not receive the extension.
*/
if (inet_pton(AF_INET, name, &addrbuf) == 1 ||
inet_pton(AF_INET6, name, &addrbuf) == 1)
return (SSL_TLSEXT_ERR_NOACK);
free(conn_ctx->servername);
if ((conn_ctx->servername = strdup(name)) == NULL)
goto err;
/* Find appropriate SSL context for requested servername. */
for (sni_ctx = ctx->sni_ctx; sni_ctx != NULL; sni_ctx = sni_ctx->next) {
if (tls_check_name(ctx, sni_ctx->ssl_cert, name,
&match) == -1)
goto err;
if (match) {
conn_ctx->keypair = sni_ctx->keypair;
SSL_set_SSL_CTX(conn_ctx->ssl_conn, sni_ctx->ssl_ctx);
return (SSL_TLSEXT_ERR_OK);
}
}
/* No match, use the existing context/certificate. */
return (SSL_TLSEXT_ERR_OK);
err:
/*
* There is no way to tell libssl that an internal failure occurred.
* The only option we have is to return a fatal alert.
*/
*al = SSL_AD_INTERNAL_ERROR;
return (SSL_TLSEXT_ERR_ALERT_FATAL);
}
static struct tls_ticket_key *
tls_server_ticket_key(struct tls_config *config, unsigned char *keyname)
{
struct tls_ticket_key *key = NULL;
time_t now;
int i;
now = time(NULL);
if (config->ticket_autorekey == 1) {
if (now - 3 * (config->session_lifetime / 4) >
config->ticket_keys[0].time) {
if (tls_config_ticket_autorekey(config) == -1)
return (NULL);
}
}
for (i = 0; i < TLS_NUM_TICKETS; i++) {
struct tls_ticket_key *tk = &config->ticket_keys[i];
if (now - config->session_lifetime > tk->time)
continue;
if (keyname == NULL || timingsafe_memcmp(keyname,
tk->key_name, sizeof(tk->key_name)) == 0) {
key = tk;
break;
}
}
return (key);
}
static int
tls_server_ticket_cb(SSL *ssl, unsigned char *keyname, unsigned char *iv,
EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int mode)
{
struct tls_ticket_key *key;
struct tls *tls_ctx;
if ((tls_ctx = SSL_get_app_data(ssl)) == NULL)
return (-1);
if (mode == 1) {
/* create new session */
key = tls_server_ticket_key(tls_ctx->config, NULL);
if (key == NULL) {
tls_set_errorx(tls_ctx, "no valid ticket key found");
return (-1);
}
memcpy(keyname, key->key_name, sizeof(key->key_name));
arc4random_buf(iv, EVP_MAX_IV_LENGTH);
if (!EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL,
key->aes_key, iv)) {
tls_set_errorx(tls_ctx, "failed to init encrypt");
return (-1);
}
if (!HMAC_Init_ex(hctx, key->hmac_key, sizeof(key->hmac_key),
EVP_sha256(), NULL)) {
tls_set_errorx(tls_ctx, "failed to init hmac");
return (-1);
}
return (0);
} else {
/* get key by name */
key = tls_server_ticket_key(tls_ctx->config, keyname);
if (key == NULL)
return (0);
if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL,
key->aes_key, iv)) {
tls_set_errorx(tls_ctx, "failed to init decrypt");
return (-1);
}
if (!HMAC_Init_ex(hctx, key->hmac_key, sizeof(key->hmac_key),
EVP_sha256(), NULL)) {
tls_set_errorx(tls_ctx, "failed to init hmac");
return (-1);
}
/* time to renew the ticket? is it the primary key? */
if (key != &tls_ctx->config->ticket_keys[0])
return (2);
return (1);
}
}
static int
tls_configure_server_ssl(struct tls *ctx, SSL_CTX **ssl_ctx,
struct tls_keypair *keypair)
{
SSL_CTX_free(*ssl_ctx);
if ((*ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
tls_set_errorx(ctx, "ssl context failure");
goto err;
}
SSL_CTX_set_options(*ssl_ctx, SSL_OP_NO_CLIENT_RENEGOTIATION);
if (SSL_CTX_set_tlsext_servername_callback(*ssl_ctx,
tls_servername_cb) != 1) {
tls_set_error(ctx, "failed to set servername callback");
goto err;
}
if (SSL_CTX_set_tlsext_servername_arg(*ssl_ctx, ctx) != 1) {
tls_set_error(ctx, "failed to set servername callback arg");
goto err;
}
if (tls_configure_ssl(ctx, *ssl_ctx) != 0)
goto err;
if (tls_configure_ssl_keypair(ctx, *ssl_ctx, keypair, 1) != 0)
goto err;
if (ctx->config->verify_client != 0) {
int verify = SSL_VERIFY_PEER;
if (ctx->config->verify_client == 1)
verify |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
if (tls_configure_ssl_verify(ctx, *ssl_ctx, verify) == -1)
goto err;
}
if (ctx->config->alpn != NULL)
SSL_CTX_set_alpn_select_cb(*ssl_ctx, tls_server_alpn_cb,
ctx);
if (ctx->config->dheparams == -1)
SSL_CTX_set_dh_auto(*ssl_ctx, 1);
else if (ctx->config->dheparams == 1024)
SSL_CTX_set_dh_auto(*ssl_ctx, 2);
if (ctx->config->ecdhecurves != NULL) {
SSL_CTX_set_ecdh_auto(*ssl_ctx, 1);
if (SSL_CTX_set1_groups(*ssl_ctx, ctx->config->ecdhecurves,
ctx->config->ecdhecurves_len) != 1) {
tls_set_errorx(ctx, "failed to set ecdhe curves");
goto err;
}
}
if (ctx->config->ciphers_server == 1)
SSL_CTX_set_options(*ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
if (SSL_CTX_set_tlsext_status_cb(*ssl_ctx, tls_ocsp_stapling_cb) != 1) {
tls_set_errorx(ctx, "failed to add OCSP stapling callback");
goto err;
}
if (ctx->config->session_lifetime > 0) {
/* set the session lifetime and enable tickets */
SSL_CTX_set_timeout(*ssl_ctx, ctx->config->session_lifetime);
SSL_CTX_clear_options(*ssl_ctx, SSL_OP_NO_TICKET);
if (!SSL_CTX_set_tlsext_ticket_key_cb(*ssl_ctx,
tls_server_ticket_cb)) {
tls_set_error(ctx,
"failed to set the TLS ticket callback");
goto err;
}
}
if (SSL_CTX_set_session_id_context(*ssl_ctx, ctx->config->session_id,
sizeof(ctx->config->session_id)) != 1) {
tls_set_error(ctx, "failed to set session id context");
goto err;
}
return (0);
err:
SSL_CTX_free(*ssl_ctx);
*ssl_ctx = NULL;
return (-1);
}
static int
tls_configure_server_sni(struct tls *ctx)
{
struct tls_sni_ctx **sni_ctx;
struct tls_keypair *kp;
if (ctx->config->keypair->next == NULL)
return (0);
/* Set up additional SSL contexts for SNI. */
sni_ctx = &ctx->sni_ctx;
for (kp = ctx->config->keypair->next; kp != NULL; kp = kp->next) {
if ((*sni_ctx = tls_sni_ctx_new()) == NULL) {
tls_set_errorx(ctx, "out of memory");
goto err;
}
(*sni_ctx)->keypair = kp;
if (tls_configure_server_ssl(ctx, &(*sni_ctx)->ssl_ctx, kp) == -1)
goto err;
if (tls_keypair_load_cert(kp, &ctx->error,
&(*sni_ctx)->ssl_cert) == -1)
goto err;
sni_ctx = &(*sni_ctx)->next;
}
return (0);
err:
return (-1);
}
int
tls_configure_server(struct tls *ctx)
{
if (tls_configure_server_ssl(ctx, &ctx->ssl_ctx,
ctx->config->keypair) == -1)
goto err;
if (tls_configure_server_sni(ctx) == -1)
goto err;
return (0);
err:
return (-1);
}
static struct tls *
tls_accept_common(struct tls *ctx)
{
struct tls *conn_ctx = NULL;
if ((ctx->flags & TLS_SERVER) == 0) {
tls_set_errorx(ctx, "not a server context");
goto err;
}
if ((conn_ctx = tls_server_conn(ctx)) == NULL) {
tls_set_errorx(ctx, "connection context failure");
goto err;
}
if ((conn_ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) {
tls_set_errorx(ctx, "ssl failure");
goto err;
}
if (SSL_set_app_data(conn_ctx->ssl_conn, conn_ctx) != 1) {
tls_set_errorx(ctx, "ssl application data failure");
goto err;
}
return conn_ctx;
err:
tls_free(conn_ctx);
return (NULL);
}
int
tls_accept_socket(struct tls *ctx, struct tls **cctx, int s)
{
return (tls_accept_fds(ctx, cctx, s, s));
}
int
tls_accept_fds(struct tls *ctx, struct tls **cctx, int fd_read, int fd_write)
{
struct tls *conn_ctx;
if ((conn_ctx = tls_accept_common(ctx)) == NULL)
goto err;
if (SSL_set_rfd(conn_ctx->ssl_conn, fd_read) != 1 ||
SSL_set_wfd(conn_ctx->ssl_conn, fd_write) != 1) {
tls_set_errorx(ctx, "ssl file descriptor failure");
goto err;
}
*cctx = conn_ctx;
return (0);
err:
tls_free(conn_ctx);
*cctx = NULL;
return (-1);
}
int
tls_accept_cbs(struct tls *ctx, struct tls **cctx,
tls_read_cb read_cb, tls_write_cb write_cb, void *cb_arg)
{
struct tls *conn_ctx;
if ((conn_ctx = tls_accept_common(ctx)) == NULL)
goto err;
if (tls_set_cbs(conn_ctx, read_cb, write_cb, cb_arg) != 0)
goto err;
*cctx = conn_ctx;
return (0);
err:
tls_free(conn_ctx);
*cctx = NULL;
return (-1);
}
int
tls_handshake_server(struct tls *ctx)
{
int ssl_ret;
int rv = -1;
if ((ctx->flags & TLS_SERVER_CONN) == 0) {
tls_set_errorx(ctx, "not a server connection context");
goto err;
}
ctx->state |= TLS_SSL_NEEDS_SHUTDOWN;
ERR_clear_error();
if ((ssl_ret = SSL_accept(ctx->ssl_conn)) != 1) {
rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "handshake");
goto err;
}
ctx->state |= TLS_HANDSHAKE_COMPLETE;
rv = 0;
err:
return (rv);
}

451
tls/tls_signer.c Normal file
View File

@@ -0,0 +1,451 @@
/* $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 <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;
};
static pthread_mutex_t signer_method_lock = PTHREAD_MUTEX_INITIALIZER;
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;
pthread_mutex_lock(&signer_method_lock);
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:
pthread_mutex_unlock(&signer_method_lock);
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);
pthread_mutex_lock(&signer_method_lock);
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:
pthread_mutex_unlock(&signer_method_lock);
return (ecdsa_method);
}

226
tls/tls_util.c Normal file
View File

@@ -0,0 +1,226 @@
/* $OpenBSD: tls_util.c,v 1.16 2023/05/14 07:26:25 op Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
* Copyright (c) 2014 Ted Unangst <tedu@openbsd.org>
* Copyright (c) 2015 Reyk Floeter <reyk@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 <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "tls.h"
#include "tls_internal.h"
static void *
memdup(const void *in, size_t len)
{
void *out;
if ((out = malloc(len)) == NULL)
return NULL;
memcpy(out, in, len);
return out;
}
int
tls_set_mem(char **dest, size_t *destlen, const void *src, size_t srclen)
{
free(*dest);
*dest = NULL;
*destlen = 0;
if (src != NULL) {
if ((*dest = memdup(src, srclen)) == NULL)
return -1;
*destlen = srclen;
}
return 0;
}
int
tls_set_string(const char **dest, const char *src)
{
free((char *)*dest);
*dest = NULL;
if (src != NULL)
if ((*dest = strdup(src)) == NULL)
return -1;
return 0;
}
/*
* Extract the host and port from a colon separated value. For a literal IPv6
* address the address must be contained with square braces. If a host and
* port are successfully extracted, the function will return 0 and the
* caller is responsible for freeing the host and port. If no port is found
* then the function will return 1, with both host and port being NULL.
* On memory allocation failure -1 will be returned.
*/
int
tls_host_port(const char *hostport, char **host, char **port)
{
char *h, *p, *s;
int rv = 1;
*host = NULL;
*port = NULL;
if ((s = strdup(hostport)) == NULL)
goto err;
h = p = s;
/* See if this is an IPv6 literal with square braces. */
if (p[0] == '[') {
h++;
if ((p = strchr(s, ']')) == NULL)
goto done;
*p++ = '\0';
}
/* Find the port separator. */
if ((p = strchr(p, ':')) == NULL)
goto done;
/* If there is another separator then we have issues. */
if (strchr(p + 1, ':') != NULL)
goto done;
*p++ = '\0';
if (asprintf(host, "%s", h) == -1) {
*host = NULL;
goto err;
}
if (asprintf(port, "%s", p) == -1) {
*port = NULL;
goto err;
}
rv = 0;
goto done;
err:
free(*host);
*host = NULL;
free(*port);
*port = NULL;
rv = -1;
done:
free(s);
return (rv);
}
int
tls_password_cb(char *buf, int size, int rwflag, void *u)
{
size_t len;
if (size < 0)
return (0);
if (u == NULL) {
memset(buf, 0, size);
return (0);
}
if ((len = strlcpy(buf, u, size)) >= (size_t)size)
return (0);
return (len);
}
uint8_t *
tls_load_file(const char *name, size_t *len, char *password)
{
FILE *fp;
EVP_PKEY *key = NULL;
BIO *bio = NULL;
char *data;
uint8_t *buf = NULL;
struct stat st;
size_t size = 0;
int fd = -1;
ssize_t n;
*len = 0;
if ((fd = open(name, O_RDONLY)) == -1)
return (NULL);
/* Just load the file into memory without decryption */
if (password == NULL) {
if (fstat(fd, &st) != 0)
goto err;
if (st.st_size < 0)
goto err;
size = (size_t)st.st_size;
if ((buf = malloc(size)) == NULL)
goto err;
n = read(fd, buf, size);
if (n < 0 || (size_t)n != size)
goto err;
close(fd);
goto done;
}
/* Or read the (possibly) encrypted key from file */
if ((fp = fdopen(fd, "r")) == NULL)
goto err;
fd = -1;
key = PEM_read_PrivateKey(fp, NULL, tls_password_cb, password);
fclose(fp);
if (key == NULL)
goto err;
/* Write unencrypted key to memory buffer */
if ((bio = BIO_new(BIO_s_mem())) == NULL)
goto err;
if (!PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL))
goto err;
if ((size = BIO_get_mem_data(bio, &data)) <= 0)
goto err;
if ((buf = malloc(size)) == NULL)
goto err;
memcpy(buf, data, size);
BIO_free_all(bio);
EVP_PKEY_free(key);
done:
*len = size;
return (buf);
err:
if (fd != -1)
close(fd);
freezero(buf, size);
BIO_free_all(bio);
EVP_PKEY_free(key);
return (NULL);
}
void
tls_unload_file(uint8_t *buf, size_t len)
{
freezero(buf, len);
}

331
tls/tls_verify.c Normal file
View File

@@ -0,0 +1,331 @@
/* $OpenBSD: tls_verify.c,v 1.28 2023/06/01 07:32:25 tb Exp $ */
/*
* Copyright (c) 2014 Jeremie Courreges-Anglas <jca@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 <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <openssl/x509v3.h>
#include <tls.h>
#include "tls_internal.h"
static int
tls_match_name(const char *cert_name, const char *name)
{
const char *cert_domain, *domain, *next_dot;
if (strcasecmp(cert_name, name) == 0)
return 0;
/* Wildcard match? */
if (cert_name[0] == '*') {
/*
* Valid wildcards:
* - "*.domain.tld"
* - "*.sub.domain.tld"
* - etc.
* Reject "*.tld".
* No attempt to prevent the use of eg. "*.co.uk".
*/
cert_domain = &cert_name[1];
/* Disallow "*" */
if (cert_domain[0] == '\0')
return -1;
/* Disallow "*foo" */
if (cert_domain[0] != '.')
return -1;
/* Disallow "*.." */
if (cert_domain[1] == '.')
return -1;
next_dot = strchr(&cert_domain[1], '.');
/* Disallow "*.bar" */
if (next_dot == NULL)
return -1;
/* Disallow "*.bar.." */
if (next_dot[1] == '.')
return -1;
domain = strchr(name, '.');
/* No wildcard match against a name with no host part. */
if (name[0] == '.')
return -1;
/* No wildcard match against a name with no domain part. */
if (domain == NULL || strlen(domain) == 1)
return -1;
if (strcasecmp(cert_domain, domain) == 0)
return 0;
}
return -1;
}
/*
* See RFC 5280 section 4.2.1.6 for SubjectAltName details.
* alt_match is set to 1 if a matching alternate name is found.
* alt_exists is set to 1 if any known alternate name exists in the certificate.
*/
static int
tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name,
int *alt_match, int *alt_exists)
{
STACK_OF(GENERAL_NAME) *altname_stack = NULL;
union tls_addr addrbuf;
int addrlen, type;
int count, i;
int critical = 0;
int rv = -1;
*alt_match = 0;
*alt_exists = 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;
addrlen = 4;
} else if (inet_pton(AF_INET6, name, &addrbuf) == 1) {
type = GEN_IPADD;
addrlen = 16;
} else {
type = GEN_DNS;
addrlen = 0;
}
count = sk_GENERAL_NAME_num(altname_stack);
for (i = 0; i < count; i++) {
GENERAL_NAME *altname;
altname = sk_GENERAL_NAME_value(altname_stack, i);
if (altname->type == GEN_DNS || altname->type == GEN_IPADD)
*alt_exists = 1;
if (altname->type != type)
continue;
if (type == GEN_DNS) {
const unsigned char *data;
int format, len;
format = ASN1_STRING_type(altname->d.dNSName);
if (format == V_ASN1_IA5STRING) {
data = ASN1_STRING_get0_data(altname->d.dNSName);
len = ASN1_STRING_length(altname->d.dNSName);
if (len < 0 || (size_t)len != strlen(data)) {
tls_set_errorx(ctx,
"error verifying name '%s': "
"NUL byte in subjectAltName, "
"probably a malicious certificate",
name);
goto err;
}
/*
* Per RFC 5280 section 4.2.1.6:
* " " is a legal domain name, but that
* dNSName must be rejected.
*/
if (strcmp(data, " ") == 0) {
tls_set_errorx(ctx,
"error verifying name '%s': "
"a dNSName of \" \" must not be "
"used", name);
goto err;
}
if (tls_match_name(data, name) == 0) {
*alt_match = 1;
goto done;
}
} else {
#ifdef DEBUG
fprintf(stdout, "%s: unhandled subjectAltName "
"dNSName encoding (%d)\n", getprogname(),
format);
#endif
}
} else if (type == GEN_IPADD) {
const unsigned char *data;
int datalen;
datalen = ASN1_STRING_length(altname->d.iPAddress);
data = ASN1_STRING_get0_data(altname->d.iPAddress);
if (datalen < 0) {
tls_set_errorx(ctx,
"Unexpected negative length for an "
"IP address: %d", datalen);
goto err;
}
/*
* Per RFC 5280 section 4.2.1.6:
* IPv4 must use 4 octets and IPv6 must use 16 octets.
*/
if (datalen == addrlen &&
memcmp(data, &addrbuf, addrlen) == 0) {
*alt_match = 1;
goto done;
}
}
}
done:
rv = 0;
err:
sk_GENERAL_NAME_pop_free(altname_stack, GENERAL_NAME_free);
return rv;
}
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;
subject_name = X509_get_subject_name(cert);
if (subject_name == NULL)
goto done;
lastpos = X509_NAME_get_index_by_NID(subject_name,
NID_commonName, lastpos);
if (lastpos == -1)
goto done;
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 mutiple Common Name fields, "
"probably a malicious or malformed certificate", name);
goto err;
}
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.
*/
if (inet_pton(AF_INET, name, &addrbuf) == 1 ||
inet_pton(AF_INET6, name, &addrbuf) == 1) {
if (strcmp(common_name, name) == 0)
*cn_match = 1;
goto done;
}
if (tls_match_name(common_name, name) == 0)
*cn_match = 1;
done:
rv = 0;
err:
free(utf8_bytes);
free(common_name);
return rv;
}
int
tls_check_name(struct tls *ctx, X509 *cert, const char *name, int *match)
{
int alt_exists;
*match = 0;
if (tls_check_subject_altname(ctx, cert, name, match,
&alt_exists) == -1)
return -1;
/*
* As per RFC 6125 section 6.4.4, if any known alternate name existed
* in the certificate, we do not attempt to match on the CN.
*/
if (*match || alt_exists)
return 0;
return tls_check_common_name(ctx, cert, name, match);
}