xref: /openbsd-src/lib/libtls/tls_bio_cb.c (revision 7a756d3745d54f76014cbb694af2a7a421409519)
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