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