Excellent. Im currently travelling but I think you will be hearing from Joel
Aside from any minor changes i will say i basically like your diff, we may need to wait for after OpenBSD 6.0 to put it in (few weeks) as we are close to release and api changes now can hurt ports but thank you very much for posting this! On Sunday, 17 July 2016, Tobias Pape <[email protected]> wrote: > Hi all, > > I'm Tobias and fond of using libtls. > I have a certain use case, where I want to do TLS/SSL but > can only work with buffers/callbacks and not sockets or FDs. > In p(l)ain openssl, this is doable, but not nice. Libtls > does not yet have such a facility. > > I did a patch (or Pull-Request in GitHub parlance) against > portable on github, it would be great if it were considered. > Then I could migrate the SSL facilities of the Squeak > programming system from openssl to libtls. > > Best regards > -Tobias > > diff --git src/lib/libtls/Makefile src/lib/libtls/Makefile > index f51f2cd..f4252a6 100644 > --- src/lib/libtls/Makefile > +++ src/lib/libtls/Makefile > @@ -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 src/lib/libtls/tls.c src/lib/libtls/tls.c > index 22e8c87..7417e8b 100644 > --- src/lib/libtls/tls.c > +++ src/lib/libtls/tls.c > @@ -393,6 +393,10 @@ tls_reset(struct tls *ctx) > tls_free_conninfo(ctx->conninfo); > free(ctx->conninfo); > ctx->conninfo = NULL; > + > + ctx->cb_read = NULL; > + ctx->cb_write = NULL; > + ctx->cb_payload = NULL; > } > > int > @@ -580,3 +584,58 @@ tls_close(struct tls *ctx) > errno = 0; > return (rv); > } > + > +static int > +tls_bio_cb_write(BIO *h, const char *buf, int num, void *payload) > +{ > + int ret = 0; > + struct tls *ctx = (struct tls *)payload; > + ret = (ctx->cb_write)(ctx, (const void*)buf, (size_t)num, > ctx->cb_payload); > + return (ret); > +} > + > +static int > +tls_bio_cb_read(BIO *h, char *buf, int size, void *payload) > +{ > + int ret = 0; > + struct tls *ctx = (struct tls *)payload; > + ret = (ctx->cb_read)(ctx, (void*)buf, (size_t)size, > ctx->cb_payload); > + return (ret); > +} > + > +static BIO * > +tls_get_new_cb_bio(struct tls *ctx) > +{ > + BIO *bcb = NULL; > + if (ctx->cb_read == NULL || ctx->cb_write == 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_cb_write(bcb, tls_bio_cb_write); > + BIO_set_cb_read(bcb, tls_bio_cb_read); > + BIO_set_cb_payload(bcb, ctx); > + return (bcb); > +} > + > +int > +tls_set_cbs(struct tls *ctx, tls_read_cb cb_read, tls_write_cb cb_write, > + void *cb_payload) > +{ > + int rv = -1; > + BIO *bcb; > + ctx->cb_read = cb_read; > + ctx->cb_write = cb_write; > + ctx->cb_payload = cb_payload; > + 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 src/lib/libtls/tls.h src/lib/libtls/tls.h > index 3e75eb7..f236245 100644 > --- src/lib/libtls/tls.h > +++ src/lib/libtls/tls.h > @@ -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 *_payload); > +typedef ssize_t (*tls_write_cb)(void *_ctx, const void *_buf, > + size_t _buflen, void *_payload); > + > int tls_init(void); > > const char *tls_config_error(struct tls_config *_config); > @@ -96,12 +101,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 _cb_read, tls_write_cb _cb_write, void *_cb_payload); > 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 _cb_read, tls_write_cb > _cb_write, > + void *_cb_payload, 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 src/lib/libtls/tls_bio_cb.c src/lib/libtls/tls_bio_cb.c > new file mode 100644 > index 0000000..f8b24b0 > --- /dev/null > +++ src/lib/libtls/tls_bio_cb.c > @@ -0,0 +1,182 @@ > +/* $ID$ */ > +/* > + * Copyright (c) 2016 Tobias Pape <[email protected] <javascript:;>> > + * > + * 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 cb_write(BIO *b, const char *buf, int num); > +static int cb_read(BIO *b, char *buf, int size); > +static int cb_puts(BIO *b, const char *str); > +static long cb_ctrl(BIO *b, int cmd, long num, void *ptr); > +static int cb_new(BIO *b); > +static int cb_free(BIO *data); > + > +struct bio_cb_st { > + int (*cb_write)(BIO *h, const char *buf, int num, void *payload); > + int (*cb_read)(BIO *h, char *buf, int size, void *payload); > + void *payload; > +}; > + > +static BIO_METHOD cb_method = { > + .type = BIO_TYPE_MEM, > + .name = "callbacks", > + .bwrite = cb_write, > + .bread = cb_read, > + .bputs = cb_puts, > + .ctrl = cb_ctrl, > + .create = cb_new, > + .destroy = cb_free > +}; > + > + > + > +BIO_METHOD * > +BIO_s_cb(void) > +{ > + return (&cb_method); > +} > + > +int > +BIO_set_cb_write(BIO *bi, int (*cb_write)(BIO *h, const char *buf, int > num, void *payload)) > +{ > + struct bio_cb_st *b; > + b = (struct bio_cb_st *)bi->ptr; > + b->cb_write = cb_write; > + return (0); > +} > + > +int > +BIO_set_cb_read(BIO *bi, int (*cb_read)(BIO *h, char *buf, int size, void > *payload)) > +{ > + struct bio_cb_st *b; > + b = (struct bio_cb_st *)bi->ptr; > + b->cb_read = cb_read; > + return (0); > +} > + > +int > +BIO_set_cb_payload(BIO *bi, void *payload) > +{ > + struct bio_cb_st *b; > + b = (struct bio_cb_st *)bi->ptr; > + b->payload = payload; > + return (0); > +} > + > +void * > +BIO_get_cb_payload(BIO *bi) > +{ > + struct bio_cb_st *b; > + b = (struct bio_cb_st *)bi->ptr; > + return (b->payload); > +} > + > + > + > +static int > +cb_new(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 > +cb_free(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 > +cb_read(BIO *b, char *buf, int size) > +{ > + int ret = -1; > + struct bio_cb_st *bcb; > + > + bcb = (struct bio_cb_st *)b->ptr; > + ret = (bcb->cb_read)(b, buf, size, bcb->payload); > + return (ret); > +} > + > + > + > +static int > +cb_write(BIO *b, const char *buf, int num) > +{ > + int ret = -1; > + struct bio_cb_st *bcb; > + > + bcb = (struct bio_cb_st *)b->ptr; > + ret = (bcb->cb_write)(b, buf, num, bcb->payload); > + return (ret); > +} > + > +static int > +cb_puts(BIO *b, const char *str) > +{ > + int n, ret; > + > + n = strlen(str); > + ret = cb_write(b, str, n); > + return (ret); > +} > + > +static long > +cb_ctrl(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); > + > +} > diff --git src/lib/libtls/tls_client.c src/lib/libtls/tls_client.c > index 2149552..c5334f8 100644 > --- src/lib/libtls/tls_client.c > +++ src/lib/libtls/tls_client.c > @@ -158,6 +158,80 @@ tls_connect_servername(struct tls *ctx, const char > *host, const char *port, > return (rv); > } > > +int > +tls_connect_cbs(struct tls *ctx, tls_read_cb cb_read, > + tls_write_cb cb_write, void *cb_payload, 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 (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, ctx->ssl_ctx, ctx->config->keypair, > 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 ((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 (tls_set_cbs(ctx, cb_read, cb_write, cb_payload) != 0) { > + tls_set_errorx(ctx, "callback registration 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_connect_socket(struct tls *ctx, int s, const char *servername) > { > diff --git src/lib/libtls/tls_init.3 src/lib/libtls/tls_init.3 > index 51b58ae..9fb4545 100644 > --- src/lib/libtls/tls_init.3 > +++ src/lib/libtls/tls_init.3 > @@ -66,8 +66,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 , > @@ -172,10 +174,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 *payload)" "ssize_t (*tls_write_cb)(void > *ctx, const void *buf, size_t buflen, void *payload)" "void *cb_payload" > "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 *payload)" > "ssize_t (*tls_write_cb)(void *ctx, const void *buf, size_t buflen, void > *payload)" "void *cb_payload" > +.Ft "int" > .Fn tls_handshake "struct tls *ctx" > .Ft "ssize_t" > .Fn tls_read "struct tls *ctx" "void *buf" "size_t buflen" > @@ -231,14 +237,18 @@ An already existing socket can be upgraded to a > secure connection by calling > .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. > .Pp > A server can accept a new client connection by calling > .Fn tls_accept_socket > 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 . > +.Fn tls_accept_fds .Using > +.Fn tls_accept_cbs , read and write callbacks can be specified to handle > the > +actual data transfer. > .Pp > The TLS handshake can be completed by calling > .Fn tls_handshake . > diff --git src/lib/libtls/tls_internal.h src/lib/libtls/tls_internal.h > index 01bda4f..41a58fa 100644 > --- src/lib/libtls/tls_internal.h > +++ src/lib/libtls/tls_internal.h > @@ -103,6 +103,10 @@ struct tls { > SSL_CTX *ssl_ctx; > X509 *ssl_peer_cert; > struct tls_conninfo *conninfo; > + > + ssize_t (*cb_read)(void *_ctx, void *_buf, size_t _buflen, void > *_payload); > + ssize_t (*cb_write)(void *_ctx, const void *_buf, size_t _buflen, > void *_payload); > + void *cb_payload; > }; > > struct tls *tls_new(void); > @@ -117,6 +121,11 @@ 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_cbs(struct tls *ctx, > + ssize_t (*cb_read)(void *_ctx, void *_buf, size_t _buflen, void > *_payload), > + ssize_t (*cb_write)(void *_ctx, const void *_buf, size_t _buflen, > void *_payload), > + void *cb_payload); > + > > int tls_error_set(struct tls_error *error, const char *fmt, ...) > __attribute__((__format__ (printf, 2, 3))) > @@ -145,4 +154,10 @@ void tls_free_conninfo(struct tls_conninfo *conninfo); > > int asn1_time_parse(const char *, size_t, struct tm *, int); > > +BIO_METHOD * BIO_s_cb(void); > +int BIO_set_cb_write(BIO *bi, int (*cb_write)(BIO *h, const char *buf, > int num, void *payload)); > +int BIO_set_cb_read(BIO *bi, int (*cb_read)(BIO *h, char *buf, int size, > void *payload)); > +int BIO_set_cb_payload(BIO *bi, void *payload); > +void *BIO_get_cb_payload(BIO *bi); > + > #endif /* HEADER_TLS_INTERNAL_H */ > diff --git src/lib/libtls/tls_server.c src/lib/libtls/tls_server.c > index 58dbb60..b05df52 100644 > --- src/lib/libtls/tls_server.c > +++ src/lib/libtls/tls_server.c > @@ -110,6 +110,49 @@ tls_configure_server(struct tls *ctx) > return (-1); > } > > +int > +tls_accept_cbs(struct tls *ctx, struct tls **cctx, tls_read_cb cb_read, > + tls_write_cb cb_write, void *cb_payload) > +{ > + 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 (tls_set_cbs(ctx, cb_read, cb_write, cb_payload) != 0) { > + tls_set_errorx(ctx, "callback registration failure"); > + goto err; > + } > + > + *cctx = conn_ctx; > + > + return (0); > + > + err: > + tls_free(conn_ctx); > + > + *cctx = NULL; > + > + return (-1); > +} > + > + > int > tls_accept_socket(struct tls *ctx, struct tls **cctx, int socket) > { > >
