machinarium: integrate with OpenBSD tls library

This commit is contained in:
Dmitry Simonenko 2017-03-23 16:02:45 +03:00
parent b0f195b9b4
commit b0b8ae0ce8
18 changed files with 4890 additions and 2 deletions

View File

@ -10,9 +10,9 @@ if ("${CMAKE_BUILD_TYPE}" STREQUAL "")
endif()
if ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
set(CMAKE_C_FLAGS "-std=gnu99 -Wall -Wextra -fPIC -g -O2")
set(CMAKE_C_FLAGS "-std=gnu99 -fPIC -g -O2 -D_GNU_SOURCE")
elseif("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
set(CMAKE_C_FLAGS "-std=gnu99 -Wall -Wextra -fPIC -g -O0")
set(CMAKE_C_FLAGS "-std=gnu99 -fPIC -g -O0 -D_GNU_SOURCE")
endif()
include_directories("${PROJECT_SOURCE_DIR}/src")
@ -33,6 +33,11 @@ include_directories(${LIBUV_INCLUDE_DIRS})
include_directories("${PROJECT_SOURCE_DIR}/lib/libcoro")
include_directories("${PROJECT_BINARY_DIR}/lib/libcoro")
# libtls
include_directories("${PROJECT_SOURCE_DIR}/lib/libtls")
include_directories("${PROJECT_BINARY_DIR}/lib/libtls")
add_subdirectory(lib/libtls)
add_subdirectory(src)
message (STATUS "")

14
lib/libtls/CMakeLists.txt Normal file
View File

@ -0,0 +1,14 @@
set(tls_library tls)
set(tls_src
tls.c
tls_client.c
tls_config.c
tls_conninfo.c
tls_server.c
tls_ocsp.c
tls_peer.c
tls_util.c
tls_verify.c
tls_compat.c)
add_library(tls_library_static STATIC ${tls_src})
set_target_properties(tls_library_static PROPERTIES OUTPUT_NAME ${tls_library})

640
lib/libtls/tls.c Normal file
View File

@ -0,0 +1,640 @@
/* $OpenBSD: tls.c,v 1.11 2015/04/15 16:08:43 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 <limits.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/dh.h>
#include "tls_internal.h"
#include "tls_compat.h"
#include "tls.h"
static struct tls_config *tls_config_default;
static int tls_initialised = 0;
int
tls_init(void)
{
if (tls_initialised)
return (0);
SSL_load_error_strings();
SSL_library_init();
if (BIO_sock_init() != 1)
return (-1);
if ((tls_config_default = tls_config_new()) == NULL)
return (-1);
tls_initialised = 1;
return (0);
}
void
tls_deinit(void)
{
if (tls_initialised) {
tls_compat_cleanup();
tls_config_free(tls_config_default);
tls_config_default = NULL;
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();
BIO_sock_cleanup();
ERR_clear_error();
ERR_remove_thread_state(NULL);
ERR_free_strings();
tls_initialised = 0;
}
}
const char *
tls_error(struct tls *ctx)
{
return ctx->errmsg;
}
static int
tls_set_verror(struct tls *ctx, int errnum, const char *fmt, va_list ap)
{
char *errmsg = NULL;
int rv = -1;
free(ctx->errmsg);
ctx->errmsg = NULL;
if (vasprintf(&errmsg, fmt, ap) == -1) {
errmsg = NULL;
goto err;
}
if (errnum == -1) {
ctx->errmsg = errmsg;
return (0);
}
if (asprintf(&ctx->errmsg, "%s: %s", errmsg, strerror(errnum)) == -1) {
ctx->errmsg = NULL;
goto err;
}
rv = 0;
err:
free(errmsg);
return (rv);
}
int
tls_set_error(struct tls *ctx, const char *fmt, ...)
{
va_list ap;
int rv;
ctx->errnum = errno;
va_start(ap, fmt);
rv = tls_set_verror(ctx, ctx->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_set_verror(ctx, -1, fmt, ap);
va_end(ap);
return (rv);
}
int
tls_set_error_libssl(struct tls *ctx, const char *fmt, ...)
{
va_list ap;
int rv;
const char *msg = NULL;
char *old;
int err;
err = ERR_peek_error();
if (err != 0)
msg = ERR_reason_error_string(err);
va_start(ap, fmt);
rv = tls_set_verror(ctx, -1, fmt, ap);
va_end(ap);
if (rv != 0 || msg == NULL)
return rv;
old = ctx->errmsg;
ctx->errmsg = NULL;
if (asprintf(&ctx->errmsg, "%s: %s", old, msg) == -1) {
ctx->errmsg = old;
return 0;
}
free(old);
return 0;
}
struct tls *
tls_new(void)
{
struct tls *ctx;
if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
return (NULL);
ctx->config = tls_config_default;
tls_reset(ctx);
return (ctx);
}
int
tls_configure(struct tls *ctx, struct tls_config *config)
{
if (config == NULL)
config = tls_config_default;
ctx->config = config;
if ((ctx->flags & TLS_SERVER) != 0)
return (tls_configure_server(ctx));
return (0);
}
int
tls_configure_keypair(struct tls *ctx, int required)
{
EVP_PKEY *pkey = NULL;
X509 *cert = NULL;
BIO *bio = NULL;
if (!required &&
ctx->config->cert_mem == NULL &&
ctx->config->key_mem == NULL &&
ctx->config->cert_file == NULL &&
ctx->config->key_file == NULL)
return(0);
if (ctx->config->cert_mem != NULL) {
if (ctx->config->cert_len > INT_MAX) {
tls_set_errorx(ctx, "certificate too long");
goto err;
}
if (SSL_CTX_use_certificate_chain_mem(ctx->ssl_ctx,
ctx->config->cert_mem, ctx->config->cert_len) != 1) {
tls_set_errorx(ctx, "failed to load certificate");
goto err;
}
cert = NULL;
}
if (ctx->config->key_mem != NULL) {
if (ctx->config->key_len > INT_MAX) {
tls_set_errorx(ctx, "key too long");
goto err;
}
if ((bio = BIO_new_mem_buf(ctx->config->key_mem,
ctx->config->key_len)) == NULL) {
tls_set_errorx(ctx, "failed to create buffer");
goto err;
}
if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL,
NULL)) == NULL) {
tls_set_errorx(ctx, "failed to read private key");
goto err;
}
if (SSL_CTX_use_PrivateKey(ctx->ssl_ctx, pkey) != 1) {
tls_set_errorx(ctx, "failed to load private key");
goto err;
}
BIO_free(bio);
bio = NULL;
EVP_PKEY_free(pkey);
pkey = NULL;
}
if (ctx->config->cert_file != NULL) {
if (SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx,
ctx->config->cert_file) != 1) {
tls_set_errorx(ctx, "failed to load certificate file");
goto err;
}
}
if (ctx->config->key_file != NULL) {
if (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx,
ctx->config->key_file, SSL_FILETYPE_PEM) != 1) {
tls_set_errorx(ctx, "failed to load private key file");
goto err;
}
}
if (SSL_CTX_check_private_key(ctx->ssl_ctx) != 1) {
tls_set_errorx(ctx, "private/public key mismatch");
goto err;
}
return (0);
err:
EVP_PKEY_free(pkey);
X509_free(cert);
BIO_free(bio);
return (1);
}
static void
tls_info_callback(const SSL *ssl, int where, int rc)
{
struct tls *ctx = SSL_get_app_data(ssl);
(void)rc;
#ifdef USE_LIBSSL_INTERNALS
if (!(ctx->state & TLS_HANDSHAKE_COMPLETE) && ssl->s3) {
/* steal info about used DH key */
if (ssl->s3->tmp.dh && !ctx->used_dh_bits) {
ctx->used_dh_bits = DH_size(ssl->s3->tmp.dh) * 8;
} else if (ssl->s3->tmp.ecdh && !ctx->used_ecdh_nid) {
ctx->used_ecdh_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ssl->s3->tmp.ecdh));
}
}
#endif
/* detect renegotation on established connection */
if (where & SSL_CB_HANDSHAKE_START) {
if (ctx->state & TLS_HANDSHAKE_COMPLETE)
ctx->state |= TLS_DO_ABORT;
}
}
static int
tls_do_abort(struct tls *ctx)
{
int ssl_ret, rv;
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)
return (rv);
}
tls_set_errorx(ctx, "unexpected handshake, closing connection");
return -1;
}
int
tls_configure_ssl(struct tls *ctx)
{
SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2);
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv3);
SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1);
SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_1);
SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_2);
if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_0) == 0)
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1);
if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_1) == 0)
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_1);
if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_2) == 0)
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_2);
if (ctx->config->ciphers != NULL) {
if (SSL_CTX_set_cipher_list(ctx->ssl_ctx,
ctx->config->ciphers) != 1) {
tls_set_errorx(ctx, "failed to set ciphers");
goto err;
}
}
SSL_CTX_set_info_callback(ctx->ssl_ctx, tls_info_callback);
#ifdef X509_V_FLAG_NO_CHECK_TIME
if (ctx->config->verify_time == 0) {
X509_VERIFY_PARAM *vfp = SSL_CTX_get0_param(ctx->ssl_ctx);
X509_VERIFY_PARAM_set_flags(vfp, X509_V_FLAG_NO_CHECK_TIME);
}
#endif
return (0);
err:
return (-1);
}
int
tls_configure_ssl_verify(struct tls *ctx, int verify)
{
SSL_CTX_set_verify(ctx->ssl_ctx, verify, NULL);
if (ctx->config->ca_mem != NULL) {
/* XXX do this in set. */
if (ctx->config->ca_len > INT_MAX) {
tls_set_errorx(ctx, "ca too long");
goto err;
}
if (SSL_CTX_load_verify_mem(ctx->ssl_ctx,
ctx->config->ca_mem, ctx->config->ca_len) != 1) {
tls_set_errorx(ctx, "ssl verify memory setup failure");
goto err;
}
} else if (SSL_CTX_load_verify_locations(ctx->ssl_ctx,
ctx->config->ca_file, ctx->config->ca_path) != 1) {
tls_set_errorx(ctx, "ssl verify setup failure");
goto err;
}
if (ctx->config->verify_depth >= 0)
SSL_CTX_set_verify_depth(ctx->ssl_ctx,
ctx->config->verify_depth);
return (0);
err:
return (-1);
}
void
tls_free(struct tls *ctx)
{
if (ctx == NULL)
return;
tls_reset(ctx);
free(ctx);
}
void
tls_reset(struct tls *ctx)
{
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;
ctx->socket = -1;
ctx->state = 0;
free(ctx->servername);
ctx->servername = NULL;
free(ctx->errmsg);
ctx->errmsg = NULL;
ctx->errnum = 0;
tls_free_conninfo(ctx->conninfo);
free(ctx->conninfo);
ctx->conninfo = NULL;
ctx->used_dh_bits = 0;
ctx->used_ecdh_nid = 0;
tls_ocsp_info_free(ctx->ocsp_info);
ctx->ocsp_info = NULL;
ctx->ocsp_result = NULL;
if (ctx->flags & TLS_OCSP_CLIENT)
tls_ocsp_client_free(ctx);
}
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) {
errstr = "Unexpected EOF";
} else {
ctx->state |= TLS_EOF_NO_CLOSE_NOTIFY;
return (0);
}
} else if (ssl_ret == -1) {
errstr = strerror(errno);
}
tls_set_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_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_errorx(ctx, "%s failed (%i)", prefix, ssl_err);
return (-1);
}
}
int
tls_handshake(struct tls *ctx)
{
int rv = -1;
if ((ctx->flags & (TLS_CLIENT | TLS_SERVER_CONN)) == 0) {
tls_set_errorx(ctx, "invalid operation for context");
goto out;
}
if (ctx->conninfo == NULL &&
(ctx->conninfo = calloc(1, sizeof(*ctx->conninfo))) == NULL)
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);
if (tls_get_conninfo(ctx) == -1)
rv = -1;
}
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;
if (ctx->state & TLS_DO_ABORT) {
rv = tls_do_abort(ctx);
goto out;
}
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;
if (ctx->state & TLS_DO_ABORT) {
rv = tls_do_abort(ctx);
goto out;
}
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;
if ((ctx->flags & (TLS_CLIENT | TLS_SERVER_CONN)) == 0) {
tls_set_errorx(ctx, "invalid operation for context");
rv = -1;
goto out;
}
if (ctx->ssl_conn != NULL) {
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;
}
}
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);
}

168
lib/libtls/tls.h Normal file
View File

