27RCSID(
"$Id: a162c8c6aa72ad8b173d5f304084668ef0903508 $")
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>
35#include <openssl/ssl.h>
36#include <openssl/evp.h>
37#include <openssl/hmac.h>
167 .inst_type =
"dpsk_autz_call_env_t",
181 .inst_type =
"dpsk_auth_call_env_t",
193 dpsk_auth_call_env_t, key_msg, key_msg_tmpl), .pair.dflt =
"FreeRADIUS-EV5.802_1X-EAPoL-Key-Msg",
235 if (!
inst->auth_type) {
236 WARN(
"No 'authenticate %s {...}' section or 'Auth-Type = %s' set. Cannot setup DPSK authentication.",
251 memcpy(my_entry.
mac, mac,
sizeof(my_entry.
mac));
252 memcpy(&my_entry.
ssid, &ssid->vb_octets,
sizeof(my_entry.
ssid));
253 my_entry.
ssid_len = ssid->vb_length;
263 RDEBUG3(
"Cache entry has expired");
275 if (PKCS5_PBKDF2_HMAC_SHA1((
const char *) psk, psk_len, (
const unsigned char *) ssid->vb_strvalue,
276 ssid->vb_length, 4096, buflen,
buffer) == 0) {
277 RERROR(
"Failed calling OpenSSL to calculate the PMK");
296 unsigned int digest_len, mic_len;
301 char const *psk_identity = NULL, *psk = NULL;
303 uint8_t const *snonce, *ap_mac;
304 uint8_t const *min_mac, *max_mac;
305 uint8_t const *min_nonce, *max_nonce;
307 uint8_t s_mac[6], message[
sizeof(
"Pairwise key expansion") + 6 + 6 + 32 + 32 + 1], frame[128];
308 uint8_t digest[EVP_MAX_MD_SIZE], mic[EVP_MAX_MD_SIZE];
309 char token_identity[256];
315 if (env->
anonce.vb_length != 32) {
320 if (env->
key_msg.vb_length <
sizeof(*eapol)) {
325 if (env->
key_msg.vb_length >
sizeof(frame)) {
356 RERROR(
"User-Name is not a recognizable hex MAC address");
361 RERROR(
"Called-Station-MAC is not a recognizable MAC address");
370 if (memcmp(s_mac, ap_mac, 6) <= 0) {
385 snonce = env->
key_msg.vb_octets + 17;
386 if (memcmp(snonce, env->
anonce.vb_octets, 32) <= 0) {
388 max_nonce = env->
anonce.vb_octets;
390 min_nonce = env->
anonce.vb_octets;
397 memcpy(message,
"Pairwise key expansion",
sizeof(
"Pairwise key expansion"));
398 p = &message[
sizeof(
"Pairwise key expansion")];
400 memcpy(p, min_mac, 6);
401 memcpy(p + 6, max_mac, 6);
404 memcpy(p, min_nonce, 32);
405 memcpy(p + 32, max_nonce, 32);
408 fr_assert(
sizeof(message) == (p + 1 - message));
416 if (
inst->cache_size) {
417 pthread_mutex_lock(&
inst->mutable->mutex);
418 entry = dpsk_cache_find(request,
inst, pmk,
sizeof(pmk), &env->
ssid, s_mac);
423 pthread_mutex_unlock(&
inst->mutable->mutex);
426 pthread_mutex_unlock(&
inst->mutable->mutex);
437 if (env->
masterkey.vb_length !=
sizeof(pmk)) {
442 memcpy(pmk, env->
masterkey.vb_octets,
sizeof(pmk));
452 if (generate_pmk(request, pmk,
sizeof(pmk), &env->
ssid, env->
psk.vb_strvalue, env->
psk.vb_length) < 0) {
460 psk_identity = env->
username.vb_strvalue;
463 psk = env->
psk.vb_strvalue;
464 psk_len = env->
psk.vb_length;
477 RERROR(
"No %s was found, and no 'filename' was configured", env->
psk_tmpl->name);
495 RDEBUG3(
"Looking for PSK in file %s", filename);
497 fp = fopen(filename,
"r");
503 fr_sbuff_init_file(&sbuff, &fctx,
buffer,
sizeof(
buffer), fp, SIZE_MAX);
510 sizeof(token_identity), quoted ? "ed_terms : &terms, NULL);
512 RDEBUG(
"Failed to find matching PSK or MAC in %s", filename);
523 RDEBUG(
"%s[%d] Failed to find ',' after identity", filename, lineno);
530 sizeof(token_identity), quoted ? "ed_terms : &terms, NULL);
532 RDEBUG(
"%s[%d] Failed parsing PSK", filename, lineno);
550 sizeof(token_identity), quoted ? "ed_terms : &terms, NULL);
552 RERROR(
"%s[%d] Failed parsing MAC", filename, lineno);
563 RERROR(
"%s[%d] Failed parsing MAC", filename, lineno);
571 if (memcmp(s_mac, token_mac, 6) != 0) {
583 psk_len = strlen(token_psk);
584 psk_identity = token_identity;
586 RDEBUG3(
"%s[%d] Trying PSK %s", filename, lineno, token_psk);
587 if (generate_pmk(request, pmk,
sizeof(pmk), &env->
ssid, psk, psk_len) < 0) {
598 digest_len =
sizeof(digest);
603 memset(digest, 0, digest_len);
605 HMAC(EVP_sha1(), pmk,
sizeof(pmk), message,
sizeof(message), digest, &digest_len);
607 RHEXDUMP3(message,
sizeof(message),
"message:");
616 memset(&zeroed->
frame.
mic[0], 0, 16);
620 mic_len =
sizeof(mic);
625 memset(mic, 0, mic_len);
627 HMAC(EVP_sha1(), digest, 16, frame, env->
key_msg.vb_length, mic, &mic_len);
632 if (memcmp(&eapol->
frame.
mic[0], mic, 16) != 0) {
660 RDEBUG(
"Checking file %s for PSK and MAC", filename);
664 RWARN(
"No 'filename' was configured.");
674 if (stage == 2)
goto stage2a;
684 RWARN(
"Found matching MAC in %s, but the PSK does not match", filename);
698 fr_assert(psk_identity == token_identity);
709 if (
inst->cache_size && psk && psk_identity) {
715 if (entry)
goto update_entry;
721 memcpy(my_entry.
mac, s_mac,
sizeof(my_entry.
mac));
722 memcpy(&my_entry.
ssid, env->
ssid.vb_octets,
sizeof(my_entry.
ssid));
732 pthread_mutex_lock(&
inst->mutable->mutex);
734 pthread_mutex_unlock(&
inst->mutable->mutex);
741 memcpy(entry->
mac, s_mac,
sizeof(entry->
mac));
742 memcpy(entry->
pmk, pmk,
sizeof(entry->
pmk));
749 MEM(entry->
ssid = talloc_memdup(entry, env->
ssid.vb_octets, env->
ssid.vb_length));
763 goto update_attributes;
770 pthread_mutex_lock(&
inst->mutable->mutex);
773 pthread_mutex_unlock(&
inst->mutable->mutex);
847 if (PKCS5_PBKDF2_HMAC_SHA1(psk->vb_strvalue, psk->vb_length,
848 (
const unsigned char *) ssid->vb_strvalue, ssid->vb_length,
850 RERROR(
"Failed calling OpenSSL to calculate the PMK");
882 rcode = memcmp(a->
mac, b->
mac,
sizeof(a->
mac));
883 if (rcode != 0)
return rcode;
906 if (!
inst->cache_size)
return 0;
908 pthread_mutex_destroy(&
inst->mutable->mutex);
922 if (!
inst->auth_type) {
923 WARN(
"Failed to find 'authenticate %s {...}' section. DPSK will likely not work",
932 if (!
inst->cache_size)
return 0;
945 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.
#define DICT_AUTOLOAD_TERMINATOR
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 void cache_entry_free(rlm_cache_entry_t *c)
Locate a cache entry in memcached.
static int8_t cache_entry_cmp(void const *one, void const *two)
Compare two entries by key.
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
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[]
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
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
#define talloc_strdup(_ctx, _str)
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.
static char const * fail_file
#define XLAT_ARGS(_list,...)
Populate local variables with value boxes from the input list.
unsigned int required
Argument must be present, and non-empty.
#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 consumed 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.