xref: /dragonfly/crypto/libressl/tls/tls_bio_cb.c (revision 961e30ea7dc61d1112b778ea4981eac68129fb86)
1 /* $OpenBSD: tls_bio_cb.c,v 1.20 2022/01/10 23:39:48 tb 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 <unistd.h>
21 
22 #include <openssl/bio.h>
23 
24 #include <tls.h>
25 #include "tls_internal.h"
26 
27 static int bio_cb_write(BIO *bio, const char *buf, int num);
28 static int bio_cb_read(BIO *bio, char *buf, int size);
29 static int bio_cb_puts(BIO *bio, const char *str);
30 static long bio_cb_ctrl(BIO *bio, int cmd, long num, void *ptr);
31 
32 static BIO_METHOD *bio_cb_method;
33 
34 static pthread_mutex_t bio_cb_method_lock = PTHREAD_MUTEX_INITIALIZER;
35 
36 static void
bio_cb_method_init(void)37 bio_cb_method_init(void)
38 {
39           BIO_METHOD *bio_method;
40 
41           if (bio_cb_method != NULL)
42                     return;
43 
44           bio_method = BIO_meth_new(BIO_TYPE_MEM, "libtls_callbacks");
45           if (bio_method == NULL)
46                     return;
47 
48           BIO_meth_set_write(bio_method, bio_cb_write);
49           BIO_meth_set_read(bio_method, bio_cb_read);
50           BIO_meth_set_puts(bio_method, bio_cb_puts);
51           BIO_meth_set_ctrl(bio_method, bio_cb_ctrl);
52 
53           bio_cb_method = bio_method;
54 }
55 
56 static BIO_METHOD *
bio_s_cb(void)57 bio_s_cb(void)
58 {
59           if (bio_cb_method != NULL)
60                     return (bio_cb_method);
61 
62           pthread_mutex_lock(&bio_cb_method_lock);
63           bio_cb_method_init();
64           pthread_mutex_unlock(&bio_cb_method_lock);
65 
66           return (bio_cb_method);
67 }
68 
69 static int
bio_cb_puts(BIO * bio,const char * str)70 bio_cb_puts(BIO *bio, const char *str)
71 {
72           return (bio_cb_write(bio, str, strlen(str)));
73 }
74 
75 static long
bio_cb_ctrl(BIO * bio,int cmd,long num,void * ptr)76 bio_cb_ctrl(BIO *bio, int cmd, long num, void *ptr)
77 {
78           long ret = 1;
79 
80           switch (cmd) {
81           case BIO_CTRL_GET_CLOSE:
82                     ret = (long)BIO_get_shutdown(bio);
83                     break;
84           case BIO_CTRL_SET_CLOSE:
85                     BIO_set_shutdown(bio, (int)num);
86                     break;
87           case BIO_CTRL_DUP:
88           case BIO_CTRL_FLUSH:
89                     break;
90           case BIO_CTRL_INFO:
91           case BIO_CTRL_GET:
92           case BIO_CTRL_SET:
93           default:
94                     ret = BIO_ctrl(BIO_next(bio), cmd, num, ptr);
95           }
96 
97           return (ret);
98 }
99 
100 static int
bio_cb_write(BIO * bio,const char * buf,int num)101 bio_cb_write(BIO *bio, const char *buf, int num)
102 {
103           struct tls *ctx = BIO_get_data(bio);
104           int rv;
105 
106           BIO_clear_retry_flags(bio);
107           rv = (ctx->write_cb)(ctx, buf, num, ctx->cb_arg);
108           if (rv == TLS_WANT_POLLIN) {
109                     BIO_set_retry_read(bio);
110                     rv = -1;
111           } else if (rv == TLS_WANT_POLLOUT) {
112                     BIO_set_retry_write(bio);
113                     rv = -1;
114           }
115           return (rv);
116 }
117 
118 static int
bio_cb_read(BIO * bio,char * buf,int size)119 bio_cb_read(BIO *bio, char *buf, int size)
120 {
121           struct tls *ctx = BIO_get_data(bio);
122           int rv;
123 
124           BIO_clear_retry_flags(bio);
125           rv = (ctx->read_cb)(ctx, buf, size, ctx->cb_arg);
126           if (rv == TLS_WANT_POLLIN) {
127                     BIO_set_retry_read(bio);
128                     rv = -1;
129           } else if (rv == TLS_WANT_POLLOUT) {
130                     BIO_set_retry_write(bio);
131                     rv = -1;
132           }
133           return (rv);
134 }
135 
136 int
tls_set_cbs(struct tls * ctx,tls_read_cb read_cb,tls_write_cb write_cb,void * cb_arg)137 tls_set_cbs(struct tls *ctx, tls_read_cb read_cb, tls_write_cb write_cb,
138     void *cb_arg)
139 {
140           const BIO_METHOD *bio_cb;
141           BIO *bio;
142           int rv = -1;
143 
144           if (read_cb == NULL || write_cb == NULL) {
145                     tls_set_errorx(ctx, "no callbacks provided");
146                     goto err;
147           }
148 
149           ctx->read_cb = read_cb;
150           ctx->write_cb = write_cb;
151           ctx->cb_arg = cb_arg;
152 
153           if ((bio_cb = bio_s_cb()) == NULL) {
154                     tls_set_errorx(ctx, "failed to create callback method");
155                     goto err;
156           }
157           if ((bio = BIO_new(bio_cb)) == NULL) {
158                     tls_set_errorx(ctx, "failed to create callback i/o");
159                     goto err;
160           }
161           BIO_set_data(bio, ctx);
162           BIO_set_init(bio, 1);
163 
164           SSL_set_bio(ctx->ssl_conn, bio, bio);
165 
166           rv = 0;
167 
168  err:
169           return (rv);
170 }
171