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 <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 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)
 {

Reply via email to