git-svn-id: http://svn.eurecom.fr/openair4G/trunk@7476 818b1a75-f10b-46b9-bf7c-635c3b92a50f
302 lines
9.3 KiB
C
302 lines
9.3 KiB
C
/*******************************************************************************
|
|
OpenAirInterface
|
|
Copyright(c) 1999 - 2014 Eurecom
|
|
|
|
OpenAirInterface is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
|
|
OpenAirInterface 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 OpenAirInterface.The full GNU General Public License is
|
|
included in this distribution in the file called "COPYING". If not,
|
|
see <http://www.gnu.org/licenses/>.
|
|
|
|
Contact Information
|
|
OpenAirInterface Admin: openair_admin@eurecom.fr
|
|
OpenAirInterface Tech : openair_tech@eurecom.fr
|
|
OpenAirInterface Dev : openair4g-devel@eurecom.fr
|
|
|
|
Address : Eurecom, Compus SophiaTech 450, route des chappes, 06451 Biot, France.
|
|
|
|
*******************************************************************************/
|
|
|
|
/*! \file s6a_auth_info.c
|
|
* \brief Handle an authentication information request procedure and generate the answer
|
|
* \author Sebastien ROUX <sebastien.roux@eurecom.fr>
|
|
* \date 2013
|
|
* \version 0.1
|
|
*/
|
|
|
|
#include <freeDiameter/freeDiameter-host.h>
|
|
#include <freeDiameter/libfdproto.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
#include <pthread.h>
|
|
|
|
#include "hss_config.h"
|
|
#include "db_proto.h"
|
|
#include "s6a_proto.h"
|
|
#include "auc.h"
|
|
#include "access_restriction.h"
|
|
|
|
int s6a_auth_info_cb(struct msg **msg, struct avp *paramavp,
|
|
struct session *sess, void *opaque,
|
|
enum disp_action *act)
|
|
{
|
|
struct msg *ans, *qry;
|
|
struct avp *avp, *failed_avp = NULL;
|
|
|
|
struct avp_hdr *hdr;
|
|
union avp_value value;
|
|
|
|
/* Database queries */
|
|
mysql_auth_info_req_t auth_info_req;
|
|
mysql_auth_info_resp_t auth_info_resp;
|
|
|
|
/* Authentication vector */
|
|
auc_vector_t vector;
|
|
|
|
int ret = 0;
|
|
int result_code = ER_DIAMETER_SUCCESS;
|
|
int experimental = 0;
|
|
uint64_t imsi;
|
|
uint8_t *sqn = NULL, *auts = NULL;
|
|
|
|
if (msg == NULL) {
|
|
return EINVAL;
|
|
}
|
|
|
|
/* Create answer header */
|
|
qry = *msg;
|
|
CHECK_FCT(fd_msg_new_answer_from_req(fd_g_config->cnf_dict, msg, 0));
|
|
ans = *msg;
|
|
|
|
/* Retrieving IMSI AVP: User-Name */
|
|
CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_imsi, &avp));
|
|
|
|
if (avp) {
|
|
CHECK_FCT(fd_msg_avp_hdr(avp, &hdr));
|
|
|
|
if (hdr->avp_value->os.len > IMSI_LENGTH_MAX) {
|
|
result_code = ER_DIAMETER_INVALID_AVP_VALUE;
|
|
goto out;
|
|
}
|
|
|
|
sprintf(auth_info_req.imsi, "%*s", (int)hdr->avp_value->os.len,
|
|
hdr->avp_value->os.data);
|
|
sscanf(auth_info_req.imsi, "%"SCNu64, &imsi);
|
|
} else {
|
|
result_code = ER_DIAMETER_MISSING_AVP;
|
|
goto out;
|
|
}
|
|
|
|
/* Retrieving Supported Features AVP. This is an optional AVP. */
|
|
CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_supported_features, &avp));
|
|
|
|
if (avp) {
|
|
CHECK_FCT(fd_msg_avp_hdr(avp, &hdr));
|
|
}
|
|
|
|
/* Retrieving the Requested-EUTRAN-Authentication-Info.
|
|
* If this AVP is not present, we have to check for
|
|
* Requested-GERAN-Authentication-Info AVP which will mean that the request
|
|
* comes from RAT other than E-UTRAN, case not handled by this HSS
|
|
* implementation.
|
|
*/
|
|
CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_req_e_utran_auth_info, &avp));
|
|
|
|
if (avp) {
|
|
struct avp *child_avp;
|
|
|
|
/* Walk through childs avp */
|
|
CHECK_FCT(fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &child_avp, NULL));
|
|
|
|
while (child_avp) {
|
|
/* Retrieve the header of the child avp */
|
|
CHECK_FCT(fd_msg_avp_hdr(child_avp, &hdr));
|
|
|
|
switch(hdr->avp_code) {
|
|
case AVP_CODE_NUMBER_OF_REQ_VECTORS: {
|
|
/* We allow only one vector request */
|
|
if (hdr->avp_value->u32 != 1) {
|
|
result_code = ER_DIAMETER_INVALID_AVP_VALUE;
|
|
failed_avp = child_avp;
|
|
goto out;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AVP_CODE_IMMEDIATE_RESP_PREF:
|
|
/* We always respond immediately to the request */
|
|
break;
|
|
|
|
case AVP_CODE_RE_SYNCHRONIZATION_INFO:
|
|
|
|
/* The resynchronization-info AVP is present.
|
|
* AUTS = Conc(SQN MS ) || MAC-S
|
|
*/
|
|
if (avp) {
|
|
auts = hdr->avp_value->os.data;
|
|
}
|
|
|
|
break;
|
|
|
|
default: {
|
|
/* This AVP is not expected on s6a interface */
|
|
result_code = ER_DIAMETER_AVP_UNSUPPORTED;
|
|
failed_avp = child_avp;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* Go to next AVP in the grouped AVP */
|
|
CHECK_FCT(fd_msg_browse(child_avp, MSG_BRW_NEXT, &child_avp, NULL));
|
|
}
|
|
} else {
|
|
CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_req_geran_auth_info, &avp));
|
|
|
|
if (avp) {
|
|
result_code = DIAMETER_ERROR_RAT_NOT_ALLOWED;
|
|
experimental = 1;
|
|
goto out;
|
|
} else {
|
|
result_code = ER_DIAMETER_INVALID_AVP_VALUE;
|
|
failed_avp = avp;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* Retrieving the Visited-PLMN-Id AVP */
|
|
CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_visited_plmn_id, &avp));
|
|
|
|
if (avp) {
|
|
/* TODO: check PLMN and allow/reject connectivity depending on roaming */
|
|
CHECK_FCT(fd_msg_avp_hdr(avp, &hdr));
|
|
|
|
if (hdr->avp_value->os.len == 3) {
|
|
if (apply_access_restriction(auth_info_req.imsi, hdr->avp_value->os.data) != 0) {
|
|
/* We found that user is roaming and has no right to do it ->
|
|
* reject the connection
|
|
*/
|
|
result_code = DIAMETER_ERROR_ROAMING_NOT_ALLOWED;
|
|
experimental = 1;
|
|
goto out;
|
|
}
|
|
} else {
|
|
result_code = ER_DIAMETER_INVALID_AVP_VALUE;
|
|
goto out;
|
|
}
|
|
} else {
|
|
/* Mandatory AVP, raise an error if not present */
|
|
result_code = ER_DIAMETER_MISSING_AVP;
|
|
goto out;
|
|
}
|
|
|
|
/* Fetch User data */
|
|
if (hss_mysql_auth_info(&auth_info_req, &auth_info_resp) != 0) {
|
|
/* Database query failed... */
|
|
result_code = DIAMETER_AUTHENTICATION_DATA_UNAVAILABLE;
|
|
experimental = 1;
|
|
goto out;
|
|
}
|
|
|
|
if (auts != NULL) {
|
|
/* Try to derive SQN_MS from previous RAND */
|
|
sqn = sqn_ms_derive(auth_info_resp.opc, auth_info_resp.key, auts, auth_info_resp.rand);
|
|
|
|
if (sqn != NULL) {
|
|
/* We succeeded to verify SQN_MS... */
|
|
|
|
/* Pick a new RAND and store SQN_MS + RAND in the HSS */
|
|
generate_random(vector.rand, RAND_LENGTH);
|
|
hss_mysql_push_rand_sqn(auth_info_req.imsi, auth_info_resp.rand, sqn);
|
|
|
|
hss_mysql_increment_sqn(auth_info_req.imsi);
|
|
|
|
free(sqn);
|
|
}
|
|
|
|
/* Fetch new user data */
|
|
if (hss_mysql_auth_info(&auth_info_req, &auth_info_resp) != 0) {
|
|
/* Database query failed... */
|
|
result_code = DIAMETER_AUTHENTICATION_DATA_UNAVAILABLE;
|
|
experimental = 1;
|
|
goto out;
|
|
}
|
|
|
|
sqn = auth_info_resp.sqn;
|
|
|
|
memcpy(vector.rand, auth_info_resp.rand, RAND_LENGTH);
|
|
} else {
|
|
/* Pick a new RAND and store SQN_MS + RAND in the HSS */
|
|
generate_random(vector.rand, RAND_LENGTH);
|
|
sqn = auth_info_resp.sqn;
|
|
hss_mysql_push_rand_sqn(auth_info_req.imsi, vector.rand, sqn);
|
|
}
|
|
|
|
hss_mysql_increment_sqn(auth_info_req.imsi);
|
|
|
|
/* Generate authentication vector */
|
|
generate_vector(auth_info_resp.opc, imsi, auth_info_resp.key,
|
|
hdr->avp_value->os.data, sqn, &vector);
|
|
|
|
/* We add the vector */
|
|
{
|
|
struct avp *e_utran_vector, *child_avp;
|
|
CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_authentication_info, 0, &avp));
|
|
|
|
CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_e_utran_vector, 0, &e_utran_vector));
|
|
|
|
CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_rand, 0, &child_avp));
|
|
value.os.data = vector.rand;
|
|
value.os.len = RAND_LENGTH_OCTETS;
|
|
CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value));
|
|
CHECK_FCT(fd_msg_avp_add(e_utran_vector, MSG_BRW_LAST_CHILD, child_avp));
|
|
|
|
CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_xres, 0, &child_avp));
|
|
value.os.data = vector.xres;
|
|
value.os.len = XRES_LENGTH_OCTETS;
|
|
CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value));
|
|
CHECK_FCT(fd_msg_avp_add(e_utran_vector, MSG_BRW_LAST_CHILD, child_avp));
|
|
|
|
CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_autn, 0, &child_avp));
|
|
value.os.data = vector.autn;
|
|
value.os.len = AUTN_LENGTH_OCTETS;
|
|
CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value));
|
|
CHECK_FCT(fd_msg_avp_add(e_utran_vector, MSG_BRW_LAST_CHILD, child_avp));
|
|
|
|
CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_kasme, 0, &child_avp));
|
|
value.os.data = vector.kasme;
|
|
value.os.len = KASME_LENGTH_OCTETS;
|
|
CHECK_FCT(fd_msg_avp_setvalue(child_avp, &value));
|
|
CHECK_FCT(fd_msg_avp_add(e_utran_vector, MSG_BRW_LAST_CHILD, child_avp));
|
|
|
|
CHECK_FCT(fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, e_utran_vector));
|
|
|
|
CHECK_FCT(fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp));
|
|
}
|
|
|
|
out:
|
|
/* Add the Auth-Session-State AVP */
|
|
CHECK_FCT(fd_msg_search_avp(qry, s6a_cnf.dataobj_s6a_auth_session_state, &avp));
|
|
CHECK_FCT(fd_msg_avp_hdr(avp, &hdr));
|
|
|
|
CHECK_FCT(fd_msg_avp_new(s6a_cnf.dataobj_s6a_auth_session_state, 0, &avp));
|
|
CHECK_FCT(fd_msg_avp_setvalue(avp, hdr->avp_value));
|
|
CHECK_FCT(fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp));
|
|
|
|
/* Append the result code to the answer */
|
|
CHECK_FCT(s6a_add_result_code(ans, failed_avp, result_code, experimental));
|
|
|
|
CHECK_FCT(fd_msg_send(msg, NULL, NULL ));
|
|
return ret;
|
|
}
|