diff --git a/lib/core/include/3gpp_types.h b/lib/core/include/3gpp_types.h index 014e93efb..ecaba1425 100644 --- a/lib/core/include/3gpp_types.h +++ b/lib/core/include/3gpp_types.h @@ -41,6 +41,7 @@ extern "C" { #define RAND_LEN 16 #define AUTN_LEN 16 +#define AUTS_LEN 14 #define MAX_RES_LEN 16 #define MAX_APN_LEN 100 diff --git a/lib/nas/nas_types.h b/lib/nas/nas_types.h index 717d2bca9..5a8a1ea53 100644 --- a/lib/nas/nas_types.h +++ b/lib/nas/nas_types.h @@ -229,7 +229,7 @@ ED4(c_uint8_t type:4;, * O TLV 16 */ typedef struct _nas_authentication_failure_parameter_t { c_uint8_t length; - c_uint8_t parameter[14]; + c_uint8_t auts[AUTS_LEN]; } __attribute__ ((packed)) nas_authentication_failure_parameter_t; /* 9.9.3.2 Authentication parameter AUTN diff --git a/src/hss/hss_context.c b/src/hss/hss_context.c index 82c0ff5fc..373df74d4 100644 --- a/src/hss/hss_context.c +++ b/src/hss/hss_context.c @@ -315,7 +315,7 @@ status_t hss_db_increment_sqn(char *imsi_bcd) bson_t *query = NULL; bson_t *update = NULL; bson_error_t error; - c_uint64_t max_sqn = 0x7ffffffffff; + c_uint64_t max_sqn = HSS_MAX_SQN; mutex_lock(self.db_lock); diff --git a/src/hss/hss_context.h b/src/hss/hss_context.h index 9cb9df1ad..f50bbe848 100644 --- a/src/hss/hss_context.h +++ b/src/hss/hss_context.h @@ -13,6 +13,8 @@ extern "C" { #define HSS_KEY_LEN 16 #define HSS_AMF_LEN 2 +#define HSS_MAX_SQN 0x7ffffffffff + typedef struct _hss_db_auth_info_t { c_uint8_t k[HSS_KEY_LEN]; c_uint8_t use_opc; diff --git a/src/hss/hss_fd_path.c b/src/hss/hss_fd_path.c index bd9a5921c..670329386 100644 --- a/src/hss/hss_fd_path.c +++ b/src/hss/hss_fd_path.c @@ -13,9 +13,6 @@ #include "hss_kdf.h" #include "milenage.h" -#define HSS_SQN_LEN 6 -#define HSS_AK_LEN 6 - /* handler for fallback cb */ static struct disp_hdl *hdl_s6a_fb = NULL; /* handler for Authentication-Information-Request cb */ @@ -38,6 +35,7 @@ static int hss_s6a_air_cb( struct msg **msg, struct avp *avp, struct session *session, void *opaque, enum disp_action *act) { struct msg *ans, *qry; + struct avp *avpch; struct avp *avp_e_utran_vector, *avp_xres, *avp_kasme, *avp_rand, *avp_autn; struct avp_hdr *hdr; union avp_value val; @@ -53,6 +51,9 @@ static int hss_s6a_air_cb( struct msg **msg, struct avp *avp, c_uint8_t kasme[SHA256_DIGEST_SIZE]; size_t xres_len = 8; +#define MAC_S_LEN 8 + c_uint8_t mac_s[MAC_S_LEN]; + hss_db_auth_info_t auth_info; c_uint8_t zero[RAND_LEN]; status_t rv; @@ -84,6 +85,43 @@ static int hss_s6a_air_cb( struct msg **msg, struct avp *avp, core_generate_random_bytes(auth_info.rand, RAND_LEN); } + if (auth_info.use_opc) + memcpy(opc, auth_info.opc, sizeof(opc)); + else + milenage_opc(auth_info.k, auth_info.op, opc); + + CHECK_FCT( fd_msg_search_avp(qry, s6a_req_eutran_auth_info, &avp) ); + if (avp) + { + CHECK_FCT( fd_avp_search_avp(avp, + s6a_re_synchronization_info, &avpch) ); + if (avpch) + { + CHECK_FCT( fd_msg_avp_hdr(avpch, &hdr) ); + hss_kdf_sqn(opc, auth_info.k, auth_info.amf, + hdr->avp_value->os.data, sqn, mac_s); + if (memcmp(mac_s, hdr->avp_value->os.data + + RAND_LEN + HSS_SQN_LEN, MAC_S_LEN) == 0) + { + core_generate_random_bytes(auth_info.rand, RAND_LEN); + auth_info.sqn = core_buffer_to_uint64(sqn, HSS_SQN_LEN); + auth_info.sqn = (auth_info.sqn + 32) & HSS_MAX_SQN; + } + else + { + d_error("Re-synch MAC failed for IMSI:`%s`", imsi_bcd); + d_print("MAC_S: "); + d_print_hex(mac_s, MAC_S_LEN); + d_print_hex(hdr->avp_value->os.data + + RAND_LEN + HSS_SQN_LEN, MAC_S_LEN); + d_print("SQN: "); + d_print_hex(sqn, HSS_SQN_LEN); + result_code = S6A_DIAMETER_AUTHENTICATION_DATA_UNAVAILABLE; + goto out; + } + } + } + rv = hss_db_update_rand_and_sqn(imsi_bcd, auth_info.rand, auth_info.sqn); if (rv != CORE_OK) { @@ -106,10 +144,6 @@ static int hss_s6a_air_cb( struct msg **msg, struct avp *avp, memcpy(visited_plmn_id, hdr->avp_value->os.data, hdr->avp_value->os.len); #endif - if (auth_info.use_opc) - memcpy(opc, auth_info.opc, sizeof(opc)); - else - milenage_opc(auth_info.k, auth_info.op, opc); milenage_generate(opc, auth_info.amf, auth_info.k, core_uint64_to_buffer(auth_info.sqn, HSS_SQN_LEN, sqn), auth_info.rand, autn, ik, ck, ak, xres, &xres_len); diff --git a/src/hss/hss_kdf.c b/src/hss/hss_kdf.c index 9ced22d38..0339a12c8 100644 --- a/src/hss/hss_kdf.c +++ b/src/hss/hss_kdf.c @@ -1,6 +1,11 @@ #define TRACE_MODULE _hss_kdf +#include "core_debug.h" #include "core_sha2_hmac.h" +#include "3gpp_types.h" + +#include "hss_kdf.h" +#include "milenage.h" #define FC_VALUE 0x10 @@ -27,3 +32,19 @@ void hss_kdf_kasme(const c_uint8_t *ck, const c_uint8_t *ik, hmac_sha256(k, 32, s, 14, kasme, 32); } + +void hss_kdf_sqn( + const c_uint8_t *opc, const c_uint8_t *k, + const c_uint8_t *amf, const c_uint8_t *auts, + c_uint8_t *sqn_ms, c_uint8_t *mac_s) +{ + int i; + c_uint8_t ak[HSS_AK_LEN]; + const c_uint8_t *rand = auts; + const c_uint8_t *conc_sqn_ms = auts + RAND_LEN; + + milenage_f2345(opc, k, rand, NULL, NULL, NULL, NULL, ak); + for (i = 0; i < HSS_SQN_LEN; i++) + sqn_ms[i] = ak[i] ^ conc_sqn_ms[i]; + milenage_f1(opc, k, auts, sqn_ms, amf, NULL, mac_s); +} diff --git a/src/hss/hss_kdf.h b/src/hss/hss_kdf.h index e39a2c3ee..0de5cf13f 100644 --- a/src/hss/hss_kdf.h +++ b/src/hss/hss_kdf.h @@ -3,8 +3,16 @@ #include "core.h" -void hss_kdf_kasme(const c_uint8_t *ck, const c_uint8_t *ik, +#define HSS_SQN_LEN 6 +#define HSS_AK_LEN 6 + +CORE_DECLARE(void) hss_kdf_kasme(const c_uint8_t *ck, const c_uint8_t *ik, const c_uint8_t plmn_id[3], const c_uint8_t *sqn, const c_uint8_t *ak, c_uint8_t *kasme); +CORE_DECLARE(void) hss_kdf_sqn( + const c_uint8_t *opc, const c_uint8_t *k, + const c_uint8_t *amf, const c_uint8_t *auts, + c_uint8_t *sqn_ms, c_uint8_t *mac_s); + #endif /* __HSS_KDF_H__ */ diff --git a/src/mme/emm_handler.c b/src/mme/emm_handler.c index 5fae9573f..6ceaba1b7 100644 --- a/src/mme/emm_handler.c +++ b/src/mme/emm_handler.c @@ -154,7 +154,7 @@ void emm_handle_attach_request( } else { - mme_s6a_send_air(mme_ue); + mme_s6a_send_air(mme_ue, NULL); } } } @@ -270,7 +270,7 @@ void emm_handle_identity_response( } else { - mme_s6a_send_air(mme_ue); + mme_s6a_send_air(mme_ue, NULL); } } } @@ -286,7 +286,7 @@ void emm_handle_identity_response( { if (MME_HAVE_SGW_S11_PATH(mme_ue)) { - mme_s6a_send_air(mme_ue); + mme_s6a_send_air(mme_ue, NULL); } else { @@ -307,7 +307,7 @@ void emm_handle_identity_response( { if (MME_HAVE_SGW_S11_PATH(mme_ue)) { - mme_s6a_send_air(mme_ue); + mme_s6a_send_air(mme_ue, NULL); } else { @@ -410,7 +410,7 @@ void emm_handle_service_request( { if (MME_HAVE_SGW_S11_PATH(mme_ue)) { - mme_s6a_send_air(mme_ue); + mme_s6a_send_air(mme_ue, NULL); } else { @@ -547,7 +547,7 @@ void emm_handle_tau_request( if (MME_HAVE_SGW_S11_PATH(mme_ue)) { /* Re-authentication */ - mme_s6a_send_air(mme_ue); + mme_s6a_send_air(mme_ue, NULL); } else { diff --git a/src/mme/emm_sm.c b/src/mme/emm_sm.c index 60d46f0a1..31670e242 100644 --- a/src/mme/emm_sm.c +++ b/src/mme/emm_sm.c @@ -303,8 +303,6 @@ void emm_state_authentication(fsm_t *s, event_t *e) &authentication_response-> authentication_response_parameter; - d_assert(mme_ue, return, "Null param"); - if (authentication_response_parameter->length != mme_ue->xres_len || memcmp(authentication_response_parameter->res, @@ -324,6 +322,18 @@ void emm_state_authentication(fsm_t *s, event_t *e) } break; } + case NAS_AUTHENTICATION_FAILURE: + { + nas_authentication_failure_t *authentication_failure = + &message->emm.authentication_failure; + nas_authentication_failure_parameter_t + *authentication_failure_parameter = + &authentication_failure-> + authentication_failure_parameter; + + mme_s6a_send_air(mme_ue, authentication_failure_parameter); + break; + } case NAS_EMM_STATUS: { emm_handle_emm_status(mme_ue, &message->emm.emm_status); diff --git a/src/mme/mme_context.h b/src/mme/mme_context.h index f65f12e1a..9107445b6 100644 --- a/src/mme/mme_context.h +++ b/src/mme/mme_context.h @@ -213,6 +213,7 @@ struct _mme_ue_t { c_uint8_t xres[MAX_RES_LEN]; c_uint8_t xres_len; c_uint8_t kasme[SHA256_DIGEST_SIZE]; + c_uint8_t rand[RAND_LEN]; c_uint8_t knas_int[SHA256_DIGEST_SIZE/2]; c_uint8_t knas_enc[SHA256_DIGEST_SIZE/2]; c_uint32_t dl_count; diff --git a/src/mme/mme_fd_path.c b/src/mme/mme_fd_path.c index be0147b1d..1221d1601 100644 --- a/src/mme/mme_fd_path.c +++ b/src/mme/mme_fd_path.c @@ -26,7 +26,8 @@ static void mme_s6a_aia_cb(void *data, struct msg **msg); static void mme_s6a_ula_cb(void *data, struct msg **msg); /* MME Sends Authentication Information Request to HSS */ -void mme_s6a_send_air(mme_ue_t *mme_ue) +void mme_s6a_send_air(mme_ue_t *mme_ue, + nas_authentication_failure_parameter_t *authentication_failure_parameter) { struct msg *req = NULL; struct avp *avp; @@ -35,6 +36,8 @@ void mme_s6a_send_air(mme_ue_t *mme_ue) struct sess_state *mi = NULL, *svg; struct session *session = NULL; + c_uint8_t resync[AUTS_LEN + RAND_LEN]; + d_assert(mme_ue, return, "Null param"); /* Clear Security Context */ @@ -93,6 +96,19 @@ void mme_s6a_send_air(mme_ue_t *mme_ue) CHECK_FCT_DO( fd_msg_avp_setvalue(avpch, &val), goto out ); CHECK_FCT_DO( fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avpch), goto out ); + if (authentication_failure_parameter) + { + CHECK_FCT_DO( fd_msg_avp_new(s6a_re_synchronization_info, 0, &avpch), + goto out ); + memcpy(resync, mme_ue->rand, RAND_LEN); + memcpy(resync+RAND_LEN, + authentication_failure_parameter->auts, AUTS_LEN); + val.os.len = RAND_LEN+AUTS_LEN; + val.os.data = resync; + CHECK_FCT_DO( fd_msg_avp_setvalue(avpch, &val), goto out ); + CHECK_FCT_DO( fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avpch), goto out ); + } + CHECK_FCT_DO( fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp), goto out ); /* Set the Visited-PLMN-Id AVP */ diff --git a/src/mme/mme_fd_path.h b/src/mme/mme_fd_path.h index c19e600cc..fd629f582 100644 --- a/src/mme/mme_fd_path.h +++ b/src/mme/mme_fd_path.h @@ -13,7 +13,8 @@ CORE_DECLARE(int) mme_fd_init(void); CORE_DECLARE(void) mme_fd_final(void); /* MME Sends Authentication Information Request to HSS */ -CORE_DECLARE(void) mme_s6a_send_air(mme_ue_t *mme_ue); +CORE_DECLARE(void) mme_s6a_send_air(mme_ue_t *mme_ue, + nas_authentication_failure_parameter_t *authentication_failure_parameter); /* MME Sends Update Location Request to HSS */ CORE_DECLARE(void) mme_s6a_send_ulr(mme_ue_t *mme_ue); diff --git a/src/mme/mme_s11_handler.c b/src/mme/mme_s11_handler.c index 1daa523a2..fd1cc281b 100644 --- a/src/mme/mme_s11_handler.c +++ b/src/mme/mme_s11_handler.c @@ -170,7 +170,7 @@ void mme_s11_handle_delete_session_response( { GTP_COUNTER_CHECK(mme_ue, GTP_COUNTER_DELETE_SESSION, CLEAR_SGW_S11_PATH(mme_ue); - mme_s6a_send_air(mme_ue); + mme_s6a_send_air(mme_ue, NULL); ); mme_sess_remove(sess); diff --git a/src/mme/mme_s6a_handler.c b/src/mme/mme_s6a_handler.c index ed45f5606..53dc28178 100644 --- a/src/mme/mme_s6a_handler.c +++ b/src/mme/mme_s6a_handler.c @@ -22,6 +22,7 @@ void mme_s6a_handle_aia(mme_ue_t *mme_ue, s6a_aia_message_t *aia_message) mme_ue->xres_len = e_utran_vector->xres_len; memcpy(mme_ue->xres, e_utran_vector->xres, mme_ue->xres_len); memcpy(mme_ue->kasme, e_utran_vector->kasme, SHA256_DIGEST_SIZE); + memcpy(mme_ue->rand, e_utran_vector->rand, RAND_LEN); rv = nas_send_authentication_request(mme_ue, e_utran_vector); d_assert(rv == CORE_OK,, "nas send failed"); diff --git a/test/attach_test.c b/test/attach_test.c index 7d3c83785..874561216 100644 --- a/test/attach_test.c +++ b/test/attach_test.c @@ -92,46 +92,6 @@ static void attach_test1(abts_case *tc, void *data) "}, " "\"__v\" : 0 " "}"; - const char *json2 = - "{" - "\"_id\" : { \"$oid\" : \"697223158b8861d7605378c6\" }, " - "\"imsi\" : \"001010123456815\", " - "\"pdn\" : [" - "{" - "\"apn\" : \"internet\", " - "\"_id\" : { \"$oid\" : \"697223158b8861d7605378c7\" }, " - "\"ambr\" : {" - "\"uplink\" : { \"$numberLong\" : \"1024000\" }, " - "\"downlink\" : { \"$numberLong\" : \"1024000\" } " - "}," - "\"qos\" : { " - "\"qci\" : 9, " - "\"arp\" : { " - "\"priority_level\" : 8," - "\"pre_emption_vulnerability\" : 1, " - "\"pre_emption_capability\" : 1" - "} " - "}, " - "\"type\" : 2" - "}" - "]," - "\"ambr\" : { " - "\"uplink\" : { \"$numberLong\" : \"1024000\" }, " - "\"downlink\" : { \"$numberLong\" : \"1024000\" } " - "}," - "\"subscribed_rau_tau_timer\" : 12," - "\"network_access_mode\" : 2, " - "\"subscriber_status\" : 0, " - "\"access_restriction_data\" : 32, " - "\"security\" : { " - "\"k\" : \"465B5CE8 B199B49F AA5F0A2E E238A6BC\", " - "\"op\" : \"5F1D289C 5D354D0A 140C2548 F5F3E3BA\", " - "\"amf\" : \"8000\", " - "\"sqn\" : { \"$numberLong\" : \"64\" }, " - "\"rand\" : \"20080C38 18183B52 2614162C 07601D0D\" " - "}, " - "\"__v\" : 0 " - "}"; core_sleep(time_from_msec(300)); @@ -180,21 +140,6 @@ static void attach_test1(abts_case *tc, void *data) } while (count == 0); bson_destroy(doc); - doc = bson_new_from_json((const uint8_t *)json2, -1, &error);; - ABTS_PTR_NOTNULL(tc, doc); - ABTS_TRUE(tc, mongoc_collection_insert(collection, - MONGOC_INSERT_NONE, doc, NULL, &error)); - bson_destroy(doc); - - doc = BCON_NEW("imsi", BCON_UTF8("001010123456815")); - ABTS_PTR_NOTNULL(tc, doc); - do - { - count = mongoc_collection_count ( - collection, MONGOC_QUERY_NONE, doc, 0, 0, NULL, &error); - } while (count == 0); - bson_destroy(doc); - /*********************************************************************** * Attach Request : Known IMSI, Integrity Protected, No Security Context * Send Initial-UE Message + Attach Request + PDN Connectivity */ @@ -422,12 +367,6 @@ static void attach_test1(abts_case *tc, void *data) MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) bson_destroy(doc); - doc = BCON_NEW("imsi", BCON_UTF8("001010123456815")); - ABTS_PTR_NOTNULL(tc, doc); - ABTS_TRUE(tc, mongoc_collection_remove(collection, - MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) - bson_destroy(doc); - mongoc_collection_destroy(collection); /* Send Identity Response */ @@ -702,6 +641,20 @@ static void attach_test2(abts_case *tc, void *data) ABTS_INT_EQUAL(tc, CORE_OK, rv); pkbuf_free(recvbuf); + /* Send Authentication Authentication Failure */ + rv = tests1ap_build_authentication_failure(&sendbuf, msgindex+2); + ABTS_INT_EQUAL(tc, CORE_OK, rv); + rv = tests1ap_enb_send(sock, sendbuf); + ABTS_INT_EQUAL(tc, CORE_OK, rv); + + /* Receive Authentication Request */ + recvbuf = pkbuf_alloc(0, MAX_SDU_LEN); + rv = tests1ap_enb_read(sock, recvbuf); + ABTS_INT_EQUAL(tc, CORE_OK, rv); + pkbuf_free(recvbuf); + + core_sleep(time_from_msec(300)); + doc = BCON_NEW("imsi", BCON_UTF8("001010123456826")); ABTS_PTR_NOTNULL(tc, doc); ABTS_TRUE(tc, mongoc_collection_remove(collection, diff --git a/test/testpacket.c b/test/testpacket.c index 81bdacec9..d0ba80001 100644 --- a/test/testpacket.c +++ b/test/testpacket.c @@ -341,6 +341,58 @@ status_t tests1ap_build_authentication_response(pkbuf_t **pkbuf, int i) return CORE_OK; } +status_t tests1ap_build_authentication_failure(pkbuf_t **pkbuf, int i) +{ + char *payload[TESTS1AP_MAX_MESSAGE] = { + "", + "", + "", + + "", + "", + "000d" + "403d000005000000 0200030008000200 21001a001413075c 15300e9a73df7ef8" + "05f893f312a930a9 8f006440080000f1 100019b010004340 060000f1100001", + + "", + "", + "", + + "", + "", + "", + + }; + + c_uint16_t len[TESTS1AP_MAX_MESSAGE] = { + 0, + 0, + 0, + + 0, + 0, + 65, + + 0, + 0, + 0, + + 0, + 0, + 0, + }; + char hexbuf[MAX_SDU_LEN]; + + *pkbuf = pkbuf_alloc(0, MAX_SDU_LEN); + if (!(*pkbuf)) return CORE_ERROR; + + (*pkbuf)->len = len[i]; + memcpy((*pkbuf)->payload, CORE_HEX(payload[i], strlen(payload[i]), hexbuf), + (*pkbuf)->len); + + return CORE_OK; +} + status_t tests1ap_build_security_mode_complete(pkbuf_t **pkbuf, int i) { char *payload[TESTS1AP_MAX_MESSAGE] = { diff --git a/test/testpacket.h b/test/testpacket.h index 805fd5b00..93ea5c694 100644 --- a/test/testpacket.h +++ b/test/testpacket.h @@ -24,6 +24,8 @@ CORE_DECLARE(status_t) tests1ap_build_initial_ue_msg(pkbuf_t **pkbuf, int i); CORE_DECLARE(status_t) tests1ap_build_identity_response(pkbuf_t **pkbuf, int i); CORE_DECLARE(status_t) tests1ap_build_authentication_response( pkbuf_t **pkbuf, int i); +CORE_DECLARE(status_t) tests1ap_build_authentication_failure( + pkbuf_t **pkbuf, int i); CORE_DECLARE(status_t) tests1ap_build_security_mode_complete( pkbuf_t **pkbuf, int i); CORE_DECLARE(status_t) tests1ap_build_esm_information_response(