25RCSID(
"$Id: 67210b48a732b93063254846da8ee12469b97df3 $")
27#include <freeradius-devel/util/dlist.h>
28#include <freeradius-devel/util/pair.h>
29#include <freeradius-devel/util/value.h>
30#include <freeradius-devel/util/time.h>
31#include <freeradius-devel/util/rb.h>
32#include <freeradius-devel/util/timer.h>
34#include <freeradius-devel/server/module_rlm.h>
35#include <freeradius-devel/server/rcode.h>
36#include <freeradius-devel/server/signal.h>
37#include <freeradius-devel/server/pair.h>
38#include <freeradius-devel/server/log.h>
40#include <freeradius-devel/unlang/call_env.h>
41#include <freeradius-devel/unlang/action.h>
42#include <freeradius-devel/unlang/interpret.h>
43#include <freeradius-devel/unlang/module.h>
45#include <freeradius-devel/tls/strerror.h>
46#include <freeradius-devel/tls/utils.h>
48#include <openssl/x509.h>
49#include <openssl/x509v3.h>
50#include <openssl/pem.h>
51#include <openssl/asn1.h>
52#include <openssl/bn.h>
135 fr_value_box_list_head_t *
cdp;
162 .pair.dflt =
"session-state.TLS-Certificate.Serial", .pair.dflt_quote =
T_BARE_WORD },
164 .pair.dflt =
"session-state.TLS-Certificate.X509v3-CRL-Distribution-Points[*]", .pair.dflt_quote =
T_BARE_WORD },
169static int8_t crl_cmp(
void const *a,
void const *b)
177static void crl_free(
void *
data)
191 while ((vb = fr_value_box_list_next(&crl->
delta_urls, vb))) {
192 find.
cdp_url = vb->vb_strvalue;
194 if (!delta)
continue;
195 DEBUG2(
"Expiring delta CRL from %s due to expired base CRL", delta->
cdp_url);
210 pthread_mutex_unlock(&
inst->mutable->mutex);
220 X509_REVOKED *revoked;
221 ASN1_INTEGER *asn1_serial = NULL;
224 asn1_serial = d2i_ASN1_INTEGER(NULL, (
unsigned char const **)&serial, talloc_array_length(serial));
225 ret = X509_CRL_get0_by_serial(crl_entry->
crl, &revoked, asn1_serial);
226 ASN1_INTEGER_free(asn1_serial);
230 RDEBUG3(
"Certificate not in CRL");
261 while ((vb = fr_value_box_list_next(&(*found)->delta_urls, vb))) {
262 find.
cdp_url = vb->vb_strvalue;
265 ret = crl_check_entry(delta, request, serial);
279 return crl_check_entry(*found, request, serial);
284 X509_CRL_free(crl_entry->
crl);
302 STACK_OF(DIST_POINT) *dps;
303 X509_STORE_CTX *verify_ctx = NULL;
309 crl->
crl = d2i_X509_CRL(NULL, (
const unsigned char **)&our_data, talloc_array_length(our_data));
310 if (crl->
crl == NULL) {
311 fr_tls_strerror_printf(
"Failed to parse CRL from %s", url);
314 if (verify_ctx) X509_STORE_CTX_free(verify_ctx);
317 talloc_set_destructor(crl, _crl_entry_free);
319 verify_ctx = X509_STORE_CTX_new();
320 if (!verify_ctx || !X509_STORE_CTX_init(verify_ctx,
inst->verify_store, NULL, NULL)) {
321 fr_tls_strerror_printf(
"Error initialising X509 store");
325 xobj = X509_STORE_CTX_get_obj_by_subject(verify_ctx, X509_LU_X509,
326 X509_CRL_get_issuer(crl->
crl));
328 fr_tls_strerror_printf(
"CRL issuer certificate not in trusted store");
331 pkey = X509_get_pubkey(X509_OBJECT_get0_X509(xobj));
332 X509_OBJECT_free(xobj);
334 fr_tls_strerror_printf(
"Error getting CRL issuer public key");
337 i = X509_CRL_verify(crl->
crl, pkey);
341 fr_tls_strerror_printf(
"Could not verify CRL signature");
345 fr_tls_strerror_printf(
"CRL certificate signature failed");
349 crl->
crl_num = X509_CRL_get_ext_d2i(crl->
crl, NID_crl_number, &i, NULL);
356 ASN1_INTEGER *base_num = X509_CRL_get_ext_d2i(crl->
crl, NID_delta_crl, &i, NULL);
358 fr_tls_strerror_printf(
"Delta CRL missing Delta CRL Indicator extension");
361 if (ASN1_INTEGER_cmp(base_num, base_crl->
crl_num) != 0) {
362 fr_tls_strerror_printf(
"Delta CRL referrs to incorrect base CRL number");
363 ASN1_INTEGER_free(base_num);
366 ASN1_INTEGER_free(base_num);
368 fr_tls_strerror_printf(
"Delta CRL number is less than base CRL number");
374 fr_tls_strerror_printf(
"Failed to parse nextUpdate from CRL");
379 ERROR(
"Failed to insert CRL into tree of CRLs");
388 if (!base_crl && (dps = X509_CRL_get_ext_d2i(crl->
crl, NID_freshest_crl, NULL, NULL))) {
390 STACK_OF(GENERAL_NAME) *
names;
395 for (i = 0; i < sk_DIST_POINT_num(dps); i++) {
396 dp = sk_DIST_POINT_value(dps, i);
397 names = dp->distpoint->name.fullname;
398 for (j = 0; j < sk_GENERAL_NAME_num(
names); j++) {
400 if (
name->type != GEN_URI)
continue;
403 (
char const *)ASN1_STRING_get0_data(
name->d.uniformResourceIdentifier),
404 ASN1_STRING_length(
name->d.uniformResourceIdentifier),
true);
405 DEBUG3(
"CRL references delta URI %pV", vb);
406 fr_value_box_list_insert_tail(&crl->
delta_urls, vb);
409 CRL_DIST_POINTS_free(dps);
413 if (base_crl &&
inst->force_delta_expiry_is_set) {
416 if (
inst->force_expiry_is_set &&
421 if (
fr_timer_in(crl, tl, &crl->
ev, expiry_time,
false, crl_expire, crl) <0) {
422 ERROR(
"Failed to set timer to expire CRL");
425 X509_STORE_CTX_free(verify_ctx);
450 if (strncmp(rctx->
cdp_url->vb_strvalue,
"http", 4) == 0) {
452 }
else if (strncmp(rctx->
cdp_url->vb_strvalue,
"ldap", 4) == 0) {
454 RWARN(
"CRL URI %pV requires LDAP, but the crl module ldap expansion is not configured", rctx->
cdp_url);
459 RERROR(
"Unsupported URI scheme in CRL URI %pV", rctx->
cdp_url);
464 NULL, crl_process_cdp_data, crl_signal, 0, rctx) < 0)
return -1;
481 switch (fr_value_box_list_num_elements(&rctx->
crl_data)) {
492 switch (crl_tmpl_yield(request, env, rctx)) {
502 pthread_mutex_unlock(&
inst->mutable->mutex);
503 fr_value_box_list_talloc_free(&rctx->
crl_data);
514 crl_data->vb_octets, rctx->
base_crl);
517 RPERROR(
"Failed to process returned CRL data");
527 if (fr_value_box_list_num_elements(&crl_entry->
delta_urls) > 0) {
532 while ((vb = fr_value_box_list_next(&crl_entry->
delta_urls, vb))) {
533 find.
cdp_url = vb->vb_strvalue;
536 ret = crl_check_entry(delta, request, env->
serial.vb_octets);
544 fr_value_box_list_insert_tail(&rctx->
missing_crls, delta_uri);
563 pthread_mutex_unlock(&
inst->mutable->mutex);
573 RDEBUG3(
"Certificate not in delta CRL, checking base CRL");
575 ret = crl_check_entry(rctx->
base_crl, request, env->
serial.vb_octets);
581 pthread_mutex_unlock(&
inst->mutable->mutex);
601 REDEBUG(
"Too many CRL values returned, failing");
619 pthread_mutex_lock(&
inst->mutable->mutex);
626 while ((rctx->
cdp_url = fr_value_box_list_pop_head(env->
cdp))) {
627 switch (crl_check_serial(
inst->mutable->crls, request, rctx->
cdp_url->vb_strvalue,
628 env->
serial.vb_octets, &found)) {
657 while ((vb = fr_value_box_list_next(&found->
delta_urls, vb))) {
659 fr_value_box_list_insert_tail(&rctx->
missing_crls, delta_uri);
667 pthread_mutex_unlock(&
inst->mutable->mutex);
678 fr_value_box_list_init(&rctx->
crl_data);
683 switch (crl_tmpl_yield(request, env, rctx)) {
693 pthread_mutex_unlock(&
inst->mutable->mutex);
700 pthread_mutex_destroy(&mutable->mutex);
715 pthread_mutex_init(&
inst->mutable->mutex, NULL);
716 talloc_set_destructor(
inst->mutable, mod_mutable_free);
718 if (!
inst->ca_file && !
inst->ca_path) {
719 cf_log_err(mctx->
mi->
conf,
"Missing ca_file / ca_path option. One or other (or both) must be specified.");
723 inst->verify_store = X509_STORE_new();
724 if (!X509_STORE_load_locations(
inst->verify_store,
inst->ca_file,
inst->ca_path)) {
725 cf_log_err(mctx->
mi->
conf,
"Failed reading Trusted root CA file \"%s\" and path \"%s\"",
730 X509_STORE_set_purpose(
inst->verify_store, X509_PURPOSE_SSL_CLIENT);
748 if (
inst->verify_store) X509_STORE_free(
inst->verify_store);
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
#define CALL_ENV_TERMINATOR
#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...
call_env_parser_t const * env
Parsing rules for call method env.
#define FR_CALL_ENV_SUBSECTION(_name, _name2, _flags, _subcs)
Specify a call_env_parser_t which defines a nested subsection.
@ CALL_ENV_FLAG_SUBSECTION
This is a subsection.
@ CALL_ENV_FLAG_SINGLE
If the tmpl produces more than one box this is an error.
@ CALL_ENV_FLAG_ATTRIBUTE
Tmpl MUST contain an attribute reference.
@ CALL_ENV_FLAG_MULTI
Multiple instances of the conf pairs are allowed.
@ CALL_ENV_FLAG_REQUIRED
Associated conf pair or section is required.
@ CALL_ENV_FLAG_PARSE_MISSING
If this subsection is missing, still parse it.
@ CALL_ENV_FLAG_BARE_WORD_ATTRIBUTE
bare words are treated as an attribute, but strings may be xlats.
#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.
#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.
#define CONF_PARSER_TERMINATOR
#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
#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,...
Defines a CONF_PAIR to C data type mapping.
A section grouping multiple CONF_PAIR.
#define cf_log_err(_cf, _fmt,...)
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Specifies an attribute which must be present for the module to function.
Specifies a dictionary which must be loaded/loadable for the module to function.
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_OCTETS
Raw octets.
void * env_data
Per call environment data.
module_instance_t const * mi
Instance of the module being instantiated.
void * rctx
Resume ctx that a module previously set.
module_instance_t * mi
Instance of the module being instantiated.
Temporary structure to hold arguments for module calls.
Temporary structure to hold arguments for detach calls.
Temporary structure to hold arguments for instantiation calls.
module_t common
Common fields presented by all modules.
static const conf_parser_t config[]
#define pair_update_request(_attr, _da)
void * fr_rb_remove(fr_rb_tree_t *tree, void const *data)
Remove an entry from the tree, without freeing the data.
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.
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
#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.
The main red black tree structure.
#define RETURN_MODULE_REJECT
#define RETURN_MODULE_RCODE(_rcode)
#define RETURN_MODULE_FAIL
rlm_rcode_t
Return codes indicating the result of the module call.
@ RLM_MODULE_OK
The module is OK, continue.
@ RLM_MODULE_REJECT
Immediately reject the request.
@ RLM_MODULE_NOTFOUND
User not found.
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
rlm_crl_mutable_t * mutable
Mutable data that's shared between all threads.
fr_value_box_list_t crl_data
Data from CRL expansion.
tmpl_t * ldap_exp
The xlat expansion used to retrieve the CRL via ldap://.
fr_value_box_list_t delta_urls
URLs from which a delta CRL can be retrieved.
bool force_delta_expiry_is_set
char const * ca_file
File containing certs for verifying CRL signatures.
crl_check_status_t
A status used to track which CRL is being checked.
@ CRL_CHECK_BASE
The base CRL is being checked.
@ CRL_CHECK_DELTA
The delta CRL exists and is being checked.
@ CRL_CHECK_FETCH_DELTA
The delta CRL is being fetched.
fr_rb_tree_t * crls
A tree of CRLs organised by CDP URL.
fr_value_box_t serial
The serial to check.
@ CRL_MISSING_DELTA
Need to load a delta CRL to supplement this CRL.
@ CRL_ENTRY_FOUND
Serial was found in this CRL.
@ CRL_ENTRY_REMOVED
Serial was "un-revoked" in this delta CRL.
@ CRL_ERROR
Unspecified error ocurred.
@ CRL_NOT_FOUND
No CRL found, need to load it from the CDP URL.
@ CRL_ENTRY_NOT_FOUND
Serial not found in this CRL.
fr_value_box_t * cdp_url
The URL we're currently attempting to load.
fr_dict_attr_autoload_t rlm_crl_dict_attr[]
static fr_dict_t const * dict_freeradius
rlm_crl_t const * inst
The instance of the CRL module.
fr_rb_node_t node
The node in the tree.
char const * cdp_url
The URL of the CRL.
X509_STORE * verify_store
Store of certificates to verify CRL signatures.
fr_time_delta_t force_delta_expiry
Force expiry of delta CRLs after this time.
static int mod_detach(UNUSED module_detach_ctx_t const *mctx)
fr_value_box_list_t missing_crls
CRLs missing from the tree.
fr_timer_list_t * timer_list
The timer list to use for CRL expiry.
static fr_dict_attr_t const * attr_crl_cdp_url
crl_entry_t * base_crl
The base CRL relating to the delta currently being fetched.
fr_time_delta_t force_expiry
Force expiry of CRLs after this time.
fr_dict_autoload_t rlm_crl_dict[]
fr_timer_t * ev
When to expire the CRL.
char const * ca_path
Directory containing certs for verifying CRL signatures.
fr_value_box_list_head_t * cdp
The CRL distribution points.
tmpl_t * http_exp
The xlat expansion used to retrieve the CRL via http://.
static int mod_instantiate(module_inst_ctx_t const *mctx)
Instantiate the module.
static fr_dict_attr_t const * attr_crl_data
CONF_SECTION * virtual_server
Virtual server to use when retrieving CRLs.
static conf_parser_t module_config[]
crl_check_status_t status
Status of the current CRL check.
ASN1_INTEGER * crl_num
The CRL number.
fr_time_delta_t early_refresh
Time interval before nextUpdate to refresh.
A single CRL in the global list of CRLs.
static int instantiate(module_inst_ctx_t const *mctx)
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
CONF_SECTION * conf
Module's instance configuration.
size_t inst_size
Size of the module's instance data.
void * data
Module's instance data.
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Named methods exported by a module.
#define pair_delete_request(_pair_or_da)
Delete a fr_pair_t in the request list.
fr_signal_t
Signals that can be generated/processed by request signal handlers.
@ FR_SIGNAL_CANCEL
Request has been cancelled.
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.
eap_aka_sim_process_conf_t * inst
#define fr_time()
Allow us to arbitrarily manipulate time.
Stores an attribute, a value and various bits of other data.
char * talloc_bstrdup(TALLOC_CTX *ctx, char const *in)
Binary safe strdup function.
#define talloc_get_type_abort_const
static const char * names[8]
static int8_t fr_time_delta_cmp(fr_time_delta_t a, fr_time_delta_t b)
Compare two fr_time_delta_t values.
static fr_time_t fr_time_from_sec(time_t when)
Convert a time_t (wallclock time) to a fr_time_t (internal time)
static fr_time_delta_t fr_time_delta_sub(fr_time_delta_t a, fr_time_delta_t b)
#define fr_time_sub(_a, _b)
Subtract one time from another.
A time delta, a difference in time measured in nanoseconds.
int fr_tls_utils_asn1time_to_epoch(time_t *out, ASN1_TIME const *asn1)
Convert OpenSSL's ASN1_TIME to an epoch time.
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.
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.
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.
#define fr_box_time_delta(_val)
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.