/** * Creates a Authorization Session for the Server, from the application specific Session starting request * It generates a new id and adds the session to the cdp list of sessions * \note Returns with a lock on AAASession->hash. Unlock when done working with the result * @returns the new AAASession or null on error */ AAASession* AAACreateServerAuthSession(AAAMessage *msg,int is_statefull,AAASessionCallback_f *cb,void *generic_data) { AAASession *s; str id; if (!msg||!msg->sessionId||!msg->sessionId->data.len){ LM_ERR("Error retrieving the Session-Id from the message.\n"); return 0; } id.s = shm_malloc(msg->sessionId->data.len); if (!id.s){ LM_ERR("Error allocating %d bytes of shm!\n",msg->sessionId->data.len); return 0; }else{ id.len = msg->sessionId->data.len; memcpy(id.s,msg->sessionId->data.s,id.len); s=cdp_new_auth_session(id,0,is_statefull); if (s) { s->u.auth.generic_data = generic_data; s->cb = cb; if (s->cb) (s->cb)(AUTH_EV_SESSION_CREATED,s); update_auth_session_timers(&(s->u.auth),msg); auth_server_statefull_sm_process(s,AUTH_EV_RECV_REQ,msg); // this is a special exception where the session lock is not released //s=0; } } return s; }
/** * 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); } }
/** * 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; }