@ -0,0 +1,168 @@
/* $OpenBSD: tls.h,v 1.12 2015/03/31 14:03:38 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.
*/
#ifndef _HEADER_TLS_H_
#define _HEADER_TLS_H_
#include <sys/types.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define TLS_API 20141031
#define TLS_PROTOCOL_TLSv1_0 (1 << 1)
#define TLS_PROTOCOL_TLSv1_1 (1 << 2)
#define TLS_PROTOCOL_TLSv1_2 (1 << 3)
#define TLS_PROTOCOL_TLSv1 \
(TLS_PROTOCOL_TLSv1_0|TLS_PROTOCOL_TLSv1_1|TLS_PROTOCOL_TLSv1_2)
#define TLS_PROTOCOLS_ALL TLS_PROTOCOL_TLSv1
#define TLS_PROTOCOLS_DEFAULT TLS_PROTOCOL_TLSv1_2
#define TLS_WANT_POLLIN -2
#define TLS_WANT_POLLOUT -3
#define TLS_NO_OCSP -4
#define TLS_NO_CERT -5
#define TLS_OCSP_RESPONSE_SUCCESSFUL 0
#define TLS_OCSP_RESPONSE_MALFORMED 1
#define TLS_OCSP_RESPONSE_INTERNALERR 2
#define TLS_OCSP_RESPONSE_TRYLATER 3
#define TLS_OCSP_RESPONSE_SIGREQUIRED 5
#define TLS_OCSP_RESPONSE_UNAUTHORIZED 6
#define TLS_OCSP_CERT_GOOD 0
#define TLS_OCSP_CERT_REVOKED 1
#define TLS_OCSP_CERT_UNKNOWN 2
#define TLS_CRL_REASON_UNPSECIFIED 0
#define TLS_CRL_REASON_KEY_COMPROMISE 1
#define TLS_CRL_REASON_CA_COMPROMISE 2
#define TLS_CRL_REASON_AFFILIATION_CHANGED 3
#define TLS_CRL_REASON_SUPERSEDED 4
#define TLS_CRL_REASON_CESSATION_OF_OPERATION 5
#define TLS_CRL_REASON_CERTIFICATE_HOLD 6
#define TLS_CRL_REASON_REMOVE_FROM_CRL 8
#define TLS_CRL_REASON_PRIVILEGE_WITH_DRAWN 9
#define TLS_CRL_REASON_AA_COMPROMISE 10
struct tls;
struct tls_config;
int tls_init(void);
void tls_deinit(void);
const char *tls_backend_version(void);
const char *tls_error(struct tls *_ctx);
struct tls_config *tls_config_new(void);
void tls_config_free(struct tls_config *_config);
int tls_config_set_ca_file(struct tls_config *_config, const char *_ca_file);
int tls_config_set_ca_path(struct tls_config *_config, const char *_ca_path);
int tls_config_set_ca_mem(struct tls_config *_config, const uint8_t *_ca,
size_t _len);
int tls_config_set_cert_file(struct tls_config *_config,
const char *_cert_file);
int tls_config_set_cert_mem(struct tls_config *_config, const uint8_t *_cert,
size_t _len);
int tls_config_set_ciphers(struct tls_config *_config, const char *_ciphers);
int tls_config_set_dheparams(struct tls_config *_config, const char *_params);
int tls_config_set_ecdhecurve(struct tls_config *_config, const char *_name);
int tls_config_set_key_file(struct tls_config *_config, const char *_key_file);
int tls_config_set_key_mem(struct tls_config *_config, const uint8_t *_key,
size_t _len);
int tls_config_set_ocsp_stapling_file(struct tls_config *_config, const char *_blob_file);
int tls_config_set_ocsp_stapling_mem(struct tls_config *_config, const uint8_t *_blob, size_t _len);
void tls_config_set_protocols(struct tls_config *_config, uint32_t _protocols);
void tls_config_set_verify_depth(struct tls_config *_config, int _verify_depth);
void tls_config_prefer_ciphers_client(struct tls_config *_config);
void tls_config_prefer_ciphers_server(struct tls_config *_config);
void tls_config_insecure_noverifycert(struct tls_config *_config);
void tls_config_insecure_noverifyname(struct tls_config *_config);
void tls_config_insecure_noverifytime(struct tls_config *_config);
void tls_config_verify(struct tls_config *_config);
void tls_config_verify_client(struct tls_config *_config);
void tls_config_verify_client_optional(struct tls_config *_config);
void tls_config_clear_keys(struct tls_config *_config);
int tls_config_parse_protocols(uint32_t *_protocols, const char *_protostr);
struct tls *tls_client(void);
struct tls *tls_server(void);
int tls_configure(struct tls *_ctx, struct tls_config *_config);
void tls_reset(struct tls *_ctx);
void tls_free(struct tls *_ctx);
int tls_accept_fds(struct tls *_ctx, struct tls **_cctx, int _fd_read,
int _fd_write);
int tls_accept_socket(struct tls *_ctx, struct tls **_cctx, int _socket);
int tls_connect(struct tls *_ctx, const char *_host, const char *_port);
int tls_connect_fds(struct tls *_ctx, int _fd_read, int _fd_write,
const char *_servername);
int tls_connect_servername(struct tls *_ctx, const char *_host,
const char *_port, const char *_servername);
int tls_connect_socket(struct tls *_ctx, int _s, const char *_servername);
int tls_handshake(struct tls *_ctx);
ssize_t tls_read(struct tls *_ctx, void *_buf, size_t _buflen);
ssize_t tls_write(struct tls *_ctx, const void *_buf, size_t _buflen);
int tls_close(struct tls *_ctx);
int tls_peer_cert_provided(struct tls *ctx);
int tls_peer_cert_contains_name(struct tls *ctx, const char *name);
const char * tls_peer_cert_hash(struct tls *_ctx);
const char * tls_peer_cert_issuer(struct tls *ctx);
const char * tls_peer_cert_subject(struct tls *ctx);
time_t tls_peer_cert_notbefore(struct tls *ctx);
time_t tls_peer_cert_notafter(struct tls *ctx);
const char * tls_conn_version(struct tls *ctx);
const char * tls_conn_cipher(struct tls *ctx);
uint8_t *tls_load_file(const char *_file, size_t *_len, char *_password);
ssize_t tls_get_connection_info(struct tls *ctx, char *buf, size_t buflen);
int tls_ocsp_refresh_stapling(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls_config *config);
int tls_ocsp_check_peer(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls *client);
int tls_get_ocsp_info(struct tls *ctx, int *response_status, int *cert_status, int *crl_reason,
time_t *this_update, time_t *next_update, time_t *revoction_time,
const char **result_text);
int tls_ocsp_check_peer_request(struct tls **ocsp_ctx_p, struct tls *target,
char **ocsp_url, void **request_blob, size_t *request_size);
int tls_ocsp_refresh_stapling_request(struct tls **ocsp_ctx_p, struct tls_config *config,
char **ocsp_url, void **request_blob, size_t *request_size);
int tls_ocsp_process_response(struct tls *ctx, const void *response_blob, size_t size);
#ifdef __cplusplus
}
#endif
#endif /* HEADER_TLS_H */

674
lib/libtls/tls_cert.c Normal file
View File

