1*de0e0e4dSAntonio Huete Jimenez /* $OpenBSD: tls_bio_cb.c,v 1.20 2022/01/10 23:39:48 tb Exp $ */
272c33676SMaxim Ag /*
372c33676SMaxim Ag * Copyright (c) 2016 Tobias Pape <tobias@netshed.de>
472c33676SMaxim Ag *
572c33676SMaxim Ag * Permission to use, copy, modify, and distribute this software for any
672c33676SMaxim Ag * purpose with or without fee is hereby granted, provided that the above
772c33676SMaxim Ag * copyright notice and this permission notice appear in all copies.
872c33676SMaxim Ag *
972c33676SMaxim Ag * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1072c33676SMaxim Ag * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1172c33676SMaxim Ag * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1272c33676SMaxim Ag * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1372c33676SMaxim Ag * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1472c33676SMaxim Ag * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1572c33676SMaxim Ag * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1672c33676SMaxim Ag */
1772c33676SMaxim Ag
1872c33676SMaxim Ag #include <fcntl.h>
1972c33676SMaxim Ag #include <stdlib.h>
2072c33676SMaxim Ag #include <unistd.h>
2172c33676SMaxim Ag
2272c33676SMaxim Ag #include <openssl/bio.h>
2372c33676SMaxim Ag
2472c33676SMaxim Ag #include <tls.h>
2572c33676SMaxim Ag #include "tls_internal.h"
2672c33676SMaxim Ag
2772c33676SMaxim Ag static int bio_cb_write(BIO *bio, const char *buf, int num);
2872c33676SMaxim Ag static int bio_cb_read(BIO *bio, char *buf, int size);
2972c33676SMaxim Ag static int bio_cb_puts(BIO *bio, const char *str);
3072c33676SMaxim Ag static long bio_cb_ctrl(BIO *bio, int cmd, long num, void *ptr);
3172c33676SMaxim Ag
32*de0e0e4dSAntonio Huete Jimenez static BIO_METHOD *bio_cb_method;
33*de0e0e4dSAntonio Huete Jimenez
34*de0e0e4dSAntonio Huete Jimenez static pthread_mutex_t bio_cb_method_lock = PTHREAD_MUTEX_INITIALIZER;
35*de0e0e4dSAntonio Huete Jimenez
36*de0e0e4dSAntonio Huete Jimenez static void
bio_cb_method_init(void)37*de0e0e4dSAntonio Huete Jimenez bio_cb_method_init(void)
38*de0e0e4dSAntonio Huete Jimenez {
39*de0e0e4dSAntonio Huete Jimenez BIO_METHOD *bio_method;
40*de0e0e4dSAntonio Huete Jimenez
41*de0e0e4dSAntonio Huete Jimenez if (bio_cb_method != NULL)
42*de0e0e4dSAntonio Huete Jimenez return;
43*de0e0e4dSAntonio Huete Jimenez
44*de0e0e4dSAntonio Huete Jimenez bio_method = BIO_meth_new(BIO_TYPE_MEM, "libtls_callbacks");
45*de0e0e4dSAntonio Huete Jimenez if (bio_method == NULL)
46*de0e0e4dSAntonio Huete Jimenez return;
47*de0e0e4dSAntonio Huete Jimenez
48*de0e0e4dSAntonio Huete Jimenez BIO_meth_set_write(bio_method, bio_cb_write);
49*de0e0e4dSAntonio Huete Jimenez BIO_meth_set_read(bio_method, bio_cb_read);
50*de0e0e4dSAntonio Huete Jimenez BIO_meth_set_puts(bio_method, bio_cb_puts);
51*de0e0e4dSAntonio Huete Jimenez BIO_meth_set_ctrl(bio_method, bio_cb_ctrl);
52*de0e0e4dSAntonio Huete Jimenez
53*de0e0e4dSAntonio Huete Jimenez bio_cb_method = bio_method;
54*de0e0e4dSAntonio Huete Jimenez }
5572c33676SMaxim Ag
5672c33676SMaxim Ag static BIO_METHOD *
bio_s_cb(void)5772c33676SMaxim Ag bio_s_cb(void)
5872c33676SMaxim Ag {
59*de0e0e4dSAntonio Huete Jimenez if (bio_cb_method != NULL)
60*de0e0e4dSAntonio Huete Jimenez return (bio_cb_method);
61*de0e0e4dSAntonio Huete Jimenez
62*de0e0e4dSAntonio Huete Jimenez pthread_mutex_lock(&bio_cb_method_lock);
63*de0e0e4dSAntonio Huete Jimenez bio_cb_method_init();
64*de0e0e4dSAntonio Huete Jimenez pthread_mutex_unlock(&bio_cb_method_lock);
65*de0e0e4dSAntonio Huete Jimenez
66*de0e0e4dSAntonio Huete Jimenez return (bio_cb_method);
6772c33676SMaxim Ag }
6872c33676SMaxim Ag
6972c33676SMaxim Ag static int
bio_cb_puts(BIO * bio,const char * str)7072c33676SMaxim Ag bio_cb_puts(BIO *bio, const char *str)
7172c33676SMaxim Ag {
7272c33676SMaxim Ag return (bio_cb_write(bio, str, strlen(str)));
7372c33676SMaxim Ag }
7472c33676SMaxim Ag
7572c33676SMaxim Ag static long
bio_cb_ctrl(BIO * bio,int cmd,long num,void * ptr)7672c33676SMaxim Ag bio_cb_ctrl(BIO *bio, int cmd, long num, void *ptr)
7772c33676SMaxim Ag {
7872c33676SMaxim Ag long ret = 1;
7972c33676SMaxim Ag
8072c33676SMaxim Ag switch (cmd) {
8172c33676SMaxim Ag case BIO_CTRL_GET_CLOSE:
82*de0e0e4dSAntonio Huete Jimenez ret = (long)BIO_get_shutdown(bio);
8372c33676SMaxim Ag break;
8472c33676SMaxim Ag case BIO_CTRL_SET_CLOSE:
85*de0e0e4dSAntonio Huete Jimenez BIO_set_shutdown(bio, (int)num);
8672c33676SMaxim Ag break;
8772c33676SMaxim Ag case BIO_CTRL_DUP:
8872c33676SMaxim Ag case BIO_CTRL_FLUSH:
8972c33676SMaxim Ag break;
9072c33676SMaxim Ag case BIO_CTRL_INFO:
9172c33676SMaxim Ag case BIO_CTRL_GET:
9272c33676SMaxim Ag case BIO_CTRL_SET:
9372c33676SMaxim Ag default:
94*de0e0e4dSAntonio Huete Jimenez ret = BIO_ctrl(BIO_next(bio), cmd, num, ptr);
9572c33676SMaxim Ag }
9672c33676SMaxim Ag
9772c33676SMaxim Ag return (ret);
9872c33676SMaxim Ag }
9972c33676SMaxim Ag
10072c33676SMaxim Ag static int
bio_cb_write(BIO * bio,const char * buf,int num)10172c33676SMaxim Ag bio_cb_write(BIO *bio, const char *buf, int num)
10272c33676SMaxim Ag {
103*de0e0e4dSAntonio Huete Jimenez struct tls *ctx = BIO_get_data(bio);
10472c33676SMaxim Ag int rv;
10572c33676SMaxim Ag
10672c33676SMaxim Ag BIO_clear_retry_flags(bio);
10772c33676SMaxim Ag rv = (ctx->write_cb)(ctx, buf, num, ctx->cb_arg);
10872c33676SMaxim Ag if (rv == TLS_WANT_POLLIN) {
10972c33676SMaxim Ag BIO_set_retry_read(bio);
11072c33676SMaxim Ag rv = -1;
11172c33676SMaxim Ag } else if (rv == TLS_WANT_POLLOUT) {
11272c33676SMaxim Ag BIO_set_retry_write(bio);
11372c33676SMaxim Ag rv = -1;
11472c33676SMaxim Ag }
11572c33676SMaxim Ag return (rv);
11672c33676SMaxim Ag }
11772c33676SMaxim Ag
11872c33676SMaxim Ag static int
bio_cb_read(BIO * bio,char * buf,int size)11972c33676SMaxim Ag bio_cb_read(BIO *bio, char *buf, int size)
12072c33676SMaxim Ag {
121*de0e0e4dSAntonio Huete Jimenez struct tls *ctx = BIO_get_data(bio);
12272c33676SMaxim Ag int rv;
12372c33676SMaxim Ag
12472c33676SMaxim Ag BIO_clear_retry_flags(bio);
12572c33676SMaxim Ag rv = (ctx->read_cb)(ctx, buf, size, ctx->cb_arg);
12672c33676SMaxim Ag if (rv == TLS_WANT_POLLIN) {
12772c33676SMaxim Ag BIO_set_retry_read(bio);
12872c33676SMaxim Ag rv = -1;
12972c33676SMaxim Ag } else if (rv == TLS_WANT_POLLOUT) {
13072c33676SMaxim Ag BIO_set_retry_write(bio);
13172c33676SMaxim Ag rv = -1;
13272c33676SMaxim Ag }
13372c33676SMaxim Ag return (rv);
13472c33676SMaxim Ag }
13572c33676SMaxim Ag
13672c33676SMaxim Ag int
tls_set_cbs(struct tls * ctx,tls_read_cb read_cb,tls_write_cb write_cb,void * cb_arg)13772c33676SMaxim Ag tls_set_cbs(struct tls *ctx, tls_read_cb read_cb, tls_write_cb write_cb,
13872c33676SMaxim Ag void *cb_arg)
13972c33676SMaxim Ag {
140*de0e0e4dSAntonio Huete Jimenez const BIO_METHOD *bio_cb;
14172c33676SMaxim Ag BIO *bio;
142*de0e0e4dSAntonio Huete Jimenez int rv = -1;
14372c33676SMaxim Ag
14472c33676SMaxim Ag if (read_cb == NULL || write_cb == NULL) {
14572c33676SMaxim Ag tls_set_errorx(ctx, "no callbacks provided");
14672c33676SMaxim Ag goto err;
14772c33676SMaxim Ag }
14872c33676SMaxim Ag
14972c33676SMaxim Ag ctx->read_cb = read_cb;
15072c33676SMaxim Ag ctx->write_cb = write_cb;
15172c33676SMaxim Ag ctx->cb_arg = cb_arg;
15272c33676SMaxim Ag
153*de0e0e4dSAntonio Huete Jimenez if ((bio_cb = bio_s_cb()) == NULL) {
154*de0e0e4dSAntonio Huete Jimenez tls_set_errorx(ctx, "failed to create callback method");
155*de0e0e4dSAntonio Huete Jimenez goto err;
156*de0e0e4dSAntonio Huete Jimenez }
157*de0e0e4dSAntonio Huete Jimenez if ((bio = BIO_new(bio_cb)) == NULL) {
15872c33676SMaxim Ag tls_set_errorx(ctx, "failed to create callback i/o");
15972c33676SMaxim Ag goto err;
16072c33676SMaxim Ag }
161*de0e0e4dSAntonio Huete Jimenez BIO_set_data(bio, ctx);
162*de0e0e4dSAntonio Huete Jimenez BIO_set_init(bio, 1);
16372c33676SMaxim Ag
16472c33676SMaxim Ag SSL_set_bio(ctx->ssl_conn, bio, bio);
16572c33676SMaxim Ag
16672c33676SMaxim Ag rv = 0;
16772c33676SMaxim Ag
16872c33676SMaxim Ag err:
16972c33676SMaxim Ag return (rv);
17072c33676SMaxim Ag }
171