27RCSID(
"$Id: 22915e17205d86fdb475b81c02c33c00dc84b73f $")
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>
169 .inst_type =
"dpsk_autz_call_env_t",
183 .inst_type =
"dpsk_auth_call_env_t",
195 dpsk_auth_call_env_t, key_msg, key_msg_tmpl), .pair.dflt =
"FreeRADIUS-EV5.802_1X-EAPoL-Key-Msg",
237 if (!
inst->auth_type) {
238 WARN(
"No 'authenticate %s {...}' section or 'Auth-Type = %s' set. Cannot setup DPSK authentication.",
253 memcpy(my_entry.
mac, mac,
sizeof(my_entry.
mac));
254 memcpy(&my_entry.
ssid, &ssid->vb_octets,
sizeof(my_entry.
ssid));
255 my_entry.
ssid_len = ssid->vb_length;
265 RDEBUG3(
"Cache entry has expired");
277 if (PKCS5_PBKDF2_HMAC_SHA1((
const char *) psk, psk_len, (
const unsigned char *) ssid->vb_strvalue,
278 ssid->vb_length, 4096, buflen,
buffer) == 0) {
279 RERROR(
"Failed calling OpenSSL to calculate the PMK");
298 unsigned int digest_len, mic_len;
303 char const *psk_identity = NULL, *psk = NULL;
305 uint8_t const *snonce, *ap_mac;
306 uint8_t const *min_mac, *max_mac;
307 uint8_t const *min_nonce, *max_nonce;
309 uint8_t s_mac[6], message[
sizeof(
"Pairwise key expansion") + 6 + 6 + 32 + 32 + 1], frame[128];
310 uint8_t digest[EVP_MAX_MD_SIZE], mic[EVP_MAX_MD_SIZE];
311 char token_identity[256];
317 if (env->
anonce.vb_length != 32) {
322 if (env->
key_msg.vb_length <
sizeof(*eapol)) {
327 if (env->
key_msg.vb_length >
sizeof(frame)) {
358 RERROR(
"User-Name is not a recognizable hex MAC address");
363 RERROR(
"Called-Station-MAC is not a recognizable MAC address");
372 if (memcmp(s_mac, ap_mac, 6) <= 0) {
387 snonce = env->
key_msg.vb_octets + 17;
388 if (memcmp(snonce, env->
anonce.vb_octets, 32) <= 0) {
390 max_nonce = env->
anonce.vb_octets;
392 min_nonce = env->
anonce.vb_octets;
399 memcpy(message,
"Pairwise key expansion",
sizeof(
"Pairwise key expansion"));
400 p = &message[
sizeof(
"Pairwise key expansion")];
402 memcpy(p, min_mac, 6);
403 memcpy(p + 6, max_mac, 6);
406 memcpy(p, min_nonce, 32);
407 memcpy(p + 32, max_nonce, 32);
410 fr_assert(
sizeof(message) == (p + 1 - message));
418 if (
inst->cache_size) {
419 entry = dpsk_cache_find(request,
inst, pmk,
sizeof(pmk), &env->
ssid, s_mac);
436 if (env->
masterkey.vb_length !=
sizeof(pmk)) {
441 memcpy(pmk, env->
masterkey.vb_octets,
sizeof(pmk));
451 if (generate_pmk(request, pmk,
sizeof(pmk), &env->
ssid, env->
psk.vb_strvalue, env->
psk.vb_length) < 0) {
459 psk_identity = env->
username.vb_strvalue;
462 psk = env->
psk.vb_strvalue;
463 psk_len = env->
psk.vb_length;
476 RERROR(
"No %s was found, and no 'filename' was configured", env->
psk_tmpl->name);
494 RDEBUG3(
"Looking for PSK in file %s", filename);
496 fp = fopen(filename,
"r");
502 fr_sbuff_init_file(&sbuff, &fctx,
buffer,
sizeof(
buffer), fp, SIZE_MAX);
509 sizeof(token_identity), quoted ? "ed_terms : &terms, NULL);
511 RDEBUG(
"Failed to find matching PSK or MAC in %s", filename);
522 RDEBUG(
"%s[%d] Failed to find ',' after identity", filename, lineno);
529 sizeof(token_identity), quoted ? "ed_terms : &terms, NULL);
531 RDEBUG(
"%s[%d] Failed parsing PSK", filename, lineno);
549 sizeof(token_identity), quoted ? "ed_terms : &terms, NULL);
551 RERROR(
"%s[%d] Failed parsing MAC", filename, lineno);
562 RERROR(
"%s[%d] Failed parsing MAC", filename, lineno);
570 if (memcmp(s_mac, token_mac, 6) != 0) {
582 psk_len = strlen(token_psk);
583 psk_identity = token_identity;
585 RDEBUG3(
"%s[%d] Trying PSK %s", filename, lineno, token_psk);
586 if (generate_pmk(request, pmk,
sizeof(pmk), &env->
ssid, psk, psk_len) < 0) {
597 digest_len =
sizeof(digest);
602 memset(digest, 0, digest_len);
604 HMAC(EVP_sha1(), pmk,
sizeof(pmk), message,
sizeof(message), digest, &digest_len);
606 RHEXDUMP3(message,
sizeof(message),
"message:");
615 memset(&zeroed->
frame.
mic[0], 0, 16);
619 mic_len =
sizeof(mic);
624 memset(mic, 0, mic_len);
626 HMAC(EVP_sha1(), digest, 16, frame, env->
key_msg.vb_length, mic, &mic_len);
631 if (memcmp(&eapol->
frame.
mic[0], mic, 16) != 0) {
659 RDEBUG(
"Checking file %s for PSK and MAC", filename);
663 RWARN(
"No 'filename' was configured.");
673 if (stage == 2)
goto stage2a;
683 RWARN(
"Found matching MAC in %s, but the PSK does not match", filename);
697 fr_assert(psk_identity == token_identity);
708 if (
inst->cache_size && psk && psk_identity) {
714 if (entry)
goto update_entry;
720 memcpy(my_entry.
mac, s_mac,
sizeof(my_entry.
mac));
721 memcpy(&my_entry.
ssid, env->
ssid.vb_octets,
sizeof(my_entry.
ssid));
731 pthread_mutex_lock(&
inst->mutable->mutex);
733 pthread_mutex_unlock(&
inst->mutable->mutex);
740 memcpy(entry->
mac, s_mac,
sizeof(entry->
mac));
741 memcpy(entry->
pmk, pmk,
sizeof(entry->
pmk));
748 MEM(entry->
ssid = talloc_memdup(entry, env->
ssid.vb_octets, env->
ssid.vb_length));
751 MEM(entry->
psk = talloc_strdup(entry, psk));
755 MEM(entry->
identity = talloc_strdup(entry, psk_identity));
762 goto update_attributes;
768 pthread_mutex_lock(&
inst->mutable->mutex);
772 pthread_mutex_unlock(&
inst->mutable->mutex);
846 if (PKCS5_PBKDF2_HMAC_SHA1(psk->vb_strvalue, psk->vb_length,
847 (
const unsigned char *) ssid->vb_strvalue, ssid->vb_length,
849 RERROR(
"Failed calling OpenSSL to calculate the PMK");
875static int8_t cmp_cache_entry(
void const *one,
void const *two)
881 rcode = memcmp(a->
mac, b->
mac,
sizeof(a->
mac));
882 if (rcode != 0)
return rcode;
890static void free_cache_entry(
void *
data)
905 if (!
inst->cache_size)
return 0;
907 pthread_mutex_destroy(&
inst->mutable->mutex);
921 if (!
inst->auth_type) {
922 WARN(
"Failed to find 'authenticate %s {...}' section. DPSK will likely not work",
931 if (!
inst->cache_size)
return 0;
944 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 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
#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.
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 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.