@ -0,0 +1,674 @@
/* $OpenBSD: tls_verify.c,v 1.7 2015/02/11 06:46:33 jsing 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 <openssl/bn.h>
#include <openssl/err.h>
#include <openssl/x509v3.h>
#include "tls_internal.h"
#include "tls_compat.h"
#include "tls.h"
#define TLS_CERT_INTERNAL_FUNCS
#include "tls_cert.h"
/*
* Load cert data from X509 cert.
*/
/* Upper bounds */
#define UB_COMMON_NAME 255
#define UB_COUNTRY_NAME 255
#define UB_STATE_NAME 255
#define UB_LOCALITY_NAME 255
#define UB_STREET_ADDRESS 255
#define UB_ORGANIZATION_NAME 255
#define UB_ORGANIZATIONAL_UNIT_NAME 255
#define UB_GNAME_DNS 255
#define UB_GNAME_EMAIL 255
#define UB_GNAME_URI 255
/* Convert ASN1_INTEGER to decimal string string */
static int
tls_parse_bigint(struct tls *ctx, const ASN1_INTEGER *asn1int, const char **dst_p)
{
long small;
BIGNUM *big;
char *tmp, buf[64];
*dst_p = NULL;
small = ASN1_INTEGER_get(asn1int);
if (small < 0) {
big = ASN1_INTEGER_to_BN(asn1int, NULL);
if (big) {
tmp = BN_bn2dec(big);
if (tmp)
*dst_p = strdup(tmp);
OPENSSL_free(tmp);
}
BN_free(big);
} else {
snprintf(buf, sizeof buf, "%lu", small);
*dst_p = strdup(buf);
}
if (*dst_p)
return 0;
tls_set_errorx(ctx, "cannot parse serial");
return -1;
}
/*
* Decode all string types used in RFC5280.
*
* OpenSSL used (before Jun 1 2014 commit) to pick between PrintableString,
* T61String, BMPString and UTF8String, depending on data. This code
* tries to match that.
*
* Disallow any ancient ASN.1 escape sequences.
*/
static int
check_invalid_bytes(struct tls *ctx, unsigned char *data, unsigned int len,
int ascii_only, const char *desc)
{
unsigned int i, c;
/* data is utf8 string, check for crap */
for (i = 0; i < len; i++) {
c = data[i];
if (ascii_only && (c & 0x80) != 0) {
tls_set_errorx(ctx, "invalid %s: contains non-ascii in ascii string", desc);
goto failed;
} else if (c < 0x20) {
/* ascii control chars, including NUL */
if (c != '\t' && c != '\n' && c != '\r') {
tls_set_errorx(ctx, "invalid %s: contains C0 control char", desc);
goto failed;
}
} else if (c == 0xC2 && (i + 1) < len) {
/* C1 control chars in UTF-8: \xc2\x80 - \xc2\x9f */
c = data[i + 1];
if (c >= 0x80 && c <= 0x9F) {
tls_set_errorx(ctx, "invalid %s: contains C1 control char", desc);
goto failed;
}
} else if (c == 0x7F) {
tls_set_errorx(ctx, "invalid %s: contains DEL char", desc);
goto failed;
}
}
return 0;
failed:
return -1;
}
static int
tls_parse_asn1string(struct tls *ctx, ASN1_STRING *a1str, const char **dst_p, int minchars, int maxchars, const char *desc)
{
int format, len, ret = -1;
unsigned char *data;
ASN1_STRING *a1utf = NULL;
int ascii_only = 0;
char *cstr = NULL;
int mbres, mbconvert = -1;
*dst_p = NULL;
format = ASN1_STRING_type(a1str);
data = ASN1_STRING_data(a1str);
len = ASN1_STRING_length(a1str);
if (len < minchars) {
tls_set_errorx(ctx, "invalid %s: string too short", desc);
goto failed;
}
switch (format) {
case V_ASN1_NUMERICSTRING:
case V_ASN1_VISIBLESTRING:
case V_ASN1_PRINTABLESTRING:
case V_ASN1_IA5STRING:
/* Ascii */
if (len > maxchars) {
tls_set_errorx(ctx, "invalid %s: string too long", desc);
goto failed;
}
ascii_only = 1;
break;
case V_ASN1_T61STRING:
/* Latin1 */
mbconvert = MBSTRING_ASC;
break;
case V_ASN1_BMPSTRING:
/* UCS-2 big-endian */
mbconvert = MBSTRING_BMP;
break;
case V_ASN1_UNIVERSALSTRING:
/* UCS-4 big-endian */
mbconvert = MBSTRING_UNIV;
break;
case V_ASN1_UTF8STRING:
/*
* UTF-8 - could be used directly if OpenSSL has already
* validated the data. ATM be safe and validate here.
*/
mbconvert = MBSTRING_UTF8;
break;
default:
tls_set_errorx(ctx, "invalid %s: unexpected string type", desc);
goto failed;
}
/* Convert to UTF-8 */
if (mbconvert != -1) {
mbres = ASN1_mbstring_ncopy(&a1utf, data, len, mbconvert, B_ASN1_UTF8STRING, minchars, maxchars);
if (mbres < 0) {
tls_set_error_libssl(ctx, "invalid %s", desc);
goto failed;
}
if (mbres != V_ASN1_UTF8STRING) {
tls_set_errorx(ctx, "multibyte conversion failed: expected UTF8 result");
goto failed;
}
data = ASN1_STRING_data(a1utf);
len = ASN1_STRING_length(a1utf);
}
/* must not allow \0 */
if (memchr(data, 0, len) != NULL) {
tls_set_errorx(ctx, "invalid %s: contains NUL", desc);
goto failed;
}
/* no escape codes please */
if (check_invalid_bytes(ctx, data, len, ascii_only, desc) < 0)
goto failed;
/* copy to new string */
cstr = malloc(len + 1);
if (!cstr) {
tls_set_error(ctx, "malloc");
goto failed;
}
memcpy(cstr, data, len);
cstr[len] = 0;
*dst_p = cstr;
ret = len;
failed:
ASN1_STRING_free(a1utf);
return ret;
}
static int
tls_cert_get_dname_string(struct tls *ctx, X509_NAME *name, int nid, const char **str_p, int minchars, int maxchars, const char *desc)
{
int loc, len;
X509_NAME_ENTRY *ne;
ASN1_STRING *a1str;
*str_p = NULL;
loc = X509_NAME_get_index_by_NID(name, nid, -1);
if (loc < 0)
return 0;
ne = X509_NAME_get_entry(name, loc);
if (!ne)
return 0;
a1str = X509_NAME_ENTRY_get_data(ne);
if (!a1str)
return 0;
len = tls_parse_asn1string(ctx, a1str, str_p, minchars, maxchars, desc);
if (len < 0)
return -1;
return 0;
}
static int
tls_load_alt_ia5string(struct tls *ctx, ASN1_IA5STRING *ia5str, struct tls_cert *cert, int slot_type, int minchars, int maxchars, const char *desc)
{
struct tls_cert_general_name *slot;
const char *data;
int len;
slot = &cert->subject_alt_names[cert->subject_alt_name_count];
len = tls_parse_asn1string(ctx, ia5str, &data, minchars, maxchars, desc);
if (len < 0)
return 0;
/*
* Per RFC 5280 section 4.2.1.6:
* " " is a legal domain name, but that
* dNSName must be rejected.
*/
if (len == 1 && data[0] == ' ') {
tls_set_errorx(ctx, "invalid %s: single space", desc);
return -1;
}
slot->name_value = data;
slot->name_type = slot_type;
cert->subject_alt_name_count++;
return 0;
}
static int
tls_load_alt_ipaddr(struct tls *ctx, ASN1_OCTET_STRING *bin, struct tls_cert *cert)
{
struct tls_cert_general_name *slot;
void *data;
int len;
slot = &cert->subject_alt_names[cert->subject_alt_name_count];
len = ASN1_STRING_length(bin);
data = ASN1_STRING_data(bin);
if (len < 0) {
tls_set_errorx(ctx, "negative length for ipaddress");
return -1;
}
/*
* Per RFC 5280 section 4.2.1.6:
* IPv4 must use 4 octets and IPv6 must use 16 octets.
*/
if (len == 4) {
slot->name_type = TLS_CERT_GNAME_IPv4;
} else if (len == 16) {
slot->name_type = TLS_CERT_GNAME_IPv6;
} else {
tls_set_errorx(ctx, "invalid length for ipaddress");
return -1;
}
slot->name_value = malloc(len);
if (slot->name_value == NULL) {
tls_set_error(ctx, "malloc");
return -1;
}
memcpy((void *)slot->name_value, data, len);
cert->subject_alt_name_count++;
return 0;
}
/* See RFC 5280 section 4.2.1.6 for SubjectAltName details. */
static int
tls_cert_get_altnames(struct tls *ctx, struct tls_cert *cert, X509 *x509_cert)
{
STACK_OF(GENERAL_NAME) *altname_stack = NULL;
GENERAL_NAME *altname;
int count, i;
int rv = -1;
altname_stack = X509_get_ext_d2i(x509_cert, NID_subject_alt_name, NULL, NULL);
if (altname_stack == NULL)
return 0;
count = sk_GENERAL_NAME_num(altname_stack);
if (count == 0) {
rv = 0;
goto out;
}
cert->subject_alt_names = calloc(sizeof (struct tls_cert_general_name), count);
if (cert->subject_alt_names == NULL) {
tls_set_error(ctx, "calloc");
goto out;
}
for (i = 0; i < count; i++) {
altname = sk_GENERAL_NAME_value(altname_stack, i);
if (altname->type == GEN_DNS) {
rv = tls_load_alt_ia5string(ctx, altname->d.dNSName, cert, TLS_CERT_GNAME_DNS, 1, UB_GNAME_DNS, "dns");
} else if (altname->type == GEN_EMAIL) {
rv = tls_load_alt_ia5string(ctx, altname->d.rfc822Name, cert, TLS_CERT_GNAME_EMAIL, 1, UB_GNAME_EMAIL, "email");
} else if (altname->type == GEN_URI) {
rv = tls_load_alt_ia5string(ctx, altname->d.uniformResourceIdentifier, cert, TLS_CERT_GNAME_URI, 1, UB_GNAME_URI, "uri");
} else if (altname->type == GEN_IPADD) {
rv = tls_load_alt_ipaddr(ctx, altname->d.iPAddress, cert);
} else {
/* ignore unknown types */
rv = 0;
}
if (rv < 0)
goto out;
}
rv = 0;
out:
sk_GENERAL_NAME_pop_free(altname_stack, GENERAL_NAME_free);
return rv;
}
static int
tls_get_dname(struct tls *ctx, X509_NAME *name, struct tls_cert_dname *dname)
{
int ret;
ret = tls_cert_get_dname_string(ctx, name, NID_commonName, &dname->common_name,
0, UB_COMMON_NAME, "commonName");
if (ret == 0)
ret = tls_cert_get_dname_string(ctx, name, NID_countryName, &dname->country_name,
0, UB_COUNTRY_NAME, "countryName");
if (ret == 0)
ret = tls_cert_get_dname_string(ctx, name, NID_stateOrProvinceName, &dname->state_or_province_name,
0, UB_STATE_NAME, "stateName");
if (ret == 0)
ret = tls_cert_get_dname_string(ctx, name, NID_localityName, &dname->locality_name,
0, UB_LOCALITY_NAME, "localityName");
if (ret == 0)
ret = tls_cert_get_dname_string(ctx, name, NID_streetAddress, &dname->street_address,
0, UB_STREET_ADDRESS, "streetAddress");
if (ret == 0)
ret = tls_cert_get_dname_string(ctx, name, NID_organizationName, &dname->organization_name,
0, UB_ORGANIZATION_NAME, "organizationName");
if (ret == 0)
ret = tls_cert_get_dname_string(ctx, name, NID_organizationalUnitName, &dname->organizational_unit_name,
0, UB_ORGANIZATIONAL_UNIT_NAME, "organizationalUnitName");
return ret;
}
static int
tls_get_basic_constraints(struct tls *ctx, struct tls_cert *cert, X509 *x509)
{
BASIC_CONSTRAINTS *bc;
int crit;
int ret = -1;
bc = X509_get_ext_d2i(x509, NID_basic_constraints, &crit, NULL);
if (!bc)
return 0;
cert->ext_set |= TLS_EXT_BASIC;
if (crit)
cert->ext_crit |= TLS_EXT_BASIC;
cert->basic_constraints_ca = bc->ca ? 1 : 0;
if (bc->pathlen) {
cert->basic_constraints_pathlen = ASN1_INTEGER_get(bc->pathlen);
if (cert->basic_constraints_pathlen < 0) {
tls_set_error(ctx, "BasicConstraints has invalid pathlen");
goto failed;
}
} else {
cert->basic_constraints_pathlen = -1;
}
ret = 0;
failed:
BASIC_CONSTRAINTS_free(bc);
return ret;
}
static uint32_t map_bits(const uint32_t map[][2], uint32_t input)
{
uint32_t i, out = 0;
for (i = 0; map[i][0]; i++) {
if (map[i][0] & input)
out |= map[i][1];
}
return out;
}
static int
tls_get_key_usage(struct tls *ctx, struct tls_cert *cert, X509 *x509)
{
static const uint32_t ku_map[][2] = {
{KU_DIGITAL_SIGNATURE, KU_DIGITAL_SIGNATURE},
{KU_NON_REPUDIATION, KU_NON_REPUDIATION},
{KU_KEY_ENCIPHERMENT, KU_KEY_ENCIPHERMENT},
{KU_DATA_ENCIPHERMENT, KU_DATA_ENCIPHERMENT},
{KU_KEY_AGREEMENT, KU_KEY_AGREEMENT},
{KU_KEY_CERT_SIGN, KU_KEY_CERT_SIGN},
{KU_CRL_SIGN, KU_CRL_SIGN},
{KU_ENCIPHER_ONLY, KU_ENCIPHER_ONLY},
{KU_DECIPHER_ONLY, KU_DECIPHER_ONLY},
{0, 0},
};
ASN1_BIT_STRING *ku;
int crit;
ku = X509_get_ext_d2i(x509, NID_key_usage, &crit, NULL);
if (!ku)
return 0;
cert->ext_set |= TLS_EXT_KEY_USAGE;
if (crit)
cert->ext_crit |= TLS_EXT_KEY_USAGE;
ASN1_BIT_STRING_free(ku);
cert->key_usage_flags = map_bits(ku_map, X509_get_key_usage(x509));
return 0;
}
static int
tls_get_ext_key_usage(struct tls *ctx, struct tls_cert *cert, X509 *x509)
{
static const uint32_t xku_map[][2] = {
{XKU_SSL_SERVER, TLS_XKU_SSL_SERVER},
{XKU_SSL_CLIENT, TLS_XKU_SSL_CLIENT},
{XKU_SMIME, TLS_XKU_SMIME},
{XKU_CODE_SIGN, TLS_XKU_CODE_SIGN},
{XKU_SGC, TLS_XKU_SGC},
{XKU_OCSP_SIGN, TLS_XKU_OCSP_SIGN},
{XKU_TIMESTAMP, TLS_XKU_TIMESTAMP},
{XKU_DVCS, TLS_XKU_DVCS},
{0, 0},
};
EXTENDED_KEY_USAGE *xku;
int crit;
xku = X509_get_ext_d2i(x509, NID_ext_key_usage, &crit, NULL);
if (!xku)
return 0;
sk_ASN1_OBJECT_pop_free(xku, ASN1_OBJECT_free);
cert->ext_set |= TLS_EXT_EXTENDED_KEY_USAGE;
if (crit)
cert->ext_crit |= TLS_EXT_EXTENDED_KEY_USAGE;
cert->extended_key_usage_flags = map_bits(xku_map, X509_get_extended_key_usage(x509));
return 0;
}
static int
tls_load_extensions(struct tls *ctx, struct tls_cert *cert, X509 *x509)
{
int ret;
/*
* Force libssl to fill extension fields under X509 struct.
* Then libtls does not need to parse raw data.
*/
X509_check_ca(x509);
ret = tls_get_basic_constraints(ctx, cert, x509);
if (ret == 0)
ret = tls_get_key_usage(ctx, cert, x509);
if (ret == 0)
ret = tls_get_ext_key_usage(ctx, cert, x509);
if (ret == 0)
ret = tls_cert_get_altnames(ctx, cert, x509);
return ret;
}
static void *
tls_calc_fingerprint(struct tls *ctx, X509 *x509, const char *algo, size_t *outlen)
{
const EVP_MD *md;
void *res;
int ret;
unsigned int tmplen, mdlen;
if (outlen)
*outlen = 0;
if (strcasecmp(algo, "sha1") == 0) {
md = EVP_sha1();
} else if (strcasecmp(algo, "sha256") == 0) {
md = EVP_sha256();
} else {
tls_set_errorx(ctx, "invalid fingerprint algorithm");
return NULL;
}
mdlen = EVP_MD_size(md);
res = malloc(mdlen);
if (!res) {
tls_set_error(ctx, "malloc");
return NULL;
}
ret = X509_digest(x509, md, res, &tmplen);
if (ret != 1 || tmplen != mdlen) {
free(res);
tls_set_errorx(ctx, "X509_digest failed");
return NULL;
}
if (outlen)
*outlen = mdlen;
return res;
}
static void
check_verify_error(struct tls *ctx, struct tls_cert *cert)
{
long vres = SSL_get_verify_result(ctx->ssl_conn);
if (vres == X509_V_OK) {
cert->successful_verify = 1;
} else {
cert->successful_verify = 0;
}
}
int
tls_parse_cert(struct tls *ctx, struct tls_cert **cert_p, const char *fingerprint_algo, X509 *x509)
{
struct tls_cert *cert = NULL;
X509_NAME *subject, *issuer;
int ret = -1;
long version;
*cert_p = NULL;
version = X509_get_version(x509);
if (version < 0) {
tls_set_errorx(ctx, "invalid version");
return -1;
}
subject = X509_get_subject_name(x509);
if (!subject) {
tls_set_errorx(ctx, "cert does not have subject");
return -1;
}
issuer = X509_get_issuer_name(x509);
if (!issuer) {
tls_set_errorx(ctx, "cert does not have issuer");
return -1;
}
cert = calloc(sizeof *cert, 1);
if (!cert) {
tls_set_error(ctx, "calloc");
goto failed;
}
cert->version = version;
if (fingerprint_algo) {
cert->fingerprint = tls_calc_fingerprint(ctx, x509, fingerprint_algo, &cert->fingerprint_size);
if (!cert->fingerprint)
goto failed;
}
ret = tls_get_dname(ctx, subject, &cert->subject);
if (ret == 0)
ret = tls_get_dname(ctx, issuer, &cert->issuer);
if (ret == 0)
ret = tls_asn1_parse_time(ctx, X509_get_notBefore(x509), &cert->not_before);
if (ret == 0)
ret = tls_asn1_parse_time(ctx, X509_get_notAfter(x509), &cert->not_after);
if (ret == 0)
ret = tls_parse_bigint(ctx, X509_get_serialNumber(x509), &cert->serial);
if (ret == 0)
ret = tls_load_extensions(ctx, cert, x509);
if (ret == 0) {
*cert_p = cert;
return 0;
}
failed:
tls_cert_free(cert);
return ret;
}
int
tls_get_peer_cert(struct tls *ctx, struct tls_cert **cert_p, const char *fingerprint_algo)
{
X509 *peer = ctx->ssl_peer_cert;
int res;
*cert_p = NULL;
if (!peer) {
tls_set_errorx(ctx, "peer does not have cert");
return TLS_NO_CERT;
}
ERR_clear_error();
res = tls_parse_cert(ctx, cert_p, fingerprint_algo, peer);
if (res == 0)
check_verify_error(ctx, *cert_p);
ERR_clear_error();
return res;
}
static void
tls_cert_free_dname(struct tls_cert_dname *dname)
{
free((void*)dname->common_name);
free((void*)dname->country_name);
free((void*)dname->state_or_province_name);
free((void*)dname->locality_name);
free((void*)dname->street_address);
free((void*)dname->organization_name);
free((void*)dname->organizational_unit_name);
}
void
tls_cert_free(struct tls_cert *cert)
{
int i;
if (!cert)
return;
tls_cert_free_dname(&cert->issuer);
tls_cert_free_dname(&cert->subject);
if (cert->subject_alt_name_count) {
for (i = 0; i < cert->subject_alt_name_count; i++)
free((void*)cert->subject_alt_names[i].name_value);
}
free(cert->subject_alt_names);
free((void*)cert->serial);
free((void*)cert->fingerprint);
free(cert);
}

105
lib/libtls/tls_cert.h Normal file
View File

@ -0,0 +1,105 @@
#ifndef _TLS_TLS_CERT_H_
#define _TLS_TLS_CERT_H_
#define TLS_CERT_GNAME_DNS 1
#define TLS_CERT_GNAME_IPv4 2
#define TLS_CERT_GNAME_IPv6 3
#define TLS_CERT_GNAME_EMAIL 4
#define TLS_CERT_GNAME_URI 5
#define TLS_KU_DIGITAL_SIGNATURE (1 << 0)
#define TLS_KU_NON_REPUDIATION (1 << 1)
#define TLS_KU_KEY_ENCIPHERMENT (1 << 2)
#define TLS_KU_DATA_ENCIPHERMENT (1 << 3)
#define TLS_KU_KEY_AGREEMENT (1 << 4)
#define TLS_KU_KEY_CERT_SIGN (1 << 5)
#define TLS_KU_CRL_SIGN (1 << 6)
#define TLS_KU_ENCIPHER_ONLY (1 << 7)
#define TLS_KU_DECIPHER_ONLY (1 << 8)
#define TLS_XKU_SSL_SERVER (1 << 0)
#define TLS_XKU_SSL_CLIENT (1 << 1)
#define TLS_XKU_SMIME (1 << 2)
#define TLS_XKU_CODE_SIGN (1 << 3)
#define TLS_XKU_OCSP_SIGN (1 << 4)
#define TLS_XKU_SGC (1 << 5)
#define TLS_XKU_TIMESTAMP (1 << 6)
#define TLS_XKU_DVCS (1 << 7)
#define TLS_EXT_BASIC (1 << 0)
#define TLS_EXT_KEY_USAGE (1 << 1)
#define TLS_EXT_EXTENDED_KEY_USAGE (1 << 2)
#define TLS_EXT_SUBJECT_ALT_NAME (1 << 3)
/*
* GeneralName
*/
struct tls_cert_general_name {
const void *name_value;
int name_type;
};
/*
* DistinguishedName
*/
struct tls_cert_dname {
const char *common_name;
const char *country_name;
const char *state_or_province_name;
const char *locality_name;
const char *street_address;
const char *organization_name;
const char *organizational_unit_name;
};
struct tls_cert {
/* Version number from cert: 0:v1, 1:v2, 2:v3 */
int version;
/* did it pass verify? useful when noverifycert is on. */
int successful_verify;
/* DistringuishedName for subject */
struct tls_cert_dname subject;
/* DistringuishedName for issuer */
struct tls_cert_dname issuer;
/* decimal number */
const char *serial;
/* Validity times */
time_t not_before;
time_t not_after;
uint32_t ext_set;
uint32_t ext_crit;
/* BasicConstraints extension */
int basic_constraints_ca;
int basic_constraints_pathlen;
/* KeyUsage extension */
uint32_t key_usage_flags;
/* ExtendedKeyUsage extension */
uint32_t extended_key_usage_flags;
/* SubjectAltName extension */
struct tls_cert_general_name *subject_alt_names;
int subject_alt_name_count;
/* Fingerprint as raw hash */
const unsigned char *fingerprint;
size_t fingerprint_size;
};
int tls_get_peer_cert(struct tls *ctx, struct tls_cert **cert_p, const char *fingerprint_algo);
void tls_cert_free(struct tls_cert *cert);
#ifdef TLS_CERT_INTERNAL_FUNCS
int tls_parse_cert(struct tls *ctx, struct tls_cert **cert_p,
const char *fingerprint_algo, X509 *x509);
#endif
#endif

