blob: ef3601e7319090a2b672fafae7d54848ae7d4fda [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file openssl/tls_tcp.c TLS/TCP backend using OpenSSL
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6
7#include <openssl/ssl.h>
8#include <openssl/err.h>
9#include <re_types.h>
10#include <re_fmt.h>
11#include <re_mem.h>
12#include <re_mbuf.h>
13#include <re_main.h>
14#include <re_sa.h>
15#include <re_net.h>
16#include <re_srtp.h>
17#include <re_tcp.h>
18#include <re_tls.h>
19#include "tls.h"
20
21
22#define DEBUG_MODULE "tls"
23#define DEBUG_LEVEL 5
24#include <re_dbg.h>
25
26
27/* NOTE: shadow struct defined in tls_*.c */
28struct tls_conn {
29 SSL *ssl;
30#ifdef TLS_BIO_OPAQUE
31 BIO_METHOD *biomet;
32#endif
33 BIO *sbio_out;
34 BIO *sbio_in;
35 struct tcp_helper *th;
36 struct tcp_conn *tcp;
37 bool active;
38 bool up;
39};
40
41
42static void destructor(void *arg)
43{
44 struct tls_conn *tc = arg;
45
46 if (tc->ssl) {
47 int r = SSL_shutdown(tc->ssl);
48 if (r <= 0)
49 ERR_clear_error();
50
51 SSL_free(tc->ssl);
52 }
53
54#ifdef TLS_BIO_OPAQUE
55 if (tc->biomet)
56 BIO_meth_free(tc->biomet);
57#endif
58
59 mem_deref(tc->th);
60 mem_deref(tc->tcp);
61}
62
63
64static int bio_create(BIO *b)
65{
66#ifdef TLS_BIO_OPAQUE
67 BIO_set_init(b, 1);
68 BIO_set_data(b, NULL);
69 BIO_set_flags(b, 0);
70#else
71 b->init = 1;
72 b->num = 0;
73 b->ptr = NULL;
74 b->flags = 0;
75#endif
76
77 return 1;
78}
79
80
81static int bio_destroy(BIO *b)
82{
83 if (!b)
84 return 0;
85
86#ifdef TLS_BIO_OPAQUE
87 BIO_set_init(b, 0);
88 BIO_set_data(b, NULL);
89 BIO_set_flags(b, 0);
90#else
91 b->ptr = NULL;
92 b->init = 0;
93 b->flags = 0;
94#endif
95
96 return 1;
97}
98
99
100static int bio_write(BIO *b, const char *buf, int len)
101{
102#ifdef TLS_BIO_OPAQUE
103 struct tls_conn *tc = BIO_get_data(b);
104#else
105 struct tls_conn *tc = b->ptr;
106#endif
107 struct mbuf mb;
108 int err;
109
110 mb.buf = (void *)buf;
111 mb.pos = 0;
112 mb.end = mb.size = len;
113
114 err = tcp_send_helper(tc->tcp, &mb, tc->th);
115 if (err)
116 return -1;
117
118 return len;
119}
120
121
122static long bio_ctrl(BIO *b, int cmd, long num, void *ptr)
123{
124 (void)b;
125 (void)num;
126 (void)ptr;
127
128 if (cmd == BIO_CTRL_FLUSH) {
129 /* The OpenSSL library needs this */
130 return 1;
131 }
132
133 return 0;
134}
135
136
137#ifdef TLS_BIO_OPAQUE
138
139static BIO_METHOD *bio_method_tcp(void)
140{
141 BIO_METHOD *method;
142
143 method = BIO_meth_new(BIO_TYPE_SOURCE_SINK, "tcp_send");
144 if (!method) {
145 DEBUG_WARNING("alloc: BIO_meth_new() failed\n");
146 ERR_clear_error();
147 return NULL;
148 }
149
150 BIO_meth_set_write(method, bio_write);
151 BIO_meth_set_ctrl(method, bio_ctrl);
152 BIO_meth_set_create(method, bio_create);
153 BIO_meth_set_destroy(method, bio_destroy);
154
155 return method;
156}
157
158#else
159
160static struct bio_method_st bio_tcp_send = {
161 BIO_TYPE_SOURCE_SINK,
162 "tcp_send",
163 bio_write,
164 0,
165 0,
166 0,
167 bio_ctrl,
168 bio_create,
169 bio_destroy,
170 0
171};
172
173#endif
174
175
176static int tls_connect(struct tls_conn *tc)
177{
178 int err = 0, r;
179
180 ERR_clear_error();
181
182 r = SSL_connect(tc->ssl);
183 if (r <= 0) {
184 const int ssl_err = SSL_get_error(tc->ssl, r);
185
186 ERR_clear_error();
187
188 switch (ssl_err) {
189
190 case SSL_ERROR_WANT_READ:
191 break;
192
193 default:
194 DEBUG_WARNING("connect: error (r=%d, ssl_err=%d)\n",
195 r, ssl_err);
196 err = EPROTO;
197 break;
198 }
199 }
200
201 return err;
202}
203
204
205static int tls_accept(struct tls_conn *tc)
206{
207 int err = 0, r;
208
209 ERR_clear_error();
210
211 r = SSL_accept(tc->ssl);
212 if (r <= 0) {
213 const int ssl_err = SSL_get_error(tc->ssl, r);
214
215 ERR_clear_error();
216
217 switch (ssl_err) {
218
219 case SSL_ERROR_WANT_READ:
220 break;
221
222 default:
223 DEBUG_WARNING("accept error: (r=%d, ssl_err=%d)\n",
224 r, ssl_err);
225 err = EPROTO;
226 break;
227 }
228 }
229
230 return err;
231}
232
233
234static bool estab_handler(int *err, bool active, void *arg)
235{
236 struct tls_conn *tc = arg;
237
238 DEBUG_INFO("tcp established (active=%u)\n", active);
239
240 if (!active)
241 return true;
242
243 tc->active = true;
244 *err = tls_connect(tc);
245
246 return true;
247}
248
249
250static bool recv_handler(int *err, struct mbuf *mb, bool *estab, void *arg)
251{
252 struct tls_conn *tc = arg;
253 int r;
254
255 /* feed SSL data to the BIO */
256 r = BIO_write(tc->sbio_in, mbuf_buf(mb), (int)mbuf_get_left(mb));
257 if (r <= 0) {
258 DEBUG_WARNING("recv: BIO_write %d\n", r);
259 ERR_clear_error();
260 *err = ENOMEM;
261 return true;
262 }
263
264 if (SSL_state(tc->ssl) != SSL_ST_OK) {
265
266 if (tc->up) {
267 *err = EPROTO;
268 return true;
269 }
270
271 if (tc->active) {
272 *err = tls_connect(tc);
273 }
274 else {
275 *err = tls_accept(tc);
276 }
277
278 DEBUG_INFO("state=0x%04x\n", SSL_state(tc->ssl));
279
280 /* TLS connection is established */
281 if (SSL_state(tc->ssl) != SSL_ST_OK)
282 return true;
283
284 *estab = true;
285 tc->up = true;
286 }
287
288 mbuf_set_pos(mb, 0);
289
290 for (;;) {
291 int n;
292
293 if (mbuf_get_space(mb) < 4096) {
294 *err = mbuf_resize(mb, mb->size + 8192);
295 if (*err)
296 return true;
297 }
298
299 ERR_clear_error();
300
301 n = SSL_read(tc->ssl, mbuf_buf(mb), (int)mbuf_get_space(mb));
302 if (n <= 0) {
303 const int ssl_err = SSL_get_error(tc->ssl, n);
304
305 ERR_clear_error();
306
307 switch (ssl_err) {
308
309 case SSL_ERROR_WANT_READ:
310 break;
311
312 case SSL_ERROR_ZERO_RETURN:
313 *err = ECONNRESET;
314 return true;
315
316 default:
317 *err = EPROTO;
318 return true;
319 }
320
321 break;
322 }
323
324 mb->pos += n;
325 }
326
327 mbuf_set_end(mb, mb->pos);
328 mbuf_set_pos(mb, 0);
329
330 return false;
331}
332
333
334static bool send_handler(int *err, struct mbuf *mb, void *arg)
335{
336 struct tls_conn *tc = arg;
337 int r;
338
339 ERR_clear_error();
340
341 r = SSL_write(tc->ssl, mbuf_buf(mb), (int)mbuf_get_left(mb));
342 if (r <= 0) {
343 DEBUG_WARNING("SSL_write: %d\n", SSL_get_error(tc->ssl, r));
344 ERR_clear_error();
345 *err = EPROTO;
346 }
347
348 return true;
349}
350
351
352/**
353 * Start TLS on a TCP-connection
354 *
355 * @param ptc Pointer to allocated TLS connectioon
356 * @param tls TLS Context
357 * @param tcp TCP Connection
358 * @param layer Protocol stack layer
359 *
360 * @return 0 if success, otherwise errorcode
361 */
362int tls_start_tcp(struct tls_conn **ptc, struct tls *tls, struct tcp_conn *tcp,
363 int layer)
364{
365 struct tls_conn *tc;
366 int err;
367
368 if (!ptc || !tls || !tcp)
369 return EINVAL;
370
371 tc = mem_zalloc(sizeof(*tc), destructor);
372 if (!tc)
373 return ENOMEM;
374
375 err = tcp_register_helper(&tc->th, tcp, layer, estab_handler,
376 send_handler, recv_handler, tc);
377 if (err)
378 goto out;
379
380 tc->tcp = mem_ref(tcp);
381
382#ifdef TLS_BIO_OPAQUE
383 tc->biomet = bio_method_tcp();
384 if (!tc->biomet) {
385 err = ENOMEM;
386 goto out;
387 }
388#endif
389
390 err = ENOMEM;
391
392 /* Connect the SSL socket */
393 tc->ssl = SSL_new(tls->ctx);
394 if (!tc->ssl) {
395 DEBUG_WARNING("alloc: SSL_new() failed (ctx=%p)\n", tls->ctx);
396 ERR_clear_error();
397 goto out;
398 }
399
400 tc->sbio_in = BIO_new(BIO_s_mem());
401 if (!tc->sbio_in) {
402 DEBUG_WARNING("alloc: BIO_new() failed\n");
403 ERR_clear_error();
404 goto out;
405 }
406
407
408#ifdef TLS_BIO_OPAQUE
409 tc->sbio_out = BIO_new(tc->biomet);
410#else
411 tc->sbio_out = BIO_new(&bio_tcp_send);
412#endif
413 if (!tc->sbio_out) {
414 DEBUG_WARNING("alloc: BIO_new_socket() failed\n");
415 ERR_clear_error();
416 BIO_free(tc->sbio_in);
417 goto out;
418 }
419
420#ifdef TLS_BIO_OPAQUE
421 BIO_set_data(tc->sbio_out, tc);
422#else
423 tc->sbio_out->ptr = tc;
424#endif
425
426 SSL_set_bio(tc->ssl, tc->sbio_in, tc->sbio_out);
427
428 err = 0;
429
430 out:
431 if (err)
432 mem_deref(tc);
433 else
434 *ptc = tc;
435
436 return err;
437}