summaryrefslogtreecommitdiff
path: root/lib/libtls
diff options
context:
space:
mode:
authorJoel Sing <jsing@cvs.openbsd.org>2014-10-31 13:46:18 +0000
committerJoel Sing <jsing@cvs.openbsd.org>2014-10-31 13:46:18 +0000
commitf44b598f55e4b6375817972d506ad93e63c93bd1 (patch)
tree853c2c67e6a38e22e1a866a41d78d694c9411397 /lib/libtls
parentc3c9d9dbad2cd6645037029656c4a7339bda69da (diff)
Rename libressl to libtls to avoid confusion and to make it easier to
distinguish between LibreSSL (the project) and libressl (the library). Discussed with many.
Diffstat (limited to 'lib/libtls')
-rw-r--r--lib/libtls/Makefile58
-rw-r--r--lib/libtls/shlib_version2
-rw-r--r--lib/libtls/tls.c300
-rw-r--r--lib/libtls/tls.h74
-rw-r--r--lib/libtls/tls_client.c212
-rw-r--r--lib/libtls/tls_config.c201
-rw-r--r--lib/libtls/tls_init.3316
-rw-r--r--lib/libtls/tls_internal.h72
-rw-r--r--lib/libtls/tls_server.c134
-rw-r--r--lib/libtls/tls_util.c81
-rw-r--r--lib/libtls/tls_verify.c225
11 files changed, 1675 insertions, 0 deletions
diff --git a/lib/libtls/Makefile b/lib/libtls/Makefile
new file mode 100644
index 00000000000..b83a6de2ce0
--- /dev/null
+++ b/lib/libtls/Makefile
@@ -0,0 +1,58 @@
+# $OpenBSD: Makefile,v 1.1 2014/10/31 13:46:17 jsing Exp $
+
+CFLAGS+= -Wall -Werror -Wimplicit
+CFLAGS+= -DLIBRESSL_INTERNAL
+
+LIB= tls
+
+DPADD= ${LIBCRYPTO} ${LIBSSL}
+
+HDRS= tls.h
+
+SRCS= tls.c \
+ tls_client.c \
+ tls_config.c \
+ tls_server.c \
+ tls_util.c \
+ tls_verify.c
+
+MAN= tls_init.3
+
+MLINKS+=tls_init.3 tls_config_new.3
+MLINKS+=tls_init.3 tls_config_free.3
+MLINKS+=tls_init.3 tls_config_set_ca_file.3
+MLINKS+=tls_init.3 tls_config_set_ca_path.3
+MLINKS+=tls_init.3 tls_config_set_cert_file.3
+MLINKS+=tls_init.3 tls_config_set_cert_mem.3
+MLINKS+=tls_init.3 tls_config_set_ciphers.3
+MLINKS+=tls_init.3 tls_config_set_ecdhcurve.3
+MLINKS+=tls_init.3 tls_config_set_key_file.3
+MLINKS+=tls_init.3 tls_config_set_key_mem.3
+MLINKS+=tls_init.3 tls_config_set_protocols.3
+MLINKS+=tls_init.3 tls_config_set_verify_depth.3
+MLINKS+=tls_init.3 tls_config_clear_keys.3
+MLINKS+=tls_init.3 tls_config_insecure_noverifyhost.3
+MLINKS+=tls_init.3 tls_config_insecure_noverifycert.3
+MLINKS+=tls_init.3 tls_config_verify.3
+MLINKS+=tls_init.3 tls_client.3
+MLINKS+=tls_init.3 tls_server.3
+MLINKS+=tls_init.3 tls_configure.3
+MLINKS+=tls_init.3 tls_error.3
+MLINKS+=tls_init.3 tls_reset.3
+MLINKS+=tls_init.3 tls_free.3
+MLINKS+=tls_init.3 tls_close.3
+MLINKS+=tls_init.3 tls_connect.3
+MLINKS+=tls_init.3 tls_connect_socket.3
+MLINKS+=tls_init.3 tls_read.3
+MLINKS+=tls_init.3 tls_write.3
+
+includes:
+ @cd ${.CURDIR}; for i in $(HDRS); do \
+ j="cmp -s $$i ${DESTDIR}/usr/include/$$i || \
+ ${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${BINGRP} -m 444 $$i\
+ ${DESTDIR}/usr/include/"; \
+ echo $$j; \
+ eval "$$j"; \
+ done;
+
+.include <bsd.lib.mk>
diff --git a/lib/libtls/shlib_version b/lib/libtls/shlib_version
new file mode 100644
index 00000000000..1edea46de91
--- /dev/null
+++ b/lib/libtls/shlib_version
@@ -0,0 +1,2 @@
+major=1
+minor=0
diff --git a/lib/libtls/tls.c b/lib/libtls/tls.c
new file mode 100644
index 00000000000..a7f612e40ba
--- /dev/null
+++ b/lib/libtls/tls.c
@@ -0,0 +1,300 @@
+/* $OpenBSD: tls.c,v 1.1 2014/10/31 13:46:17 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 <sys/socket.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/x509.h>
+
+#include <tls.h>
+#include "tls_internal.h"
+
+static struct tls_config *tls_config_default;
+
+int
+tls_init(void)
+{
+ static int tls_initialised = 0;
+
+ if (tls_initialised)
+ return (0);
+
+ SSL_load_error_strings();
+ SSL_library_init();
+
+ if ((tls_config_default = tls_config_new()) == NULL)
+ return (-1);
+
+ tls_initialised = 1;
+
+ return (0);
+}
+
+const char *
+tls_error(struct tls *ctx)
+{
+ return ctx->errmsg;
+}
+
+int
+tls_set_error(struct tls *ctx, char *fmt, ...)
+{
+ va_list ap;
+ int rv;
+
+ ctx->err = errno;
+ free(ctx->errmsg);
+ ctx->errmsg = NULL;
+
+ va_start(ap, fmt);
+ rv = vasprintf(&ctx->errmsg, fmt, ap);
+ va_end(ap);
+
+ return (rv);
+}
+
+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)
+{
+ EVP_PKEY *pkey = NULL;
+ X509 *cert = NULL;
+ BIO *bio = NULL;
+
+ if (ctx->config->cert_mem != NULL) {
+ if (SSL_CTX_use_certificate_chain(ctx->ssl_ctx,
+ ctx->config->cert_mem, ctx->config->cert_len) != 1) {
+ tls_set_error(ctx, "failed to load certificate");
+ goto err;
+ }
+ cert = NULL;
+ }
+ if (ctx->config->key_mem != NULL) {
+ if ((bio = BIO_new_mem_buf(ctx->config->key_mem,
+ ctx->config->key_len)) == NULL) {
+ tls_set_error(ctx, "failed to create buffer");
+ goto err;
+ }
+ if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL,
+ NULL)) == NULL) {
+ tls_set_error(ctx, "failed to read private key");
+ goto err;
+ }
+ if (SSL_CTX_use_PrivateKey(ctx->ssl_ctx, pkey) != 1) {
+ tls_set_error(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_error(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_error(ctx, "failed to load private key file");
+ goto err;
+ }
+ }
+
+ if (SSL_CTX_check_private_key(ctx->ssl_ctx) != 1) {
+ tls_set_error(ctx, "private/public key mismatch");
+ goto err;
+ }
+
+ return (0);
+
+err:
+ EVP_PKEY_free(pkey);
+ X509_free(cert);
+ BIO_free(bio);
+
+ return (1);
+}
+
+int
+tls_configure_ssl(struct tls *ctx)
+{
+ 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_error(ctx, "failed to set ciphers");
+ goto err;
+ }
+ }
+
+ 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);
+
+ ctx->ssl_conn = NULL;
+ ctx->ssl_ctx = NULL;
+
+ ctx->socket = -1;
+
+ ctx->err = 0;
+ free(ctx->errmsg);
+ ctx->errmsg = NULL;
+}
+
+int
+tls_read(struct tls *ctx, void *buf, size_t buflen, size_t *outlen)
+{
+ int ret, ssl_err;
+
+ ret = SSL_read(ctx->ssl_conn, buf, buflen);
+ if (ret > 0) {
+ *outlen = (size_t)ret;
+ return (0);
+ }
+
+ ssl_err = SSL_get_error(ctx->ssl_conn, ret);
+ switch (ssl_err) {
+ case SSL_ERROR_WANT_READ:
+ return (TLS_READ_AGAIN);
+ case SSL_ERROR_WANT_WRITE:
+ return (TLS_WRITE_AGAIN);
+ default:
+ tls_set_error(ctx, "read failed (%i)", ssl_err);
+ return (-1);
+ }
+}
+
+int
+tls_write(struct tls *ctx, const void *buf, size_t buflen, size_t *outlen)
+{
+ int ret, ssl_err;
+
+ ret = SSL_write(ctx->ssl_conn, buf, buflen);
+ if (ret > 0) {
+ *outlen = (size_t)ret;
+ return (0);
+ }
+
+ ssl_err = SSL_get_error(ctx->ssl_conn, ret);
+ switch (ssl_err) {
+ case SSL_ERROR_WANT_READ:
+ return (TLS_READ_AGAIN);
+ case SSL_ERROR_WANT_WRITE:
+ return (TLS_WRITE_AGAIN);
+ default:
+ tls_set_error(ctx, "write failed (%i)", ssl_err);
+ return (-1);
+ }
+}
+
+int
+tls_close(struct tls *ctx)
+{
+ /* XXX - handle case where multiple calls are required. */
+ if (ctx->ssl_conn != NULL) {
+ if (SSL_shutdown(ctx->ssl_conn) == -1) {
+ tls_set_error(ctx, "SSL shutdown failed");
+ goto err;
+ }
+ }
+
+ if (ctx->socket != -1) {
+ if (shutdown(ctx->socket, SHUT_RDWR) != 0) {
+ tls_set_error(ctx, "shutdown");
+ goto err;
+ }
+ if (close(ctx->socket) != 0) {
+ tls_set_error(ctx, "close");
+ goto err;
+ }
+ ctx->socket = -1;
+ }
+
+ return (0);
+
+err:
+ return (-1);
+}
diff --git a/lib/libtls/tls.h b/lib/libtls/tls.h
new file mode 100644
index 00000000000..0fa776e584c
--- /dev/null
+++ b/lib/libtls/tls.h
@@ -0,0 +1,74 @@
+/* $OpenBSD: tls.h,v 1.1 2014/10/31 13:46:17 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
+
+#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_DEFAULT TLS_PROTOCOL_TLSv1
+
+#define TLS_READ_AGAIN -2
+#define TLS_WRITE_AGAIN -3
+
+struct tls;
+struct tls_config;
+
+int tls_init(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_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_ecdhcurve(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);
+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_clear_keys(struct tls_config *config);
+void tls_config_insecure_noverifyhost(struct tls_config *config);
+void tls_config_insecure_noverifycert(struct tls_config *config);
+void tls_config_verify(struct tls_config *config);
+
+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_socket(struct tls *ctx, struct tls **cctx, int socket);
+int tls_connect(struct tls *ctx, const char *host, const char *port);
+int tls_connect_socket(struct tls *ctx, int s, const char *hostname);
+int tls_read(struct tls *ctx, void *buf, size_t buflen, size_t *outlen);
+int tls_write(struct tls *ctx, const void *buf, size_t buflen, size_t *outlen);
+int tls_close(struct tls *ctx);
+
+#endif /* HEADER_TLS_H */
diff --git a/lib/libtls/tls_client.c b/lib/libtls/tls_client.c
new file mode 100644
index 00000000000..853766f87b6
--- /dev/null
+++ b/lib/libtls/tls_client.c
@@ -0,0 +1,212 @@
+/* $OpenBSD: tls_client.c,v 1.1 2014/10/31 13:46:17 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 <sys/types.h>
+#include <sys/socket.h>
+
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <openssl/x509.h>
+
+#include <tls.h>
+#include "tls_internal.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)
+{
+ 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_error(ctx, "not a client context");
+ goto err;
+ }
+
+ if (host == NULL) {
+ tls_set_error(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_error(ctx, "memory allocation failure");
+ goto err;
+ }
+ if (ret != 0)
+ port = HTTPS_PORT;
+ }
+
+ h = (hs != NULL) ? hs : host;
+ p = (ps != NULL) ? ps : port;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ if ((ret = getaddrinfo(h, p, &hints, &res0)) != 0) {
+ tls_set_error(ctx, "%s", gai_strerror(ret));
+ goto err;
+ }
+ 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 (tls_connect_socket(ctx, s, h) != 0) {
+ close(s);
+ goto err;
+ }
+
+ rv = 0;
+
+err:
+
+ free(hs);
+ free(ps);
+
+ return (rv);
+}
+
+int
+tls_connect_socket(struct tls *ctx, int socket, const char *hostname)
+{
+ union { struct in_addr ip4; struct in6_addr ip6; } addrbuf;
+ X509 *cert = NULL;
+ int ret;
+
+ if ((ctx->flags & TLS_CLIENT) == 0) {
+ tls_set_error(ctx, "not a client context");
+ goto err;
+ }
+
+ ctx->socket = socket;
+
+ if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) {
+ tls_set_error(ctx, "ssl context failure");
+ goto err;
+ }
+
+ if (tls_configure_ssl(ctx) != 0)
+ goto err;
+
+ if (ctx->config->verify_host) {
+ if (hostname == NULL) {
+ tls_set_error(ctx, "server name not specified");
+ goto err;
+ }
+ }
+
+ if (ctx->config->verify_cert) {
+ SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
+
+ if (SSL_CTX_load_verify_locations(ctx->ssl_ctx,
+ ctx->config->ca_file, ctx->config->ca_path) != 1) {
+ tls_set_error(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);
+ }
+
+ if ((ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) {
+ tls_set_error(ctx, "ssl connection failure");
+ goto err;
+ }
+ if (SSL_set_fd(ctx->ssl_conn, ctx->socket) != 1) {
+ tls_set_error(ctx, "ssl file descriptor failure");
+ goto err;
+ }
+
+ /*
+ * RFC4366 (SNI): Literal IPv4 and IPv6 addresses are not
+ * permitted in "HostName".
+ */
+ if (hostname != NULL &&
+ inet_pton(AF_INET, hostname, &addrbuf) != 1 &&
+ inet_pton(AF_INET6, hostname, &addrbuf) != 1) {
+ if (SSL_set_tlsext_host_name(ctx->ssl_conn, hostname) == 0) {
+ tls_set_error(ctx, "SNI host name failed");
+ goto err;
+ }
+ }
+
+ if ((ret = SSL_connect(ctx->ssl_conn)) != 1) {
+ tls_set_error(ctx, "SSL connect failed: %i",
+ SSL_get_error(ctx->ssl_conn, ret));
+ goto err;
+ }
+
+ if (ctx->config->verify_host) {
+ cert = SSL_get_peer_certificate(ctx->ssl_conn);
+ if (cert == NULL) {
+ tls_set_error(ctx, "no server certificate");
+ goto err;
+ }
+ if (tls_check_hostname(cert, hostname) != 0) {
+ tls_set_error(ctx, "host `%s' not present in"
+ " server certificate", hostname);
+ goto err;
+ }
+ }
+
+ return (0);
+
+err:
+ X509_free(cert);
+
+ return (-1);
+}
diff --git a/lib/libtls/tls_config.c b/lib/libtls/tls_config.c
new file mode 100644
index 00000000000..0e435f616aa
--- /dev/null
+++ b/lib/libtls/tls_config.c
@@ -0,0 +1,201 @@
+/* $OpenBSD: tls_config.c,v 1.1 2014/10/31 13:46:17 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 <errno.h>
+#include <stdlib.h>
+
+#include <tls.h>
+#include "tls_internal.h"
+
+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) {
+ tls_config_free(config);
+ return (NULL);
+ }
+ tls_config_set_ecdhcurve(config, "auto");
+ tls_config_set_protocols(config, TLS_PROTOCOLS_DEFAULT);
+ tls_config_set_verify_depth(config, 6);
+
+ tls_config_verify(config);
+
+ return (config);
+}
+
+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_cert_mem(config, NULL, 0);
+ tls_config_set_key_mem(config, NULL, 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_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)
+{
+ return set_string(&config->ciphers, ciphers);
+}
+
+int
+tls_config_set_ecdhcurve(struct tls_config *config, const char *name)
+{
+ int nid;
+
+ if (name == NULL)
+ nid = NID_undef;
+ else if (strcasecmp(name, "auto") == 0)
+ nid = -1;
+ else if ((nid = OBJ_txt2nid(name)) == NID_undef)
+ return (-1);
+
+ config->ecdhcurve = 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);
+}
+
+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_insecure_noverifyhost(struct tls_config *config)
+{
+ config->verify_host = 0;
+}
+
+void
+tls_config_insecure_noverifycert(struct tls_config *config)
+{
+ config->verify_cert = 0;
+}
+
+void
+tls_config_verify(struct tls_config *config)
+{
+ config->verify_host = 1;
+ config->verify_cert = 1;
+}
diff --git a/lib/libtls/tls_init.3 b/lib/libtls/tls_init.3
new file mode 100644
index 00000000000..faa9b995393
--- /dev/null
+++ b/lib/libtls/tls_init.3
@@ -0,0 +1,316 @@
+.\" $OpenBSD: tls_init.3,v 1.1 2014/10/31 13:46:17 jsing Exp $
+.\"
+.\" Copyright (c) 2014 Ted Unangst <tedu@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.
+.\"
+.Dd $Mdocdate: October 31 2014 $
+.Dt TLS 3
+.Os
+.Sh NAME
+.Nm tls_init ,
+.Nm tls_error ,
+.Nm tls_config_new ,
+.Nm tls_config_free ,
+.Nm tls_config_set_ca_file ,
+.Nm tls_config_set_ca_path ,
+.Nm tls_config_set_cert_file ,
+.Nm tls_config_set_cert_mem ,
+.Nm tls_config_set_ciphers ,
+.Nm tls_config_set_ecdhcurve ,
+.Nm tls_config_set_key_file ,
+.Nm tls_config_set_key_mem ,
+.Nm tls_config_set_protocols ,
+.Nm tls_config_set_verify_depth ,
+.Nm tls_config_clear_keys ,
+.Nm tls_config_insecure_noverifyhost ,
+.Nm tls_config_insecure_noverifycert ,
+.Nm tls_config_verify ,
+.Nm tls_client ,
+.Nm tls_server ,
+.Nm tls_configure ,
+.Nm tls_reset ,
+.Nm tls_close ,
+.Nm tls_free ,
+.Nm tls_connect ,
+.Nm tls_connect_socket ,
+.Nm tls_read ,
+.Nm tls_write ,
+.Nd tls TLS client and server API
+.Sh SYNOPSIS
+.In tls.h
+.Ft "int"
+.Fn tls_init "void"
+.Ft "const char *"
+.Fn tls_error "struct tls *ctx"
+.Ft "struct tls_config *"
+.Fn tls_config_new "void"
+.Ft "void"
+.Fn tls_config_free "struct tls_config *config"
+.Ft "int"
+.Fn tls_config_set_ca_file "struct tls_config *config" "const char *ca_file"
+.Ft "int"
+.Fn tls_config_set_ca_path "struct tls_config *config" "const char *ca_path"
+.Ft "int"
+.Fn tls_config_set_cert_file "struct tls_config *config" "const char *cert_file"
+.Ft "int"
+.Fn tls_config_set_cert_mem "struct tls_config *config" "const uint8_t *cert" "size_t len"
+.Ft "int"
+.Fn tls_config_set_ciphers "struct tls_config *config" "const char *ciphers"
+.Ft "int"
+.Fn tls_config_set_ecdhcurve "struct tls_config *config" "const char *name"
+.Ft "int"
+.Fn tls_config_set_key_file "struct tls_config *config" "const char *key_file"
+.Ft "int"
+.Fn tls_config_set_key_mem "struct tls_config *config" "const uint8_t *key" "size_t len"
+.Ft "int"
+.Fn tls_config_set_protocols "struct tls_config *config" "uint32_t protocols"
+.Ft "int"
+.Fn tls_config_set_verify_depth "struct tls_config *config" "int verify_depth"
+.Ft "void"
+.Fn tls_config_clear_keys "struct tls_config *config"
+.Ft "void"
+.Fn tls_config_insecure_noverifyhost "struct tls_config *config"
+.Ft "void"
+.Fn tls_config_insecure_noverifycert "struct tls_config *config"
+.Ft "void"
+.Fn tls_config_verify "struct tls_config *config"
+.Ft "struct tls *"
+.Fn tls_client void
+.Ft "struct tls *"
+.Fn tls_server void
+.Ft "int"
+.Fn tls_configure "struct tls *ctx" "struct tls_config *config"
+.Ft "void"
+.Fn tls_reset "struct tls *ctx"
+.Ft "int"
+.Fn tls_close "struct tls *ctx"
+.Ft "void"
+.Fn tls_free "struct tls *ctx"
+.Ft "int"
+.Fn tls_connect "struct tls *ctx" "const char *host" "const char *port"
+.Ft "int"
+.Fn tls_connect_socket "struct tls *ctx" "int s" "const char *hostname"
+.Ft "int"
+.Fn tls_read "struct tls *ctx" "void *buf" "size_t buflen" "size_t *outlen"
+.Ft "int"
+.Fn tls_write "struct tls *ctx" "const void *buf" "size_t buflen"
+.Sh DESCRIPTION
+The
+.Nm tls
+family of functions establishes a secure communications channel
+using the TLS socket protocol.
+Both clients and servers are supported.
+.Pp
+The
+.Fn tls_init
+function should be called once before any function is used.
+.Pp
+Before a connection is created, a configuration must be created.
+The
+.Fn tls_config_new
+function returns a new default configuration that can be used for future
+connections.
+Several functions exist to change the options of the configuration; see below.
+.Pp
+A
+.Em tls
+connection is represented as a
+.Em context .
+A new
+.Em context
+is created by either the
+.Fn tls_client
+or
+.Fn tls_server
+functions.
+The context can then be configured with the function
+.Fn tls_configure .
+The same
+.Em tls_config
+object can be used to configure multiple contexts.
+.Pp
+A client connection is initiated after configuration by calling
+.Fn tls_connect .
+This function will create a new socket, connect to the specified host and
+port, and then establish a secure connection.
+An already existing socket can be upgraded to a secure connection by calling
+.Fn tls_connect_socket .
+.Pp
+Two functions are provided for input and output,
+.Fn tls_read
+and
+.Fn tls_write .
+.Pp
+After use, a tls
+.Em context
+should be closed with
+.Fn tls_close ,
+and then freed by calling
+.Fn tls_free .
+When no more contexts are to be created, the
+.Em tls_config
+object should be freed by calling
+.Fn tls_config_free .
+.Sh FUNCTIONS
+The
+.Fn tls_init
+function initializes global data structures.
+It should be called once before any other functions.
+.Pp
+The following functions create and free configuration objects.
+.Bl -bullet -offset four
+.It
+.Fn tls_config_new
+allocates a new default configuration object.
+.It
+.Fn tls_config_free
+frees a configuration object.
+.El
+.Pp
+The following functions modify a configuration by setting parameters.
+Configuration options may apply to only clients or only servers or both.
+.Bl -bullet -offset four
+.It
+.Fn tls_config_set_ca_file
+sets the filename used to load a file
+containing the root certificates.
+.Em (Client)
+.It
+.Fn tls_config_set_ca_path
+sets the path (directory) which should be searched for root
+certificates.
+.Em (Client)
+.It
+.Fn tls_config_set_cert_file
+sets file from which the public certificate will be read.
+.Em (Client and server)
+.It
+.Fn tls_config_set_cert_mem
+sets the public certificate directly from memory.
+.Em (Client and server)
+.It
+.Fn tls_config_set_ciphers
+sets the list of ciphers that may be used.
+.Em (Client and server)
+.It
+.Fn tls_config_set_key_file
+sets the file from which the private key will be read.
+.Em (Server)
+.It
+.Fn tls_config_set_key_mem
+directly sets the private key from memory.
+.Em (Server)
+.It
+.Fn tls_config_set_protocols
+sets which versions of the protocol may be used.
+Possible values are the bitwise OR of:
+.Pp
+.Bl -tag -width "TLS_PROTOCOL_TLSv1_2" -offset indent -compact
+.It Dv TLS_PROTOCOL_TLSv1_0
+.It Dv TLS_PROTOCOL_TLSv1_1
+.It Dv TLS_PROTOCOL_TLSv1_2
+.El
+.Pp
+Additionally, the values
+.Dv TLS_PROTOCOL_TLSv1
+(all TLS versions) and
+.Dv TLS_PROTOCOLS_DEFAULT
+(currently all TLS versions) may be used.
+.Em (Client and server)
+.It
+.Fn tls_config_clear_keys
+clears any secret keys from memory.
+.Em (Server)
+.It
+.Fn tls_config_insecure_noverifyhost
+disables hostname verification.
+Be careful when using this option.
+.Em (Client)
+.It
+.Fn tls_config_insecure_noverifycert
+disables certificate verification.
+Be extremely careful when using this option.
+.Em (Client)
+.It
+.Fn tls_config_verify
+reenables hostname and certificate verification.
+.Em (Client)
+.El
+.Pp
+The following functions create, prepare, and free a connection context.
+.Bl -bullet -offset four
+.It
+.Fn tls_client
+creates a new tls context for client connections.
+.It
+.Fn tls_server
+creates a new tls context for server connections.
+.It
+.Fn tls_configure
+readies a tls context for use by applying the configuration
+options.
+.It
+.Fn tls_close
+closes a connection after use.
+.It
+.Fn tls_free
+frees a tls context after use.
+.El
+.Pp
+The following functions initiate a connection and perform input and output
+operations.
+.Bl -bullet -offset four
+.It
+.Fn tls_connect
+connects a client context to the server named by
+.Fa host .
+The
+.Fa port
+may be numeric or a service name.
+If it is NULL then a host of the format "hostname:port" is permitted.
+.It
+.Fn tls_connect_socket
+connects a client context to an already established socket connection.
+.It
+.Fn tls_read
+reads
+.Fa buflen
+bytes of data from the socket into
+.Fa buf .
+The amount of data read is returned in
+.Fa outlen .
+.It
+.Fn tls_write
+writes
+.Fa buflen
+bytes of data from
+.Fa buf
+to the socket.
+The amount of data written is returned in
+.Fa outlen .
+.El
+.Sh RETURN VALUES
+Functions that return
+.Vt int
+will return 0 on success and -1 on error.
+Functions that return a pointer will return NULL on error.
+.\" .Sh ERRORS
+.\" .Sh SEE ALSO
+.Sh HISTORY
+The
+.Nm tls
+API first appeared in
+.Ox 5.6
+as a response to the unnecessary challenges other APIs present in
+order to use them safely.
diff --git a/lib/libtls/tls_internal.h b/lib/libtls/tls_internal.h
new file mode 100644
index 00000000000..da696e228d3
--- /dev/null
+++ b/lib/libtls/tls_internal.h
@@ -0,0 +1,72 @@
+/* $OpenBSD: tls_internal.h,v 1.1 2014/10/31 13:46:17 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 <openssl/ssl.h>
+
+#define HTTPS_PORT "443"
+
+#define _PATH_SSL_CA_FILE "/etc/ssl/cert.pem"
+
+struct tls_config {
+ const char *ca_file;
+ const char *ca_path;
+ const char *cert_file;
+ char *cert_mem;
+ size_t cert_len;
+ const char *ciphers;
+ int ecdhcurve;
+ const char *key_file;
+ char *key_mem;
+ size_t key_len;
+ uint32_t protocols;
+ int verify_cert;
+ int verify_host;
+ int verify_depth;
+};
+
+#define TLS_CLIENT (1 << 0)
+#define TLS_SERVER (1 << 1)
+#define TLS_SERVER_CONN (1 << 2)
+
+struct tls {
+ struct tls_config *config;
+ uint64_t flags;
+
+ int err;
+ char *errmsg;
+
+ int socket;
+
+ SSL *ssl_conn;
+ SSL_CTX *ssl_ctx;
+};
+
+struct tls *tls_new(void);
+struct tls *tls_server_conn(struct tls *ctx);
+
+int tls_check_hostname(X509 *cert, const char *host);
+int tls_configure_keypair(struct tls *ctx);
+int tls_configure_server(struct tls *ctx);
+int tls_configure_ssl(struct tls *ctx);
+int tls_host_port(const char *hostport, char **host, char **port);
+int tls_set_error(struct tls *ctx, char *fmt, ...);
+
+#endif /* HEADER_TLS_INTERNAL_H */
diff --git a/lib/libtls/tls_server.c b/lib/libtls/tls_server.c
new file mode 100644
index 00000000000..001f19ded4d
--- /dev/null
+++ b/lib/libtls/tls_server.c
@@ -0,0 +1,134 @@
+/* $OpenBSD: tls_server.c,v 1.1 2014/10/31 13:46:17 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 <tls.h>
+#include "tls_internal.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;
+
+ 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;
+
+ if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
+ tls_set_error(ctx, "ssl context failure");
+ goto err;
+ }
+
+ if (tls_configure_ssl(ctx) != 0)
+ goto err;
+ if (tls_configure_keypair(ctx) != 0)
+ goto err;
+
+ if (ctx->config->ecdhcurve == -1) {
+ SSL_CTX_set_ecdh_auto(ctx->ssl_ctx, 1);
+ } else if (ctx->config->ecdhcurve != NID_undef) {
+ if ((ecdh_key = EC_KEY_new_by_curve_name(
+ ctx->config->ecdhcurve)) == NULL) {
+ tls_set_error(ctx, "failed to set ECDH 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);
+ }
+
+ return (0);
+
+err:
+ return (-1);
+}
+
+int
+tls_accept_socket(struct tls *ctx, struct tls **cctx, int socket)
+{
+ struct tls *conn_ctx = *cctx;
+ int ret, ssl_err;
+
+ if ((ctx->flags & TLS_SERVER) == 0) {
+ tls_set_error(ctx, "not a server context");
+ goto err;
+ }
+
+ if (conn_ctx == NULL) {
+ if ((conn_ctx = tls_server_conn(ctx)) == NULL) {
+ tls_set_error(ctx, "connection context failure");
+ goto err;
+ }
+ *cctx = conn_ctx;
+
+ conn_ctx->socket = socket;
+
+ if ((conn_ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) {
+ tls_set_error(ctx, "ssl failure");
+ goto err;
+ }
+
+ if (SSL_set_fd(conn_ctx->ssl_conn, socket) != 1) {
+ tls_set_error(ctx, "ssl set fd failure");
+ goto err;
+ }
+ SSL_set_app_data(conn_ctx->ssl_conn, conn_ctx);
+ }
+
+ if ((ret = SSL_accept(conn_ctx->ssl_conn)) != 1) {
+ ssl_err = SSL_get_error(conn_ctx->ssl_conn, ret);
+ switch (ssl_err) {
+ case SSL_ERROR_WANT_READ:
+ return (TLS_READ_AGAIN);
+ case SSL_ERROR_WANT_WRITE:
+ return (TLS_WRITE_AGAIN);
+ default:
+ tls_set_error(ctx, "ssl accept failure (%i)",
+ ssl_err);
+ goto err;
+ }
+ }
+
+ return (0);
+
+err:
+ return (-1);
+}
diff --git a/lib/libtls/tls_util.c b/lib/libtls/tls_util.c
new file mode 100644
index 00000000000..2adfb674b8f
--- /dev/null
+++ b/lib/libtls/tls_util.c
@@ -0,0 +1,81 @@
+/* $OpenBSD: tls_util.c,v 1.1 2014/10/31 13:46:17 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 <stdlib.h>
+
+#include "tls_internal.h"
+
+/*
+ * 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);
+}
diff --git a/lib/libtls/tls_verify.c b/lib/libtls/tls_verify.c
new file mode 100644
index 00000000000..fa0010922fc
--- /dev/null
+++ b/lib/libtls/tls_verify.c
@@ -0,0 +1,225 @@
+/* $OpenBSD: tls_verify.c,v 1.1 2014/10/31 13:46:17 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 <sys/socket.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <string.h>
+
+#include <openssl/x509v3.h>
+
+#include "tls_internal.h"
+
+int tls_match_hostname(const char *cert_hostname, const char *hostname);
+int tls_check_subject_altname(X509 *cert, const char *host);
+int tls_check_common_name(X509 *cert, const char *host);
+
+int
+tls_match_hostname(const char *cert_hostname, const char *hostname)
+{
+ const char *cert_domain, *domain, *next_dot;
+
+ if (strcasecmp(cert_hostname, hostname) == 0)
+ return 0;
+
+ /* Wildcard match? */
+ if (cert_hostname[0] == '*') {
+ /*
+ * Valid wildcards:
+ * - "*.domain.tld"
+ * - "*.sub.domain.tld"
+ * - etc.
+ * Reject "*.tld".
+ * No attempt to prevent the use of eg. "*.co.uk".
+ */
+ cert_domain = &cert_hostname[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(hostname, '.');
+
+ /* No wildcard match against a hostname with no domain part. */
+ if (domain == NULL || strlen(domain) == 1)
+ return -1;
+
+ if (strcasecmp(cert_domain, domain) == 0)
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+tls_check_subject_altname(X509 *cert, const char *host)
+{
+ STACK_OF(GENERAL_NAME) *altname_stack = NULL;
+ union { struct in_addr ip4; struct in6_addr ip6; } 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, host, &addrbuf) == 1) {
+ type = GEN_IPADD;
+ addrlen = 4;
+ } else if (inet_pton(AF_INET6, host, &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) {
+ unsigned char *data;
+ int format;
+
+ format = ASN1_STRING_type(altname->d.dNSName);
+ if (format == V_ASN1_IA5STRING) {
+ data = ASN1_STRING_data(altname->d.dNSName);
+
+ if (ASN1_STRING_length(altname->d.dNSName) !=
+ (int)strlen(data)) {
+ fprintf(stdout, "%s: NUL byte in "
+ "subjectAltName, probably a "
+ "malicious certificate.\n",
+ getprogname());
+ rv = -2;
+ break;
+ }
+
+ if (tls_match_hostname(data, host) == 0) {
+ rv = 0;
+ break;
+ }
+ } else
+ fprintf(stdout, "%s: unhandled subjectAltName "
+ "dNSName encoding (%d)\n", getprogname(),
+ format);
+
+ } 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 == addrlen &&
+ memcmp(data, &addrbuf, addrlen) == 0) {
+ rv = 0;
+ break;
+ }
+ }
+ }
+
+ sk_GENERAL_NAME_free(altname_stack);
+ return rv;
+}
+
+int
+tls_check_common_name(X509 *cert, const char *host)
+{
+ X509_NAME *name;
+ char *common_name = NULL;
+ int common_name_len;
+ int rv = -1;
+ union { struct in_addr ip4; struct in6_addr ip6; } addrbuf;
+
+ name = X509_get_subject_name(cert);
+ if (name == NULL)
+ goto out;
+
+ common_name_len = X509_NAME_get_text_by_NID(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(name, NID_commonName, common_name,
+ common_name_len + 1);
+
+ /* NUL bytes in CN? */
+ if (common_name_len != (int)strlen(common_name)) {
+ fprintf(stdout, "%s: NUL byte in Common Name field, "
+ "probably a malicious certificate.\n", getprogname());
+ rv = -2;
+ goto out;
+ }
+
+ if (inet_pton(AF_INET, host, &addrbuf) == 1 ||
+ inet_pton(AF_INET6, host, &addrbuf) == 1) {
+ /*
+ * We don't want to attempt wildcard matching against IP
+ * addresses, so perform a simple comparison here.
+ */
+ if (strcmp(common_name, host) == 0)
+ rv = 0;
+ else
+ rv = -1;
+ goto out;
+ }
+
+ if (tls_match_hostname(common_name, host) == 0)
+ rv = 0;
+out:
+ free(common_name);
+ return rv;
+}
+
+int
+tls_check_hostname(X509 *cert, const char *host)
+{
+ int rv;
+
+ rv = tls_check_subject_altname(cert, host);
+ if (rv == 0 || rv == -2)
+ return rv;
+
+ return tls_check_common_name(cert, host);
+}