The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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  */
25 RCSID("$Id: cb3b6a5bcef930b0d59f712fe48354580f5b3df6 $")
26 USES_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  */
37 struct 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  */
47 static BIO_METHOD *tls_bio_talloc_meth;
48 
49 /** Thread local aggregation BIO
50  */
51 static _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  */
59 static 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  */
79 static 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  */
91 static 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  */
123 static 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  */
172 uint8_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  */
193 char *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  */
215 void 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  */
223 static 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  */
234 fr_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  */
242 fr_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  */
262 BIO *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  */
292 uint8_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  */
305 char *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  */
314 void 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  */
327 static 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  */
342 BIO *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  */
363 int 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  */
394 void 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
#define USES_APPLE_DEPRECATED_API
Definition: build.h:431
#define RCSID(id)
Definition: build.h:444
#define unlikely(_x)
Definition: build.h:378
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:762
static void fr_dbuff_free_talloc(fr_dbuff_t *dbuff)
Free the talloc buffer associated with a dbuff.
Definition: dbuff.h:448
#define fr_dbuff_current(_dbuff_or_marker)
Return the 'current' position of a dbuff or marker.
Definition: dbuff.h:906
#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:1150
#define fr_dbuff_out_memcpy(_out, _dbuff_or_marker, _outlen)
Copy exactly _outlen bytes from the dbuff.
Definition: dbuff.h:1724
#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:738
#define fr_dbuff_in_bytes(_dbuff_or_marker,...)
Copy a byte sequence into a dbuff or marker.
Definition: dbuff.h:1460
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:406
#define fr_dbuff_in_memcpy_partial(_out, _in, _inlen)
Copy at most _inlen bytes into the dbuff.
Definition: dbuff.h:1433
#define FR_DBUFF_BIND_END_ABS(_dbuff_or_marker)
Create a new dbuff pointing to the same underlying buffer.
Definition: dbuff.h:260
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition: debug.h:137
#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:208
static fr_slen_t in
Definition: dict.h:645
talloc_free(reap)
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
unsigned long int size_t
Definition: merged_model.c:25
static fr_bio_t * bio
Definition: radclient-ng.c:86
static char buff[sizeof("18446744073709551615")+3]
Definition: size_tests.c:41
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
init
Enter the EAP-IDENTITY state.
Definition: state_machine.c:90
static size_t char ** out
Definition: value.h:984