The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
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: af93cb570680693f1130b78c776bf4e3922bab1c $
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: af93cb570680693f1130b78c776bf4e3922bab1c $")
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/** Detach both dbuffs from the shared backing buffer
167 *
168 * The in/out dbuffs are bound to the same talloc buffer, so when the buffer
169 * is freed, or ownership is handed to a caller, both dbuffs must forget the
170 * buffer together. A stale sibling pointer double-frees in the destructor,
171 * or trips the "BIO not finalised" assert on reuse.
172 */
173static inline CC_HINT(always_inline) void tls_bio_dbuff_detach(fr_tls_bio_dbuff_t *bd)
174{
175 bd->dbuff_in.buff = NULL;
176 bd->dbuff_out.buff = NULL;
177}
178
179/** Finalise a talloc aggregation buffer, returning the underlying talloc array holding the data
180 *
181 * @return
182 * - NULL if the aggregation buffer wasn't initialised.
183 * - A talloc_array holding the aggregated data.
184 */
185uint8_t *fr_tls_bio_dbuff_finalise(fr_tls_bio_dbuff_t *bd)
186{
187 uint8_t *buff;
188
189 if (unlikely(!bd)) return NULL;
190 if (unlikely(!bd->dbuff_in.buff)) return NULL;
191
192 fr_dbuff_trim_talloc(&bd->dbuff_in, SIZE_MAX);
193
194 buff = bd->dbuff_in.buff;
195 tls_bio_dbuff_detach(bd);
196 return buff;
197}
198
199/** Finalise a talloc aggregation buffer, returning the underlying talloc array holding the data
200 *
201 * @return
202 * - NULL if the aggregation buffer wasn't initialised.
203 * - A talloc_array holding the aggregated data.
204 */
205char *fr_tls_bio_dbuff_finalise_bstr(fr_tls_bio_dbuff_t *bd)
206{
207 uint8_t *buff;
208
209 if (unlikely(!bd)) return NULL;
210 if (unlikely(!bd->dbuff_in.buff)) return NULL;
211
212 if (fr_dbuff_in_bytes(&bd->dbuff_in, 0x00) <= 0) return NULL;
213 fr_dbuff_trim_talloc(&bd->dbuff_in, SIZE_MAX);
214
215 buff = bd->dbuff_in.buff;
216 tls_bio_dbuff_detach(bd);
217 talloc_set_type(buff, char);
218
219 return (char *)buff;
220}
221
222/* Reset pointer positions for in/out
223 *
224 * Leaves the underlying buffer intact to avoid useless free/malloc.
225 */
226void fr_tls_bio_dbuff_reset(fr_tls_bio_dbuff_t *bd)
227{
228 fr_dbuff_set_to_start(&bd->dbuff_in);
229}
230
231/** Free the underlying BIO, and the buffer if it wasn't finalised
232 *
233 */
234static int _fr_tls_bio_dbuff_free(fr_tls_bio_dbuff_t *bd)
235{
236 BIO_free(bd->bio);
237 if (bd->free_buff) fr_dbuff_free_talloc(&bd->dbuff_out);
238
239 return 0;
240}
241
242/** Return the output dbuff
243 *
244 */
245fr_dbuff_t *fr_tls_bio_dbuff_out(fr_tls_bio_dbuff_t *bd)
246{
247 return &bd->dbuff_out;
248}
249
250/** Return the input dbuff
251 *
252 */
253fr_dbuff_t *fr_tls_bio_dbuff_in(fr_tls_bio_dbuff_t *bd)
254{
255 return &bd->dbuff_in;
256}
257
258/** Allocate a new BIO/talloc buffer
259 *
260 * @param[out] out Where to write a pointer to the #fr_tls_bio_dbuff_t.
261 * When this structure is freed the underlying BIO *
262 * will also be freed. May be NULL.
263 * @param[in] bio_ctx to allocate the BIO and wrapper struct in. May be NULL.
264 * @param[in] buff_ctx to allocate the expanding buffer in. May be NULL.
265 * @param[in] init how much memory to allocate initially.
266 * @param[in] max the maximum amount of memory to allocate (0 for unlimited).
267 * @param[in] free_buff free the talloced buffer when the #fr_tls_bio_dbuff_t is
268 * freed.
269 * @return
270 * - A new BIO - Do not free manually, free the #fr_tls_bio_dbuff_t or
271 * the ctx containing it instead.
272 */
273BIO *fr_tls_bio_dbuff_alloc(fr_tls_bio_dbuff_t **out, TALLOC_CTX *bio_ctx, TALLOC_CTX *buff_ctx,
274 size_t init, size_t max, bool free_buff)
275{
276 fr_tls_bio_dbuff_t *bd;
277
278 MEM(bd = talloc_zero(bio_ctx, fr_tls_bio_dbuff_t));
279 MEM(bd->bio = BIO_new(tls_bio_talloc_meth));
280 BIO_set_data(bd->bio, bd);
281
282 /*
283 * Initialise the dbuffs
284 */
285 MEM(fr_dbuff_init_talloc(buff_ctx, &bd->dbuff_out, &bd->tctx, init, max)); /* Where we read from */
286 bd->dbuff_in = FR_DBUFF_BIND_END_ABS(&bd->dbuff_out); /* Where we write to */
287 bd->dbuff_out.is_const = 1;
288 bd->free_buff = free_buff;
289
290 talloc_set_destructor(bd, _fr_tls_bio_dbuff_free);
291
292 if (out) *out = bd;
293
294 return bd->bio;
295}
296
297/** Finalise a talloc aggregation buffer, returning the underlying talloc array holding the data
298 *
299 * @return
300 * - NULL if the aggregation buffer wasn't initialised.
301 * - A talloc_array holding the aggregated data.
302 */
303uint8_t *fr_tls_bio_dbuff_thread_local_finalise(void)
304{
305 return fr_tls_bio_dbuff_finalise(tls_bio_talloc_agg);
306}
307
308/** Finalise a talloc aggregation buffer, returning the underlying talloc array holding the data
309 *
310 * This variant adds an additional \0 byte, and sets the talloc chunk type to char.
311 *
312 * @return
313 * - NULL if the aggregation buffer wasn't initialised.
314 * - A talloc_array holding the aggregated data.
315 */
316char *fr_tls_bio_dbuff_thread_local_finalise_bstr(void)
317{
318 return fr_tls_bio_dbuff_finalise_bstr(tls_bio_talloc_agg);
319}
320
321/** Discard any data in a talloc aggregation buffer
322 *
323 * fr_tls_bio_dbuff_thread_local must be called again before using the BIO
324 */
325void fr_tls_bio_dbuff_thread_local_clear(void)
326{
327 fr_tls_bio_dbuff_t *bd = tls_bio_talloc_agg;
328
329 if (unlikely(!bd)) return;
330 if (unlikely(!bd->dbuff_in.buff)) return;
331
332 fr_dbuff_free_talloc(&bd->dbuff_in);
333 tls_bio_dbuff_detach(bd);
334}
335
336/** Frees the thread local TALLOC bio and its underlying OpenSSL BIO *
337 *
338 */
339static int _fr_tls_bio_dbuff_thread_local_free(void *bio_talloc_agg)
340{
341 fr_tls_bio_dbuff_t *our_bio_talloc_agg = talloc_get_type_abort(bio_talloc_agg, fr_tls_bio_dbuff_t);
342
343 return talloc_free(our_bio_talloc_agg); /* Frees the #fr_tls_bio_dbuff_t and BIO */
344}
345
346/** Return a BIO which will aggregate data in an expandable talloc buffer
347 *
348 * @note Only one of these BIOs may be in use at a given time.
349 *
350 * @param[in] init how much memory to allocate initially.
351 * @param[in] max the maximum amount of memory to allocate (0 for unlimited).
352 * @return A thread local BIO to pass to OpenSSL logging functions.
353 */
354BIO *fr_tls_bio_dbuff_thread_local(TALLOC_CTX *ctx, size_t init, size_t max)
355{
356 fr_tls_bio_dbuff_t *bd = tls_bio_talloc_agg;
357
358 if (unlikely(!bd)) {
359 fr_tls_bio_dbuff_alloc(&bd, NULL, ctx, init, max, true);
360 fr_atexit_thread_local(tls_bio_talloc_agg, _fr_tls_bio_dbuff_thread_local_free, bd);
361
362 return bd->bio;
363 }
364
365 fr_assert_msg(!tls_bio_talloc_agg->dbuff_out.buff, "BIO not finalised");
366 MEM(fr_dbuff_init_talloc(ctx, &bd->dbuff_out, &bd->tctx, init, max)); /* Where we read from */
367 bd->dbuff_in = FR_DBUFF_BIND_END_ABS(&bd->dbuff_out); /* Where we write to */
368
369 return tls_bio_talloc_agg->bio;
370}
371
372/** Initialise the BIO logging meths which are used to create thread local logging BIOs
373 *
374 */
375int fr_tls_bio_init(void)
376{
377 /*
378 * As per the boringSSL documentation
379 *
380 * BIO_TYPE_START is the first user-allocated |BIO| type.
381 * No pre-defined type, flag bits aside, may exceed this
382 * value.
383 *
384 * The low byte here defines the BIO ID, and the high byte
385 * defines its capabilities.
386 */
387 tls_bio_talloc_meth = BIO_meth_new(BIO_get_new_index() | BIO_TYPE_SOURCE_SINK, "fr_tls_bio_dbuff_t");
388 if (unlikely(!tls_bio_talloc_meth)) return -1;
389
390 /*
391 * If BIO_meth_set_create is ever used here be sure to call
392 * BIO_set_init(bio, 1); in the create callbacks else all
393 * operations on the BIO will fail.
394 */
395 BIO_meth_set_write(tls_bio_talloc_meth, _tls_bio_talloc_write_cb);
396 BIO_meth_set_puts(tls_bio_talloc_meth, _tls_bio_talloc_puts_cb);
397 BIO_meth_set_read(tls_bio_talloc_meth, _tls_bio_talloc_read_cb);
398 BIO_meth_set_gets(tls_bio_talloc_meth, _tls_bio_talloc_gets_cb);
399
400 return 0;
401}
402
403/** Free the global log method templates
404 *
405 */
406void fr_tls_bio_free(void)
407{
408 if (tls_bio_talloc_meth) {
409 BIO_meth_free(tls_bio_talloc_meth);
410 tls_bio_talloc_meth = NULL;
411 }
412}
413#endif /* WITH_TLS */
#define _Thread_local
Definition atexit.h:213
#define fr_atexit_thread_local(_name, _free, _uctx)
Definition atexit.h:224
#define USES_APPLE_DEPRECATED_API
Definition build.h:547
#define RCSID(id)
Definition build.h:560
#define unlikely(_x)
Definition build.h:455
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:775
static void fr_dbuff_free_talloc(fr_dbuff_t *dbuff)
Free the talloc buffer associated with a dbuff.
Definition dbuff.h:461
#define fr_dbuff_current(_dbuff_or_marker)
Return the 'current' position of a dbuff or marker.
Definition dbuff.h:919
#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:1160
#define fr_dbuff_out_memcpy(_out, _dbuff_or_marker, _outlen)
Copy exactly _outlen bytes from the dbuff.
Definition dbuff.h:1737
#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:751
#define fr_dbuff_in_bytes(_dbuff_or_marker,...)
Copy a byte sequence into a dbuff or marker.
Definition dbuff.h:1470
#define fr_dbuff_in_memcpy_partial(_out, _in, _inlen)
Copy at most _inlen bytes into the dbuff.
Definition dbuff.h:1443
#define FR_DBUFF_BIND_END_ABS(_dbuff_or_marker)
Create a new dbuff pointing to the same underlying buffer.
Definition dbuff.h:268
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:419
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:131
#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:202
#define MEM(x)
Definition debug.h:36
static fr_slen_t in
Definition dict.h:882
talloc_free(hp)
long int ssize_t
unsigned char uint8_t
unsigned long int size_t
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:37
init
Enter the EAP-IDENTITY state.
static size_t char ** out
Definition value.h:1030