Files
openairinterface5g/openair-cn/OPENAIRHSS/s6a/s6a_auth_info.c
T
2015-05-28 12:38:52 +00:00

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;
}