EAP_AKA_SERVER_CHALLENGE, //!< We've challenged the supplicant.
EAP_AKA_SERVER_SUCCESS_NOTIFICATION, //!< Send success notification.
EAP_AKA_SERVER_SUCCESS, //!< Authentication completed successfully.
- EAP_AKA_SERVER_GENERAL_FAILURE_NOTIFICATION, //!< Send failure notification.
+ EAP_AKA_SERVER_FAILURE_NOTIFICATION, //!< Send failure notification.
EAP_AKA_SERVER_FAILURE, //!< Send an EAP-Failure.
EAP_AKA_SERVER_MAX_STATES
} eap_aka_server_state_t;
typedef struct {
eap_aka_server_state_t state; //!< Current session state.
+ bool challenge_success; //!< Whether we received the correct
+ ///< challenge response.
+
fr_sim_id_req_type_t id_req; //!< The type of identity we're requesting
///< or previously requested.
fr_sim_keys_t keys; //!< Various EAP-AKA keys.
{ "CHALLENGE", EAP_AKA_SERVER_CHALLENGE },
{ "SUCCESS-NOTIFICATION", EAP_AKA_SERVER_SUCCESS_NOTIFICATION },
{ "SUCCESS", EAP_AKA_SERVER_SUCCESS },
- { "GENERAL-FAILURE-NOTIFICATION", EAP_AKA_SERVER_GENERAL_FAILURE_NOTIFICATION },
+ { "FAILURE-NOTIFICATION", EAP_AKA_SERVER_FAILURE_NOTIFICATION },
{ "FAILURE", EAP_AKA_SERVER_FAILURE },
{ NULL }
};
*
* Challenges will come from one of three places eventually:
*
- * 1 from attributes like FR_EAP_SIM_RANDx
+ * 1 from attributes like FR_EAP_AKA_RANDx
* (these might be retrieved from a database)
*
* 2 from internally implemented SIM authenticators
*/
static int eap_aka_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;
+ REQUEST *request = eap_session->request;
+ RADIUS_PACKET *packet = eap_session->request->reply;
+ eap_aka_session_t *eap_aka_session = talloc_get_type_abort(eap_session->opaque, eap_aka_session_t);
+ fr_cursor_t cursor;
+ VALUE_PAIR *vp;
RDEBUG2("Sending AKA-Notification (Success)");
eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
+ if (!fr_cond_assert(eap_aka_session->challenge_success)) return -1;
+
fr_cursor_init(&cursor, &packet->vps);
/*
vp->vp_uint32 = FR_EAP_AKA_NOTIFICATION_VALUE_SUCCESS;
fr_cursor_append(&cursor, vp);
+ /*
+ * Need to include an AT_MAC attribute so that it will get
+ * calculated.
+ */
+ vp = fr_pair_afrom_child_num(packet, dict_aka_root, FR_EAP_AKA_MAC);
+ fr_pair_replace(&packet->vps, vp);
+
/*
* Encode the packet
*/
*/
static int eap_aka_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 AKA-Notification (General-Failure)");
- eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
+ REQUEST *request = eap_session->request;
+ RADIUS_PACKET *packet = eap_session->request->reply;
+ fr_cursor_t cursor;
+ VALUE_PAIR *vp;
+ eap_aka_session_t *eap_aka_session = talloc_get_type_abort(eap_session->opaque, eap_aka_session_t);
fr_cursor_init(&cursor, &packet->vps);
+ vp = fr_pair_find_by_child_num(packet->vps, dict_sim_root, FR_EAP_AKA_NOTIFICATION, TAG_ANY);
+ if (!vp) {
+ vp = fr_pair_afrom_child_num(packet, dict_sim_root, FR_EAP_AKA_NOTIFICATION);
+ vp->vp_uint16 = FR_EAP_AKA_NOTIFICATION_VALUE_GENERAL_FAILURE;
+ fr_cursor_append(&cursor, vp);
+ }
+
+ /*
+ * Change the failure notification depending where
+ * we are in the state machine.
+ */
+ if (eap_aka_session->challenge_success) {
+ vp->vp_uint16 &= ~0x40000; /* Unset phase bit */
+ } else {
+ vp->vp_uint16 |= 0x40000; /* Set phase bit */
+ }
+
+ RDEBUG2("Sending AKA-Notification (%pV)", &vp->data);
+ eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
+
/*
* Set the subtype to notification
*/
vp->vp_uint32 = FR_EAP_AKA_SUBTYPE_VALUE_AKA_NOTIFICATION;
fr_cursor_append(&cursor, vp);
- vp = fr_pair_afrom_child_num(packet, dict_aka_root, FR_EAP_AKA_NOTIFICATION);
- vp->vp_uint32 = FR_EAP_AKA_NOTIFICATION_VALUE_GENERAL_FAILURE;
- fr_cursor_append(&cursor, vp);
+ /*
+ * If we're after the challenge phase
+ * then we need to include a MAC to
+ * protect notifications.
+ */
+ if (eap_aka_session->challenge_success) {
+ vp = fr_pair_afrom_child_num(packet, dict_sim_root, FR_EAP_AKA_MAC);
+ fr_pair_replace(&packet->vps, vp);
+ }
/*
* Encode the packet
case EAP_AKA_SERVER_IDENTITY:
if (eap_aka_send_identity_request(eap_session) < 0) {
notify_failure:
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
return;
}
break;
/*
* Send a general failure notification
*/
- case EAP_AKA_SERVER_GENERAL_FAILURE_NOTIFICATION:
+ case EAP_AKA_SERVER_FAILURE_NOTIFICATION:
if (eap_aka_send_eap_failure_notification(eap_session) < 0) { /* Fallback to EAP-Failure */
eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE);
}
default:
rad_assert(0); /* Invalid transition */
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
return;
}
}
case SIM_PERMANENT_ID_REQ:
eap_aka_state_enter(eap_session, EAP_AKA_SERVER_CHALLENGE);
// REDEBUG2("Failed to negotiate a usable identity");
-// eap_aka_state_enter(eap_session, eap_aka_session, EAP_AKA_SERVER_GENERAL_FAILURE_NOTIFICATION);
+// eap_aka_state_enter(eap_session, eap_aka_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
break;
case SIM_NO_ID_REQ:
RDEBUG2("EAP-AKA-RES matches XRES");
+ eap_aka_session->challenge_success = true;
+
/*
* If the peer wants a Success notification, then
* send a success notification, otherwise send a
*/
if (ret < 0) {
RPEDEBUG2("Failed decoding EAP-AKA attributes");
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
return RLM_MODULE_HANDLED; /* We need to process more packets */
}
subtype_vp = fr_pair_find_by_child_num(vps, dict_aka_root, FR_EAP_AKA_SUBTYPE, TAG_ANY);
if (!subtype_vp) {
REDEBUG("Missing EAP-AKA-Subtype");
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
return RLM_MODULE_HANDLED; /* We need to process more packets */
}
subtype = subtype_vp->vp_uint32;
switch (subtype) {
case EAP_AKA_IDENTITY:
if (process_eap_aka_identity(eap_session, vps) == 0) return RLM_MODULE_HANDLED;
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
return RLM_MODULE_HANDLED; /* We need to process more packets */
/*
vp = fr_pair_afrom_child_num(vps, dict_aka_root, FR_EAP_AKA_NOTIFICATION);
if (!vp) {
REDEBUG2("Received AKA-Notification with no notification code");
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
return RLM_MODULE_HANDLED; /* We need to process more packets */
}
* send an EAP-Failure in this case.
*/
REDEBUG("Unexpected subtype %pV", &subtype_vp->data);
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
return RLM_MODULE_HANDLED; /* We need to process more packets */
}
switch (subtype) {
case EAP_AKA_CHALLENGE:
if (process_eap_aka_challenge(eap_session, vps) == 0) return RLM_MODULE_HANDLED;
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
return RLM_MODULE_HANDLED; /* We need to process more packets */
case EAP_AKA_SYNCHRONIZATION_FAILURE:
REDEBUG("EAP-AKA Peer synchronization failure"); /* We can't handle these yet */
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
return RLM_MODULE_HANDLED; /* We need to process more packets */
/*
/*
* Peer acked our failure
*/
- case EAP_AKA_SERVER_GENERAL_FAILURE_NOTIFICATION:
+ case EAP_AKA_SERVER_FAILURE_NOTIFICATION:
switch (subtype) {
case EAP_AKA_NOTIFICATION:
RDEBUG2("AKA-Notification ACKed, sending EAP-Failure");
*/
default:
rad_assert(0);
- eap_aka_state_enter(eap_session, EAP_AKA_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
return RLM_MODULE_HANDLED; /* We need to process more packets */
}
}
EAP_SIM_SERVER_CHALLENGE,
EAP_SIM_SERVER_SUCCESS_NOTIFICATION,
EAP_SIM_SERVER_SUCCESS,
- EAP_SIM_SERVER_GENERAL_FAILURE_NOTIFICATION,
+ EAP_SIM_SERVER_FAILURE_NOTIFICATION,
EAP_SIM_SERVER_FAILURE,
EAP_SIM_SERVER_MAX_STATES
} eap_sim_server_state_t;
-typedef struct eap_aka_session {
+typedef struct {
eap_sim_server_state_t state; //!< Current session state.
+ bool challenge_success; //!< Whether we received the correct
+ ///< challenge response.
+
fr_sim_keys_t keys; //!< Various EAP-AKA keys.
fr_sim_id_req_type_t id_req; //!< The type of identity we're requesting
///< or previously requested.
+
bool request_identity; //!< Always send an identity request before a
///< challenge.
{ "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-NOTIFICATION", EAP_SIM_SERVER_FAILURE_NOTIFICATION },
{ "FAILURE", EAP_SIM_SERVER_FAILURE },
{ NULL }
};
/*
* build a reply to be sent.
*/
-static int eap_sim_compose(eap_session_t *eap_session)
+static int eap_sim_compose(eap_session_t *eap_session, uint8_t const *hmac_extra, size_t hmac_extra_len)
{
eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
vp_cursor_t cursor;
.hmac_md = EVP_sha1(),
.eap_packet = eap_session->this_round->request,
- .hmac_extra = eap_sim_session->keys.gsm.nonce_mt,
- .hmac_extra_len = sizeof(eap_sim_session->keys.gsm.nonce_mt)
+ .hmac_extra = hmac_extra,
+ .hmac_extra_len = hmac_extra_len
};
ssize_t ret;
/*
* Encode the packet
*/
- if (eap_sim_compose(eap_session) < 0) {
+ if (eap_sim_compose(eap_session, NULL, 0) < 0) {
fr_pair_list_free(&packet->vps);
return -1;
}
*/
static int eap_sim_send_challenge(eap_session_t *eap_session)
{
- static uint8_t hmac_zero[16] = { 0x00 };
-
REQUEST *request = eap_session->request;
eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
VALUE_PAIR **to_peer, *vp;
* 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_peer, vp);
/*
* Encode the packet
*/
- if (eap_sim_compose(eap_session) < 0) {
+ if (eap_sim_compose(eap_session,
+ eap_sim_session->keys.gsm.nonce_mt, sizeof(eap_sim_session->keys.gsm.nonce_mt)) < 0) {
fr_pair_list_free(&packet->vps);
return -1;
}
*/
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;
+ REQUEST *request = eap_session->request;
+ RADIUS_PACKET *packet = eap_session->request->reply;
+ eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
+ fr_cursor_t cursor;
+ VALUE_PAIR *vp;
RDEBUG2("Sending SIM-Notification (Success)");
eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
+ if (!fr_cond_assert(eap_sim_session->challenge_success)) return -1;
+
fr_cursor_init(&cursor, &packet->vps);
/*
vp->vp_uint32 = FR_EAP_SIM_NOTIFICATION_VALUE_SUCCESS;
fr_cursor_append(&cursor, vp);
+ /*
+ * 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_replace(&packet->vps, vp);
+
/*
* Encode the packet
*/
- if (eap_sim_compose(eap_session) < 0) {
+ if (eap_sim_compose(eap_session, NULL, 0) < 0) {
fr_pair_list_free(&packet->vps);
return -1;
}
*/
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;
+ REQUEST *request = eap_session->request;
+ RADIUS_PACKET *packet = eap_session->request->reply;
+ fr_cursor_t cursor;
+ VALUE_PAIR *vp;
+ eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
fr_cursor_init(&cursor, &packet->vps);
+ vp = fr_pair_find_by_child_num(packet->vps, dict_sim_root, FR_EAP_SIM_NOTIFICATION, TAG_ANY);
+ if (!vp) {
+ vp = fr_pair_afrom_child_num(packet, dict_sim_root, FR_EAP_SIM_NOTIFICATION);
+ vp->vp_uint16 = FR_EAP_SIM_NOTIFICATION_VALUE_GENERAL_FAILURE;
+ fr_cursor_append(&cursor, vp);
+ }
+
+ /*
+ * Change the failure notification depending where
+ * we are in the state machine.
+ */
+ if (eap_sim_session->challenge_success) {
+ vp->vp_uint16 &= ~0x40000; /* Unset phase bit */
+ } else {
+ vp->vp_uint16 |= 0x40000; /* Set phase bit */
+ }
+
+ RDEBUG2("Sending SIM-Notification (%pV)", &vp->data);
+ eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
+
/*
* Set the subtype to notification
*/
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);
+ /*
+ * If we're after the challenge phase
+ * then we need to include a MAC to
+ * protect notifications.
+ */
+ if (eap_sim_session->challenge_success) {
+ vp = fr_pair_afrom_child_num(packet, dict_sim_root, FR_EAP_SIM_MAC);
+ fr_pair_replace(&packet->vps, vp);
+ }
/*
* Encode the packet
*/
- if (eap_sim_compose(eap_session) < 0) {
+ if (eap_sim_compose(eap_session, NULL, 0) < 0) {
fr_pair_list_free(&packet->vps);
return -1;
}
case EAP_SIM_SERVER_START:
if (eap_sim_send_start(eap_session) < 0) {
notify_failure:
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
return;
}
break;
/*
* Send a general failure notification
*/
- case EAP_SIM_SERVER_GENERAL_FAILURE_NOTIFICATION:
+ case EAP_SIM_SERVER_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);
}
default:
rad_assert(0); /* Invalid transition */
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
return;
}
}
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);
+// eap_sim_state_enter(eap_session, eap_sim_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
break;
}
return -1;
}
+ eap_sim_session->challenge_success = true;
+
/*
* If the peer wants a Success notification, then
* send a success notification, otherwise send a
*/
if (ret < 0) {
RPEDEBUG2("Failed decoding EAP-SIM attributes");
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
return RLM_MODULE_HANDLED; /* We need to process more packets */
}
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);
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
return RLM_MODULE_HANDLED; /* We need to process more packets */
}
subtype = subtype_vp->vp_uint32;
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);
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
return RLM_MODULE_HANDLED; /* We need to process more packets */
/*
vp = fr_pair_afrom_child_num(from_peer, dict_sim_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);
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
return RLM_MODULE_HANDLED; /* We need to process more packets */
}
* 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);
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
return RLM_MODULE_HANDLED; /* We need to process more packets */
}
}
/*
* Peer acked our failure
*/
- case EAP_SIM_SERVER_GENERAL_FAILURE_NOTIFICATION:
+ case EAP_SIM_SERVER_FAILURE_NOTIFICATION:
switch (subtype) {
case EAP_SIM_NOTIFICATION:
RDEBUG2("SIM-Notification ACKed, sending EAP-Failure");
*/
default:
rad_assert(0);
- eap_sim_state_enter(eap_session, EAP_SIM_SERVER_GENERAL_FAILURE_NOTIFICATION);
+ eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
return RLM_MODULE_HANDLED; /* We need to process more packets */
}