The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_crl.c
Go to the documentation of this file.
1/*
2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: eda5843eeb0802451d2a638e203ce466574780b8 $
19 * @file rlm_crl.c
20 * @brief Check a certificate's serial number against a CRL
21 *
22 * @author Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 * @copyright 2025 Network RADIUS SAS (legal@networkradius.com)
24 */
25RCSID("$Id: eda5843eeb0802451d2a638e203ce466574780b8 $")
26
27#include <freeradius-devel/server/base.h>
28#include <freeradius-devel/server/module_rlm.h>
29
30#include <freeradius-devel/tls/strerror.h>
31#include <freeradius-devel/tls/utils.h>
32
33#include <openssl/x509v3.h>
34#include <openssl/pem.h>
35#include <openssl/asn1.h>
36#include <openssl/bn.h>
37
38/** Thread specific structure to hold requests awaiting CRL fetching */
39typedef struct {
40 fr_rb_tree_t pending; //!< Requests yielded while the CRL is being fetched.
42
43/** Global tree of CRLs
44 *
45 * Separate from the instance data because that's protected.
46 */
47typedef struct {
48 fr_rb_tree_t *crls; //!< A tree of CRLs organised by CDP URL.
49 fr_timer_list_t *timer_list; //!< The timer list to use for CRL expiry.
50 ///< This gets serviced by the main loop.
51 rlm_crl_thread_t *fetching; //!< Pointer to thread instance data of
52 ///< thread which is fetching a CRL.
55
56typedef struct {
57 CONF_SECTION *virtual_server; //!< Virtual server to use when retrieving CRLs
58 fr_time_delta_t force_expiry; //!< Force expiry of CRLs after this time
60 fr_time_delta_t force_delta_expiry; //!< Force expiry of delta CRLs after this time
62 fr_time_delta_t early_refresh; //!< Time interval before nextUpdate to refresh
63 char const *ca_file; //!< File containing certs for verifying CRL signatures.
64 char const *ca_path; //!< Directory containing certs for verifying CRL signatures.
65 X509_STORE *verify_store; //!< Store of certificates to verify CRL signatures.
66 rlm_crl_mutable_t *mutable; //!< Mutable data that's shared between all threads.
67 CONF_SECTION *cs; //!< Module instance config.
68 bool trigger_rate_limit; //!< Rate limit triggers.
69} rlm_crl_t;
70
71/** A single CRL in the global list of CRLs */
72typedef struct {
73 X509_CRL *crl; //!< The CRL.
74 char const *cdp_url; //!< The URL of the CRL.
75 ASN1_INTEGER *crl_num; //!< The CRL number.
76 fr_timer_t *ev; //!< When to expire the CRL
77 fr_rb_node_t node; //!< The node in the tree
78 fr_value_box_list_t delta_urls; //!< URLs from which a delta CRL can be retrieved.
79 rlm_crl_t const *inst; //!< The instance of the CRL module.
80 rlm_crl_thread_t *thread; //!< The thread which fetched this entry.
82
83/** Structure to record a request which is waiting for CRL fetching to complete */
88
89/** A status used to track which CRL is being checked */
90typedef enum {
91 CRL_CHECK_BASE = 0, //!< The base CRL is being checked
92 CRL_CHECK_FETCH_DELTA, //!< The delta CRL is being fetched
93 CRL_CHECK_DELTA //!< The delta CRL exists and is being checked
95
96typedef struct {
97 fr_value_box_t *cdp_url; //!< The URL we're currently attempting to load.
98 crl_entry_t *base_crl; //!< The base CRL relating to the delta currently being fetched.
99 fr_value_box_list_t crl_data; //!< Data from CRL expansion.
100 fr_value_box_list_t missing_crls; //!< CRLs missing from the tree
101 crl_check_status_t status; //!< Status of the current CRL check.
103
105 { FR_CONF_OFFSET_IS_SET("force_expiry", FR_TYPE_TIME_DELTA, 0, rlm_crl_t, force_expiry) },
106 { FR_CONF_OFFSET_IS_SET("force_delta_expiry", FR_TYPE_TIME_DELTA, 0, rlm_crl_t, force_delta_expiry) },
107 { FR_CONF_OFFSET("early_refresh", rlm_crl_t, early_refresh) },
108 { FR_CONF_OFFSET("ca_file", rlm_crl_t, ca_file) },
109 { FR_CONF_OFFSET("ca_path", rlm_crl_t, ca_path) },
110 { FR_CONF_OFFSET("trigger_rate_limit", rlm_crl_t, trigger_rate_limit), .dflt = "yes" },
112};
113
115
118 { .out = &dict_freeradius, .proto = "freeradius" },
120};
121
124
127 { .out = &attr_crl_data, .name = "CRL.Data", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
128 { .out = &attr_crl_cdp_url, .name = "CRL.CDP-URL", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
130};
131
132typedef struct {
133 tmpl_t *http_exp; //!< The xlat expansion used to retrieve the CRL via http://
134 tmpl_t *ldap_exp; //!< The xlat expansion used to retrieve the CRL via ldap://
135 tmpl_t *ftp_exp; //!< The xlat expansion used to retrieve the CRL via ftp://
136 fr_value_box_t serial; //!< The serial to check
137 fr_value_box_list_head_t *cdp; //!< The CRL distribution points
139
140typedef enum {
141 CRL_ERROR = -1, //!< Unspecified error ocurred.
142 CRL_ENTRY_NOT_FOUND = 0, //!< Serial not found in this CRL.
143 CRL_ENTRY_FOUND = 1, //!< Serial was found in this CRL.
144 CRL_ENTRY_REMOVED = 2, //!< Serial was "un-revoked" in this delta CRL.
145 CRL_NOT_FOUND = 3, //!< No CRL found, need to load it from the CDP URL
146 CRL_MISSING_DELTA = 4, //!< Need to load a delta CRL to supplement this CRL.
147} crl_ret_t;
148
149#ifdef WITH_TLS
150static const call_env_method_t crl_env = {
152 .env = (call_env_parser_t[]){
154 ((call_env_parser_t[]) {
156 ((call_env_parser_t[]) {
161 }))},
163 }))},
165 .pair.dflt = "session-state.TLS-Certificate.Serial", .pair.dflt_quote = T_BARE_WORD },
167 .pair.dflt = "session-state.TLS-Certificate.X509v3-CRL-Distribution-Points[*]", .pair.dflt_quote = T_BARE_WORD },
169 },
170};
171
172static int8_t crl_cmp(void const *a, void const *b)
173{
174 crl_entry_t const *crl_a = (crl_entry_t const *)a;
175 crl_entry_t const *crl_b = (crl_entry_t const *)b;
176
177 return CMP(strcmp(crl_a->cdp_url, crl_b->cdp_url), 0);
178}
179
180static void crl_free(void *data)
181{
183}
184
185static int8_t crl_pending_cmp(void const *a, void const *b)
186{
187 crl_pending_t const *pending_a = (crl_pending_t const *)a;
188 crl_pending_t const *pending_b = (crl_pending_t const *)b;
189
190 return CMP(pending_a->request, pending_b->request);
191}
192
193static void crl_expire(fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
194{
195 crl_entry_t *crl = talloc_get_type_abort(uctx, crl_entry_t);
196
197 DEBUG2("CRL associated with CDP %s expired", crl->cdp_url);
198
199 /*
200 * If the mutex is locked and this thread is fetching a CRL, asynchonously,
201 * insert a new timer event - otherwise the mutex will never be unlocked.
202 */
203 if (pthread_mutex_trylock(&crl->inst->mutable->mutex) != 0) {
204 if (crl->inst->mutable->fetching == crl->thread) {
205 if (fr_timer_in(crl, tl, &crl->ev, fr_time_delta_from_sec(1), false, crl_expire, crl) <0) {
206 ERROR("Failed inserting CRL expiry event");
207 }
208 return;
209 }
210 pthread_mutex_lock(&crl->inst->mutable->mutex);
211 }
212 fr_rb_remove(crl->inst->mutable->crls, crl);
213 pthread_mutex_unlock(&crl->inst->mutable->mutex);
214
215 if (trigger_enabled()) {
217 fr_pair_t *vp;
220 fr_value_box_strdup_shallow(&vp->data, NULL, crl->cdp_url, true);
221 trigger(unlang_interpret_get_thread_default(), crl->inst->cs, NULL, "modules.crl.expired",
223 }
224
225 talloc_free(crl);
226}
227
228/** Make sure we don't lock up the server if a request is cancelled
229 */
230static void crl_signal(module_ctx_t const *mctx, UNUSED request_t *request, fr_signal_t action)
231{
233
234 if (action == FR_SIGNAL_CANCEL) {
235 pthread_mutex_unlock(&inst->mutable->mutex);
237 }
238}
239
240/** See if a particular serial is present in a CRL list
241 *
242 */
243static crl_ret_t crl_check_entry(crl_entry_t *crl_entry, request_t *request, uint8_t const *serial)
244{
245 X509_REVOKED *revoked;
246 ASN1_INTEGER *asn1_serial = NULL;
247 int ret;
248
249 asn1_serial = d2i_ASN1_INTEGER(NULL, (unsigned char const **)&serial, talloc_array_length(serial));
250 ret = X509_CRL_get0_by_serial(crl_entry->crl, &revoked, asn1_serial);
251 ASN1_INTEGER_free(asn1_serial);
252 switch (ret) {
253 /* The docs describe 0 as "failure" - but that means "failed to find"*/
254 case 0:
255 RDEBUG3("Certificate not in CRL");
256 return CRL_ENTRY_NOT_FOUND;
257
258 case 1:
259 REDEBUG2("Certificate revoked by %s", crl_entry->cdp_url);
260 return CRL_ENTRY_FOUND;
261
262 case 2:
263 RDEBUG3("Certificate un-revoked by %s", crl_entry->cdp_url);
264 return CRL_ENTRY_REMOVED;
265 }
266
267 return CRL_ERROR;
268}
269
270/** Resolve a cdp_url to a CRL entry, and check serial against it, if it exists
271 *
272 */
273static crl_ret_t crl_check_serial(fr_rb_tree_t *crls, request_t *request, char const *cdp_url, uint8_t const *serial,
274 crl_entry_t **found)
275{
276 crl_entry_t *delta, find = { .cdp_url = cdp_url};
277 fr_value_box_t *vb = NULL;
279
280 *found = fr_rb_find(crls, &find);
281 if (*found == NULL) return CRL_NOT_FOUND;
282
283 /*
284 * First check the delta if it should exist
285 */
286 while ((vb = fr_value_box_list_next(&(*found)->delta_urls, vb))) {
287 find.cdp_url = vb->vb_strvalue;
288 delta = fr_rb_find(crls, &find);
289 if (delta) {
290 ret = crl_check_entry(delta, request, serial);
291
292 /*
293 * An entry found in a delta overrides the base CRL
294 */
295 if (ret != CRL_ENTRY_NOT_FOUND) return ret;
296 break;
297 } else {
298 ret = CRL_MISSING_DELTA;
299 }
300 }
301
302 if (ret == CRL_MISSING_DELTA) return ret;
303
304 return crl_check_entry(*found, request, serial);
305}
306
307static int _crl_entry_free(crl_entry_t *crl_entry)
308{
309 X509_CRL_free(crl_entry->crl);
310 if (crl_entry->crl_num) ASN1_INTEGER_free(crl_entry->crl_num);
311 return 0;
312}
313
314/** Add an entry to the cdp_url -> crl tree
315 *
316 * @note Must be called with the mutex held.
317 */
318static crl_entry_t *crl_entry_create(rlm_crl_t const *inst, fr_timer_list_t *tl, char const *url, uint8_t const *data,
319 crl_entry_t *base_crl)
320{
321 uint8_t const *our_data = data;
322 crl_entry_t *crl;
323 time_t next_update;
324 fr_time_t now = fr_time();
325 fr_time_delta_t expiry_time;
326 int i;
327 STACK_OF(DIST_POINT) *dps;
328 X509_STORE_CTX *verify_ctx = NULL;
329 X509_OBJECT *xobj;
330 EVP_PKEY *pkey;
331
332 MEM(crl = talloc_zero(inst->mutable->crls, crl_entry_t));
333 crl->cdp_url = talloc_bstrdup(crl, url);
334 crl->crl = d2i_X509_CRL(NULL, (const unsigned char **)&our_data, talloc_array_length(our_data));
335 if (crl->crl == NULL) {
336 fr_tls_strerror_printf("Failed to parse CRL from %s", url);
337 error:
338 talloc_free(crl);
339 if (verify_ctx) X509_STORE_CTX_free(verify_ctx);
340 return NULL;
341 }
342 talloc_set_destructor(crl, _crl_entry_free);
343
344 verify_ctx = X509_STORE_CTX_new();
345 if (!verify_ctx || !X509_STORE_CTX_init(verify_ctx, inst->verify_store, NULL, NULL)) {
346 fr_tls_strerror_printf("Error initialising X509 store");
347 goto error;
348 }
349
350 xobj = X509_STORE_CTX_get_obj_by_subject(verify_ctx, X509_LU_X509,
351 X509_CRL_get_issuer(crl->crl));
352 if (!xobj) {
353 fr_tls_strerror_printf("CRL issuer certificate not in trusted store");
354 goto error;
355 }
356 pkey = X509_get_pubkey(X509_OBJECT_get0_X509(xobj));
357 X509_OBJECT_free(xobj);
358 if (!pkey) {
359 fr_tls_strerror_printf("Error getting CRL issuer public key");
360 goto error;
361 }
362 i = X509_CRL_verify(crl->crl, pkey);
363 EVP_PKEY_free(pkey);
364
365 if (i < 0) {
366 fr_tls_strerror_printf("Could not verify CRL signature");
367 goto error;
368 }
369 if (i == 0) {
370 fr_tls_strerror_printf("CRL certificate signature failed");
371 goto error;
372 }
373
374 crl->crl_num = X509_CRL_get_ext_d2i(crl->crl, NID_crl_number, &i, NULL);
375
376 /*
377 * If we're passed a base_crl, then this is a delta - check the delta
378 * relates to the correct base.
379 */
380 if (base_crl) {
381 ASN1_INTEGER *base_num = X509_CRL_get_ext_d2i(crl->crl, NID_delta_crl, &i, NULL);
382 if (!base_num) {
383 fr_tls_strerror_printf("Delta CRL missing Delta CRL Indicator extension");
384 goto error;
385 }
386 if (ASN1_INTEGER_cmp(base_num, base_crl->crl_num) > 0) {
387 uint64_t delta_base, crl_num;
388 ASN1_INTEGER_get_uint64(&delta_base, base_num);
389 ASN1_INTEGER_get_uint64(&crl_num, base_crl->crl_num);
390 fr_tls_strerror_printf("Delta CRL referrs to base CRL number %"PRIu64", current base is %"PRIu64,
391 delta_base, crl_num);
392 ASN1_INTEGER_free(base_num);
393 goto error;
394 }
395 ASN1_INTEGER_free(base_num);
396 if (ASN1_INTEGER_cmp(crl->crl_num, base_crl->crl_num) < 0) {
397 uint64_t delta_num, crl_num;
398 ASN1_INTEGER_get_uint64(&delta_num, crl->crl_num);
399 ASN1_INTEGER_get_uint64(&crl_num, base_crl->crl_num);
400 fr_tls_strerror_printf("Delta CRL number %"PRIu64" is less than base CRL number %"PRIu64,
401 delta_num, crl_num);
402 goto error;
403 }
404 }
405
406 if (fr_tls_utils_asn1time_to_epoch(&next_update, X509_CRL_get0_nextUpdate(crl->crl)) < 0) {
407 fr_tls_strerror_printf("Failed to parse nextUpdate from CRL");
408 goto error;
409 }
410
411 if (!fr_rb_insert(inst->mutable->crls, crl)) {
412 ERROR("Failed to insert CRL into tree of CRLs");
413 goto error;
414 }
415 crl->inst = inst;
416
417 /*
418 * Check if this CRL has a Freshest CRL extension - the list of URIs to get deltas from
419 */
420 fr_value_box_list_init(&crl->delta_urls);
421 if (!base_crl && (dps = X509_CRL_get_ext_d2i(crl->crl, NID_freshest_crl, NULL, NULL))) {
422 DIST_POINT *dp;
423 STACK_OF(GENERAL_NAME) *names;
424 GENERAL_NAME *name;
425 int j;
426 fr_value_box_t *vb;
427
428 for (i = 0; i < sk_DIST_POINT_num(dps); i++) {
429 dp = sk_DIST_POINT_value(dps, i);
430 names = dp->distpoint->name.fullname;
431 for (j = 0; j < sk_GENERAL_NAME_num(names); j++) {
432 name = sk_GENERAL_NAME_value(names, j);
433 if (name->type != GEN_URI) continue;
434 MEM(vb = fr_value_box_alloc_null(crl));
435 fr_value_box_bstrndup(vb, vb, NULL,
436 (char const *)ASN1_STRING_get0_data(name->d.uniformResourceIdentifier),
437 ASN1_STRING_length(name->d.uniformResourceIdentifier), true);
438 DEBUG3("CRL references delta URI %pV", vb);
439 fr_value_box_list_insert_tail(&crl->delta_urls, vb);
440 }
441 }
442 CRL_DIST_POINTS_free(dps);
443 }
444
445 expiry_time = fr_time_delta_sub(fr_time_sub(fr_time_from_sec(next_update), now), inst->early_refresh);
446 if (base_crl && inst->force_delta_expiry_is_set) {
447 if (fr_time_delta_cmp(expiry_time, inst->force_delta_expiry)) expiry_time = inst->force_delta_expiry;
448 } else {
449 if (inst->force_expiry_is_set &&
450 (fr_time_delta_cmp(expiry_time, inst->force_expiry) > 0)) expiry_time = inst->force_expiry;
451 }
452
453 DEBUG3("CRL from %s will expire in %pVs", url, fr_box_time_delta(expiry_time));
454 if (fr_timer_in(crl, tl, &crl->ev, expiry_time, false, crl_expire, crl) <0) {
455 ERROR("Failed to set timer to expire CRL");
456 }
457
458 X509_STORE_CTX_free(verify_ctx);
459 return crl;
460}
461
462static unlang_action_t CC_HINT(nonnull) crl_process_cdp_data(unlang_result_t *p_result, module_ctx_t const *mctx,
463 request_t *request);
464
465/** Yield to a tmpl to retrieve CRL data
466 *
467 * @param request the current request.
468 * @param inst module instance data.
469 * @param thread thread instance data.
470 * @param env the call_env for this module call.
471 * @param rctx the resume ctx for this module call.
472 *
473 * @returns
474 * - 1 - new tmpl pushed.
475 * - 0 - no tmpl pushed, soft fail.
476 * - -1 - no tmpl pushed, hard fail
477 */
478static int crl_tmpl_yield(request_t *request, rlm_crl_t const *inst, rlm_crl_thread_t *thread, rlm_crl_env_t *env,
479 rlm_crl_rctx_t *rctx)
480{
481 fr_pair_t *vp;
482 tmpl_t *vpt;
483
485 MEM(fr_value_box_copy(vp, &vp->data, rctx->cdp_url) == 0);
486
487 if (strncmp(rctx->cdp_url->vb_strvalue, "http", 4) == 0) {
488 vpt = env->http_exp;
489 } else if (strncmp(rctx->cdp_url->vb_strvalue, "ldap", 4) == 0) {
490 if (!env->ldap_exp) {
491 RWARN("CRL URI %pV requires LDAP, but the crl module ldap expansion is not configured", rctx->cdp_url);
492 return 0;
493 }
494 vpt = env->ldap_exp;
495 } else if (strncmp(rctx->cdp_url->vb_strvalue, "ftp", 3) == 0) {
496 if (!env->ftp_exp) {
497 RWARN("CRL URI %pV requires FTP, but the crl module ftp expansion is not configured", rctx->cdp_url);
498 return 0;
499 }
500 vpt = env->ftp_exp;
501 } else {
502 RERROR("Unsupported URI scheme in CRL URI %pV", rctx->cdp_url);
503 return -1;
504 }
505
506 trigger(unlang_interpret_get_thread_default(), inst->cs, NULL, "modules.crl.fetchuri", inst->trigger_rate_limit,
507 &request->request_pairs);
508
509 if (unlang_module_yield_to_tmpl(rctx, &rctx->crl_data, request, vpt,
510 NULL, crl_process_cdp_data, crl_signal, 0, rctx) < 0) return -1;
511 inst->mutable->fetching = thread;
512 return 1;
513}
514
515static unlang_action_t crl_by_url_start(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request);
516
517/** Process the response from evaluating the cdp_url -> crl_data expansion
518 *
519 * This is the resumption function when we yield to get CRL data associated with a URL
520 */
521static unlang_action_t CC_HINT(nonnull) crl_process_cdp_data(unlang_result_t *p_result, module_ctx_t const *mctx,
522 request_t *request)
523{
525 rlm_crl_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_crl_thread_t);
526 rlm_crl_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_crl_env_t);
527 rlm_crl_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, rlm_crl_rctx_t);
530 crl_pending_t *pending;
531
532 inst->mutable->fetching = NULL;
533 switch (fr_value_box_list_num_elements(&rctx->crl_data)) {
534 case 0:
535 REDEBUG("No CRL data returned from %pV, failing", rctx->cdp_url);
536 trigger(unlang_interpret_get_thread_default(), inst->cs, NULL, "modules.crl.fetchfail",
537 inst->trigger_rate_limit, &request->request_pairs);
538 again:
539 talloc_free(rctx->cdp_url);
540
541 /*
542 * If there are more URIs to try, push a new tmpl to expand.
543 */
544 rctx->cdp_url = fr_value_box_list_pop_head(&rctx->missing_crls);
545 if (rctx->cdp_url) {
546 switch (crl_tmpl_yield(request, inst, t, env, rctx)) {
547 case 0:
548 goto again;
549 case 1:
551 default:
552 break;
553 }
554 }
555 fail:
556 fr_value_box_list_talloc_free(&rctx->crl_data);
558 goto finish;
559
560 case 1:
561 {
562 crl_entry_t *crl_entry;
563 fr_value_box_t *crl_data = fr_value_box_list_pop_head(&rctx->crl_data);
564
565 crl_entry = crl_entry_create(inst, unlang_interpret_event_list(request)->tl,
566 rctx->cdp_url->vb_strvalue,
567 crl_data->vb_octets, rctx->base_crl);
568 talloc_free(crl_data);
569 if (!crl_entry) {
570 RPERROR("Failed to process returned CRL data");
571 trigger(unlang_interpret_get_thread_default(), inst->cs, NULL, "modules.crl.fetchbad",
572 inst->trigger_rate_limit, &request->request_pairs);
573 goto again;
574 }
575
576 /*
577 * We've successfully loaded a URI - so we can clear the list of missing crls
578 * This can then be re-used to hold missing delta CRLs if needed.
579 */
580 fr_value_box_list_talloc_free(&rctx->missing_crls);
581
582 if (fr_value_box_list_num_elements(&crl_entry->delta_urls) > 0) {
583 crl_entry_t *delta, find;
584 fr_value_box_t *vb = NULL, *delta_uri;
585
586 rctx->status = CRL_CHECK_DELTA;
587 while ((vb = fr_value_box_list_next(&crl_entry->delta_urls, vb))) {
588 find.cdp_url = vb->vb_strvalue;
589 delta = fr_rb_find(inst->mutable->crls, &find);
590 if (delta) {
591 ret = crl_check_entry(delta, request, env->serial.vb_octets);
592 /*
593 * The delta contained an entry for this serial - so this
594 * is the return status.
595 */
596 if (ret != CRL_ENTRY_NOT_FOUND) break;
597 } else {
598 delta_uri = fr_value_box_acopy(rctx, vb);
599 fr_value_box_list_insert_tail(&rctx->missing_crls, delta_uri);
600 }
601 }
602
603 /*
604 * None of the delta CRL URIs were found, so go and get one.
605 * The list of URIs to fetch will now be in rctx->missing_crls
606 */
607 if (ret == CRL_NOT_FOUND) {
609 rctx->base_crl = crl_entry;
610 goto again;
611 }
612 }
613
614 if (rctx->status != CRL_CHECK_DELTA) ret = crl_check_entry(crl_entry, request, env->serial.vb_octets);
615 check_return:
616 switch (ret) {
617 case CRL_ENTRY_FOUND:
618 rcode = RLM_MODULE_REJECT;
619 goto finish;
620
622 /*
623 * We have a CRL, but the serial is not in it.
624 *
625 * If this was after fetching a delta, go check the base
626 */
627 if (rctx->status == CRL_CHECK_FETCH_DELTA) {
628 RDEBUG3("Certificate not in delta CRL, checking base CRL");
629 rctx->status = CRL_CHECK_BASE;
630 ret = crl_check_entry(rctx->base_crl, request, env->serial.vb_octets);
631 goto check_return;
632 }
634
636 rcode = RLM_MODULE_OK;
638 goto finish;
639
640 case CRL_ERROR:
641 goto fail;
642
643 /*
644 * This should never be returned by crl_check_entry because we provided the entry!
645 */
647 case CRL_NOT_FOUND:
648 fr_assert(0);
649 goto fail;
650 }
651
652 }
653 break;
654
655 default:
656 REDEBUG("Too many CRL values returned, failing");
657 goto fail;
658 }
659
660finish:
661 pthread_mutex_unlock(&inst->mutable->mutex);
662 pending = fr_rb_first(&t->pending);
663 if (pending) unlang_interpret_mark_runnable(pending->request);
664 RETURN_UNLANG_RCODE(rcode);
665}
666
667static unlang_action_t CC_HINT(nonnull(1,2,3,4,6)) crl_by_url(unlang_result_t *p_result, rlm_crl_t const *inst,
669 rlm_crl_rctx_t *rctx, request_t *request)
670{
672 crl_entry_t *found;
673
674 if (!rctx) rctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), rlm_crl_rctx_t);
675 fr_value_box_list_init(&rctx->missing_crls);
676
677 pthread_mutex_lock(&inst->mutable->mutex);
678
679 /*
680 * Fast path when we have a CRL.
681 * All distribution points are considered equivalent, so check if
682 * if we have any of them before attempting to fetch missing ones.
683 */
684 while ((rctx->cdp_url = fr_value_box_list_pop_head(env->cdp))) {
685 switch (crl_check_serial(inst->mutable->crls, request, rctx->cdp_url->vb_strvalue,
686 env->serial.vb_octets, &found)) {
687 case CRL_ENTRY_FOUND:
688 rcode = RLM_MODULE_REJECT;
689 break;
690
693 rcode = RLM_MODULE_OK;
694 break;
695
696 case CRL_ERROR:
697 continue;
698
699 case CRL_NOT_FOUND:
700 fr_value_box_list_insert_tail(&rctx->missing_crls, rctx->cdp_url);
701 rcode = RLM_MODULE_NOTFOUND;
702 continue;
703
705 {
706 /*
707 * We found a base CRL, but it has a delta which
708 * was not found. Populate the "missing" list with
709 * the CDP for the delta and go get it.
710 */
711 fr_value_box_t *vb = NULL, *delta_uri;
712 rctx->base_crl = found;
713 rctx->status = CRL_CHECK_FETCH_DELTA;
714 fr_value_box_list_talloc_free(&rctx->missing_crls);
715 while ((vb = fr_value_box_list_next(&found->delta_urls, vb))) {
716 delta_uri = fr_value_box_acopy(rctx, vb);
717 fr_value_box_list_insert_tail(&rctx->missing_crls, delta_uri);
718 }
719 goto fetch_missing;
720 }
721 }
722 }
723
724 if (rcode != RLM_MODULE_NOTFOUND) {
725 crl_pending_t *pending;
726 finish:
727 pthread_mutex_unlock(&inst->mutable->mutex);
728
729 pending = fr_rb_first(&t->pending);
730 if (pending) unlang_interpret_mark_runnable(pending->request);
731
732 RETURN_UNLANG_RCODE(rcode);
733 }
734
735 /*
736 * Need to convert a missing cdp_url to a CRL entry
737 *
738 * We yield to an expansion to allow this to happen, then parse the CRL data
739 * and check if the serial has an entry in the CRL.
740 */
741fetch_missing:
742 fr_value_box_list_init(&rctx->crl_data);
743
744again:
745 rctx->cdp_url = fr_value_box_list_pop_head(&rctx->missing_crls);
746
747 switch (crl_tmpl_yield(request, inst, t, env, rctx)) {
748 case 0:
749 goto again;
750 case 1:
751 /*
752 * The lock is released after the pushed tmpl result is handled
753 */
754 /* coverity[missing_unlock] */
756 default:
757 rcode = RLM_MODULE_FAIL;
758 goto finish;
759 }
760}
761
762static unlang_action_t CC_HINT(nonnull) crl_by_url_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
763{
765 rlm_crl_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_crl_env_t);
766 rlm_crl_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_crl_thread_t);
767 crl_pending_t find, *found;
768
769 find.request = request;
770 found = fr_rb_find(&t->pending, &find);
771 if (!found) RETURN_UNLANG_NOOP;
772
773 fr_rb_delete(&t->pending, found);
774 return crl_by_url(p_result, inst, t, env, mctx->rctx, request);
775}
776
777static void crl_by_url_cancel(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
778{
779 rlm_crl_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_crl_thread_t);
780 crl_pending_t *found, find;
781
782 find.request = request;
783 found = fr_rb_find(&t->pending, &find);
784 if (!found) return;
785
786 fr_rb_delete(&t->pending, found);
787}
788
789static unlang_action_t CC_HINT(nonnull) crl_by_url_start(unlang_result_t *p_result, module_ctx_t const *mctx,
790 request_t *request)
791{
793 rlm_crl_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_crl_env_t);
794 rlm_crl_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_crl_thread_t);
795 crl_pending_t *pending;
796
797 if (fr_value_box_list_num_elements(env->cdp) == 0) RETURN_UNLANG_NOOP;
798
799 if (inst->mutable->fetching != t) return crl_by_url(p_result, inst, t, env, mctx->rctx, request);
800
801 MEM(pending = talloc_zero(t, crl_pending_t));
802 pending->request = request;
803
804 fr_rb_insert(&t->pending, pending);
805 RDEBUG3("Yielding request until CRL fetching completed");
806 return unlang_module_yield(request, crl_by_url_resume, crl_by_url_cancel, ~FR_SIGNAL_CANCEL, mctx->rctx);
807}
808
810{
811 rlm_crl_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_crl_thread_t);
812
813 fr_rb_inline_init(&t->pending, crl_pending_t, node, crl_pending_cmp, NULL);
814
815 return 0;
816}
817
818static int mod_mutable_free(rlm_crl_mutable_t *mutable)
819{
820 pthread_mutex_destroy(&mutable->mutex);
821 return 0;
822}
823
824static int mod_detach(module_detach_ctx_t const *mctx)
825{
826 rlm_crl_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_crl_t);
827
828 if (inst->verify_store) X509_STORE_free(inst->verify_store);
829 talloc_free(inst->mutable);
830 return 0;
831}
832#endif
833
834/** Instantiate the module
835 *
836 */
837static int mod_instantiate(module_inst_ctx_t const *mctx)
838{
839#ifdef WITH_TLS
840 rlm_crl_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_crl_t);
841
842 MEM(inst->mutable = talloc_zero(NULL, rlm_crl_mutable_t));
843 MEM(inst->mutable->crls = fr_rb_inline_talloc_alloc(inst->mutable, crl_entry_t, node, crl_cmp, crl_free));
844 pthread_mutex_init(&inst->mutable->mutex, NULL);
845 talloc_set_destructor(inst->mutable, mod_mutable_free);
846
847 if (!inst->ca_file && !inst->ca_path) {
848 cf_log_err(mctx->mi->conf, "Missing ca_file / ca_path option. One or other (or both) must be specified.");
849 fail:
850 talloc_free(inst->mutable);
851 return -1;
852 }
853
854 inst->verify_store = X509_STORE_new();
855 if (!X509_STORE_load_locations(inst->verify_store, inst->ca_file, inst->ca_path)) {
856 cf_log_err(mctx->mi->conf, "Failed reading Trusted root CA file \"%s\" and path \"%s\"",
857 inst->ca_file, inst->ca_path);
858 goto fail;
859 }
860
861 X509_STORE_set_purpose(inst->verify_store, X509_PURPOSE_SSL_CLIENT);
862
863 inst->cs = mctx->mi->conf;
864
865 return 0;
866#else
867 cf_log_err(mctx->mi->conf, "rlm_crl requires OpenSSL");
868 return -1;
869#endif
870}
871
872extern module_rlm_t rlm_crl;
874 .common = {
875 .magic = MODULE_MAGIC_INIT,
876 .inst_size = sizeof(rlm_crl_t),
877 .instantiate = mod_instantiate,
878 .name = "crl",
881#ifdef WITH_TLS
882 .thread_instantiate = mod_thread_instantiate,
883 .detach = mod_detach,
884#endif
885 },
886#ifdef WITH_TLS
887 .method_group = {
888 .bindings = (module_method_binding_t[]){
889 { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = crl_by_url_start, .method_env = &crl_env },
891 }
892 }
893#endif
894};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
Definition action.h:39
va_list args
Definition acutest.h:770
#define RCSID(id)
Definition build.h:506
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:343
#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 UNUSED
Definition build.h:336
#define CALL_ENV_TERMINATOR
Definition call_env.h:236
#define FR_CALL_ENV_METHOD_OUT(_inst)
Helper macro for populating the size/type fields of a call_env_method_t from the output structure typ...
Definition call_env.h:240
call_env_parser_t const * env
Parsing rules for call method env.
Definition call_env.h:247
#define FR_CALL_ENV_SUBSECTION(_name, _name2, _flags, _subcs)
Specify a call_env_parser_t which defines a nested subsection.
Definition call_env.h:402
@ CALL_ENV_FLAG_SUBSECTION
This is a subsection.
Definition call_env.h:87
@ CALL_ENV_FLAG_SINGLE
If the tmpl produces more than one box this is an error.
Definition call_env.h:77
@ CALL_ENV_FLAG_ATTRIBUTE
Tmpl MUST contain an attribute reference.
Definition call_env.h:86
@ CALL_ENV_FLAG_NONE
Definition call_env.h:74
@ CALL_ENV_FLAG_MULTI
Multiple instances of the conf pairs are allowed.
Definition call_env.h:78
@ CALL_ENV_FLAG_REQUIRED
Associated conf pair or section is required.
Definition call_env.h:75
@ CALL_ENV_FLAG_PARSE_MISSING
If this subsection is missing, still parse it.
Definition call_env.h:88
@ CALL_ENV_FLAG_BARE_WORD_ATTRIBUTE
bare words are treated as an attribute, but strings may be xlats.
Definition call_env.h:92
@ CALL_ENV_FLAG_NULLABLE
Tmpl expansions are allowed to produce no output.
Definition call_env.h:80
#define FR_CALL_ENV_OFFSET(_name, _cast_type, _flags, _struct, _field)
Specify a call_env_parser_t which writes out runtime results to the specified field.
Definition call_env.h:340
#define FR_CALL_ENV_PARSE_ONLY_OFFSET(_name, _cast_type, _flags, _struct, _parse_field)
Specify a call_env_parser_t which writes out the result of the parsing phase to the field specified.
Definition call_env.h:389
Per method call config.
Definition call_env.h:180
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:657
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:280
#define FR_CONF_OFFSET_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:594
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:285
#define CF_IDENT_ANY
Definition cf_util.h:75
#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
talloc_free(hp)
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:1636
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:1681
unlang_interpret_t * unlang_interpret_get_thread_default(void)
Get the default interpreter for this thread.
Definition interpret.c:2076
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition interpret.c:2053
#define DEBUG3(_fmt,...)
Definition log.h:266
#define RDEBUG3(fmt,...)
Definition log.h:355
#define RWARN(fmt,...)
Definition log.h:309
#define RERROR(fmt,...)
Definition log.h:310
#define RPERROR(fmt,...)
Definition log.h:314
#define REDEBUG2(fmt,...)
Definition log.h:384
#define fr_time()
Definition event.c:60
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_OCTETS
Raw octets.
unsigned char uint8_t
void * env_data
Per call environment data.
Definition module_ctx.h:44
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:42
void * thread
Thread specific instance data.
Definition module_ctx.h:43
void * rctx
Resume ctx that a module previously set.
Definition module_ctx.h:45
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
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
fr_pair_t * fr_pair_afrom_da_nested(TALLOC_CTX *ctx, fr_pair_list_t *list, fr_dict_attr_t const *da)
Create a pair (and all intermediate parents), and append it to the list.
Definition pair.c:480
static const conf_parser_t config[]
Definition base.c:163
static char const * url[FR_RADIUS_FAIL_MAX+1]
#define fr_assert(_expr)
Definition rad_assert.h:37
#define pair_update_request(_attr, _da)
#define REDEBUG(fmt,...)
#define DEBUG2(fmt,...)
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
void * fr_rb_first(fr_rb_tree_t *tree)
Definition rb.c:786
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:626
bool fr_rb_delete(fr_rb_tree_t *tree, void const *data)
Remove node and free data (if a free function was specified)
Definition rb.c:741
#define fr_rb_inline_talloc_alloc(_ctx, _type, _field, _data_cmp, _data_free)
Allocs a red black that verifies elements are of a specific talloc type.
Definition rb.h:244
#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 RETURN_UNLANG_RCODE(_rcode)
Definition rcode.h:61
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:44
@ 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_REJECT
Immediately reject the request.
Definition rcode.h:47
@ RLM_MODULE_NOTFOUND
User not found.
Definition rcode.h:53
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:54
#define RETURN_UNLANG_NOOP
Definition rcode.h:69
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)
pthread_mutex_t mutex
Definition rlm_crl.c:53
rlm_crl_mutable_t * mutable
Mutable data that's shared between all threads.
Definition rlm_crl.c:66
fr_value_box_list_t crl_data
Data from CRL expansion.
Definition rlm_crl.c:99
tmpl_t * ldap_exp
The xlat expansion used to retrieve the CRL via ldap://.
Definition rlm_crl.c:134
fr_value_box_list_t delta_urls
URLs from which a delta CRL can be retrieved.
Definition rlm_crl.c:78
bool force_delta_expiry_is_set
Definition rlm_crl.c:61
rlm_crl_thread_t * fetching
Pointer to thread instance data of thread which is fetching a CRL.
Definition rlm_crl.c:51
char const * ca_file
File containing certs for verifying CRL signatures.
Definition rlm_crl.c:63
crl_check_status_t
A status used to track which CRL is being checked.
Definition rlm_crl.c:90
@ CRL_CHECK_BASE
The base CRL is being checked.
Definition rlm_crl.c:91
@ CRL_CHECK_DELTA
The delta CRL exists and is being checked.
Definition rlm_crl.c:93
@ CRL_CHECK_FETCH_DELTA
The delta CRL is being fetched.
Definition rlm_crl.c:92
bool force_expiry_is_set
Definition rlm_crl.c:59
fr_rb_tree_t * crls
A tree of CRLs organised by CDP URL.
Definition rlm_crl.c:48
fr_value_box_t serial
The serial to check.
Definition rlm_crl.c:136
rlm_crl_thread_t * thread
The thread which fetched this entry.
Definition rlm_crl.c:80
crl_ret_t
Definition rlm_crl.c:140
@ CRL_MISSING_DELTA
Need to load a delta CRL to supplement this CRL.
Definition rlm_crl.c:146
@ CRL_ENTRY_FOUND
Serial was found in this CRL.
Definition rlm_crl.c:143
@ CRL_ENTRY_REMOVED
Serial was "un-revoked" in this delta CRL.
Definition rlm_crl.c:144
@ CRL_ERROR
Unspecified error ocurred.
Definition rlm_crl.c:141
@ CRL_NOT_FOUND
No CRL found, need to load it from the CDP URL.
Definition rlm_crl.c:145
@ CRL_ENTRY_NOT_FOUND
Serial not found in this CRL.
Definition rlm_crl.c:142
fr_value_box_t * cdp_url
The URL we're currently attempting to load.
Definition rlm_crl.c:97
fr_dict_attr_autoload_t rlm_crl_dict_attr[]
Definition rlm_crl.c:126
static fr_dict_t const * dict_freeradius
Definition rlm_crl.c:114
rlm_crl_t const * inst
The instance of the CRL module.
Definition rlm_crl.c:79
fr_rb_node_t node
The node in the tree.
Definition rlm_crl.c:77
char const * cdp_url
The URL of the CRL.
Definition rlm_crl.c:74
X509_STORE * verify_store
Store of certificates to verify CRL signatures.
Definition rlm_crl.c:65
fr_time_delta_t force_delta_expiry
Force expiry of delta CRLs after this time.
Definition rlm_crl.c:60
fr_rb_node_t node
Definition rlm_crl.c:86
X509_CRL * crl
The CRL.
Definition rlm_crl.c:73
request_t * request
Definition rlm_crl.c:85
bool trigger_rate_limit
Rate limit triggers.
Definition rlm_crl.c:68
fr_value_box_list_t missing_crls
CRLs missing from the tree.
Definition rlm_crl.c:100
fr_timer_list_t * timer_list
The timer list to use for CRL expiry.
Definition rlm_crl.c:49
static fr_dict_attr_t const * attr_crl_cdp_url
Definition rlm_crl.c:123
crl_entry_t * base_crl
The base CRL relating to the delta currently being fetched.
Definition rlm_crl.c:98
fr_time_delta_t force_expiry
Force expiry of CRLs after this time.
Definition rlm_crl.c:58
fr_rb_tree_t pending
Requests yielded while the CRL is being fetched.
Definition rlm_crl.c:40
fr_dict_autoload_t rlm_crl_dict[]
Definition rlm_crl.c:117
fr_timer_t * ev
When to expire the CRL.
Definition rlm_crl.c:76
module_rlm_t rlm_crl
Definition rlm_crl.c:873
tmpl_t * ftp_exp
The xlat expansion used to retrieve the CRL via ftp://.
Definition rlm_crl.c:135
char const * ca_path
Directory containing certs for verifying CRL signatures.
Definition rlm_crl.c:64
fr_value_box_list_head_t * cdp
The CRL distribution points.
Definition rlm_crl.c:137
tmpl_t * http_exp
The xlat expansion used to retrieve the CRL via http://.
Definition rlm_crl.c:133
static int mod_instantiate(module_inst_ctx_t const *mctx)
Instantiate the module.
Definition rlm_crl.c:837
static fr_dict_attr_t const * attr_crl_data
Definition rlm_crl.c:122
CONF_SECTION * virtual_server
Virtual server to use when retrieving CRLs.
Definition rlm_crl.c:57
static conf_parser_t module_config[]
Definition rlm_crl.c:104
crl_check_status_t status
Status of the current CRL check.
Definition rlm_crl.c:101
ASN1_INTEGER * crl_num
The CRL number.
Definition rlm_crl.c:75
fr_time_delta_t early_refresh
Time interval before nextUpdate to refresh.
Definition rlm_crl.c:62
CONF_SECTION * cs
Module instance config.
Definition rlm_crl.c:67
A single CRL in the global list of CRLs.
Definition rlm_crl.c:72
Structure to record a request which is waiting for CRL fetching to complete.
Definition rlm_crl.c:84
Global tree of CRLs.
Definition rlm_crl.c:47
Thread specific structure to hold requests awaiting CRL fetching.
Definition rlm_crl.c:39
static int mod_mutable_free(rlm_kv_mutable_t *mutable)
Definition rlm_kv.c:265
static char const * name
#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
size_t inst_size
Size of the module's instance data.
Definition module.h:212
void * data
Module's instance data.
Definition module.h:293
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:152
Named methods exported by a module.
Definition module.h:174
#define pair_delete_request(_pair_or_da)
Delete a fr_pair_t in the request list.
Definition pair.h:172
static fr_slen_t vpt
Definition tmpl.h:1269
fr_signal_t
Signals that can be generated/processed by request signal handlers.
Definition signal.h:38
@ FR_SIGNAL_CANCEL
Request has been cancelled.
Definition signal.h:40
unlang_action_t unlang_module_yield(request_t *request, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition module.c:431
unlang_action_t unlang_module_yield_to_tmpl(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *request, tmpl_t const *vpt, unlang_tmpl_args_t *args, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Push a pre-compiled tmpl and resumption state onto the stack for evaluation.
Definition module.c:216
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
char * talloc_bstrdup(TALLOC_CTX *ctx, char const *in)
Binary safe strdup function.
Definition talloc.c:589
#define talloc_get_type_abort_const
Definition talloc.h:110
static const char * names[8]
Definition time.c:584
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
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
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
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
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
@ T_BARE_WORD
Definition token.h:118
bool trigger_enabled(void)
Return whether triggers are enabled.
Definition trigger.c:94
int trigger(unlang_interpret_t *intp, CONF_SECTION const *cs, CONF_PAIR **trigger_cp, char const *name, bool rate_limit, fr_pair_list_t *args)
Execute a trigger - call an executable to process an event.
Definition trigger.c:155
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_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
Definition value.c:4379
void fr_value_box_strdup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Assign a buffer containing a nul terminated string to a box, but don't copy it.
Definition value.c:4714
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:4823
static fr_slen_t data
Definition value.h:1340
static fr_value_box_t * fr_value_box_acopy(TALLOC_CTX *ctx, fr_value_box_t const *src)
Copy an existing box, allocating a new box to hold its contents.
Definition value.h:744
#define fr_box_time_delta(_val)
Definition value.h:366
int nonnull(2, 5))
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
Definition value.h:655