The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_crl.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 (at
5 * 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: 38d2e911cfd420f98618776f2d18a89b984b025a $
19 * @file rlm_crl.c
20 * @brief Check a certificate's serial number against a CRL
21 *
22 * @author Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 * @copyright 2025 Network RADIUS SAS (legal@networkradius.com)
24 */
25RCSID("$Id: 38d2e911cfd420f98618776f2d18a89b984b025a $")
26
27#include <freeradius-devel/crl/crl.h>
28#include <freeradius-devel/server/base.h>
29#include <freeradius-devel/server/module_rlm.h>
30#include <freeradius-devel/io/coord_pair.h>
31
32#include <freeradius-devel/tls/strerror.h>
33#include <freeradius-devel/tls/utils.h>
34
35#include <freeradius-devel/unlang/xlat_func.h>
36
37#include <openssl/x509v3.h>
38
39/** Thread specific structure to hold requests awaiting CRL fetching */
40typedef struct {
41 fr_rb_tree_t pending; //!< Requests yielded while the CRL is being fetched.
42 fr_coord_worker_t *cw; //!< Worker side of coordinator communication.
43 fr_rb_tree_t crls; //!< CRLs fetched from the coordinator.
44 fr_rb_tree_t fails; //!< Recent CRLs which have failed to fetch.
46
47typedef struct {
48 fr_time_delta_t retry_delay; //!< Time to hold off between CRL fetching failures.
49 char const **urls; //!< Initial list of URLs to fetch.
50 fr_coord_pair_reg_t *coord_pair_reg; //!< coord_pair registration for fetching CRLs.
51 fr_coord_reg_t *coord_reg; //!< coord registration for fetching CRLs.
52} rlm_crl_t;
53
54/** A single CRL in the thread specific list of CRLs */
55typedef struct {
56 X509_CRL *crl; //!< The CRL.
57 char const *cdp_url; //!< The URL of the CRL.
58 fr_rb_node_t node; //!< The node in the tree
59 fr_value_box_list_t delta_urls; //!< URLs from which a delta CRL can be retrieved.
61
62/** Structure to record recent fetch failures
63 */
64typedef struct {
65 char const *cdp_url; //!< The URL which failed to fetch.
66 fr_rb_node_t node; //!< Node in the tree of failures.
67 fr_time_t fail_time; //!< When did the failure occur.
69
70/** Structure to record a request which is waiting for CRL fetching to complete */
75
76/** A status used to track which CRL is being checked */
77typedef enum {
78 CRL_CHECK_BASE = 0, //!< The base CRL is being checked
79 CRL_CHECK_FETCH_DELTA, //!< The delta CRL is being fetched
80 CRL_CHECK_DELTA //!< The delta CRL exists and is being checked
82
83typedef struct {
84 fr_value_box_t *cdp_url; //!< The URL we're currently attempting to load.
85 crl_entry_t *base_crl; //!< The base CRL relating to the delta currently being fetched.
86 fr_value_box_list_t crl_data; //!< Data from CRL expansion.
87 fr_value_box_list_t missing_crls; //!< CRLs missing from the tree
88 crl_check_status_t status; //!< Status of the current CRL check.
90
92 { FR_CONF_OFFSET("retry_delay", rlm_crl_t, retry_delay), .dflt = "30s" },
95};
96
97/** Callback IDs used by CRL coordinator calls
98 */
102
103static fr_dict_t const *dict_crl;
104
107 { .out = &dict_crl, .proto = "crl" },
109};
110
116
119 { .out = &attr_crl_data, .name = "CRL-Data", .type = FR_TYPE_OCTETS, .dict = &dict_crl },
120 { .out = &attr_crl_cdp_url, .name = "CDP-URL", .type = FR_TYPE_STRING, .dict = &dict_crl },
121 { .out = &attr_base_crl, .name = "Base-CRL", .type = FR_TYPE_STRING, .dict = &dict_crl },
122 { .out = &attr_delta_crl, .name = "Delta-CRL", .type = FR_TYPE_STRING, .dict = &dict_crl },
123 { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_crl },
125};
126
127typedef struct {
128 fr_value_box_t serial; //!< The serial to check
129 fr_value_box_list_head_t *cdp; //!< The CRL distribution points
131
132typedef enum {
133 CRL_ERROR = -1, //!< Unspecified error ocurred.
134 CRL_ENTRY_NOT_FOUND = 0, //!< Serial not found in this CRL.
135 CRL_ENTRY_FOUND = 1, //!< Serial was found in this CRL.
136 CRL_ENTRY_REMOVED = 2, //!< Serial was "un-revoked" in this delta CRL.
137 CRL_NOT_FOUND = 3, //!< No CRL found, need to load it from the CDP URL
138 CRL_MISSING_DELTA = 4, //!< Need to load a delta CRL to supplement this CRL.
139} crl_ret_t;
140
141#ifdef WITH_TLS
142static const call_env_method_t crl_env = {
144 .env = (call_env_parser_t[]){
146 .pair.dflt = "session-state.TLS-Certificate.Serial", .pair.dflt_quote = T_BARE_WORD },
148 .pair.dflt = "session-state.TLS-Certificate.X509v3-CRL-Distribution-Points[*]", .pair.dflt_quote = T_BARE_WORD },
150 },
151};
152
153static int8_t crl_cmp(void const *a, void const *b)
154{
155 crl_entry_t const *crl_a = (crl_entry_t const *)a;
156 crl_entry_t const *crl_b = (crl_entry_t const *)b;
157
158 return CMP(strcmp(crl_a->cdp_url, crl_b->cdp_url), 0);
159}
160
161static int8_t crl_pending_cmp(void const *a, void const *b)
162{
163 crl_pending_t const *pending_a = (crl_pending_t const *)a;
164 crl_pending_t const *pending_b = (crl_pending_t const *)b;
165
166 return CMP(pending_a->request, pending_b->request);
167}
168
169static int8_t crl_fail_cmp(void const *a, void const *b)
170{
171 crl_fail_t const *fail_a = (crl_fail_t const *)a;
172 crl_fail_t const *fail_b = (crl_fail_t const *)b;
173
174 return CMP(strcmp(fail_a->cdp_url, fail_b->cdp_url), 0);
175}
176
177static xlat_arg_parser_t const crl_refresh_xlat_arg[] = {
178 { .required=true, .concat = true, .type = FR_TYPE_STRING },
179 { .concat = true, .type = FR_TYPE_STRING },
181};
182
183/** Forcibly trigger refresh of a CRL
184 *
185 * Example:
186 @verbatim
187 %crl.refresh('http://example.com/ca.crl')
188 @endverbatim
189 *
190 * @ingroup xlat_functions
191 */
192static xlat_action_t crl_refresh_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx,
193 UNUSED request_t *request, fr_value_box_list_t *args)
194{
195 rlm_crl_t const *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_crl_t);
196 rlm_crl_thread_t *t = talloc_get_type_abort(xctx->mctx->thread, rlm_crl_thread_t);
197 fr_value_box_t *vb, *url, *base_crl;
198 fr_pair_list_t list;
199 fr_pair_t *vp;
200 TALLOC_CTX *local = talloc_new(NULL);
201 int ret;
202 crl_entry_t find, *found;
203 XLAT_ARGS(args, &url, &base_crl);
204
205 fr_pair_list_init(&list);
207 if (!vp) {
208 error:
209 talloc_free(local);
210 return XLAT_ACTION_FAIL;
211 }
212
213 if (fr_pair_append_by_da(local, &vp, &list, attr_crl_cdp_url) < 0) goto error;
214 if (fr_value_box_copy(vp, &vp->data, url) < 0) goto error;
215
216 if (base_crl) {
217 if (fr_pair_append_by_da(local, &vp, &list, attr_base_crl) < 0) goto error;
218 if (fr_value_box_copy(vp, &vp->data, base_crl) < 0) goto error;
219 }
220
221 ret = fr_worker_to_coord_pair_send(t->cw, inst->coord_pair_reg, &list);
222
223 talloc_free(local);
224
225 if (ret < 0) return XLAT_ACTION_FAIL;
226
227 find = (crl_entry_t) {
228 .cdp_url = url->vb_strvalue
229 };
230 found = fr_rb_find(&t->crls, &find);
231 if (found) {
232 fr_rb_remove(&t->crls, found);
233 talloc_free(found);
234 }
235
236 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
237 vb->vb_bool = true;
239
240 return XLAT_ACTION_DONE;
241}
242
243/** See if a particular serial is present in a CRL list
244 *
245 */
246static crl_ret_t crl_check_entry(crl_entry_t *crl_entry, request_t *request, uint8_t const *serial)
247{
248 X509_REVOKED *revoked;
249 ASN1_INTEGER *asn1_serial = NULL;
250 int ret;
251
252 asn1_serial = d2i_ASN1_INTEGER(NULL, (unsigned char const **)&serial, talloc_array_length(serial));
253 ret = X509_CRL_get0_by_serial(crl_entry->crl, &revoked, asn1_serial);
254 ASN1_INTEGER_free(asn1_serial);
255 switch (ret) {
256 /* The docs describe 0 as "failure" - but that means "failed to find"*/
257 case 0:
258 RDEBUG3("Certificate not in CRL");
259 return CRL_ENTRY_NOT_FOUND;
260
261 case 1:
262 REDEBUG2("Certificate revoked by %s", crl_entry->cdp_url);
263 return CRL_ENTRY_FOUND;
264
265 case 2:
266 RDEBUG3("Certificate un-revoked by %s", crl_entry->cdp_url);
267 return CRL_ENTRY_REMOVED;
268 }
269
270 return CRL_ERROR;
271}
272
273/** Resolve a cdp_url to a CRL entry, and check serial against it, if it exists
274 *
275 */
276static crl_ret_t crl_check_serial(fr_rb_tree_t *crls, request_t *request, char const *cdp_url, uint8_t const *serial,
277 crl_entry_t **found)
278{
279 crl_entry_t *delta, find = { .cdp_url = cdp_url};
280 fr_value_box_t *vb = NULL;
282
283 *found = fr_rb_find(crls, &find);
284 if (*found == NULL) return CRL_NOT_FOUND;
285
286 /*
287 * First check the delta if it should exist
288 */
289 while ((vb = fr_value_box_list_next(&(*found)->delta_urls, vb))) {
290 find.cdp_url = vb->vb_strvalue;
291 delta = fr_rb_find(crls, &find);
292 if (delta) {
293 ret = crl_check_entry(delta, request, serial);
294
295 /*
296 * An entry found in a delta overrides the base CRL
297 */
298 if (ret != CRL_ENTRY_NOT_FOUND) return ret;
299 break;
300 } else {
301 ret = CRL_MISSING_DELTA;
302 }
303 }
304
305 if (ret == CRL_MISSING_DELTA) return ret;
306
307 return crl_check_entry(*found, request, serial);
308}
309
310static int _crl_entry_free(crl_entry_t *crl_entry)
311{
312 X509_CRL_free(crl_entry->crl);
313 return 0;
314}
315
316/** Request a CRL from the coordinator
317 *
318 * @param inst Module instance
319 * @param t Thread data
320 * @param cdp_urls List of URLs to fetch the CRL from
321 * @param base_crl URL of the base CRL if a delta is being requested.
322 * @return
323 * - 0 on success
324 * - -1 on failure
325 */
326static int crl_fetch_start(rlm_crl_t const *inst, rlm_crl_thread_t *t, fr_value_box_list_t *cdp_urls, char const *base_crl) {
327 fr_pair_list_t list;
328 fr_pair_t *vp;
329 TALLOC_CTX *local = talloc_new(NULL);
330 int ret;
331
332 fr_pair_list_init(&list);
334 if (!vp) {
335 error:
336 talloc_free(local);
337 return -1;
338 }
339
340 fr_value_box_list_foreach(cdp_urls, cdp) {
341 if (fr_pair_append_by_da(local, &vp, &list, attr_crl_cdp_url) < 0) goto error;
342 if (fr_value_box_copy(vp, &vp->data, cdp) < 0) goto error;
343 }
344
345 if (base_crl) {
346 if (fr_pair_append_by_da(local, &vp, &list, attr_base_crl) < 0) goto error;
347 fr_value_box_strdup(vp, &vp->data, NULL, base_crl, false);
348 }
349
350 ret = fr_worker_to_coord_pair_send(t->cw, inst->coord_pair_reg, &list);
351
352 talloc_free(local);
353
354 return ret;
355}
356
357static void crl_by_url_cancel(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
358{
359 rlm_crl_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_crl_thread_t);
360 crl_pending_t find, *found;
361
362 find = (crl_pending_t) {
363 .request = request
364 };
365
366 found = fr_rb_find(&t->pending, &find);
367 if (!found) return;
368
369 fr_rb_remove(&t->pending, found);
370 talloc_free(found);
371}
372
373static unlang_action_t CC_HINT(nonnull) mod_crl_by_url(unlang_result_t *p_result, module_ctx_t const *mctx,
374 request_t *request)
375{
377 rlm_crl_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_crl_env_t);
378 rlm_crl_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_crl_thread_t);
379 fr_value_box_t *cdp = NULL;
380 crl_entry_t *found;
381 fr_value_box_list_t missing;
382 fr_value_box_t *uri;
383 char const *base_url = NULL;
384 int ret;
385 crl_pending_t *pending;
386 crl_fail_t find, *fail;
387
388 fr_value_box_list_init(&missing);
389
390 while ((cdp = fr_value_box_list_next(env->cdp, cdp))) {
391 switch (crl_check_serial(&t->crls, request, cdp->vb_strvalue, env->serial.vb_octets, &found)) {
392 case CRL_ENTRY_FOUND:
394
398
399 case CRL_ERROR:
400 continue;
401
402 case CRL_NOT_FOUND:
403 uri = fr_value_box_acopy(NULL, cdp);
404 fr_value_box_list_insert_tail(&missing, uri);
405 continue;
406
408 fr_value_box_t *vb = NULL;
409 fr_value_box_list_talloc_free(&missing);
410 while ((vb = fr_value_box_list_next(&found->delta_urls, vb))) {
411 uri = fr_value_box_acopy(NULL, vb);
412 fr_value_box_list_insert_tail(&missing, uri);
413 }
414 base_url = cdp->vb_strvalue;
415 break;
416 }
417 }
418
419 if (fr_value_box_list_num_elements(&missing) == 0) RETURN_UNLANG_FAIL;
420
421 find = (crl_fail_t) {
422 .cdp_url = fr_value_box_list_head(&missing)->vb_strvalue
423 };
424
425 /*
426 * Check to see if the missing CRL has failed to be fetched
427 * recently. If it has, within the retry delay time, then
428 * fail this request.
429 */
430 fail = fr_rb_find(&t->fails, &find);
431 if (fail) {
432 if (fr_time_gt(fr_time_add(fail->fail_time, inst->retry_delay), fr_time())) {
433 fr_value_box_list_talloc_free(&missing);
435 }
436
437 fr_rb_delete(&t->fails, fail);
438 }
439
440 ret = crl_fetch_start(inst, t, &missing, base_url);
441
442 fr_value_box_list_talloc_free(&missing);
443
444 if (ret < 0) RETURN_UNLANG_FAIL;
445
446 if (unlang_module_yield(request, mod_crl_by_url, crl_by_url_cancel,
448
449 MEM(pending = talloc_zero(t, crl_pending_t));
450 pending->request = request;
451
452 if (!fr_rb_insert(&t->pending, pending)) {
453 talloc_free(pending);
455 }
456
457 RDEBUG3("Yielding request until CRL fetching completed");
458
459 return UNLANG_ACTION_YIELD;
460}
461
462/** Resume requests waiting for a CRL fetch.
463 */
464static void crl_pending_resume(rlm_crl_thread_t *thread)
465{
466 crl_pending_t *pending;
468
469 for (pending = fr_rb_iter_init_inorder(&thread->pending, &iter);
470 pending;
471 pending = fr_rb_iter_next_inorder(&thread->pending, &iter)) {
472 fr_rb_iter_delete_inorder(&thread->pending, &iter);
474 talloc_free(pending);
475 }
476}
477
478/** Callback for worker receiving Fetch-OK packet from coordinator
479 */
480static void recv_crl_ok(UNUSED fr_coord_worker_t *cw, UNUSED fr_coord_pair_reg_t *coord_pair_reg,
481 fr_pair_list_t const *list, UNUSED fr_time_t now,
482 module_ctx_t *mctx, UNUSED void *uctx)
483{
484 fr_pair_t *url, *crl, *delta = NULL;
485 crl_entry_t *crl_entry, find;
486 rlm_crl_thread_t *thread = talloc_get_type_abort(mctx->thread, rlm_crl_thread_t);
487 uint8_t const *data;
488
490 if (!url) {
491 ERROR("Missing URL");
492 return;
493 }
494
495 find = (crl_entry_t) {
496 .cdp_url = url->vp_strvalue
497 };
498
499 crl_entry = fr_rb_find(&thread->crls, &find);
500
501 crl = fr_pair_find_by_da(list, NULL, attr_crl_data);
502 if (!crl) {
503 ERROR("No CRL data");
504 return;
505 }
506
507 /*
508 * If this CRL didn't previously exist, create the entry
509 */
510 if (!crl_entry) {
511 MEM(crl_entry = talloc_zero(thread, crl_entry_t));
512 crl_entry->cdp_url = talloc_strdup(crl_entry, url->vp_strvalue);
513 fr_value_box_list_init(&crl_entry->delta_urls);
514 if (!fr_rb_insert(&thread->crls, crl_entry)) {
515 talloc_free(crl_entry);
516 return;
517 }
518 talloc_set_destructor(crl_entry, _crl_entry_free);
519 } else {
520 X509_CRL_free(crl_entry->crl);
521 fr_value_box_list_talloc_free(&crl_entry->delta_urls);
522 }
523
524 data = crl->vp_octets;
525 crl_entry->crl = d2i_X509_CRL(NULL, (const unsigned char **)&data, crl->vp_size);
526 if (unlikely(!crl_entry->crl)) {
527 ERROR("Failed to parse CRL");
528 error:
529 fr_rb_remove(&thread->crls, crl_entry);
530 talloc_free(crl_entry);
531 }
532
533 DEBUG3("CRL %pP refreshed", crl);
534
535 while ((delta = fr_pair_find_by_da(list, delta, attr_delta_crl))) {
536 fr_value_box_t *vb;
537 MEM(vb = fr_value_box_alloc_null(crl_entry));
538 if (unlikely(fr_value_box_copy(vb, vb, &delta->data) < 0)) goto error;
539 fr_value_box_list_insert_tail(&crl_entry->delta_urls, vb);
540 }
541
542 crl_pending_resume(thread);
543}
544
545/** Callback for worker receiving Fetch-Fail packet from coordinator
546 */
547static void recv_crl_fail(UNUSED fr_coord_worker_t *cw, UNUSED fr_coord_pair_reg_t *coord_pair_reg,
548 fr_pair_list_t const *list, fr_time_t now, module_ctx_t *mctx, UNUSED void *uctx)
549{
550 fr_pair_t *vp;
551 rlm_crl_thread_t *thread = talloc_get_type_abort(mctx->thread, rlm_crl_thread_t);
552
553 /*
554 * Record the URL of the fetch that failed.
555 */
557 if (vp) {
558 crl_fail_t *fail;
559
560 MEM(fail = talloc_zero(thread, crl_fail_t));
561 fail->cdp_url = talloc_strdup(fail, vp->vp_strvalue);
562 fail->fail_time = now;
563
564 if (unlikely(!fr_rb_insert(&thread->fails, fail))) {
565 talloc_free(fail);
566 }
567 }
568
569 crl_pending_resume(thread);
570}
571
572/** Callback for worker receiving CRL-Expire packet from coordinator
573 */
574static void recv_crl_expire(UNUSED fr_coord_worker_t *cw, UNUSED fr_coord_pair_reg_t *coord_pair_reg,
575 fr_pair_list_t const *list, UNUSED fr_time_t now, module_ctx_t *mctx, UNUSED void *uctx)
576{
577 fr_pair_t *vp;
578 rlm_crl_thread_t *thread = talloc_get_type_abort(mctx->thread, rlm_crl_thread_t);
579 crl_entry_t *crl_entry, find;
580
582 if (!vp) return;
583
584 find = (crl_entry_t) {
585 .cdp_url = vp->vp_strvalue
586 };
587
588 crl_entry = fr_rb_find(&thread->crls, &find);
589
590 if (!crl_entry) return;
591
592 /*
593 * If the expired CRL has any deltas, remove them as well.
594 */
595 fr_value_box_list_foreach(&crl_entry->delta_urls, delta) {
596 crl_entry_t *delta_entry;
597
598 find.cdp_url = delta->vb_strvalue;
599 delta_entry = fr_rb_find(&thread->crls, &find);
600 if (!delta_entry) continue;
601
602 WARN("Delta CRL %s expired", delta_entry->cdp_url);
603 fr_rb_remove(&thread->crls, delta_entry);
604 talloc_free(delta_entry);
605 }
606
607 WARN("CRL %s expired", crl_entry->cdp_url);
608 fr_rb_remove(&thread->crls, crl_entry);
609 talloc_free(crl_entry);
610}
611
613{
614 rlm_crl_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_crl_thread_t);
615
616 fr_rb_inline_init(&t->pending, crl_pending_t, node, crl_pending_cmp, NULL);
617 fr_rb_inline_init(&t->crls, crl_entry_t, node, crl_cmp, NULL);
618 fr_rb_inline_init(&t->fails, crl_fail_t, node, crl_fail_cmp, NULL);
619
620 return 0;
621}
622
623static int mod_coord_attach(module_thread_inst_ctx_t const *mctx)
624{
625 rlm_crl_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_crl_thread_t);
626 rlm_crl_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_crl_t);
627 size_t i;
628
629 t->cw = fr_coord_attach(t, mctx->el, inst->coord_reg);
630
631 if (!t->cw) {
632 ERROR("Failed to attach to coordinator");
633 return -1;
634 }
635
636 if (fr_schedule_worker_id() != 0) return 0;
637
638 /*
639 * If any urls have been configured to pre-load, trigger those from
640 * worker ID 0.
641 */
642 if (!inst->urls) return 0;
643
644 for (i = 0; i < talloc_array_length(inst->urls); i++) {
645 fr_value_box_list_t list;
647
648 DEBUG2("Pre-fetching CRL from %s", inst->urls[i]);
649 fr_value_box_list_init(&list);
650 fr_value_box_init(&vb, FR_TYPE_STRING, NULL, false);
651 fr_value_box_strdup_shallow(&vb, NULL, inst->urls[i], false);
652 fr_value_box_list_insert_head(&list, &vb);
653 if (crl_fetch_start(inst, t, &list, NULL) < 0) {
654 ERROR("Failed to initiate fetch for %s", inst->urls[i]);
655 return -1;
656 }
657 }
658
659 return 0;
660}
661
662static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
663{
664 rlm_crl_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_crl_thread_t);
665
666 if (!t->cw) return 0;
667
668 fr_coord_detach(t->cw, true);
669 t->cw = NULL;
670 return 0;
671}
672
673static int mod_bootstrap(module_inst_ctx_t const *mctx)
674{
675 xlat_t *xlat;
676
677 if (unlikely(!(xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, "refresh", crl_refresh_xlat,
678 FR_TYPE_BOOL)))) return -1;
679 xlat_func_args_set(xlat, crl_refresh_xlat_arg);
680
681 return 0;
682}
683
684static int mod_detach(module_detach_ctx_t const *mctx)
685{
686 rlm_crl_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_crl_t);
687
688 fr_coord_deregister(inst->coord_reg);
689 return 0;
690}
691
692static fr_coord_cb_reg_t coord_callbacks[] = {
695};
696
697static fr_coord_worker_cb_reg_t worker_callbacks[] = {
700};
701
702static fr_coord_worker_pair_cb_reg_t worker_pair_callbacks[] = {
703 { .packet_type = FR_CRL_FETCH_OK, .callback = recv_crl_ok },
704 { .packet_type = FR_CRL_FETCH_FAIL, .callback = recv_crl_fail },
705 { .packet_type = FR_CRL_CRL_EXPIRE, .callback = recv_crl_expire },
707};
708#endif
709
710/** Instantiate the module
711 *
712 */
713static int mod_instantiate(module_inst_ctx_t const *mctx)
714{
715#ifdef WITH_TLS
716 rlm_crl_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_crl_t);
717
718 inst->coord_pair_reg = fr_coord_pair_register(inst,
720 .worker_cb = worker_pair_callbacks,
722 .root = fr_dict_root(dict_crl),
723 .cs = mctx->mi->conf,
724 }
725 );
726 if (!inst->coord_pair_reg) return -1;
727
728 FR_COORD_PAIR_CB_CTX_SET(coord_callbacks, worker_callbacks, inst->coord_pair_reg);
729
730 inst->coord_reg = fr_coord_register(inst,
732 .name = mctx->mi->name,
733 .coord_cb = coord_callbacks,
734 .worker_cb = worker_callbacks,
735 .mi = mctx->mi
736 });
737
738 if (!inst->coord_reg) return -1;
739
740 return 0;
741#else
742 cf_log_err(mctx->mi->conf, "rlm_crl requires OpenSSL");
743 return -1;
744#endif
745}
746
747extern module_rlm_t rlm_crl;
749 .common = {
750 .magic = MODULE_MAGIC_INIT,
751 .inst_size = sizeof(rlm_crl_t),
752 .instantiate = mod_instantiate,
753 .name = "crl",
756#ifdef WITH_TLS
757 .thread_instantiate = mod_thread_instantiate,
758 .thread_detach = mod_thread_detach,
759 .coord_attach = mod_coord_attach,
760 .bootstrap = mod_bootstrap,
761 .detach = mod_detach,
762#endif
763 },
764#ifdef WITH_TLS
765 .method_group = {
766 .bindings = (module_method_binding_t[]){
767 { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_crl_by_url, .method_env = &crl_env },
769 }
770 }
771#endif
772};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_YIELD
Temporarily pause execution until an event occurs.
Definition action.h:41
va_list args
Definition acutest.h:770
#define RCSID(id)
Definition build.h:512
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:113
#define unlikely(_x)
Definition build.h:407
#define UNUSED
Definition build.h:336
#define CALL_ENV_TERMINATOR
Definition call_env.h:236
#define FR_CALL_ENV_METHOD_OUT(_inst)
Helper macro for populating the size/type fields of a call_env_method_t from the output structure typ...
Definition call_env.h:240
call_env_parser_t const * env
Parsing rules for call method env.
Definition call_env.h:247
@ CALL_ENV_FLAG_SINGLE
If the tmpl produces more than one box this is an error.
Definition call_env.h:77
@ CALL_ENV_FLAG_ATTRIBUTE
Tmpl MUST contain an attribute reference.
Definition call_env.h:86
@ CALL_ENV_FLAG_MULTI
Multiple instances of the conf pairs are allowed.
Definition call_env.h:78
@ CALL_ENV_FLAG_REQUIRED
Associated conf pair or section is required.
Definition call_env.h:75
@ CALL_ENV_FLAG_BARE_WORD_ATTRIBUTE
bare words are treated as an attribute, but strings may be xlats.
Definition call_env.h:92
@ CALL_ENV_FLAG_NULLABLE
Tmpl expansions are allowed to produce no output.
Definition call_env.h:80
#define FR_CALL_ENV_OFFSET(_name, _cast_type, _flags, _struct, _field)
Specify a call_env_parser_t which writes out runtime results to the specified field.
Definition call_env.h:340
Per method call config.
Definition call_env.h:180
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:657
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:280
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:268
@ CONF_FLAG_MULTI
CONF_PAIR can have multiple copies.
Definition cf_parse.h:446
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:594
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:285
#define CF_IDENT_ANY
Definition cf_util.h:75
fr_coord_reg_t * fr_coord_register(TALLOC_CTX *ctx, fr_coord_reg_ctx_t *reg_ctx)
Register a coordinator.
Definition coord.c:138
fr_coord_worker_t * fr_coord_attach(TALLOC_CTX *ctx, fr_event_list_t *el, fr_coord_reg_t *coord_reg)
Attach a worker to a coordinator.
Definition coord.c:663
void fr_coord_deregister(fr_coord_reg_t *coord_reg)
De-register a coordinator.
Definition coord.c:175
int fr_coord_detach(fr_coord_worker_t *cw, bool exiting)
Signal a coordinator that a worker wants to detach.
Definition coord.c:624
A coordinator registration.
Definition coord.c:85
The worker end of worker <-> coordinator communication.
Definition coord.c:73
#define FR_COORD_CALLBACK_TERMINATOR
Definition coord.h:60
int fr_worker_to_coord_pair_send(fr_coord_worker_t *cw, fr_coord_pair_reg_t *coord_pair_reg, fr_pair_list_t *list)
Send a pair list from a worker to a coordinator.
Definition coord_pair.c:811
fr_coord_pair_reg_t * fr_coord_pair_register(TALLOC_CTX *ctx, fr_coord_pair_reg_ctx_t *reg_ctx)
Register a set of callbacks for pair list based coordinator messages.
Definition coord_pair.c:113
uint32_t packet_type
Packet type value for this callback.
Definition coord_pair.h:38
#define FR_COORD_WORKER_PAIR_CALLBACK(_id)
Set callback for handling coordinator -> worker pair list data.
Definition coord_pair.h:85
#define FR_COORD_PAIR_CALLBACK(_id)
Set callback for handling worker -> coordinator pair list data.
Definition coord_pair.h:80
struct fr_coord_pair_reg_s fr_coord_pair_reg_t
Definition coord_pair.h:32
#define FR_COORD_PAIR_CB_CTX_SET(_in_cb, _out_cb, _reg)
Set up ctx on pair list callbacks.
Definition coord_pair.h:92
@ FR_CRL_CRL_REFRESH
Definition crl.h:33
@ FR_CRL_FETCH_OK
Definition crl.h:34
@ FR_CRL_CRL_EXPIRE
Definition crl.h:36
@ FR_CRL_CRL_FETCH
Definition crl.h:32
@ FR_CRL_FETCH_FAIL
Definition crl.h:35
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition dcursor.h:406
#define MEM(x)
Definition debug.h:46
#define ERROR(fmt,...)
Definition dhcpclient.c:40
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2665
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:292
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:305
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:311
Specifies an attribute which must be present for the module to function.
Definition dict.h:291
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:304
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
talloc_free(hp)
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:1640
#define DEBUG3(_fmt,...)
Definition log.h:266
#define RDEBUG3(fmt,...)
Definition log.h:355
#define REDEBUG2(fmt,...)
Definition log.h:384
#define fr_time()
Definition event.c:60
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_BOOL
A truth value.
@ FR_TYPE_OCTETS
Raw octets.
unsigned int uint32_t
unsigned char uint8_t
void * env_data
Per call environment data.
Definition module_ctx.h:44
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:42
void * thread
Thread specific instance data.
Definition module_ctx.h:43
void * rctx
Resume ctx that a module previously set.
Definition module_ctx.h:45
fr_event_list_t * el
Event list to register any IO handlers and timers against.
Definition module_ctx.h:68
module_instance_t * mi
Module instance to detach.
Definition module_ctx.h:57
void * thread
Thread instance data.
Definition module_ctx.h:67
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:64
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for module calls.
Definition module_ctx.h:41
Temporary structure to hold arguments for detach calls.
Definition module_ctx.h:56
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
Temporary structure to hold arguments for thread_instantiation calls.
Definition module_ctx.h:63
xlat_t * module_rlm_xlat_register(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, char const *name, xlat_func_t func, fr_type_t return_type)
Definition module_rlm.c:232
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
int fr_pair_append_by_da(TALLOC_CTX *ctx, fr_pair_t **out, fr_pair_list_t *list, fr_dict_attr_t const *da)
Alloc a new fr_pair_t (and append)
Definition pair.c:1471
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:707
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
static const conf_parser_t config[]
Definition base.c:163
static char const * url[FR_RADIUS_FAIL_MAX+1]
#define DEBUG2(fmt,...)
#define WARN(fmt,...)
void * fr_rb_iter_init_inorder(fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Initialise an in-order iterator.
Definition rb.c:824
void fr_rb_iter_delete_inorder(fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Remove the current node from the tree.
Definition rb.c:899
void * fr_rb_remove(fr_rb_tree_t *tree, void const *data)
Remove an entry from the tree, without freeing the data.
Definition rb.c:695
void * fr_rb_find(fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition rb.c:577
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:626
bool fr_rb_delete(fr_rb_tree_t *tree, void const *data)
Remove node and free data (if a free function was specified)
Definition rb.c:741
void * fr_rb_iter_next_inorder(UNUSED fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Return the next node.
Definition rb.c:850
#define fr_rb_inline_init(_tree, _type, _field, _data_cmp, _data_free)
Initialises a red black tree.
Definition rb.h:178
Iterator structure for in-order traversal of an rbtree.
Definition rb.h:319
The main red black tree structure.
Definition rb.h:71
#define RETURN_UNLANG_FAIL
Definition rcode.h:63
#define RETURN_UNLANG_REJECT
Definition rcode.h:62
#define RETURN_UNLANG_OK
Definition rcode.h:64
static int mod_detach(module_detach_ctx_t const *mctx)
Definition rlm_always.c:136
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition rlm_always.c:172
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
static fr_dict_attr_t const * attr_delta_crl
Definition rlm_crl.c:114
fr_value_box_list_t crl_data
Data from CRL expansion.
Definition rlm_crl.c:86
static fr_dict_attr_t const * attr_packet_type
Definition rlm_crl.c:115
fr_rb_node_t node
Node in the tree of failures.
Definition rlm_crl.c:66
fr_value_box_list_t delta_urls
URLs from which a delta CRL can be retrieved.
Definition rlm_crl.c:59
fr_coord_pair_reg_t * coord_pair_reg
coord_pair registration for fetching CRLs.
Definition rlm_crl.c:50
fr_coord_reg_t * coord_reg
coord registration for fetching CRLs.
Definition rlm_crl.c:51
crl_check_status_t
A status used to track which CRL is being checked.
Definition rlm_crl.c:77
@ CRL_CHECK_BASE
The base CRL is being checked.
Definition rlm_crl.c:78
@ CRL_CHECK_DELTA
The delta CRL exists and is being checked.
Definition rlm_crl.c:80
@ CRL_CHECK_FETCH_DELTA
The delta CRL is being fetched.
Definition rlm_crl.c:79
fr_value_box_t serial
The serial to check.
Definition rlm_crl.c:128
crl_ret_t
Definition rlm_crl.c:132
@ CRL_MISSING_DELTA
Need to load a delta CRL to supplement this CRL.
Definition rlm_crl.c:138
@ CRL_ENTRY_FOUND
Serial was found in this CRL.
Definition rlm_crl.c:135
@ CRL_ENTRY_REMOVED
Serial was "un-revoked" in this delta CRL.
Definition rlm_crl.c:136
@ CRL_ERROR
Unspecified error ocurred.
Definition rlm_crl.c:133
@ CRL_NOT_FOUND
No CRL found, need to load it from the CDP URL.
Definition rlm_crl.c:137
@ CRL_ENTRY_NOT_FOUND
Serial not found in this CRL.
Definition rlm_crl.c:134
fr_value_box_t * cdp_url
The URL we're currently attempting to load.
Definition rlm_crl.c:84
fr_dict_attr_autoload_t rlm_crl_dict_attr[]
Definition rlm_crl.c:118
char const * cdp_url
The URL which failed to fetch.
Definition rlm_crl.c:65
fr_coord_worker_t * cw
Worker side of coordinator communication.
Definition rlm_crl.c:42
static fr_dict_attr_t const * attr_base_crl
Definition rlm_crl.c:113
fr_time_t fail_time
When did the failure occur.
Definition rlm_crl.c:67
fr_rb_node_t node
The node in the tree.
Definition rlm_crl.c:58
char const * cdp_url
The URL of the CRL.
Definition rlm_crl.c:57
fr_rb_tree_t fails
Recent CRLs which have failed to fetch.
Definition rlm_crl.c:44
fr_rb_node_t node
Definition rlm_crl.c:73
X509_CRL * crl
The CRL.
Definition rlm_crl.c:56
request_t * request
Definition rlm_crl.c:72
fr_value_box_list_t missing_crls
CRLs missing from the tree.
Definition rlm_crl.c:87
rlm_crl_coord_callback_t
Callback IDs used by CRL coordinator calls.
Definition rlm_crl.c:99
@ CRL_COORD_PAIR_CALLBACK_ID
Definition rlm_crl.c:100
fr_rb_tree_t crls
CRLs fetched from the coordinator.
Definition rlm_crl.c:43
static fr_dict_attr_t const * attr_crl_cdp_url
Definition rlm_crl.c:112
crl_entry_t * base_crl
The base CRL relating to the delta currently being fetched.
Definition rlm_crl.c:85
static fr_dict_t const * dict_crl
Definition rlm_crl.c:103
char const ** urls
Initial list of URLs to fetch.
Definition rlm_crl.c:49
fr_rb_tree_t pending
Requests yielded while the CRL is being fetched.
Definition rlm_crl.c:41
fr_dict_autoload_t rlm_crl_dict[]
Definition rlm_crl.c:106
module_rlm_t rlm_crl
Definition rlm_crl.c:748
fr_value_box_list_head_t * cdp
The CRL distribution points.
Definition rlm_crl.c:129
fr_time_delta_t retry_delay
Time to hold off between CRL fetching failures.
Definition rlm_crl.c:48
static int mod_instantiate(module_inst_ctx_t const *mctx)
Instantiate the module.
Definition rlm_crl.c:713
static fr_dict_attr_t const * attr_crl_data
Definition rlm_crl.c:111
static conf_parser_t module_config[]
Definition rlm_crl.c:91
crl_check_status_t status
Status of the current CRL check.
Definition rlm_crl.c:88
A single CRL in the thread specific list of CRLs.
Definition rlm_crl.c:55
Structure to record recent fetch failures.
Definition rlm_crl.c:64
Structure to record a request which is waiting for CRL fetching to complete.
Definition rlm_crl.c:71
Thread specific structure to hold requests awaiting CRL fetching.
Definition rlm_crl.c:40
static char const * name
int fr_schedule_worker_id(void)
Return the worker id for the current thread.
Definition schedule.c:110
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:39
#define MODULE_THREAD_INST(_ctype)
Definition module.h:258
char const * name
Instance name e.g. user_database.
Definition module.h:357
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:351
size_t inst_size
Size of the module's instance data.
Definition module.h:212
void * data
Module's instance data.
Definition module.h:293
void * boot
Data allocated during the boostrap phase.
Definition module.h:296
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:152
Named methods exported by a module.
Definition module.h:174
fr_signal_t
Signals that can be generated/processed by request signal handlers.
Definition signal.h:38
@ FR_SIGNAL_CANCEL
Request has been cancelled.
Definition signal.h:40
unlang_action_t unlang_module_yield(request_t *request, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition module.c:431
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
#define talloc_get_type_abort_const
Definition talloc.h:110
#define talloc_strdup(_ctx, _str)
Definition talloc.h:142
#define fr_time_add(_a, _b)
Add a time/time delta together.
Definition time.h:196
#define fr_time_gt(_a, _b)
Definition time.h:237
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
@ T_BARE_WORD
Definition token.h:118
#define XLAT_ARGS(_list,...)
Populate local variables with value boxes from the input list.
Definition xlat.h:383
unsigned int required
Argument must be present, and non-empty.
Definition xlat.h:146
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:170
xlat_action_t
Definition xlat.h:37
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition xlat.h:44
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition xlat.h:43
Definition for a single argument consumed by an xlat function.
Definition xlat.h:145
#define fr_pair_list_append_by_da(_ctx, _vp, _list, _attr, _val, _tainted)
Append a pair to a list, assigning its value.
Definition pair.h:304
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
Definition value.c:4379
int fr_value_box_strdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Copy a nul terminated string to a fr_value_box_t.
Definition value.c:4604
void fr_value_box_strdup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Assign a buffer containing a nul terminated string to a box, but don't copy it.
Definition value.c:4714
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:644
static fr_slen_t data
Definition value.h:1340
static fr_value_box_t * fr_value_box_acopy(TALLOC_CTX *ctx, fr_value_box_t const *src)
Copy an existing box, allocating a new box to hold its contents.
Definition value.h:744
int nonnull(2, 5))
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition value.h:655
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:610
#define fr_value_box_list_foreach(_list_head, _iter)
Definition value.h:224
static size_t char ** out
Definition value.h:1030
module_ctx_t const * mctx
Synthesised module calling ctx.
Definition xlat_ctx.h:52
An xlat calling ctx.
Definition xlat_ctx.h:49
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition xlat_func.c:363