summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJoel Sing <jsing@cvs.openbsd.org>2018-02-10 04:41:25 +0000
committerJoel Sing <jsing@cvs.openbsd.org>2018-02-10 04:41:25 +0000
commit995ed69762577d52380bf729ab06c8822257eb18 (patch)
treeced0f54d178cfb5810c63ef6afc6684ad6a26e98 /lib
parentc362bb3830fe2aeec3e45bae3d555b7f46132851 (diff)
Add support to libtls for client-side TLS session resumption.
A libtls client can specify a session file descriptor (a regular file with appropriate ownership and permissions) and libtls will manage reading and writing of session data across TLS handshakes. Discussed at length with deraadt@ and tedu@. Rides previous minor bump. ok beck@
Diffstat (limited to 'lib')
-rw-r--r--lib/libtls/Symbols.list2
-rw-r--r--lib/libtls/tls.h4
-rw-r--r--lib/libtls/tls_client.c128
-rw-r--r--lib/libtls/tls_config.c41
-rw-r--r--lib/libtls/tls_conninfo.c21
-rw-r--r--lib/libtls/tls_internal.h4
6 files changed, 195 insertions, 5 deletions
diff --git a/lib/libtls/Symbols.list b/lib/libtls/Symbols.list
index 1e7538cfd43..923924fc400 100644
--- a/lib/libtls/Symbols.list
+++ b/lib/libtls/Symbols.list
@@ -42,6 +42,7 @@ tls_config_set_ocsp_staple_file
tls_config_set_protocols
tls_config_set_session_id
tls_config_set_session_lifetime
+tls_config_set_session_fd
tls_config_set_verify_depth
tls_config_skip_private_key_check
tls_config_verify
@@ -51,6 +52,7 @@ tls_configure
tls_conn_alpn_selected
tls_conn_cipher
tls_conn_servername
+tls_conn_session_resumed
tls_conn_version
tls_connect
tls_connect_cbs
diff --git a/lib/libtls/tls.h b/lib/libtls/tls.h
index cc8627f2af2..8d66c2fbaad 100644
--- a/lib/libtls/tls.h
+++ b/lib/libtls/tls.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls.h,v 1.51 2017/08/10 18:18:30 jsing Exp $ */
+/* $OpenBSD: tls.h,v 1.52 2018/02/10 04:41:24 jsing Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
*
@@ -128,6 +128,7 @@ int tls_config_set_ocsp_staple_mem(struct tls_config *_config,
int tls_config_set_ocsp_staple_file(struct tls_config *_config,
const char *_staple_file);
int tls_config_set_protocols(struct tls_config *_config, uint32_t _protocols);
+int tls_config_set_session_fd(struct tls_config *_config, int _session_fd);
int tls_config_set_verify_depth(struct tls_config *_config, int _verify_depth);
void tls_config_prefer_ciphers_client(struct tls_config *_config);
@@ -188,6 +189,7 @@ const uint8_t *tls_peer_cert_chain_pem(struct tls *_ctx, size_t *_len);
const char *tls_conn_alpn_selected(struct tls *_ctx);
const char *tls_conn_cipher(struct tls *_ctx);
const char *tls_conn_servername(struct tls *_ctx);
+int tls_conn_session_resumed(struct tls *_ctx);
const char *tls_conn_version(struct tls *_ctx);
uint8_t *tls_load_file(const char *_file, size_t *_len, char *_password);
diff --git a/lib/libtls/tls_client.c b/lib/libtls/tls_client.c
index c79f462a3a5..14c716fa171 100644
--- a/lib/libtls/tls_client.c
+++ b/lib/libtls/tls_client.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls_client.c,v 1.43 2017/08/10 18:18:30 jsing Exp $ */
+/* $OpenBSD: tls_client.c,v 1.44 2018/02/10 04:41:24 jsing Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
*
@@ -17,10 +17,12 @@
#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/stat.h>
#include <arpa/inet.h>
#include <netinet/in.h>
+#include <limits.h>
#include <netdb.h>
#include <stdlib.h>
#include <unistd.h>
@@ -159,6 +161,118 @@ tls_connect_servername(struct tls *ctx, const char *host, const char *port,
}
static int
+tls_client_read_session(struct tls *ctx)
+{
+ int sfd = ctx->config->session_fd;
+ uint8_t *session = NULL;
+ size_t session_len = 0;
+ SSL_SESSION *ss = NULL;
+ BIO *bio = NULL;
+ struct stat sb;
+ ssize_t n;
+ int rv = -1;
+
+ if (fstat(sfd, &sb) == -1) {
+ tls_set_error(ctx, "failed to stat session file");
+ goto err;
+ }
+ if (sb.st_size < 0 || sb.st_size > INT_MAX) {
+ tls_set_errorx(ctx, "invalid session file size");
+ goto err;
+ }
+ session_len = (size_t)sb.st_size;
+
+ /* A zero size file means that we do not yet have a valid session. */
+ if (session_len == 0)
+ goto done;
+
+ if ((session = malloc(session_len)) == NULL)
+ goto err;
+
+ n = pread(sfd, session, session_len, 0);
+ if (n < 0 || (size_t)n != session_len) {
+ tls_set_error(ctx, "failed to read session file");
+ goto err;
+ }
+ if ((bio = BIO_new_mem_buf(session, session_len)) == NULL)
+ goto err;
+ if ((ss = PEM_read_bio_SSL_SESSION(bio, NULL, tls_password_cb,
+ NULL)) == NULL) {
+ tls_set_errorx(ctx, "failed to parse session");
+ goto err;
+ }
+
+ if (SSL_set_session(ctx->ssl_conn, ss) != 1) {
+ tls_set_errorx(ctx, "failed to set session");
+ goto err;
+ }
+
+ done:
+ rv = 0;
+
+ err:
+ freezero(session, session_len);
+ SSL_SESSION_free(ss);
+ BIO_free(bio);
+
+ return rv;
+}
+
+static int
+tls_client_write_session(struct tls *ctx)
+{
+ int sfd = ctx->config->session_fd;
+ SSL_SESSION *ss = NULL;
+ BIO *bio = NULL;
+ long data_len;
+ char *data;
+ off_t offset;
+ size_t len;
+ ssize_t n;
+ int rv = -1;
+
+ if ((ss = SSL_get1_session(ctx->ssl_conn)) == NULL) {
+ if (ftruncate(sfd, 0) == -1) {
+ tls_set_error(ctx, "failed to truncate session file");
+ goto err;
+ }
+ goto done;
+ }
+
+ if ((bio = BIO_new(BIO_s_mem())) == NULL)
+ goto err;
+ if (PEM_write_bio_SSL_SESSION(bio, ss) == 0)
+ goto err;
+ if ((data_len = BIO_get_mem_data(bio, &data)) <= 0)
+ goto err;
+
+ len = (size_t)data_len;
+ offset = 0;
+
+ if (ftruncate(sfd, len) == -1) {
+ tls_set_error(ctx, "failed to truncate session file");
+ goto err;
+ }
+ while (len > 0) {
+ if ((n = pwrite(sfd, data + offset, len, offset)) == -1) {
+ tls_set_error(ctx, "failed to write session file");
+ goto err;
+ }
+ offset += n;
+ len -= n;
+ }
+
+ done:
+ rv = 0;
+
+ err:
+ SSL_SESSION_free(ss);
+ BIO_free_all(bio);
+
+ return (rv);
+}
+
+static int
tls_connect_common(struct tls *ctx, const char *servername)
{
union tls_addr addrbuf;
@@ -221,6 +335,12 @@ tls_connect_common(struct tls *ctx, const char *servername)
goto err;
}
+ if (ctx->config->session_fd != -1) {
+ SSL_clear_options(ctx->ssl_conn, SSL_OP_NO_TICKET);
+ if (tls_client_read_session(ctx) == -1)
+ goto err;
+ }
+
if (SSL_set_tlsext_status_type(ctx->ssl_conn, TLSEXT_STATUSTYPE_ocsp) != 1) {
tls_set_errorx(ctx, "ssl OCSP extension setup failure");
goto err;
@@ -336,6 +456,12 @@ tls_handshake_client(struct tls *ctx)
}
ctx->state |= TLS_HANDSHAKE_COMPLETE;
+
+ if (ctx->config->session_fd != -1) {
+ if (tls_client_write_session(ctx) == -1)
+ goto err;
+ }
+
rv = 0;
err:
diff --git a/lib/libtls/tls_config.c b/lib/libtls/tls_config.c
index 3db75dc62fc..6dfebfaebf9 100644
--- a/lib/libtls/tls_config.c
+++ b/lib/libtls/tls_config.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls_config.c,v 1.47 2018/02/08 05:56:49 jsing Exp $ */
+/* $OpenBSD: tls_config.c,v 1.48 2018/02/10 04:41:24 jsing Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
*
@@ -89,6 +89,7 @@ tls_config_new(void)
goto err;
config->refcount = 1;
+ config->session_fd = -1;
/*
* Default configuration.
@@ -670,6 +671,44 @@ tls_config_set_protocols(struct tls_config *config, uint32_t protocols)
}
int
+tls_config_set_session_fd(struct tls_config *config, int session_fd)
+{
+ struct stat sb;
+ mode_t mugo;
+
+ if (session_fd == -1) {
+ config->session_fd = session_fd;
+ return (0);
+ }
+
+ if (fstat(session_fd, &sb) == -1) {
+ tls_config_set_error(config, "failed to stat session file");
+ return (-1);
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ tls_config_set_errorx(config,
+ "session file is not a regular file");
+ return (-1);
+ }
+
+ if (sb.st_uid != getuid()) {
+ tls_config_set_errorx(config, "session file has incorrect "
+ "owner (uid %i != %i)", sb.st_uid, getuid());
+ return (-1);
+ }
+ mugo = sb.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
+ if (mugo != (S_IRUSR|S_IWUSR)) {
+ tls_config_set_errorx(config, "session file has incorrect "
+ "permissions (%o != 600)", mugo);
+ return (-1);
+ }
+
+ config->session_fd = session_fd;
+
+ return (0);
+}
+
+int
tls_config_set_verify_depth(struct tls_config *config, int verify_depth)
{
config->verify_depth = verify_depth;
diff --git a/lib/libtls/tls_conninfo.c b/lib/libtls/tls_conninfo.c
index 685ed194e4e..34535b5668c 100644
--- a/lib/libtls/tls_conninfo.c
+++ b/lib/libtls/tls_conninfo.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls_conninfo.c,v 1.17 2018/02/08 10:02:48 jsing Exp $ */
+/* $OpenBSD: tls_conninfo.c,v 1.18 2018/02/10 04:41:24 jsing Exp $ */
/*
* Copyright (c) 2015 Joel Sing <jsing@openbsd.org>
* Copyright (c) 2015 Bob Beck <beck@openbsd.org>
@@ -221,6 +221,14 @@ tls_conninfo_cert_pem(struct tls *ctx)
return rv;
}
+static int
+tls_conninfo_session(struct tls *ctx)
+{
+ ctx->conninfo->session_resumed = SSL_session_reused(ctx->ssl_conn);
+
+ return 0;
+}
+
int
tls_conninfo_populate(struct tls *ctx)
{
@@ -260,6 +268,9 @@ tls_conninfo_populate(struct tls *ctx)
if (tls_conninfo_cert_pem(ctx) == -1)
goto err;
+ if (tls_conninfo_session(ctx) == -1)
+ goto err;
+
return (0);
err:
@@ -313,6 +324,14 @@ tls_conn_servername(struct tls *ctx)
return (ctx->conninfo->servername);
}
+int
+tls_conn_session_resumed(struct tls *ctx)
+{
+ if (ctx->conninfo == NULL)
+ return (0);
+ return (ctx->conninfo->session_resumed);
+}
+
const char *
tls_conn_version(struct tls *ctx)
{
diff --git a/lib/libtls/tls_internal.h b/lib/libtls/tls_internal.h
index eb08d470740..14265037eb5 100644
--- a/lib/libtls/tls_internal.h
+++ b/lib/libtls/tls_internal.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls_internal.h,v 1.68 2018/02/08 10:19:31 jsing Exp $ */
+/* $OpenBSD: tls_internal.h,v 1.69 2018/02/10 04:41:24 jsing Exp $ */
/*
* Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org>
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
@@ -95,6 +95,7 @@ struct tls_config {
int ocsp_require_stapling;
uint32_t protocols;
unsigned char session_id[TLS_MAX_SESSION_ID_LENGTH];
+ int session_fd;
int session_lifetime;
struct tls_ticket_key ticket_keys[TLS_NUM_TICKETS];
uint32_t ticket_keyrev;
@@ -111,6 +112,7 @@ struct tls_conninfo {
char *alpn;
char *cipher;
char *servername;
+ int session_resumed;
char *version;
char *hash;