From ad909e37b20a2c933e88e0e359b2fdb401d05092 Mon Sep 17 00:00:00 2001 From: bcook <> Date: Sun, 4 Sep 2016 12:26:43 +0000 Subject: Add callback-based interface to libtls. This allows working with buffers and callback functions instead of directly on sockets or file descriptors. Original patch from Tobias Pape . ok beck@ --- src/lib/libtls/Makefile | 3 +- src/lib/libtls/shlib_version | 2 +- src/lib/libtls/tls.c | 6 +- src/lib/libtls/tls.h | 11 ++- src/lib/libtls/tls_bio_cb.c | 224 ++++++++++++++++++++++++++++++++++++++++++ src/lib/libtls/tls_client.c | 76 ++++++++++---- src/lib/libtls/tls_init.3 | 14 ++- src/lib/libtls/tls_internal.h | 9 +- src/lib/libtls/tls_server.c | 60 +++++++++-- src/lib/libtls/tls_verify.c | 3 +- 10 files changed, 371 insertions(+), 37 deletions(-) create mode 100644 src/lib/libtls/tls_bio_cb.c (limited to 'src') diff --git a/src/lib/libtls/Makefile b/src/lib/libtls/Makefile index ca2f00bc8f..356c6f5b2e 100644 --- a/src/lib/libtls/Makefile +++ b/src/lib/libtls/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.23 2016/03/30 06:38:43 jmc Exp $ +# $OpenBSD: Makefile,v 1.24 2016/09/04 12:26:43 bcook Exp $ CFLAGS+= -Wall -Werror -Wimplicit CFLAGS+= -DLIBRESSL_INTERNAL @@ -13,6 +13,7 @@ LDADD+= -L${BSDOBJDIR}/lib/libssl/ssl -lssl HDRS= tls.h SRCS= tls.c \ + tls_bio_cb.c \ tls_client.c \ tls_config.c \ tls_conninfo.c \ diff --git a/src/lib/libtls/shlib_version b/src/lib/libtls/shlib_version index 5e6c3dd51b..f6b149e586 100644 --- a/src/lib/libtls/shlib_version +++ b/src/lib/libtls/shlib_version @@ -1,2 +1,2 @@ major=11 -minor=3 +minor=4 diff --git a/src/lib/libtls/tls.c b/src/lib/libtls/tls.c index 85faedd56d..00564edb3c 100644 --- a/src/lib/libtls/tls.c +++ b/src/lib/libtls/tls.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tls.c,v 1.48 2016/08/22 17:12:35 jsing Exp $ */ +/* $OpenBSD: tls.c,v 1.49 2016/09/04 12:26:43 bcook Exp $ */ /* * Copyright (c) 2014 Joel Sing * @@ -424,6 +424,10 @@ tls_reset(struct tls *ctx) tls_sni_ctx_free(sni); } ctx->sni_ctx = NULL; + + ctx->read_cb = NULL; + ctx->write_cb = NULL; + ctx->cb_arg = NULL; } int diff --git a/src/lib/libtls/tls.h b/src/lib/libtls/tls.h index 107614c759..e4dd31775a 100644 --- a/src/lib/libtls/tls.h +++ b/src/lib/libtls/tls.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tls.h,v 1.35 2016/08/22 14:58:26 jsing Exp $ */ +/* $OpenBSD: tls.h,v 1.36 2016/09/04 12:26:43 bcook Exp $ */ /* * Copyright (c) 2014 Joel Sing * @@ -44,6 +44,11 @@ extern "C" { struct tls; struct tls_config; +typedef ssize_t (*tls_read_cb)(void *_ctx, void *_buf, size_t _buflen, + void *_cb_arg); +typedef ssize_t (*tls_write_cb)(void *_ctx, const void *_buf, + size_t _buflen, void *_cb_arg); + int tls_init(void); const char *tls_config_error(struct tls_config *_config); @@ -102,12 +107,16 @@ 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_accept_cbs(struct tls *_ctx, struct tls **_cctx, + tls_read_cb _read_cb, tls_write_cb _write_cb, void *_cb_arg); 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_connect_cbs(struct tls *_ctx, tls_read_cb _read_cb, + tls_write_cb _write_cb, void *_cb_arg, 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); diff --git a/src/lib/libtls/tls_bio_cb.c b/src/lib/libtls/tls_bio_cb.c new file mode 100644 index 0000000000..fc1c964912 --- /dev/null +++ b/src/lib/libtls/tls_bio_cb.c @@ -0,0 +1,224 @@ +/* $ID$ */ +/* + * Copyright (c) 2016 Tobias Pape + * + * 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 +#include +#include + +#include "tls.h" +#include "tls_internal.h" + +#include + +static int write_cb(BIO *b, const char *buf, int num); +static int read_cb(BIO *b, char *buf, int size); +static int puts_cb(BIO *b, const char *str); +static long ctrl_cb(BIO *b, int cmd, long num, void *ptr); +static int new_cb(BIO *b); +static int free_cb(BIO *data); + +struct bio_cb_st { + int (*write_cb)(BIO *h, const char *buf, int num, void *cb_arg); + int (*read_cb)(BIO *h, char *buf, int size, void *cb_arg); + void *cb_arg; +}; + +static BIO_METHOD cb_method = { + .type = BIO_TYPE_MEM, + .name = "libtls_callbacks", + .bwrite = write_cb, + .bread = read_cb, + .bputs = puts_cb, + .ctrl = ctrl_cb, + .create = new_cb, + .destroy = free_cb +}; + +static BIO_METHOD * +bio_s_cb(void) +{ + return (&cb_method); +} + +static int +bio_set_write_cb(BIO *bi, + int (*write_cb)(BIO *h, const char *buf, int num, void *cb_arg)) +{ + struct bio_cb_st *b; + b = (struct bio_cb_st *)bi->ptr; + b->write_cb = write_cb; + return (0); +} + +static int +bio_set_read_cb(BIO *bi, + int (*read_cb)(BIO *h, char *buf, int size, void *cb_arg)) +{ + struct bio_cb_st *b; + b = (struct bio_cb_st *)bi->ptr; + b->read_cb = read_cb; + return (0); +} + +static int +bio_set_cb_arg(BIO *bi, void *cb_arg) +{ + struct bio_cb_st *b; + b = (struct bio_cb_st *)bi->ptr; + b->cb_arg = cb_arg; + return (0); +} + +static int +new_cb(BIO *bi) +{ + struct bio_cb_st *bcb; + + bcb = calloc(1, sizeof(struct bio_cb_st)); + if (bcb == NULL) + return (0); + + bi->shutdown = 1; + bi->init = 1; + bi->num = -1; + bi->ptr = (char *)bcb; + + return (1); +} + +static int +free_cb(BIO *bi) +{ + if (bi == NULL) + return (0); + + if (bi->shutdown) { + if ((bi->init) && (bi->ptr != NULL)) { + struct bio_cb_st *b; + b = (struct bio_cb_st *)bi->ptr; + free(b); + bi->ptr = NULL; + } + } + + return (1); +} + +static int +read_cb(BIO *b, char *buf, int size) +{ + struct bio_cb_st *bcb = b->ptr; + return (bcb->read_cb(b, buf, size, bcb->cb_arg)); +} + +static int +write_cb(BIO *b, const char *buf, int num) +{ + struct bio_cb_st *bcb = b->ptr; + return (bcb->write_cb(b, buf, num, bcb->cb_arg)); +} + +static int +puts_cb(BIO *b, const char *str) +{ + int n; + + n = strlen(str); + return (write_cb(b, str, n)); +} + +static long +ctrl_cb(BIO *b, int cmd, long num, void *ptr) +{ + long ret = 1; + + switch (cmd) { + case BIO_CTRL_GET_CLOSE: + ret = (long)b->shutdown; + break; + case BIO_CTRL_SET_CLOSE: + b->shutdown = (int)num; + break; + case BIO_CTRL_DUP: + break; + case BIO_CTRL_INFO: + case BIO_CTRL_GET: + case BIO_CTRL_SET: + default: + ret = BIO_ctrl(b->next_bio, cmd, num, ptr); + } + + return (ret); +} + +static int +tls_bio_write_cb(BIO *h, const char *buf, int num, void *cb_arg) +{ + struct tls *ctx = cb_arg; + return (ctx->write_cb)(ctx, buf, num, ctx->cb_arg); +} + +static int +tls_bio_read_cb(BIO *h, char *buf, int size, void *cb_arg) +{ + struct tls *ctx = cb_arg; + return (ctx->read_cb)(ctx, buf, size, ctx->cb_arg); +} + +static BIO * +tls_get_new_cb_bio(struct tls *ctx) +{ + BIO *bcb; + if (ctx->read_cb == NULL || ctx->write_cb == NULL) + tls_set_errorx(ctx, "no callbacks registered"); + + bcb = BIO_new(bio_s_cb()); + if (bcb == NULL) { + tls_set_errorx(ctx, "failed to create callback i/o"); + return (NULL); + } + + bio_set_write_cb(bcb, tls_bio_write_cb); + bio_set_read_cb(bcb, tls_bio_read_cb); + bio_set_cb_arg(bcb, ctx); + + return (bcb); +} + +int +tls_set_cbs(struct tls *ctx, tls_read_cb read_cb, tls_write_cb write_cb, + void *cb_arg) +{ + int rv = -1; + BIO *bcb; + ctx->read_cb = read_cb; + ctx->write_cb = write_cb; + ctx->cb_arg = cb_arg; + + bcb = tls_get_new_cb_bio(ctx); + if (bcb == NULL) { + tls_set_errorx(ctx, "failed to create callback i/o"); + goto err; + } + + SSL_set_bio(ctx->ssl_conn, bcb, bcb); + + rv = 0; + + err: + return (rv); +} diff --git a/src/lib/libtls/tls_client.c b/src/lib/libtls/tls_client.c index c360ecad52..f8d35a18c1 100644 --- a/src/lib/libtls/tls_client.c +++ b/src/lib/libtls/tls_client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tls_client.c,v 1.34 2016/08/15 14:04:23 jsing Exp $ */ +/* $OpenBSD: tls_client.c,v 1.35 2016/09/04 12:26:43 bcook Exp $ */ /* * Copyright (c) 2014 Joel Sing * @@ -158,15 +158,8 @@ tls_connect_servername(struct tls *ctx, const char *host, const char *port, 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) +static int +connect_common(struct tls *ctx, const char *servername) { union tls_addr addrbuf; int rv = -1; @@ -176,11 +169,6 @@ tls_connect_fds(struct tls *ctx, int fd_read, int fd_write, 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"); @@ -195,6 +183,7 @@ tls_connect_fds(struct tls *ctx, int fd_read, int fd_write, if (tls_configure_ssl(ctx, ctx->ssl_ctx) != 0) goto err; + if (tls_configure_ssl_keypair(ctx, ctx->ssl_ctx, ctx->config->keypair, 0) != 0) goto err; @@ -205,6 +194,7 @@ tls_connect_fds(struct tls *ctx, int fd_read, int fd_write, goto err; } } + if (ctx->config->verify_cert && (tls_configure_ssl_verify(ctx, ctx->ssl_ctx, SSL_VERIFY_PEER) == -1)) @@ -214,15 +204,11 @@ tls_connect_fds(struct tls *ctx, int fd_read, int fd_write, 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; - } /* * RFC4366 (SNI): Literal IPv4 and IPv6 addresses are not @@ -236,6 +222,56 @@ tls_connect_fds(struct tls *ctx, int fd_read, int fd_write, goto err; } } + rv = 0; + + err: + return (rv); +} + +int +tls_connect_socket(struct tls *ctx, int s, const char *servername) +{ + return tls_connect_fds(ctx, s, s, servername); +} + +int +tls_connect_fds(struct tls *ctx, int fd_read, int fd_write, + const char *servername) +{ + int rv = -1; + + if (fd_read < 0 || fd_write < 0) { + tls_set_errorx(ctx, "invalid file descriptors"); + goto err; + } + + if (connect_common(ctx, servername) != 0) + goto err; + + if (SSL_set_rfd(ctx->ssl_conn, fd_read) != 1 || + SSL_set_wfd(ctx->ssl_conn, fd_write) != 1) { + tls_set_errorx(ctx, "ssl file descriptor failure"); + goto err; + } + + rv = 0; + err: + return (rv); +} + +int +tls_connect_cbs(struct tls *ctx, tls_read_cb read_cb, + tls_write_cb write_cb, void *cb_arg, const char *servername) +{ + int rv = -1; + + if (connect_common(ctx, servername) != 0) + goto err; + + if (tls_set_cbs(ctx, read_cb, write_cb, cb_arg) != 0) { + tls_set_errorx(ctx, "callback registration failure"); + goto err; + } rv = 0; diff --git a/src/lib/libtls/tls_init.3 b/src/lib/libtls/tls_init.3 index f9a8ac1f80..762a76633b 100644 --- a/src/lib/libtls/tls_init.3 +++ b/src/lib/libtls/tls_init.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: tls_init.3,v 1.68 2016/09/04 12:03:24 jsing Exp $ +.\" $OpenBSD: tls_init.3,v 1.69 2016/09/04 12:26:43 bcook Exp $ .\" .\" Copyright (c) 2014 Ted Unangst .\" @@ -71,8 +71,10 @@ .Nm tls_connect_fds , .Nm tls_connect_servername , .Nm tls_connect_socket , +.Nm tls_connect_cbs , .Nm tls_accept_fds , .Nm tls_accept_socket , +.Nm tls_accept_cbs , .Nm tls_handshake , .Nm tls_read , .Nm tls_write , @@ -187,10 +189,14 @@ .Ft "int" .Fn tls_connect_socket "struct tls *ctx" "int s" "const char *servername" .Ft "int" +.Fn tls_connect_cbs "struct tls *ctx" "ssize_t (*tls_read_cb)(void *ctx, void *buf, size_t buflen, void *cb_arg)" "ssize_t (*tls_write_cb)(void *ctx, const void *buf, size_t buflen, void *cb_arg)" "void *cb_arg" "const char *servername" +.Ft "int" .Fn tls_accept_fds "struct tls *tls" "struct tls **cctx" "int fd_read" "int fd_write" .Ft "int" .Fn tls_accept_socket "struct tls *tls" "struct tls **cctx" "int socket" .Ft "int" +.Fn tls_accept_cbs "struct tls *ctx" "struct tls **cctx" "ssize_t (*tls_read_cb)(void *ctx, void *buf, size_t buflen, void *cb_arg)" "ssize_t (*tls_write_cb)(void *ctx, const void *buf, size_t buflen, void *cb_arg)" "void *cb_arg" +.Ft "int" .Fn tls_handshake "struct tls *ctx" .Ft "ssize_t" .Fn tls_read "struct tls *ctx" "void *buf" "size_t buflen" @@ -247,6 +253,9 @@ An already existing socket can be upgraded to a secure connection by calling Alternatively, a secure connection can be established over a pair of existing file descriptors by calling .Fn tls_connect_fds . +Calling +.Fn tls_connect_cbs +allows specifying read and write callback functions to handle data transfer. The specified cb_arg parameter is passed back to the functions, and can contain a pointer to any caller-specified data. .Pp A server can accept a new client connection by calling .Fn tls_accept_socket @@ -254,6 +263,9 @@ on an already established socket connection. Alternatively, a new client connection can be accepted over a pair of existing file descriptors by calling .Fn tls_accept_fds . +Calling +.Fn tls_accept_cbs +allows specifying read and write callback functions to handle data transfer. The specified cb_arg parameter is passed back to the functions, and can contain a pointer to any caller-specified data. .Pp The TLS handshake can be completed by calling .Fn tls_handshake . diff --git a/src/lib/libtls/tls_internal.h b/src/lib/libtls/tls_internal.h index c7bf50af83..c272038055 100644 --- a/src/lib/libtls/tls_internal.h +++ b/src/lib/libtls/tls_internal.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tls_internal.h,v 1.42 2016/08/22 17:12:35 jsing Exp $ */ +/* $OpenBSD: tls_internal.h,v 1.43 2016/09/04 12:26:43 bcook Exp $ */ /* * Copyright (c) 2014 Jeremie Courreges-Anglas * Copyright (c) 2014 Joel Sing @@ -117,6 +117,10 @@ struct tls { X509 *ssl_peer_cert; struct tls_conninfo *conninfo; + + tls_read_cb read_cb; + tls_write_cb write_cb; + void *cb_arg; }; struct tls_sni_ctx *tls_sni_ctx_new(void); @@ -140,6 +144,9 @@ int tls_config_load_file(struct tls_error *error, const char *filetype, const char *filename, char **buf, size_t *len); int tls_host_port(const char *hostport, char **host, char **port); +int tls_set_cbs(struct tls *ctx, + tls_read_cb read_cb, tls_write_cb write_cb, void *cb_arg); + int tls_error_set(struct tls_error *error, const char *fmt, ...) __attribute__((__format__ (printf, 2, 3))) __attribute__((__nonnull__ (2))); diff --git a/src/lib/libtls/tls_server.c b/src/lib/libtls/tls_server.c index 044678c705..01f9ed3b7f 100644 --- a/src/lib/libtls/tls_server.c +++ b/src/lib/libtls/tls_server.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tls_server.c,v 1.25 2016/08/22 14:51:37 jsing Exp $ */ +/* $OpenBSD: tls_server.c,v 1.26 2016/09/04 12:26:43 bcook Exp $ */ /* * Copyright (c) 2014 Joel Sing * @@ -279,14 +279,8 @@ tls_configure_server(struct tls *ctx) 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) +static struct tls * +accept_common(struct tls *ctx) { struct tls *conn_ctx = NULL; @@ -304,10 +298,34 @@ tls_accept_fds(struct tls *ctx, struct tls **cctx, int fd_read, int fd_write) tls_set_errorx(ctx, "ssl failure"); goto err; } + if (SSL_set_app_data(conn_ctx->ssl_conn, conn_ctx) != 1) { tls_set_errorx(ctx, "ssl application data failure"); goto err; } + + return conn_ctx; + + err: + tls_free(conn_ctx); + + return (NULL); +} + +int +tls_accept_socket(struct tls *ctx, struct tls **cctx, int 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; + + if ((conn_ctx = accept_common(ctx)) == NULL) + goto err; + if (SSL_set_rfd(conn_ctx->ssl_conn, fd_read) != 1 || SSL_set_wfd(conn_ctx->ssl_conn, fd_write) != 1) { tls_set_errorx(ctx, "ssl file descriptor failure"); @@ -317,10 +335,32 @@ tls_accept_fds(struct tls *ctx, struct tls **cctx, int fd_read, int fd_write) *cctx = conn_ctx; return (0); - err: tls_free(conn_ctx); + *cctx = NULL; + + return (-1); +} + +int +tls_accept_cbs(struct tls *ctx, struct tls **cctx, + tls_read_cb read_cb, tls_write_cb write_cb, void *cb_arg) +{ + struct tls *conn_ctx; + + if ((conn_ctx = accept_common(ctx)) == NULL) + goto err; + + if (tls_set_cbs(ctx, read_cb, write_cb, cb_arg) != 0) { + tls_set_errorx(ctx, "callback registration failure"); + goto err; + } + + *cctx = conn_ctx; + return (0); + err: + tls_free(conn_ctx); *cctx = NULL; return (-1); diff --git a/src/lib/libtls/tls_verify.c b/src/lib/libtls/tls_verify.c index 9e73750a6e..e1073e863a 100644 --- a/src/lib/libtls/tls_verify.c +++ b/src/lib/libtls/tls_verify.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tls_verify.c,v 1.16 2016/08/02 07:47:11 jsing Exp $ */ +/* $OpenBSD: tls_verify.c,v 1.17 2016/09/04 12:26:43 bcook Exp $ */ /* * Copyright (c) 2014 Jeremie Courreges-Anglas * @@ -24,6 +24,7 @@ #include +#include #include "tls_internal.h" static int tls_match_name(const char *cert_name, const char *name); -- cgit v1.2.3-55-g6feb