287
lib/libtls/tls_client.c Normal file
View File

@ -0,0 +1,287 @@
/* $OpenBSD: tls_client.c,v 1.16 2015/03/21 15:35:15 sthen 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 <limits.h>
#include <openssl/err.h>
#include <openssl/x509.h>
#include "tls_internal.h"
#include "tls_compat.h"
#include "tls.h"
struct tls *
tls_client(void)
{
struct tls *ctx;
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,
* otherwise use the default.
*/
if ((p = (char *)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);
}
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)
{
union tls_addr addrbuf;
int rv = -1;
if ((ctx->flags & TLS_CLIENT) == 0) {
tls_set_errorx(ctx, "not a client context");
goto err;
}
if (fd_read < 0 || fd_write < 0) {
tls_set_errorx(ctx, "invalid file descriptors");
goto err;
}
if (servername != NULL) {
if ((ctx->servername = strdup(servername)) == NULL) {
tls_set_errorx(ctx, "out of memory");
goto err;
}
}
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) != 0)
goto err;
if (tls_configure_keypair(ctx, 0) != 0)
goto err;
if (ctx->config->verify_name) {
if (servername == NULL) {
tls_set_errorx(ctx, "server name not specified");
goto err;
}
}
if (ctx->config->verify_cert &&
(tls_configure_ssl_verify(ctx, SSL_VERIFY_PEER) == -1))
goto err;
if (SSL_CTX_set_tlsext_status_cb(ctx->ssl_ctx, tls_ocsp_verify_callback) != 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 (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;
}
if (SSL_set_tlsext_status_type(ctx->ssl_conn, TLSEXT_STATUSTYPE_ocsp) != 1) {
tls_set_errorx(ctx, "ssl OCSP extension setup failure");
goto err;
}
/*
* RFC4366 (SNI): Literal IPv4 and IPv6 addresses are not
* permitted in "HostName".
*/
if (servername != NULL &&
inet_pton(AF_INET, servername, &addrbuf) != 1 &&
inet_pton(AF_INET6, servername, &addrbuf) != 1) {
if (SSL_set_tlsext_host_name(ctx->ssl_conn, servername) == 0) {
tls_set_errorx(ctx, "server name indication failure");
goto err;
}
}
rv = 0;
err:
return (rv);
}
int
tls_handshake_client(struct tls *ctx)
{
X509 *cert = NULL;
int ssl_ret;
int rv = -1;
if ((ctx->flags & TLS_CLIENT) == 0) {
tls_set_errorx(ctx, "not a client context");
goto err;
}
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 ((rv = tls_check_name(ctx, cert,
ctx->servername)) != 0) {
if (rv != -2)
tls_set_errorx(ctx, "name `%s' not present in"
" server certificate", ctx->servername);
goto err;
}
}
ctx->state |= TLS_HANDSHAKE_COMPLETE;
rv = 0;
err:
X509_free(cert);
return (rv);
}

440
lib/libtls/tls_compat.c Normal file
View File

@ -0,0 +1,440 @@
/*
* Compatibility with various libssl implementations.
*
* Copyright (c) 2015 Marko Kreen
*
* Permission to use, copy, modify, and/or 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/err.h>
#include <openssl/dh.h>
#include "tls_internal.h"
#include "tls_compat.h"
#include "tls.h"
#ifndef SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE
#undef SSLerr
#undef X509err
#endif
#ifndef SSLerr
#define SSLerr(a,b) do {} while (0)
#define X509err(a,b) do {} while (0)
#endif
#ifndef SSL_CTX_set_dh_auto
#define DH_CLEANUP
/*
* SKIP primes, used by OpenSSL and PostgreSQL.
*
* https://tools.ietf.org/html/draft-ietf-ipsec-skip-06
*/
static const char file_dh1024[] =
"-----BEGIN DH PARAMETERS-----\n"
"MIGHAoGBAPSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsY\n"
"jY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6\n"
"ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpL3jHAgEC\n"
"-----END DH PARAMETERS-----\n";
static const char file_dh2048[] =
"-----BEGIN DH PARAMETERS-----\n"
"MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV\n"
"89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50\n"
"T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb\n"
"zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX\n"
"Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT\n"
"CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n"
"-----END DH PARAMETERS-----\n";
static const char file_dh4096[] =
"-----BEGIN DH PARAMETERS-----\n"
"MIICCAKCAgEA+hRyUsFN4VpJ1O8JLcCo/VWr19k3BCgJ4uk+d+KhehjdRqNDNyOQ\n"
"l/MOyQNQfWXPeGKmOmIig6Ev/nm6Nf9Z2B1h3R4hExf+zTiHnvVPeRBhjdQi81rt\n"
"Xeoh6TNrSBIKIHfUJWBh3va0TxxjQIs6IZOLeVNRLMqzeylWqMf49HsIXqbcokUS\n"
"Vt1BkvLdW48j8PPv5DsKRN3tloTxqDJGo9tKvj1Fuk74A+Xda1kNhB7KFlqMyN98\n"
"VETEJ6c7KpfOo30mnK30wqw3S8OtaIR/maYX72tGOno2ehFDkq3pnPtEbD2CScxc\n"
"alJC+EL7RPk5c/tgeTvCngvc1KZn92Y//EI7G9tPZtylj2b56sHtMftIoYJ9+ODM\n"
"sccD5Piz/rejE3Ome8EOOceUSCYAhXn8b3qvxVI1ddd1pED6FHRhFvLrZxFvBEM9\n"
"ERRMp5QqOaHJkM+Dxv8Cj6MqrCbfC4u+ZErxodzuusgDgvZiLF22uxMZbobFWyte\n"
"OvOzKGtwcTqO/1wV5gKkzu1ZVswVUQd5Gg8lJicwqRWyyNRczDDoG9jVDxmogKTH\n"
"AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n"
"KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n"
"-----END DH PARAMETERS-----\n";
static DH *dh1024, *dh2048, *dh4096;
static DH *load_dh_buffer(struct tls *ctx, DH **dhp, const char *buf)
{
BIO *bio;
DH *dh = *dhp;
if (dh == NULL) {
bio = BIO_new_mem_buf((char *)buf, strlen(buf));
if (bio) {
dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
BIO_free(bio);
}
*dhp = dh;
}
if (ctx)
ctx->used_dh_bits = DH_size(dh) * 8;
return dh;
}
static DH *dh_auto_cb(SSL *s, int is_export, int keylength)
{
EVP_PKEY *pk;
int bits;
struct tls *ctx = SSL_get_app_data(s);
pk = SSL_get_privatekey(s);
if (!pk)
return load_dh_buffer(ctx, &dh2048, file_dh2048);
bits = EVP_PKEY_bits(pk);
if (bits >= 3072)
return load_dh_buffer(ctx, &dh4096, file_dh4096);
if (bits >= 1536)
return load_dh_buffer(ctx, &dh2048, file_dh2048);
return load_dh_buffer(ctx, &dh1024, file_dh1024);
}
static DH *dh_legacy_cb(SSL *s, int is_export, int keylength)
{
struct tls *ctx = SSL_get_app_data(s);
return load_dh_buffer(ctx, &dh1024, file_dh1024);
}
long SSL_CTX_set_dh_auto(SSL_CTX *ctx, int onoff)
{
if (onoff == 0)
return 1;
if (onoff == 2) {
SSL_CTX_set_tmp_dh_callback(ctx, dh_legacy_cb);
} else {
SSL_CTX_set_tmp_dh_callback(ctx, dh_auto_cb);
}
SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE);
return 1;
}
#endif
#ifndef SSL_CTX_set_ecdh_auto
#define ECDH_CLEANUP
/*
* Use same curve as EC key, fallback to NIST P-256.
*/
static EC_KEY *ecdh_cache;
#ifdef USE_LIBSSL_INTERNALS
static EC_KEY *ecdh_auto_cb(SSL *ssl, int is_export, int keylength)
{
int last_nid;
int nid = 0;
EVP_PKEY *pk;
EC_KEY *ec;
struct tls *ctx = SSL_get_app_data(ssl);
/* pick curve from EC key */
pk = SSL_get_privatekey(ssl);
if (pk && EVP_PKEY_id(pk) == EVP_PKEY_EC) {
ec = EVP_PKEY_get1_EC_KEY(pk);
if (ec) {
nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
EC_KEY_free(ec);
}
}
/* ssl->tlsext_ellipticcurvelist is empty, nothing else to do... */
if (nid == 0)
nid = NID_X9_62_prime256v1;
if (ctx)
ctx->used_ecdh_nid = nid;
if (ecdh_cache) {
last_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ecdh_cache));
if (last_nid == nid)
return ecdh_cache;
EC_KEY_free(ecdh_cache);
ecdh_cache = NULL;
}
ecdh_cache = EC_KEY_new_by_curve_name(nid);
return ecdh_cache;
}
#endif
long SSL_CTX_set_ecdh_auto(SSL_CTX *ctx, int onoff)
{
if (onoff) {
SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE);
#ifdef USE_LIBSSL_INTERNALS
SSL_CTX_set_tmp_ecdh_callback(ctx, ecdh_auto_cb);
#endif
}
return 1;
}
#endif
void tls_compat_cleanup(void)
{
#ifdef DH_CLEANUP
if (dh1024) { DH_free(dh1024); dh1024 = NULL; }
if (dh2048) { DH_free(dh2048); dh2048 = NULL; }
if (dh4096) { DH_free(dh4096); dh4096 = NULL; }
#endif
#ifdef ECDH_CLEANUP
if (ecdh_cache) {
EC_KEY_free(ecdh_cache);
ecdh_cache = NULL;
}
#endif
}
#ifndef HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM
/*
* Load certs for public key from memory.
*/
int
SSL_CTX_use_certificate_chain_mem(SSL_CTX *ctx, void *data, int data_len)
{
pem_password_cb *psw_fn = NULL;
void *psw_arg = NULL;
X509 *cert;
BIO *bio = NULL;
int ok;
#ifdef USE_LIBSSL_INTERNALS
psw_fn = ctx->default_passwd_callback;
psw_arg = ctx->default_passwd_callback_userdata;
#endif
ERR_clear_error();
/* Read from memory */
bio = BIO_new_mem_buf(data, data_len);
if (!bio) {
SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_BUF_LIB);
goto failed;
}
/* Load primary cert */
cert = PEM_read_bio_X509_AUX(bio, NULL, psw_fn, psw_arg);
if (!cert) {
SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_PEM_LIB);
goto failed;
}
/* Increments refcount */
ok = SSL_CTX_use_certificate(ctx, cert);
X509_free(cert);
if (!ok || ERR_peek_error())
goto failed;
/* Load extra certs */
ok = SSL_CTX_clear_extra_chain_certs(ctx);
while (ok) {
cert = PEM_read_bio_X509(bio, NULL, psw_fn, psw_arg);
if (!cert) {
/* Is it EOF? */
unsigned long err = ERR_peek_last_error();
if (ERR_GET_LIB(err) != ERR_LIB_PEM)
break;
if (ERR_GET_REASON(err) != PEM_R_NO_START_LINE)
break;
/* On EOF do successful exit */
BIO_free(bio);
ERR_clear_error();
return 1;
}
/* Does not increment refcount */
ok = SSL_CTX_add_extra_chain_cert(ctx, cert);
if (!ok)
X509_free(cert);
}
failed:
if (bio)
BIO_free(bio);
return 0;
}
#endif
#ifndef HAVE_SSL_CTX_LOAD_VERIFY_MEM
/*
* Load CA certs for verification from memory.
*/
int SSL_CTX_load_verify_mem(SSL_CTX *ctx, void *data, int data_len)
{
STACK_OF(X509_INFO) *stack = NULL;
X509_STORE *store;
X509_INFO *info;
int nstack, i, ret = 0, got = 0;
BIO *bio;
/* Read from memory */
bio = BIO_new_mem_buf(data, data_len);
if (!bio)
goto failed;
/* Parse X509_INFO records */
stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
if (!stack)
goto failed;
/* Loop over stack, add certs and revocation records to store */
store = SSL_CTX_get_cert_store(ctx);
nstack = sk_X509_INFO_num(stack);
for (i = 0; i < nstack; i++) {
info = sk_X509_INFO_value(stack, i);
if (info->x509 && !X509_STORE_add_cert(store, info->x509))
goto failed;
if (info->crl && !X509_STORE_add_crl(store, info->crl))
goto failed;
if (info->x509 || info->crl)
got = 1;
}
ret = got;
failed:
if (bio)
BIO_free(bio);
if (stack)
sk_X509_INFO_pop_free(stack, X509_INFO_free);
if (!ret)
X509err(X509_F_X509_LOAD_CERT_CRL_FILE, ERR_R_PEM_LIB);
return ret;
}
#endif
#ifndef HAVE_ASN1_TIME_PARSE
static int
parse2num(const char **str_p, int min, int max)
{
const char *s = *str_p;
if (s && s[0] >= '0' && s[0] <= '9' && s[1] >= '0' && s[1] <= '9') {
int val = (s[0] - '0') * 10 + (s[1] - '0');
if (val >= min && val <= max) {
*str_p += 2;
return val;
}
}
*str_p = NULL;
return 0;
}
int
asn1_time_parse(const char *src, size_t len, struct tm *tm, int mode)
{
char buf[16];
const char *s = buf;
int utctime;
int year;
memset(tm, 0, sizeof *tm);
if (mode != 0)
return -1;
/*
* gentime: YYYYMMDDHHMMSSZ
* utctime: YYMMDDHHMM(SS)(Z)
*/
if (len == 15) {
utctime = 0;
} else if (len > 8 && len < 15) {
utctime = 1;
} else {
return -1;
}
memcpy(buf, src, len);
buf[len] = '\0';
year = parse2num(&s, 0, 99);
if (utctime) {
if (year < 50)
year = 2000 + year;
else
year = 1900 + year;
} else {
year = year*100 + parse2num(&s, 0, 99);
}
tm->tm_year = year - 1900;
tm->tm_mon = parse2num(&s, 1, 12) - 1;
tm->tm_mday = parse2num(&s, 1, 31);
tm->tm_hour = parse2num(&s, 0, 23);
tm->tm_min = parse2num(&s, 0, 59);
if (utctime) {
if (s && s[0] != 'Z' && s[0] != '\0')
tm->tm_sec = parse2num(&s, 0, 61);
} else {
tm->tm_sec = parse2num(&s, 0, 61);
}
if (s) {
if (s[0] == '\0')
goto good;
if (s[0] == 'Z' && s[1] == '\0')
goto good;
}
return -1;
good:
return utctime ? V_ASN1_UTCTIME : V_ASN1_GENERALIZEDTIME;
}
#endif /* HAVE_ASN1_TIME_PARSE */
int
tls_asn1_parse_time(struct tls *ctx, const ASN1_TIME *asn1time, time_t *dst)
{
struct tm tm;
int res;
time_t tval;
*dst = 0;
if (!asn1time)
return 0;
if (asn1time->type != V_ASN1_GENERALIZEDTIME &&
asn1time->type != V_ASN1_UTCTIME) {
tls_set_errorx(ctx, "Invalid time object type: %d", asn1time->type);
return -1;
}
res = asn1_time_parse((char*)asn1time->data, asn1time->length, &tm, 0);
if (res == -1) {
tls_set_errorx(ctx, "Invalid asn1 time");
return -1;
}
tval = timegm(&tm);
if (tval == (time_t)-1) {
tls_set_error(ctx, "Cannot convert asn1 time");
return -1;
}
*dst = tval;
return 0;
}

