1*7a756d37Sjoshua /* $OpenBSD: tls_bio_cb.c,v 1.22 2024/03/26 06:24:52 joshua Exp $ */
2ed19021fSbcook /*
3ed19021fSbcook * Copyright (c) 2016 Tobias Pape <tobias@netshed.de>
4ed19021fSbcook *
5ed19021fSbcook * Permission to use, copy, modify, and distribute this software for any
6ed19021fSbcook * purpose with or without fee is hereby granted, provided that the above
7ed19021fSbcook * copyright notice and this permission notice appear in all copies.
8ed19021fSbcook *
9ed19021fSbcook * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10ed19021fSbcook * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11ed19021fSbcook * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12ed19021fSbcook * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13ed19021fSbcook * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14ed19021fSbcook * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15ed19021fSbcook * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16ed19021fSbcook */
17ed19021fSbcook
18008126b9Sjsing #include <fcntl.h>
19ed19021fSbcook #include <stdlib.h>
20e6d77be9Sop #include <string.h>
21ed19021fSbcook #include <unistd.h>
22ed19021fSbcook
23ed19021fSbcook #include <openssl/bio.h>
24ed19021fSbcook
25008126b9Sjsing #include <tls.h>
26008126b9Sjsing #include "tls_internal.h"
27008126b9Sjsing
2815951292Sjsing static int bio_cb_write(BIO *bio, const char *buf, int num);
2915951292Sjsing static int bio_cb_read(BIO *bio, char *buf, int size);
3015951292Sjsing static int bio_cb_puts(BIO *bio, const char *str);
3115951292Sjsing static long bio_cb_ctrl(BIO *bio, int cmd, long num, void *ptr);
32ed19021fSbcook
337225d6bcStb static BIO_METHOD *bio_cb_method;
347225d6bcStb
357225d6bcStb static pthread_mutex_t bio_cb_method_lock = PTHREAD_MUTEX_INITIALIZER;
367225d6bcStb
377225d6bcStb static void
bio_cb_method_init(void)387225d6bcStb bio_cb_method_init(void)
397225d6bcStb {
407225d6bcStb BIO_METHOD *bio_method;
417225d6bcStb
427225d6bcStb if (bio_cb_method != NULL)
437225d6bcStb return;
447225d6bcStb
457225d6bcStb bio_method = BIO_meth_new(BIO_TYPE_MEM, "libtls_callbacks");
467225d6bcStb if (bio_method == NULL)
477225d6bcStb return;
487225d6bcStb
497225d6bcStb BIO_meth_set_write(bio_method, bio_cb_write);
507225d6bcStb BIO_meth_set_read(bio_method, bio_cb_read);
517225d6bcStb BIO_meth_set_puts(bio_method, bio_cb_puts);
527225d6bcStb BIO_meth_set_ctrl(bio_method, bio_cb_ctrl);
537225d6bcStb
547225d6bcStb bio_cb_method = bio_method;
557225d6bcStb }
56ed19021fSbcook
57ed19021fSbcook static BIO_METHOD *
bio_s_cb(void)58ed19021fSbcook bio_s_cb(void)
59ed19021fSbcook {
607225d6bcStb if (bio_cb_method != NULL)
617225d6bcStb return (bio_cb_method);
627225d6bcStb
637225d6bcStb pthread_mutex_lock(&bio_cb_method_lock);
647225d6bcStb bio_cb_method_init();
657225d6bcStb pthread_mutex_unlock(&bio_cb_method_lock);
667225d6bcStb
677225d6bcStb return (bio_cb_method);
68ed19021fSbcook }
69ed19021fSbcook
70ed19021fSbcook static int
bio_cb_puts(BIO * bio,const char * str)7115951292Sjsing bio_cb_puts(BIO *bio, const char *str)
72ed19021fSbcook {
73c2d9881dSjsing return (bio_cb_write(bio, str, strlen(str)));
74ed19021fSbcook }
75ed19021fSbcook
76ed19021fSbcook static long
bio_cb_ctrl(BIO * bio,int cmd,long num,void * ptr)7715951292Sjsing bio_cb_ctrl(BIO *bio, int cmd, long num, void *ptr)
78ed19021fSbcook {
79ed19021fSbcook long ret = 1;
80ed19021fSbcook
81ed19021fSbcook switch (cmd) {
82ed19021fSbcook case BIO_CTRL_GET_CLOSE:
837225d6bcStb ret = (long)BIO_get_shutdown(bio);
84ed19021fSbcook break;
85ed19021fSbcook case BIO_CTRL_SET_CLOSE:
867225d6bcStb BIO_set_shutdown(bio, (int)num);
87ed19021fSbcook break;
88ed19021fSbcook case BIO_CTRL_DUP:
89f713bb53Sbcook case BIO_CTRL_FLUSH:
90ed19021fSbcook break;
91ed19021fSbcook case BIO_CTRL_INFO:
92ed19021fSbcook case BIO_CTRL_GET:
93ed19021fSbcook case BIO_CTRL_SET:
94ed19021fSbcook default:
957225d6bcStb ret = BIO_ctrl(BIO_next(bio), cmd, num, ptr);
96ed19021fSbcook }
97ed19021fSbcook
98ed19021fSbcook return (ret);
99ed19021fSbcook }
100ed19021fSbcook
101ed19021fSbcook static int
bio_cb_write(BIO * bio,const char * buf,int num)10211756bf8Sjsing bio_cb_write(BIO *bio, const char *buf, int num)
103ed19021fSbcook {
1047225d6bcStb struct tls *ctx = BIO_get_data(bio);
105f616df6fSjsing int rv;
106f616df6fSjsing
10715951292Sjsing BIO_clear_retry_flags(bio);
108f616df6fSjsing rv = (ctx->write_cb)(ctx, buf, num, ctx->cb_arg);
10948924131Sbcook if (rv == TLS_WANT_POLLIN) {
11015951292Sjsing BIO_set_retry_read(bio);
11148924131Sbcook rv = -1;
11248924131Sbcook } else if (rv == TLS_WANT_POLLOUT) {
11315951292Sjsing BIO_set_retry_write(bio);
11448924131Sbcook rv = -1;
11548924131Sbcook }
11648924131Sbcook return (rv);
117ed19021fSbcook }
118ed19021fSbcook
119ed19021fSbcook static int
bio_cb_read(BIO * bio,char * buf,int size)12011756bf8Sjsing bio_cb_read(BIO *bio, char *buf, int size)
121ed19021fSbcook {
1227225d6bcStb struct tls *ctx = BIO_get_data(bio);
123f616df6fSjsing int rv;
124f616df6fSjsing
12515951292Sjsing BIO_clear_retry_flags(bio);
126f616df6fSjsing rv = (ctx->read_cb)(ctx, buf, size, ctx->cb_arg);
12748924131Sbcook if (rv == TLS_WANT_POLLIN) {
12815951292Sjsing BIO_set_retry_read(bio);
12948924131Sbcook rv = -1;
13048924131Sbcook } else if (rv == TLS_WANT_POLLOUT) {
13115951292Sjsing BIO_set_retry_write(bio);
13248924131Sbcook rv = -1;
13348924131Sbcook }
13448924131Sbcook return (rv);
135ed19021fSbcook }
136ed19021fSbcook
137ed19021fSbcook int
tls_set_cbs(struct tls * ctx,tls_read_cb read_cb,tls_write_cb write_cb,void * cb_arg)138ed19021fSbcook tls_set_cbs(struct tls *ctx, tls_read_cb read_cb, tls_write_cb write_cb,
139ed19021fSbcook void *cb_arg)
140ed19021fSbcook {
1417225d6bcStb const BIO_METHOD *bio_cb;
14215951292Sjsing BIO *bio;
1437225d6bcStb int rv = -1;
14415951292Sjsing
145d103824dSjsing if (read_cb == NULL || write_cb == NULL) {
146*7a756d37Sjoshua tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "no callbacks provided");
147d103824dSjsing goto err;
148d103824dSjsing }
149d103824dSjsing
150ed19021fSbcook ctx->read_cb = read_cb;
151ed19021fSbcook ctx->write_cb = write_cb;
152ed19021fSbcook ctx->cb_arg = cb_arg;
153ed19021fSbcook
1547225d6bcStb if ((bio_cb = bio_s_cb()) == NULL) {
155*7a756d37Sjoshua tls_set_errorx(ctx, TLS_ERROR_UNKNOWN,
156*7a756d37Sjoshua "failed to create callback method");
1577225d6bcStb goto err;
1587225d6bcStb }
1597225d6bcStb if ((bio = BIO_new(bio_cb)) == NULL) {
160*7a756d37Sjoshua tls_set_errorx(ctx, TLS_ERROR_UNKNOWN,
161*7a756d37Sjoshua "failed to create callback i/o");
162ed19021fSbcook goto err;
163d103824dSjsing }
1647225d6bcStb BIO_set_data(bio, ctx);
1657225d6bcStb BIO_set_init(bio, 1);
166ed19021fSbcook
16715951292Sjsing SSL_set_bio(ctx->ssl_conn, bio, bio);
168ed19021fSbcook
169ed19021fSbcook rv = 0;
170ed19021fSbcook
171ed19021fSbcook err:
172ed19021fSbcook return (rv);
173ed19021fSbcook }
174