/** 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; 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_da(packet->vps, attr_eap_sim_notification, TAG_ANY); if (!vp) { vp = fr_pair_afrom_da(packet, attr_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 &= ~0x4000; /* Unset phase bit */ } else { vp->vp_uint16 |= 0x4000; /* Set phase bit */ } vp->vp_uint16 &= ~0x8000; /* In both cases success bit should be low */ RDEBUG2("Sending SIM-Notification (%pV)", &vp->data); eap_session->this_round->request->code = FR_EAP_CODE_REQUEST; /* * Set the subtype to notification */ vp = fr_pair_afrom_da(packet, attr_eap_sim_subtype); vp->vp_uint16 = FR_EAP_SIM_SUBTYPE_VALUE_SIM_NOTIFICATION; 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_da(packet, attr_eap_sim_mac); fr_pair_replace(&packet->vps, vp); } /* * Encode the packet */ if (eap_sim_compose(eap_session, NULL, 0) < 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; 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); /* * Set the subtype to notification */ vp = fr_pair_afrom_da(packet, attr_eap_sim_subtype); vp->vp_uint16 = FR_EAP_SIM_SUBTYPE_VALUE_SIM_NOTIFICATION; fr_cursor_append(&cursor, vp); vp = fr_pair_afrom_da(packet, attr_eap_sim_notification); vp->vp_uint16 = 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_da(packet, attr_eap_sim_mac); fr_pair_replace(&packet->vps, vp); /* * Encode the packet */ if (eap_sim_compose(eap_session, NULL, 0) < 0) { fr_pair_list_free(&packet->vps); return -1; } return 0; }
/* * run the server state machine. */ static void eap_sim_stateenter(EAP_HANDLER *handler, struct eap_sim_server_state *ess, enum eapsim_serverstates newstate) { switch(newstate) { case eapsim_server_start: /* * send the EAP-SIM Start message, listing the * versions that we support. */ eap_sim_sendstart(handler); break; case eapsim_server_challenge: /* * send the EAP-SIM Challenge message. */ eap_sim_sendchallenge(handler); break; case eapsim_server_success: /* * send the EAP Success message */ eap_sim_sendsuccess(handler); handler->eap_ds->request->code = PW_EAP_SUCCESS; break; default: /* * nothing to do for this transition. */ break; } ess->state = newstate; /* build the target packet */ eap_sim_compose(handler); }
/** Run the server state machine * */ static void eap_sim_stateenter(eap_handler_t *handler, eap_sim_state_t *ess, enum eapsim_serverstates newstate) { switch(newstate) { /* * Send the EAP-SIM Start message, listing the versions that we support. */ case EAPSIM_SERVER_START: eap_sim_sendstart(handler); break; /* * Send the EAP-SIM Challenge message. */ case EAPSIM_SERVER_CHALLENGE: eap_sim_sendchallenge(handler); break; /* * Send the EAP Success message */ case EAPSIM_SERVER_SUCCESS: eap_sim_sendsuccess(handler); handler->eap_ds->request->code = PW_EAP_SUCCESS; break; /* * Nothing to do for this transition. */ default: break; } ess->state = newstate; /* build the target packet */ eap_sim_compose(handler); }
/** Send NONCE_S and re-key * */ static int eap_sim_send_reauthentication(eap_session_t *eap_session) { 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, *mk, *counter; RADIUS_PACKET *packet; rad_assert(eap_session->request != NULL); rad_assert(eap_session->request->reply); /* * to_peer is the data to the client */ packet = eap_session->request->reply; to_peer = &packet->vps; /* * If any of the session resumption inputs (on our side) * are missing or malformed, return an error code * and the state machine will jump to the start state. */ mk = fr_pair_find_by_da(request->control, attr_eap_sim_mk, TAG_ANY); if (!mk) { RWDEBUG2("Missing &control:EAP-SIM-MK, skipping session resumption"); return -1; } if (mk->vp_length != SIM_MK_SIZE) { RWDEBUG("&control:EAP-SIM-MK has incorrect length, expected %u bytes got %zu bytes", SIM_MK_SIZE, mk->vp_length); return -1; } counter = fr_pair_find_by_da(request->control, attr_eap_sim_counter, TAG_ANY); if (!counter) { RWDEBUG2("Missing &control:EAP-SIM-Counter, skipping session resumption"); return -1; } /* * All set, calculate keys! */ fr_sim_crypto_keys_init_kdf_0_reauth(&eap_sim_session->keys, mk->vp_octets, counter->vp_uint16); fr_sim_crypto_kdf_0_reauth(&eap_sim_session->keys); if (RDEBUG_ENABLED3) fr_sim_crypto_keys_log(request, &eap_sim_session->keys); RDEBUG2("Sending SIM-Reauthentication"); eap_session->this_round->request->code = FR_EAP_CODE_REQUEST; /* * Set subtype to challenge. */ vp = fr_pair_afrom_da(packet, attr_eap_sim_subtype); vp->vp_uint16 = EAP_SIM_REAUTH; fr_pair_replace(to_peer, vp); /* * Add nonce_s */ MEM(vp = fr_pair_afrom_da(packet, attr_eap_sim_nonce_s)); fr_pair_value_memcpy(vp, eap_sim_session->keys.reauth.nonce_s, sizeof(eap_sim_session->keys.reauth.nonce_s)); fr_pair_replace(to_peer, vp); /* * Indicate we'd like to use protected success messages */ if (eap_sim_session->send_result_ind) { MEM(vp = fr_pair_afrom_da(packet, attr_eap_sim_result_ind)); vp->vp_bool = true; fr_pair_replace(to_peer, vp); } /* * Need to include an AT_MAC attribute so that it will get * calculated. */ vp = fr_pair_afrom_da(packet, attr_eap_sim_mac); fr_pair_replace(to_peer, vp); /* * We've sent the challenge so the peer should now be able * to accept encrypted attributes. */ eap_sim_session->allow_encrypted = true; /* * Encode the packet */ if (eap_sim_compose(eap_session, NULL, 0) < 0) { fr_pair_list_free(&packet->vps); return -1; } return 0; }
/** Send the challenge itself * * Challenges will come from one of three places eventually: * * 1 from attributes like FR_EAP_SIM_RANDx * (these might be retrieved from a database) * * 2 from internally implemented SIM authenticators * (a simple one based upon XOR will be provided) * * 3 from some kind of SS7 interface. * * For now, they only come from attributes. * It might be that the best way to do 2/3 will be with a different * module to generate/calculate things. */ static int eap_sim_send_challenge(eap_session_t *eap_session) { 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; RADIUS_PACKET *packet; fr_sim_vector_src_t src = SIM_VECTOR_SRC_AUTO; rad_assert(eap_session->request != NULL); rad_assert(eap_session->request->reply); 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; } /* * 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); RDEBUG2("Sending SIM-Challenge"); eap_session->this_round->request->code = FR_EAP_CODE_REQUEST; /* * to_peer is the data to the client */ packet = eap_session->request->reply; to_peer = &packet->vps; /* * Okay, we got the challenges! Put them into attributes. */ MEM(vp = fr_pair_afrom_da(packet, attr_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_peer, vp); MEM(vp = fr_pair_afrom_da(packet, attr_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_peer, vp); MEM(vp = fr_pair_afrom_da(packet, attr_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_peer, vp); /* * Set subtype to challenge. */ vp = fr_pair_afrom_da(packet, attr_eap_sim_subtype); vp->vp_uint16 = EAP_SIM_CHALLENGE; fr_pair_replace(to_peer, vp); /* * Indicate we'd like to use protected success messages */ if (eap_sim_session->send_result_ind) { MEM(vp = fr_pair_afrom_da(packet, attr_eap_sim_result_ind)); vp->vp_bool = true; fr_pair_replace(to_peer, vp); } /* * Need to include an AT_MAC attribute so that it will get * calculated. */ vp = fr_pair_afrom_da(packet, attr_eap_sim_mac); fr_pair_replace(to_peer, vp); /* * We've sent the challenge so the peer should now be able * to accept encrypted attributes. */ eap_sim_session->allow_encrypted = true; /* * Encode the packet */ 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; } return 0; }
/* * build a reply to be sent. */ 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); fr_cursor_t cursor; fr_cursor_t to_encode; VALUE_PAIR *head = NULL, *vp; REQUEST *request = eap_session->request; fr_sim_encode_ctx_t encoder_ctx = { .root = fr_dict_root(dict_eap_sim), .keys = &eap_sim_session->keys, .iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, .iv_included = false, .hmac_md = EVP_sha1(), .eap_packet = eap_session->this_round->request, .hmac_extra = hmac_extra, .hmac_extra_len = hmac_extra_len }; ssize_t ret; /* we will set the ID on requests, since we have to HMAC it */ eap_session->this_round->set_request_id = true; fr_cursor_init(&cursor, &eap_session->request->reply->vps); fr_cursor_init(&to_encode, &head); while ((vp = fr_cursor_current(&cursor))) { if (!fr_dict_parent_common(fr_dict_root(dict_eap_sim), vp->da, true)) { fr_cursor_next(&cursor); continue; } vp = fr_cursor_remove(&cursor); /* * Silently discard encrypted attributes until * the peer should have k_encr. These can be * added by policy, and seem to cause * wpa_supplicant to fail if sent before the challenge. */ if (!eap_sim_session->allow_encrypted && fr_dict_parent_common(attr_eap_sim_encr_data, vp->da, true)) { RWDEBUG("Silently discarding &reply:%s: Encrypted attributes not allowed in this round", vp->da->name); talloc_free(vp); continue; } fr_cursor_append(&to_encode, vp); } RDEBUG2("Encoding EAP-SIM attributes"); log_request_pair_list(L_DBG_LVL_2, request, head, NULL); eap_session->this_round->request->type.num = FR_EAP_SIM; eap_session->this_round->request->id = eap_sim_session->sim_id++ & 0xff; eap_session->this_round->set_request_id = true; ret = fr_sim_encode(eap_session->request, head, &encoder_ctx); fr_cursor_head(&to_encode); fr_cursor_free_list(&to_encode); if (ret < 0) { RPEDEBUG("Failed encoding EAP-SIM data"); return -1; } return 0; } static int eap_sim_send_start(eap_session_t *eap_session) { REQUEST *request = eap_session->request; VALUE_PAIR **vps, *vp; uint16_t version; eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t); RADIUS_PACKET *packet; rad_assert(eap_session->request != NULL); rad_assert(eap_session->request->reply); RDEBUG2("Sending SIM-State"); eap_session->this_round->request->code = FR_EAP_CODE_REQUEST; eap_sim_session->allow_encrypted = false; /* In case this is after failed fast-resumption */ /* these are the outgoing attributes */ packet = eap_session->request->reply; vps = &packet->vps; rad_assert(vps != NULL); /* * Add appropriate TLVs for the EAP things we wish to send. */ vp = fr_pair_afrom_da(packet, attr_eap_sim_version_list); vp->vp_uint16 = EAP_SIM_VERSION; fr_pair_add(vps, vp); /* record it in the ess */ version = htons(EAP_SIM_VERSION); memcpy(eap_sim_session->keys.gsm.version_list, &version, sizeof(version)); eap_sim_session->keys.gsm.version_list_len = 2; /* * Select the right type of identity request attribute */ switch (eap_sim_session->id_req) { case SIM_ANY_ID_REQ: vp = fr_pair_afrom_da(packet, attr_eap_sim_any_id_req); break; case SIM_PERMANENT_ID_REQ: vp = fr_pair_afrom_da(packet, attr_eap_sim_permanent_id_req); break; case SIM_FULLAUTH_ID_REQ: vp = fr_pair_afrom_da(packet, attr_eap_sim_fullauth_id_req); break; default: rad_assert(0); } vp->vp_bool = true; fr_pair_replace(vps, vp); /* the SUBTYPE, set to start. */ vp = fr_pair_afrom_da(packet, attr_eap_sim_subtype); vp->vp_uint16 = EAP_SIM_START; fr_pair_replace(vps, vp); /* * Encode the packet */ if (eap_sim_compose(eap_session, NULL, 0) < 0) { fr_pair_list_free(&packet->vps); return -1; } return 0; }