47
lib/libtls/tls_compat.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef _TLS_COMPAT_H_
#define _TLS_COMPAT_H_
#include <openssl/ssl.h>
/* OpenSSL 1.1+ has hidden struct fields */
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
#define USE_LIBSSL_INTERNALS
#define X509_get_key_usage(x509) ((x509)->ex_kusage)
#define X509_get_extended_key_usage(x509) ((x509)->ex_xkusage)
#define SSL_CTX_get0_param(ssl_ctx) ((ssl_ctx)->param)
#endif
/* ecdh_auto is broken - ignores main EC key */
#undef SSL_CTX_set_ecdh_auto
/* dh_auto seems fine, but use ours to get DH info */
#undef SSL_CTX_set_dh_auto
#ifndef SSL_CTX_set_dh_auto
long SSL_CTX_set_dh_auto(SSL_CTX *ctx, int onoff);
#endif
#ifndef SSL_CTX_set_ecdh_auto
long SSL_CTX_set_ecdh_auto(SSL_CTX *ctx, int onoff);
#endif
#ifndef HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM
int SSL_CTX_use_certificate_chain_mem(SSL_CTX *ctx, void *buf, int len);
#endif
#ifndef HAVE_SSL_CTX_LOAD_VERIFY_MEM
int SSL_CTX_load_verify_mem(SSL_CTX *ctx, void *buf, int len);
#endif
/* BoringSSL has no OCSP support */
#ifdef OPENSSL_IS_BORINGSSL
#define SSL_CTX_set_tlsext_status_cb(a,b) (1)
#define SSL_set_tlsext_status_type(a,b) (1)
#endif
void tls_compat_cleanup(void);
#endif

365
lib/libtls/tls_config.c Normal file
View File

@ -0,0 +1,365 @@
/* $OpenBSD: tls_config.c,v 1.8 2015/02/22 14:59:37 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 <ctype.h>
#include "tls_internal.h"
#include "tls_compat.h"
#include "tls.h"
static inline void
explicit_bzero(void *dest, int size)
{
memset(dest, 0, size);
}
static int
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;
}
static void *
memdup(const void *in, size_t len)
{
void *out;
if ((out = malloc(len)) == NULL)
return NULL;
memcpy(out, in, len);
return out;
}
static int
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;
}
struct tls_config *
tls_config_new(void)
{
struct tls_config *config;
if ((config = calloc(1, sizeof(*config))) == NULL)
return (NULL);
/*
* Default configuration.
*/
if (tls_config_set_ca_file(config, _PATH_SSL_CA_FILE) != 0)
goto err;
if (tls_config_set_dheparams(config, "none") != 0)
goto err;
if (tls_config_set_ecdhecurve(config, "auto") != 0)
goto err;
if (tls_config_set_ciphers(config, "secure") != 0)
goto err;
tls_config_set_protocols(config, TLS_PROTOCOLS_DEFAULT);
tls_config_set_verify_depth(config, 6);
tls_config_prefer_ciphers_server(config);
tls_config_verify(config);
return (config);
err:
tls_config_free(config);
return (NULL);
}
void
tls_config_free(struct tls_config *config)
{
if (config == NULL)
return;
tls_config_clear_keys(config);
free((char *)config->ca_file);
free((char *)config->ca_path);
free((char *)config->cert_file);
free(config->cert_mem);
free((char *)config->ciphers);
free((char *)config->key_file);
free(config->key_mem);
free(config);
}
void
tls_config_clear_keys(struct tls_config *config)
{
tls_config_set_ca_mem(config, NULL, 0);
tls_config_set_cert_mem(config, NULL, 0);
tls_config_set_key_mem(config, NULL, 0);
}
int
tls_config_parse_protocols(uint32_t *protocols, const char *protostr)
{
uint32_t proto, protos = 0;
char *s, *p, *q;
int negate;
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_0;
else if (strcasecmp(p, "tlsv1.1") == 0)
proto = TLS_PROTOCOL_TLSv1_1;
else if (strcasecmp(p, "tlsv1.2") == 0)
proto = TLS_PROTOCOL_TLSv1_2;
if (proto == 0) {
free(s);
return (-1);
}
if (negate)
protos &= ~proto;
else
protos |= proto;
}
*protocols = protos;
free(s);
return (0);
}
int
tls_config_set_ca_file(struct tls_config *config, const char *ca_file)
{
return set_string(&config->ca_file, ca_file);
}
int
tls_config_set_ca_path(struct tls_config *config, const char *ca_path)
{
return 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 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 set_string(&config->cert_file, cert_file);
}
int
tls_config_set_cert_mem(struct tls_config *config, const uint8_t *cert,
size_t len)
{
return set_mem(&config->cert_mem, &config->cert_len, cert, len);
}
int
tls_config_set_ciphers(struct tls_config *config, const char *ciphers)
{
if (ciphers == NULL ||
strcasecmp(ciphers, "default") == 0 ||
strcasecmp(ciphers, "secure") == 0)
ciphers = TLS_CIPHERS_DEFAULT;
else if (strcasecmp(ciphers, "compat") == 0 ||
strcasecmp(ciphers, "legacy") == 0)
ciphers = TLS_CIPHERS_COMPAT;
else if (strcasecmp(ciphers, "insecure") == 0 ||
strcasecmp(ciphers, "all") == 0)
ciphers = TLS_CIPHERS_ALL;
else if (strcasecmp(ciphers, "normal") == 0)
ciphers = TLS_CIPHERS_NORMAL;
else if (strcasecmp(ciphers, "fast") == 0)
ciphers = TLS_CIPHERS_FAST;
return set_string(&config->ciphers, ciphers);
}
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
return (-1);
config->dheparams = keylen;
return (0);
}
int
tls_config_set_ecdhecurve(struct tls_config *config, const char *name)
{
int nid;
if (name == NULL || strcasecmp(name, "none") == 0)
nid = NID_undef;
else if (strcasecmp(name, "auto") == 0)
nid = -1;
else if ((nid = OBJ_txt2nid(name)) == NID_undef)
return (-1);
config->ecdhecurve = nid;
return (0);
}
int
tls_config_set_key_file(struct tls_config *config, const char *key_file)
{
return set_string(&config->key_file, key_file);
}
int
tls_config_set_key_mem(struct tls_config *config, const uint8_t *key,
size_t len)
{
if (config->key_mem)
explicit_bzero(config->key_mem, config->key_len);
return set_mem(&config->key_mem, &config->key_len, key, len);
}
int
tls_config_set_ocsp_stapling_file(struct tls_config *config, const char *blob_file)
{
if (blob_file != NULL)
tls_config_set_ocsp_stapling_mem(config, NULL, 0);
return set_string(&config->ocsp_file, blob_file);
}
int
tls_config_set_ocsp_stapling_mem(struct tls_config *config, const uint8_t *blob, size_t len)
{
if (blob != NULL)
tls_config_set_ocsp_stapling_file(config, NULL);
return set_mem(&config->ocsp_mem, &config->ocsp_len, blob, len);
}
void
tls_config_set_protocols(struct tls_config *config, uint32_t protocols)
{
config->protocols = protocols;
}
void
tls_config_set_verify_depth(struct tls_config *config, int verify_depth)
{
config->verify_depth = verify_depth;
}
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_verify_client(struct tls_config *config)
{
config->verify_client = 1;
}
void
tls_config_verify_client_optional(struct tls_config *config)
{
config->verify_client = 2;
}

216
lib/libtls/tls_conninfo.c Normal file
View File

