The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
base.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: 081f116cfa4b120119cebb92965e2bb273684649 $
19 * @file src/process/crl/base.c
20 * @brief State machine for CRL coordinator thread
21 *
22 * @copyright 2026 Network RADIUS SAS (legal@networkradius.com)
23 */
24#ifdef WITH_TLS
25#include <freeradius-devel/crl/crl.h>
26#include <freeradius-devel/io/coord_pair.h>
27#include <freeradius-devel/server/main_config.h>
28#include <freeradius-devel/server/protocol.h>
29#include <freeradius-devel/tls/strerror.h>
30#include <freeradius-devel/tls/utils.h>
31#include <freeradius-devel/unlang/interpret.h>
32#include <freeradius-devel/util/debug.h>
33
34#include <openssl/x509.h>
35#include <openssl/x509v3.h>
36#include <openssl/asn1.h>
37
38static fr_dict_t const *dict_crl;
39static fr_dict_t const *dict_freeradius;
40
41extern fr_dict_autoload_t process_crl_dict[];
42fr_dict_autoload_t process_crl_dict[] = {
43 { .out = &dict_crl, .proto = "crl" },
44 { .out = &dict_freeradius, .proto = "freeradius" },
46};
47
50static fr_dict_attr_t const *attr_base_crl;
51static fr_dict_attr_t const *attr_crl_data;
52static fr_dict_attr_t const *attr_crl_num;
53static fr_dict_attr_t const *attr_delta_crl;
54static fr_dict_attr_t const *attr_last_update;
55static fr_dict_attr_t const *attr_next_update;
56static fr_dict_attr_t const *attr_worker_id;
57
58extern fr_dict_attr_autoload_t process_crl_dict_attr[];
59fr_dict_attr_autoload_t process_crl_dict_attr[] = {
60 { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_crl },
61 { .out = &attr_crl_cdp_url, .name = "CDP-URL", .type = FR_TYPE_STRING, .dict = &dict_crl },
62 { .out = &attr_base_crl, .name = "Base-CRL", .type = FR_TYPE_STRING, .dict = &dict_crl },
63 { .out = &attr_crl_data, .name = "CRL-Data", .type = FR_TYPE_OCTETS, .dict = &dict_crl },
64 { .out = &attr_crl_num, .name = "CRL-Num", .type = FR_TYPE_UINT64, .dict = &dict_crl },
65 { .out = &attr_delta_crl, .name = "Delta-CRL", .type = FR_TYPE_STRING, .dict = &dict_crl },
66 { .out = &attr_last_update, .name = "Last-Update", .type = FR_TYPE_DATE, .dict = &dict_crl },
67 { .out = &attr_next_update, .name = "Next-Update", .type = FR_TYPE_DATE, .dict = &dict_crl },
68 { .out = &attr_worker_id, .name = "Worker-Id", .type = FR_TYPE_INT32, .dict = &dict_freeradius },
69
71};
72
73typedef struct {
74 uint64_t nothing; // so that the next field isn't at offset 0
75
76 CONF_SECTION *crl_fetch;
77 CONF_SECTION *fetch_ok;
78 CONF_SECTION *fetch_fail;
79 CONF_SECTION *do_not_respond;
80} process_crl_sections_t;
81
82typedef struct {
83 fr_rb_tree_t crls; //!< Fetched CRL data.
84 fr_dlist_head_t fetching; //!< List of CRLs currently being fetched.
85} process_crl_mutable_t;
86
87typedef struct {
88 process_crl_sections_t sections; //!< Pointers to various config sections
89 ///< we need to execute
90
91 fr_time_delta_t force_refresh; //!< Force refresh of CRLs after this time
92 bool force_refresh_is_set;
93 fr_time_delta_t force_delta_refresh; //!< Force refresh of delta CRLs after this time
94 bool force_delta_refresh_is_set;
95 fr_time_delta_t early_refresh; //!< Time interval before nextUpdate to refresh
96 fr_time_delta_t retry_delay; //!< Delay between retries of failed refreshes.
97 char const *ca_file; //!< File containing certs for verifying CRL signatures.
98 char const *ca_path; //!< Directory containing certs for verifying CRL signatures.
99
100 bool allow_expired; //!< Will CRLs be accepted after nextUpdate
101 bool allow_not_yet_valid; //!< Will CRLs be accepted before lastUpdate
102
103 X509_STORE *verify_store; //!< Store of certificates to verify CRL signatures;
104
105 process_crl_mutable_t *mutable; //!< Mutable data
106} process_crl_t;
107
108typedef struct {
109 fr_event_list_t *el; //!< Event list for CRL refresh events.
110} process_thread_crl_t;
111
112static const conf_parser_t config[] = {
113 { FR_CONF_OFFSET_IS_SET("force_refresh", FR_TYPE_TIME_DELTA, 0, process_crl_t, force_refresh) },
114 { FR_CONF_OFFSET_IS_SET("force_delta_refresh", FR_TYPE_TIME_DELTA, 0, process_crl_t, force_delta_refresh) },
115 { FR_CONF_OFFSET("early_refresh", process_crl_t, early_refresh) },
116 { FR_CONF_OFFSET("retry_delay", process_crl_t, retry_delay), .dflt = "30s" },
117 { FR_CONF_OFFSET("ca_file", process_crl_t, ca_file) },
118 { FR_CONF_OFFSET("ca_path", process_crl_t, ca_path) },
119 { FR_CONF_OFFSET("allow_expired", process_crl_t, allow_expired) },
120 { FR_CONF_OFFSET("allow_not_yet_valid", process_crl_t, allow_not_yet_valid) },
122};
123
124typedef enum {
125 CRL_TYPE_BASE,
126 CRL_TYPE_DELTA
127} crl_type_t;
128
129/** A single CRL in the list of CRLs
130 */
131typedef struct {
132 char const *cdp_url; //!< The URL of the CRL.
133 crl_type_t type; //!< What type of CRL is this.
134 uint8_t *crl_data; //!< The CRL data.
135 time_t last_update; //!< Last update value extracted from the CRL.
136 time_t next_update; //!< Next update value extracted from the CRL.
137 ASN1_INTEGER *crl_num; //!< The CRL number.
138 fr_rb_node_t node; //!< The node in the tree of CRLs;
139 fr_time_t refresh; //!< Refresh time of the CRL.
140 union {
141 fr_value_box_list_t delta_urls; //!< URLs from which a delta CRL can be retrieved
142 char const *base_url; //!< Base URL if this is a delta
143 };
144 process_crl_t *inst; //!< Module instance this entry is associated with.
145 fr_coord_pair_t *coord_pair; //!< The coord_pair which requested this CRL.
146 fr_timer_t *ev; //!< Timer event for renewal.
148
149/** An entry in the list of CRLs currently being fetched.
150 */
151typedef struct {
152 char const *cdp_url; //!< URL being fetched.
153 bool *workers; //!< True for each worker waiting for a response.
154 fr_dlist_t entry; //!< Entry in list of CRLs being fetched.
155} crl_fetch_t;
156
157/** An entry in the list of CRL requests pending a currently running fetch
158 */
159typedef struct {
160 request_t *request;
161 int32_t worker_id;
162 fr_dlist_t entry;
163} crl_pending_request_t;
164
165/** Compare two CRLs in the list of entries by URL
166 */
167static int8_t crl_cmp(void const *a, void const *b)
168{
169 crl_entry_t const *crl_a = (crl_entry_t const *)a;
170 crl_entry_t const *crl_b = (crl_entry_t const *)b;
171
172 return CMP(strcmp(crl_a->cdp_url, crl_b->cdp_url), 0);
173}
174
175/** Resume context for CRL requests */
176typedef struct {
177 unlang_result_t result; //!< Where process section results are written to
178 int32_t worker_id; //!< The worker which sent the data leading to this request.
179 char const *cdp_url; //!< The URL of the CRL being requested.
180 crl_entry_t *crl_entry; //!< The existing instance a CRL, if previously fetched.
181 crl_entry_t *base_crl; //!< The base CRL a delta CRL is related to.
182 bool cached; //!< This is a cached response.
183 bool refresh; //!< Is this a refresh request.
184} process_crl_rctx_t;
185
186#define FR_CRL_PACKET_CODE_VALID(_code) (((_code) > 0) && ((_code) < FR_CRL_CODE_MAX))
187#define FR_CRL_PROCESS_CODE_VALID(_code) (FR_CRL_PACKET_CODE_VALID(_code) || (_code == FR_CRL_DO_NOT_RESPOND))
188
189#define PROCESS_PACKET_TYPE fr_crl_packet_code_t
190#define PROCESS_CODE_MAX FR_CRL_CODE_MAX
191#define PROCESS_CODE_DO_NOT_RESPOND FR_CRL_DO_NOT_RESPOND
192#define PROCESS_PACKET_CODE_VALID FR_CRL_PROCESS_CODE_VALID
193#define PROCESS_INST process_crl_t
194#define PROCESS_RCTX process_crl_rctx_t
195
196#include <freeradius-devel/server/process.h>
197
198RECV(crl_fetch);
199RECV(crl_refresh);
200
201/** Common setup used by both CRL-Fetch and CRL-Refresh
202 */
203static unlang_action_t fetch_setup_common(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request,
204 fr_pair_t *vp, process_crl_rctx_t *rctx)
205{
206 process_crl_t *inst = talloc_get_type_abort(mctx->mi->data, process_crl_t);
207 crl_fetch_t *fetch;
208
209 /*
210 * Check to see if we're already fetching this URL. If so, record
211 * that this worker wants the reply, and just Do Not Respond.
212 */
213 fr_dlist_foreach(&inst->mutable->fetching, crl_fetch_t, fetching) {
214 if (strcmp(fetching->cdp_url, vp->vp_strvalue) == 0) {
215 if (rctx->worker_id >= 0) fetching->workers[rctx->worker_id] = true;
216 RDEBUG2("CRL already being fetched");
217 return CALL_SEND_TYPE(FR_CRL_DO_NOT_RESPOND);
218 }
219 }
220
221 /*
222 * Cache the URI / Base CRL incase the pair gets altered / deleted in the process section.
223 */
224 rctx->cdp_url = talloc_bstrdup(rctx, vp->vp_strvalue);
225
226 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_base_crl);
227 if (vp) {
228 crl_entry_t find;
229 find.cdp_url = vp->vp_strvalue;
230 rctx->base_crl = fr_rb_find(&inst->mutable->crls, &find);
231 }
232
233 /*
234 * Record what CRL we're fetching in the list.
235 */
236 MEM(fetch = talloc(inst->mutable, crl_fetch_t));
237 fetch->cdp_url = rctx->cdp_url;
238 fetch->workers = talloc_zero_array(fetch, bool, main_config->max_workers);
239 fr_dlist_insert_tail(&inst->mutable->fetching, fetch);
240
241 return CALL_RECV(generic);
242}
243
244/** Build reply pairs from a CRL
245 *
246 * Pairs are marked as immutable to prevent changes when run through send Fetch-OK
247 */
248static int crl_build_reply(request_t *request, crl_entry_t *crl_entry)
249{
250 fr_pair_t *vp;
251
252 if (pair_update_reply(&vp, attr_crl_cdp_url) < 0) return -1;;
254 fr_value_box_strdup(vp, &vp->data, NULL, crl_entry->cdp_url, false);
256
257 if (fr_pair_append_by_da(request->reply_ctx, &vp, &request->reply_pairs, attr_crl_num)< 0) return -1;
258 ASN1_INTEGER_get_uint64(&vp->vp_uint64, crl_entry->crl_num);
260
261 if (crl_entry->type == CRL_TYPE_BASE) {
262 fr_value_box_list_foreach(&crl_entry->delta_urls, delta_url) {
263 if (fr_pair_append_by_da(request->reply_ctx, &vp, &request->reply_pairs,
264 attr_delta_crl) < 0) return -1;
265 if (unlikely(fr_value_box_copy(vp, &vp->data, delta_url) < 0)) {
266 fr_pair_remove(&request->reply_pairs, vp);
268 }
270 }
271 }
272
273 if (fr_pair_append_by_da(request->reply_ctx, &vp, &request->reply_pairs, attr_last_update) < 0) return -1;
274 vp->vp_date = fr_unix_time_from_sec(crl_entry->last_update);
276
277 if (fr_pair_append_by_da(request->reply_ctx, &vp, &request->reply_pairs, attr_next_update) < 0) return -1;
278 vp->vp_date = fr_unix_time_from_sec(crl_entry->next_update);
280
281 return 0;
282}
283
284/** CRL-Fetch packets are sent from workers to the coordinator.
285 *
286 * They can reply with cached data if it exists, otherwise a fetch will
287 * be initiated.
288 *
289 * Replies will always be sent back.
290 */
291RECV(crl_fetch)
292{
293 process_crl_t *inst = talloc_get_type_abort(mctx->mi->data, process_crl_t);
294 process_crl_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, process_crl_rctx_t);
295 fr_pair_t *vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_worker_id);
296 crl_entry_t find, *crl_entry;
297
298 if (!vp) return UNLANG_ACTION_FAIL;
299 rctx->worker_id = vp->vp_int32;
300
301 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_crl_cdp_url);
302 if (!vp) return UNLANG_ACTION_FAIL;
303
304 find.cdp_url = vp->vp_strvalue;
305 rctx->crl_entry = crl_entry = fr_rb_find(&inst->mutable->crls, &find);
306
307 /*
308 * If we found the data, send it back without running the process section as long as it's not expired.
309 */
310 if (crl_entry && (inst->allow_expired || fr_time_gt(fr_time_from_sec(crl_entry->next_update),
311 fr_time_from_sec(time(NULL))))) {
312 if (fr_pair_append_by_da(request->reply_ctx, &vp, &request->reply_pairs,
313 attr_crl_data) < 0) return CALL_SEND_TYPE(FR_CRL_FETCH_FAIL);
314 fr_value_box_memdup_buffer(vp, &vp->data, NULL, crl_entry->crl_data, false);
316
317 if (crl_build_reply(request, crl_entry) < 0) return CALL_SEND_TYPE(FR_CRL_FETCH_FAIL);
318
319 rctx->cached = true;
320 return CALL_SEND_TYPE(FR_CRL_FETCH_OK);
321 }
322
323 return fetch_setup_common(p_result, mctx, request, vp, rctx);
324}
325
326/** CRL-Refresh packets are generated within the coordinator by refresh timer events
327 *
328 * Fetching of CRL data is run through the recv Fetch-CRL process section.
329 * Data will only be sent to workers if the refresh fetched a newer CRL.
330 */
331RECV(crl_refresh) {
332 process_crl_t *inst = talloc_get_type_abort(mctx->mi->data, process_crl_t);
333 process_crl_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, process_crl_rctx_t);
334 fr_pair_t *vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_crl_cdp_url);
335 crl_entry_t find;
336
337 if (!vp) return UNLANG_ACTION_FAIL;
338
339 find.cdp_url = vp->vp_strvalue;
340 rctx->crl_entry = fr_rb_find(&inst->mutable->crls, &find);
341 if (rctx->crl_entry) rctx->refresh = true;
342
343 return fetch_setup_common(p_result, mctx, request, vp, rctx);
344}
345
346/** Tidy up CRL entries when freeing.
347 */
348static int _crl_entry_free(crl_entry_t *to_free)
349{
350 if (to_free->crl_num) ASN1_INTEGER_free(to_free->crl_num);
351
352 /*
353 * Ensure the entry is no-longer in the tree.
354 */
355 if (fr_rb_node_inline_in_tree(&to_free->node)) fr_rb_remove(&to_free->inst->mutable->crls, to_free);
356
357 /*
358 * If the entry referenced deltas, those must be removed as well.
359 */
360 if ((to_free->type == CRL_TYPE_BASE) && fr_value_box_list_initialised(&to_free->delta_urls)) {
361 fr_value_box_list_foreach(&to_free->delta_urls, delta) {
362 crl_entry_t find, *delta_crl;
363
364 find.cdp_url = delta->vb_strvalue;
365 delta_crl = fr_rb_find(&to_free->inst->mutable->crls, &find);
366 if (!delta_crl) continue;
367
368 fr_rb_remove(&to_free->inst->mutable->crls, delta_crl);
369 talloc_free(delta_crl);
370 }
371 }
372
373 return 0;
374}
375
376/** Event callback to trigger the refresh of a CRL
377 */
378static void crl_refresh_event(fr_timer_list_t *tl, fr_time_t now, void *uctx)
379{
380 crl_entry_t *crl_entry = talloc_get_type_abort(uctx, crl_entry_t);
381 fr_pair_list_t list;
382 fr_pair_t *vp;
383 TALLOC_CTX *local = talloc_new(NULL);
384
385 DEBUG2("Refreshing CRL from CDP %s", crl_entry->cdp_url);
386
387 fr_pair_list_init(&list);
389 if (!vp) {
390 fail:
391 talloc_free(local);
392 return;
393 }
394
395 if (fr_pair_append_by_da(local, &vp, &list, attr_crl_cdp_url) < 0) goto fail;
396 fr_value_box_strdup(vp, &vp->data, NULL, crl_entry->cdp_url, false);
397
398 if (crl_entry->type == CRL_TYPE_DELTA) {
399 if (fr_pair_append_by_da(local, &vp, &list, attr_base_crl) < 0) goto fail;
400 fr_value_box_strdup(vp, &vp->data, NULL, crl_entry->cdp_url, false);
401 }
402
403 if (fr_coord_pair_coord_request_start(crl_entry->coord_pair, &list, now) < 0) {
404 ERROR("Failed to initialise CRL refresh request");
405 if (fr_timer_in(crl_entry, tl, &crl_entry->ev, crl_entry->inst->retry_delay, false,
406 crl_refresh_event, crl_entry) <0) {
407 ERROR("Failed to set timer to retry CRL refresh");
408 }
409 }
410
411 talloc_free(local);
412}
413
414static inline void crl_fetching_entry_remove(fr_dlist_head_t *fetching, char const *cdp_url) {
415 fr_dlist_foreach(fetching, crl_fetch_t, fetch) {
416 if (strcmp(fetch->cdp_url, cdp_url) == 0) {
417 fr_dlist_remove(fetching, fetch);
418 talloc_free(fetch);
419 return;
420 }
421 }
422}
423
424RESUME(crl_fetch)
425{
426 process_crl_t *inst = talloc_get_type_abort(mctx->mi->data, process_crl_t);
427 process_thread_crl_t *thread = talloc_get_type_abort(mctx->thread, process_thread_crl_t);
428 process_crl_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, process_crl_rctx_t);
429 crl_entry_t *crl_entry = NULL;
430 fr_pair_t *crl_data;
431 uint8_t const *data;
432 X509_CRL *crl;
433 X509_STORE_CTX *verify_ctx = NULL;
434 X509_OBJECT *xobj;
435 EVP_PKEY *pkey;
436 STACK_OF(DIST_POINT) *dps;
437 int i;
438 fr_time_t now = fr_time();
439 fr_time_delta_t refresh_delta;
440
441 switch (RESULT_RCODE) {
443 return CALL_RESUME(recv_generic);
444 default:
445 break;
446 }
447
448 /*
449 * If no CRL data was returned, that's a failure
450 */
451 crl_data = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_crl_data);
452 if (!crl_data) {
453 RERROR("No %s found", attr_crl_data->name);
454 return CALL_SEND_TYPE(FR_CRL_FETCH_FAIL);
455 }
456 fr_pair_immutable(crl_data);
457
458 /*
459 * Parse the CRL data and verify it is correctly signed
460 */
461 data = crl_data->vp_octets;
462 crl = d2i_X509_CRL(NULL, (const unsigned char **)&data, crl_data->vp_size);
463 if (!crl) {
464 fr_tls_strerror_printf("Failed to parse CRL from %s", rctx->cdp_url);
465 return CALL_SEND_TYPE(FR_CRL_FETCH_FAIL);
466 }
467
468 verify_ctx = X509_STORE_CTX_new();
469 if (!verify_ctx || !X509_STORE_CTX_init(verify_ctx, inst->verify_store, NULL, NULL)) {
470 fr_tls_strerror_printf("Error initialising X509 store");
471 error:
472 if (verify_ctx) X509_STORE_CTX_free(verify_ctx);
473 RPERROR("Error verifying CRL");
474 X509_CRL_free(crl);
475 talloc_free(crl_entry);
476 return CALL_SEND_TYPE(FR_CRL_FETCH_FAIL);
477 }
478
479 xobj = X509_STORE_CTX_get_obj_by_subject(verify_ctx, X509_LU_X509, X509_CRL_get_issuer(crl));
480 if (!xobj) {
481 fr_tls_strerror_printf("CRL issuer certificate not in trusted store");
482 goto error;
483 }
484 pkey = X509_get_pubkey(X509_OBJECT_get0_X509(xobj));
485 X509_OBJECT_free(xobj);
486 if (!pkey) {
487 fr_tls_strerror_printf("Error getting CRL issuer public key");
488 goto error;
489 }
490 i = X509_CRL_verify(crl, pkey);
491 EVP_PKEY_free(pkey);
492
493 if (i < 0) {
494 fr_tls_strerror_printf("Could not verify CRL signature");
495 goto error;
496 }
497 if (i == 0) {
498 fr_tls_strerror_printf("CRL certificate signature failed");
499 goto error;
500 }
501
502 /*
503 * Now we have a verified CRL, start building the entry for the global list
504 */
505 MEM(crl_entry = talloc_zero(inst->mutable, crl_entry_t));
506 crl_entry->inst = inst;
507
508 /*
509 * If we're passed a base_crl, then this is a delta.
510 */
511 crl_entry->type = rctx->base_crl ? CRL_TYPE_DELTA : CRL_TYPE_BASE;
512 talloc_set_destructor(crl_entry, _crl_entry_free);
513
514 if (fr_tls_utils_asn1time_to_epoch(&crl_entry->next_update, X509_CRL_get0_nextUpdate(crl)) < 0) {
515 RPERROR("Failed to parse nextUpdate from CRL");
516 goto error;
517 }
518
519 if (!inst->allow_expired && fr_time_lt(fr_time_from_sec(crl_entry->next_update),
520 fr_time_from_sec(time(NULL)))) {
521 RPERROR("Fetched CRL expired at %pV", fr_box_time(fr_time_from_sec(crl_entry->next_update)));
522 goto error;
523 }
524
525 crl_entry->crl_num = X509_CRL_get_ext_d2i(crl, NID_crl_number, &i, NULL);
526 if (!crl_entry->crl_num) {
527 fr_tls_strerror_printf("Missing CRL number");
528 goto error;
529 }
530
531 /*
532 * If there is existing data for this CRL, the it should only be updated if the number has increased.
533 */
534 if (rctx->crl_entry) {
535 if (ASN1_INTEGER_cmp(crl_entry->crl_num, rctx->crl_entry->crl_num) == 0) {
536 uint64_t new_num;
537 ASN1_INTEGER_get_uint64(&new_num, crl_entry->crl_num);
538 RDEBUG3("Refresh returned the same CRL number (%"PRIu64") as the existing entry", new_num);
539 goto use_old;
540 }
541 if (ASN1_INTEGER_cmp(crl_entry->crl_num, rctx->crl_entry->crl_num) < 0) {
542 uint64_t old_num, new_num;
543 ASN1_INTEGER_get_uint64(&old_num, rctx->crl_entry->crl_num);
544 ASN1_INTEGER_get_uint64(&new_num, crl_entry->crl_num);
545 RERROR("Got CRL number %"PRIu64" which is less than current number %"PRIu64,
546 new_num, old_num);
547 use_old:
548 crl_fetching_entry_remove(&inst->mutable->fetching, rctx->cdp_url);
549 talloc_free(crl_entry);
550 crl_entry = rctx->crl_entry;
551
552 if (rctx->refresh) {
553 /*
554 * A refresh which didn't produce updated data means noop.
555 */
556 rctx->result.rcode = RLM_MODULE_NOOP;
557 } else {
558 /*
559 * CRL-Fetch requests expect a response.
560 */
561 if (crl_build_reply(request, crl_entry) < 0) goto error;
562 }
563 goto timer;
564 }
565
566 /*
567 * The fetched entry data is newer, free the old entry.
568 * This will also remove it from the tree and clear any
569 * related deltas.
570 */
571 TALLOC_FREE(rctx->crl_entry);
572 }
573
574 /*
575 * The fetched CRL is now validated and newer than the old
576 * entry, complete building the entry.
577 */
578 crl_entry->cdp_url = talloc_bstrdup(crl_entry, rctx->cdp_url);
579 crl_entry->crl_data = talloc_typed_memdup(crl_entry, crl_data->vp_octets, crl_data->vp_length);
580 crl_entry->coord_pair = fr_coord_pair_request_coord_pair(request);
581
582 /*
583 * If this is a delta check it relates to the correct base.
584 */
585 if (crl_entry->type == CRL_TYPE_DELTA) {
586 ASN1_INTEGER *base_num = X509_CRL_get_ext_d2i(crl, NID_delta_crl, &i, NULL);
587
588#ifdef __clang_analyzer__
589 /*
590 * type is set to CRL_TYPE_DELTA by the presence of a base_crl
591 * but the analysers don't detect this.
592 */
593 if (unlikely(!rctx->base_crl)) goto error;
594#endif
595 fr_assert(rctx->base_crl);
596 if (!base_num) {
597 RERROR("Delta CRL missing Delta CRL Indicator extension");
598 goto error;
599 }
600 if (ASN1_INTEGER_cmp(base_num, rctx->base_crl->crl_num) > 0) {
601 uint64_t delta_base, crl_num;
602 ASN1_INTEGER_get_uint64(&delta_base, base_num);
603 ASN1_INTEGER_get_uint64(&crl_num, rctx->base_crl->crl_num);
604 RERROR("Delta CRL referrs to base CRL number %"PRIu64", current base is %"PRIu64,
605 delta_base, crl_num);
606 ASN1_INTEGER_free(base_num);
607 goto error;
608 }
609 ASN1_INTEGER_free(base_num);
610 if (ASN1_INTEGER_cmp(crl_entry->crl_num, rctx->base_crl->crl_num) < 0) {
611 uint64_t delta_num, crl_num;
612 ASN1_INTEGER_get_uint64(&delta_num, crl_entry->crl_num);
613 ASN1_INTEGER_get_uint64(&crl_num, rctx->base_crl->crl_num);
614 RERROR("Delta CRL number %"PRIu64" is less than base CRL number %"PRIu64, delta_num, crl_num);
615 goto error;
616 }
617 crl_entry->base_url = talloc_strdup(crl_entry, rctx->base_crl->cdp_url);
618 }
619
620 if (fr_tls_utils_asn1time_to_epoch(&crl_entry->last_update, X509_CRL_get0_lastUpdate(crl)) < 0) {
621 RPERROR("Failed to parse lastUpdate from CRL");
622 goto error;
623 }
624
625 if (!inst->allow_not_yet_valid && fr_time_gt(fr_time_from_sec(crl_entry->last_update),
626 fr_time_from_sec(time(NULL)))) {
627 RPERROR("Fetched CRL is not valid until %pV", fr_box_time(fr_time_from_sec(crl_entry->last_update)));
628 goto error;
629 }
630
631 /*
632 * Check if this CRL has a Freshest CRL extension - the list of URIs to get deltas from
633 */
634 if ((crl_entry->type == CRL_TYPE_BASE) && (dps = X509_CRL_get_ext_d2i(crl, NID_freshest_crl, NULL, NULL))) {
635 DIST_POINT *dp;
636 STACK_OF(GENERAL_NAME) *names;
637 GENERAL_NAME *name;
638 int j;
639 fr_value_box_t *vb;
640
641 fr_value_box_list_init(&crl_entry->delta_urls);
642 for (i = 0; i < sk_DIST_POINT_num(dps); i++) {
643 dp = sk_DIST_POINT_value(dps, i);
644 names = dp->distpoint->name.fullname;
645 for (j = 0; j < sk_GENERAL_NAME_num(names); j++) {
646 name = sk_GENERAL_NAME_value(names, j);
647 if (name->type != GEN_URI) continue;
648 MEM(vb = fr_value_box_alloc_null(crl_entry));
649 fr_value_box_bstrndup(vb, vb, NULL,
650 (char const *)ASN1_STRING_get0_data(name->d.uniformResourceIdentifier),
651 ASN1_STRING_length(name->d.uniformResourceIdentifier), true);
652 RDEBUG3("CRL references delta URI %pV", vb);
653 fr_value_box_list_insert_tail(&crl_entry->delta_urls, vb);
654 }
655 }
656 CRL_DIST_POINTS_free(dps);
657
658 /*
659 * If the CRL has a delta then fetch it. An updated CRL means the
660 * delta must be updated or it's invalid as it will point to the wrong
661 * version of the base CRL.
662 */
663 if (fr_value_box_list_num_elements(&crl_entry->delta_urls) > 0) {
664 fr_pair_list_t list;
665 fr_pair_t *vp;
666 TALLOC_CTX *local = talloc_new(NULL);
667
668 fr_pair_list_init(&list);
670 if (!vp) goto free_local;
671
672 fr_value_box_list_foreach(&crl_entry->delta_urls, delta) {
673 if (fr_pair_append_by_da(local, &vp, &list, attr_crl_cdp_url) < 0) goto free_local;
674 if (unlikely(fr_value_box_copy(vp, &vp->data, delta) < 0)) goto free_local;
675 }
676
677 if (fr_pair_append_by_da(local, &vp, &list, attr_base_crl) < 0) goto free_local;
678 if (unlikely(fr_value_box_strdup(vp, &vp->data, NULL, rctx->cdp_url,
679 false) < 0)) goto free_local;
680
681 if (fr_coord_pair_coord_request_start(crl_entry->coord_pair, &list, now) < 0) {
682 RERROR("Failed to start fetch of delta CRL");
683 }
684
685 free_local:
686 talloc_free(local);
687 }
688 }
689
690 if (!fr_rb_insert(&inst->mutable->crls, crl_entry)) {
691 RERROR("Failed storing CRL");
692 goto error;
693 }
694
695 if (crl_build_reply(request, crl_entry) < 0) goto error;
696
697timer:
698 /*
699 * Setup a timer to refresh the CRL
700 */
701 crl_entry->refresh = fr_time_from_sec(crl_entry->next_update);
702 refresh_delta = fr_time_delta_sub(fr_time_sub(crl_entry->refresh, now), inst->early_refresh);
703 if (rctx->base_crl && inst->force_delta_refresh_is_set) {
704 if (fr_time_delta_cmp(refresh_delta, inst->force_delta_refresh)) refresh_delta = inst->force_delta_refresh;
705 } else {
706 if (inst->force_refresh_is_set &&
707 (fr_time_delta_cmp(refresh_delta, inst->force_refresh) > 0)) refresh_delta = inst->force_refresh;
708 }
709
710 /*
711 * A negative expiry time will occur if a refresh fails to fetch a newer CRL
712 * or the CRL has already expired. In that case use retry_delay to rate limit retrys.
713 */
714 if (fr_time_delta_isneg(refresh_delta)) refresh_delta = inst->retry_delay;
715
716 RDEBUG2("CRL from %s will refresh in %pVs", rctx->cdp_url, fr_box_time_delta(refresh_delta));
717
718 if (fr_timer_in(crl_entry, thread->el->tl, &crl_entry->ev, refresh_delta, false, crl_refresh_event, crl_entry) <0) {
719 RERROR("Failed to set timer to refresh CRL");
720 }
721
722 X509_STORE_CTX_free(verify_ctx);
723 X509_CRL_free(crl);
724
725 return CALL_RESUME(recv_generic);
726}
727
728RESUME_FLAG(send_crl_ok, UNUSED,)
729{
730 process_crl_t *inst = talloc_get_type_abort(mctx->mi->data, process_crl_t);
731 process_crl_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, process_crl_rctx_t);
732
733 /*
734 * Remove the fetching entry. We use broadcast for the reply after
735 * a fetch so no need to individually send to workers.
736 */
737 crl_fetching_entry_remove(&inst->mutable->fetching, rctx->cdp_url);
738
739 if (rctx->cached) {
740 /*
741 * Cached replies are only sent to the worker which requested the data.
742 */
743 RDEBUG3("Sending cached CRL to worker %d", rctx->worker_id);
744 fr_coord_to_worker_reply_send(request, rctx->worker_id);
745 } else {
746 /*
747 * Successful update of a CRL is broadcast to all clients.
748 */
749 RDEBUG3("Sending updated CRL to all workers");
751 }
752
754}
755
756RESUME_FLAG(send_crl_fail, UNUSED,)
757{
758 process_crl_t *inst = talloc_get_type_abort(mctx->mi->data, process_crl_t);
759 process_thread_crl_t *thread = talloc_get_type_abort(mctx->thread, process_thread_crl_t);
760 process_crl_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, process_crl_rctx_t);
761 fr_pair_t *vp;
762
763 if (rctx->refresh) {
764 crl_fetching_entry_remove(&inst->mutable->fetching, rctx->cdp_url);
765
766 /*
767 * Set up retry of failed refresh.
768 */
769 RDEBUG2("Refresh of CRL from %s will retry in %pVs", rctx->cdp_url, fr_box_time_delta(inst->retry_delay));
770
771 if (fr_timer_in(rctx->crl_entry, thread->el->tl, &rctx->crl_entry->ev, inst->retry_delay,
772 false, crl_refresh_event, rctx->crl_entry) <0) {
773 RERROR("Failed to set timer to retry CRL fetch");
774 }
775
776 /*
777 * Refresh requests are local to the coordinator, so there
778 * is nothing to send back on a failure, unless the CRL has
779 * expired and expired CRLs are not allowed.
780 */
781 if (inst->allow_expired || fr_time_gt(fr_time_from_sec(rctx->crl_entry->next_update),
782 fr_time_from_sec(time(NULL)))) {
784 }
785
786 /*
787 * A refresh failed on an expired CRL, notify all workers with a CRL-Expire reply.
788 */
789 fr_pair_list_free(&request->reply_pairs);
790 if (fr_pair_append_by_da(request->reply_ctx, &vp, &request->reply_pairs,
792 vp->vp_uint32 = FR_CRL_CRL_EXPIRE;
793
794 if (fr_pair_append_by_da(request->reply_ctx, &vp, &request->reply_pairs,
796 fr_value_box_strdup(vp, &vp->data, NULL, rctx->cdp_url, false);
797
799
801 }
802
803 /*
804 * The fetch failed, ensure we don't send CRL data back
805 */
806 fr_pair_delete_by_da(&request->reply_pairs, attr_crl_data);
807
809 fr_value_box_strdup(vp, &vp->data, NULL, rctx->cdp_url, false);
810
811 RDEBUG3("Sending fail to worker %d", rctx->worker_id);
812 fr_coord_to_worker_reply_send(request, rctx->worker_id);
813
814 /*
815 * If any other workers were waiting for this CRL, send them
816 * the failure as well.
817 */
818 fr_dlist_foreach(&inst->mutable->fetching, crl_fetch_t, fetch) {
819 if (strcmp(fetch->cdp_url, rctx->cdp_url) == 0) {
820 uint32_t i;
821 fr_dlist_remove(&inst->mutable->fetching, fetch);
822 for (i = 0; i < main_config->max_workers; i++) {
823 if (fetch->workers[i]) {
825 RDEBUG3("Sending fail to worker %d", i);
826 }
827 }
828 talloc_free(fetch);
829 break;
830 }
831 }
832
834}
835
836static unlang_action_t mod_process(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
837{
838 fr_process_state_t const *state;
839
841
842 (void)talloc_get_type_abort_const(mctx->mi->data, process_crl_t);
843 fr_assert(FR_CRL_PACKET_CODE_VALID(request->packet->code));
844
845 request->component = "crl";
846 request->module = NULL;
847 fr_assert(request->proto_dict == dict_crl);
848
849 UPDATE_STATE(packet);
850
851 if (!state->recv) {
852 REDEBUG("Invalid packet type (%u)", request->packet->code);
854 }
855
856 log_request_pair_list(L_DBG_LVL_1, request, NULL, &request->request_pairs, NULL);
857
858 return state->recv(p_result, mctx, request);
859}
860
861static int mod_instantiate(module_inst_ctx_t const *mctx)
862{
863 process_crl_t *inst = talloc_get_type_abort(mctx->mi->data, process_crl_t);
864
865 MEM(inst->mutable = talloc_zero(NULL, process_crl_mutable_t));
866
867 fr_rb_inline_init(&inst->mutable->crls, crl_entry_t, node, crl_cmp, NULL);
868 fr_dlist_init(&inst->mutable->fetching, crl_fetch_t, entry);
869
870 inst->verify_store = X509_STORE_new();
871 if (!X509_STORE_load_locations(inst->verify_store, inst->ca_file, inst->ca_path)) {
872 cf_log_err(mctx->mi->conf, "Failed reading Trusted root CA file \"%s\" and path \"%s\"",
873 inst->ca_file, inst->ca_path);
874 return -1;
875 }
876
877 X509_STORE_set_purpose(inst->verify_store, X509_PURPOSE_SSL_CLIENT);
878
879 return 0;
880}
881
882static int mod_detach(module_detach_ctx_t const *mctx)
883{
884 process_crl_t *inst = talloc_get_type_abort(mctx->mi->data, process_crl_t);
885
886 if (inst->verify_store) X509_STORE_free(inst->verify_store);
887 talloc_free(inst->mutable);
888
889 return 0;
890}
891
893{
894 process_thread_crl_t *t = talloc_get_type_abort(mctx->thread, process_thread_crl_t);
895
896 t->el = mctx->el;
897 return 0;
898}
899
900static fr_process_state_t const process_state[] = {
901 /*
902 * Fetch a CRL
903 */
904 [ FR_CRL_CRL_FETCH ] = {
905 .packet_type = {
915 },
916 .default_rcode = RLM_MODULE_NOOP,
917 .recv = recv_crl_fetch,
918 .resume = resume_crl_fetch,
919 .section_offset = offsetof(process_crl_sections_t, crl_fetch),
920 },
921 [ FR_CRL_CRL_REFRESH ] = {
922 .packet_type = {
932 },
933 .default_rcode = RLM_MODULE_NOOP,
934 .recv = recv_crl_refresh,
935 .resume = resume_crl_fetch,
936 .section_offset = offsetof(process_crl_sections_t, crl_fetch),
937 },
938 [ FR_CRL_FETCH_OK ] = {
939 .packet_type = {
945 },
946 .default_rcode = RLM_MODULE_NOOP,
947 .result_rcode = RLM_MODULE_OK,
948 .send = send_generic,
949 .resume = resume_send_crl_ok,
950 .section_offset = offsetof(process_crl_sections_t, fetch_ok),
951 },
952 [ FR_CRL_FETCH_FAIL ] = {
953 .packet_type = {
959 },
960 .default_rcode = RLM_MODULE_NOOP,
961 .result_rcode = RLM_MODULE_REJECT,
962 .send = send_generic,
963 .resume = resume_send_crl_fail,
964 .section_offset = offsetof(process_crl_sections_t, fetch_fail),
965 },
967 .packet_type = {
972
979 },
980 .default_rcode = RLM_MODULE_NOOP,
981 .result_rcode = RLM_MODULE_HANDLED,
982 .send = send_generic,
983 .resume = resume_send_generic,
984 .section_offset = offsetof(process_crl_sections_t, do_not_respond),
985 }
986};
987
989 {
990 .section = SECTION_NAME("recv", "CRL-Fetch"),
991 .actions = &mod_actions_authenticate,
992 .offset = PROCESS_CONF_OFFSET(crl_fetch),
993 },
994 {
995 .section = SECTION_NAME("send", "Fetch-OK"),
997 .offset = PROCESS_CONF_OFFSET(fetch_ok),
998 },
999 {
1000 .section = SECTION_NAME("send", "Fetch-Fail"),
1002 .offset = PROCESS_CONF_OFFSET(fetch_fail),
1003 },
1004 {
1005 .section = SECTION_NAME("send", "Do-Not-Respond"),
1007 .offset = PROCESS_CONF_OFFSET(do_not_respond),
1008 },
1009
1011};
1012
1013extern fr_process_module_t process_crl;
1014fr_process_module_t process_crl = {
1015 .common = {
1016 .magic = MODULE_MAGIC_INIT,
1017 .name = "crl",
1018 .config = config,
1019 MODULE_INST(process_crl_t),
1020 MODULE_RCTX(process_crl_rctx_t),
1021 .instantiate = mod_instantiate,
1022 .detach = mod_detach,
1023 MODULE_THREAD_INST(process_thread_crl_t),
1024 .thread_instantiate = mod_thread_instantiate
1025 },
1026 .process = mod_process,
1027 .compile_list = compile_list,
1028 .dict = &dict_crl,
1029 .packet_type = &attr_packet_type
1030};
1031#endif
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_FAIL
Encountered an unexpected error.
Definition action.h:36
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition action.h:37
#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 CONF_PARSER_TERMINATOR
Definition cf_parse.h:669
#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_IS_SET(_name, _type, _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:294
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:606
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:287
int fr_coord_to_worker_reply_broadcast(request_t *request)
Send a reply list from a coordinator to all workers.
Definition coord_pair.c:781
int fr_coord_to_worker_reply_send(request_t *request, uint32_t worker_id)
Send a reply list from a coordinator to a worker.
Definition coord_pair.c:753
fr_coord_pair_t * fr_coord_pair_request_coord_pair(request_t *request)
Return the coord_pair associated with a coord_pair internal request.
Definition coord_pair.c:870
int fr_coord_pair_coord_request_start(fr_coord_pair_t *coord_pair, fr_pair_list_t *list, fr_time_t now)
Start a coordinator request to run through a coord_pair process module.
Definition coord_pair.c:885
@ 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_DO_NOT_RESPOND
Definition crl.h:38
@ FR_CRL_FETCH_FAIL
Definition crl.h:35
#define MEM(x)
Definition debug.h:46
#define ERROR(fmt,...)
Definition dhcpclient.c:40
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
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:242
#define fr_dlist_foreach(_list_head, _type, _iter)
Iterate over the contents of a list.
Definition dlist.h:98
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:620
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:360
Head of a doubly linked list.
Definition dlist.h:51
Entry in a doubly linked list.
Definition dlist.h:41
talloc_free(hp)
static fr_dict_t const * dict_freeradius
Definition base.c:37
fr_dict_attr_t const * attr_packet_type
Definition base.c:91
void log_request_pair_list(fr_log_lvl_t lvl, request_t *request, fr_pair_t const *parent, fr_pair_list_t const *vps, char const *prefix)
Print a fr_pair_list_t.
Definition log.c:831
#define RDEBUG3(fmt,...)
Definition log.h:355
#define RERROR(fmt,...)
Definition log.h:310
#define RPERROR(fmt,...)
Definition log.h:314
#define fr_time()
Definition event.c:60
Stores all information relating to an event list.
Definition event.c:377
@ L_DBG_LVL_1
Highest priority debug messages (-x).
Definition log.h:67
main_config_t const * main_config
Main server configuration.
Definition main_config.c:56
uint32_t max_workers
for the scheduler
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_DATE
Unix time stamp, always has value >2^31.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_INT32
32 Bit signed integer.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
@ FR_TYPE_OCTETS
Raw octets.
unsigned int uint32_t
unsigned char uint8_t
unlang_mod_actions_t const mod_actions_authenticate
Definition mod_action.c:29
unlang_mod_action_t actions[RLM_MODULE_NUMCODES]
Definition mod_action.h:69
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 * 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
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
int fr_pair_delete_by_da(fr_pair_list_t *list, fr_dict_attr_t const *da)
Delete matching pairs from the specified list.
Definition pair.c:1696
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
bool fr_pair_immutable(fr_pair_t const *vp)
Definition pair.c:2283
static unlang_action_t mod_process(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition base.c:188
static const virtual_server_compile_t compile_list[]
Definition base.c:214
static fr_process_state_t const process_state[]
Definition base.c:69
RESUME_FLAG(recv_bfd, UNUSED,)
Definition base.c:119
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition base.c:752
RECV(for_any_server)
Validate a solicit/rebind/confirm message.
Definition base.c:400
static const conf_parser_t config[]
Definition base.c:163
#define PROCESS_TRACE
Trace each state function as it's entered.
Definition process.h:55
#define PROCESS_CONF_OFFSET(_x)
Definition process.h:79
module_t common
Common fields for all loadable modules.
Common public symbol definition for all process modules.
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
#define DEBUG2(fmt,...)
static fr_schedule_worker_t workers[MAX_WORKERS]
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
static bool fr_rb_node_inline_in_tree(fr_rb_node_t const *node)
Check to see if an item is in a tree by examining its inline fr_rb_node_t.
Definition rb.h:312
#define fr_rb_inline_init(_tree, _type, _field, _data_cmp, _data_free)
Initialises a red black tree.
Definition rb.h:178
The main red black tree structure.
Definition rb.h:71
#define RLM_MODULE_USER_SECTION_REJECT
Rcodes that translate to a user configurable section failing overall.
Definition rcode.h:79
#define RETURN_UNLANG_FAIL
Definition rcode.h:63
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition rcode.h:51
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:49
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:48
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
Definition rcode.h:52
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition rcode.h:47
@ RLM_MODULE_TIMEOUT
Module (or section) timed out.
Definition rcode.h:56
@ RLM_MODULE_NOTFOUND
User not found.
Definition rcode.h:53
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition rcode.h:55
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:54
@ RLM_MODULE_HANDLED
The module handled the request, so stop.
Definition rcode.h:50
static int mod_detach(module_detach_ctx_t const *mctx)
Definition rlm_always.c:136
static int mod_thread_instantiate(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 delta_urls
URLs from which a delta CRL can be retrieved.
Definition rlm_crl.c:59
static fr_dict_attr_t const * attr_base_crl
Definition rlm_crl.c:113
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
static fr_dict_attr_t const * attr_crl_cdp_url
Definition rlm_crl.c:112
static fr_dict_t const * dict_crl
Definition rlm_crl.c:103
static fr_dict_attr_t const * attr_crl_data
Definition rlm_crl.c:111
A single CRL in the thread specific list of CRLs.
Definition rlm_crl.c:55
static char const * name
static _Thread_local int worker_id
Internal ID of the current worker thread.
Definition schedule.c:104
#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
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:351
void * data
Module's instance data.
Definition module.h:293
#define MODULE_RCTX(_ctype)
Definition module.h:259
#define MODULE_INST(_ctype)
Definition module.h:257
conf_parser_t const * config
How to convert a CONF_SECTION to a module instance.
Definition module.h:206
#define pair_update_reply(_attr, _da)
Return or allocate a fr_pair_t in the reply list.
Definition pair.h:129
eap_aka_sim_process_conf_t * inst
#define RESUME(_x)
fr_aka_sim_id_type_t type
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
char * talloc_bstrdup(TALLOC_CTX *ctx, char const *in)
Binary safe strdup function.
Definition talloc.c:589
uint8_t * talloc_typed_memdup(TALLOC_CTX *ctx, uint8_t const *in, size_t inlen)
Call talloc_memdup, setting the type on the new chunk correctly.
Definition talloc.c:445
#define talloc_get_type_abort_const
Definition talloc.h:110
#define talloc_strdup(_ctx, _str)
Definition talloc.h:142
static const char * names[8]
Definition time.c:600
static int8_t fr_time_delta_cmp(fr_time_delta_t a, fr_time_delta_t b)
Compare two fr_time_delta_t values.
Definition time.h:930
#define fr_time_delta_isneg(_a)
Definition time.h:291
static fr_unix_time_t fr_unix_time_from_sec(int64_t sec)
Definition time.h:449
static fr_time_t fr_time_from_sec(time_t when)
Convert a time_t (wallclock time) to a fr_time_t (internal time)
Definition time.h:858
#define fr_time_gt(_a, _b)
Definition time.h:237
static fr_time_delta_t fr_time_delta_sub(fr_time_delta_t a, fr_time_delta_t b)
Definition time.h:261
#define fr_time_sub(_a, _b)
Subtract one time from another.
Definition time.h:229
#define fr_time_lt(_a, _b)
Definition time.h:239
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
An event timer list.
Definition timer.c:49
A timer event.
Definition timer.c:83
#define fr_timer_in(...)
Definition timer.h:87
static fr_event_list_t * el
fr_pair_t * fr_pair_remove(fr_pair_list_t *list, fr_pair_t *vp)
Remove fr_pair_t from a list without freeing.
Definition pair_inline.c:93
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
#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
static void fr_pair_set_immutable(fr_pair_t *vp)
Definition pair.h:699
int fr_tls_utils_asn1time_to_epoch(time_t *out, ASN1_TIME const *asn1)
Convert OpenSSL's ASN1_TIME to an epoch time.
Definition utils.c:115
int fr_value_box_memdup_buffer(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, uint8_t const *src, bool tainted)
Copy a talloced buffer to a fr_value_box_t.
Definition value.c:5141
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:4394
void fr_value_box_clear_value(fr_value_box_t *data)
Clear/free any existing value.
Definition value.c:4331
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:4619
int fr_value_box_bstrndup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Copy a string to to a fr_value_box_t.
Definition value.c:4838
static fr_slen_t data
Definition value.h:1340
#define fr_box_time_delta(_val)
Definition value.h:366
#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_box_time(_val)
Definition value.h:349
#define fr_value_box_list_foreach(_list_head, _iter)
Definition value.h:224
section_name_t const * section
Identifier for the section.
#define COMPILE_TERMINATOR
Processing sections which are allowed in this virtual server.