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; } }
/// Rejects a request statelessly. void ICSCFProxy::reject_request(pjsip_rx_data* rdata, int status_code) { pj_status_t status; ACR* acr = _acr_factory->get_acr(get_trail(rdata), CALLING_PARTY); acr->rx_request(rdata->msg_info.msg, rdata->pkt_info.timestamp); if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { LOG_ERROR("Rejecting %.*s request with %d status code", rdata->msg_info.msg->line.req.method.name.slen, rdata->msg_info.msg->line.req.method.name.ptr, status_code); pjsip_tx_data* tdata; status = PJUtils::create_response(stack_data.endpt, rdata, status_code, NULL, &tdata); if (status == PJ_SUCCESS) { // Pass the response to the ACR. acr->tx_response(tdata->msg); status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); if (status != PJ_SUCCESS) { // LCOV_EXCL_START pjsip_tx_data_dec_ref(tdata); // LCOV_EXCL_STOP } } } // Send the ACR and delete it. acr->send_message(); delete acr; }
/// Handle a received CANCEL request. void ICSCFProxy::UASTsx::process_cancel_request(pjsip_rx_data* rdata) { // Pass the CANCEL to the BasicProxy code to handle. BasicProxy::UASTsx::process_cancel_request(rdata); // Create and send an ACR for the CANCEL request. ACR* acr = create_acr(); acr->rx_request(rdata->msg_info.msg, rdata->pkt_info.timestamp); acr->send_message(); 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 process_subscription_request(pjsip_rx_data* rdata) { pj_status_t status; int st_code = PJSIP_SC_OK; SAS::TrailId trail = get_trail(rdata); // 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(rdata->msg_info.to->uri); pjsip_msg *msg = rdata->msg_info.msg; pjsip_expires_hdr* expires = (pjsip_expires_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); int expiry = (expires != NULL) ? expires->ivalue : DEFAULT_SUBSCRIPTION_EXPIRES; if (expiry > max_expires) { // Expiry is too long, set it to the maximum. expiry = max_expires; } 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). LOG_ERROR("Rejecting subscribe request using invalid URI scheme"); SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_URLSCHEME, 0); // Can't log the public ID as the subscribe has failed too early SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_NOT_FOUND, NULL, NULL, NULL); return; } bool emergency_subscription = false; pjsip_contact_hdr* contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL); 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(rdata->msg_info.msg, 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. LOG_ERROR("Rejecting subscribe request from emergency registration"); SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED_EARLY_EMERGENCY, 0); // Can't log the public ID as the subscribe has failed too early SAS::report_event(event); // Allow-Events is a mandatory header on 489 responses. pjsip_generic_string_hdr* allow_events_hdr = pjsip_generic_string_hdr_create(rdata->tp_info.pool, &STR_ALLOW_EVENTS, &STR_REG); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_BAD_EVENT, NULL, (pjsip_hdr*)allow_events_hdr, NULL); return; } ACR* acr = acr_factory->get_acr(get_trail(rdata), CALLING_PARTY, ACR::requested_node_role(rdata->msg_info.msg)); acr->rx_request(rdata->msg_info.msg, rdata->pkt_info.timestamp); // Canonicalize the public ID from the URI in the To header. std::string public_id = PJUtils::public_id_from_uri(uri); LOG_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((const pj_str_t*)&rdata->msg_info.cid->id);; // Add SAS markers to the trail attached to the message so the trail // becomes searchable. SAS::Event event(trail, SASEvent::SUBSCRIBE_START, 0); event.add_var_param(public_id); SAS::report_event(event); LOG_DEBUG("Report SAS start marker - trail (%llx)", trail); SAS::Marker start_marker(trail, MARKER_ID_START, 1u); SAS::report_marker(start_marker); PJUtils::report_sas_to_from_markers(trail, rdata->msg_info.msg); PJUtils::mark_sas_call_branch_ids(trail, NULL, rdata->msg_info.msg); // Query the HSS for the associated URIs. std::vector<std::string> uris; std::map<std::string, Ifcs> ifc_map; // Subscriber must have already registered to be making a subscribe std::string state; HTTPCode http_code = hss->get_registration_data(public_id, state, ifc_map, uris, trail); if ((http_code != HTTP_OK) || (state != "REGISTERED")) { // We failed to get the list of associated URIs. This indicates that the // HSS is unavailable, the public identity doesn't exist or the public // identity doesn't belong to the private identity. // The client shouldn't retry when the subscriber isn't present in the // HSS; reject with a 403 in this case. // // The client should retry on timeout but no other Clearwater nodes should // (as Sprout will already have retried on timeout). Reject with a 504 // (503 is used for overload). st_code = PJSIP_SC_SERVER_TIMEOUT; if (http_code == HTTP_NOT_FOUND) { st_code = PJSIP_SC_FORBIDDEN; } LOG_ERROR("Rejecting SUBSCRIBE request"); PJUtils::respond_stateless(stack_data.endpt, rdata, st_code, NULL, NULL, NULL); delete acr; return; } // Determine the AOR from the first entry in the uris array. std::string aor = uris.front(); LOG_DEBUG("aor = %s", aor.c_str()); LOG_DEBUG("SUBSCRIBE for public ID %s uses AOR %s", public_id.c_str(), aor.c_str()); // Get the system time in seconds for calculating absolute expiry times. int now = time(NULL); // Write to the local store, checking the remote store if there is no entry locally. If the write to the local store succeeds, then write to the remote store. pjsip_tx_data* tdata_notify = NULL; RegStore::AoR* aor_data = NULL; std::string subscription_id; pj_status_t notify_status = write_subscriptions_to_store(store, aor, rdata, now, NULL, remote_store, &tdata_notify, &aor_data, true, subscription_id, trail); if (aor_data != NULL) { // Log the subscriptions. log_subscriptions(aor, aor_data); // If we have a remote store, try to store this there too. We don't worry // about failures in this case. if (remote_store != NULL) { RegStore::AoR* remote_aor_data = NULL; std::string ignore; write_subscriptions_to_store(remote_store, aor, rdata, now, aor_data, NULL, &tdata_notify, &remote_aor_data, false, ignore, trail); delete remote_aor_data; } } else { // Failed to connect to the local store. Reject the subscribe with a 500 // response. // LCOV_EXCL_START - the can't fail to connect to the store we use for UT st_code = PJSIP_SC_INTERNAL_SERVER_ERROR; // LCOV_EXCL_STOP } // Build and send the reply. pjsip_tx_data* tdata; status = PJUtils::create_response(stack_data.endpt, rdata, st_code, NULL, &tdata); if (status != PJ_SUCCESS) { // LCOV_EXCL_START - don't know how to get PJSIP to fail to create a response LOG_ERROR("Error building SUBSCRIBE %d response %s", st_code, PJUtils::pj_status_to_string(status).c_str()); SAS::Event event(trail, SASEvent::SUBSCRIBE_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Error building SUBSCRIBE (" + std::to_string(st_code) + ") " + PJUtils::pj_status_to_string(status); event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL, NULL); delete acr; delete aor_data; return; // LCOV_EXCL_STOP } // Add expires headers pjsip_expires_hdr* expires_hdr = pjsip_expires_hdr_create(tdata->pool, expiry); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr); // Add the to tag to the response pjsip_to_hdr *to = (pjsip_to_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_TO, NULL); pj_strdup2(tdata->pool, &to->tag, subscription_id.c_str()); // Pass the response to the ACR. acr->tx_response(tdata->msg); // Send the response. status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); // Send the ACR and delete it. acr->send_message(); delete acr; // Send the Notify if (tdata_notify != NULL && notify_status == PJ_SUCCESS) { set_trail(tdata_notify, trail); status = PJUtils::send_request(tdata_notify, 0, NULL, NULL, true); if (status != PJ_SUCCESS) { // LCOV_EXCL_START SAS::Event event(trail, SASEvent::NOTIFICATION_FAILED, 0); std::string error_msg = "Failed to send NOTIFY - error code: " + std::to_string(status); event.add_var_param(error_msg); SAS::report_event(event); // LCOV_EXCL_STOP } } LOG_DEBUG("Report SAS end marker - trail (%llx)", trail); SAS::Marker end_marker(trail, MARKER_ID_END, 1u); SAS::report_marker(end_marker); delete aor_data; }
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); }
void process_register_request(pjsip_rx_data* rdata) { pj_status_t status; int st_code = PJSIP_SC_OK; SAS::TrailId trail = get_trail(rdata); // 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(rdata->msg_info.to->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). LOG_ERROR("Rejecting register request using invalid URI scheme"); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); // Can't log the public ID as the REGISTER has failed too early std::string public_id = "UNKNOWN"; std::string error_msg = "Rejecting register request using invalid URI scheme"; event.add_var_param(public_id); event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_NOT_FOUND, NULL, NULL, NULL); return; } // Allocate an ACR for this transaction and pass the request to it. ACR* acr = acr_factory->get_acr(get_trail(rdata), CALLING_PARTY, ACR::requested_node_role(rdata->msg_info.msg)); acr->rx_request(rdata->msg_info.msg, rdata->pkt_info.timestamp); // Canonicalize the public ID from the URI in the To header. std::string public_id = PJUtils::public_id_from_uri(uri); LOG_DEBUG("Process REGISTER for public ID %s", public_id.c_str()); // Get the call identifier and the cseq number from the respective headers. std::string cid = PJUtils::pj_str_to_string((const pj_str_t*)&rdata->msg_info.cid->id);; pjsip_msg *msg = rdata->msg_info.msg; // Add SAS markers to the trail attached to the message so the trail // becomes searchable. LOG_DEBUG("Report SAS start marker - trail (%llx)", trail); SAS::Marker start_marker(trail, MARKER_ID_START, 1u); SAS::report_marker(start_marker); PJUtils::report_sas_to_from_markers(trail, rdata->msg_info.msg); PJUtils::mark_sas_call_branch_ids(trail, NULL, rdata->msg_info.msg); // Query the HSS for the associated URIs. std::vector<std::string> uris; std::map<std::string, Ifcs> ifc_map; std::string private_id; std::string private_id_for_binding; bool success = get_private_id(rdata, private_id); if (!success) { // There are legitimate cases where we don't have a private ID // here (for example, on a re-registration where Bono has set the // Integrity-Protected header), so this is *not* a failure // condition. // We want the private ID here so that Homestead can use it to // subscribe for updates from the HSS - but on a re-registration, // Homestead should already have subscribed for updates during the // initial registration, so we can just make a request using our // public ID. private_id = ""; // IMS compliant clients will always have the Auth header on all REGISTERs, // including reREGISTERS. Non-IMS clients won't, but their private ID // will always be the public ID with the sip: removed. private_id_for_binding = PJUtils::default_private_id_from_uri(uri); } else { private_id_for_binding = private_id; } SAS::Event event(trail, SASEvent::REGISTER_START, 0); event.add_var_param(public_id); event.add_var_param(private_id); SAS::report_event(event); std::string regstate; std::deque<std::string> ccfs; std::deque<std::string> ecfs; HTTPCode http_code = hss->update_registration_state(public_id, private_id, HSSConnection::REG, regstate, ifc_map, uris, ccfs, ecfs, trail); if ((http_code != HTTP_OK) || (regstate != HSSConnection::STATE_REGISTERED)) { // We failed to register this subscriber at the HSS. This indicates that the // HSS is unavailable, the public identity doesn't exist or the public // identity doesn't belong to the private identity. // The client shouldn't retry when the subscriber isn't present in the // HSS; reject with a 403 in this case. // // The client should retry on timeout but no other Clearwater nodes should // (as Sprout will already have retried on timeout). Reject with a 504 // (503 is used for overload). st_code = PJSIP_SC_SERVER_TIMEOUT; if (http_code == HTTP_NOT_FOUND) { st_code = PJSIP_SC_FORBIDDEN; } LOG_ERROR("Rejecting register request with invalid public/private identity"); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Rejecting register request with invalid public/private identity"; event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, st_code, NULL, NULL, NULL); delete acr; return; } // Determine the AOR from the first entry in the uris array. std::string aor = uris.front(); LOG_DEBUG("REGISTER for public ID %s uses AOR %s", public_id.c_str(), aor.c_str()); // Get the system time in seconds for calculating absolute expiry times. int now = time(NULL); int expiry = 0; bool is_initial_registration; // Loop through each contact header. If every registration is an emergency // registration and its expiry is 0 then reject with a 501. // If there are valid registration updates to make then attempt to write to // store, which also stops emergency registrations from being deregistered. bool reject_with_501 = true; bool any_emergency_registrations = false; bool reject_with_400 = false; pjsip_contact_hdr* contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL); while (contact_hdr != NULL) { pjsip_expires_hdr* expires = (pjsip_expires_hdr*)pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL); expiry = (contact_hdr->expires != -1) ? contact_hdr->expires : (expires != NULL) ? expires->ivalue : max_expires; if ((contact_hdr->star) && (expiry != 0)) { // Wildcard contact, which can only be used if the expiry is 0 LOG_ERROR("Attempted to deregister all bindings, but expiry value wasn't 0"); reject_with_400 = true; break; } reject_with_501 = (reject_with_501 && PJUtils::is_emergency_registration(contact_hdr) && (expiry == 0)); any_emergency_registrations = (any_emergency_registrations || PJUtils::is_emergency_registration(contact_hdr)); contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr->next); } if (reject_with_400) { SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Rejecting register request with invalid contact header"; event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_BAD_REQUEST, NULL, NULL, NULL); delete acr; return; } if (reject_with_501) { LOG_ERROR("Rejecting register request as attempting to deregister an emergency registration"); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Rejecting deregister request for emergency registrations"; event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_NOT_IMPLEMENTED, NULL, NULL, NULL); delete acr; return; } // Write to the local store, checking the remote store if there is no entry locally. RegStore::AoR* aor_data = write_to_store(store, aor, rdata, now, expiry, is_initial_registration, NULL, remote_store, true, private_id_for_binding, trail); if (aor_data != NULL) { // Log the bindings. log_bindings(aor, aor_data); // If we have a remote store, try to store this there too. We don't worry // about failures in this case. if (remote_store != NULL) { int tmp_expiry = 0; bool ignored; RegStore::AoR* remote_aor_data = write_to_store(remote_store, aor, rdata, now, tmp_expiry, ignored, aor_data, NULL, false, private_id_for_binding, trail); delete remote_aor_data; } } else { // Failed to connect to the local store. Reject the register with a 500 // response. // LCOV_EXCL_START - the can't fail to connect to the store we use for UT st_code = PJSIP_SC_INTERNAL_SERVER_ERROR; SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Unable to access Registration Store"; event.add_var_param(error_msg); SAS::report_event(event); // LCOV_EXCL_STOP } // Build and send the reply. pjsip_tx_data* tdata; status = PJUtils::create_response(stack_data.endpt, rdata, st_code, NULL, &tdata); if (status != PJ_SUCCESS) { // LCOV_EXCL_START - don't know how to get PJSIP to fail to create a response std::string error_msg = "Error building REGISTER " + std::to_string(status) + " response " + PJUtils::pj_status_to_string(status); LOG_ERROR(error_msg.c_str()); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); event.add_var_param(error_msg); SAS::report_event(event); PJUtils::respond_stateless(stack_data.endpt, rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL, NULL); delete acr; delete aor_data; return; // LCOV_EXCL_STOP } if (st_code != PJSIP_SC_OK) { // LCOV_EXCL_START - we only reject REGISTER if something goes wrong, and // we aren't covering any of those paths so we can't hit this either status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "REGISTER failed with status code: " + std::to_string(st_code); event.add_var_param(error_msg); SAS::report_event(event); delete acr; delete aor_data; return; // LCOV_EXCL_STOP } // Add supported and require headers for RFC5626. pjsip_generic_string_hdr* gen_hdr; gen_hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_SUPPORTED, &STR_OUTBOUND); if (gen_hdr == NULL) { // LCOV_EXCL_START - can't see how this could ever happen LOG_ERROR("Failed to add RFC 5626 headers"); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Failed to add RFC 5636 headers"; event.add_var_param(error_msg); SAS::report_event(event); tdata->msg->line.status.code = PJSIP_SC_INTERNAL_SERVER_ERROR; pjsip_tx_data_invalidate_msg(tdata); status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); delete acr; delete aor_data; return; // LCOV_EXCL_STOP } pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gen_hdr); // Add contact headers for all active bindings. for (RegStore::AoR::Bindings::const_iterator i = aor_data->bindings().begin(); i != aor_data->bindings().end(); ++i) { RegStore::AoR::Binding* binding = i->second; if (binding->_expires > now) { // The binding hasn't expired. Parse the Contact URI from the store, // making sure it is formatted as a name-address. pjsip_uri* uri = PJUtils::uri_from_string(binding->_uri, tdata->pool, PJ_TRUE); if (uri != NULL) { // Contact URI is well formed, so include this in the response. pjsip_contact_hdr* contact = pjsip_contact_hdr_create(tdata->pool); contact->star = 0; contact->uri = uri; contact->q1000 = binding->_priority; contact->expires = binding->_expires - now; pj_list_init(&contact->other_param); for (std::map<std::string, std::string>::iterator j = binding->_params.begin(); j != binding->_params.end(); ++j) { pjsip_param *new_param = PJ_POOL_ALLOC_T(tdata->pool, pjsip_param); pj_strdup2(tdata->pool, &new_param->name, j->first.c_str()); pj_strdup2(tdata->pool, &new_param->value, j->second.c_str()); pj_list_insert_before(&contact->other_param, new_param); } // The pub-gruu parameter on the Contact header is calculated // from the instance-id, to avoid unnecessary storage in // memcached. std::string gruu = binding->pub_gruu_quoted_string(tdata->pool); if (!gruu.empty()) { pjsip_param *new_param = PJ_POOL_ALLOC_T(tdata->pool, pjsip_param); pj_strdup2(tdata->pool, &new_param->name, "pub-gruu"); pj_strdup2(tdata->pool, &new_param->value, gruu.c_str()); pj_list_insert_before(&contact->other_param, new_param); } pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)contact); } else { // Contact URI is malformed. Log an error, but otherwise don't try and // fix it. // LCOV_EXCL_START hard to hit - needs bad data in the store LOG_WARNING("Badly formed contact URI %s for address of record %s", binding->_uri.c_str(), aor.c_str()); SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0); event.add_var_param(public_id); std::string error_msg = "Badly formed contact URI - " + binding->_uri; event.add_var_param(error_msg); SAS::report_event(event); // LCOV_EXCL_STOP } } } // Deal with path header related fields in the response. pjsip_routing_hdr* path_hdr = (pjsip_routing_hdr*) pjsip_msg_find_hdr_by_name(msg, &STR_PATH, NULL); if ((path_hdr != NULL) && (!aor_data->bindings().empty())) { // We have bindings with path headers so we must require outbound. pjsip_require_hdr* require_hdr = pjsip_require_hdr_create(tdata->pool); require_hdr->count = 1; require_hdr->values[0] = STR_OUTBOUND; pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)require_hdr); } // Echo back any Path headers as per RFC 3327, section 5.3. We take these // from the request as they may not exist in the bindings any more if the // bindings have expired. while (path_hdr) { pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, path_hdr)); path_hdr = (pjsip_routing_hdr*) pjsip_msg_find_hdr_by_name(msg, &STR_PATH, path_hdr->next); } // Add the Service-Route header. It isn't safe to do this with the // pre-built header from the global pool because the chaining data // structures in the header may get overwritten, but it is safe to do a // shallow clone. pjsip_hdr* clone = (pjsip_hdr*) pjsip_hdr_shallow_clone(tdata->pool, service_route); pjsip_msg_insert_first_hdr(tdata->msg, clone); // Add P-Associated-URI headers for all of the associated URIs. for (std::vector<std::string>::iterator it = uris.begin(); it != uris.end(); it++) { pjsip_routing_hdr* pau = identity_hdr_create(tdata->pool, STR_P_ASSOCIATED_URI); pau->name_addr.uri = PJUtils::uri_from_string(*it, tdata->pool); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pau); } // Add a PCFA header. PJUtils::add_pcfa_header(tdata->msg, tdata->pool, ccfs, ecfs, true); // Pass the response to the ACR. acr->tx_response(tdata->msg); // Send the response, but prevent the transmitted data from being freed, as we may need to inform the // ASes of the 200 OK response we sent. pjsip_tx_data_add_ref(tdata); status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); // Send the ACR and delete it. acr->send_message(); delete acr; // TODO in sto397: we should do third-party registration once per // service profile (i.e. once per iFC, using an arbitrary public // ID). hss->get_subscription_data should be enhanced to provide an // appropriate data structure (representing the ServiceProfile // nodes) and we should loop through that. Don't send any register that // contained emergency registrations to the application servers. if (!any_emergency_registrations) { RegistrationUtils::register_with_application_servers(ifc_map[public_id], store, rdata, tdata, expiry, is_initial_registration, public_id, trail); } // Now we can free the tdata. pjsip_tx_data_dec_ref(tdata); LOG_DEBUG("Report SAS end marker - trail (%llx)", trail); SAS::Marker end_marker(trail, MARKER_ID_END, 1u); SAS::report_marker(end_marker); delete aor_data; }
pj_bool_t authenticate_rx_request(pjsip_rx_data* rdata) { pj_status_t status; std::string resync; SAS::TrailId trail = get_trail(rdata); if (rdata->tp_info.transport->local_name.port != stack_data.scscf_port) { // Request not received on S-CSCF port, so don't authenticate it. std::string error_msg = "Request wasn't received on S-CSCF port"; log_sas_auth_not_needed_event(trail, error_msg); return PJ_FALSE; } if (rdata->msg_info.msg->line.req.method.id != PJSIP_REGISTER_METHOD) { // Non-REGISTER request, so don't do authentication as it must have come // from an authenticated or trusted source. std::string error_msg = "Request wasn't a REGISTER"; log_sas_auth_not_needed_event(trail, error_msg); return PJ_FALSE; } // Authentication isn't required for emergency registrations. An emergency // registration is one where each Contact header contains 'sos' as the SIP // URI parameter. bool emergency_reg = true; pjsip_contact_hdr* contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL); while ((contact_hdr != NULL) && (emergency_reg)) { emergency_reg = PJUtils::is_emergency_registration(contact_hdr); contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr->next); } if (emergency_reg) { std::string error_msg = "Request is an emergency REGISTER"; log_sas_auth_not_needed_event(trail, error_msg); return PJ_FALSE; } // Check to see if the request has already been integrity protected? pjsip_authorization_hdr* auth_hdr = (pjsip_authorization_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION, NULL); if ((auth_hdr != NULL) && (auth_hdr->credential.digest.response.slen == 0)) { // There is an authorization header with no challenge response, so check // for the integrity-protected indication. LOG_DEBUG("Authorization header in request with no challenge response"); pjsip_param* integrity = pjsip_param_find(&auth_hdr->credential.digest.other_param, &STR_INTEGRITY_PROTECTED); // Request has an integrity protected indication, so let it through if // it is set to a "yes" value. if ((integrity != NULL) && ((pj_stricmp(&integrity->value, &STR_YES) == 0) || (pj_stricmp(&integrity->value, &STR_TLS_YES) == 0) || (pj_stricmp(&integrity->value, &STR_IP_ASSOC_YES) == 0))) { // Request is already integrity protected, so let it through. LOG_INFO("Request integrity protected by edge proxy"); std::string error_msg = "Request integrity protected by edge proxy"; log_sas_auth_not_needed_event(trail, error_msg); return PJ_FALSE; } } int sc = PJSIP_SC_UNAUTHORIZED; status = PJSIP_EAUTHNOAUTH; if ((auth_hdr != NULL) && (auth_hdr->credential.digest.response.slen != 0)) { std::string impi = PJUtils::pj_str_to_string(&auth_hdr->credential.digest.username); std::string nonce = PJUtils::pj_str_to_string(&auth_hdr->credential.digest.nonce); uint64_t cas = 0; Json::Value* av = av_store->get_av(impi, nonce, cas, trail); // Request contains a response to a previous challenge, so pass it to // the authentication module to verify. LOG_DEBUG("Verify authentication information in request"); status = pjsip_auth_srv_verify2(&auth_srv, rdata, &sc, (void*)av); if (status == PJ_SUCCESS) { // The authentication information in the request was verified. LOG_DEBUG("Request authenticated successfully"); SAS::Event event(trail, SASEvent::AUTHENTICATION_SUCCESS, 0); SAS::report_event(event); (*av)["tombstone"] = Json::Value("true"); bool rc = av_store->set_av(impi, nonce, av, cas, trail); if (!rc) { // LCOV_EXCL_START LOG_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(&auth_hdr->credential.digest.algorithm, &STR_AKAV1_MD5)) { LOG_DEBUG("AKA authentication so check for client resync request"); pjsip_param* p = pjsip_param_find(&auth_hdr->credential.digest.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.) LOG_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(&auth_hdr->credential.digest.nonce); if ((auts.length() != 14) || (nonce.length() != 32)) { // AUTS and/or nonce are malformed, so reject the request. LOG_WARNING("Invalid auts/nonce on resync request from private identity %.*s", auth_hdr->credential.digest.username.slen, auth_hdr->credential.digest.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. resync = nonce.substr(0,16) + auts; status = PJSIP_EAUTHNOAUTH; sc = PJSIP_SC_UNAUTHORIZED; } } } if (status == PJ_SUCCESS) { // Request authentication completed, so let the message through to // other modules. delete av; return PJ_FALSE; } } delete av; } // 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); PJUtils::report_sas_to_from_markers(trail, rdata->msg_info.msg); PJUtils::mark_sas_call_branch_ids(trail, NULL, rdata->msg_info.msg); // 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. ACR* acr = acr_factory->get_acr(trail, CALLING_PARTY, ACR::requested_node_role(rdata->msg_info.msg)); 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. LOG_DEBUG("No authentication information in request or stale nonce, so reject with challenge"); sc = PJSIP_SC_UNAUTHORIZED; 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(auth_hdr, resync, rdata, tdata); } else { // Authentication failed. std::string error_msg = PJUtils::pj_status_to_string(status); LOG_ERROR("Authentication failed, %s", error_msg.c_str()); SAS::Event event(trail, SASEvent::AUTHENTICATION_FAILED, 0); event.add_var_param(error_msg); SAS::report_event(event); if (sc != PJSIP_SC_UNAUTHORIZED) { // 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, 0); } if (analytics != NULL) { analytics->auth_failure(PJUtils::pj_str_to_string(&auth_hdr->credential.digest.username), PJUtils::public_id_from_uri((pjsip_uri*)pjsip_uri_get_uri(PJSIP_MSG_TO_HDR(rdata->msg_info.msg)->uri))); } // @TODO - need more diagnostics here so we can identify and flag // attacks. 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); status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL); // Send the ACR. acr->send_message(); delete acr; return PJ_TRUE; }
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; }