@ -0,0 +1,216 @@
/* $OpenBSD: tls_peer.c,v 1.3 2015/09/11 13:22:39 beck 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 <openssl/x509.h>
#include "tls_internal.h"
#include "tls_compat.h"
#include "tls.h"
static 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 = realloc(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)
{
unsigned char d[EVP_MAX_MD_SIZE];
char *dhex = NULL;
unsigned int dlen;
int rv = -1;
*hash = NULL;
if (ctx->ssl_peer_cert == NULL)
return (0);
if (X509_digest(ctx->ssl_peer_cert, EVP_sha256(), d, &dlen) != 1) {
tls_set_errorx(ctx, "digest failed");
goto err;
}
if (tls_hex_string(d, dlen, &dhex, NULL) != 0) {
tls_set_errorx(ctx, "digest hex string failed");
goto err;
}
if (asprintf(hash, "SHA256:%s", dhex) == -1) {
tls_set_errorx(ctx, "out of memory");
*hash = NULL;
goto err;
}
rv = 0;
err:
free(dhex);
return (rv);
}
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;
int rv = -1;
memset(&before_tm, 0, sizeof(before_tm));
memset(&after_tm, 0, sizeof(after_tm));
if (ctx->ssl_peer_cert != NULL) {
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((char*)before->data, before->length, &before_tm, 0) == -1)
goto err;
if (asn1_time_parse((char*)after->data, after->length, &after_tm, 0) == -1)
goto err;
if ((*notbefore = timegm(&before_tm)) == -1)
goto err;
if ((*notafter = timegm(&after_tm)) == -1)
goto err;
}
rv = 0;
err:
return (rv);
}
int
tls_get_conninfo(struct tls *ctx) {
const char * tmp;
tls_free_conninfo(ctx->conninfo);
if (ctx->ssl_peer_cert != NULL) {
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;
}
if ((tmp = SSL_get_version(ctx->ssl_conn)) == NULL)
goto err;
ctx->conninfo->version = strdup(tmp);
if (ctx->conninfo->version == NULL)
goto err;
if ((tmp = SSL_get_cipher(ctx->ssl_conn)) == NULL)
goto err;
ctx->conninfo->cipher = strdup(tmp);
if (ctx->conninfo->cipher == NULL)
goto err;
return (0);
err:
tls_free_conninfo(ctx->conninfo);
return (-1);
}
void
tls_free_conninfo(struct tls_conninfo *conninfo) {
if (conninfo != NULL) {
free(conninfo->hash);
conninfo->hash = NULL;
free(conninfo->subject);
conninfo->subject = NULL;
free(conninfo->issuer);
conninfo->issuer = NULL;
free(conninfo->version);
conninfo->version = NULL;
free(conninfo->cipher);
conninfo->cipher = NULL;
}
}
const char *
tls_conn_cipher(struct tls *ctx)
{
if (ctx->conninfo == NULL)
return (NULL);
return (ctx->conninfo->cipher);
}
const char *
tls_conn_version(struct tls *ctx)
{
if (ctx->conninfo == NULL)
return (NULL);
return (ctx->conninfo->version);
}

199
lib/libtls/tls_internal.h Normal file
View File

@ -0,0 +1,199 @@
/* $OpenBSD: tls_internal.h,v 1.11 2015/02/22 14:50:41 jsing 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 <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
#include <openssl/ssl.h>
#define _PATH_SSL_CA_FILE "/etc/ssl/certs/ca-certificates.crt"
/*
* Anything that is not completely broken.
*
* Also fixes 3DES ordering bug in older OpenSSLs.
*/
#define TLS_CIPHERS_COMPAT "HIGH:+3DES:!aNULL"
/*
* All==insecure.
*/
#define TLS_CIPHERS_ALL "ALL:!aNULL:!eNULL"
/*
* TLSv1.2+AEAD+ECDHE/DHE. CBC modes are dubious due to spec bugs in TLS.
*/
#define TLS_CIPHERS_DEFAULT "HIGH+EECDH:HIGH+EDH:!SSLv3:!SHA384:!SHA256:!DSS:!aNULL"
/*
* Compact subset of reasonable suites only.
*
* Priorities, in order:
* - ECDHE > DHE > RSA
* - AESGCM > CBC
* - TLSv1.2 > TLSv1.0
* - AES256 > AES128.
*/
#define TLS_CIPHERS_NORMAL "HIGH+EECDH:HIGH+EDH:HIGH+RSA:+SHA384:+SHA256:+SSLv3:+EDH:+RSA:-3DES:3DES+RSA:!CAMELLIA:!DSS:!aNULL"
/*
* Prefer performance if it does not affect security.
*
* Same as "normal" but prefers AES128 to AES256.
*/
#define TLS_CIPHERS_FAST "HIGH+EECDH:HIGH+EDH:HIGH+RSA:+AES256:+SHA256:+SHA384:+SSLv3:+EDH:+RSA:-3DES:3DES+RSA:!CAMELLIA:!DSS:!aNULL"
union tls_addr {
struct in_addr ip4;
struct in6_addr ip6;
};
struct tls_config {
const char *ca_file;
const char *ca_path;
char *ca_mem;
size_t ca_len;
const char *cert_file;
char *cert_mem;
size_t cert_len;
const char *ciphers;
int ciphers_server;
int dheparams;
int ecdhecurve;
const char *key_file;
char *key_mem;
size_t key_len;
const char *ocsp_file;
char *ocsp_mem;
size_t ocsp_len;
uint32_t protocols;
int verify_cert;
int verify_client;
int verify_depth;
int verify_name;
int verify_time;
};
struct tls_conninfo {
char *issuer;
char *subject;
char *hash;
char *serial;
char *fingerprint;
char *version;
char *cipher;
time_t notbefore;
time_t notafter;
};
#define TLS_CLIENT (1 << 0)
#define TLS_SERVER (1 << 1)
#define TLS_SERVER_CONN (1 << 2)
#define TLS_OCSP_CLIENT (1 << 3)
#define TLS_EOF_NO_CLOSE_NOTIFY (1 << 0)
#define TLS_HANDSHAKE_COMPLETE (1 << 1)
#define TLS_DO_ABORT (1 << 8)
struct tls_ocsp_query;
struct tls_ocsp_info;
struct tls {
struct tls_config *config;
uint32_t flags;
uint32_t state;
char *errmsg;
int errnum;
char *servername;
int socket;
SSL *ssl_conn;
SSL_CTX *ssl_ctx;
X509 *ssl_peer_cert;
struct tls_conninfo *conninfo;
int used_dh_bits;
int used_ecdh_nid;
const char *ocsp_result;
struct tls_ocsp_info *ocsp_info;
struct tls_ocsp_query *ocsp_query;
};
struct tls_ocsp_info {
int response_status;
int cert_status;
int crl_reason;
time_t this_update;
time_t next_update;
time_t revocation_time;
};
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 tls_configure_keypair(struct tls *ctx, int);
int tls_configure_server(struct tls *ctx);
int tls_configure_ssl(struct tls *ctx);
int tls_configure_ssl_verify(struct tls *ctx, int verify);
int tls_handshake_client(struct tls *ctx);
int tls_handshake_server(struct tls *ctx);
int tls_host_port(const char *hostport, char **host, char **port);
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_error_libssl(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_get_conninfo(struct tls *ctx);
void tls_free_conninfo(struct tls_conninfo *conninfo);
int tls_ocsp_verify_callback(SSL *ssl, void *arg);
int tls_ocsp_stapling_callback(SSL *ssl, void *arg);
void tls_ocsp_client_free(struct tls *ctx);
void tls_ocsp_info_free(struct tls_ocsp_info *info);
int tls_asn1_parse_time(struct tls *ctx, const ASN1_TIME *asn1time, time_t *dst);
int asn1_time_parse(const char *, size_t, struct tm *, int);
#endif /* HEADER_TLS_INTERNAL_H */

969
lib/libtls/tls_ocsp.c Normal file
View File

@ -0,0 +1,969 @@
/*
* Copyright (c) 2015 Marko Kreen <markokr@gmail.com>
*
* 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/err.h>
#include "tls_internal.h"
#include "tls_compat.h"
#include "tls.h"
#ifndef OPENSSL_NO_OCSP
#include <openssl/ocsp.h>
#define MAXAGE_SEC (14*24*60*60)
#define JITTER_SEC (60)
#define USE_NONCE 0
#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10002000
#define BUGGY_VERIFY
#endif
/*
* State for request.
*/
struct tls_ocsp_query {
/* responder location */
char *ocsp_url;
/* request blob */
uint8_t *request_data;
size_t request_size;
/* network state */
BIO *bio;
SSL_CTX *ssl_ctx;
OCSP_REQ_CTX *http_req;
/* cert data, this struct does not own these */
X509 *main_cert;
STACK_OF(X509) *extra_certs;
SSL_CTX *cert_ssl_ctx;
};
/*
* Extract OCSP response info.
*/
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_info *info;
int res;
info = calloc(1, sizeof (struct tls_ocsp_info));
if (!info) {
tls_set_error(ctx, "calloc");
return -1;
}
info->response_status = response_status;
info->cert_status = cert_status;
info->crl_reason = crl_reason;
res = tls_asn1_parse_time(ctx, revtime, &info->revocation_time);
if (res == 0)
res = tls_asn1_parse_time(ctx, thisupd, &info->this_update);
if (res == 0)
res = tls_asn1_parse_time(ctx, nextupd, &info->next_update);
if (res == 0) {
ctx->ocsp_info = info;
} else {
tls_ocsp_info_free(info);
}
return res;
}
static void
tls_ocsp_fill_result(struct tls *ctx, int res)
{
struct tls_ocsp_info *info = ctx->ocsp_info;
if (res < 0) {
ctx->ocsp_result = "error";
} else if (info->response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
ctx->ocsp_result = OCSP_response_status_str(info->response_status);
} else if (info->cert_status != V_OCSP_CERTSTATUS_REVOKED) {
ctx->ocsp_result = OCSP_cert_status_str(info->cert_status);
} else {
ctx->ocsp_result = OCSP_crl_reason_str(info->crl_reason);
}
}
void
tls_ocsp_info_free(struct tls_ocsp_info *info)
{
free(info);
}
int
tls_get_ocsp_info(struct tls *ctx, int *response_status, int *cert_status,
int *crl_reason, time_t *this_update,
time_t *next_update, time_t *revoction_time,
const char **result_text)
{
static const struct tls_ocsp_info no_ocsp = { -1, -1, -1, 0, 0, 0 };
const struct tls_ocsp_info *info = ctx->ocsp_info;
const char *ocsp_result = ctx->ocsp_result;
int ret = 0;
if (!info) {
info = &no_ocsp;
ret = -1;
}
if (!ocsp_result) {
ret = TLS_NO_OCSP;
ocsp_result = "no-ocsp";
}
if (response_status)
*response_status = info->response_status;
if (cert_status)
*cert_status = info->cert_status;
if (crl_reason)
*crl_reason = info->crl_reason;
if (this_update)
*this_update = info->this_update;
if (next_update)
*next_update = info->next_update;
if (revoction_time)
*revoction_time = info->revocation_time;
if (result_text)
*result_text = ocsp_result;
return ret;
}
/*
* Verify stapled response
*/
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;
X509_OBJECT tmpobj;
OCSP_CERTID *cid = NULL;
X509_STORE *store;
int ok;
issuer_name = X509_get_issuer_name(main_cert);
if (!issuer_name)
return NULL;
if (extra_certs) {
issuer = X509_find_by_subject(extra_certs, issuer_name);
if (issuer)
return OCSP_cert_to_id(NULL, main_cert, issuer);
}
store = SSL_CTX_get_cert_store(ssl_ctx);
if (!store)
return NULL;
ok = X509_STORE_CTX_init(&storectx, store, main_cert, extra_certs);
if (ok != 1)
return NULL;
ok = X509_STORE_get_by_subject(&storectx, X509_LU_X509, issuer_name, &tmpobj);
if (ok == 1) {
cid = OCSP_cert_to_id(NULL, main_cert, tmpobj.data.x509);
X509_free(tmpobj.data.x509);
}
X509_STORE_CTX_cleanup(&storectx);
return cid;
}
static int
tls_ocsp_verify_response(struct tls *ctx, X509 *main_cert, STACK_OF(X509) *extra_certs,
SSL_CTX *ssl_ctx, OCSP_RESPONSE *resp)
{
OCSP_BASICRESP *br = NULL;
STACK_OF(X509) *ocsp_chain = 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 ssl_res, ret = -1;
unsigned long flags;
#ifdef BUGGY_VERIFY
STACK_OF(X509) *tmpchain = NULL;
#endif
br = OCSP_response_get1_basic(resp);
if (!br) {
tls_set_errorx(ctx, "ocsp error: cannot load");
goto error;
}
#ifdef BUGGY_VERIFY
/*
* There may be OCSP-subCA in OCSP response that chains to subCA
* in main TLS headers. Need to present both chains to verify.
*
* OCSP_basic_verify() bugs:
* - Does not use br->certs when building chain.
* - Does not use main_certs when validating br->certs.
*/
if (br->certs && extra_certs) {
int i;
combined = sk_X509_new_null();
if (!combined) {
tls_set_errorx(ctx, "ocsp error: cannot merge");
goto error;
}
for (i = 0; i < sk_X509_num(br->certs); i++) {
ssl_res = sk_X509_push(combined, sk_X509_value(br->certs, i));
if (ssl_res == 0) {
tls_set_errorx(ctx, "ocsp error: cannot merge");
goto error;
}
}
for (i = 0; i < sk_X509_num(extra_certs); i++) {
ssl_res = sk_X509_push(combined, sk_X509_value(extra_certs, i));
if (ssl_res == 0) {
tls_set_errorx(ctx, "ocsp error: cannot merge");
goto error;
}
}
/* OCSP_TRUSTOTHER would skip br->certs checks */
flags = 0;
flags = OCSP_TRUSTOTHER;
ocsp_chain = combined;
/* Bug: does not use main_certs when validating br->certs */
if ((flags & OCSP_TRUSTOTHER) == 0) {
tmpchain = br->certs;
br->certs = combined;
}
} else if (br->certs && !extra_certs) {
/* can this happen? */
flags = 0;
ocsp_chain = br->certs;
} else
#endif
{
/*
* Skip validation of 'extra_certs' as this should be done
* already as part of main handshake.
*/
flags = OCSP_TRUSTOTHER;
ocsp_chain = extra_certs;
}
/* now verify */
ssl_res = OCSP_basic_verify(br, ocsp_chain, SSL_CTX_get_cert_store(ssl_ctx), flags);
#ifdef BUGGY_VERIFY
/* replace back */
if (tmpchain) {
br->certs = tmpchain;
tmpchain = NULL;
}
#endif
if (ssl_res != 1) {
tls_set_error_libssl(ctx, "ocsp verify failed");
goto error;
}
/* signature OK, look inside */
response_status = OCSP_response_status(resp);
if (response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
tls_set_errorx(ctx, "ocsp verify failed: unsuccessful response - %s",
OCSP_response_status_str(response_status));
goto error;
}
cid = tls_ocsp_get_certid(main_cert, extra_certs, ssl_ctx);
if (!cid) {
tls_set_errorx(ctx, "ocsp verify failed: no issuer cert");
goto error;
}
ssl_res = OCSP_resp_find_status(br, cid, &cert_status, &crl_reason, &revtime, &thisupd, &nextupd);
if (ssl_res != 1) {
tls_set_errorx(ctx, "ocsp verify failed: no result for cert");
goto error;
}
ssl_res = OCSP_check_validity(thisupd, nextupd, JITTER_SEC, MAXAGE_SEC);
if (ssl_res != 1) {
tls_set_errorx(ctx, "ocsp verify failed: bad age");
goto error;
}
ssl_res = tls_ocsp_fill_info(ctx, response_status, cert_status, crl_reason, revtime, thisupd, nextupd);
if (ssl_res != 0)
goto error;
/* 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 error;
}
ret = 0;
error:
sk_X509_free(combined);
OCSP_CERTID_free(cid);
OCSP_BASICRESP_free(br);
return ret;
}
/*
* Same callback on client-side has different error proto:
* 1=OK, 0=bad, -1=internal error
*/
int
tls_ocsp_verify_callback(SSL *ssl, void *arg)
{
OCSP_RESPONSE *resp = NULL;
STACK_OF(X509) *extra_certs = NULL;
X509 *peer = NULL;
const unsigned char *raw = NULL;
int size, res = -1;
struct tls *ctx;
(void)arg;
ctx = SSL_get_app_data(ssl);
if (!ctx)
return -1;
size = SSL_get_tlsext_status_ocsp_resp(ssl, &raw);
if (size <= 0)
return 1;
peer = SSL_get_peer_certificate(ssl);
if (!peer) {
tls_set_errorx(ctx, "ocsp verify failed: no peer cert");
goto error;
}
resp = d2i_OCSP_RESPONSE(NULL, &raw, size);
if (!resp) {
tls_set_errorx(ctx, "ocsp verify failed: parse failed");
goto error;
}
extra_certs = SSL_get_peer_cert_chain(ssl);
res = tls_ocsp_verify_response(ctx, peer, extra_certs, ctx->ssl_ctx, resp);
error:
tls_ocsp_fill_result(ctx, res);
OCSP_RESPONSE_free(resp);
X509_free(peer);
return (res == 0) ? 1 : 0;
}
/*
* Staple OCSP response to server handshake.
*/
int
tls_ocsp_stapling_callback(SSL *ssl, void *arg)
{
struct tls *ctx;
char *mem, *fmem = NULL;
uint8_t *xmem;
size_t len;
int ret = SSL_TLSEXT_ERR_ALERT_FATAL;
(void)arg;
ctx = SSL_get_app_data(ssl);
if (!ctx)
return SSL_TLSEXT_ERR_NOACK;
if (ctx->config->ocsp_file) {
fmem = mem = (char*)tls_load_file(ctx->config->ocsp_file, &len, NULL);
if (!mem)
goto err;
} else {
mem = ctx->config->ocsp_mem;
len = ctx->config->ocsp_len;
if (!mem)
return SSL_TLSEXT_ERR_NOACK;
}
xmem = OPENSSL_malloc(len);
if (xmem) {
memcpy(xmem, mem, len);
if (SSL_set_tlsext_status_ocsp_resp(ctx->ssl_conn, xmem, len) != 1) {
OPENSSL_free(xmem);
goto err;
}
ret = SSL_TLSEXT_ERR_OK;
}
err:
free(fmem);
return ret;
}
/*
* Query OCSP responder over HTTP(S).
*/
void
tls_ocsp_client_free(struct tls *ctx)
{
struct tls_ocsp_query *q;
if (!ctx)
return;
q = ctx->ocsp_query;
if (q) {
if (q->http_req)
OCSP_REQ_CTX_free(q->http_req);
BIO_free_all(q->bio);
SSL_CTX_free(q->ssl_ctx);
free(q->ocsp_url);
free(q->request_data);
free(q);
ctx->ocsp_query = NULL;
}
}
static struct tls *
tls_ocsp_client_new(void)
{
struct tls *ctx;
ctx = tls_new();
if (!ctx)
return NULL;
ctx->flags = TLS_OCSP_CLIENT;
ctx->ocsp_query = calloc(1, sizeof (struct tls_ocsp_query));
if (!ctx->ocsp_query) {
tls_free(ctx);
return NULL;
}
return ctx;
}
static int
tls_build_ocsp_request(struct tls *ctx)
{
struct tls_ocsp_query *q;
int ok, ret = -1;
OCSP_REQUEST *req = NULL;
OCSP_CERTID *cid = NULL;
OCSP_ONEREQ *onereq = NULL;
BIO *mem = NULL;
void *data;
q = ctx->ocsp_query;
cid = tls_ocsp_get_certid(q->main_cert, q->extra_certs, q->cert_ssl_ctx);
if (!cid) {
tls_set_errorx(ctx, "Cannot create cert-id");
goto failed;
}
req = OCSP_REQUEST_new();
if (!req) {
tls_set_error_libssl(ctx, "Cannot create request");
goto failed;
}
onereq = OCSP_request_add0_id(req, cid);
if (!onereq) {
tls_set_error_libssl(ctx, "Cannot add cert-id to request");
goto failed;
}
cid = NULL;
/*
* Now render it.
*/
mem = BIO_new(BIO_s_mem());
if (!mem) {
tls_set_errorx(ctx, "BIO_new");
goto failed;
}
ok = i2d_OCSP_REQUEST_bio(mem, req);
if (!ok) {
tls_set_error_libssl(ctx, "i2d_OCSP_RESPONSE_bio");
goto failed;
}
q->request_size = BIO_get_mem_data(mem, &data);
q->request_data = malloc(q->request_size);
if (!q->request_data) {
tls_set_error(ctx, "Failed to allocate request data");
goto failed;
}
memcpy(q->request_data, data, q->request_size);
req = NULL;
ret = 0;
failed:
OCSP_CERTID_free(cid);
OCSP_REQUEST_free(req);
BIO_free(mem);
return ret;
}
static int
tls_ocsp_setup(struct tls **ocsp_ctx_p, struct tls_config *config, struct tls *target)
{
struct tls *ctx;
struct tls_ocsp_query *q;
int ret;
STACK_OF(OPENSSL_STRING) *ocsp_urls;
ctx = tls_ocsp_client_new();
if (!ctx)
return -1;
*ocsp_ctx_p = ctx;
q = ctx->ocsp_query;
if (config) {
/* create ctx->ssl_ctx */
ctx->flags = TLS_SERVER;
ret = tls_configure(ctx, config);
ctx->flags = TLS_OCSP_CLIENT;
if (ret != 0)
return ret;
q->main_cert = SSL_get_certificate(ctx->ssl_conn);
q->cert_ssl_ctx = ctx->ssl_ctx;
SSL_CTX_get_extra_chain_certs(ctx->ssl_ctx, &q->extra_certs);
} else {
/* steal state from target struct */
q->main_cert = SSL_get_peer_certificate(target->ssl_conn);
q->extra_certs = SSL_get_peer_cert_chain(target->ssl_conn);
q->cert_ssl_ctx = target->ssl_ctx;
X509_free(q->main_cert); /* unref */
}
if (!q->main_cert) {
tls_set_errorx(ctx, "No cert");
return -1;
}
ocsp_urls = X509_get1_ocsp(q->main_cert);
if (!ocsp_urls)
return TLS_NO_OCSP;
q->ocsp_url = strdup(sk_OPENSSL_STRING_value(ocsp_urls, 0));
if (!q->ocsp_url) {
tls_set_errorx(ctx, "Cannot copy URL");
goto failed;
}
ret = tls_build_ocsp_request(ctx);
if (ret != 0)
goto failed;
*ocsp_ctx_p = ctx;
failed:
X509_email_free(ocsp_urls);
return ret;
}
static int
tls_ocsp_process_response_parsed(struct tls *ctx, struct tls_config *config, OCSP_RESPONSE *resp)
{
struct tls_ocsp_query *q = ctx->ocsp_query;
BIO *mem = NULL;
size_t len;
unsigned char *data;
int ret = -1, ok, res;
res = tls_ocsp_verify_response(ctx, q->main_cert, q->extra_certs, q->cert_ssl_ctx, resp);
if (res < 0)
goto failed;
/* Update blob in config */
if (config) {
mem = BIO_new(BIO_s_mem());
if (!mem) {
tls_set_error_libssl(ctx, "BIO_new");
goto failed;
}
ok = i2d_OCSP_RESPONSE_bio(mem, resp);
if (!ok) {
tls_set_error_libssl(ctx, "i2d_OCSP_RESPONSE_bio");
goto failed;
}
len = BIO_get_mem_data(mem, &data);
res = tls_config_set_ocsp_stapling_mem(config, data, len);
if (res < 0)
goto failed;
}
ret = 0;
failed:
BIO_free(mem);
tls_ocsp_fill_result(ctx, ret);
return ret;
}
static int
tls_ocsp_create_request(struct tls **ocsp_ctx_p,
struct tls_config *config, struct tls *target,
char **ocsp_url,
void **request_blob, size_t *request_size)
{
int res;
struct tls_ocsp_query *q;
res = tls_ocsp_setup(ocsp_ctx_p, config, target);
if (res != 0)
return res;
q = (*ocsp_ctx_p)->ocsp_query;
*ocsp_url = q->ocsp_url;
*request_blob = q->request_data;
*request_size = q->request_size;
return 0;
}
/*
* Public API for request blobs.
*/
int
tls_ocsp_check_peer_request(struct tls **ocsp_ctx_p, struct tls *target,
char **ocsp_url, void **request_blob, size_t *request_size)
{
return tls_ocsp_create_request(ocsp_ctx_p, NULL, target,
ocsp_url, request_blob, request_size);
}
int
tls_ocsp_refresh_stapling_request(struct tls **ocsp_ctx_p,
struct tls_config *config,
char **ocsp_url, void **request_blob, size_t *request_size)
{
return tls_ocsp_create_request(ocsp_ctx_p, config, NULL,
ocsp_url, request_blob, request_size);
}
int
tls_ocsp_process_response(struct tls *ctx, const void *response_blob, size_t size)
{
int ret;
OCSP_RESPONSE *resp;
const unsigned char *raw = response_blob;
resp = d2i_OCSP_RESPONSE(NULL, &raw, size);
if (!resp) {
ctx->ocsp_result = "parse-failed";
tls_set_error_libssl(ctx, "parse failed");
return -1;
}
ret = tls_ocsp_process_response_parsed(ctx, ctx->config, resp);
OCSP_RESPONSE_free(resp);
return ret;
}
/*
* Network processing
*/
static int
tls_ocsp_build_http_req(struct tls *ctx)
{
struct tls_ocsp_query *q = ctx->ocsp_query;
int ok;
OCSP_REQUEST *req;
OCSP_REQ_CTX *sreq;
const unsigned char *data;
int ret=-1, https=0;
char *host=NULL, *port=NULL, *path=NULL;
ok = OCSP_parse_url(q->ocsp_url, &host, &port, &path, &https);
if (ok != 1) {
tls_set_error_libssl(ctx, "Cannot parse URL");
goto failed;
}
sreq = OCSP_sendreq_new(q->bio, path, NULL, -1);
if (!sreq) {
tls_set_error_libssl(ctx, "OCSP HTTP request setup failed");
goto failed;
}
q->http_req = sreq;
ok = OCSP_REQ_CTX_add1_header(sreq, "Host", host);
/* add payload after headers */
if (ok) {
data = q->request_data;
req = d2i_OCSP_REQUEST(NULL, &data, q->request_size);
if (req)
ok = OCSP_REQ_CTX_set1_req(sreq, req);
else
ok = false;
OCSP_REQUEST_free(req);
}
if (ok)
ret = 0;
failed:
free(host);
free(port);
free(path);
return ret;
}
static int
tls_ocsp_connection_setup(struct tls *ctx)
{
SSL *ssl = NULL;
int ret = -1, ok, https=0;
struct tls_ocsp_query *q = ctx->ocsp_query;
union { struct in_addr ip4; struct in6_addr ip6; } addrbuf;
char *host=NULL, *port=NULL, *path=NULL;
ok = OCSP_parse_url(q->ocsp_url, &host, &port, &path, &https);
if (ok != 1) {
tls_set_error_libssl(ctx, "Cannot parse URL");
goto failed;
}
if (https) {
q->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
if (!q->ssl_ctx) {
tls_set_error_libssl(ctx, "Cannot init SSL");
goto failed;
}
SSL_CTX_set_options(q->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
SSL_CTX_clear_options(q->ssl_ctx, SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2);
SSL_CTX_set_cipher_list(q->ssl_ctx, TLS_CIPHERS_COMPAT);
SSL_CTX_set_mode(q->ssl_ctx, SSL_MODE_AUTO_RETRY);
q->bio = BIO_new_ssl_connect(q->ssl_ctx);
if (q->bio) {
if (inet_pton(AF_INET, host, &addrbuf) != 1 &&
inet_pton(AF_INET6, host, &addrbuf) != 1) {
if (!BIO_get_ssl(q->bio, &ssl)) {
tls_set_errorx(ctx, "cannot get ssl struct");
goto failed;
}
if (SSL_set_tlsext_host_name(ssl, host) == 0) {
tls_set_errorx(ctx, "server name indication failure");
goto failed;
}
}
}
} else {
q->bio = BIO_new(BIO_s_connect());
}
if (!q->bio) {
tls_set_error_libssl(ctx, "Cannot connect");
goto failed;
}
BIO_set_conn_hostname(q->bio, host);
BIO_set_conn_port(q->bio, port);
BIO_set_nbio(q->bio, 1);
ret = 0;
failed:
free(host);
free(port);
free(path);
return ret;
}
static int
tls_ocsp_evloop(struct tls *ctx, int *fd_p, struct tls_config *config)
{
struct tls_ocsp_query *q = ctx->ocsp_query;
OCSP_RESPONSE *ocsp_resp = NULL;
int ret, ok;
if (q->http_req == NULL) {
ok = BIO_do_connect(q->bio);
if (ok != 1 && !BIO_should_retry(q->bio)) {
tls_set_error_libssl(ctx, "Connection failure");
goto error;
}
*fd_p = BIO_get_fd(q->bio, NULL);
if (*fd_p < 0) {
tls_set_error_libssl(ctx, "Cannot get FD");
goto error;
}
if (ok != 1)
return TLS_WANT_POLLOUT;
ret = tls_ocsp_build_http_req(ctx);
if (ret != 0)
goto error;
}
ok = OCSP_sendreq_nbio(&ocsp_resp, q->http_req);
if (ok == 1) {
ret = tls_ocsp_process_response_parsed(ctx, config, ocsp_resp);
return ret;
} else if (ok == 0) {
tls_set_error_libssl(ctx, "OCSP request failed");
goto error;
} else if (BIO_should_read(q->bio)) {
return TLS_WANT_POLLIN;
} else if (BIO_should_write(q->bio)) {
return TLS_WANT_POLLOUT;
}
tls_set_error_libssl(ctx, "Unexpected request error");
error:
tls_ocsp_fill_result(ctx, -1);
return -1;
}
static int
tls_ocsp_do_poll(struct tls *ctx, int errcode, int fd)
{
struct pollfd pfd;
int res;
memset(&pfd, 0, sizeof(pfd));
pfd.fd = fd;
if (errcode == TLS_WANT_POLLIN) {
pfd.events = POLLIN;
} else if (errcode == TLS_WANT_POLLOUT) {
pfd.events = POLLOUT;
} else {
tls_set_error(ctx, "bad code to poll");
return -1;
}
res = poll(&pfd, 1, -1);
if (res == 1) {
return 0;
} else if (res == 0) {
tls_set_errorx(ctx, "poll timed out");
return -1;
}
tls_set_error(ctx, "poll error");
return -1;
}
static int
tls_ocsp_query_async(struct tls **ocsp_ctx_p, int *fd_p, struct tls_config *config, struct tls *target)
{
struct tls *ctx = *ocsp_ctx_p;
int ret;
if (!ctx) {
ret = tls_ocsp_setup(&ctx, config, target);
if (ret != 0)
goto failed;
ret = tls_ocsp_connection_setup(ctx);
if (ret != 0)
goto failed;
*ocsp_ctx_p = ctx;
}
return tls_ocsp_evloop(ctx, fd_p, config);
failed:
tls_free(ctx);
return -1;
}
static int
tls_ocsp_common_query(struct tls **ocsp_ctx_p, int *fd_p, struct tls_config *config, struct tls *target)
{
struct tls *ctx = NULL;
int ret, fd;
fd = -1;
/* async path */
if (fd_p)
return tls_ocsp_query_async(ocsp_ctx_p, fd_p, config, target);
/* sync path */
while (1) {
ret = tls_ocsp_query_async(&ctx, &fd, config, target);
if (ret != TLS_WANT_POLLIN && ret != TLS_WANT_POLLOUT)
break;
ret = tls_ocsp_do_poll(ctx, ret, fd);
if (ret != 0)
break;
}
*ocsp_ctx_p = ctx;
return ret;
}
/*
* Public API.
*/
int
tls_ocsp_check_peer(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls *target)
{
return tls_ocsp_common_query(ocsp_ctx_p, async_fd_p, NULL, target);
}
int
tls_ocsp_refresh_stapling(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls_config *config)
{
return tls_ocsp_common_query(ocsp_ctx_p, async_fd_p, config, NULL);
}
#else /* No OCSP */
void tls_ocsp_info_free(struct tls_ocsp_info *info) {}
void tls_ocsp_client_free(struct tls *ctx) {}
int
tls_get_ocsp_info(struct tls *ctx, int *response_status, int *cert_status,
int *crl_reason, time_t *this_update,
time_t *next_update, time_t *revoction_time,
const char **result_text)
{
if (response_status) *response_status = -1;
if (cert_status) *cert_status = -1;
if (crl_reason) *crl_reason = -1;
if (result_text) *result_text = "OCSP not supported";
if (this_update) *this_update = 0;
if (next_update) *next_update = 0;
if (revoction_time) *revoction_time = 0;
return TLS_NO_OCSP;
}
int
tls_ocsp_check_peer(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls *target)
{
*ocsp_ctx_p = NULL;
return TLS_NO_OCSP;
}
int
tls_ocsp_refresh_stapling(struct tls **ocsp_ctx_p, int *async_fd_p, struct tls_config *config)
{
*ocsp_ctx_p = NULL;
return TLS_NO_OCSP;
}
#endif /* OPENSSL_NO_OCSP */

83
lib/libtls/tls_peer.c Normal file
View File

@ -0,0 +1,83 @@
/* $OpenBSD: tls_peer.c,v 1.3 2015/09/11 13:22:39 beck 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_internal.h"
#include "tls_compat.h"
#include "tls.h"
const char *
tls_peer_cert_hash(struct tls *ctx)
{
if (ctx->conninfo)
return (ctx->conninfo->hash);
return NULL;
}
const char *
tls_peer_cert_issuer(struct tls *ctx)
{
if (ctx->conninfo)
return (ctx->conninfo->issuer);
return NULL;
}
const char *
tls_peer_cert_subject(struct tls *ctx)
{
if (ctx->conninfo)
return (ctx->conninfo->subject);
return NULL;
}
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)
{
if (ctx->ssl_peer_cert == NULL)
return (0);
return (tls_check_name(ctx, ctx->ssl_peer_cert, name) == 0);
}
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);
}

193
lib/libtls/tls_server.c Normal file
View File

@ -0,0 +1,193 @@
/* $OpenBSD: tls_server.c,v 1.6 2015/03/31 12:21:27 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/ec.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include "tls_internal.h"
#include "tls_compat.h"
#include "tls.h"
struct tls *
tls_server(void)
{
struct tls *ctx;
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;
(void)ctx;
if ((conn_ctx = tls_new()) == NULL)
return (NULL);
conn_ctx->flags |= TLS_SERVER_CONN;
return (conn_ctx);
}
int
tls_configure_server(struct tls *ctx)
{
EC_KEY *ecdh_key;
unsigned char sid[SSL_MAX_SSL_SESSION_ID_LENGTH];
if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
tls_set_errorx(ctx, "ssl context failure");
goto err;
}
if (tls_configure_ssl(ctx) != 0)
goto err;
if (tls_configure_keypair(ctx, 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, verify) == -1)
goto err;
}
if (ctx->config->dheparams == -1)
SSL_CTX_set_dh_auto(ctx->ssl_ctx, 1);
else if (ctx->config->dheparams == 1024)
SSL_CTX_set_dh_auto(ctx->ssl_ctx, 2);
if (ctx->config->ecdhecurve == -1) {
SSL_CTX_set_ecdh_auto(ctx->ssl_ctx, 1);
} else if (ctx->config->ecdhecurve != NID_undef) {
if ((ecdh_key = EC_KEY_new_by_curve_name(
ctx->config->ecdhecurve)) == NULL) {
tls_set_errorx(ctx, "failed to set ECDHE curve");
goto err;
}
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_SINGLE_ECDH_USE);
SSL_CTX_set_tmp_ecdh(ctx->ssl_ctx, ecdh_key);
EC_KEY_free(ecdh_key);
}
if (ctx->config->ciphers_server == 1)
SSL_CTX_set_options(ctx->ssl_ctx,
SSL_OP_CIPHER_SERVER_PREFERENCE);
if (SSL_CTX_set_tlsext_status_cb(ctx->ssl_ctx, tls_ocsp_stapling_callback) != 1) {
tls_set_errorx(ctx, "ssl OCSP stapling setup failure");
goto err;
}
/*
* Set session ID context to a random value. We don't support
* persistent caching of sessions so it is OK to set a temporary
* session ID context that is valid during run time.
*/
if (!RAND_bytes(sid, sizeof(sid))) {
tls_set_errorx(ctx, "failed to generate session id");
goto err;
}
if (!SSL_CTX_set_session_id_context(ctx->ssl_ctx, sid, sizeof(sid))) {
tls_set_errorx(ctx, "failed to set session id context");
goto err;
}
return (0);
err:
return (-1);
}
int
tls_accept_socket(struct tls *ctx, struct tls **cctx, int socket)
{
return (tls_accept_fds(ctx, cctx, socket, socket));
}
int
tls_accept_fds(struct tls *ctx, struct tls **cctx, int fd_read, int fd_write)
{
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;
}
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_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;
}
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);
}

