# define EAP_TLS_MPPE_KEY_LEN 32
#endif
+FR_NAME_NUMBER const sim_state_table[] = {
+ { "START", EAP_SIM_SERVER_START },
+ { "CHALLENGE", EAP_SIM_SERVER_CHALLENGE },
+ { "SUCCESS-NOTIFICATION", EAP_SIM_SERVER_SUCCESS_NOTIFICATION },
+ { "SUCCESS", EAP_SIM_SERVER_SUCCESS },
+ { "GENERAL-FAILURE-NOTIFICATION", EAP_SIM_SERVER_GENERAL_FAILURE_NOTIFICATION },
+ { "FAILURE", EAP_SIM_SERVER_FAILURE },
+ { NULL }
+};
+
+static CONF_PARSER submodule_config[] = {
+ { FR_CONF_OFFSET("virtual_server", FR_TYPE_STRING, rlm_eap_sim_t, virtual_server) },
+ CONF_PARSER_TERMINATOR
+};
+
/*
* build a reply to be sent.
*/
return 0;
}
-static int eap_sim_send_state(eap_session_t *eap_session)
+static int eap_sim_send_start(eap_session_t *eap_session)
{
REQUEST *request = eap_session->request;
VALUE_PAIR **vps, *vp;
vp->vp_uint32 = eap_sim_session->sim_id++;
fr_pair_replace(vps, vp);
- /* the ANY_ID attribute. We do not support re-auth or pseudonym */
- MEM(vp = fr_pair_afrom_child_num(packet, dict_sim_root, FR_EAP_SIM_FULLAUTH_ID_REQ));
+ /*
+ * Select the right type of identity request attribute
+ */
+ switch (eap_sim_session->id_req) {
+ case SIM_ANY_ID_REQ:
+ vp = fr_pair_afrom_child_num(packet, dict_sim_root, FR_EAP_SIM_ANY_ID_REQ);
+ break;
+
+ case SIM_PERMANENT_ID_REQ:
+ vp = fr_pair_afrom_child_num(packet, dict_sim_root, FR_EAP_SIM_PERMANENT_ID_REQ);
+ break;
+
+ case SIM_FULLAUTH_ID_REQ:
+ vp = fr_pair_afrom_child_num(packet, dict_sim_root, FR_EAP_SIM_FULLAUTH_ID_REQ);
+ break;
+
+ default:
+ rad_assert(0);
+ }
vp->vp_bool = true;
- fr_pair_add(vps, vp);
+ fr_pair_replace(vps, vp);
/* the SUBTYPE, set to start. */
vp = fr_pair_afrom_child_num(packet, dict_sim_root, FR_EAP_SIM_SUBTYPE);
vp->vp_uint32 = EAP_SIM_START;
fr_pair_replace(vps, vp);
+ /*
+ * Encode the packet
+ */
+ if (eap_sim_compose(eap_session) < 0) {
+ fr_pair_list_free(&packet->vps);
+ return -1;
+ }
+
return 0;
}
static uint8_t hmac_zero[16] = { 0x00 };
REQUEST *request = eap_session->request;
- eap_sim_session_t *eap_sim_session;
- VALUE_PAIR **from_peer, **to_client, *vp;
+ eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
+ VALUE_PAIR **to_peer, *vp;
RADIUS_PACKET *packet;
+ fr_sim_vector_src_t src = SIM_VECTOR_SRC_AUTO;
- eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
rad_assert(eap_session->request != NULL);
rad_assert(eap_session->request->reply);
- RDEBUG2("Sending SIM-Challenge");
- eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
+ RDEBUG2("Acquiring GSM vector(s)");
+ if ((fr_sim_vector_gsm_from_attrs(eap_session, request->control, 0, &eap_sim_session->keys, &src) != 0) ||
+ (fr_sim_vector_gsm_from_attrs(eap_session, request->control, 1, &eap_sim_session->keys, &src) != 0) ||
+ (fr_sim_vector_gsm_from_attrs(eap_session, request->control, 2, &eap_sim_session->keys, &src) != 0)) {
+ REDEBUG("Failed retrieving SIM vectors");
+ return RLM_MODULE_FAIL;
+ }
/*
- * from_peer is the data from the client but this is for non-protocol data here.
- * We should already have consumed any client originated data.
+ * All set, calculate keys!
*/
- from_peer = &eap_session->request->packet->vps;
+ fr_sim_crypto_kdf_0_gsm(&eap_sim_session->keys);
+ if (RDEBUG_ENABLED3) fr_sim_crypto_keys_log(request, &eap_sim_session->keys);
+
+ RDEBUG2("Sending SIM-Challenge");
+ eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
/*
- * to_client is the data to the client
+ * to_peer is the data to the client
*/
packet = eap_session->request->reply;
- to_client = &packet->vps;
+ to_peer = &packet->vps;
/*
* Okay, we got the challenges! Put them into attributes.
*/
MEM(vp = fr_pair_afrom_child_num(packet, dict_sim_root, FR_EAP_SIM_RAND));
fr_pair_value_memcpy(vp, eap_sim_session->keys.gsm.vector[0].rand, SIM_VECTOR_GSM_RAND_SIZE);
- fr_pair_add(to_client, vp);
+ fr_pair_add(to_peer, vp);
MEM(vp = fr_pair_afrom_child_num(packet, dict_sim_root, FR_EAP_SIM_RAND));
fr_pair_value_memcpy(vp, eap_sim_session->keys.gsm.vector[1].rand, SIM_VECTOR_GSM_RAND_SIZE);
- fr_pair_add(to_client, vp);
+ fr_pair_add(to_peer, vp);
MEM(vp = fr_pair_afrom_child_num(packet, dict_sim_root, FR_EAP_SIM_RAND));
fr_pair_value_memcpy(vp, eap_sim_session->keys.gsm.vector[2].rand, SIM_VECTOR_GSM_RAND_SIZE);
- fr_pair_add(to_client, vp);
+ fr_pair_add(to_peer, vp);
/*
- * Set the EAP_ID - new value
+ * Set subtype to challenge.
*/
- vp = fr_pair_afrom_child_num(packet, fr_dict_root(fr_dict_internal), FR_EAP_ID);
- vp->vp_uint32 = eap_sim_session->sim_id++;
- fr_pair_replace(to_client, vp);
+ vp = fr_pair_afrom_child_num(packet, dict_sim_root, FR_EAP_SIM_SUBTYPE);
+ vp->vp_uint32 = EAP_SIM_CHALLENGE;
+ fr_pair_replace(to_peer, vp);
/*
- * Use the SIM identity, if available
+ * Indicate we'd like to use protected success messages
*/
- vp = fr_pair_find_by_child_num(*from_peer, dict_sim_root, FR_EAP_SIM_IDENTITY, TAG_ANY);
- if (vp) {
- MEM(eap_sim_session->keys.identity = (uint8_t *)talloc_bstrndup(eap_sim_session,
- vp->vp_strvalue, vp->vp_length));
- eap_sim_session->keys.identity_len = vp->vp_length;
- /*
- * Make a copy of the identity
- */
- } else {
- if (eap_sim_session->keys.identity) talloc_const_free(eap_sim_session->keys.identity);
-
- eap_sim_session->keys.identity_len = talloc_array_length(eap_session->identity) - 1;
- MEM(eap_sim_session->keys.identity = talloc_memdup(eap_sim_session, eap_session->identity,
- eap_sim_session->keys.identity_len));
+ if (eap_sim_session->send_result_ind) {
+ MEM(vp = fr_pair_afrom_child_num(packet, dict_sim_root, FR_EAP_SIM_RESULT_IND));
+ vp->vp_bool = true;
+ fr_pair_replace(to_peer, vp);
}
- /*
- * All set, calculate keys!
- */
- fr_sim_crypto_kdf_0_gsm(&eap_sim_session->keys);
-
- if (RDEBUG_ENABLED3) fr_sim_crypto_keys_log(request, &eap_sim_session->keys);
-
/*
* Need to include an AT_MAC attribute so that it will get
* calculated.
*/
vp = fr_pair_afrom_child_num(packet, dict_sim_root, FR_EAP_SIM_MAC);
fr_pair_value_memcpy(vp, hmac_zero, sizeof(hmac_zero));
- fr_pair_replace(to_client, vp);
+ fr_pair_replace(to_peer, vp);
/*
- * Set subtype to challenge.
+ * Encode the packet
+ */
+ if (eap_sim_compose(eap_session) < 0) {
+ fr_pair_list_free(&packet->vps);
+ return -1;
+ }
+
+ return 0;
+}
+
+/** Send a success notification
+ *
+ */
+static int eap_sim_send_eap_success_notification(eap_session_t *eap_session)
+{
+ REQUEST *request = eap_session->request;
+ RADIUS_PACKET *packet = eap_session->request->reply;
+ fr_cursor_t cursor;
+ VALUE_PAIR *vp;
+
+ RDEBUG2("Sending SIM-Notification (Success)");
+ eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
+
+ fr_cursor_init(&cursor, &packet->vps);
+
+ /*
+ * Set the subtype to notification
*/
vp = fr_pair_afrom_child_num(packet, dict_sim_root, FR_EAP_SIM_SUBTYPE);
- vp->vp_uint32 = EAP_SIM_CHALLENGE;
- fr_pair_replace(to_client, vp);
+ vp->vp_uint32 = FR_EAP_SIM_SUBTYPE_VALUE_SIM_NOTIFICATION;
+ fr_cursor_append(&cursor, vp);
+
+ vp = fr_pair_afrom_child_num(packet, dict_sim_root, FR_EAP_SIM_NOTIFICATION);
+ vp->vp_uint32 = FR_EAP_SIM_NOTIFICATION_VALUE_SUCCESS;
+ fr_cursor_append(&cursor, vp);
+
+ /*
+ * Encode the packet
+ */
+ if (eap_sim_compose(eap_session) < 0) {
+ fr_pair_list_free(&packet->vps);
+ return -1;
+ }
return 0;
}
* The only work to be done is the add the appropriate SEND/RECV
* radius attributes derived from the MSK.
*/
-static int eap_sim_send_success(eap_session_t *eap_session)
+static int eap_sim_send_eap_success(eap_session_t *eap_session)
{
- uint8_t *p;
-
REQUEST *request = eap_session->request;
- eap_sim_session_t *eap_sim_session;
- VALUE_PAIR *vp;
- RADIUS_PACKET *packet;
+ uint8_t *p;
+ eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
RDEBUG2("Sending SIM-Success");
eap_session->this_round->request->code = FR_EAP_CODE_SUCCESS;
eap_session->finished = true;
- /* to_client is the data to the client. */
- packet = eap_session->request->reply;
- eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
-
- /* set the EAP_ID - new value */
- vp = fr_pair_afrom_child_num(packet, fr_dict_root(fr_dict_internal), FR_EAP_ID);
- vp->vp_uint32 = eap_sim_session->sim_id++;
- fr_pair_replace(&eap_session->request->reply->vps, vp);
-
p = eap_sim_session->keys.msk;
eap_add_reply(eap_session->request, "MS-MPPE-Recv-Key", p, EAP_TLS_MPPE_KEY_LEN);
p += EAP_TLS_MPPE_KEY_LEN;
return 0;
}
+/** Send a failure message
+ *
+ */
+static int eap_sim_send_eap_failure_notification(eap_session_t *eap_session)
+{
+ REQUEST *request = eap_session->request;
+ RADIUS_PACKET *packet = eap_session->request->reply;
+ fr_cursor_t cursor;
+ VALUE_PAIR *vp;
+
+ RDEBUG2("Sending SIM-Notification (General-Failure)");
+ eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
+
+ fr_cursor_init(&cursor, &packet->vps);
+
+ /*
+ * Set the subtype to notification
+ */
+ vp = fr_pair_afrom_child_num(packet, dict_sim_root, FR_EAP_SIM_SUBTYPE);
+ vp->vp_uint32 = FR_EAP_SIM_SUBTYPE_VALUE_SIM_NOTIFICATION;
+ fr_cursor_append(&cursor, vp);
+
+ vp = fr_pair_afrom_child_num(packet, dict_sim_root, FR_EAP_SIM_NOTIFICATION);
+ vp->vp_uint32 = FR_EAP_SIM_NOTIFICATION_VALUE_GENERAL_FAILURE;
+ fr_cursor_append(&cursor, vp);
+
+ /*
+ * Encode the packet
+ */
+ if (eap_sim_compose(eap_session) < 0) {
+ fr_pair_list_free(&packet->vps);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int eap_sim_send_eap_failure(eap_session_t *eap_session)
+{
+ REQUEST *request = eap_session->request;
+
+ RDEBUG2("Sending EAP-Failure");
+
+ eap_session->this_round->request->code = FR_EAP_CODE_FAILURE;
+ eap_session->finished = true;
+
+ return 0;
+}
+
/** Run the server state machine
*
*/
-static void eap_sim_state_enter(eap_session_t *eap_session,
- eap_sim_session_t *eap_sim_session,
- eap_sim_server_state_t new_state)
+static void eap_sim_state_enter(eap_session_t *eap_session, eap_sim_server_state_t new_state)
{
+ REQUEST *request = eap_session->request;
+ eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
+
+ if (new_state != eap_sim_session->state) {
+ RDEBUG2("Changed state %s -> %s",
+ fr_int2str(sim_state_table, eap_sim_session->state, "<unknown>"),
+ fr_int2str(sim_state_table, new_state, "<unknown>"));
+ eap_sim_session->state = new_state;
+ } else {
+ RDEBUG2("Reentering state %s",
+ fr_int2str(sim_state_table, eap_sim_session->state, "<unknown>"));
+ }
+
switch (new_state) {
/*
- * Send the EAP-SIM Start message, listing the versions that we support.
+ * Send our version list
*/
case EAP_SIM_SERVER_START:
- eap_sim_send_state(eap_session);
- eap_sim_compose(eap_session); /* Encode SIM TLVs */
+ if (eap_sim_send_start(eap_session) < 0) {
+ notify_failure:
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ return;
+ }
break;
+
/*
* Send the EAP-SIM Challenge message.
*/
case EAP_SIM_SERVER_CHALLENGE:
- eap_sim_send_challenge(eap_session);
- eap_sim_compose(eap_session); /* Encode SIM TLVs */
+ if (eap_sim_send_challenge(eap_session) < 0) goto notify_failure;
break;
/*
- * Send the EAP Success message
+ * Sent a protected success notification
+ */
+ case EAP_SIM_SERVER_SUCCESS_NOTIFICATION:
+ if (eap_sim_send_eap_success_notification(eap_session) < 0) goto notify_failure;
+ break;
+
+ /*
+ * Send the EAP Success message (we're done)
*/
case EAP_SIM_SERVER_SUCCESS:
- eap_sim_send_success(eap_session);
+ if (eap_sim_send_eap_success(eap_session) < 0) goto notify_failure;
+ return;
- break;
/*
- * Nothing to do for this transition.
+ * Send a general failure notification
*/
+ case EAP_SIM_SERVER_GENERAL_FAILURE_NOTIFICATION:
+ if (eap_sim_send_eap_failure_notification(eap_session) < 0) { /* Fallback to EAP-Failure */
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE);
+ }
+ return;
+
+ /*
+ * Send an EAP-Failure (we're done)
+ */
+ case EAP_SIM_SERVER_FAILURE:
+ eap_sim_send_eap_failure(eap_session);
+ return;
+
default:
- eap_sim_compose(eap_session); /* Encode SIM TLVs */
- break;
+ rad_assert(0); /* Invalid transition */
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ return;
}
-
- eap_sim_session->state = new_state;
}
-static rlm_rcode_t CC_HINT(nonnull) mod_process(void *instance, eap_session_t *eap_session);
-
/** Process an EAP-Sim/Response/Start
*
* Verify that client chose a version, and provided a NONCE_MT,
VALUE_PAIR *nonce_vp, *selected_version_vp;
eap_sim_session_t *eap_sim_session;
uint16_t eap_sim_version;
+ VALUE_PAIR *id;
+ fr_sim_id_type_t type = SIM_ID_TYPE_UNKNOWN;
+ fr_sim_method_hint_t method = SIM_METHOD_HINT_UNKNOWN;
eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
+ /*
+ * For fullauth We require both the NONCE_MT
+ * and SELECTED_VERSION from the peer, else
+ * the packet is invalid.
+ */
nonce_vp = fr_pair_find_by_child_num(vps, dict_sim_root, FR_EAP_SIM_NONCE_MT, TAG_ANY);
selected_version_vp = fr_pair_find_by_child_num(vps, dict_sim_root, FR_EAP_SIM_SELECTED_VERSION, TAG_ANY);
if (!nonce_vp || !selected_version_vp) {
- RDEBUG2("Client did not select a version and send a NONCE");
- eap_sim_state_enter(eap_session, eap_sim_session, EAP_SIM_SERVER_START);
-
- return 0;
+ REDEBUG2("Client did not select a version and send a NONCE");
+ return -1;
}
eap_sim_version = selected_version_vp->vp_uint16;
if (eap_sim_version != EAP_SIM_VERSION) {
- REDEBUG("EAP-SIM version %i is unknown", eap_sim_version);
+ REDEBUG2("EAP-SIM-Version %i is unknown", eap_sim_version);
return -1;
}
memcpy(eap_sim_session->keys.gsm.nonce_mt, nonce_vp->vp_octets, 16);
/*
- * Everything looks good, change states
+ * See if we got an AT_IDENTITY
+ */
+ id = fr_pair_find_by_child_num(vps, dict_sim_root, FR_EAP_SIM_IDENTITY, TAG_ANY);
+ if (id) {
+ if (fr_sim_id_type(&type, &method,
+ eap_session->identity, talloc_array_length(eap_session->identity) - 1) < 0) {
+ RWDEBUG2("Failed parsing identity: %s", fr_strerror());
+ }
+
+ /*
+ * Update cryptographic identity
+ */
+ talloc_const_free(eap_sim_session->keys.identity);
+ eap_sim_session->keys.identity_len = id->vp_length;
+ MEM(eap_sim_session->keys.identity = talloc_memdup(eap_sim_session, id->vp_strvalue, id->vp_length));
+ }
+
+ /*
+ * @TODO Run a virtual server to see if we can use the
+ * identity we just acquired, or whether we need to
+ * negotiate the next permissive ID.
+ */
+
+ /*
+ * Negotiate the next permissive form
+ * if identity, or fail.
*/
- eap_sim_state_enter(eap_session, eap_sim_session, EAP_SIM_SERVER_CHALLENGE);
+ switch (eap_sim_session->id_req) {
+ case SIM_ANY_ID_REQ:
+ eap_sim_session->id_req = SIM_FULLAUTH_ID_REQ;
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_START);
+ break;
+
+ case SIM_FULLAUTH_ID_REQ:
+ eap_sim_session->id_req = SIM_PERMANENT_ID_REQ;
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_START);
+ break;
+
+ case SIM_NO_ID_REQ:
+ case SIM_PERMANENT_ID_REQ:
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_CHALLENGE);
+// REDEBUG2("Failed to negotiate a usable identity");
+// eap_sim_state_enter(eap_session, eap_sim_session, EAP_SIM_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ break;
+ }
return 0;
}
+
/** Process an EAP-Sim/Response/Challenge
*
* Verify that MAC that we received matches what we would have
uint8_t sres_cat[SIM_VECTOR_GSM_SRES_SIZE * 3];
uint8_t *p = sres_cat;
- uint8_t calc_mac[SIM_MAC_SIZE];
+ uint8_t calc_mac[SIM_MAC_DIGEST_SIZE];
ssize_t slen;
VALUE_PAIR *mac;
p += SIM_VECTOR_GSM_SRES_SIZE;
memcpy(p, eap_sim_session->keys.gsm.vector[2].sres, SIM_VECTOR_GSM_SRES_SIZE);
- mac = fr_pair_find_by_child_num(vps, dict_aka_root, FR_EAP_AKA_RES, TAG_ANY);
+ mac = fr_pair_find_by_child_num(vps, dict_sim_root, FR_EAP_SIM_MAC, TAG_ANY);
if (!mac) {
REDEBUG("Missing AT_MAC attribute");
return -1;
}
- if (mac->vp_length != SIM_MAC_SIZE) {
- REDEBUG("AT_MAC incorrect length, expected %u bytes got %zu bytes",
- SIM_MAC_SIZE, mac->vp_length);
+ if (mac->vp_length != SIM_MAC_DIGEST_SIZE) {
+ REDEBUG("EAP-SIM-MAC has incorrect length, expected %u bytes got %zu bytes",
+ SIM_MAC_DIGEST_SIZE, mac->vp_length);
return -1;
}
slen = fr_sim_crypto_sign_packet(calc_mac, eap_session->this_round->response, true, EVP_sha1(),
eap_sim_session->keys.k_aut, eap_sim_session->keys.k_aut_len,
- NULL, 0);
+ sres_cat, sizeof(sres_cat));
if (slen < 0) {
RPEDEBUG("Failed calculating MAC");
return -1;
- }
-
- if (slen == 0) {
+ } else if (slen == 0) {
REDEBUG("Missing EAP-SIM-MAC attribute in packet buffer");
return -1;
}
RDEBUG2("EAP-SIM-MAC matches calculated MAC");
} else {
REDEBUG("EAP-SIM-MAC does not match calculated MAC");
- RHEXDUMP_INLINE(L_DBG_LVL_2, mac->vp_octets, SIM_MAC_SIZE, "Received");
- RHEXDUMP_INLINE(L_DBG_LVL_2, calc_mac, SIM_MAC_SIZE, "Expected");
+ RHEXDUMP_INLINE(L_DBG_LVL_2, mac->vp_octets, SIM_MAC_DIGEST_SIZE, "Received");
+ RHEXDUMP_INLINE(L_DBG_LVL_2, calc_mac, SIM_MAC_DIGEST_SIZE, "Expected");
return -1;
}
- /* everything looks good, change states */
- eap_sim_state_enter(eap_session, eap_sim_session, EAP_SIM_SERVER_SUCCESS);
+ /*
+ * If the peer wants a Success notification, then
+ * send a success notification, otherwise send a
+ * normal EAP-Success.
+ */
+ if (fr_pair_find_by_child_num(vps, dict_aka_root, FR_EAP_SIM_RESULT_IND, TAG_ANY)) {
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_SUCCESS_NOTIFICATION);
+ } else {
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_SUCCESS);
+ }
return 0;
}
{
REQUEST *request = eap_session->request;
eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
-
fr_sim_decode_ctx_t ctx = {
.keys = &eap_sim_session->keys,
.root = dict_sim_root
};
- VALUE_PAIR *vp, *vps;
+ VALUE_PAIR *subtype_vp, *from_peer, *vp;
vp_cursor_t cursor;
eap_sim_subtype_t subtype;
/*
* VPS is the data from the client
*/
- vps = eap_session->request->packet->vps;
+ from_peer = eap_session->request->packet->vps;
fr_pair_cursor_init(&cursor, &request->packet->vps);
fr_pair_cursor_last(&cursor);
eap_session->this_round->response->type.data,
eap_session->this_round->response->type.length,
&ctx);
- if (ret < 0) return 0;
+ /*
+ * RFC 4186 says we *MUST* notify, not just
+ * send an EAP-Failure in this case where
+ * we cannot decode an EAP-AKA packet.
+ */
+ if (ret < 0) {
+ RPEDEBUG2("Failed decoding EAP-SIM attributes");
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ return RLM_MODULE_HANDLED; /* We need to process more packets */
+ }
vp = fr_pair_cursor_current(&cursor);
if (vp && RDEBUG_ENABLED2) {
rdebug_pair_list(L_DBG_LVL_2, request, vp, NULL);
}
- /*
- * See what kind of message we have gotten
- */
- vp = fr_pair_find_by_child_num(vps, dict_sim_root, FR_EAP_SIM_SUBTYPE, TAG_ANY);
- if (!vp) {
- REDEBUG2("No subtype attribute was created, message dropped");
- return 0;
+ subtype_vp = fr_pair_find_by_child_num(from_peer, dict_sim_root, FR_EAP_SIM_SUBTYPE, TAG_ANY);
+ if (!subtype_vp) {
+ REDEBUG("Missing EAP-SIM-Subtype");
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ return RLM_MODULE_HANDLED; /* We need to process more packets */
}
- subtype = vp->vp_uint32;
+ subtype = subtype_vp->vp_uint32;
+ switch (eap_sim_session->state) {
/*
- * Client error supersedes anything else.
+ * Response to our advertised versions and request for an ID
+ * This is very similar to Identity negotiation in EAP-AKA[']
*/
- if (subtype == EAP_SIM_CLIENT_ERROR) {
- REDEBUG("Client encountered an error");
- return RLM_MODULE_FAIL;
- }
-
- switch (eap_sim_session->state) {
case EAP_SIM_SERVER_START:
switch (subtype) {
+ case EAP_SIM_START:
+ if (process_eap_sim_start(eap_session, from_peer) == 0) return RLM_MODULE_HANDLED;
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ return RLM_MODULE_HANDLED; /* We need to process more packets */
+
/*
- * Pretty much anything else here is illegal, so we will retransmit the request.
+ * Case 1 where we're allowed to send an EAP-Failure
+ *
+ * This can happen in the case of a conservative
+ * peer, where it refuses to provide the permanent
+ * identity.
*/
+ case EAP_SIM_CLIENT_ERROR:
+ {
+ char buff[20];
+
+ vp = fr_pair_find_by_child_num(from_peer, dict_sim_root, FR_EAP_SIM_CLIENT_ERROR_CODE, TAG_ANY);
+ if (!vp) {
+ REDEBUG("EAP-SIM Peer rejected SIM-Start (%s) with client-error message but "
+ "has not supplied a client error code",
+ fr_int2str(sim_id_request_table, eap_sim_session->id_req, "<INVALID>"));
+ } else {
+ REDEBUG("Client rejected SIM-Start (%s) with error: %s (%i)",
+ fr_int2str(sim_id_request_table, eap_sim_session->id_req, "<INVALID>"),
+ fr_pair_value_enum(vp, buff), vp->vp_uint16);
+ }
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE);
+ return RLM_MODULE_REJECT;
+ }
+
+ case EAP_SIM_NOTIFICATION:
+ notification:
+ {
+ char buff[20];
+
+ vp = fr_pair_afrom_child_num(from_peer, dict_aka_root, FR_EAP_SIM_NOTIFICATION);
+ if (!vp) {
+ REDEBUG2("Received SIM-Notification with no notification code");
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ return RLM_MODULE_HANDLED; /* We need to process more packets */
+ }
+
+ /*
+ * Case 2 where we're allowed to send an EAP-Failure
+ */
+ if (!(vp->vp_uint16 & 0x8000)) {
+ REDEBUG2("SIM-Notification %s (%i) indicates failure", fr_pair_value_enum(vp, buff),
+ vp->vp_uint16);
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE);
+ return RLM_MODULE_REJECT;
+ }
+
+ /*
+ * ...if it's not a failure, then re-enter the
+ * current state.
+ */
+ REDEBUG2("Got SIM-Notification %s (%i)", fr_pair_value_enum(vp, buff), vp->vp_uint16);
+ eap_sim_state_enter(eap_session, eap_sim_session->state);
+ return RLM_MODULE_HANDLED;
+
default:
- eap_sim_state_enter(eap_session, eap_sim_session, EAP_SIM_SERVER_START);
- return RLM_MODULE_OK;
- /*
- * A response to our EAP-Sim/Request/Start!
- */
- case EAP_SIM_START:
- return process_eap_sim_start(eap_session, vps);
+ unexpected_subtype:
+ /*
+ * RFC 4186 says we *MUST* notify, not just
+ * send an EAP-Failure in this case.
+ */
+ REDEBUG("Unexpected subtype %pV", &subtype_vp->data);
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ return RLM_MODULE_HANDLED; /* We need to process more packets */
+ }
}
+ /*
+ * Process the response to our previous challenge.
+ */
case EAP_SIM_SERVER_CHALLENGE:
switch (subtype) {
- /*
- * Pretty much anything else here is illegal, so we will retransmit the request.
- */
- default:
- eap_sim_state_enter(eap_session, eap_sim_session, EAP_SIM_SERVER_CHALLENGE);
- return 1;
/*
* A response to our EAP-Sim/Request/Challenge!
*/
case EAP_SIM_CHALLENGE:
- return process_eap_sim_challenge(eap_session, vps);
+ return process_eap_sim_challenge(eap_session, from_peer);
+
+ case EAP_SIM_CLIENT_ERROR:
+ {
+ char buff[20];
+
+ vp = fr_pair_find_by_child_num(from_peer, dict_aka_root, FR_EAP_SIM_CLIENT_ERROR_CODE, TAG_ANY);
+ if (!vp) {
+ REDEBUG("EAP-SIM Peer rejected SIM-Challenge with client-error message but "
+ "has not supplied a client error code");
+ } else {
+ REDEBUG("Client rejected SIM-Challenge with error: %s (%i)",
+ fr_pair_value_enum(vp, buff), vp->vp_uint16);
+ }
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE);
+ return RLM_MODULE_REJECT;
+ }
+
+ case EAP_AKA_NOTIFICATION:
+ goto notification;
+
+ default:
+ goto unexpected_subtype;
}
+ /*
+ * Peer acked our failure
+ */
+ case EAP_SIM_SERVER_GENERAL_FAILURE_NOTIFICATION:
+ switch (subtype) {
+ case EAP_SIM_NOTIFICATION:
+ RDEBUG2("SIM-Notification ACKed, sending EAP-Failure");
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE);
+ return RLM_MODULE_REJECT;
+
+ default:
+ goto unexpected_subtype;
+ }
+
+ /*
+ * Something bad happened...
+ */
default:
rad_assert(0);
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ return RLM_MODULE_HANDLED; /* We need to process more packets */
}
return RLM_MODULE_FAIL;
*/
static rlm_rcode_t mod_session_init(UNUSED void *instance, eap_session_t *eap_session)
{
- REQUEST *request = eap_session->request;
- eap_sim_session_t *eap_sim_session;
- time_t n;
- fr_sim_vector_src_t src = SIM_VECTOR_SRC_AUTO;
+ REQUEST *request = eap_session->request;
+ eap_sim_session_t *eap_sim_session;
+// rlm_eap_sim_t *inst = instance;
+ fr_sim_id_type_t type;
+ fr_sim_method_hint_t method;
MEM(eap_sim_session = talloc_zero(eap_session, eap_sim_session_t));
eap_session->opaque = eap_sim_session;
/*
- * Save the keying material, because it could change on a subsequent retrieval.
+ * Set default configuration, we may allow these
+ * to be toggled by attributes later.
*/
- RDEBUG2("New EAP-SIM session. Acquiring SIM vectors");
- if ((fr_sim_vector_gsm_from_attrs(eap_session, request->control, 0, &eap_sim_session->keys, &src) != 0) ||
- (fr_sim_vector_gsm_from_attrs(eap_session, request->control, 1, &eap_sim_session->keys, &src) != 0) ||
- (fr_sim_vector_gsm_from_attrs(eap_session, request->control, 2, &eap_sim_session->keys, &src) != 0)) {
- REDEBUG("Failed retrieving SIM vectors");
- return RLM_MODULE_FAIL;
- }
+ eap_sim_session->send_result_ind = true;
+ eap_sim_session->id_req = SIM_ANY_ID_REQ; /* Set the default */
/*
- * This value doesn't have be strong, but it is good if it is different now and then.
+ * This value doesn't have be strong, but it is
+ * good if it is different now and then.
*/
- time(&n);
- eap_sim_session->sim_id = (n & 0xff);
-
- eap_sim_state_enter(eap_session, eap_sim_session, EAP_SIM_SERVER_START);
-
- eap_session->process = mod_process;
-
- return RLM_MODULE_OK;
-}
+ eap_sim_session->sim_id = (fr_rand() & 0xff);
-/*
- * Attach the module.
- */
-static int mod_instantiate(UNUSED void *instance, CONF_SECTION *cs)
-{
- fr_dict_attr_t const *da;
- CONF_SECTION *subcs = NULL;
+ /*
+ * Save the keying material, because it could change on a subsequent retrieval.
+ */
+ RDEBUG2("New EAP-SIM session");
- da = fr_dict_attr_child_by_num(dict_sim_root, FR_EAP_SIM_SUBTYPE);
- if (!da) {
- cf_log_err(cs, "Failed to find EAP-Sim-Subtype attribute");
- return -1;
+ /*
+ * Process the identity that we received in the
+ * EAP-Identity-Response and use it to determine
+ * the initial request we send to the Supplicant.
+ */
+ if (fr_sim_id_type(&type, &method,
+ eap_session->identity, talloc_array_length(eap_session->identity) - 1) < 0) {
+ RWDEBUG2("Failed parsing identity, continuing anyway: %s", fr_strerror());
}
- while ((subcs = cf_section_find_next(cs, subcs, "process", NULL))) {
- char const *name2;
- fr_dict_enum_t *dv;
-
- name2 = cf_section_name2(subcs);
- if (!name2) {
- cf_log_err(subcs, "Cannot compile 'process { ... }' section");
- return -1;
- }
-
- dv = fr_dict_enum_by_alias(NULL, da, name2);
- if (!dv) {
- cf_log_err(subcs, "Unknown EAP-SIM-Subtype %s", name2);
- return -1;
- }
+ switch (method) {
+ default:
+ RWDEBUG("EAP-Identity-Response hints that EAP-%s should be started, but we're attempting EAP-SIM",
+ fr_int2str(sim_id_method_hint_table, method, "<INVALID>"));
+ break;
- cf_log_debug(subcs, "Loading process %s {...}", name2);
+ case SIM_METHOD_HINT_SIM:
+ case SIM_METHOD_HINT_UNKNOWN:
+ break;
+ }
+ eap_session->process = mod_process;
- if (unlang_compile(subcs, MOD_AUTHORIZE) < 0) {
- cf_log_err(subcs, "Failed compiling 'process %s { ... }' section", name2);
- return -1;
- }
+ /*
+ * Figure out what type of identity we have
+ * and use it to determine the initial
+ * request we send.
+ */
+ switch (type) {
+ /*
+ * These types need to be transformed into something
+ * usable before we can do anything.
+ */
+ case SIM_ID_TYPE_UNKNOWN:
+ case SIM_ID_TYPE_PSEUDONYM:
+ case SIM_ID_TYPE_FASTAUTH:
+ /*
+ * Permanent ID means we can just send the challenge
+ */
+ case SIM_ID_TYPE_PERMANENT:
+ eap_sim_session->keys.identity_len = talloc_array_length(eap_session->identity) - 1;
+ MEM(eap_sim_session->keys.identity = talloc_memdup(eap_sim_session, eap_session->identity,
+ eap_sim_session->keys.identity_len));
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_START);
+ return RLM_MODULE_OK;
}
- return 0;
+ return RLM_MODULE_OK;
}
static int mod_load(void)
.magic = RLM_MODULE_INIT,
.provides = { FR_EAP_SIM },
+ .inst_size = sizeof(rlm_eap_sim_t),
+ .config = submodule_config,
+
.load = mod_load,
.unload = mod_unload,
- .instantiate = mod_instantiate, /* Create new submodule instance */
.session_init = mod_session_init, /* Initialise a new EAP session */
.process = mod_process, /* Process next round of EAP method */
};