/* * Copyright (C) 2019,2020 by Sukchan Lee * * This file is part of Open5GS. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "sbi-path.h" #include "nnrf-handler.h" #include "nudm-handler.h" bool udm_nudm_ueau_handle_get( udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg) { OpenAPI_authentication_info_request_t *AuthenticationInfoRequest = NULL; OpenAPI_resynchronization_info_t *ResynchronizationInfo = NULL; char *serving_network_name = NULL; char *ausf_instance_id = NULL; ogs_assert(udm_ue); ogs_assert(stream); ogs_assert(recvmsg); AuthenticationInfoRequest = recvmsg->AuthenticationInfoRequest; if (!AuthenticationInfoRequest) { ogs_error("[%s] No AuthenticationInfoRequest", udm_ue->suci); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, recvmsg, "No AuthenticationInfoRequest", udm_ue->suci)); return false; } serving_network_name = AuthenticationInfoRequest->serving_network_name; if (!AuthenticationInfoRequest) { ogs_error("[%s] No servingNetworkName", udm_ue->suci); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, recvmsg, "No servingNetworkName", udm_ue->suci)); return false; } if (udm_ue->serving_network_name) ogs_free(udm_ue->serving_network_name); udm_ue->serving_network_name = ogs_strdup(serving_network_name); ogs_assert(udm_ue->serving_network_name); ausf_instance_id = AuthenticationInfoRequest->ausf_instance_id; if (!AuthenticationInfoRequest) { ogs_error("[%s] No ausfInstanceId", udm_ue->suci); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, recvmsg, "No ausfInstanceId", udm_ue->suci)); return false; } if (udm_ue->ausf_instance_id) ogs_free(udm_ue->ausf_instance_id); udm_ue->ausf_instance_id = ogs_strdup(ausf_instance_id); ogs_assert(udm_ue->ausf_instance_id); ResynchronizationInfo = AuthenticationInfoRequest->resynchronization_info; if (!ResynchronizationInfo) { ogs_assert(true == udm_sbi_discover_and_send(OGS_SBI_SERVICE_TYPE_NUDR_DR, NULL, udm_nudr_dr_build_authentication_subscription, udm_ue, stream, NULL)); } else { uint8_t rand[OGS_RAND_LEN]; uint8_t auts[OGS_AUTS_LEN]; uint8_t sqn_ms[OGS_SQN_LEN]; uint8_t mac_s[OGS_MAC_S_LEN]; uint64_t sqn = 0; if (!ResynchronizationInfo->rand) { ogs_error("[%s] No RAND", udm_ue->suci); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, recvmsg, "No RAND", udm_ue->suci)); return false; } if (!ResynchronizationInfo->auts) { ogs_error("[%s] No AUTS", udm_ue->suci); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, recvmsg, "No AUTS", udm_ue->suci)); return false; } ogs_ascii_to_hex( ResynchronizationInfo->rand, strlen(ResynchronizationInfo->rand), rand, sizeof(rand)); ogs_ascii_to_hex( ResynchronizationInfo->auts, strlen(ResynchronizationInfo->auts), auts, sizeof(auts)); if (memcmp(udm_ue->rand, rand, OGS_RAND_LEN) != 0) { ogs_error("[%s] Invalid RAND", udm_ue->suci); ogs_log_hexdump(OGS_LOG_ERROR, udm_ue->rand, sizeof(udm_ue)->rand); ogs_log_hexdump(OGS_LOG_ERROR, rand, sizeof(rand)); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, recvmsg, "Invalid RAND", udm_ue->suci)); return false; } ogs_auc_sqn(udm_ue->opc, udm_ue->k, rand, auts, sqn_ms, mac_s); if (memcmp(auts + OGS_SQN_LEN, mac_s, OGS_MAC_S_LEN) != 0) { ogs_error("[%s] Re-synch MAC failed", udm_ue->suci); ogs_log_print(OGS_LOG_ERROR, "[MAC_S] "); ogs_log_hexdump(OGS_LOG_ERROR, mac_s, OGS_MAC_S_LEN); ogs_log_hexdump(OGS_LOG_ERROR, auts + OGS_SQN_LEN, OGS_MAC_S_LEN); ogs_log_hexdump(OGS_LOG_ERROR, sqn_ms, OGS_SQN_LEN); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_UNAUTHORIZED, recvmsg, "Re-sync MAC failed", udm_ue->suci)); return false; } sqn = ogs_buffer_to_uint64(sqn_ms, OGS_SQN_LEN); /* 33.102 C.3.4 Guide : IND + 1 * * General rule: index values IND used in the array scheme, * according to Annex C.1.2, shall be allocated cyclically * within its range 0, ... , a-1. This means that the index value IND * used with the previously generated authentication vector is stored * in SQN HE , and the next authentication vector shall use index * value IND +1 mod a. * * In future releases there may be additional information * about the requesting node identity. If this information is * available it is recommended to use it in the following way: * * - If the new request comes from the same serving node * as the previous request, then the index value used for * the new request shall be the same as was used for * the previous request. */ sqn = (sqn + 32 + 1) & OGS_MAX_SQN; ogs_uint64_to_buffer(sqn, OGS_SQN_LEN, udm_ue->sqn); ogs_assert(true == udm_sbi_discover_and_send(OGS_SBI_SERVICE_TYPE_NUDR_DR, NULL, udm_nudr_dr_build_authentication_subscription, udm_ue, stream, udm_ue->sqn)); } return true; } bool udm_nudm_ueau_handle_result_confirmation_inform( udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, ogs_sbi_message_t *message) { OpenAPI_auth_event_t *AuthEvent = NULL; ogs_assert(udm_ue); ogs_assert(stream); ogs_assert(message); AuthEvent = message->AuthEvent; if (!AuthEvent) { ogs_error("[%s] No AuthEvent", udm_ue->suci); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, message, "No AuthEvent", udm_ue->suci)); return false; } if (!AuthEvent->nf_instance_id) { ogs_error("[%s] No nfInstanceId", udm_ue->suci); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, message, "No nfInstanceId", udm_ue->suci)); return false; } if (!AuthEvent->success) { ogs_error("[%s] No success", udm_ue->suci); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, message, "No success", udm_ue->suci)); return false; } if (!AuthEvent->time_stamp) { ogs_error("[%s] No timeStamp", udm_ue->suci); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, message, "No timeStamp", udm_ue->suci)); return false; } if (!AuthEvent->auth_type) { ogs_error("[%s] No authType", udm_ue->suci); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, message, "No authType", udm_ue->suci)); return false; } if (!AuthEvent->serving_network_name) { ogs_error("[%s] No servingNetworkName", udm_ue->suci); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, message, "No servingNetworkName", udm_ue->suci)); return false; } udm_ue->auth_event = OpenAPI_auth_event_copy( udm_ue->auth_event, message->AuthEvent); ogs_assert(true == udm_sbi_discover_and_send(OGS_SBI_SERVICE_TYPE_NUDR_DR, NULL, udm_nudr_dr_build_update_authentication_status, udm_ue, stream, NULL)); return true; } bool udm_nudm_uecm_handle_registration( udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, ogs_sbi_message_t *message) { OpenAPI_amf3_gpp_access_registration_t *Amf3GppAccessRegistration = NULL; OpenAPI_guami_t *Guami = NULL; ogs_assert(udm_ue); ogs_assert(stream); ogs_assert(message); Amf3GppAccessRegistration = message->Amf3GppAccessRegistration; if (!Amf3GppAccessRegistration) { ogs_error("[%s] No Amf3GppAccessRegistration", udm_ue->supi); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, message, "No Amf3GppAccessRegistration", udm_ue->supi)); return false; } if (!Amf3GppAccessRegistration->dereg_callback_uri) { ogs_error("[%s] No dregCallbackUri", udm_ue->supi); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, message, "No dregCallbackUri", udm_ue->supi)); return false; } Guami = Amf3GppAccessRegistration->guami; if (!Guami) { ogs_error("[%s] No Guami", udm_ue->supi); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, message, "No Guami", udm_ue->supi)); return false; } if (!Guami->amf_id) { ogs_error("[%s] No Guami.AmfId", udm_ue->supi); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, message, "No Guami.AmfId", udm_ue->supi)); return false; } if (!Guami->plmn_id) { ogs_error("[%s] No PlmnId", udm_ue->supi); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, message, "No PlmnId", udm_ue->supi)); return false; } if (!Guami->plmn_id->mnc) { ogs_error("[%s] No PlmnId.Mnc", udm_ue->supi); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, message, "No PlmnId.Mnc", udm_ue->supi)); return false; } if (!Guami->plmn_id->mcc) { ogs_error("[%s] No PlmnId.Mcc", udm_ue->supi); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, message, "No PlmnId.Mcc", udm_ue->supi)); return false; } if (!Amf3GppAccessRegistration->rat_type) { ogs_error("[%s] No RatType", udm_ue->supi); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, message, "No RatType", udm_ue->supi)); return false; } if (udm_ue->dereg_callback_uri) ogs_free(udm_ue->dereg_callback_uri); udm_ue->dereg_callback_uri = ogs_strdup( Amf3GppAccessRegistration->dereg_callback_uri); ogs_assert(udm_ue->dereg_callback_uri); ogs_sbi_parse_guami(&udm_ue->guami, Guami); udm_ue->rat_type = Amf3GppAccessRegistration->rat_type; udm_ue->amf_3gpp_access_registration = OpenAPI_amf3_gpp_access_registration_copy( udm_ue->amf_3gpp_access_registration, message->Amf3GppAccessRegistration); ogs_assert(true == udm_sbi_discover_and_send(OGS_SBI_SERVICE_TYPE_NUDR_DR, NULL, udm_nudr_dr_build_update_amf_context, udm_ue, stream, NULL)); return true; } bool udm_nudm_uecm_handle_registration_update( udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, ogs_sbi_message_t *message) { OpenAPI_amf3_gpp_access_registration_modification_t *Amf3GppAccessRegistrationModification = NULL; OpenAPI_guami_t *Guami = NULL; ogs_guami_t recv_guami; OpenAPI_list_t *PatchItemList = NULL; OpenAPI_patch_item_t item; ogs_assert(udm_ue); ogs_assert(stream); ogs_assert(message); Amf3GppAccessRegistrationModification = message->Amf3GppAccessRegistrationModification; if (!Amf3GppAccessRegistrationModification) { ogs_error("[%s] No Amf3GppAccessRegistrationModification", udm_ue->supi); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, message, "No Amf3GppAccessRegistrationModification", udm_ue->supi)); return false; } Guami = Amf3GppAccessRegistrationModification->guami; if (!Guami) { ogs_error("[%s] No Guami", udm_ue->supi); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, message, "No Guami", udm_ue->supi)); return false; } if (!Guami->amf_id) { ogs_error("[%s] No Guami.AmfId", udm_ue->supi); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, message, "No Guami.AmfId", udm_ue->supi)); return false; } if (!Guami->plmn_id) { ogs_error("[%s] No PlmnId", udm_ue->supi); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, message, "No PlmnId", udm_ue->supi)); return false; } if (!Guami->plmn_id->mnc) { ogs_error("[%s] No PlmnId.Mnc", udm_ue->supi); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, message, "No PlmnId.Mnc", udm_ue->supi)); return false; } if (!Guami->plmn_id->mcc) { ogs_error("[%s] No PlmnId.Mcc", udm_ue->supi); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, message, "No PlmnId.Mcc", udm_ue->supi)); return false; } /* TS 29.503: 5.3.2.4.2 AMF deregistration for 3GPP access * 2a. The UDM shall check whether the received GUAMI matches the stored * GUAMI. If so, the UDM shall set the PurgeFlag. The UDM responds with * "204 No Content". * 2b. Otherwise the UDM responds with "403 Forbidden". */ ogs_sbi_parse_guami(&recv_guami, Guami); if (memcmp(&recv_guami, &udm_ue->guami, sizeof(recv_guami)) != 0) { ogs_error("[%s] Guami mismatch", udm_ue->supi); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_FORBIDDEN, message, "Guami mismatch", udm_ue->supi)); } if (Amf3GppAccessRegistrationModification->is_purge_flag) { udm_ue->amf_3gpp_access_registration->is_purge_flag = Amf3GppAccessRegistrationModification->is_purge_flag; udm_ue->amf_3gpp_access_registration->purge_flag = Amf3GppAccessRegistrationModification->purge_flag; } PatchItemList = OpenAPI_list_create(); ogs_assert(PatchItemList); if (Amf3GppAccessRegistrationModification->is_purge_flag) { memset(&item, 0, sizeof(item)); item.op = OpenAPI_patch_operation_replace; item.path = (char *)"PurgeFlag"; item.value = OpenAPI_any_type_create_bool( Amf3GppAccessRegistrationModification->purge_flag); ogs_assert(item.value); OpenAPI_list_add(PatchItemList, &item); } ogs_assert(true == udm_sbi_discover_and_send(OGS_SBI_SERVICE_TYPE_NUDR_DR, NULL, udm_nudr_dr_build_patch_amf_context, udm_ue, stream, PatchItemList)); return true; } bool udm_nudm_sdm_handle_subscription_provisioned( udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg) { ogs_sbi_message_t sendmsg; ogs_sbi_response_t *response = NULL; ogs_assert(udm_ue); ogs_assert(stream); ogs_assert(recvmsg); SWITCH(recvmsg->h.resource.component[1]) CASE(OGS_SBI_RESOURCE_NAME_UE_CONTEXT_IN_SMF_DATA) OpenAPI_ue_context_in_smf_data_t UeContextInSmfData; memset(&UeContextInSmfData, 0, sizeof(UeContextInSmfData)); memset(&sendmsg, 0, sizeof(sendmsg)); sendmsg.UeContextInSmfData = &UeContextInSmfData; response = ogs_sbi_build_response(&sendmsg, OGS_SBI_HTTP_STATUS_OK); ogs_assert(response); ogs_sbi_server_send_response(stream, response); break; DEFAULT ogs_error("Invalid resource name [%s]", recvmsg->h.resource.component[3]); return false; END return true; }