mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
906 lines
33 KiB
906 lines
33 KiB
/*
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com
|
|
* Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com
|
|
*
|
|
* The initial version of this code was written by Dragos Vingarzan
|
|
* (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
|
|
* Fruanhofer Institute. It was and still is maintained in a separate
|
|
* branch of the original SER. We are therefore migrating it to
|
|
* Kamailio/SR and look forward to maintaining it from here on out.
|
|
* 2011/2012 Smile Communications, Pty. Ltd.
|
|
* ported/maintained/improved by
|
|
* Jason Penton (jason(dot)penton(at)smilecoms.com and
|
|
* Richard Good (richard(dot)good(at)smilecoms.com) as part of an
|
|
* effort to add full IMS support to Kamailio/SR using a new and
|
|
* improved architecture
|
|
*
|
|
* NB: Alot of this code was originally part of OpenIMSCore,
|
|
* FhG Fokus.
|
|
* Copyright (C) 2004-2006 FhG Fokus
|
|
* Thanks for great work! This is an effort to
|
|
* break apart the various CSCF functions into logically separate
|
|
* components. We hope this will drive wider use. We also feel
|
|
* that in this way the architecture is more complete and thereby easier
|
|
* to manage in the Kamailio/SR environment
|
|
*
|
|
* This file is part of Kamailio, a free SIP server.
|
|
*
|
|
* Kamailio 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 2 of the License, or
|
|
* (at your option) any later version
|
|
*
|
|
* Kamailio 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, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#include <time.h>
|
|
|
|
#include "authstatemachine.h"
|
|
#include "diameter_ims.h"
|
|
|
|
// all this 4 includes are here because of what i do in Send_ASA
|
|
#include "peer.h"
|
|
#include "peermanager.h"
|
|
#include "routing.h"
|
|
#include "receiver.h"
|
|
#include "common.h"
|
|
|
|
char *auth_states[] = {"Idle", "Pending", "Open", "Discon"};
|
|
char *auth_events[] = {};
|
|
|
|
extern dp_config *config; // because i want to use tc for the expire times...
|
|
|
|
/*
|
|
* Alberto Diez changes the default behaviour on error is going to be to return the default state
|
|
* that is STATE_MAINTAINED
|
|
* this is because in the Rx specification 3GPP TS 29214 v7.1.0 (2007-06)
|
|
* the AVP Auth Session State is not included in any message exchange,
|
|
* therefor we could as well not even check for it, but diameter rfc says to look at this
|
|
*
|
|
*/
|
|
|
|
int get_auth_session_state(AAAMessage* msg) {
|
|
if (!msg) goto error;
|
|
AAA_AVP* rc = AAAFindMatchingAVP(msg, 0, AVP_Auth_Session_State, 0, 0);
|
|
if (!rc) goto error;
|
|
return get_4bytes(rc->data.s);
|
|
|
|
error:
|
|
LM_DBG("get_auth_session_state(): no AAAMessage or Auth Session State not found\n");
|
|
return STATE_MAINTAINED;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the Session-Timeout, Auth-Lifetime and Auth-Grace-Period AVPs and update the session timers accordingly
|
|
* @param x
|
|
* @param msg
|
|
*/
|
|
void update_auth_session_timers(cdp_auth_session_t *x, AAAMessage *msg) {
|
|
AAA_AVP *avp;
|
|
uint32_t session_timeout = 0, grace_period = 0, auth_lifetime = 0;
|
|
int update_grace = 0, update_lifetime = 0;
|
|
|
|
avp = AAAFindMatchingAVP(msg, 0, AVP_Auth_Grace_Period, 0, 0);
|
|
if (avp && avp->data.len == 4) {
|
|
grace_period = get_4bytes(avp->data.s);
|
|
update_grace = 1;
|
|
} else {
|
|
if (!avp) {
|
|
grace_period = x->last_requested_grace;
|
|
}
|
|
}
|
|
if (update_grace)
|
|
x->grace_period = grace_period;
|
|
|
|
|
|
avp = AAAFindMatchingAVP(msg, 0, AVP_Authorization_Lifetime, 0, 0);
|
|
if (avp && avp->data.len == 4) {
|
|
auth_lifetime = get_4bytes(avp->data.s);
|
|
update_lifetime = 1;
|
|
|
|
} else {
|
|
if (!avp) {
|
|
LM_DBG("using timers from our request as there is nothing in the response (lifetime) - last requested lifetime was [%d]\n", x->last_requested_lifetime);
|
|
if (x->last_requested_lifetime > 0) {
|
|
update_lifetime = 1;
|
|
auth_lifetime = x->last_requested_lifetime;
|
|
}
|
|
}
|
|
}
|
|
if (update_lifetime) {
|
|
switch (auth_lifetime) {
|
|
case 0:
|
|
x->lifetime = time(0);
|
|
break;
|
|
case 0xFFFFFFFF:
|
|
x->lifetime = -1;
|
|
break;
|
|
default:
|
|
x->lifetime = time(0) + auth_lifetime;
|
|
}
|
|
if (x->timeout != -1 && x->timeout < x->lifetime) {
|
|
x->timeout = x->lifetime + x->grace_period;
|
|
}
|
|
}
|
|
|
|
avp = AAAFindMatchingAVP(msg, 0, AVP_Session_Timeout, 0, 0);
|
|
if (avp && avp->data.len == 4) {
|
|
session_timeout = get_4bytes(avp->data.s);
|
|
switch (session_timeout) {
|
|
case 0:
|
|
x->timeout = time(0) + config->default_auth_session_timeout;
|
|
break;
|
|
case 0xFFFFFFFF:
|
|
x->timeout = -1;
|
|
break;
|
|
default:
|
|
x->timeout = time(0) + session_timeout;
|
|
}
|
|
if (!x->lifetime) x->lifetime = x->timeout;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add Session-Timeout, Auth-Lifetime and Auth-Grace-Period AVPs to outgoing messages in case they are missing
|
|
* @param x
|
|
* @param msg
|
|
*/
|
|
void add_auth_session_timers(cdp_auth_session_t *x, AAAMessage *msg) {
|
|
AAA_AVP *avp;
|
|
char data[4];
|
|
uint32_t v;
|
|
uint32_t lifetime, timeout, grace;
|
|
|
|
avp = AAAFindMatchingAVP(msg, 0, AVP_Authorization_Lifetime, 0, 0);
|
|
if (!avp) {
|
|
if (x->lifetime == -1) v = 0xFFFFFFFF;
|
|
else {
|
|
v = x->lifetime - time(0);
|
|
if (v < 0) v = 0;
|
|
}
|
|
x->last_requested_lifetime = v;
|
|
set_4bytes(data, v);
|
|
avp = AAACreateAVP(AVP_Authorization_Lifetime, AAA_AVP_FLAG_MANDATORY, 0, data, 4, AVP_DUPLICATE_DATA);
|
|
if (avp) AAAAddAVPToMessage(msg, avp, msg->avpList.tail);
|
|
} else {
|
|
if (avp->data.len == 4) {
|
|
lifetime = get_4bytes(avp->data.s);
|
|
switch (lifetime) {
|
|
case 0:
|
|
x->last_requested_lifetime = 0;
|
|
break;
|
|
case 0xFFFFFFFF:
|
|
x->last_requested_lifetime = -1;
|
|
break;
|
|
default:
|
|
x->last_requested_lifetime = lifetime;
|
|
}
|
|
}
|
|
}
|
|
if (x->lifetime != -1) {
|
|
avp = AAAFindMatchingAVP(msg, 0, AVP_Auth_Grace_Period, 0, 0);
|
|
if (!avp) {
|
|
v = x->grace_period;
|
|
set_4bytes(data, v);
|
|
avp = AAACreateAVP(AVP_Auth_Grace_Period, AAA_AVP_FLAG_MANDATORY, 0, data, 4, AVP_DUPLICATE_DATA);
|
|
if (avp) AAAAddAVPToMessage(msg, avp, msg->avpList.tail);
|
|
x->last_requested_grace = v;
|
|
} else {
|
|
if (avp->data.len == 4) {
|
|
grace = get_4bytes(avp->data.s);
|
|
switch (grace) {
|
|
case 0:
|
|
x->last_requested_grace = 0;
|
|
break;
|
|
case 0xFFFFFFFF:
|
|
x->last_requested_grace = -1;
|
|
break;
|
|
default:
|
|
x->last_requested_grace = grace;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
avp = AAAFindMatchingAVP(msg, 0, AVP_Session_Timeout, 0, 0);
|
|
if (!avp) {
|
|
if (x->timeout == -1) v = 0xFFFFFFFF;
|
|
else {
|
|
v = x->timeout - time(0);
|
|
if (v < 0) v = 0;
|
|
}
|
|
set_4bytes(data, v);
|
|
avp = AAACreateAVP(AVP_Session_Timeout, AAA_AVP_FLAG_MANDATORY, 0, data, 4, AVP_DUPLICATE_DATA);
|
|
if (avp) AAAAddAVPToMessage(msg, avp, msg->avpList.tail);
|
|
x->last_requested_timeout = v;
|
|
} else {
|
|
if (avp->data.len == 4) {
|
|
timeout = get_4bytes(avp->data.s);
|
|
switch (timeout) {
|
|
case 0:
|
|
x->last_requested_timeout = 0;
|
|
break;
|
|
case 0xFFFFFFFF:
|
|
x->last_requested_timeout = -1;
|
|
break;
|
|
default:
|
|
x->last_requested_timeout = lifetime;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* stateful client state machine
|
|
* \Note - should be called with a lock on the session and will unlock it - do not use it after!
|
|
* @param auth - AAAAuthSession which uses this state machine
|
|
* @param ev - Event
|
|
* @param msg - AAAMessage
|
|
* @returns 0 if msg should be given to the upper layer 1 if not
|
|
*/
|
|
inline int auth_client_statefull_sm_process(cdp_session_t* s, int event, AAAMessage* msg) {
|
|
|
|
cdp_auth_session_t *x;
|
|
int rc;
|
|
int rv = 0; //return value
|
|
|
|
if (!s) {
|
|
switch (event) {
|
|
case AUTH_EV_RECV_ASR:
|
|
Send_ASA(0, msg);
|
|
break;
|
|
default:
|
|
LM_ERR("auth_client_statefull_sm_process(): Received invalid event %d with no session!\n",
|
|
event);
|
|
}
|
|
return rv;
|
|
}
|
|
x = &(s->u.auth);
|
|
|
|
if (s->cb) (s->cb)(event, s);
|
|
LM_INFO("after callback of event %i\n", event);
|
|
|
|
//if (x && x->state && msg) LM_ERR("auth_client_statefull_sm_process [event %i] [state %i] endtoend %u hopbyhop %u\n",event,x->state,msg->endtoendId,msg->hopbyhopId);
|
|
|
|
switch (x->state) {
|
|
case AUTH_ST_IDLE:
|
|
switch (event) {
|
|
case AUTH_EV_SEND_REQ:
|
|
s->application_id = msg->applicationId;
|
|
s->u.auth.state = AUTH_ST_PENDING;
|
|
update_auth_session_timers(x, msg);
|
|
add_auth_session_timers(x, msg);
|
|
|
|
//Richard add this add: add destination realm to cdp session
|
|
//use msg origin realm as the destination realm
|
|
//we do this here as this is were the state changes to open
|
|
//Where must we free this?
|
|
s->dest_realm.s = (char*) shm_malloc(msg->dest_realm->data.len);
|
|
memcpy(s->dest_realm.s, msg->dest_realm->data.s, msg->dest_realm->data.len);
|
|
s->dest_realm.len = msg->dest_realm->data.len;
|
|
|
|
|
|
//LM_INFO("state machine: i was in idle and i am going to pending\n");
|
|
break;
|
|
default:
|
|
LM_ERR("auth_client_statefull_sm_process(): Received invalid event %d while in state %s!(data %p)\n",
|
|
event, auth_states[x->state], x->generic_data);
|
|
}
|
|
break;
|
|
|
|
case AUTH_ST_PENDING:
|
|
if (event == AUTH_EV_RECV_ANS && msg && !is_req(msg)) {
|
|
rc = get_result_code(msg);
|
|
if (rc >= 2000 && rc < 3000 && get_auth_session_state(msg) == STATE_MAINTAINED)
|
|
event = AUTH_EV_RECV_ANS_SUCCESS;
|
|
else
|
|
event = AUTH_EV_RECV_ANS_UNSUCCESS;
|
|
}
|
|
|
|
switch (event) {
|
|
case AUTH_EV_RECV_ANS_SUCCESS:
|
|
x->state = AUTH_ST_OPEN;
|
|
update_auth_session_timers(x, msg);
|
|
//LM_INFO("state machine: i was in pending and i am going to open\n");
|
|
break;
|
|
case AUTH_EV_RECV_ANS_UNSUCCESS:
|
|
LM_DBG("In state AUTH_ST_PENDING and received AUTH_EV_RECV_ANS_UNSUCCESS - nothing to do but clean up session\n");
|
|
case AUTH_EV_SESSION_TIMEOUT:
|
|
case AUTH_EV_SERVICE_TERMINATED:
|
|
case AUTH_EV_SESSION_GRACE_TIMEOUT:
|
|
cdp_session_cleanup(s, NULL);
|
|
s = 0;
|
|
break;
|
|
|
|
default:
|
|
LM_ERR("auth_client_stateless_sm_process(): Received invalid event %d while in state %s!\n",
|
|
event, auth_states[x->state]);
|
|
}
|
|
break;
|
|
|
|
case AUTH_ST_OPEN:
|
|
if (event == AUTH_EV_RECV_ANS && msg && !is_req(msg)) {
|
|
rc = get_result_code(msg);
|
|
if (rc >= 2000 && rc < 3000 && get_auth_session_state(msg) == STATE_MAINTAINED)
|
|
event = AUTH_EV_RECV_ANS_SUCCESS;
|
|
else
|
|
event = AUTH_EV_RECV_ANS_UNSUCCESS;
|
|
}
|
|
|
|
switch (event) {
|
|
case AUTH_EV_SEND_REQ:
|
|
// if the request is STR i should move to Discon ..
|
|
// this is not in the state machine but I (Alberto Diez) need it
|
|
if (msg->commandCode == IMS_STR)
|
|
s->u.auth.state = AUTH_ST_DISCON;
|
|
else {
|
|
s->u.auth.state = AUTH_ST_OPEN;
|
|
add_auth_session_timers(x, msg);
|
|
}
|
|
break;
|
|
|
|
case AUTH_EV_RECV_ANS_SUCCESS:
|
|
x->state = AUTH_ST_OPEN;
|
|
update_auth_session_timers(x, msg);
|
|
//LM_INFO("state machine: i was in open and i am going to open\n");
|
|
break;
|
|
|
|
case AUTH_EV_RECV_ANS_UNSUCCESS:
|
|
x->state = AUTH_ST_DISCON;
|
|
//LM_INFO("state machine: i was in open and i am going to discon\n");
|
|
break;
|
|
|
|
case AUTH_EV_SESSION_TIMEOUT:
|
|
case AUTH_EV_SERVICE_TERMINATED:
|
|
case AUTH_EV_SESSION_GRACE_TIMEOUT:
|
|
x->state = AUTH_ST_DISCON;
|
|
//LM_INFO("state machine: i was in open and i am going to discon\n");
|
|
|
|
Send_STR(s, msg);
|
|
break;
|
|
|
|
case AUTH_EV_SEND_ASA_SUCCESS:
|
|
x->state = AUTH_ST_DISCON;
|
|
//LM_INFO("state machine: i was in open and i am going to discon\n");
|
|
Send_STR(s, msg);
|
|
break;
|
|
|
|
case AUTH_EV_SEND_ASA_UNSUCCESS:
|
|
x->state = AUTH_ST_OPEN;
|
|
update_auth_session_timers(x, msg);
|
|
//LM_INFO("state machine: i was in open and i am going to open\n");
|
|
break;
|
|
|
|
case AUTH_EV_RECV_ASR:
|
|
// two cases , client will comply or will not
|
|
// our client is very nice and always complys.. because
|
|
// our brain is in the PCRF... if he says to do this , we do it
|
|
// Alberto Diez , (again this is not Diameter RFC)
|
|
x->state = AUTH_ST_DISCON;
|
|
Send_ASA(s, msg);
|
|
Send_STR(s, msg);
|
|
break;
|
|
|
|
default:
|
|
LM_ERR("auth_client_statefull_sm_process(): Received invalid event %d while in state %s!\n",
|
|
event, auth_states[x->state]);
|
|
}
|
|
break;
|
|
|
|
case AUTH_ST_DISCON:
|
|
switch (event) {
|
|
case AUTH_EV_RECV_ASR:
|
|
x->state = AUTH_ST_DISCON;
|
|
//LM_INFO("state machine: i was in discon and i am going to discon\n");
|
|
Send_ASA(s, msg);
|
|
break;
|
|
|
|
// Just added this because it might happen if the other peer doesnt
|
|
// send a valid STA, then the session stays open forever...
|
|
// We dont accept that... we have lifetime+grace_period for that
|
|
// This is not in the Diameter RFC ...
|
|
case AUTH_EV_SESSION_TIMEOUT:
|
|
case AUTH_EV_SESSION_GRACE_TIMEOUT:
|
|
// thats the addition
|
|
case AUTH_EV_RECV_STA:
|
|
x->state = AUTH_ST_IDLE;
|
|
LM_INFO("state machine: AUTH_EV_RECV_STA about to clean up\n");
|
|
if (msg) AAAFreeMessage(&msg); // if might be needed in frequency
|
|
// If I register a ResponseHandler then i Free the STA there not here..
|
|
// but i dont have interest in that now..
|
|
cdp_session_cleanup(s, NULL);
|
|
s = 0;
|
|
rv = 1;
|
|
break;
|
|
|
|
default:
|
|
LM_ERR("auth_client_statefull_sm_process(): Received invalid event %d while in state %s!\n",
|
|
event, auth_states[x->state]);
|
|
}
|
|
break;
|
|
default:
|
|
LM_ERR("auth_client_statefull_sm_process(): Received event %d while in invalid state %d!\n",
|
|
event, x->state);
|
|
}
|
|
if (s) {
|
|
if (s->cb) (s->cb)(AUTH_EV_SESSION_MODIFIED, s);
|
|
AAASessionsUnlock(s->hash);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Authorization Server State-Machine - Statefull
|
|
* \Note - should be called with a lock on the session and will unlock it - do not use it after!
|
|
* @param s
|
|
* @param event
|
|
* @param msg
|
|
*/
|
|
inline void auth_server_statefull_sm_process(cdp_session_t* s, int event, AAAMessage* msg) {
|
|
cdp_auth_session_t *x;
|
|
|
|
if (!s) return;
|
|
x = &(s->u.auth);
|
|
|
|
if (s->cb) (s->cb)(event, s);
|
|
LM_DBG("after callback for event %i\n", event);
|
|
|
|
switch (x->state) {
|
|
case AUTH_ST_IDLE:
|
|
switch (event) {
|
|
case AUTH_EV_RECV_STR:
|
|
break;
|
|
case AUTH_EV_RECV_REQ:
|
|
// The RequestHandler will generate a Send event for the answer
|
|
// and we will only then now if the user is authorised or not
|
|
// if its not authorised it will move back to idle and cleanup the session
|
|
// so no big deal
|
|
// but this is not the Diameter RFC...
|
|
x->state = AUTH_ST_OPEN;
|
|
// execute the cb here because we won't have a chance later
|
|
if (s->cb) (s->cb)(AUTH_EV_SESSION_MODIFIED, s);
|
|
// Don't unlock the session hash table because the session is returned to the user
|
|
// This can only be called from the AAACreateServerAuthSession()!
|
|
s = 0;
|
|
break;
|
|
case AUTH_EV_SEND_STA:
|
|
x->state = AUTH_ST_IDLE;
|
|
cdp_session_cleanup(s, msg);
|
|
s = 0;
|
|
break;
|
|
|
|
/* Just in case we have some lost sessions */
|
|
case AUTH_EV_SESSION_TIMEOUT:
|
|
case AUTH_EV_SESSION_GRACE_TIMEOUT:
|
|
cdp_session_cleanup(s, msg);
|
|
s = 0;
|
|
break;
|
|
|
|
default:
|
|
LM_ERR("auth_client_statefull_sm_process(): Received invalid event %d while in state %s!\n",
|
|
event, auth_states[x->state]);
|
|
}
|
|
break;
|
|
|
|
case AUTH_ST_OPEN:
|
|
|
|
|
|
if (event == AUTH_EV_SEND_ANS && msg && !is_req(msg)) {
|
|
int rc = get_result_code(msg);
|
|
if (rc >= 2000 && rc < 3000)
|
|
event = AUTH_EV_SEND_ANS_SUCCESS;
|
|
else
|
|
event = AUTH_EV_SEND_ANS_UNSUCCESS;
|
|
}
|
|
|
|
|
|
switch (event) {
|
|
case AUTH_EV_RECV_STR:
|
|
break;
|
|
case AUTH_EV_SEND_ANS_SUCCESS:
|
|
x->state = AUTH_ST_OPEN;
|
|
update_auth_session_timers(x, msg);
|
|
add_auth_session_timers(x, msg);
|
|
break;
|
|
case AUTH_EV_SEND_ANS_UNSUCCESS:
|
|
x->state = AUTH_ST_IDLE;
|
|
cdp_session_cleanup(s, msg);
|
|
s = 0;
|
|
break;
|
|
case AUTH_EV_SEND_ASR:
|
|
x->state = AUTH_ST_DISCON;
|
|
break;
|
|
case AUTH_EV_SESSION_TIMEOUT:
|
|
case AUTH_EV_SESSION_GRACE_TIMEOUT:
|
|
x->state = AUTH_ST_IDLE;
|
|
LM_DBG("before session cleanup\n");
|
|
cdp_session_cleanup(s, msg);
|
|
s = 0;
|
|
break;
|
|
case AUTH_EV_SEND_STA:
|
|
LM_ERR("SENDING STA!!!\n");
|
|
x->state = AUTH_ST_IDLE;
|
|
cdp_session_cleanup(s, msg);
|
|
s = 0;
|
|
break;
|
|
default:
|
|
LM_ERR("auth_client_statefull_sm_process(): Received invalid event %d while in state %s!\n",
|
|
event, auth_states[x->state]);
|
|
}
|
|
break;
|
|
|
|
case AUTH_ST_DISCON:
|
|
switch (event) {
|
|
case AUTH_EV_RECV_STR:
|
|
break;
|
|
case AUTH_EV_RECV_ASA:
|
|
case AUTH_EV_RECV_ASA_SUCCESS:
|
|
x->state = AUTH_ST_IDLE;
|
|
//cdp_session_cleanup(s,msg);
|
|
break;
|
|
case AUTH_EV_RECV_ASA_UNSUCCESS:
|
|
Send_ASR(s, msg);
|
|
// how many times will this be done?
|
|
x->state = AUTH_ST_DISCON;
|
|
break;
|
|
case AUTH_EV_SEND_STA:
|
|
x->state = AUTH_ST_IDLE;
|
|
cdp_session_cleanup(s, msg);
|
|
s = 0;
|
|
break;
|
|
default:
|
|
LM_ERR("auth_client_statefull_sm_process(): Received invalid event %d while in state %s!\n",
|
|
event, auth_states[x->state]);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
LM_ERR("auth_client_statefull_sm_process(): Received event %d while in invalid state %d!\n",
|
|
event, x->state);
|
|
}
|
|
if (s) {
|
|
if (s->cb) (s->cb)(AUTH_EV_SESSION_MODIFIED, s);
|
|
AAASessionsUnlock(s->hash);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Authorization Client State-Machine - Stateless
|
|
* \Note - should be called with a lock on the session and will unlock it - do not use it after!
|
|
* @param s
|
|
* @param event
|
|
* @param msg
|
|
*/
|
|
inline void auth_client_stateless_sm_process(cdp_session_t* s, int event, AAAMessage *msg) {
|
|
cdp_auth_session_t *x;
|
|
int rc;
|
|
if (!s) return;
|
|
x = &(s->u.auth);
|
|
switch (x->state) {
|
|
case AUTH_ST_IDLE:
|
|
switch (event) {
|
|
case AUTH_EV_SEND_REQ:
|
|
x->state = AUTH_ST_PENDING;
|
|
break;
|
|
default:
|
|
LM_ERR("auth_client_stateless_sm_process(): Received invalid event %d while in state %s!\n",
|
|
event, auth_states[x->state]);
|
|
}
|
|
break;
|
|
|
|
case AUTH_ST_PENDING:
|
|
if (!is_req(msg)) {
|
|
rc = get_result_code(msg);
|
|
if (rc >= 2000 && rc < 3000 && get_auth_session_state(msg) == NO_STATE_MAINTAINED)
|
|
event = AUTH_EV_RECV_ANS_SUCCESS;
|
|
else
|
|
event = AUTH_EV_RECV_ANS_UNSUCCESS;
|
|
}
|
|
switch (event) {
|
|
case AUTH_EV_RECV_ANS_SUCCESS:
|
|
x->state = AUTH_ST_OPEN;
|
|
break;
|
|
case AUTH_EV_RECV_ANS_UNSUCCESS:
|
|
x->state = AUTH_ST_IDLE;
|
|
break;
|
|
default:
|
|
LM_ERR("auth_client_stateless_sm_process(): Received invalid event %d while in state %s!\n",
|
|
event, auth_states[x->state]);
|
|
}
|
|
break;
|
|
|
|
case AUTH_ST_OPEN:
|
|
switch (event) {
|
|
case AUTH_EV_SESSION_TIMEOUT:
|
|
x->state = AUTH_ST_IDLE;
|
|
break;
|
|
case AUTH_EV_SERVICE_TERMINATED:
|
|
x->state = AUTH_ST_IDLE;
|
|
break;
|
|
default:
|
|
LM_ERR("auth_client_stateless_sm_process(): Received invalid event %d while in state %s!\n",
|
|
event, auth_states[x->state]);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
LM_ERR("auth_client_stateless_sm_process(): Received event %d while in invalid state %d!\n",
|
|
event, x->state);
|
|
}
|
|
if (s) AAASessionsUnlock(s->hash);
|
|
}
|
|
|
|
/**
|
|
* Authorization Server State-Machine - Stateless
|
|
* \Note - should be called with a lock on the session and will unlock it - do not use it after!
|
|
*
|
|
* @param auth
|
|
* @param event
|
|
* @param msg
|
|
*/
|
|
inline void auth_server_stateless_sm_process(cdp_session_t* s, int event, AAAMessage* msg) {
|
|
/* empty - no state change, anyway */
|
|
/*
|
|
cdp_auth_session_t *x;
|
|
int rc;
|
|
if (!s) return;
|
|
x = &(s->u.auth);
|
|
switch(x->state){
|
|
case AUTH_ST_IDLE:
|
|
switch(event){
|
|
default:
|
|
LM_ERR("auth_server_stateless_sm_process(): Received invalid event %d while in state %s!\n",
|
|
event,auth_state[x->state]);
|
|
}
|
|
break;
|
|
default:
|
|
LM_ERR("auth_server_stateless_sm_process(): Received event %d while in invalid state %d!\n",
|
|
event,x->state);
|
|
}
|
|
*/
|
|
if (s) AAASessionsUnlock(s->hash);
|
|
}
|
|
|
|
/* copies the Origin-Host AVP from the src message in a Destination-Host AVP in the dest message
|
|
* copies the Origin-Realm AVP from the src message in a Destination-Realm AVP in the dest message
|
|
*
|
|
*/
|
|
int dup_routing_avps(AAAMessage* src, AAAMessage *dest) {
|
|
|
|
AAA_AVP * avp;
|
|
str dest_realm;
|
|
|
|
if (!src)
|
|
return 1;
|
|
|
|
/* Removed By Jason to facilitate use of Diameter clustering (MUX) in SLEE architecture (Realm-routing only) - TODO - check spec */
|
|
/*avp = AAAFindMatchingAVP(src,src->avpList.head,AVP_Origin_Host,0,AAA_FORWARD_SEARCH);
|
|
if(avp && avp->data.s && avp->data.len) {
|
|
LM_DBG("dup_routing_avps: Origin Host AVP present, duplicating %.*s\n",
|
|
avp->data.len, avp->data.s);
|
|
dest_host = avp->data;
|
|
avp = AAACreateAVP(AVP_Destination_Host,AAA_AVP_FLAG_MANDATORY,0,
|
|
dest_host.s,dest_host.len,AVP_DUPLICATE_DATA);
|
|
if (!avp) {
|
|
LM_ERR("dup_routing_avps: Failed creating Destination Host avp\n");
|
|
goto error;
|
|
}
|
|
if (AAAAddAVPToMessage(dest,avp,dest->avpList.tail)!=AAA_ERR_SUCCESS) {
|
|
LM_ERR("dup_routing_avps: Failed adding Destination Host avp to message\n");
|
|
AAAFreeAVP(&avp);
|
|
goto error;
|
|
}
|
|
}*/
|
|
|
|
avp = AAAFindMatchingAVP(src, src->avpList.head, AVP_Origin_Realm, 0, AAA_FORWARD_SEARCH);
|
|
if (avp && avp->data.s && avp->data.len) {
|
|
LM_DBG("dup_routing_avps: Origin Realm AVP present, duplicating %.*s\n",
|
|
avp->data.len, avp->data.s);
|
|
dest_realm = avp->data;
|
|
avp = AAACreateAVP(AVP_Destination_Realm, AAA_AVP_FLAG_MANDATORY, 0,
|
|
dest_realm.s, dest_realm.len, AVP_DUPLICATE_DATA);
|
|
if (!avp) {
|
|
LM_ERR("dup_routing_avps: Failed creating Destination Host avp\n");
|
|
goto error;
|
|
}
|
|
if (AAAAddAVPToMessage(dest, avp, dest->avpList.tail) != AAA_ERR_SUCCESS) {
|
|
LM_ERR("dup_routing_avps: Failed adding Destination Host avp to message\n");
|
|
AAAFreeAVP(&avp);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
error:
|
|
return 0;
|
|
|
|
}
|
|
|
|
void Send_ASA(cdp_session_t* s, AAAMessage* msg) {
|
|
AAAMessage *asa;
|
|
char x[4];
|
|
AAA_AVP *avp;
|
|
LM_INFO("Send_ASA(): sending ASA\n");
|
|
if (!s) {
|
|
//send an ASA for UNKNOWN_SESSION_ID - use AAASendMessage()
|
|
// msg is the ASR received
|
|
asa = AAANewMessage(IMS_ASA, 0, 0, msg);
|
|
if (!asa) return;
|
|
|
|
set_4bytes(x, AAA_SUCCESS);
|
|
AAACreateAndAddAVPToMessage(asa, AVP_Result_Code, AAA_AVP_FLAG_MANDATORY, 0, x, 4);
|
|
|
|
AAASendMessage(asa, 0, 0);
|
|
} else {
|
|
// send... many cases... maybe not needed.
|
|
// for now we do the same
|
|
asa = AAANewMessage(IMS_ASA, 0, 0, msg);
|
|
if (!asa) return;
|
|
|
|
set_4bytes(x, AAA_SUCCESS);
|
|
AAACreateAndAddAVPToMessage(asa, AVP_Result_Code, AAA_AVP_FLAG_MANDATORY, 0, x, 4);
|
|
|
|
avp = AAAFindMatchingAVP(msg, 0, AVP_Origin_Host, 0, 0);
|
|
if (avp) {
|
|
// This is because AAASendMessage is not going to find a route to the
|
|
// the PCRF because TS 29.214 says no Destination-Host and no Auth-Application-Id
|
|
// in the ASA
|
|
LM_INFO("sending ASA to peer %.*s\n", avp->data.len, avp->data.s);
|
|
peer *p;
|
|
p = get_peer_by_fqdn(&avp->data);
|
|
if (!peer_send_msg(p, asa)) {
|
|
if (asa) AAAFreeMessage(&asa); //needed in frequency
|
|
} else
|
|
LM_INFO("success sending ASA\n");
|
|
} else if (!AAASendMessage(asa, 0, 0)) {
|
|
LM_ERR("Send_ASA() : error sending ASA\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
int add_vendor_specific_application_id_group(AAAMessage * msg, unsigned int vendor_id, unsigned int auth_app_id) {
|
|
char x[4];
|
|
AAA_AVP_LIST list_grp = {0, 0};
|
|
AAA_AVP *avp;
|
|
str group = {0, 0};
|
|
|
|
set_4bytes(x, vendor_id);
|
|
if (!(avp = AAACreateAVP(AVP_Vendor_Id, AAA_AVP_FLAG_MANDATORY, 0,
|
|
x, 4, AVP_DUPLICATE_DATA))) goto error;
|
|
AAAAddAVPToList(&list_grp, avp);
|
|
|
|
set_4bytes(x, auth_app_id);
|
|
if (!(avp = AAACreateAVP(AVP_Auth_Application_Id, AAA_AVP_FLAG_MANDATORY, 0,
|
|
x, 4, AVP_DUPLICATE_DATA))) goto error;
|
|
AAAAddAVPToList(&list_grp, avp);
|
|
|
|
group = AAAGroupAVPS(list_grp);
|
|
if (!group.s || !group.len) goto error;
|
|
|
|
if (!(avp = AAACreateAVP(AVP_Vendor_Specific_Application_Id, AAA_AVP_FLAG_MANDATORY, 0,
|
|
group.s, group.len, AVP_DUPLICATE_DATA))) goto error;
|
|
|
|
if (AAAAddAVPToMessage(msg, avp, msg->avpList.tail) != AAA_ERR_SUCCESS) goto error;
|
|
|
|
AAAFreeAVPList(&list_grp);
|
|
shm_free(group.s);
|
|
group.s = NULL;
|
|
|
|
return 1;
|
|
|
|
error:
|
|
|
|
AAAFreeAVPList(&list_grp);
|
|
if (group.s) shm_free(group.s);
|
|
return 0;
|
|
}
|
|
|
|
void Send_STR(cdp_session_t* s, AAAMessage* msg) {
|
|
AAAMessage *str = 0;
|
|
AAA_AVP *avp = 0;
|
|
peer *p = 0;
|
|
char x[4];
|
|
LM_DBG("sending STR\n");
|
|
//if (msg) LM_DBG("Send_STR() : sending STR for %d, flags %#1x endtoend %u hopbyhop %u\n",msg->commandCode,msg->flags,msg->endtoendId,msg->hopbyhopId);
|
|
//else LM_DBG("Send_STR() called from AAATerminateAuthSession or some other event\n");
|
|
str = AAACreateRequest(s->application_id, IMS_STR, Flag_Proxyable, s);
|
|
|
|
if (!str) {
|
|
LM_ERR("Send_STR(): error creating STR!\n");
|
|
return;
|
|
}
|
|
if (!dup_routing_avps(msg, str)) {
|
|
LM_ERR("Send_STR(): error duplicating routing AVPs!\n");
|
|
AAAFreeMessage(&str);
|
|
return;
|
|
}
|
|
if (s->vendor_id != 0 && !add_vendor_specific_application_id_group(str, s->vendor_id, s->application_id)) {
|
|
LM_ERR("Send_STR(): error adding Vendor-Id-Specific-Application-Id Group!\n");
|
|
AAAFreeMessage(&str);
|
|
return;
|
|
}
|
|
|
|
//Richard added this - if timers expire dest realm is not here!
|
|
LM_DBG("Adding dest realm if not there already...\n");
|
|
LM_DBG("Destination realm: [%.*s] \n", s->dest_realm.len, s->dest_realm.s);
|
|
/* Add Destination-Realm AVP, if not already there */
|
|
avp = AAAFindMatchingAVP(str, str->avpList.head, AVP_Destination_Realm, 0, AAA_FORWARD_SEARCH);
|
|
if (!avp) {
|
|
avp = AAACreateAVP(AVP_Destination_Realm, AAA_AVP_FLAG_MANDATORY, 0,
|
|
s->dest_realm.s, s->dest_realm.len, AVP_DUPLICATE_DATA);
|
|
AAAAddAVPToMessage(str, avp, str->avpList.tail);
|
|
}
|
|
|
|
|
|
|
|
|
|
set_4bytes(x, s->application_id);
|
|
avp = AAACreateAVP(AVP_Auth_Application_Id, AAA_AVP_FLAG_MANDATORY, 0, x, 4, AVP_DUPLICATE_DATA);
|
|
AAAAddAVPToMessage(str, avp, str->avpList.tail);
|
|
|
|
set_4bytes(x, 4); // Diameter_administrative
|
|
avp = AAACreateAVP(AVP_Termination_Cause, AAA_AVP_FLAG_MANDATORY, 0, x, 4, AVP_DUPLICATE_DATA);
|
|
AAAAddAVPToMessage(str, avp, str->avpList.tail);
|
|
//todo - add all the other avps
|
|
|
|
/* we are already locked on the auth session*/
|
|
p = get_routing_peer(s, str);
|
|
|
|
if (!p) {
|
|
LM_ERR("unable to get routing peer in Send_STR \n");
|
|
if (str) AAAFreeMessage(&str); //needed in frequency
|
|
return;
|
|
}
|
|
//if (str) LM_CRIT("Send_STR() : sending STR %d, flags %#1x endtoend %u hopbyhop %u\n",str->commandCode,str->flags,str->endtoendId,str->hopbyhopId);
|
|
if (!peer_send_msg(p, str)) {
|
|
LM_DBG("Send_STR peer_send_msg return error!\n");
|
|
if (str) AAAFreeMessage(&str); //needed in frequency
|
|
} else {
|
|
LM_DBG("success sending STR\n");
|
|
}
|
|
}
|
|
|
|
void Send_ASR(cdp_session_t* s, AAAMessage* msg) {
|
|
AAAMessage *asr = 0;
|
|
AAA_AVP *avp = 0;
|
|
peer *p = 0;
|
|
char x[4];
|
|
LM_DBG("Send_ASR() : sending ASR\n");
|
|
asr = AAACreateRequest(s->application_id, IMS_ASR, Flag_Proxyable, s);
|
|
|
|
if (!asr) {
|
|
LM_ERR("Send_ASR(): error creating ASR!\n");
|
|
return;
|
|
}
|
|
|
|
set_4bytes(x, s->application_id);
|
|
avp = AAACreateAVP(AVP_Auth_Application_Id, AAA_AVP_FLAG_MANDATORY, 0, x, 4, AVP_DUPLICATE_DATA);
|
|
AAAAddAVPToMessage(asr, avp, asr->avpList.tail);
|
|
|
|
set_4bytes(x, 3); // Not specified
|
|
avp = AAACreateAVP(AVP_IMS_Abort_Cause, AAA_AVP_FLAG_MANDATORY, 0, x, 4, AVP_DUPLICATE_DATA);
|
|
AAAAddAVPToMessage(asr, avp, asr->avpList.tail);
|
|
//todo - add all the other avps
|
|
|
|
p = get_routing_peer(s, asr);
|
|
if (!p) {
|
|
LM_ERR("unable to get routing peer in Send_ASR \n");
|
|
if (asr) AAAFreeMessage(&asr); //needed in frequency
|
|
}
|
|
|
|
if (!peer_send_msg(p, asr)) {
|
|
if (asr) AAAFreeMessage(&asr); //needed in frequency
|
|
} else
|
|
LM_DBG("success sending ASR\n");
|
|
}
|