27RCSID(
"$Id: 48664b9b936365c4aa180a87acb279142a05f64e $")
29#include <freeradius-devel/server/base.h>
30#include <freeradius-devel/server/module_rlm.h>
31#include <freeradius-devel/server/tmpl_dcursor.h>
32#include <freeradius-devel/unlang/xlat_func.h>
33#include <freeradius-devel/util/base16.h>
34#include <freeradius-devel/util/rb.h>
36#include <openssl/ssl.h>
37#include <openssl/evp.h>
38#include <openssl/hmac.h>
81#define PTHREAD_MUTEX_LOCK pthread_mutex_lock
82#define PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock
84#define PTHREAD_MUTEX_LOCK(_x)
85#define PTHREAD_MUTEX_UNLOCK(_x)
178 .inst_type =
"dpsk_autz_call_env_t",
192 .inst_type =
"dpsk_auth_call_env_t",
204 dpsk_auth_call_env_t, key_msg, key_msg_tmpl), .pair.dflt =
"FreeRADIUS-EV5.802_1X-EAPoL-Key-Msg",
246 if (!
inst->auth_type) {
247 WARN(
"No 'authenticate %s {...}' section or 'Auth-Type = %s' set. Cannot setup DPSK authentication.",
262 memcpy(my_entry.
mac, mac,
sizeof(my_entry.
mac));
263 memcpy(&my_entry.
ssid, &ssid->vb_octets,
sizeof(my_entry.
ssid));
264 my_entry.
ssid_len = ssid->vb_length;
274 RDEBUG3(
"Cache entry has expired");
286 if (PKCS5_PBKDF2_HMAC_SHA1((
const char *) psk, psk_len, (
const unsigned char *) ssid->vb_strvalue,
287 ssid->vb_length, 4096, buflen,
buffer) == 0) {
288 RERROR(
"Failed calling OpenSSL to calculate the PMK");
307 unsigned int digest_len, mic_len;
312 char const *psk_identity = NULL, *psk = NULL;
314 uint8_t const *snonce, *ap_mac;
315 uint8_t const *min_mac, *max_mac;
316 uint8_t const *min_nonce, *max_nonce;
318 uint8_t s_mac[6], message[
sizeof(
"Pairwise key expansion") + 6 + 6 + 32 + 32 + 1], frame[128];
319 uint8_t digest[EVP_MAX_MD_SIZE], mic[EVP_MAX_MD_SIZE];
320 char token_identity[256];
326 if (env->
anonce.vb_length != 32) {
331 if (env->
key_msg.vb_length <
sizeof(*eapol)) {
336 if (env->
key_msg.vb_length >
sizeof(frame)) {
361 RERROR(
"User-Name is not a recognizable hex MAC address");
366 RERROR(
"Called-Station-MAC is not a recognizable MAC address");
375 if (memcmp(s_mac, ap_mac, 6) <= 0) {
390 snonce = env->
key_msg.vb_octets + 17;
391 if (memcmp(snonce, env->
anonce.vb_octets, 32) <= 0) {
393 max_nonce = env->
anonce.vb_octets;
395 min_nonce = env->
anonce.vb_octets;
402 memcpy(message,
"Pairwise key expansion",
sizeof(
"Pairwise key expansion"));
403 p = &message[
sizeof(
"Pairwise key expansion")];
405 memcpy(p, min_mac, 6);
406 memcpy(p + 6, max_mac, 6);
409 memcpy(p, min_nonce, 32);
410 memcpy(p + 32, max_nonce, 32);
413 fr_assert(
sizeof(message) == (p + 1 - message));
421 if (
inst->cache_size) {
422 entry = dpsk_cache_find(request,
inst, pmk,
sizeof(pmk), &env->
ssid, s_mac);
439 if (env->
masterkey.vb_length !=
sizeof(pmk)) {
444 memcpy(pmk, env->
masterkey.vb_octets,
sizeof(pmk));
454 if (generate_pmk(request, pmk,
sizeof(pmk), &env->
ssid, env->
psk.vb_strvalue, env->
psk.vb_length) < 0) {
462 psk_identity = env->
username.vb_strvalue;
465 psk = env->
psk.vb_strvalue;
466 psk_len = env->
psk.vb_length;
479 RERROR(
"No %s was found, and no 'filename' was configured", env->
psk_tmpl->name);
497 RDEBUG3(
"Looking for PSK in file %s", filename);
499 fp = fopen(filename,
"r");
505 fr_sbuff_init_file(&sbuff, &fctx,
buffer,
sizeof(
buffer), fp, SIZE_MAX);
512 sizeof(token_identity), quoted ? "ed_terms : &terms, NULL);
514 RDEBUG(
"Failed to find matching PSK or MAC in %s", filename);
525 RDEBUG(
"%s[%d] Failed to find ',' after identity", filename, lineno);
532 sizeof(token_identity), quoted ? "ed_terms : &terms, NULL);
534 RDEBUG(
"%s[%d] Failed parsing PSK", filename, lineno);
552 sizeof(token_identity), quoted ? "ed_terms : &terms, NULL);
554 RERROR(
"%s[%d] Failed parsing MAC", filename, lineno);
565 RERROR(
"%s[%d] Failed parsing MAC", filename, lineno);
573 if (memcmp(s_mac, token_mac, 6) != 0) {
585 psk_len = strlen(token_psk);
586 psk_identity = token_identity;
588 RDEBUG3(
"%s[%d] Trying PSK %s", filename, lineno, token_psk);
589 if (generate_pmk(request, pmk,
sizeof(pmk), &env->
ssid, psk, psk_len) < 0) {
600 digest_len =
sizeof(digest);
601 HMAC(EVP_sha1(), pmk,
sizeof(pmk), message,
sizeof(message), digest, &digest_len);
603 RHEXDUMP3(message,
sizeof(message),
"message:");
612 memset(&zeroed->
frame.
mic[0], 0, 16);
616 mic_len =
sizeof(mic);
617 HMAC(EVP_sha1(), digest, 16, frame, env->
key_msg.vb_length, mic, &mic_len);
622 if (memcmp(&eapol->
frame.
mic[0], mic, 16) != 0) {
650 RDEBUG(
"Checking file %s for PSK and MAC", filename);
654 RWARN(
"No 'filename' was configured.");
664 if (stage == 2)
goto stage2a;
674 RWARN(
"Found matching MAC in %s, but the PSK does not match", filename);
688 fr_assert(psk_identity == token_identity);
699 if (
inst->cache_size && psk && psk_identity) {
705 if (entry)
goto update_entry;
711 memcpy(my_entry.
mac, s_mac,
sizeof(my_entry.
mac));
712 memcpy(&my_entry.
ssid, env->
ssid.vb_octets,
sizeof(my_entry.
ssid));
731 memcpy(entry->
mac, s_mac,
sizeof(entry->
mac));
732 memcpy(entry->
pmk, pmk,
sizeof(entry->
pmk));
739 MEM(entry->
ssid = talloc_memdup(entry, env->
ssid.vb_octets, env->
ssid.vb_length));
742 MEM(entry->
psk = talloc_strdup(entry, psk));
746 MEM(entry->
identity = talloc_strdup(entry, psk_identity));
753 goto update_attributes;
837 if (PKCS5_PBKDF2_HMAC_SHA1(psk->vb_strvalue, psk->vb_length,
838 (
const unsigned char *) ssid->vb_strvalue, ssid->vb_length,
840 RERROR(
"Failed calling OpenSSL to calculate the PMK");
866static int8_t cmp_cache_entry(
void const *one,
void const *two)
872 rcode = memcmp(a->
mac, b->
mac,
sizeof(a->
mac));
873 if (rcode != 0)
return rcode;
881static void free_cache_entry(
void *
data)
896 if (!
inst->cache_size)
return 0;
899 pthread_mutex_destroy(&
inst->mutable->mutex);
914 if (!
inst->auth_type) {
915 WARN(
"Failed to find 'authenticate %s {...}' section. DPSK will likely not work",
924 if (!
inst->cache_size)
return 0;
937 if (pthread_mutex_init(&
inst->mutable->mutex, NULL) < 0) {
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
static int const char char buffer[256]
#define fr_base16_decode(_err, _out, _in, _no_trailing)
#define L(_str)
Helper for initialising arrays of string literals.
#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_PARSE_OFFSET(_name, _cast_type, _flags, _struct, _field, _parse_field)
Specify a call_env_parser_t which writes out runtime results and the result of the parsing phase to t...
size_t inst_size
Size of per call env.
@ CALL_ENV_FLAG_ATTRIBUTE
Tmpl MUST contain an attribute reference.
@ CALL_ENV_FLAG_REQUIRED
Associated conf pair or section is required.
@ 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_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
#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_TIME_DELTA_BOUND_CHECK(_name, _var, _op, _bound)
Defines a CONF_PAIR to C data type mapping.
#define cf_log_err(_cf, _fmt,...)
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
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.
fr_dict_enum_value_t const * fr_dict_enum_by_name(fr_dict_attr_t const *da, char const *name, ssize_t len)
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.
Value of an enumerated attribute.
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
static bool fr_dlist_entry_in_list(fr_dlist_t const *entry)
Check if a list entry is part of a list.
static void fr_dlist_entry_unlink(fr_dlist_t *entry)
Remove an item from the dlist when we don't have access to the head.
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Head of a doubly linked list.
Entry in a doubly linked list.
#define RHEXDUMP3(_data, _len, _fmt,...)
int map_to_vp(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, UNUSED void *uctx)
Convert a map to a fr_pair_t.
int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t func, void *ctx)
Convert map_t to fr_pair_t (s) and add them to a request_t.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_OCTETS
Raw octets.
size_t fr_sbuff_out_bstrncpy_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, fr_sbuff_term_t const *tt, fr_sbuff_unescape_rules_t const *u_rules)
void * env_data
Per call environment data.
module_instance_t const * mi
Instance of the module being instantiated.
module_instance_t * mi
Module instance to detach.
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.
bool module_rlm_section_type_set(request_t *request, fr_dict_attr_t const *type_da, fr_dict_enum_value_t const *enumv)
Set the next section type if it's not already set.
module_t common
Common fields presented by all modules.
static const conf_parser_t config[]
static int mod_load(void)
static void mod_unload(void)
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
Return how many nodes there are in a tree.
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.
bool fr_rb_delete(fr_rb_tree_t *tree, void const *data)
Remove node and free data (if a free function was specified)
#define fr_rb_inline_init(_tree, _type, _field, _data_cmp, _data_free)
Initialises a red black tree.
The main red black tree structure.
#define RETURN_UNLANG_UPDATED
#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_UPDATED
OK (pairs modified).
#define RETURN_UNLANG_NOOP
static int mod_detach(module_detach_ctx_t const *mctx)
static unlang_action_t mod_authorize(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
static unlang_action_t mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
tmpl_t * psk_identity_dest_tmpl
struct rlm_dpsk_s rlm_dpsk_t
static fr_dict_t const * dict_freeradius
#define PTHREAD_MUTEX_LOCK(_x)
static fr_dict_attr_t const * attr_auth_type
fr_dict_autoload_t rlm_dpsk_dict[]
fr_value_box_t psk_identity
fr_dict_attr_autoload_t rlm_dpsk_dict_attr[]
#define PTHREAD_MUTEX_UNLOCK(_x)
static const conf_parser_t module_config[]
rlm_dpsk_mutable_t * mutable
fr_dict_enum_value_t const * auth_type
fr_time_delta_t cache_lifetime
fr_value_box_t calledstation
static int mod_instantiate(module_inst_ctx_t const *mctx)
uint8_t replay_counter[8]
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_adv_past_whitespace(_sbuff, _len, _tt)
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
#define fr_sbuff_adv_past_blank(_sbuff, _len, _tt)
#define FR_SBUFF_OUT(_start, _len_or_end)
Set of terminal elements.
File sbuff extension structure.
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
char const * name
Instance name e.g. user_database.
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.
@ TMPL_TYPE_DATA
Value in native boxed format.
tmpl_t * tmpl_init_shallow(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len, tmpl_rules_t const *t_rules))
Initialise a tmpl without copying the input name string.
eap_aka_sim_process_conf_t * inst
#define fr_time()
Allow us to arbitrarily manipulate time.
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Stores an attribute, a value and various bits of other data.
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
#define talloc_get_type_abort_const
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
#define fr_time_add(_a, _b)
Add a time/time delta together.
#define fr_time_gt(_a, _b)
A time delta, a difference in time measured in nanoseconds.
void tmpl_dcursor_clear(tmpl_dcursor_ctx_t *cc)
Clear any temporary state allocations.
#define tmpl_dcursor_init(_err, _ctx, _cc, _cursor, _request, _vpt)
Maintains state between cursor calls.
uint8_t required
Argument must be present, and non-empty.
#define XLAT_ARGS(_list,...)
Populate local variables with value boxes from the input list.
#define XLAT_ARG_PARSER_TERMINATOR
@ XLAT_ACTION_FAIL
An xlat function failed.
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition for a single argument consumend by an xlat function.
void fr_value_box_bstrndup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Assign a string to to a fr_value_box_t.
int fr_value_box_memdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, uint8_t const *src, size_t len, bool tainted)
Copy a buffer to a fr_value_box_t.
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
static size_t char ** out
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
xlat_t * xlat_func_register(TALLOC_CTX *ctx, char const *name, xlat_func_t func, fr_type_t return_type)
Register an xlat function.
void xlat_func_unregister(char const *name)
Unregister an xlat function.