The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
bio.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: cb3b6a5bcef930b0d59f712fe48354580f5b3df6 $
19 *
20 * @file tls/bio.c
21 * @brief Custom BIOs to pass to OpenSSL's functions
22 *
23 * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24 */
25RCSID("$Id: cb3b6a5bcef930b0d59f712fe48354580f5b3df6 $")
26USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */
27
28#ifdef WITH_TLS
29#include <freeradius-devel/util/atexit.h>
30
31#include "bio.h"
32
33/** Holds the state of a talloc aggregation 'write' BIO
34 *
35 * With these BIOs OpenSSL is the producer, and we're the consumer.
36 */
37struct fr_tls_bio_dbuff_s {
38 BIO *bio; //!< Logging bio to write to.
39 fr_dbuff_t dbuff_in; //!< dbuff used to write data to our talloced buffer.
40 fr_dbuff_t dbuff_out; //!< dbuff used to read data from our talloced buffer.
41 fr_dbuff_uctx_talloc_t tctx; //!< extra talloc information for the dbuff.
42 bool free_buff; //!< Free the talloced buffer when this structure is freed.
43};
44
45/** Template for the thread local request log BIOs
46 */
47static BIO_METHOD *tls_bio_talloc_meth;
48
49/** Thread local aggregation BIO
50 */
51static _Thread_local fr_tls_bio_dbuff_t *tls_bio_talloc_agg;
52
53/** Aggregates BIO_write() calls into a talloc'd buffer
54 *
55 * @param[in] bio that was written to.
56 * @param[in] in data being written to BIO.
57 * @param[in] len Length of data being written.
58 */
59static int _tls_bio_talloc_write_cb(BIO *bio, char const *in, int len)
60{
61 fr_tls_bio_dbuff_t *bd = talloc_get_type_abort(BIO_get_data(bio), fr_tls_bio_dbuff_t);
62
63 fr_assert_msg(bd->dbuff_in.buff, "BIO not initialised");
64
65 /*
66 * Shift out any data which has been read
67 * since we were last called.
68 */
69 fr_dbuff_shift(&bd->dbuff_in, fr_dbuff_used(&bd->dbuff_out));
70
71 return fr_dbuff_in_memcpy_partial(&bd->dbuff_in, (uint8_t const *)in, (size_t)len);
72}
73
74/** Aggregates BIO_puts() calls into a talloc'd buffer
75 *
76 * @param[in] bio that was written to.
77 * @param[in] in data being written to BIO.
78 */
79static int _tls_bio_talloc_puts_cb(BIO *bio, char const *in)
80{
81 return _tls_bio_talloc_write_cb(bio, in, strlen(in));
82}
83
84/** Serves BIO_read() from a talloced buffer
85 *
86 * @param[in] bio performing the read operation.
87 * @param[out] buf to write data to.
88 * @param[in] size of data to write (maximum).
89 * @return The amount of data written.
90 */
91static int _tls_bio_talloc_read_cb(BIO *bio, char *buf, int size)
92{
93 fr_tls_bio_dbuff_t *bd = talloc_get_type_abort(BIO_get_data(bio), fr_tls_bio_dbuff_t);
94 size_t to_copy;
95 ssize_t slen;
96
97 fr_assert_msg(bd->dbuff_out.buff, "BIO not initialised");
98
99 to_copy = fr_dbuff_remaining(&bd->dbuff_out);
100 if (to_copy > (size_t)size) to_copy = (size_t)size;
101
102 slen = fr_dbuff_out_memcpy((uint8_t *)buf, &bd->dbuff_out, to_copy);
103 if (!fr_cond_assert(slen >= 0)) { /* Shouldn't happen */
104 buf[0] = '\0';
105 return (int)slen;
106 }
107
108 fr_dbuff_shift(&bd->dbuff_in, (size_t)slen); /* Shift contents */
109
110 return (int)slen;
111}
112
113/** Serves BIO_gets() from a talloced buffer
114 *
115 * Writes all data up to size, or up to and including the next \n to
116 * the provided buffer.
117 *
118 * @param[in] bio performing the gets operation.
119 * @param[out] buf to write data to.
120 * @param[in] size of data to write (maximum).
121 * @return The amount of data written.
122 */
123static int _tls_bio_talloc_gets_cb(BIO *bio, char *buf, int size)
124{
125 fr_tls_bio_dbuff_t *bd = talloc_get_type_abort(BIO_get_data(bio), fr_tls_bio_dbuff_t);
126 size_t to_copy;
127 uint8_t *p;
128 ssize_t slen;
129
130 fr_assert_msg(bd->dbuff_out.buff, "BIO not initialised");
131
132 /*
133 * Deal with stupid corner case
134 */
135 if (unlikely(size == 1)) {
136 buf[0] = '\0';
137 return 0;
138 } else if (unlikely(size == 0)) {
139 return 0;
140 }
141
142 /*
143 * Copy up to the next line, or the end of the buffer
144 */
145 p = memchr(fr_dbuff_current(&bd->dbuff_out), '\n', fr_dbuff_remaining(&bd->dbuff_out));
146 if (!p) {
147 to_copy = fr_dbuff_remaining(&bd->dbuff_out);
148 } else {
149 to_copy = (p - fr_dbuff_current(&bd->dbuff_out)) + 1; /* Preserve the \n as per BIO_read() man page */
150 }
151
152 if (to_copy >= (size_t)size) to_copy = (size_t)size - 1; /* Space for \0 */
153
154 slen = fr_dbuff_out_memcpy((uint8_t *)buf, &bd->dbuff_out, to_copy);
155 if (!fr_cond_assert(slen > 0)) { /* Shouldn't happen */
156 buf[0] = '\0';
157 return (int)slen;
158 }
159
160 buf[to_copy] = '\0';
161 fr_dbuff_shift(&bd->dbuff_in, (size_t)slen); /* Shift contents */
162
163 return (int)to_copy;
164}
165
166/** Finalise a talloc aggregation buffer, returning the underlying talloc array holding the data
167 *
168 * @return
169 * - NULL if the aggregation buffer wasn't initialised.
170 * - A talloc_array holding the aggregated data.
171 */
172uint8_t *fr_tls_bio_dbuff_finalise(fr_tls_bio_dbuff_t *bd)
173{
174 uint8_t *buff;
175
176 if (unlikely(!bd)) return NULL;
177 if (unlikely(!bd->dbuff_in.buff)) return NULL;
178
179 fr_dbuff_trim_talloc(&bd->dbuff_in, SIZE_MAX);
180
181 buff = bd->dbuff_in.buff;
182 bd->dbuff_in.buff = NULL;
183 bd->dbuff_out.buff = NULL;
184 return buff;
185}
186
187/** Finalise a talloc aggregation buffer, returning the underlying talloc array holding the data
188 *
189 * @return
190 * - NULL if the aggregation buffer wasn't initialised.
191 * - A talloc_array holding the aggregated data.
192 */
193char *fr_tls_bio_dbuff_finalise_bstr(fr_tls_bio_dbuff_t *bd)
194{
195 uint8_t *buff;
196
197 if (unlikely(!bd)) return NULL;
198 if (unlikely(!bd->dbuff_in.buff)) return NULL;
199
200 fr_dbuff_in_bytes(&bd->dbuff_in, 0x00);
201 fr_dbuff_trim_talloc(&bd->dbuff_in, SIZE_MAX);
202
203 buff = bd->dbuff_in.buff;
204 bd->dbuff_in.buff = NULL;
205 bd->dbuff_out.buff = NULL;
206 talloc_set_type(buff, char);
207
208 return (char *)buff;
209}
210
211/* Reset pointer positions for in/out
212 *
213 * Leaves the underlying buffer intact to avoid useless free/malloc.
214 */
215void fr_tls_bio_dbuff_reset(fr_tls_bio_dbuff_t *bd)
216{
217 fr_dbuff_set_to_start(&bd->dbuff_in);
218}
219
220/** Free the underlying BIO, and the buffer if it wasn't finalised
221 *
222 */
223static int _fr_tls_bio_dbuff_free(fr_tls_bio_dbuff_t *bd)
224{
225 BIO_free(bd->bio);
226 if (bd->free_buff) fr_dbuff_free_talloc(&bd->dbuff_out);
227
228 return 0;
229}
230
231/** Return the output dbuff
232 *
233 */
234fr_dbuff_t *fr_tls_bio_dbuff_out(fr_tls_bio_dbuff_t *bd)
235{
236 return &bd->dbuff_out;
237}
238
239/** Return the input dbuff
240 *
241 */
242fr_dbuff_t *fr_tls_bio_dbuff_in(fr_tls_bio_dbuff_t *bd)
243{
244 return &bd->dbuff_in;
245}
246
247/** Allocate a new BIO/talloc buffer
248 *
249 * @param[out] out Where to write a pointer to the #fr_tls_bio_dbuff_t.
250 * When this structure is freed the underlying BIO *
251 * will also be freed. May be NULL.
252 * @param[in] bio_ctx to allocate the BIO and wrapper struct in. May be NULL.
253 * @param[in] buff_ctx to allocate the expanding buffer in. May be NULL.
254 * @param[in] init how much memory to allocate initially.
255 * @param[in] max the maximum amount of memory to allocate (0 for unlimited).
256 * @param[in] free_buff free the talloced buffer when the #fr_tls_bio_dbuff_t is
257 * freed.
258 * @return
259 * - A new BIO - Do not free manually, free the #fr_tls_bio_dbuff_t or
260 * the ctx containing it instead.
261 */
262BIO *fr_tls_bio_dbuff_alloc(fr_tls_bio_dbuff_t **out, TALLOC_CTX *bio_ctx, TALLOC_CTX *buff_ctx,
263 size_t init, size_t max, bool free_buff)
264{
265 fr_tls_bio_dbuff_t *bd;
266
267 MEM(bd = talloc_zero(bio_ctx, fr_tls_bio_dbuff_t));
268 MEM(bd->bio = BIO_new(tls_bio_talloc_meth));
269 BIO_set_data(bd->bio, bd);
270
271 /*
272 * Initialise the dbuffs
273 */
274 MEM(fr_dbuff_init_talloc(buff_ctx, &bd->dbuff_out, &bd->tctx, init, max)); /* Where we read from */
275 bd->dbuff_in = FR_DBUFF_BIND_END_ABS(&bd->dbuff_out); /* Where we write to */
276 bd->dbuff_out.is_const = 1;
277 bd->free_buff = free_buff;
278
279 talloc_set_destructor(bd, _fr_tls_bio_dbuff_free);
280
281 if (out) *out = bd;
282
283 return bd->bio;
284}
285
286/** Finalise a talloc aggregation buffer, returning the underlying talloc array holding the data
287 *
288 * @return
289 * - NULL if the aggregation buffer wasn't initialised.
290 * - A talloc_array holding the aggregated data.
291 */
292uint8_t *fr_tls_bio_dbuff_thread_local_finalise(void)
293{
294 return fr_tls_bio_dbuff_finalise(tls_bio_talloc_agg);
295}
296
297/** Finalise a talloc aggregation buffer, returning the underlying talloc array holding the data
298 *
299 * This variant adds an additional \0 byte, and sets the talloc chunk type to char.
300 *
301 * @return
302 * - NULL if the aggregation buffer wasn't initialised.
303 * - A talloc_array holding the aggregated data.
304 */
305char *fr_tls_bio_dbuff_thread_local_finalise_bstr(void)
306{
307 return fr_tls_bio_dbuff_finalise_bstr(tls_bio_talloc_agg);
308}
309
310/** Discard any data in a talloc aggregation buffer
311 *
312 * fr_tls_bio_dbuff_thread_local must be called again before using the BIO
313 */
314void fr_tls_bio_dbuff_thread_local_clear(void)
315{
316 fr_tls_bio_dbuff_t *bd = tls_bio_talloc_agg;
317
318 if (unlikely(!bd)) return;
319 if (unlikely(!bd->dbuff_in.buff)) return;
320
321 fr_dbuff_free_talloc(&bd->dbuff_in);
322}
323
324/** Frees the thread local TALLOC bio and its underlying OpenSSL BIO *
325 *
326 */
327static int _fr_tls_bio_dbuff_thread_local_free(void *bio_talloc_agg)
328{
329 fr_tls_bio_dbuff_t *our_bio_talloc_agg = talloc_get_type_abort(bio_talloc_agg, fr_tls_bio_dbuff_t);
330
331 return talloc_free(our_bio_talloc_agg); /* Frees the #fr_tls_bio_dbuff_t and BIO */
332}
333
334/** Return a BIO which will aggregate data in an expandable talloc buffer
335 *
336 * @note Only one of these BIOs may be in use at a given time.
337 *
338 * @param[in] init how much memory to allocate initially.
339 * @param[in] max the maximum amount of memory to allocate (0 for unlimited).
340 * @return A thread local BIO to pass to OpenSSL logging functions.
341 */
342BIO *fr_tls_bio_dbuff_thread_local(TALLOC_CTX *ctx, size_t init, size_t max)
343{
344 fr_tls_bio_dbuff_t *bd = tls_bio_talloc_agg;
345
346 if (unlikely(!bd)) {
347 fr_tls_bio_dbuff_alloc(&bd, NULL, ctx, init, max, true);
348 fr_atexit_thread_local(tls_bio_talloc_agg, _fr_tls_bio_dbuff_thread_local_free, bd);
349
350 return bd->bio;
351 }
352
353 fr_assert_msg(!tls_bio_talloc_agg->dbuff_out.buff, "BIO not finalised");
354 MEM(fr_dbuff_init_talloc(ctx, &bd->dbuff_out, &bd->tctx, init, max)); /* Where we read from */
355 bd->dbuff_in = FR_DBUFF_BIND_END_ABS(&bd->dbuff_out); /* Where we write to */
356
357 return tls_bio_talloc_agg->bio;
358}
359
360/** Initialise the BIO logging meths which are used to create thread local logging BIOs
361 *
362 */
363int fr_tls_bio_init(void)
364{
365 /*
366 * As per the boringSSL documentation
367 *
368 * BIO_TYPE_START is the first user-allocated |BIO| type.
369 * No pre-defined type, flag bits aside, may exceed this
370 * value.
371 *
372 * The low byte here defines the BIO ID, and the high byte
373 * defines its capabilities.
374 */
375 tls_bio_talloc_meth = BIO_meth_new(BIO_get_new_index() | BIO_TYPE_SOURCE_SINK, "fr_tls_bio_dbuff_t");
376 if (unlikely(!tls_bio_talloc_meth)) return -1;
377
378 /*
379 * If BIO_meth_set_create is ever used here be sure to call
380 * BIO_set_init(bio, 1); in the create callbacks else all
381 * operations on the BIO will fail.
382 */
383 BIO_meth_set_write(tls_bio_talloc_meth, _tls_bio_talloc_write_cb);
384 BIO_meth_set_puts(tls_bio_talloc_meth, _tls_bio_talloc_puts_cb);
385 BIO_meth_set_read(tls_bio_talloc_meth, _tls_bio_talloc_read_cb);
386 BIO_meth_set_gets(tls_bio_talloc_meth, _tls_bio_talloc_gets_cb);
387
388 return 0;
389}
390
391/** Free the global log method templates
392 *
393 */
394void fr_tls_bio_free(void)
395{
396 if (tls_bio_talloc_meth) {
397 BIO_meth_free(tls_bio_talloc_meth);
398 tls_bio_talloc_meth = NULL;
399 }
400}
401#endif /* WITH_TLS */
#define fr_atexit_thread_local(_name, _free, _uctx)
Definition atexit.h:221
static bool init
Definition fuzzer.c:41
#define USES_APPLE_DEPRECATED_API
Definition build.h:470
#define RCSID(id)
Definition build.h:483
#define unlikely(_x)
Definition build.h:381
int fr_dbuff_trim_talloc(fr_dbuff_t *dbuff, size_t len)
Trim a talloced dbuff to the minimum length required to represent the contained string.
Definition dbuff.c:297
size_t fr_dbuff_shift(fr_dbuff_t *dbuff, size_t shift)
Shift the contents of the dbuff, returning the number of bytes we managed to shift.
Definition dbuff.c:116
#define fr_dbuff_used(_dbuff_or_marker)
Return the number of bytes remaining between the start of the dbuff or marker and the current positio...
Definition dbuff.h:767
static void fr_dbuff_free_talloc(fr_dbuff_t *dbuff)
Free the talloc buffer associated with a dbuff.
Definition dbuff.h:453
#define fr_dbuff_current(_dbuff_or_marker)
Return the 'current' position of a dbuff or marker.
Definition dbuff.h:911
#define fr_dbuff_set_to_start(_dbuff_or_marker)
Reset the 'current' position of the dbuff or marker to the 'start' of the buffer.
Definition dbuff.h:1155
#define fr_dbuff_out_memcpy(_out, _dbuff_or_marker, _outlen)
Copy exactly _outlen bytes from the dbuff.
Definition dbuff.h:1732
#define fr_dbuff_remaining(_dbuff_or_marker)
Return the number of bytes remaining between the dbuff or marker and the end of the buffer.
Definition dbuff.h:743
#define fr_dbuff_in_bytes(_dbuff_or_marker,...)
Copy a byte sequence into a dbuff or marker.
Definition dbuff.h:1465
#define fr_dbuff_in_memcpy_partial(_out, _in, _inlen)
Copy at most _inlen bytes into the dbuff.
Definition dbuff.h:1438
#define FR_DBUFF_BIND_END_ABS(_dbuff_or_marker)
Create a new dbuff pointing to the same underlying buffer.
Definition dbuff.h:260
static fr_dbuff_t * fr_dbuff_init_talloc(TALLOC_CTX *ctx, fr_dbuff_t *dbuff, fr_dbuff_uctx_talloc_t *tctx, size_t init, size_t max)
Initialise a special dbuff which automatically extends as additional data is written.
Definition dbuff.h:411
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:139
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:210
#define MEM(x)
Definition debug.h:36
static fr_slen_t in
Definition dict.h:824
talloc_free(reap)
long int ssize_t
unsigned char uint8_t
unsigned long int size_t
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:41
static size_t char ** out
Definition value.h:997