25RCSID(
"$Id: 328d3086c99d9354bcb84d8edbe47c00abc468c5 $")
27#include <freeradius-devel/server/base.h>
28#include <freeradius-devel/server/module_rlm.h>
29#include <freeradius-devel/unlang/call_env.h>
31#include <freeradius-devel/tls/strerror.h>
32#include <freeradius-devel/tls/utils.h>
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>
122 fr_value_box_list_head_t *
cdp;
150 .pair.dflt =
"session-state.TLS-Certificate.Serial", .pair.dflt_quote =
T_BARE_WORD },
152 .pair.dflt =
"session-state.TLS-Certificate.X509v3-CRL-Distribution-Points[*]", .pair.dflt_quote =
T_BARE_WORD },
157static int8_t crl_cmp(
void const *a,
void const *b)
165static void crl_free(
void *
data)
188 pthread_mutex_unlock(&
inst->mutable->mutex);
198 X509_REVOKED *revoked;
199 ASN1_INTEGER *asn1_serial = NULL;
202 asn1_serial = d2i_ASN1_INTEGER(NULL, (
unsigned char const **)&serial, talloc_array_length(serial));
203 ret = X509_CRL_get0_by_serial(crl_entry->
crl, &revoked, asn1_serial);
204 ASN1_INTEGER_free(asn1_serial);
208 RDEBUG3(
"Certificate not in CRL");
239 while ((vb = fr_value_box_list_next(&(*found)->delta_urls, vb))) {
240 find.
cdp_url = vb->vb_strvalue;
243 ret = crl_check_entry(delta, request, serial);
257 return crl_check_entry(*found, request, serial);
262 X509_CRL_free(crl_entry->
crl);
280 STACK_OF(DIST_POINT) *dps;
281 X509_STORE_CTX *verify_ctx = NULL;
287 crl->
crl = d2i_X509_CRL(NULL, (
const unsigned char **)&our_data, talloc_array_length(our_data));
288 if (crl->
crl == NULL) {
289 fr_tls_strerror_printf(
"Failed to parse CRL from %s", url);
292 if (verify_ctx) X509_STORE_CTX_free(verify_ctx);
295 talloc_set_destructor(crl, _crl_entry_free);
297 verify_ctx = X509_STORE_CTX_new();
298 if (!verify_ctx || !X509_STORE_CTX_init(verify_ctx,
inst->verify_store, NULL, NULL)) {
299 fr_tls_strerror_printf(
"Error initialising X509 store");
303 xobj = X509_STORE_CTX_get_obj_by_subject(verify_ctx, X509_LU_X509,
304 X509_CRL_get_issuer(crl->
crl));
306 fr_tls_strerror_printf(
"CRL issuer certificate not in trusted store");
309 pkey = X509_get_pubkey(X509_OBJECT_get0_X509(xobj));
310 X509_OBJECT_free(xobj);
312 fr_tls_strerror_printf(
"Error getting CRL issuer public key");
315 i = X509_CRL_verify(crl->
crl, pkey);
319 fr_tls_strerror_printf(
"Could not verify CRL signature");
323 fr_tls_strerror_printf(
"CRL certificate signature failed");
327 crl->
crl_num = X509_CRL_get_ext_d2i(crl->
crl, NID_crl_number, &i, NULL);
334 ASN1_INTEGER *base_num = X509_CRL_get_ext_d2i(crl->
crl, NID_delta_crl, &i, NULL);
336 fr_tls_strerror_printf(
"Delta CRL missing Delta CRL Indicator extension");
339 if (ASN1_INTEGER_cmp(base_num, base_crl->
crl_num) > 0) {
340 uint64_t delta_base, crl_num;
341 ASN1_INTEGER_get_uint64(&delta_base, base_num);
342 ASN1_INTEGER_get_uint64(&crl_num, base_crl->
crl_num);
343 fr_tls_strerror_printf(
"Delta CRL referrs to base CRL number %"PRIu64
", current base is %"PRIu64,
344 delta_base, crl_num);
345 ASN1_INTEGER_free(base_num);
348 ASN1_INTEGER_free(base_num);
350 uint64_t delta_num, crl_num;
351 ASN1_INTEGER_get_uint64(&delta_num, crl->
crl_num);
352 ASN1_INTEGER_get_uint64(&crl_num, base_crl->
crl_num);
353 fr_tls_strerror_printf(
"Delta CRL number %"PRIu64
" is less than base CRL number %"PRIu64,
360 fr_tls_strerror_printf(
"Failed to parse nextUpdate from CRL");
365 ERROR(
"Failed to insert CRL into tree of CRLs");
374 if (!base_crl && (dps = X509_CRL_get_ext_d2i(crl->
crl, NID_freshest_crl, NULL, NULL))) {
376 STACK_OF(GENERAL_NAME) *
names;
381 for (i = 0; i < sk_DIST_POINT_num(dps); i++) {
382 dp = sk_DIST_POINT_value(dps, i);
383 names = dp->distpoint->name.fullname;
384 for (j = 0; j < sk_GENERAL_NAME_num(
names); j++) {
386 if (
name->type != GEN_URI)
continue;
389 (
char const *)ASN1_STRING_get0_data(
name->d.uniformResourceIdentifier),
390 ASN1_STRING_length(
name->d.uniformResourceIdentifier),
true);
391 DEBUG3(
"CRL references delta URI %pV", vb);
392 fr_value_box_list_insert_tail(&crl->
delta_urls, vb);
395 CRL_DIST_POINTS_free(dps);
399 if (base_crl &&
inst->force_delta_expiry_is_set) {
402 if (
inst->force_expiry_is_set &&
407 if (
fr_timer_in(crl, tl, &crl->
ev, expiry_time,
false, crl_expire, crl) <0) {
408 ERROR(
"Failed to set timer to expire CRL");
411 X509_STORE_CTX_free(verify_ctx);
437 if (strncmp(rctx->
cdp_url->vb_strvalue,
"http", 4) == 0) {
439 }
else if (strncmp(rctx->
cdp_url->vb_strvalue,
"ldap", 4) == 0) {
441 RWARN(
"CRL URI %pV requires LDAP, but the crl module ldap expansion is not configured", rctx->
cdp_url);
445 }
else if (strncmp(rctx->
cdp_url->vb_strvalue,
"ftp", 3) == 0) {
447 RWARN(
"CRL URI %pV requires FTP, but the crl module ftp expansion is not configured", rctx->
cdp_url);
452 RERROR(
"Unsupported URI scheme in CRL URI %pV", rctx->
cdp_url);
457 NULL, crl_process_cdp_data, crl_signal, 0, rctx) < 0)
return -1;
475 switch (fr_value_box_list_num_elements(&rctx->
crl_data)) {
486 switch (crl_tmpl_yield(request, env, rctx)) {
496 pthread_mutex_unlock(&
inst->mutable->mutex);
497 fr_value_box_list_talloc_free(&rctx->
crl_data);
508 crl_data->vb_octets, rctx->
base_crl);
511 RPERROR(
"Failed to process returned CRL data");
521 if (fr_value_box_list_num_elements(&crl_entry->
delta_urls) > 0) {
526 while ((vb = fr_value_box_list_next(&crl_entry->
delta_urls, vb))) {
527 find.
cdp_url = vb->vb_strvalue;
530 ret = crl_check_entry(delta, request, env->
serial.vb_octets);
538 fr_value_box_list_insert_tail(&rctx->
missing_crls, delta_uri);
557 pthread_mutex_unlock(&
inst->mutable->mutex);
567 RDEBUG3(
"Certificate not in delta CRL, checking base CRL");
569 ret = crl_check_entry(rctx->
base_crl, request, env->
serial.vb_octets);
575 pthread_mutex_unlock(&
inst->mutable->mutex);
595 REDEBUG(
"Too many CRL values returned, failing");
615 pthread_mutex_lock(&
inst->mutable->mutex);
622 while ((rctx->
cdp_url = fr_value_box_list_pop_head(env->
cdp))) {
623 switch (crl_check_serial(
inst->mutable->crls, request, rctx->
cdp_url->vb_strvalue,
624 env->
serial.vb_octets, &found)) {
653 while ((vb = fr_value_box_list_next(&found->
delta_urls, vb))) {
655 fr_value_box_list_insert_tail(&rctx->
missing_crls, delta_uri);
663 pthread_mutex_unlock(&
inst->mutable->mutex);
674 fr_value_box_list_init(&rctx->
crl_data);
679 switch (crl_tmpl_yield(request, env, rctx)) {
689 pthread_mutex_unlock(&
inst->mutable->mutex);
696 pthread_mutex_destroy(&mutable->mutex);
711 pthread_mutex_init(&
inst->mutable->mutex, NULL);
712 talloc_set_destructor(
inst->mutable, mod_mutable_free);
714 if (!
inst->ca_file && !
inst->ca_path) {
715 cf_log_err(mctx->
mi->
conf,
"Missing ca_file / ca_path option. One or other (or both) must be specified.");
719 inst->verify_store = X509_STORE_new();
720 if (!X509_STORE_load_locations(
inst->verify_store,
inst->ca_file,
inst->ca_path)) {
721 cf_log_err(mctx->
mi->
conf,
"Failed reading Trusted root CA file \"%s\" and path \"%s\"",
726 X509_STORE_set_purpose(
inst->verify_store, X509_PURPOSE_SSL_CLIENT);
744 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.
@ CALL_ENV_FLAG_NULLABLE
Tmpl expansions are allowed to produce no output.
#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.
#define REDEBUG2(fmt,...)
@ 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_UNLANG_RCODE(_rcode)
#define RETURN_UNLANG_FAIL
#define RETURN_UNLANG_REJECT
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.
#define RETURN_UNLANG_NOOP
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.
tmpl_t * ftp_exp
The xlat expansion used to retrieve the CRL via ftp://.
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.