xref: /openbsd-src/lib/libtls/tls_bio_cb.c (revision 7a756d3745d54f76014cbb694af2a7a421409519)
1 /* $OpenBSD: tls_bio_cb.c,v 1.22 2024/03/26 06:24:52 joshua Exp $ */
2 /*
3  * Copyright (c) 2016 Tobias Pape <tobias@netshed.de>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <fcntl.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22 
23 #include <openssl/bio.h>
24 
25 #include <tls.h>
26 #include "tls_internal.h"
27 
28 static int bio_cb_write(BIO *bio, const char *buf, int num);
29 static int bio_cb_read(BIO *bio, char *buf, int size);
30 static int bio_cb_puts(BIO *bio, const char *str);
31 static long bio_cb_ctrl(BIO *bio, int cmd, long num, void *ptr);
32 
33 static BIO_METHOD *bio_cb_method;
34 
35 static pthread_mutex_t bio_cb_method_lock = PTHREAD_MUTEX_INITIALIZER;
36 
37 static void
bio_cb_method_init(void)38 bio_cb_method_init(void)
39 {
40 	BIO_METHOD *bio_method;
41 
42 	if (bio_cb_method != NULL)
43 		return;
44 
45 	bio_method = BIO_meth_new(BIO_TYPE_MEM, "libtls_callbacks");
46 	if (bio_method == NULL)
47 		return;
48 
49 	BIO_meth_set_write(bio_method, bio_cb_write);
50 	BIO_meth_set_read(bio_method, bio_cb_read);
51 	BIO_meth_set_puts(bio_method, bio_cb_puts);
52 	BIO_meth_set_ctrl(bio_method, bio_cb_ctrl);
53 
54 	bio_cb_method = bio_method;
55 }
56 
57 static BIO_METHOD *
bio_s_cb(void)58 bio_s_cb(void)
59 {
60 	if (bio_cb_method != NULL)
61 		return (bio_cb_method);
62 
63 	pthread_mutex_lock(&bio_cb_method_lock);
64 	bio_cb_method_init();
65 	pthread_mutex_unlock(&bio_cb_method_lock);
66 
67 	return (bio_cb_method);
68 }
69 
70 static int
bio_cb_puts(BIO * bio,const char * str)71 bio_cb_puts(BIO *bio, const char *str)
72 {
73 	return (bio_cb_write(bio, str, strlen(str)));
74 }
75 
76 static long
bio_cb_ctrl(BIO * bio,int cmd,long num,void * ptr)77 bio_cb_ctrl(BIO *bio, int cmd, long num, void *ptr)
78 {
79 	long ret = 1;
80 
81 	switch (cmd) {
82 	case BIO_CTRL_GET_CLOSE:
83 		ret = (long)BIO_get_shutdown(bio);
84 		break;
85 	case BIO_CTRL_SET_CLOSE:
86 		BIO_set_shutdown(bio, (int)num);
87 		break;
88 	case BIO_CTRL_DUP:
89 	case BIO_CTRL_FLUSH:
90 		break;
91 	case BIO_CTRL_INFO:
92 	case BIO_CTRL_GET:
93 	case BIO_CTRL_SET:
94 	default:
95 		ret = BIO_ctrl(BIO_next(bio), cmd, num, ptr);
96 	}
97 
98 	return (ret);
99 }
100 
101 static int
bio_cb_write(BIO * bio,const char * buf,int num)102 bio_cb_write(BIO *bio, const char *buf, int num)
103 {
104 	struct tls *ctx = BIO_get_data(bio);
105 	int rv;
106 
107 	BIO_clear_retry_flags(bio);
108 	rv = (ctx->write_cb)(ctx, buf, num, ctx->cb_arg);
109 	if (rv == TLS_WANT_POLLIN) {
110 		BIO_set_retry_read(bio);
111 		rv = -1;
112 	} else if (rv == TLS_WANT_POLLOUT) {
113 		BIO_set_retry_write(bio);
114 		rv = -1;
115 	}
116 	return (rv);
117 }
118 
119 static int
bio_cb_read(BIO * bio,char * buf,int size)120 bio_cb_read(BIO *bio, char *buf, int size)
121 {
122 	struct tls *ctx = BIO_get_data(bio);
123 	int rv;
124 
125 	BIO_clear_retry_flags(bio);
126 	rv = (ctx->read_cb)(ctx, buf, size, ctx->cb_arg);
127 	if (rv == TLS_WANT_POLLIN) {
128 		BIO_set_retry_read(bio);
129 		rv = -1;
130 	} else if (rv == TLS_WANT_POLLOUT) {
131 		BIO_set_retry_write(bio);
132 		rv = -1;
133 	}
134 	return (rv);
135 }
136 
137 int
tls_set_cbs(struct tls * ctx,tls_read_cb read_cb,tls_write_cb write_cb,void * cb_arg)138 tls_set_cbs(struct tls *ctx, tls_read_cb read_cb, tls_write_cb write_cb,
139     void *cb_arg)
140 {
141 	const BIO_METHOD *bio_cb;
142 	BIO *bio;
143 	int rv = -1;
144 
145 	if (read_cb == NULL || write_cb == NULL) {
146 		tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "no callbacks provided");
147 		goto err;
148 	}
149 
150 	ctx->read_cb = read_cb;
151 	ctx->write_cb = write_cb;
152 	ctx->cb_arg = cb_arg;
153 
154 	if ((bio_cb = bio_s_cb()) == NULL) {
155 		tls_set_errorx(ctx, TLS_ERROR_UNKNOWN,
156 		    "failed to create callback method");
157 		goto err;
158 	}
159 	if ((bio = BIO_new(bio_cb)) == NULL) {
160 		tls_set_errorx(ctx, TLS_ERROR_UNKNOWN,
161 		    "failed to create callback i/o");
162 		goto err;
163 	}
164 	BIO_set_data(bio, ctx);
165 	BIO_set_init(bio, 1);
166 
167 	SSL_set_bio(ctx->ssl_conn, bio, bio);
168 
169 	rv = 0;
170 
171  err:
172 	return (rv);
173 }
174