226
lib/libtls/tls_util.c Normal file
View File

@ -0,0 +1,226 @@
/* $OpenBSD: tls_util.c,v 1.1 2014/10/31 13:46:17 jsing Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@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 <openssl/dh.h>
#include <openssl/evp.h>
#include <sys/stat.h>
#include "tls_internal.h"
#include "tls_compat.h"
#include "tls.h"
const char *
tls_backend_version(void)
{
return SSLeay_version(SSLEAY_VERSION);
}
/*
* 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 fail;
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 seperator. */
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)
goto fail;
if (asprintf(port, "%s", p) == -1)
goto fail;
rv = 0;
goto done;
fail:
free(*host);
*host = NULL;
free(*port);
*port = NULL;
rv = -1;
done:
free(s);
return (rv);
}
static int
tls_password_cb(char *buf, int size, int rwflag, void *u)
{
size_t len;
if (u == NULL) {
memset(buf, 0, size);
return (0);
}
len = snprintf(buf, size, "%s", u);
if (len >= (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;
uint8_t *buf = NULL;
char *data;
struct stat st;
size_t size;
int fd = -1;
*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 fail;
size = (size_t)st.st_size;
if ((buf = calloc(1, size + 1)) == NULL)
goto fail;
if (read(fd, buf, size) != (ssize_t)size)
goto fail;
close(fd);
goto done;
}
/* Or read the (possibly) encrypted key from file */
if ((fp = fdopen(fd, "r")) == NULL)
goto fail;
fd = -1;
key = PEM_read_PrivateKey(fp, NULL, tls_password_cb, password);
fclose(fp);
if (key == NULL)
goto fail;
/* Write unencrypted key to memory buffer */
if ((bio = BIO_new(BIO_s_mem())) == NULL)
goto fail;
if (!PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL))
goto fail;
if ((size = BIO_get_mem_data(bio, &data)) <= 0)
goto fail;
if ((buf = calloc(1, size)) == NULL)
goto fail;
memcpy(buf, data, size);
BIO_free_all(bio);
EVP_PKEY_free(key);
done:
*len = size;
return (buf);
fail:
free(buf);
if (fd != -1)
close(fd);
if (bio != NULL)
BIO_free_all(bio);
if (key != NULL)
EVP_PKEY_free(key);
return (NULL);
}
ssize_t
tls_get_connection_info(struct tls *ctx, char *buf, size_t buflen)
{
SSL *conn = ctx->ssl_conn;
const char *ocsp_pfx = "", *ocsp_info = "";
const char *proto = "-", *cipher = "-";
char dh[64];
int used_dh_bits = ctx->used_dh_bits, used_ecdh_nid = ctx->used_ecdh_nid;
if (conn != NULL) {
proto = SSL_get_version(conn);
cipher = SSL_get_cipher(conn);
#ifdef SSL_get_server_tmp_key
if (ctx->flags & TLS_CLIENT) {
EVP_PKEY *pk = NULL;
int ok = SSL_get_server_tmp_key(conn, &pk);
int pk_type = EVP_PKEY_id(pk);
if (ok && pk) {
if (pk_type == EVP_PKEY_DH) {
DH *dh = EVP_PKEY_get0(pk);
used_dh_bits = DH_size(dh) * 8;
} else if (pk_type == EVP_PKEY_EC) {
EC_KEY *ecdh = EVP_PKEY_get0(pk);
const EC_GROUP *eg = EC_KEY_get0_group(ecdh);
used_ecdh_nid = EC_GROUP_get_curve_name(eg);
}
EVP_PKEY_free(pk);
}
}
#endif
}
if (used_dh_bits) {
snprintf(dh, sizeof dh, "/DH=%d", used_dh_bits);
} else if (used_ecdh_nid) {
snprintf(dh, sizeof dh, "/ECDH=%s", OBJ_nid2sn(used_ecdh_nid));
} else {
dh[0] = 0;
}
if (ctx->ocsp_result) {
ocsp_info = ctx->ocsp_result;
ocsp_pfx = "/OCSP=";
}
return snprintf(buf, buflen, "%s/%s%s%s%s", proto, cipher, dh, ocsp_pfx, ocsp_info);
}

