On Sun, Jul 17, 2016 at 01:32:43PM +0200, Tobias Pape 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
Thanks Tobias,
I took some time rebasing and reworking this a bit. Here are the main
things:
* renamed and hid the exported private BIO_* functions from
tls_bio_cb.c, so it does not look like we are creating new functions
in libssl's BIO_ namespace
* refactored tls_connect* and tls_accept* to use shared setup, rather
than copying the internals of tls_*_fds into tls_*_cbs
* renamed 'cb_read/write' to 'read/write_cb' for consistency with other
variable naming
* use the callback function typedefs directly rather than redefining in
multiple places
Tested with nc, but the callback-specific functionality is only
build-tested. How does this work for your use-cases?
- Brent
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 09:38:56 -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 09:38:56 -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 09:38:56 -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_payload = 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 09:38:57 -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 *_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);
@@ -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_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 _read_cb,
+ tls_write_cb _write_cb, 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);
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 09:38:57 -0000
@@ -0,0 +1,222 @@
+/* $ID$ */
+/*
+ * Copyright (c) 2016 Tobias Pape <[email protected]>
+ *
+ * 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 *payload);
+ int (*read_cb)(BIO *h, char *buf, int size, void *payload);
+ void *payload;
+};
+
+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 *payload))
+{
+ 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
*payload))
+{
+ struct bio_cb_st *b;
+ b = (struct bio_cb_st *)bi->ptr;
+ b->read_cb = read_cb;
+ return (0);
+}
+
+static 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);
+}
+
+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->payload));
+}
+
+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->payload));
+}
+
+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 *payload)
+{
+ struct tls *ctx = payload;
+ return (ctx->write_cb)(ctx, buf, num, ctx->cb_payload);
+}
+
+static int
+tls_bio_read_cb(BIO *h, char *buf, int size, void *payload)
+{
+ struct tls *ctx = payload;
+ return (ctx->read_cb)(ctx, buf, size, ctx->cb_payload);
+}
+
+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_payload(bcb, ctx);
+
+ return (bcb);
+}
+
+int
+tls_set_cbs(struct tls *ctx, tls_read_cb read_cb, tls_write_cb write_cb,
+ void *cb_payload)
+{
+ int rv = -1;
+ BIO *bcb;
+ ctx->read_cb = read_cb;
+ ctx->write_cb = write_cb;
+ 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);
+}
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 09:38:57 -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_payload, const char *servername)
+{
+ int rv = -1;
+
+ if (connect_common(ctx, servername) != 0)
+ goto err;
+
+ if (tls_set_cbs(ctx, read_cb, write_cb, cb_payload) != 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 09:38:58 -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 *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"
@@ -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.
.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 .
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 09:38:58 -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_payload;
};
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_payload);
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 09:38:58 -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_payload)
+{
+ struct tls *conn_ctx;
+
+ if ((conn_ctx = accept_common(ctx)) == NULL)
+ goto err;
+
+ if (tls_set_cbs(ctx, read_cb, write_cb, 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);
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 09:38:58 -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);