void ICSCFSproutletTsx::on_rx_cancel(int status_code, pjsip_msg* cancel_req) { // If this is cancelling a terminating INVITE then check whether we need to // update our session establishment stats. if (!_originating && (_req_type == PJSIP_INVITE_METHOD) && (!_session_set_up)) { // Session has failed to establish but for a reason that is not the fault // of the network. _icscf->_session_establishment_tbl->increment_attempts(); _icscf->_session_establishment_tbl->increment_failures(); _icscf->_session_establishment_network_tbl->increment_attempts(); _icscf->_session_establishment_network_tbl->increment_successes(); // Set _session_set_up to true so that we don't count this session again // when we receive the subsequent final response. _session_set_up = true; } if ((status_code == PJSIP_SC_REQUEST_TERMINATED) && (cancel_req != NULL)) { // Create and send an ACR for the CANCEL request. ACR* acr = _icscf->get_acr(trail()); // @TODO - timestamp from request. acr->rx_request(cancel_req); acr->send(); delete acr; } }
// LCOV_EXCL_START - TODO add to UTs void BGCFSproutletTsx::on_rx_cancel(int status_code, pjsip_msg* cancel_req) { if ((status_code == PJSIP_SC_REQUEST_TERMINATED) && (cancel_req != NULL)) { // Create and send an ACR for the CANCEL request. ACR* acr = _bgcf->get_acr(trail()); // @TODO - timestamp from request. acr->rx_request(cancel_req); acr->send(); delete acr; } }
void SubscriptionSproutletTsx::process_subscription_request(pjsip_msg* req) { SAS::TrailId trail_id = trail(); // Get the URI from the To header and check it is a SIP or SIPS URI. pjsip_uri* uri = (pjsip_uri*)pjsip_uri_get_uri(PJSIP_MSG_TO_HDR(req)->uri); if ((!PJSIP_URI_SCHEME_IS_SIP(uri)) && (!PJSIP_URI_SCHEME_IS_TEL(uri))) { // Reject a non-SIP/TEL URI with 404 Not Found (RFC3261 isn't clear // whether 404 is the right status code - it says 404 should be used if // the AoR isn't valid for the domain in the RequestURI). TRC_DEBUG("Rejecting subscribe request using invalid URI scheme"); SAS::Event event(trail_id, SASEvent::SUBSCRIBE_FAILED_EARLY_URLSCHEME, 0); SAS::report_event(event); pjsip_msg* rsp = create_response(req, PJSIP_SC_NOT_FOUND); send_response(rsp); free_msg(req); return; } // Check if the contact header is present. If it isn't, we want to abort // processing before going any further, to avoid unnecessary work. pjsip_contact_hdr* contact_hdr = (pjsip_contact_hdr*)pjsip_msg_find_hdr(req, PJSIP_H_CONTACT, NULL); if (contact_hdr == NULL) { TRC_DEBUG("Unable to parse contact header from request. " "Aborting processing"); pjsip_msg* rsp = create_response(req, PJSIP_SC_BAD_REQUEST); send_response(rsp); free_msg(req); return; } // Check if this is a subscription request from a binding that was emergency // registered. bool emergency_subscription = false; while (contact_hdr != NULL) { emergency_subscription = PJUtils::is_emergency_registration(contact_hdr); if (!emergency_subscription) { break; } contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(req, PJSIP_H_CONTACT, contact_hdr->next); } if (emergency_subscription) { // Reject a subscription with a Contact header containing a contact address // that's been registered for emergency service. TRC_DEBUG("Rejecting subscribe request from emergency registration"); SAS::Event event(trail_id, SASEvent::SUBSCRIBE_FAILED_EARLY_EMERGENCY, 0); SAS::report_event(event); // Allow-Events is a mandatory header on 489 responses. pjsip_msg* rsp = create_response(req, PJSIP_SC_BAD_EVENT); pjsip_generic_string_hdr* allow_events_hdr = pjsip_generic_string_hdr_create(get_pool(rsp), &STR_ALLOW_EVENTS, &STR_REG); pjsip_msg_add_hdr(rsp, (pjsip_hdr*)allow_events_hdr); send_response(rsp); free_msg(req); return; } // At this point we are going to attempt to process the subscribe. // Canonicalize the public ID from the URI in the To header. std::string public_id = PJUtils::public_id_from_uri(uri); TRC_DEBUG("Process SUBSCRIBE for public ID %s", public_id.c_str()); // Get the call identifier from the headers. std::string cid = PJUtils::pj_str_to_string(&PJSIP_MSG_CID_HDR(req)->id); // Add SAS markers to the trail attached to the message so the trail // becomes searchable. SAS::Event event(trail_id, SASEvent::SUBSCRIBE_START, 0); event.add_var_param(public_id); SAS::report_event(event); // Create an ACR for the request. The node role is always considered // originating for SUBSCRIBE requests. ACR* acr = _subscription->_acr_factory->get_acr(trail_id, ACR::CALLING_PARTY, ACR::NODE_ROLE_ORIGINATING); acr->rx_request(req); // Work out the expiry time of the subscription. pjsip_expires_hdr* expires = (pjsip_expires_hdr*)pjsip_msg_find_hdr(req, PJSIP_H_EXPIRES, NULL); int expiry = (expires != NULL) ? expires->ivalue : SubscriptionSproutlet::DEFAULT_SUBSCRIPTION_EXPIRES; if (expiry > _subscription->_max_expires) { // Expiry is too long, set it to the maximum. TRC_DEBUG("Requested expiry (%d) is too long, setting to the maximum (%d)", expiry, _subscription->_max_expires); expiry = _subscription->_max_expires; } // Create a subscription object from the request that we can pass down to // be set into/updated in the different stores Subscription* new_subscription = create_subscription(req, expiry); HSSConnection::irs_info irs_info; HTTPCode rc; // Update or remove the subscription in the subscriber manager depending on // the expiry time. if (expiry != 0) { TRC_DEBUG("Adding/updating the subscription with ID %s", new_subscription->get_id().c_str()); Subscriptions new_subscriptions; new_subscriptions.insert(std::make_pair(new_subscription->get_id(), new_subscription)); rc = _subscription->_sm->update_subscriptions( public_id, new_subscriptions, irs_info, trail_id); } else { TRC_DEBUG("Removing the subscription with ID %s", new_subscription->get_id().c_str()); rc = _subscription->_sm->remove_subscriptions(public_id, {new_subscription->get_id()}, irs_info, trail_id); } pjsip_status_code st_code = determine_sm_sip_response(rc, irs_info._regstate, "SUBSCRIBE"); pjsip_msg* rsp = create_response(req, st_code); if (st_code == PJSIP_SC_OK) { // The subscribe was successful. SAS log, and add headers to the response. TRC_DEBUG("The subscribe has been successful"); SAS::Event sub_accepted(trail_id, SASEvent::SUBSCRIBE_ACCEPTED, 0); SAS::report_event(sub_accepted); // Add expires headers pjsip_expires_hdr* expires_hdr = pjsip_expires_hdr_create(get_pool(rsp), expiry); pjsip_msg_add_hdr(rsp, (pjsip_hdr*)expires_hdr); // Add the contact header pjsip_contact_hdr* contact_hdr = pjsip_contact_hdr_create(get_pool(rsp)); pjsip_name_addr* contact_uri = pjsip_name_addr_create(get_pool(rsp)); contact_uri->uri = (pjsip_uri*)stack_data.scscf_uri; contact_hdr->uri = (pjsip_uri*)contact_uri; pjsip_msg_add_hdr(rsp, (pjsip_hdr*)contact_hdr); // Add a P-Charging-Function-Addresses header to the successful SUBSCRIBE // response containing the charging addresses returned by the HSS. PJUtils::add_pcfa_header(rsp, get_pool(rsp), irs_info._ccfs, irs_info._ecfs, false); } else if (st_code == PJSIP_SC_TEMPORARILY_UNAVAILABLE) { // A 480 response means that the subscriber wasn't registered. SAS log // this. TRC_DEBUG("The subscribe has failed as the subscriber (%s) wasn't registered", public_id.c_str()); SAS::Event event(trail_id, SASEvent::SUBSCRIBE_FAILED_EARLY_NOT_REG, 0); SAS::report_event(event); } else { // The subscribe was unsuccessful. TRC_DEBUG("The subscribe failed with return code %d", st_code); SAS::Event sub_failed(trail_id, SASEvent::SUBSCRIBE_FAILED, 0); sub_failed.add_var_param(public_id); SAS::report_event(sub_failed); } // Add the to tag to the response (even if the subscribe was rejected). pjsip_to_hdr *to = (pjsip_to_hdr*) pjsip_msg_find_hdr(rsp, PJSIP_H_TO, NULL); pj_strdup2(get_pool(rsp), &to->tag, new_subscription->_to_tag.c_str()); // Pass the response to the ACR. acr->tx_response(rsp); // Send the response. send_response(rsp); // Send the ACR and delete it. acr->send(); delete acr; acr = NULL; delete new_subscription; new_subscription = NULL; free_msg(req); }
pj_bool_t authenticate_rx_request(pjsip_rx_data* rdata) { TRC_DEBUG("Authentication module invoked"); pj_status_t status; bool is_register = (rdata->msg_info.msg->line.req.method.id == PJSIP_REGISTER_METHOD); SNMP::SuccessFailCountTable* auth_stats_table = NULL; std::string resync; SAS::TrailId trail = get_trail(rdata); if (!needs_authentication(rdata, trail)) { TRC_DEBUG("Request does not need authentication"); return PJ_FALSE; } TRC_DEBUG("Request needs authentication"); rapidjson::Document* av = NULL; const int unauth_sc = is_register ? PJSIP_SC_UNAUTHORIZED : PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED; int sc = unauth_sc; status = PJSIP_EAUTHNOAUTH; pjsip_digest_credential* credentials = get_credentials(rdata); if ((credentials != NULL) && (credentials->response.slen != 0)) { std::string impi = PJUtils::pj_str_to_string(&credentials->username); std::string nonce = PJUtils::pj_str_to_string(&credentials->nonce); uint64_t cas = 0; av = av_store->get_av(impi, nonce, cas, trail); if (!is_register) { // Challenged non-register requests must be SIP digest, so only one table // needed for this case. auth_stats_table = auth_stats_tables->non_register_auth_tbl; } else { if (!pj_strcmp2(&credentials->algorithm, "MD5")) { auth_stats_table = auth_stats_tables->sip_digest_auth_tbl; } else if (!pj_strcmp2(&credentials->algorithm, "AKAv1-MD5")) { auth_stats_table = auth_stats_tables->ims_aka_auth_tbl; } else { // Authorization header did not specify an algorithm, so check the av for // this information instead. if ((av != NULL) && (av->HasMember("aka"))) { auth_stats_table = auth_stats_tables->ims_aka_auth_tbl; } else { // Use the digest table if the AV specified digest, or as a fallback if there was no AV auth_stats_table = auth_stats_tables->sip_digest_auth_tbl; } } } if (auth_stats_table != NULL) { auth_stats_table->increment_attempts(); } // Request contains a response to a previous challenge, so pass it to // the authentication module to verify. TRC_DEBUG("Verify authentication information in request"); status = pjsip_auth_srv_verify2((is_register ? &auth_srv : &auth_srv_proxy), rdata, &sc, (void*)av); if (status == PJ_SUCCESS) { // The authentication information in the request was verified. TRC_DEBUG("Request authenticated successfully"); SAS::Event event(trail, SASEvent::AUTHENTICATION_SUCCESS, 0); SAS::report_event(event); if (auth_stats_table != NULL) { auth_stats_table->increment_successes(); } // Write a tombstone flag back to the AV store, handling contention. // We don't actually expect anything else to be writing to this row in // the AV store, but there is a window condition where we failed to read // from the primary, successfully read from the backup (with a different // CAS value) and then try to write back to the primary, which fails due // to "contention". Store::Status store_status; do { // Set the tomestone flag in the JSON authentication vector. rapidjson::Value tombstone_value; tombstone_value.SetBool(true); av->AddMember("tombstone", tombstone_value, (*av).GetAllocator()); // Store it. If this fails due to contention, read the updated JSON. store_status = av_store->set_av(impi, nonce, av, cas, trail); if (store_status == Store::DATA_CONTENTION) { // LCOV_EXCL_START - No support for contention in UT TRC_DEBUG("Data contention writing tombstone - retry"); delete av; av = av_store->get_av(impi, nonce, cas, trail); if (av == NULL) { store_status = Store::ERROR; } // LCOV_EXCL_STOP } } while (store_status == Store::DATA_CONTENTION); if (store_status != Store::OK) { // LCOV_EXCL_START TRC_ERROR("Tried to tombstone AV for %s/%s after processing an authentication, but failed", impi.c_str(), nonce.c_str()); // LCOV_EXCL_STOP } // If doing AKA authentication, check for an AUTS parameter. We only // check this if the request authenticated as actioning it otherwise // is a potential denial of service attack. if (!pj_strcmp(&credentials->algorithm, &STR_AKAV1_MD5)) { TRC_DEBUG("AKA authentication so check for client resync request"); pjsip_param* p = pjsip_param_find(&credentials->other_param, &STR_AUTS); if (p != NULL) { // Found AUTS parameter, so UE is requesting a resync. We need to // redo the authentication, passing an auts parameter to the HSS // comprising the first 16 octets of the nonce (RAND) and the 14 // octets of the auts parameter. (See TS 33.203 and table 6.3.3 of // TS 29.228 for details.) TRC_DEBUG("AKA SQN resync request from UE"); std::string auts = PJUtils::pj_str_to_string(&p->value); std::string nonce = PJUtils::pj_str_to_string(&credentials->nonce); // Convert the auts and nonce to binary for manipulation nonce = base64_decode(nonce); auts = base64_decode(auts); if ((auts.length() != 14) || (nonce.length() != 32)) { // AUTS and/or nonce are malformed, so reject the request. TRC_WARNING("Invalid auts/nonce on resync request from private identity %.*s", credentials->username.slen, credentials->username.ptr); status = PJSIP_EAUTHINAKACRED; sc = PJSIP_SC_FORBIDDEN; } else { // auts and nonce are as expected, so create the resync string // that needs to be passed to the HSS, and act as if no // authentication information was received. The resync string // should be RAND || AUTS. resync = base64_encode(nonce.substr(0, 16) + auts); status = PJSIP_EAUTHNOAUTH; sc = unauth_sc; } } } if (status == PJ_SUCCESS) { // Request authentication completed, so let the message through to other // modules. Remove any Proxy-Authorization headers first so they are not // passed to downstream devices. We can't do this for Authorization // headers, as these may need to be included in 3rd party REGISTER // messages. while (pjsip_msg_find_remove_hdr(rdata->msg_info.msg, PJSIP_H_PROXY_AUTHORIZATION, NULL) != NULL); delete av; return PJ_FALSE; } } } // The message either has insufficient authentication information, or // has failed authentication. In either case, the message will be // absorbed and responded to by the authentication module, so we need to // add SAS markers so the trail will become searchable. SAS::Marker start_marker(trail, MARKER_ID_START, 1u); SAS::report_marker(start_marker); // Add a SAS end marker SAS::Marker end_marker(trail, MARKER_ID_END, 1u); SAS::report_marker(end_marker); // Create an ACR for the message and pass the request to it. Role is always // considered originating for a REGISTER request. ACR* acr = acr_factory->get_acr(trail, CALLING_PARTY, NODE_ROLE_ORIGINATING); acr->rx_request(rdata->msg_info.msg, rdata->pkt_info.timestamp); pjsip_tx_data* tdata; if ((status == PJSIP_EAUTHNOAUTH) || (status == PJSIP_EAUTHACCNOTFOUND)) { // No authorization information in request, or no authentication vector // found in the store (so request is likely stale), so must issue // challenge. TRC_DEBUG("No authentication information in request or stale nonce, so reject with challenge"); pj_bool_t stale = (status == PJSIP_EAUTHACCNOTFOUND); sc = unauth_sc; if (stale && auth_stats_table != NULL) { auth_stats_table->increment_failures(); } status = PJUtils::create_response(stack_data.endpt, rdata, sc, NULL, &tdata); if (status != PJ_SUCCESS) { // Failed to create a response. This really shouldn't happen, but there // is nothing else we can do. // LCOV_EXCL_START delete acr; return PJ_TRUE; // LCOV_EXCL_STOP } create_challenge(credentials, stale, resync, rdata, tdata); } else { // Authentication failed. std::string error_msg = PJUtils::pj_status_to_string(status); TRC_ERROR("Authentication failed, %s", error_msg.c_str()); if (auth_stats_table != NULL) { auth_stats_table->increment_failures(); } SAS::Event event(trail, SASEvent::AUTHENTICATION_FAILED, 0); event.add_var_param(error_msg); SAS::report_event(event); if (sc != unauth_sc) { // Notify Homestead and the HSS that this authentication attempt // has definitively failed. std::string impi; std::string impu; PJUtils::get_impi_and_impu(rdata, impi, impu); hss->update_registration_state(impu, impi, HSSConnection::AUTH_FAIL, trail); } if (analytics != NULL) { analytics->auth_failure(PJUtils::pj_str_to_string(&credentials->username), PJUtils::public_id_from_uri((pjsip_uri*)pjsip_uri_get_uri(PJSIP_MSG_TO_HDR(rdata->msg_info.msg)->uri))); } status = PJUtils::create_response(stack_data.endpt, rdata, sc, NULL, &tdata); if (status != PJ_SUCCESS) { // Failed to create a response. This really shouldn't happen, but there // is nothing else we can do. // LCOV_EXCL_START delete acr; return PJ_TRUE; // LCOV_EXCL_STOP } } acr->tx_response(tdata->msg); // Issue the challenge response transaction-statefully. This is so that: // * if we challenge an INVITE, the UE can ACK the 407 // * if a challenged request gets retransmitted, we don't repeat the work pjsip_transaction* tsx = NULL; status = pjsip_tsx_create_uas2(NULL, rdata, NULL, &tsx); set_trail(tsx, trail); if (status != PJ_SUCCESS) { // LCOV_EXCL_START - defensive code not hit in UT TRC_WARNING("Couldn't create PJSIP transaction for authentication response: %d" " (sending statelessly instead)", status); // Send the response statelessly in this case - it's better than nothing pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); // LCOV_EXCL_STOP } else { // Let the tsx know about the original message pjsip_tsx_recv_msg(tsx, rdata); // Send our response in this transaction pjsip_tsx_send_msg(tsx, tdata); } // Send the ACR. acr->send(); delete acr; delete av; return PJ_TRUE; }