256
lib/libtls/tls_verify.c Normal file
View File

@ -0,0 +1,256 @@
/* $OpenBSD: tls_verify.c,v 1.7 2015/02/11 06:46:33 jsing 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 <openssl/x509v3.h>
#include "tls_internal.h"
#include "tls_compat.h"
#include "tls.h"
static int tls_match_name(const char *cert_name, const char *name);
static int tls_check_subject_altname(struct tls *ctx, X509 *cert,
const char *name);
static int tls_check_common_name(struct tls *ctx, X509 *cert, const char *name);
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. */
static int
tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name)
{
STACK_OF(GENERAL_NAME) *altname_stack = NULL;
union tls_addr addrbuf;
int addrlen, type;
int count, i;
int rv = -1;
altname_stack = X509_get_ext_d2i(cert, NID_subject_alt_name,
NULL, NULL);
if (altname_stack == NULL)
return -1;
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 != type)
continue;
if (type == GEN_DNS) {
void *data;
int format, len;
format = ASN1_STRING_type(altname->d.dNSName);
if (format == V_ASN1_IA5STRING) {
data = ASN1_STRING_data(altname->d.dNSName);
len = ASN1_STRING_length(altname->d.dNSName);
if (len < 0 || len != (int)strlen(data)) {
tls_set_errorx(ctx,
"error verifying name '%s': "
"NUL byte in subjectAltName, "
"probably a malicious certificate",
name);
rv = -2;
break;
}
/*
* 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_error(ctx,
"error verifying name '%s': "
"a dNSName of \" \" must not be "
"used", name);
rv = -2;
break;
}
if (tls_match_name(data, name) == 0) {
rv = 0;
break;
}
} else {
#ifdef DEBUG
fprintf(stdout, "%s: unhandled subjectAltName "
"dNSName encoding (%d)\n", getprogname(),
format);
#endif
}
} else if (type == GEN_IPADD) {
unsigned char *data;
int datalen;
datalen = ASN1_STRING_length(altname->d.iPAddress);
data = ASN1_STRING_data(altname->d.iPAddress);
if (datalen < 0) {
tls_set_errorx(ctx,
"Unexpected negative length for an "
"IP address: %d", datalen);
rv = -2;
break;
}
/*
* 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) {
rv = 0;
break;
}
}
}
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)
{
X509_NAME *subject_name;
char *common_name = NULL;
union tls_addr addrbuf;
int common_name_len;
int rv = -1;
subject_name = X509_get_subject_name(cert);
if (subject_name == NULL)
goto out;
common_name_len = X509_NAME_get_text_by_NID(subject_name,
NID_commonName, NULL, 0);
if (common_name_len < 0)
goto out;
common_name = calloc(common_name_len + 1, 1);
if (common_name == NULL)
goto out;
X509_NAME_get_text_by_NID(subject_name, NID_commonName, common_name,
common_name_len + 1);
/* NUL bytes in CN? */
if (common_name_len != (int)strlen(common_name)) {
tls_set_errorx(ctx, "error verifying name '%s': "
"NUL byte in Common Name field, "
"probably a malicious certificate", name);
rv = -2;
goto out;
}
if (inet_pton(AF_INET, name, &addrbuf) == 1 ||
inet_pton(AF_INET6, name, &addrbuf) == 1) {
/*
* We don't want to attempt wildcard matching against IP
* addresses, so perform a simple comparison here.
*/
if (strcmp(common_name, name) == 0)
rv = 0;
else
rv = -1;
goto out;
}
if (tls_match_name(common_name, name) == 0)
rv = 0;
out:
free(common_name);
return rv;
}
int
tls_check_name(struct tls *ctx, X509 *cert, const char *name)
{
int rv;
rv = tls_check_subject_altname(ctx, cert, name);
if (rv == 0 || rv == -2)
return rv;
return tls_check_common_name(ctx, cert, name);
}

View File

@ -1,3 +1,4 @@
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra")
set(machine_library machinarium)