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: 60f30d0a663de84e2d391f70e8edae8fd65251a4 $
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: 60f30d0a663de84e2d391f70e8edae8fd65251a4 $")
26
27#include <freeradius-devel/server/base.h>
28#include <freeradius-devel/server/module_rlm.h>
29#include <freeradius-devel/unlang/call_env.h>
30
31#include <freeradius-devel/tls/strerror.h>
32#include <freeradius-devel/tls/utils.h>
33
34#include <openssl/x509.h>
35#include <openssl/x509v3.h>
36#include <openssl/pem.h>
37#include <openssl/asn1.h>
38#include <openssl/bn.h>
39
40/** Thread specific structure to hold requests awaiting CRL fetching */
41typedef struct {
42 fr_rb_tree_t pending; //!< Requests yielded while the CRL is being fetched.
44
45/** Global tree of CRLs
46 *
47 * Separate from the instance data because that's protected.
48 */
49typedef struct {
50 fr_rb_tree_t *crls; //!< A tree of CRLs organised by CDP URL.
51 fr_timer_list_t *timer_list; //!< The timer list to use for CRL expiry.
52 ///< This gets serviced by the main loop.
53 rlm_crl_thread_t *fetching; //!< Pointer to thread instance data of
54 ///< thread which is fetching a CRL.
57
58typedef struct {
59 CONF_SECTION *virtual_server; //!< Virtual server to use when retrieving CRLs
60 fr_time_delta_t force_expiry; //!< Force expiry of CRLs after this time
62 fr_time_delta_t force_delta_expiry; //!< Force expiry of delta CRLs after this time
64 fr_time_delta_t early_refresh; //!< Time interval before nextUpdate to refresh
65 char const *ca_file; //!< File containing certs for verifying CRL signatures.
66 char const *ca_path; //!< Directory containing certs for verifying CRL signatures.
67 X509_STORE *verify_store; //!< Store of certificates to verify CRL signatures.
68 rlm_crl_mutable_t *mutable; //!< Mutable data that's shared between all threads.
69 CONF_SECTION *cs; //!< Module instance config.
70 bool trigger_rate_limit; //!< Rate limit triggers.
71} rlm_crl_t;
72
73/** A single CRL in the global list of CRLs */
74typedef struct {
75 X509_CRL *crl; //!< The CRL.
76 char const *cdp_url; //!< The URL of the CRL.
77 ASN1_INTEGER *crl_num; //!< The CRL number.
78 fr_timer_t *ev; //!< When to expire the CRL
79 fr_rb_node_t node; //!< The node in the tree
80 fr_value_box_list_t delta_urls; //!< URLs from which a delta CRL can be retrieved.
81 rlm_crl_t const *inst; //!< The instance of the CRL module.
82 rlm_crl_thread_t *thread; //!< The thread which fetched this entry.
84
85/** Structure to record a request which is waiting for CRL fetching to complete */
90
91/** A status used to track which CRL is being checked */
92typedef enum {
93 CRL_CHECK_BASE = 0, //!< The base CRL is being checked
94 CRL_CHECK_FETCH_DELTA, //!< The delta CRL is being fetched
95 CRL_CHECK_DELTA //!< The delta CRL exists and is being checked
97
98typedef struct {
99 fr_value_box_t *cdp_url; //!< The URL we're currently attempting to load.
100 crl_entry_t *base_crl; //!< The base CRL relating to the delta currently being fetched.
101 fr_value_box_list_t crl_data; //!< Data from CRL expansion.
102 fr_value_box_list_t missing_crls; //!< CRLs missing from the tree
103 crl_check_status_t status; //!< Status of the current CRL check.
105
107 { FR_CONF_OFFSET_IS_SET("force_expiry", FR_TYPE_TIME_DELTA, 0, rlm_crl_t, force_expiry) },
108 { FR_CONF_OFFSET_IS_SET("force_delta_expiry", FR_TYPE_TIME_DELTA, 0, rlm_crl_t, force_delta_expiry) },
109 { FR_CONF_OFFSET("early_refresh", rlm_crl_t, early_refresh) },
110 { FR_CONF_OFFSET("ca_file", rlm_crl_t, ca_file) },
111 { FR_CONF_OFFSET("ca_path", rlm_crl_t, ca_path) },
112 { FR_CONF_OFFSET("trigger_rate_limit", rlm_crl_t, trigger_rate_limit), .dflt = "yes" },
114};
115
117
120 { .out = &dict_freeradius, .proto = "freeradius" },
121 { NULL }
122};
123
126
129 { .out = &attr_crl_data, .name = "CRL.Data", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
130 { .out = &attr_crl_cdp_url, .name = "CRL.CDP-URL", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
131 { NULL }
132};
133
134typedef struct {
135 tmpl_t *http_exp; //!< The xlat expansion used to retrieve the CRL via http://
136 tmpl_t *ldap_exp; //!< The xlat expansion used to retrieve the CRL via ldap://
137 tmpl_t *ftp_exp; //!< The xlat expansion used to retrieve the CRL via ftp://
138 fr_value_box_t serial; //!< The serial to check
139 fr_value_box_list_head_t *cdp; //!< The CRL distribution points
141
142typedef enum {
143 CRL_ERROR = -1, //!< Unspecified error ocurred.
144 CRL_ENTRY_NOT_FOUND = 0, //!< Serial not found in this CRL.
145 CRL_ENTRY_FOUND = 1, //!< Serial was found in this CRL.
146 CRL_ENTRY_REMOVED = 2, //!< Serial was "un-revoked" in this delta CRL.
147 CRL_NOT_FOUND = 3, //!< No CRL found, need to load it from the CDP URL
148 CRL_MISSING_DELTA = 4, //!< Need to load a delta CRL to supplement this CRL.
149} crl_ret_t;
150
151#ifdef WITH_TLS
152static const call_env_method_t crl_env = {
154 .env = (call_env_parser_t[]){
156 ((call_env_parser_t[]) {
158 ((call_env_parser_t[]) {
163 }))},
165 }))},
167 .pair.dflt = "session-state.TLS-Certificate.Serial", .pair.dflt_quote = T_BARE_WORD },
169 .pair.dflt = "session-state.TLS-Certificate.X509v3-CRL-Distribution-Points[*]", .pair.dflt_quote = T_BARE_WORD },
171 },
172};
173
174static int8_t crl_cmp(void const *a, void const *b)
175{
176 crl_entry_t const *crl_a = (crl_entry_t const *)a;
177 crl_entry_t const *crl_b = (crl_entry_t const *)b;
178
179 return CMP(strcmp(crl_a->cdp_url, crl_b->cdp_url), 0);
180}
181
182static void crl_free(void *data)
183{
185}
186
187static int8_t crl_pending_cmp(void const *a, void const *b)
188{
189 crl_pending_t const *pending_a = (crl_pending_t const *)a;
190 crl_pending_t const *pending_b = (crl_pending_t const *)b;
191
192 return CMP(pending_a->request, pending_b->request);
193}
194
195static void crl_expire(fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
196{
197 crl_entry_t *crl = talloc_get_type_abort(uctx, crl_entry_t);
198
199 DEBUG2("CRL associated with CDP %s expired", crl->cdp_url);
200
201 /*
202 * If the mutex is locked and this thread is fetching a CRL, asynchonously,
203 * insert a new timer event - otherwise the mutex will never be unlocked.
204 */
205 if (pthread_mutex_trylock(&crl->inst->mutable->mutex) != 0) {
206 if (crl->inst->mutable->fetching == crl->thread) {
207 if (fr_timer_in(crl, tl, &crl->ev, fr_time_delta_from_sec(1), false, crl_expire, crl) <0) {
208 ERROR("Failed inserting CRL expiry event");
209 }
210 return;
211 }
212 pthread_mutex_lock(&crl->inst->mutable->mutex);
213 }
214 fr_rb_remove(crl->inst->mutable->crls, crl);
215 pthread_mutex_unlock(&crl->inst->mutable->mutex);
216
217 if (trigger_enabled()) {
219 fr_pair_t *vp;
222 fr_value_box_strdup_shallow(&vp->data, NULL, crl->cdp_url, true);
223 trigger(unlang_interpret_get_thread_default(), crl->inst->cs, NULL, "modules.crl.expired",
225 }
226
227 talloc_free(crl);
228}
229
230/** Make sure we don't lock up the server if a request is cancelled
231 */
232static void crl_signal(module_ctx_t const *mctx, UNUSED request_t *request, fr_signal_t action)
233{
235
236 if (action == FR_SIGNAL_CANCEL) {
237 pthread_mutex_unlock(&inst->mutable->mutex);
239 }
240}
241
242/** See if a particular serial is present in a CRL list
243 *
244 */
245static crl_ret_t crl_check_entry(crl_entry_t *crl_entry, request_t *request, uint8_t const *serial)
246{
247 X509_REVOKED *revoked;
248 ASN1_INTEGER *asn1_serial = NULL;
249 int ret;
250
251 asn1_serial = d2i_ASN1_INTEGER(NULL, (unsigned char const **)&serial, talloc_array_length(serial));
252 ret = X509_CRL_get0_by_serial(crl_entry->crl, &revoked, asn1_serial);
253 ASN1_INTEGER_free(asn1_serial);
254 switch (ret) {
255 /* The docs describe 0 as "failure" - but that means "failed to find"*/
256 case 0:
257 RDEBUG3("Certificate not in CRL");
258 return CRL_ENTRY_NOT_FOUND;
259
260 case 1:
261 REDEBUG2("Certificate revoked by %s", crl_entry->cdp_url);
262 return CRL_ENTRY_FOUND;
263
264 case 2:
265 RDEBUG3("Certificate un-revoked by %s", crl_entry->cdp_url);
266 return CRL_ENTRY_REMOVED;
267 }
268
269 return CRL_ERROR;
270}
271
272/** Resolve a cdp_url to a CRL entry, and check serial against it, if it exists
273 *
274 */
275static crl_ret_t crl_check_serial(fr_rb_tree_t *crls, request_t *request, char const *cdp_url, uint8_t const *serial,
276 crl_entry_t **found)
277{
278 crl_entry_t *delta, find = { .cdp_url = cdp_url};
279 fr_value_box_t *vb = NULL;
281
282 *found = fr_rb_find(crls, &find);
283 if (*found == NULL) return CRL_NOT_FOUND;
284
285 /*
286 * First check the delta if it should exist
287 */
288 while ((vb = fr_value_box_list_next(&(*found)->delta_urls, vb))) {
289 find.cdp_url = vb->vb_strvalue;
290 delta = fr_rb_find(crls, &find);
291 if (delta) {
292 ret = crl_check_entry(delta, request, serial);
293
294 /*
295 * An entry found in a delta overrides the base CRL
296 */
297 if (ret != CRL_ENTRY_NOT_FOUND) return ret;
298 break;
299 } else {
300 ret = CRL_MISSING_DELTA;
301 }
302 }
303
304 if (ret == CRL_MISSING_DELTA) return ret;
305
306 return crl_check_entry(*found, request, serial);
307}
308
309static int _crl_entry_free(crl_entry_t *crl_entry)
310{
311 X509_CRL_free(crl_entry->crl);
312 if (crl_entry->crl_num) ASN1_INTEGER_free(crl_entry->crl_num);
313 return 0;
314}
315
316/** Add an entry to the cdp_url -> crl tree
317 *
318 * @note Must be called with the mutex held.
319 */
320static crl_entry_t *crl_entry_create(rlm_crl_t const *inst, fr_timer_list_t *tl, char const *url, uint8_t const *data,
321 crl_entry_t *base_crl)
322{
323 uint8_t const *our_data = data;
324 crl_entry_t *crl;
325 time_t next_update;
326 fr_time_t now = fr_time();
327 fr_time_delta_t expiry_time;
328 int i;
329 STACK_OF(DIST_POINT) *dps;
330 X509_STORE_CTX *verify_ctx = NULL;
331 X509_OBJECT *xobj;
332 EVP_PKEY *pkey;
333
334 MEM(crl = talloc_zero(inst->mutable->crls, crl_entry_t));
335 crl->cdp_url = talloc_bstrdup(crl, url);
336 crl->crl = d2i_X509_CRL(NULL, (const unsigned char **)&our_data, talloc_array_length(our_data));
337 if (crl->crl == NULL) {
338 fr_tls_strerror_printf("Failed to parse CRL from %s", url);
339 error:
340 talloc_free(crl);
341 if (verify_ctx) X509_STORE_CTX_free(verify_ctx);
342 return NULL;
343 }
344 talloc_set_destructor(crl, _crl_entry_free);
345
346 verify_ctx = X509_STORE_CTX_new();
347 if (!verify_ctx || !X509_STORE_CTX_init(verify_ctx, inst->verify_store, NULL, NULL)) {
348 fr_tls_strerror_printf("Error initialising X509 store");
349 goto error;
350 }
351
352 xobj = X509_STORE_CTX_get_obj_by_subject(verify_ctx, X509_LU_X509,
353 X509_CRL_get_issuer(crl->crl));
354 if (!xobj) {
355 fr_tls_strerror_printf("CRL issuer certificate not in trusted store");
356 goto error;
357 }
358 pkey = X509_get_pubkey(X509_OBJECT_get0_X509(xobj));
359 X509_OBJECT_free(xobj);
360 if (!pkey) {
361 fr_tls_strerror_printf("Error getting CRL issuer public key");
362 goto error;
363 }
364 i = X509_CRL_verify(crl->crl, pkey);
365 EVP_PKEY_free(pkey);
366
367 if (i < 0) {
368 fr_tls_strerror_printf("Could not verify CRL signature");
369 goto error;
370 }
371 if (i == 0) {
372 fr_tls_strerror_printf("CRL certificate signature failed");
373 goto error;
374 }
375
376 crl->crl_num = X509_CRL_get_ext_d2i(crl->crl, NID_crl_number, &i, NULL);
377
378 /*
379 * If we're passed a base_crl, then this is a delta - check the delta
380 * relates to the correct base.
381 */
382 if (base_crl) {
383 ASN1_INTEGER *base_num = X509_CRL_get_ext_d2i(crl->crl, NID_delta_crl, &i, NULL);
384 if (!base_num) {
385 fr_tls_strerror_printf("Delta CRL missing Delta CRL Indicator extension");
386 goto error;
387 }
388 if (ASN1_INTEGER_cmp(base_num, base_crl->crl_num) > 0) {
389 uint64_t delta_base, crl_num;
390 ASN1_INTEGER_get_uint64(&delta_base, base_num);
391 ASN1_INTEGER_get_uint64(&crl_num, base_crl->crl_num);
392 fr_tls_strerror_printf("Delta CRL referrs to base CRL number %"PRIu64", current base is %"PRIu64,
393 delta_base, crl_num);
394 ASN1_INTEGER_free(base_num);
395 goto error;
396 }
397 ASN1_INTEGER_free(base_num);
398 if (ASN1_INTEGER_cmp(crl->crl_num, base_crl->crl_num) < 0) {
399 uint64_t delta_num, crl_num;
400 ASN1_INTEGER_get_uint64(&delta_num, crl->crl_num);
401 ASN1_INTEGER_get_uint64(&crl_num, base_crl->crl_num);
402 fr_tls_strerror_printf("Delta CRL number %"PRIu64" is less than base CRL number %"PRIu64,
403 delta_num, crl_num);
404 goto error;
405 }
406 }
407
408 if (fr_tls_utils_asn1time_to_epoch(&next_update, X509_CRL_get0_nextUpdate(crl->crl)) < 0) {
409 fr_tls_strerror_printf("Failed to parse nextUpdate from CRL");
410 goto error;
411 }
412
413 if (!fr_rb_insert(inst->mutable->crls, crl)) {
414 ERROR("Failed to insert CRL into tree of CRLs");
415 goto error;
416 }
417 crl->inst = inst;
418
419 /*
420 * Check if this CRL has a Freshest CRL extension - the list of URIs to get deltas from
421 */
422 fr_value_box_list_init(&crl->delta_urls);
423 if (!base_crl && (dps = X509_CRL_get_ext_d2i(crl->crl, NID_freshest_crl, NULL, NULL))) {
424 DIST_POINT *dp;
425 STACK_OF(GENERAL_NAME) *names;
426 GENERAL_NAME *name;
427 int j;
428 fr_value_box_t *vb;
429
430 for (i = 0; i < sk_DIST_POINT_num(dps); i++) {
431 dp = sk_DIST_POINT_value(dps, i);
432 names = dp->distpoint->name.fullname;
433 for (j = 0; j < sk_GENERAL_NAME_num(names); j++) {
434 name = sk_GENERAL_NAME_value(names, j);
435 if (name->type != GEN_URI) continue;
436 MEM(vb = fr_value_box_alloc_null(crl));
437 fr_value_box_bstrndup(vb, vb, NULL,
438 (char const *)ASN1_STRING_get0_data(name->d.uniformResourceIdentifier),
439 ASN1_STRING_length(name->d.uniformResourceIdentifier), true);
440 DEBUG3("CRL references delta URI %pV", vb);
441 fr_value_box_list_insert_tail(&crl->delta_urls, vb);
442 }
443 }
444 CRL_DIST_POINTS_free(dps);
445 }
446
447 expiry_time = fr_time_delta_sub(fr_time_sub(fr_time_from_sec(next_update), now), inst->early_refresh);
448 if (base_crl && inst->force_delta_expiry_is_set) {
449 if (fr_time_delta_cmp(expiry_time, inst->force_delta_expiry)) expiry_time = inst->force_delta_expiry;
450 } else {
451 if (inst->force_expiry_is_set &&
452 (fr_time_delta_cmp(expiry_time, inst->force_expiry) > 0)) expiry_time = inst->force_expiry;
453 }
454
455 DEBUG3("CRL from %s will expire in %pVs", url, fr_box_time_delta(expiry_time));
456 if (fr_timer_in(crl, tl, &crl->ev, expiry_time, false, crl_expire, crl) <0) {
457 ERROR("Failed to set timer to expire CRL");
458 }
459
460 X509_STORE_CTX_free(verify_ctx);
461 return crl;
462}
463
464static unlang_action_t CC_HINT(nonnull) crl_process_cdp_data(unlang_result_t *p_result, module_ctx_t const *mctx,
465 request_t *request);
466
467/** Yield to a tmpl to retrieve CRL data
468 *
469 * @param request the current request.
470 * @param inst module instance data.
471 * @param thread thread instance data.
472 * @param env the call_env for this module call.
473 * @param rctx the resume ctx for this module call.
474 *
475 * @returns
476 * - 1 - new tmpl pushed.
477 * - 0 - no tmpl pushed, soft fail.
478 * - -1 - no tmpl pushed, hard fail
479 */
480static int crl_tmpl_yield(request_t *request, rlm_crl_t const *inst, rlm_crl_thread_t *thread, rlm_crl_env_t *env,
481 rlm_crl_rctx_t *rctx)
482{
483 fr_pair_t *vp;
484 tmpl_t *vpt;
485
487 MEM(fr_value_box_copy(vp, &vp->data, rctx->cdp_url) == 0);
488
489 if (strncmp(rctx->cdp_url->vb_strvalue, "http", 4) == 0) {
490 vpt = env->http_exp;
491 } else if (strncmp(rctx->cdp_url->vb_strvalue, "ldap", 4) == 0) {
492 if (!env->ldap_exp) {
493 RWARN("CRL URI %pV requires LDAP, but the crl module ldap expansion is not configured", rctx->cdp_url);
494 return 0;
495 }
496 vpt = env->ldap_exp;
497 } else if (strncmp(rctx->cdp_url->vb_strvalue, "ftp", 3) == 0) {
498 if (!env->ftp_exp) {
499 RWARN("CRL URI %pV requires FTP, but the crl module ftp expansion is not configured", rctx->cdp_url);
500 return 0;
501 }
502 vpt = env->ftp_exp;
503 } else {
504 RERROR("Unsupported URI scheme in CRL URI %pV", rctx->cdp_url);
505 return -1;
506 }
507
508 trigger(unlang_interpret_get_thread_default(), inst->cs, NULL, "modules.crl.fetchuri", inst->trigger_rate_limit,
509 &request->request_pairs);
510
511 if (unlang_module_yield_to_tmpl(rctx, &rctx->crl_data, request, vpt,
512 NULL, crl_process_cdp_data, crl_signal, 0, rctx) < 0) return -1;
513 inst->mutable->fetching = thread;
514 return 1;
515}
516
517static unlang_action_t crl_by_url_start(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request);
518
519/** Process the response from evaluating the cdp_url -> crl_data expansion
520 *
521 * This is the resumption function when we yield to get CRL data associated with a URL
522 */
523static unlang_action_t CC_HINT(nonnull) crl_process_cdp_data(unlang_result_t *p_result, module_ctx_t const *mctx,
524 request_t *request)
525{
527 rlm_crl_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_crl_thread_t);
528 rlm_crl_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_crl_env_t);
529 rlm_crl_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, rlm_crl_rctx_t);
532 crl_pending_t *pending;
533
534 inst->mutable->fetching = NULL;
535 switch (fr_value_box_list_num_elements(&rctx->crl_data)) {
536 case 0:
537 REDEBUG("No CRL data returned from %pV, failing", rctx->cdp_url);
538 trigger(unlang_interpret_get_thread_default(), inst->cs, NULL, "modules.crl.fetchfail",
539 inst->trigger_rate_limit, &request->request_pairs);
540 again:
541 talloc_free(rctx->cdp_url);
542
543 /*
544 * If there are more URIs to try, push a new tmpl to expand.
545 */
546 rctx->cdp_url = fr_value_box_list_pop_head(&rctx->missing_crls);
547 if (rctx->cdp_url) {
548 switch (crl_tmpl_yield(request, inst, t, env, rctx)) {
549 case 0:
550 goto again;
551 case 1:
553 default:
554 break;
555 }
556 }
557 fail:
558 fr_value_box_list_talloc_free(&rctx->crl_data);
560 goto finish;
561
562 case 1:
563 {
564 crl_entry_t *crl_entry;
565 fr_value_box_t *crl_data = fr_value_box_list_pop_head(&rctx->crl_data);
566
567 crl_entry = crl_entry_create(inst, unlang_interpret_event_list(request)->tl,
568 rctx->cdp_url->vb_strvalue,
569 crl_data->vb_octets, rctx->base_crl);
570 talloc_free(crl_data);
571 if (!crl_entry) {
572 RPERROR("Failed to process returned CRL data");
573 trigger(unlang_interpret_get_thread_default(), inst->cs, NULL, "modules.crl.fetchbad",
574 inst->trigger_rate_limit, &request->request_pairs);
575 goto again;
576 }
577
578 /*
579 * We've successfully loaded a URI - so we can clear the list of missing crls
580 * This can then be re-used to hold missing delta CRLs if needed.
581 */
582 fr_value_box_list_talloc_free(&rctx->missing_crls);
583
584 if (fr_value_box_list_num_elements(&crl_entry->delta_urls) > 0) {
585 crl_entry_t *delta, find;
586 fr_value_box_t *vb = NULL, *delta_uri;
587
588 rctx->status = CRL_CHECK_DELTA;
589 while ((vb = fr_value_box_list_next(&crl_entry->delta_urls, vb))) {
590 find.cdp_url = vb->vb_strvalue;
591 delta = fr_rb_find(inst->mutable->crls, &find);
592 if (delta) {
593 ret = crl_check_entry(delta, request, env->serial.vb_octets);
594 /*
595 * The delta contained an entry for this serial - so this
596 * is the return status.
597 */
598 if (ret != CRL_ENTRY_NOT_FOUND) break;
599 } else {
600 delta_uri = fr_value_box_acopy(rctx, vb);
601 fr_value_box_list_insert_tail(&rctx->missing_crls, delta_uri);
602 }
603 }
604
605 /*
606 * None of the delta CRL URIs were found, so go and get one.
607 * The list of URIs to fetch will now be in rctx->missing_crls
608 */
609 if (ret == CRL_NOT_FOUND) {
611 rctx->base_crl = crl_entry;
612 goto again;
613 }
614 }
615
616 if (rctx->status != CRL_CHECK_DELTA) ret = crl_check_entry(crl_entry, request, env->serial.vb_octets);
617 check_return:
618 switch (ret) {
619 case CRL_ENTRY_FOUND:
620 rcode = RLM_MODULE_REJECT;
621 goto finish;
622
624 /*
625 * We have a CRL, but the serial is not in it.
626 *
627 * If this was after fetching a delta, go check the base
628 */
629 if (rctx->status == CRL_CHECK_FETCH_DELTA) {
630 RDEBUG3("Certificate not in delta CRL, checking base CRL");
631 rctx->status = CRL_CHECK_BASE;
632 ret = crl_check_entry(rctx->base_crl, request, env->serial.vb_octets);
633 goto check_return;
634 }
636
638 rcode = RLM_MODULE_OK;
640 goto finish;
641
642 case CRL_ERROR:
643 goto fail;
644
645 /*
646 * This should never be returned by crl_check_entry because we provided the entry!
647 */
649 case CRL_NOT_FOUND:
650 fr_assert(0);
651 goto fail;
652 }
653
654 }
655 break;
656
657 default:
658 REDEBUG("Too many CRL values returned, failing");
659 goto fail;
660 }
661
662finish:
663 pthread_mutex_unlock(&inst->mutable->mutex);
664 pending = fr_rb_first(&t->pending);
665 if (pending) unlang_interpret_mark_runnable(pending->request);
666 RETURN_UNLANG_RCODE(rcode);
667}
668
669static unlang_action_t CC_HINT(nonnull(1,2,3,4,6)) crl_by_url(unlang_result_t *p_result, rlm_crl_t const *inst,
671 rlm_crl_rctx_t *rctx, request_t *request)
672{
674 crl_entry_t *found;
675
676 if (!rctx) rctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), rlm_crl_rctx_t);
677 fr_value_box_list_init(&rctx->missing_crls);
678
679 pthread_mutex_lock(&inst->mutable->mutex);
680
681 /*
682 * Fast path when we have a CRL.
683 * All distribution points are considered equivalent, so check if
684 * if we have any of them before attempting to fetch missing ones.
685 */
686 while ((rctx->cdp_url = fr_value_box_list_pop_head(env->cdp))) {
687 switch (crl_check_serial(inst->mutable->crls, request, rctx->cdp_url->vb_strvalue,
688 env->serial.vb_octets, &found)) {
689 case CRL_ENTRY_FOUND:
690 rcode = RLM_MODULE_REJECT;
691 break;
692
695 rcode = RLM_MODULE_OK;
696 break;
697
698 case CRL_ERROR:
699 continue;
700
701 case CRL_NOT_FOUND:
702 fr_value_box_list_insert_tail(&rctx->missing_crls, rctx->cdp_url);
703 rcode = RLM_MODULE_NOTFOUND;
704 continue;
705
707 {
708 /*
709 * We found a base CRL, but it has a delta which
710 * was not found. Populate the "missing" list with
711 * the CDP for the delta and go get it.
712 */
713 fr_value_box_t *vb = NULL, *delta_uri;
714 rctx->base_crl = found;
715 rctx->status = CRL_CHECK_FETCH_DELTA;
716 fr_value_box_list_talloc_free(&rctx->missing_crls);
717 while ((vb = fr_value_box_list_next(&found->delta_urls, vb))) {
718 delta_uri = fr_value_box_acopy(rctx, vb);
719 fr_value_box_list_insert_tail(&rctx->missing_crls, delta_uri);
720 }
721 goto fetch_missing;
722 }
723 }
724 }
725
726 if (rcode != RLM_MODULE_NOTFOUND) {
727 crl_pending_t *pending;
728 finish:
729 pthread_mutex_unlock(&inst->mutable->mutex);
730
731 pending = fr_rb_first(&t->pending);
732 if (pending) unlang_interpret_mark_runnable(pending->request);
733
734 RETURN_UNLANG_RCODE(rcode);
735 }
736
737 /*
738 * Need to convert a missing cdp_url to a CRL entry
739 *
740 * We yield to an expansion to allow this to happen, then parse the CRL data
741 * and check if the serial has an entry in the CRL.
742 */
743fetch_missing:
744 fr_value_box_list_init(&rctx->crl_data);
745
746again:
747 rctx->cdp_url = fr_value_box_list_pop_head(&rctx->missing_crls);
748
749 switch (crl_tmpl_yield(request, inst, t, env, rctx)) {
750 case 0:
751 goto again;
752 case 1:
753 /*
754 * The lock is released after the pushed tmpl result is handled
755 */
756 /* coverity[missing_unlock] */
758 default:
759 rcode = RLM_MODULE_FAIL;
760 goto finish;
761 }
762}
763
764static unlang_action_t CC_HINT(nonnull) crl_by_url_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
765{
767 rlm_crl_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_crl_env_t);
768 rlm_crl_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_crl_thread_t);
769 crl_pending_t find, *found;
770
771 find.request = request;
772 found = fr_rb_find(&t->pending, &find);
773 if (!found) RETURN_UNLANG_NOOP;
774
775 fr_rb_delete(&t->pending, found);
776 return crl_by_url(p_result, inst, t, env, mctx->rctx, request);
777}
778
779static void crl_by_url_cancel(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
780{
781 rlm_crl_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_crl_thread_t);
782 crl_pending_t *found, find;
783
784 find.request = request;
785 found = fr_rb_find(&t->pending, &find);
786 if (!found) return;
787
788 fr_rb_delete(&t->pending, found);
789}
790
791static unlang_action_t CC_HINT(nonnull) crl_by_url_start(unlang_result_t *p_result, module_ctx_t const *mctx,
792 request_t *request)
793{
795 rlm_crl_env_t *env = talloc_get_type_abort(mctx->env_data, rlm_crl_env_t);
796 rlm_crl_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_crl_thread_t);
797 crl_pending_t *pending;
798
799 if (fr_value_box_list_num_elements(env->cdp) == 0) RETURN_UNLANG_NOOP;
800
801 if (inst->mutable->fetching != t) return crl_by_url(p_result, inst, t, env, mctx->rctx, request);
802
803 MEM(pending = talloc_zero(t, crl_pending_t));
804 pending->request = request;
805
806 fr_rb_insert(&t->pending, pending);
807 RDEBUG3("Yielding request until CRL fetching completed");
808 return unlang_module_yield(request, crl_by_url_resume, crl_by_url_cancel, ~FR_SIGNAL_CANCEL, mctx->rctx);
809}
810
812{
813 rlm_crl_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_crl_thread_t);
814
815 fr_rb_inline_init(&t->pending, crl_pending_t, node, crl_pending_cmp, NULL);
816
817 return 0;
818}
819
820static int mod_mutable_free(rlm_crl_mutable_t *mutable)
821{
822 pthread_mutex_destroy(&mutable->mutex);
823 return 0;
824}
825
826static int mod_detach(module_detach_ctx_t const *mctx)
827{
828 rlm_crl_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_crl_t);
829
830 if (inst->verify_store) X509_STORE_free(inst->verify_store);
831 talloc_free(inst->mutable);
832 return 0;
833}
834#endif
835
836/** Instantiate the module
837 *
838 */
839static int mod_instantiate(module_inst_ctx_t const *mctx)
840{
841#ifdef WITH_TLS
842 rlm_crl_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_crl_t);
843
844 MEM(inst->mutable = talloc_zero(NULL, rlm_crl_mutable_t));
845 MEM(inst->mutable->crls = fr_rb_inline_talloc_alloc(inst->mutable, crl_entry_t, node, crl_cmp, crl_free));
846 pthread_mutex_init(&inst->mutable->mutex, NULL);
847 talloc_set_destructor(inst->mutable, mod_mutable_free);
848
849 if (!inst->ca_file && !inst->ca_path) {
850 cf_log_err(mctx->mi->conf, "Missing ca_file / ca_path option. One or other (or both) must be specified.");
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 return -1;
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:485
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:324
#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:112
#define UNUSED
Definition build.h:317
#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:662
#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:284
#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:298
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:599
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:286
#define CF_IDENT_ANY
Definition cf_util.h:78
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:41
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:287
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:300
Specifies an attribute which must be present for the module to function.
Definition dict.h:286
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:299
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:1620
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:1665
unlang_interpret_t * unlang_interpret_get_thread_default(void)
Get the default interpreter for this thread.
Definition interpret.c:2040
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition interpret.c:2017
#define DEBUG3(_fmt,...)
Definition log.h:266
#define RDEBUG3(fmt,...)
Definition log.h:343
#define RWARN(fmt,...)
Definition log.h:297
#define RERROR(fmt,...)
Definition log.h:298
#define RPERROR(fmt,...)
Definition log.h:302
#define REDEBUG2(fmt,...)
Definition log.h:372
talloc_free(reap)
@ 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:473
static const conf_parser_t config[]
Definition base.c:186
#define fr_assert(_expr)
Definition rad_assert.h:38
#define pair_update_request(_attr, _da)
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define DEBUG2(fmt,...)
Definition radclient.h:43
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:246
#define fr_rb_inline_init(_tree, _type, _field, _data_cmp, _data_free)
Initialises a red black tree.
Definition rb.h:180
The main red black tree structure.
Definition rb.h:73
#define RETURN_UNLANG_RCODE(_rcode)
Definition rcode.h:57
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:45
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:44
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition rcode.h:43
@ RLM_MODULE_NOTFOUND
User not found.
Definition rcode.h:49
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:50
#define RETURN_UNLANG_NOOP
Definition rcode.h:65
static int mod_detach(module_detach_ctx_t const *mctx)
Definition rlm_always.c:137
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
pthread_mutex_t mutex
Definition rlm_crl.c:55
rlm_crl_mutable_t * mutable
Mutable data that's shared between all threads.
Definition rlm_crl.c:68
fr_value_box_list_t crl_data
Data from CRL expansion.
Definition rlm_crl.c:101
tmpl_t * ldap_exp
The xlat expansion used to retrieve the CRL via ldap://.
Definition rlm_crl.c:136
fr_value_box_list_t delta_urls
URLs from which a delta CRL can be retrieved.
Definition rlm_crl.c:80
bool force_delta_expiry_is_set
Definition rlm_crl.c:63
rlm_crl_thread_t * fetching
Pointer to thread instance data of thread which is fetching a CRL.
Definition rlm_crl.c:53
char const * ca_file
File containing certs for verifying CRL signatures.
Definition rlm_crl.c:65
crl_check_status_t
A status used to track which CRL is being checked.
Definition rlm_crl.c:92
@ CRL_CHECK_BASE
The base CRL is being checked.
Definition rlm_crl.c:93
@ CRL_CHECK_DELTA
The delta CRL exists and is being checked.
Definition rlm_crl.c:95
@ CRL_CHECK_FETCH_DELTA
The delta CRL is being fetched.
Definition rlm_crl.c:94
bool force_expiry_is_set
Definition rlm_crl.c:61
fr_rb_tree_t * crls
A tree of CRLs organised by CDP URL.
Definition rlm_crl.c:50
fr_value_box_t serial
The serial to check.
Definition rlm_crl.c:138
rlm_crl_thread_t * thread
The thread which fetched this entry.
Definition rlm_crl.c:82
crl_ret_t
Definition rlm_crl.c:142
@ CRL_MISSING_DELTA
Need to load a delta CRL to supplement this CRL.
Definition rlm_crl.c:148
@ CRL_ENTRY_FOUND
Serial was found in this CRL.
Definition rlm_crl.c:145
@ CRL_ENTRY_REMOVED
Serial was "un-revoked" in this delta CRL.
Definition rlm_crl.c:146
@ CRL_ERROR
Unspecified error ocurred.
Definition rlm_crl.c:143
@ CRL_NOT_FOUND
No CRL found, need to load it from the CDP URL.
Definition rlm_crl.c:147
@ CRL_ENTRY_NOT_FOUND
Serial not found in this CRL.
Definition rlm_crl.c:144
fr_value_box_t * cdp_url
The URL we're currently attempting to load.
Definition rlm_crl.c:99
fr_dict_attr_autoload_t rlm_crl_dict_attr[]
Definition rlm_crl.c:128
static fr_dict_t const * dict_freeradius
Definition rlm_crl.c:116
rlm_crl_t const * inst
The instance of the CRL module.
Definition rlm_crl.c:81
fr_rb_node_t node
The node in the tree.
Definition rlm_crl.c:79
char const * cdp_url
The URL of the CRL.
Definition rlm_crl.c:76
X509_STORE * verify_store
Store of certificates to verify CRL signatures.
Definition rlm_crl.c:67
fr_time_delta_t force_delta_expiry
Force expiry of delta CRLs after this time.
Definition rlm_crl.c:62
fr_rb_node_t node
Definition rlm_crl.c:88
X509_CRL * crl
The CRL.
Definition rlm_crl.c:75
request_t * request
Definition rlm_crl.c:87
bool trigger_rate_limit
Rate limit triggers.
Definition rlm_crl.c:70
fr_value_box_list_t missing_crls
CRLs missing from the tree.
Definition rlm_crl.c:102
fr_timer_list_t * timer_list
The timer list to use for CRL expiry.
Definition rlm_crl.c:51
static fr_dict_attr_t const * attr_crl_cdp_url
Definition rlm_crl.c:125
crl_entry_t * base_crl
The base CRL relating to the delta currently being fetched.
Definition rlm_crl.c:100
fr_time_delta_t force_expiry
Force expiry of CRLs after this time.
Definition rlm_crl.c:60
fr_rb_tree_t pending
Requests yielded while the CRL is being fetched.
Definition rlm_crl.c:42
fr_dict_autoload_t rlm_crl_dict[]
Definition rlm_crl.c:119
fr_timer_t * ev
When to expire the CRL.
Definition rlm_crl.c:78
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:137
char const * ca_path
Directory containing certs for verifying CRL signatures.
Definition rlm_crl.c:66
fr_value_box_list_head_t * cdp
The CRL distribution points.
Definition rlm_crl.c:139
tmpl_t * http_exp
The xlat expansion used to retrieve the CRL via http://.
Definition rlm_crl.c:135
static int mod_instantiate(module_inst_ctx_t const *mctx)
Instantiate the module.
Definition rlm_crl.c:839
static fr_dict_attr_t const * attr_crl_data
Definition rlm_crl.c:124
CONF_SECTION * virtual_server
Virtual server to use when retrieving CRLs.
Definition rlm_crl.c:59
static conf_parser_t module_config[]
Definition rlm_crl.c:106
crl_check_status_t status
Status of the current CRL check.
Definition rlm_crl.c:103
ASN1_INTEGER * crl_num
The CRL number.
Definition rlm_crl.c:77
fr_time_delta_t early_refresh
Time interval before nextUpdate to refresh.
Definition rlm_crl.c:64
CONF_SECTION * cs
Module instance config.
Definition rlm_crl.c:69
A single CRL in the global list of CRLs.
Definition rlm_crl.c:74
Structure to record a request which is waiting for CRL fetching to complete.
Definition rlm_crl.c:86
Global tree of CRLs.
Definition rlm_crl.c:49
Thread specific structure to hold requests awaiting CRL fetching.
Definition rlm_crl.c:41
static char const * name
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:40
#define MODULE_THREAD_INST(_ctype)
Definition module.h:256
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:349
size_t inst_size
Size of the module's instance data.
Definition module.h:212
void * data
Module's instance data.
Definition module.h:291
#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:429
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
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition state_test.c:8
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:558
#define talloc_get_type_abort_const
Definition talloc.h:287
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:50
A timer event.
Definition timer.c:84
#define fr_timer_in(...)
Definition timer.h:87
@ T_BARE_WORD
Definition token.h:120
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:149
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:4148
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:4477
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:4589
static fr_slen_t data
Definition value.h:1291
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:737
#define fr_box_time_delta(_val)
Definition value.h:365
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:654