On Sun, Sep 04, 2016 at 05:57:54AM -0400, Ted Unangst wrote: > Brent Cook wrote: > > @@ -246,14 +252,18 @@ An already existing socket can be upgrad > > .Fn tls_connect_socket . > > Alternatively, a secure connection can be established over a pair of > > existing > > file descriptors by calling > > -.Fn tls_connect_fds . > > +.Fn tls_connect_fds . Using > > +.Fn tls_connect_cbs , read and write callbacks can be specified to handle > > the > > +actual data transfer. > > I think we need just a wee bit more documentation. payload is not the clearest > name. It sounds like connection data. I think cookie? Or cbarg? Is it > necessary to pass the tls context to the callback? I think that's unusual. > > read callback should be more like: > > ssize_t (*read_cb)(void *buf, size_t buflen, void *cbarg);
Agreed, I was also a bit unclear on payload at first (though it grew on me over time, so I didn't change it). Here's an update with the parameter renamed and better documented. ok? Index: Makefile =================================================================== RCS file: /cvs/src/lib/libtls/Makefile,v retrieving revision 1.23 diff -u -p -u -p -r1.23 Makefile --- Makefile 30 Mar 2016 06:38:43 -0000 1.23 +++ Makefile 4 Sep 2016 10:23:42 -0000 @@ -13,6 +13,7 @@ LDADD+= -L${BSDOBJDIR}/lib/libssl/ssl -l HDRS= tls.h SRCS= tls.c \ + tls_bio_cb.c \ tls_client.c \ tls_config.c \ tls_conninfo.c \ Index: shlib_version =================================================================== RCS file: /cvs/src/lib/libtls/shlib_version,v retrieving revision 1.20 diff -u -p -u -p -r1.20 shlib_version --- shlib_version 31 Aug 2016 23:05:30 -0000 1.20 +++ shlib_version 4 Sep 2016 10:23:42 -0000 @@ -1,2 +1,2 @@ major=11 -minor=3 +minor=4 Index: tls.c =================================================================== RCS file: /cvs/src/lib/libtls/tls.c,v retrieving revision 1.48 diff -u -p -u -p -r1.48 tls.c --- tls.c 22 Aug 2016 17:12:35 -0000 1.48 +++ tls.c 4 Sep 2016 10:23:42 -0000 @@ -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 Index: tls.h =================================================================== RCS file: /cvs/src/lib/libtls/tls.h,v retrieving revision 1.35 diff -u -p -u -p -r1.35 tls.h --- tls.h 22 Aug 2016 14:58:26 -0000 1.35 +++ tls.h 4 Sep 2016 10:23:42 -0000 @@ -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); Index: tls_bio_cb.c =================================================================== RCS file: tls_bio_cb.c diff -N tls_bio_cb.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tls_bio_cb.c 4 Sep 2016 10:23:42 -0000 @@ -0,0 +1,224 @@ +/* $ID$ */ +/* + * Copyright (c) 2016 Tobias Pape <tob...@netshed.de> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> + +#include "tls.h" +#include "tls_internal.h" + +#include <openssl/bio.h> + +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); +} Index: tls_client.c =================================================================== RCS file: /cvs/src/lib/libtls/tls_client.c,v retrieving revision 1.34 diff -u -p -u -p -r1.34 tls_client.c --- tls_client.c 15 Aug 2016 14:04:23 -0000 1.34 +++ tls_client.c 4 Sep 2016 10:23:42 -0000 @@ -158,15 +158,8 @@ tls_connect_servername(struct tls *ctx, 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_ 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_ 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_ 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_ 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 @@ -235,6 +221,56 @@ tls_connect_fds(struct tls *ctx, int fd_ tls_set_errorx(ctx, "server name indication failure"); 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; Index: tls_init.3 =================================================================== RCS file: /cvs/src/lib/libtls/tls_init.3,v retrieving revision 1.67 diff -u -p -u -p -r1.67 tls_init.3 --- tls_init.3 22 Aug 2016 14:55:59 -0000 1.67 +++ tls_init.3 4 Sep 2016 10:23:43 -0000 @@ -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 upgrad 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 connect 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 . Index: tls_internal.h =================================================================== RCS file: /cvs/src/lib/libtls/tls_internal.h,v retrieving revision 1.42 diff -u -p -u -p -r1.42 tls_internal.h --- tls_internal.h 22 Aug 2016 17:12:35 -0000 1.42 +++ tls_internal.h 4 Sep 2016 10:23:43 -0000 @@ -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); @@ -139,6 +143,9 @@ int tls_handshake_server(struct tls *ctx int tls_config_load_file(struct tls_error *error, const char *filetype, const char *filename, char **buf, size_t *len); int tls_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))) Index: tls_server.c =================================================================== RCS file: /cvs/src/lib/libtls/tls_server.c,v retrieving revision 1.25 diff -u -p -u -p -r1.25 tls_server.c --- tls_server.c 22 Aug 2016 14:51:37 -0000 1.25 +++ tls_server.c 4 Sep 2016 10:23:43 -0000 @@ -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 t 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 t *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); Index: tls_verify.c =================================================================== RCS file: /cvs/src/lib/libtls/tls_verify.c,v retrieving revision 1.16 diff -u -p -u -p -r1.16 tls_verify.c --- tls_verify.c 2 Aug 2016 07:47:11 -0000 1.16 +++ tls_verify.c 4 Sep 2016 10:23:43 -0000 @@ -24,6 +24,7 @@ #include <openssl/x509v3.h> +#include <tls.h> #include "tls_internal.h" static int tls_match_name(const char *cert_name, const char *name);