/src/freeradius-server/src/lib/tls/log.c
Line | Count | Source |
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: f2a4bedfee68dbe3fc16c8f57f824770de295f0e $ |
19 | | * |
20 | | * @file tls/log.c |
21 | | * @brief Retrieve errors and log messages from OpenSSL's overly complex logging system. |
22 | | * |
23 | | * @copyright 2016,2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org) |
24 | | * @copyright 2016 The FreeRADIUS server project |
25 | | */ |
26 | | RCSID("$Id: f2a4bedfee68dbe3fc16c8f57f824770de295f0e $") |
27 | | USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */ |
28 | | |
29 | | #ifdef WITH_TLS |
30 | 0 | #define LOG_PREFIX "tls" |
31 | | |
32 | | #include <freeradius-devel/util/debug.h> |
33 | | #include <freeradius-devel/util/atexit.h> |
34 | | #include <freeradius-devel/tls/strerror.h> |
35 | | #include <freeradius-devel/tls/utils.h> |
36 | | |
37 | | #include "log.h" |
38 | | |
39 | | /** Holds the state of a log BIO |
40 | | * |
41 | | * Most of these fields are expected to change between uses of the BIO. |
42 | | * |
43 | | * BIOs do not have indexed extension structures like other structures in OpenSSL, |
44 | | * so we're forced to place all information in a structure, and populate it just |
45 | | * prior to a BIO being used. |
46 | | * |
47 | | * These BIOs are thread local to avoid conflicts or locking issues. |
48 | | */ |
49 | | typedef struct { |
50 | | BIO *bio; //!< Logging bio to write to. |
51 | | fr_sbuff_t sbuff; //!< Used to aggregate line data. |
52 | | fr_sbuff_uctx_talloc_t tctx; //!< extra talloc information for the sbuff. |
53 | | fr_sbuff_marker_t logged_m; //!< How much data has been written. |
54 | | |
55 | | request_t *request; //!< the current request. Only used for the |
56 | | ///< request log BIOs. |
57 | | fr_log_type_t type; //!< The type of log messages the bio will produce. |
58 | | fr_log_lvl_t lvl; //!< Level to log message at. |
59 | | char const *file; //!< File this log bio was bound on. |
60 | | int line; //!< Line this log bio was bound on. |
61 | | } fr_tls_log_bio_t; |
62 | | |
63 | | /** Template for the thread local request log BIOs |
64 | | */ |
65 | | static BIO_METHOD *tls_request_log_meth; |
66 | | |
67 | | /** Template for the global log BIOs |
68 | | */ |
69 | | static BIO_METHOD *tls_global_log_meth; |
70 | | |
71 | | /** Counter for users of the request log bio |
72 | | * |
73 | | */ |
74 | | static _Atomic(uint32_t) tls_request_log_ref; |
75 | | |
76 | | /** Counter for users of the global log bio |
77 | | * |
78 | | */ |
79 | | static _Atomic(uint32_t) tls_global_log_ref; |
80 | | |
81 | | /** Thread local request log BIO |
82 | | */ |
83 | | static _Thread_local fr_tls_log_bio_t *request_log_bio; |
84 | | |
85 | | /** Thread local global log BIO |
86 | | */ |
87 | | static _Thread_local fr_tls_log_bio_t *global_log_bio; |
88 | | |
89 | | /** Print out the current stack of certs |
90 | | * |
91 | | * @param[in] file File where this function is being called. |
92 | | * @param[in] line Line where this function is being called. |
93 | | * @param[in] request Current request, may be NULL. |
94 | | * @param[in] log_type The type of log message to produce L_INFO, L_ERR, L_DBG etc... |
95 | | * @param[in] chain The certificate chain. |
96 | | * @param[in] cert The leaf certificate. |
97 | | */ |
98 | | void _fr_tls_chain_log(char const *file, int line, |
99 | | request_t *request, fr_log_type_t log_type, |
100 | | STACK_OF(X509) *chain, X509 *cert) |
101 | 0 | { |
102 | | /* |
103 | | * Dump to the thread local buffer |
104 | | */ |
105 | 0 | fr_strerror_clear(); |
106 | 0 | _fr_tls_strerror_push_chain(file, line, chain, cert); |
107 | 0 | if (request) { |
108 | 0 | log_request_perror(log_type, L_DBG_LVL_OFF, request, file, line, NULL); |
109 | 0 | } else { |
110 | 0 | fr_perror(NULL); |
111 | 0 | } |
112 | 0 | } |
113 | | |
114 | | /** Print out the current stack of certs |
115 | | * |
116 | | * @param[in] file File where this function is being called. |
117 | | * @param[in] line Line where this function is being called. |
118 | | * @param[in] request Current request, may be NULL. |
119 | | * @param[in] log_type The type of log message to produce L_INFO, L_ERR, L_DBG etc... |
120 | | * @param[in] chain The certificate chain. |
121 | | * @param[in] cert The leaf certificate. |
122 | | * @param[in] marker The certificate we want to mark. |
123 | | */ |
124 | | void _fr_tls_chain_marker_log(char const *file, int line, |
125 | | request_t *request, fr_log_type_t log_type, |
126 | | STACK_OF(X509) *chain, X509 *cert, X509 *marker) |
127 | 0 | { |
128 | | /* |
129 | | * Dump to the thread local buffer |
130 | | */ |
131 | 0 | fr_strerror_clear(); |
132 | 0 | _fr_tls_strerror_push_chain_marker(file, line, chain, cert, marker); |
133 | 0 | if (request) { |
134 | 0 | log_request_perror(log_type, L_DBG_LVL_OFF, request, file, line, NULL); |
135 | 0 | } else { |
136 | 0 | fr_perror(NULL); |
137 | 0 | } |
138 | 0 | } |
139 | | |
140 | | /** Print out the current stack of X509 objects (certificates only) |
141 | | * |
142 | | * @param[in] file File where this function is being called. |
143 | | * @param[in] line Line where this function is being called. |
144 | | * @param[in] request Current request, may be NULL. |
145 | | * @param[in] log_type The type of log message to produce L_INFO, L_ERR, L_DBG etc... |
146 | | * @param[in] objects A stack of X509 objects |
147 | | */ |
148 | | void _fr_tls_x509_objects_log(char const *file, int line, |
149 | | request_t *request, fr_log_type_t log_type, |
150 | | STACK_OF(X509_OBJECT) *objects) |
151 | 0 | { |
152 | |
|
153 | 0 | fr_strerror_clear(); |
154 | 0 | _fr_tls_strerror_push_x509_objects(file, line, objects); |
155 | 0 | if (request) { |
156 | 0 | log_request_perror(log_type, L_DBG_LVL_OFF, request, file, line, NULL); |
157 | 0 | } else { |
158 | 0 | fr_perror(NULL); |
159 | 0 | } |
160 | 0 | } |
161 | | |
162 | | /** Print errors raised by OpenSSL I/O functions |
163 | | * |
164 | | * Drains the thread local OpenSSL error queue, and prints out errors |
165 | | * based on the SSL handle and the return code of the I/O function. |
166 | | * |
167 | | * OpenSSL lists I/O functions to be: |
168 | | * - SSL_connect |
169 | | * - SSL_accept |
170 | | * - SSL_do_handshake |
171 | | * - SSL_read |
172 | | * - SSL_peek |
173 | | * - SSL_write |
174 | | * |
175 | | * @param request The current request (may be NULL). |
176 | | * @param err returned from SSL_get_error(). |
177 | | * @param fmt Error message describing the operation being attempted. |
178 | | * @param ... Arguments for msg. |
179 | | * @return |
180 | | * - 0 TLS session may still be viable. |
181 | | * - -1 TLS session cannot continue. |
182 | | */ |
183 | | int fr_tls_log_io_error(request_t *request, int err, char const *fmt, ...) |
184 | 0 | { |
185 | 0 | static fr_table_num_ordered_t const ssl_io_error_table[] = { |
186 | 0 | { L("SSL_ERROR_NONE"), SSL_ERROR_NONE }, |
187 | 0 | { L("SSL_ERROR_ZERO_RETURN"), SSL_ERROR_ZERO_RETURN }, |
188 | 0 | { L("SSL_ERROR_WANT_READ"), SSL_ERROR_WANT_READ }, |
189 | 0 | { L("SSL_ERROR_WANT_WRITE"), SSL_ERROR_WANT_WRITE }, |
190 | 0 | { L("SSL_ERROR_WANT_CONNECT"), SSL_ERROR_WANT_CONNECT }, |
191 | 0 | { L("SSL_ERROR_WANT_ACCEPT"), SSL_ERROR_WANT_ACCEPT }, |
192 | 0 | { L("SSL_ERROR_WANT_X509_LOOKUP"), SSL_ERROR_WANT_X509_LOOKUP }, |
193 | 0 | { L("SSL_ERROR_WANT_ASYNC"), SSL_ERROR_WANT_ASYNC }, |
194 | 0 | { L("SSL_ERROR_WANT_ASYNC_JOB"), SSL_ERROR_WANT_ASYNC_JOB }, |
195 | 0 | { L("SSL_ERROR_WANT_CLIENT_HELLO_CB"), SSL_ERROR_WANT_CLIENT_HELLO_CB }, |
196 | 0 | { L("SSL_ERROR_SYSCALL"), SSL_ERROR_SYSCALL }, |
197 | 0 | { L("SSL_ERROR_SSL"), SSL_ERROR_SSL } |
198 | 0 | }; |
199 | 0 | static size_t ssl_io_error_table_len = NUM_ELEMENTS(ssl_io_error_table); |
200 | |
|
201 | 0 | va_list ap; |
202 | 0 | char *msg = NULL; |
203 | |
|
204 | 0 | switch (err) { |
205 | | /* |
206 | | * These seem to be harmless and already "dealt |
207 | | * with" by our non-blocking environment. NB: |
208 | | * "ZERO_RETURN" is the clean "error" |
209 | | * indicating a successfully closed SSL |
210 | | * tunnel. We let this happen because our IO |
211 | | * loop should not appear to have broken on |
212 | | * this condition - and outside the IO loop, the |
213 | | * "shutdown" state is checked. |
214 | | * |
215 | | * Don't print anything if we ignore the error. |
216 | | */ |
217 | 0 | case SSL_ERROR_NONE: |
218 | 0 | case SSL_ERROR_WANT_READ: |
219 | 0 | case SSL_ERROR_WANT_WRITE: |
220 | 0 | case SSL_ERROR_WANT_X509_LOOKUP: |
221 | 0 | case SSL_ERROR_ZERO_RETURN: |
222 | 0 | if (DEBUG_ENABLED2 || RDEBUG_ENABLED2) { |
223 | 0 | va_start(ap, fmt); |
224 | 0 | msg = fr_vasprintf(NULL, fmt, ap); |
225 | 0 | va_end(ap); |
226 | |
|
227 | 0 | ROPTIONAL(RDEBUG2, DEBUG2, "%s - %s (%i)", |
228 | 0 | msg, fr_table_str_by_value(ssl_io_error_table, err, "<UNKNOWN>"), err); |
229 | 0 | talloc_free(msg); |
230 | 0 | } |
231 | 0 | break; |
232 | | |
233 | | /* |
234 | | * These seem to be indications of a genuine |
235 | | * error that should result in the SSL tunnel |
236 | | * being regarded as "dead". |
237 | | */ |
238 | 0 | case SSL_ERROR_SYSCALL: |
239 | 0 | va_start(ap, fmt); |
240 | 0 | msg = fr_vasprintf(NULL, fmt, ap); |
241 | 0 | va_end(ap); |
242 | |
|
243 | 0 | ROPTIONAL(REDEBUG, ERROR, "%s - System call (I/O) error - %s (%i)", |
244 | 0 | msg, fr_table_str_by_value(ssl_io_error_table, err, "<UNKNOWN>"), err); |
245 | |
|
246 | 0 | talloc_free(msg); |
247 | 0 | return -1; |
248 | | |
249 | | /* |
250 | | * Docs say a more verbose error is available |
251 | | * in the normal error stack. |
252 | | */ |
253 | 0 | case SSL_ERROR_SSL: |
254 | 0 | va_start(ap, fmt); |
255 | 0 | (void)fr_tls_strerror_vprintf(fmt, ap); |
256 | 0 | va_end(ap); |
257 | |
|
258 | 0 | ROPTIONAL(RPERROR, PERROR, ""); |
259 | 0 | return -1; |
260 | | |
261 | | /* |
262 | | * For any other errors that (a) exist, and (b) |
263 | | * crop up - we need to interpret what to do with |
264 | | * them - so "politely inform" the caller that |
265 | | * the code needs updating here. |
266 | | */ |
267 | 0 | default: |
268 | 0 | va_start(ap, fmt); |
269 | 0 | msg = fr_vasprintf(NULL, fmt, ap); |
270 | 0 | va_end(ap); |
271 | |
|
272 | 0 | ROPTIONAL(REDEBUG, ERROR, "%s - TLS session error - %s (%i)", |
273 | 0 | msg, fr_table_str_by_value(ssl_io_error_table, err, "<UNKNOWN>"), err); |
274 | |
|
275 | 0 | talloc_free(msg); |
276 | |
|
277 | 0 | return -1; |
278 | 0 | } |
279 | | |
280 | 0 | return 0; |
281 | 0 | } |
282 | | |
283 | | |
284 | | /** Print errors in the TLS thread local error stack |
285 | | * |
286 | | * Drains the thread local OpenSSL error queue, and prints out errors. |
287 | | * |
288 | | * @param[in] request The current request (may be NULL). |
289 | | * @param[in] msg Error message describing the operation being attempted. |
290 | | * @param[in] ... Arguments for msg. |
291 | | * @return the number of errors drained from the stack. |
292 | | */ |
293 | | int fr_tls_log(request_t *request, char const *msg, ...) |
294 | 0 | { |
295 | 0 | va_list ap; |
296 | 0 | int ret; |
297 | |
|
298 | 0 | va_start(ap, msg); |
299 | 0 | ret = fr_tls_strerror_vprintf(msg, ap); |
300 | 0 | va_end(ap); |
301 | |
|
302 | 0 | ROPTIONAL(RPERROR, PERROR, ""); |
303 | |
|
304 | 0 | return ret; |
305 | 0 | } |
306 | | |
307 | | /** Clear errors in the TLS thread local error stack |
308 | | * |
309 | | */ |
310 | | void fr_tls_log_clear(void) |
311 | 0 | { |
312 | 0 | while (ERR_get_error() != 0); |
313 | 0 | } |
314 | | |
315 | | /** Increment the bio meth reference counter |
316 | | * |
317 | | */ |
318 | | static int tls_log_request_bio_create_cb(BIO *bio) |
319 | 0 | { |
320 | 0 | atomic_fetch_add(&tls_request_log_ref, 1); |
321 | 0 | BIO_set_init(bio, 1); |
322 | 0 | return 1; |
323 | 0 | } |
324 | | |
325 | | /** Converts BIO_write() calls to request log calls |
326 | | * |
327 | | * This callback is used to glue the output of OpenSSL functions into request log calls. |
328 | | * |
329 | | * @param[in] bio that was written to. |
330 | | * @param[in] in data being written to BIO. |
331 | | * @param[in] len Length of data being written. |
332 | | */ |
333 | | static int tls_log_request_bio_write_cb(BIO *bio, char const *in, int len) |
334 | 0 | { |
335 | 0 | fr_tls_log_bio_t *lb = talloc_get_type_abort(BIO_get_data(bio), fr_tls_log_bio_t); |
336 | 0 | request_t *request = talloc_get_type_abort(lb->request, request_t); |
337 | 0 | log_request_func_t func; |
338 | 0 | fr_slen_t slen; |
339 | 0 | char *le; |
340 | | |
341 | | /* |
342 | | * Pick the right logging function based on the type |
343 | | */ |
344 | 0 | if ((lb->type == L_ERR) || (lb->type == L_DBG_ERR) || (lb->type == L_DBG_ERR_REQ)) { |
345 | 0 | func = log_request_error; |
346 | 0 | } else { |
347 | 0 | func = log_request; |
348 | 0 | } |
349 | | |
350 | | /* |
351 | | * OpenSSL feeds us data in fragments so we need |
352 | | * to aggregate it, then look for new line chars |
353 | | * as an indication we need to print the line. |
354 | | */ |
355 | 0 | slen = fr_sbuff_in_bstrncpy(&lb->sbuff, in, len); |
356 | 0 | if (unlikely(slen < 0)) { |
357 | | /* |
358 | | * We failed to copy the data into the buffer |
359 | | * so we can't do anything with it. |
360 | | */ |
361 | 0 | REDEBUG2("Failed copying %u bytes into TLS log aggregation buffer, " |
362 | 0 | "needed %zu more bytes", len, (size_t)(-(slen))); |
363 | 0 | return 0; |
364 | 0 | } |
365 | | |
366 | | /* |
367 | | * Split incoming data on new lines |
368 | | */ |
369 | 0 | while (fr_sbuff_behind(&lb->logged_m)) { |
370 | 0 | le = memchr(fr_sbuff_current(&lb->logged_m), '\n', |
371 | 0 | fr_sbuff_current(&lb->sbuff) - fr_sbuff_current(&lb->logged_m)); |
372 | | /* |
373 | | * Wait until we have a complete line |
374 | | */ |
375 | 0 | if (le == NULL) break; |
376 | | |
377 | | /* |
378 | | * Skip empty lines |
379 | | */ |
380 | 0 | if ((le - fr_sbuff_current(&lb->logged_m)) > 0) { |
381 | 0 | func(lb->type, lb->lvl, request, __FILE__, __LINE__, "%pV", |
382 | 0 | fr_box_strvalue_len(fr_sbuff_current(&lb->logged_m), |
383 | 0 | le - fr_sbuff_current(&lb->logged_m))); |
384 | 0 | } |
385 | |
|
386 | 0 | fr_sbuff_set(&lb->logged_m, le + 1); |
387 | 0 | } |
388 | | |
389 | | /* |
390 | | * Clear out printed data |
391 | | */ |
392 | 0 | fr_sbuff_shift(&lb->sbuff, fr_sbuff_used(&lb->logged_m), false); |
393 | |
|
394 | 0 | return len; /* Amount of data written */ |
395 | 0 | } |
396 | | |
397 | | /** Converts BIO_puts() calls to request log calls |
398 | | * |
399 | | * This callback is used to glue the output of OpenSSL functions into request log calls. |
400 | | * |
401 | | * @param[in] bio that was written to. |
402 | | * @param[in] in data being written to BIO. |
403 | | */ |
404 | | static int tls_log_request_bio_puts_cb(BIO *bio, char const *in) |
405 | 0 | { |
406 | 0 | return tls_log_request_bio_write_cb(bio, in, strlen(in)); |
407 | 0 | } |
408 | | |
409 | | /** Decrement the bio meth reference counter |
410 | | * |
411 | | */ |
412 | | static int tls_log_request_bio_free_cb(BIO *bio) |
413 | 0 | { |
414 | 0 | atomic_fetch_sub(&tls_request_log_ref, 1); |
415 | 0 | BIO_set_init(bio, 0); |
416 | 0 | return 1; |
417 | 0 | } |
418 | | |
419 | | /** Increment the bio meth reference counter |
420 | | * |
421 | | */ |
422 | | static int tls_log_global_bio_create_cb(BIO *bio) |
423 | 0 | { |
424 | 0 | atomic_fetch_add(&tls_global_log_ref, 1); |
425 | 0 | BIO_set_init(bio, 1); |
426 | 0 | return 1; |
427 | 0 | } |
428 | | |
429 | | /** Converts BIO_write() calls to global log calls |
430 | | * |
431 | | * This callback is used to glue the output of OpenSSL functions into global log calls. |
432 | | * |
433 | | * @param[in] bio that was written to. |
434 | | * @param[in] in data being written to BIO. |
435 | | * @param[in] len Length of data being written. |
436 | | */ |
437 | | static int tls_log_global_bio_write_cb(BIO *bio, char const *in, int len) |
438 | 0 | { |
439 | 0 | fr_tls_log_bio_t *lb = talloc_get_type_abort(BIO_get_data(bio), fr_tls_log_bio_t); |
440 | 0 | char *le; |
441 | | |
442 | | /* |
443 | | * OpenSSL feeds us data in fragments so we need |
444 | | * to aggregate it, then look for new line chars |
445 | | * as an indication we need to print the line. |
446 | | */ |
447 | 0 | if (fr_sbuff_in_bstrncpy(&lb->sbuff, in, len) < 0) return 0; |
448 | | |
449 | | /* |
450 | | * Split incoming data on new lines |
451 | | */ |
452 | 0 | while (fr_sbuff_behind(&lb->logged_m)) { |
453 | 0 | le = memchr(fr_sbuff_current(&lb->logged_m), '\n', |
454 | 0 | fr_sbuff_current(&lb->sbuff) - fr_sbuff_current(&lb->logged_m)); |
455 | | /* |
456 | | * Wait until we have a complete line |
457 | | */ |
458 | 0 | if (le == NULL) break; |
459 | | |
460 | | /* |
461 | | * Skip empty lines |
462 | | */ |
463 | 0 | if ((le - fr_sbuff_current(&lb->logged_m)) > 0) { |
464 | 0 | if (fr_debug_lvl >= lb->lvl) fr_log(&default_log, lb->type, __FILE__, __LINE__, |
465 | 0 | "%pV", |
466 | 0 | fr_box_strvalue_len(fr_sbuff_current(&lb->logged_m), |
467 | 0 | le - fr_sbuff_current(&lb->logged_m))); |
468 | 0 | } |
469 | |
|
470 | 0 | fr_sbuff_set(&lb->logged_m, le + 1); |
471 | 0 | } |
472 | | |
473 | | /* |
474 | | * Clear out printed data |
475 | | */ |
476 | 0 | fr_sbuff_shift(&lb->sbuff, fr_sbuff_used(&lb->logged_m), false); |
477 | |
|
478 | 0 | return len; /* Amount of data written */ |
479 | 0 | } |
480 | | |
481 | | /** Converts BIO_puts() calls to global log calls |
482 | | * |
483 | | * This callback is used to glue the output of OpenSSL functions into global log calls. |
484 | | * |
485 | | * @param[in] bio that was written to. |
486 | | * @param[in] in data being written to BIO. |
487 | | */ |
488 | | static int tls_log_global_bio_puts_cb(BIO *bio, char const *in) |
489 | 0 | { |
490 | 0 | return tls_log_global_bio_write_cb(bio, in, strlen(in)); |
491 | 0 | } |
492 | | |
493 | | /** Decrement the bio meth reference counter |
494 | | * |
495 | | */ |
496 | | static int tls_log_global_bio_free_cb(BIO *bio) |
497 | 0 | { |
498 | 0 | atomic_fetch_sub(&tls_global_log_ref, 1); |
499 | 0 | BIO_set_init(bio, 0); |
500 | 0 | return 1; |
501 | 0 | } |
502 | | |
503 | | /** Frees a logging bio and its underlying OpenSSL BIO * |
504 | | * |
505 | | */ |
506 | | static int _fr_tls_log_bio_free(void *log_bio) |
507 | 0 | { |
508 | 0 | fr_tls_log_bio_t *our_log_bio = talloc_get_type_abort(log_bio, fr_tls_log_bio_t); |
509 | |
|
510 | 0 | BIO_free(our_log_bio->bio); |
511 | 0 | our_log_bio->bio = NULL; |
512 | 0 | return talloc_free(our_log_bio); |
513 | 0 | } |
514 | | |
515 | | /** Return a request log BIO to use with OpenSSL logging functions |
516 | | * |
517 | | * @note The contents of the BIO will only be written to the logging system on finding |
518 | | * a new line. If data remains in the BIO when it is re-initialised (this function |
519 | | * is called again), that data will be discarded. |
520 | | * |
521 | | * @note The returned BIO should be assumed to be invalid if the request yields. |
522 | | * |
523 | | * @param[in] file of caller. |
524 | | * @param[in] line of caller. |
525 | | * @param[in] request to temporarily associate with logging BIO. |
526 | | * @param[in] type to temporarily assign to logging bio. |
527 | | * @param[in] lvl to temporarily assign to logging bio. |
528 | | * @return A thread local BIO to pass to OpenSSL logging functions. |
529 | | */ |
530 | | BIO *_fr_tls_request_log_bio(char const *file, int line, request_t *request, fr_log_type_t type, fr_log_lvl_t lvl) |
531 | 0 | { |
532 | 0 | if (unlikely(!request_log_bio)) { |
533 | 0 | fr_tls_log_bio_t *lb; |
534 | |
|
535 | 0 | MEM(lb = talloc(NULL, fr_tls_log_bio_t)); |
536 | 0 | *lb = (fr_tls_log_bio_t) { |
537 | 0 | .bio = BIO_new(tls_request_log_meth), |
538 | 0 | .request = request, |
539 | 0 | .type = type, |
540 | 0 | .lvl = lvl, |
541 | 0 | .file = file, |
542 | 0 | .line = line |
543 | 0 | }; |
544 | 0 | MEM(lb->bio); |
545 | 0 | BIO_set_data(lb->bio, lb); /* So we can retrieve the fr_tls_lb_t in the callbacks */ |
546 | 0 | fr_sbuff_init_talloc(lb, &lb->sbuff, &lb->tctx, 1024, 10 * 1024); /* start 1k, max 10k */ |
547 | 0 | fr_atexit_thread_local(request_log_bio, _fr_tls_log_bio_free, lb); |
548 | 0 | fr_sbuff_marker(&lb->logged_m, &lb->sbuff); |
549 | 0 | return lb->bio; |
550 | 0 | } |
551 | 0 | fr_sbuff_reset_talloc(&request_log_bio->sbuff); /* Reset to initial size */ |
552 | 0 | fr_sbuff_marker(&request_log_bio->logged_m, &request_log_bio->sbuff); |
553 | 0 | request_log_bio->request = request; |
554 | 0 | request_log_bio->type = type; |
555 | 0 | request_log_bio->lvl = lvl; |
556 | 0 | request_log_bio->file = file; |
557 | 0 | request_log_bio->line = line; |
558 | |
|
559 | 0 | return request_log_bio->bio; |
560 | 0 | } |
561 | | |
562 | | /** Return a global log BIO to use with OpenSSL logging functions |
563 | | * |
564 | | * @note The contents of the BIO will only be written to the logging system on finding |
565 | | * a new line. If data remains in the BIO when it is re-initialised (this function |
566 | | * is called again), that data will be discarded. |
567 | | * |
568 | | * @note The returned BIO should be assumed to be invalid if the current request yields. |
569 | | * |
570 | | * @param[in] file of caller. |
571 | | * @param[in] line of caller. |
572 | | * @param[in] type to temporarily assign to logging bio. |
573 | | * @param[in] lvl to temporarily assign to logging bio. |
574 | | * @return A thread local BIO to pass to OpenSSL logging functions. |
575 | | */ |
576 | | BIO *_fr_tls_global_log_bio(char const *file, int line, fr_log_type_t type, fr_log_lvl_t lvl) |
577 | 0 | { |
578 | 0 | if (unlikely(!global_log_bio)) { |
579 | 0 | fr_tls_log_bio_t *lb; |
580 | |
|
581 | 0 | MEM(lb = talloc(NULL, fr_tls_log_bio_t)); |
582 | 0 | *lb = (fr_tls_log_bio_t) { |
583 | 0 | .bio = BIO_new(tls_global_log_meth), |
584 | 0 | .type = type, |
585 | 0 | .lvl = lvl, |
586 | 0 | .file = file, |
587 | 0 | .line = line |
588 | 0 | }; |
589 | 0 | MEM(lb->bio); |
590 | 0 | BIO_set_data(lb->bio, lb); /* So we can retrieve the fr_tls_lb_t in the callbacks */ |
591 | 0 | fr_sbuff_init_talloc(lb, &lb->sbuff, &lb->tctx, 1024, 10 * 1024); /* start 1k, max 10k */ |
592 | 0 | fr_atexit_thread_local(global_log_bio, _fr_tls_log_bio_free, lb); |
593 | 0 | fr_sbuff_marker(&lb->logged_m, &lb->sbuff); |
594 | 0 | return lb->bio; |
595 | 0 | } |
596 | | |
597 | 0 | fr_sbuff_reset_talloc(&global_log_bio->sbuff); /* Reset to initial size */ |
598 | 0 | fr_sbuff_marker(&global_log_bio->logged_m, &global_log_bio->sbuff); |
599 | 0 | global_log_bio->type = type; |
600 | 0 | global_log_bio->lvl = lvl; |
601 | 0 | global_log_bio->file = file; |
602 | 0 | global_log_bio->line = line; |
603 | |
|
604 | 0 | return global_log_bio->bio; |
605 | 0 | } |
606 | | |
607 | | /** Initialise the BIO logging meths which are used to create thread local logging BIOs |
608 | | * |
609 | | */ |
610 | | int fr_tls_log_init(void) |
611 | 0 | { |
612 | | /* |
613 | | * As per the boringSSL documentation |
614 | | * |
615 | | * BIO_TYPE_START is the first user-allocated |BIO| type. |
616 | | * No pre-defined type, flag bits aside, may exceed this |
617 | | * value. |
618 | | * |
619 | | * The low byte here defines the BIO ID, and the high byte |
620 | | * defines its capabilities. |
621 | | */ |
622 | 0 | tls_request_log_meth = BIO_meth_new(BIO_get_new_index() | BIO_TYPE_SOURCE_SINK, "fr_tls_request_log"); |
623 | 0 | if (unlikely(!tls_request_log_meth)) return -1; |
624 | | |
625 | 0 | BIO_meth_set_create(tls_request_log_meth, tls_log_request_bio_create_cb); |
626 | 0 | BIO_meth_set_write(tls_request_log_meth, tls_log_request_bio_write_cb); |
627 | 0 | BIO_meth_set_puts(tls_request_log_meth, tls_log_request_bio_puts_cb); |
628 | 0 | BIO_meth_set_destroy(tls_request_log_meth, tls_log_request_bio_free_cb); |
629 | |
|
630 | 0 | tls_global_log_meth = BIO_meth_new(BIO_get_new_index() | BIO_TYPE_SOURCE_SINK, "fr_tls_global_log"); |
631 | 0 | if (unlikely(!tls_global_log_meth)) { |
632 | 0 | BIO_meth_free(tls_request_log_meth); |
633 | 0 | tls_request_log_meth = NULL; |
634 | 0 | return -1; |
635 | 0 | } |
636 | | |
637 | 0 | BIO_meth_set_create(tls_global_log_meth, tls_log_global_bio_create_cb); |
638 | 0 | BIO_meth_set_write(tls_global_log_meth, tls_log_global_bio_write_cb); |
639 | 0 | BIO_meth_set_puts(tls_global_log_meth, tls_log_global_bio_puts_cb); |
640 | 0 | BIO_meth_set_destroy(tls_global_log_meth, tls_log_global_bio_free_cb); |
641 | |
|
642 | 0 | return 0; |
643 | 0 | } |
644 | | |
645 | | /** Free the global log method templates |
646 | | * |
647 | | */ |
648 | | void fr_tls_log_free(void) |
649 | 0 | { |
650 | | /* |
651 | | * These must be freed first else |
652 | | * we get crashes in the OpenSSL |
653 | | * code when we try to free them. |
654 | | */ |
655 | 0 | fr_assert_msg(atomic_load(&tls_request_log_ref) == 0, "request log BIO refs remaining %u", atomic_load(&tls_request_log_ref)); |
656 | 0 | fr_assert_msg(atomic_load(&tls_global_log_ref) == 0, "global log BIO refs remaining %u", atomic_load(&tls_global_log_ref)); |
657 | |
|
658 | 0 | if (tls_request_log_meth) { |
659 | 0 | BIO_meth_free(tls_request_log_meth); |
660 | 0 | tls_request_log_meth = NULL; |
661 | 0 | } |
662 | |
|
663 | 0 | if (tls_global_log_meth) { |
664 | 0 | BIO_meth_free(tls_global_log_meth); |
665 | | tls_global_log_meth = NULL; |
666 | 0 | } |
667 | 0 | } |
668 | | #endif /* WITH_TLS */ |