/*! * \internal * \brief Create an identity header for an outgoing message * \param hdr_name The name of the header to create * \param tdata The message to place the header on * \param id The identification information for the new header * \return newly-created header */ static pjsip_fromto_hdr *create_new_id_hdr(const pj_str_t *hdr_name, pjsip_tx_data *tdata, const struct ast_party_id *id) { pjsip_fromto_hdr *id_hdr; pjsip_fromto_hdr *base; pjsip_name_addr *id_name_addr; pjsip_sip_uri *id_uri; base = tdata->msg->type == PJSIP_REQUEST_MSG ? PJSIP_MSG_FROM_HDR(tdata->msg) : PJSIP_MSG_TO_HDR(tdata->msg); id_hdr = pjsip_from_hdr_create(tdata->pool); id_hdr->type = PJSIP_H_OTHER; pj_strdup(tdata->pool, &id_hdr->name, hdr_name); id_hdr->sname.slen = 0; id_name_addr = pjsip_uri_clone(tdata->pool, base->uri); id_uri = pjsip_uri_get_uri(id_name_addr->uri); if (id->name.valid) { int name_buf_len = strlen(id->name.str) * 2 + 1; char *name_buf = ast_alloca(name_buf_len); ast_escape_quoted(id->name.str, name_buf, name_buf_len); pj_strdup2(tdata->pool, &id_name_addr->display, name_buf); } pj_strdup2(tdata->pool, &id_uri->user, id->number.str); id_hdr->uri = (pjsip_uri *) id_name_addr; return id_hdr; }
/// Apply the mangalgorithm to the To URI of req. void MangelwurzelTsx::mangle_to(pjsip_msg* req, pj_pool_t* pool) { pjsip_to_hdr* to_hdr = PJSIP_MSG_TO_HDR(req); if (to_hdr != NULL) { mangle_uri((pjsip_uri*)pjsip_uri_get_uri(to_hdr->uri), pool, false); } }
void SessionExpiresHelper::process_request(pjsip_msg* req, pj_pool_t* pool, SAS::TrailId trail) { // Session expires is only allowed on INVITE and UPDATE methods. pjsip_method* method = &req->line.req.method; if ((pjsip_method_cmp(method, pjsip_get_invite_method()) != 0) && (pjsip_method_cmp(method, &METHOD_UPDATE) != 0)) { return; } // Store if this is the initial transaction on a dialog, and if the UAC // supports session timers. We need both of these when processing the // response. _initial_request = (PJSIP_MSG_TO_HDR(req)->tag.slen == 0); _uac_supports_timer = timer_supported(req); // Find the session-expires header (if present) and the minimum // session-expires. Note that the latter has a default value. pjsip_session_expires_hdr* se_hdr = (pjsip_session_expires_hdr*) pjsip_msg_find_hdr_by_name(req, &STR_SESSION_EXPIRES, NULL); pjsip_min_se_hdr* min_se_hdr = (pjsip_min_se_hdr*) pjsip_msg_find_hdr_by_name(req, &STR_MIN_SE, NULL); SessionInterval min_se = (min_se_hdr != NULL) ? min_se_hdr->expires : DEFAULT_MIN_SE; if ((se_hdr != NULL) && (se_hdr->expires < _target_se)) { // The request already has a session expires that is below our target. We // don't need to change the value. TRC_DEBUG("Session expires already set to %d", se_hdr->expires); } else { // No pre-existing session expires, or the current value is greater than // our target. Set it to as close to our target as possible, but don't set // it below the min-SE. if (se_hdr == NULL) { se_hdr = pjsip_session_expires_hdr_create(pool); pjsip_msg_add_hdr(req, (pjsip_hdr*)se_hdr); } se_hdr->expires = std::max(_target_se, min_se); TRC_DEBUG("Set session expires to %d", se_hdr->expires); } // Make a note of the session expires (we may need it when processing the // response) _se_on_req = se_hdr->expires; }
void PJUtils::add_integrity_protected_indication(pjsip_tx_data* tdata, Integrity integrity) { pjsip_authorization_hdr* auth_hdr = (pjsip_authorization_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_AUTHORIZATION, NULL); if (auth_hdr == NULL) { auth_hdr = pjsip_authorization_hdr_create(tdata->pool); auth_hdr->scheme = pj_str("Digest"); // Construct a default private identifier from the URI in the To header. LOG_DEBUG("Construct default private identity"); pjsip_uri* to_uri = (pjsip_uri*)pjsip_uri_get_uri(PJSIP_MSG_TO_HDR(tdata->msg)->uri); std::string private_id = PJUtils::default_private_id_from_uri(to_uri); pj_strdup2(tdata->pool, &auth_hdr->credential.digest.username, private_id.c_str()); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)auth_hdr); } pjsip_param* new_param = (pjsip_param*) pj_pool_alloc(tdata->pool, sizeof(pjsip_param)); new_param->name = STR_INTEGRITY_PROTECTED; switch (integrity) { case Integrity::YES: new_param->value = STR_YES; break; case Integrity::NO: new_param->value = STR_NO; break; case Integrity::TLS_YES: new_param->value = STR_TLS_YES; break; case Integrity::TLS_PENDING: new_param->value = STR_TLS_PENDING; break; case Integrity::IP_ASSOC_YES: new_param->value = STR_IP_ASSOC_YES; break; case Integrity::IP_ASSOC_PENDING: new_param->value = STR_IP_ASSOC_PENDING; break; case Integrity::AUTH_DONE: new_param->value = STR_AUTH_DONE; break; default: break; } LOG_INFO("Adding integrity-protected=%.*s indicator to message", new_param->value.slen, new_param->value.ptr); pj_list_insert_before(&auth_hdr->credential.common.other_param, new_param); }
static int diversion_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata) { pjsip_fromto_hdr *hdr = get_diversion_header(rdata); if (hdr) { set_redirecting(session, hdr, (pjsip_name_addr*) PJSIP_MSG_TO_HDR(rdata->msg_info.msg)->uri); } return 0; }
/*! * \internal * \brief Overwrite fields in the outbound 'To' header * * Updates display name in an outgoing To header. * * \param tdata the outbound message data structure * \param to info to copy into the header */ static void update_to(pjsip_tx_data *tdata, char *to) { pjsip_name_addr *name_addr = (pjsip_name_addr *) PJSIP_MSG_TO_HDR(tdata->msg)->uri; pjsip_uri *parsed; if ((parsed = pjsip_parse_uri(tdata->pool, to, strlen(to), PJSIP_PARSE_URI_AS_NAMEADDR))) { pjsip_name_addr *parsed_name_addr = (pjsip_name_addr *)parsed; if (pj_strlen(&parsed_name_addr->display)) { pj_strdup(tdata->pool, &name_addr->display, &parsed_name_addr->display); } } }
/// Apply the mangalgorithm to the From tag, To tag (if present) and call ID of /// req. void MangelwurzelTsx::mangle_dialog_identifiers(pjsip_msg* req, pj_pool_t* pool) { pjsip_from_hdr* from_hdr = PJSIP_MSG_FROM_HDR(req); if (from_hdr != NULL) { std::string from_tag = PJUtils::pj_str_to_string(&from_hdr->tag); mangle_string(from_tag); TRC_DEBUG("From tag mangled to %s", from_tag.c_str()); from_hdr->tag = pj_strdup3(pool, from_tag.c_str()); } pjsip_to_hdr* to_hdr = PJSIP_MSG_TO_HDR(req); if (to_hdr != NULL) { std::string to_tag = PJUtils::pj_str_to_string(&to_hdr->tag); mangle_string(to_tag); TRC_DEBUG("To tag mangled to %s", to_tag.c_str()); to_hdr->tag = pj_strdup3(pool, to_tag.c_str()); } pjsip_cid_hdr* cid_hdr = (pjsip_cid_hdr*)pjsip_msg_find_hdr(req, PJSIP_H_CALL_ID, NULL); if (cid_hdr != NULL) { std::string call_id = PJUtils::pj_str_to_string(&cid_hdr->id); mangle_string(call_id); TRC_DEBUG("Call ID manged to %s", call_id.c_str()); cid_hdr->id = pj_strdup3(pool, call_id.c_str()); // Report a SAS marker for the new call ID so that the two dialogs can be // correlated in SAS. TRC_DEBUG("Logging SAS Call-ID marker, Call-ID %.*s", cid_hdr->id.slen, cid_hdr->id.ptr); SAS::Marker cid_marker(trail(), MARKER_ID_SIP_CALL_ID, 1u); cid_marker.add_var_param(cid_hdr->id.slen, cid_hdr->id.ptr); SAS::report_marker(cid_marker, SAS::Marker::Scope::Trace); } }
/*! * \internal * \brief Add a P-Asserted-Identity header to an outbound message * \param tdata The message to add the header to * \param id The identification information used to populate the header */ static void add_pai_header(const struct ast_sip_session *session, pjsip_tx_data *tdata, const struct ast_party_id *id) { static const pj_str_t pj_pai_name = { "P-Asserted-Identity", 19 }; pjsip_fromto_hdr *base; pjsip_fromto_hdr *pai_hdr; pjsip_fromto_hdr *old_pai; /* Since inv_session reuses responses, we have to make sure there's not already * a P-Asserted-Identity present. If there is, we just modify the old one. */ old_pai = pjsip_msg_find_hdr_by_name(tdata->msg, &pj_pai_name, NULL); if (old_pai) { /* If type is OTHER, then the existing header was most likely * added by the PJSIP_HEADER dial plan function as a simple * name/value pair. We can't pass this to modify_id_header because * there are no virtual functions to get the uri. We could parse * it into a pjsip_fromto_hdr but it isn't worth it since * modify_id_header is just going to overwrite the name and number * anyway. We'll just remove it from the header list instead * and create a new one. */ if (old_pai->type == PJSIP_H_OTHER) { pj_list_erase(old_pai); } else { ast_sip_modify_id_header(tdata->pool, old_pai, id); add_privacy_header(tdata, id); return; } } base = tdata->msg->type == PJSIP_REQUEST_MSG ? session->saved_from_hdr : PJSIP_MSG_TO_HDR(tdata->msg); pai_hdr = create_new_id_hdr(&pj_pai_name, base, tdata, id); if (!pai_hdr) { return; } add_privacy_header(tdata, id); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)pai_hdr); }
static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata) { char local_buf[256]; char remote_buf[256]; char *uuid; struct hepv3_capture_info *capture_info; pjsip_cid_hdr *cid_hdr; pjsip_from_hdr *from_hdr; pjsip_to_hdr *to_hdr; capture_info = hepv3_create_capture_info(tdata->buf.start, (size_t)(tdata->buf.cur - tdata->buf.start)); if (!capture_info) { return PJ_SUCCESS; } pj_sockaddr_print(&tdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3); pj_sockaddr_print(&tdata->tp_info.dst_addr, remote_buf, sizeof(remote_buf), 3); cid_hdr = PJSIP_MSG_CID_HDR(tdata->msg); from_hdr = PJSIP_MSG_FROM_HDR(tdata->msg); to_hdr = PJSIP_MSG_TO_HDR(tdata->msg); uuid = assign_uuid(&cid_hdr->id, &to_hdr->tag, &from_hdr->tag); if (!uuid) { ao2_ref(capture_info, -1); return PJ_SUCCESS; } ast_sockaddr_parse(&capture_info->src_addr, local_buf, PARSE_PORT_REQUIRE); ast_sockaddr_parse(&capture_info->dst_addr, remote_buf, PARSE_PORT_REQUIRE); capture_info->capture_time = ast_tvnow(); capture_info->capture_type = HEPV3_CAPTURE_TYPE_SIP; capture_info->uuid = uuid; capture_info->zipped = 0; hepv3_send_packet(capture_info); return PJ_SUCCESS; }
static void diversion_incoming_response(struct ast_sip_session *session, pjsip_rx_data *rdata) { static const pj_str_t contact_name = { "Contact", 7 }; pjsip_status_line status = rdata->msg_info.msg->line.status; pjsip_fromto_hdr *div_hdr; pjsip_contact_hdr *contact_hdr; if ((status.code != 302) && (status.code != 181)) { return; } /* use the diversion header info if there is one. if not one then use the session caller id info. if that doesn't exist use info from the To hdr*/ if (!(div_hdr = get_diversion_header(rdata)) && !session->id.number.valid) { div_hdr = PJSIP_MSG_TO_HDR(rdata->msg_info.msg); } contact_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &contact_name, NULL); set_redirecting(session, div_hdr, contact_hdr ? (pjsip_name_addr*)contact_hdr->uri : (pjsip_name_addr*)PJSIP_MSG_FROM_HDR(rdata->msg_info.msg)->uri); }
void PJUtils::get_impi_and_impu(pjsip_rx_data* rdata, std::string& impi_out, std::string& impu_out) { // 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); pjsip_uri* to_uri = (pjsip_uri*)pjsip_uri_get_uri(PJSIP_MSG_TO_HDR(rdata->msg_info.msg)->uri); impu_out = PJUtils::public_id_from_uri(to_uri); if ((auth_hdr != NULL) && (auth_hdr->credential.digest.username.slen != 0)) { // private user identity is supplied in the Authorization header so use it. impi_out = PJUtils::pj_str_to_string(&auth_hdr->credential.digest.username); LOG_DEBUG("Private identity from authorization header = %s", impi_out.c_str()); } else { // private user identity not supplied, so construct a default from the // public user identity by stripping the sip: prefix. impi_out = PJUtils::default_private_id_from_uri(to_uri); LOG_DEBUG("Private identity defaulted from public identity = %s", impi_out.c_str()); } }
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; }
/// Initialise the UAS transaction object. pj_status_t ICSCFProxy::UASTsx::init(pjsip_rx_data* rdata) { // Do the BasicProxy initialization first. pj_status_t status = BasicProxy::UASTsx::init(rdata); pjsip_msg* msg = rdata->msg_info.msg; // Create an ACR if ACR generation is enabled. _acr = create_acr(); // Parse interesting parameters from the request for the later lookups. if (msg->line.req.method.id == PJSIP_REGISTER_METHOD) { // REGISTER request. LOG_DEBUG("I-CSCF initialize transaction for REGISTER request"); _case = SessionCase::REGISTER; std::string impu; std::string impi; std::string visited_network; std::string auth_type; // Get the public identity from the To: header. pjsip_to_hdr* to_hdr = PJSIP_MSG_TO_HDR(msg); pjsip_uri* to_uri = (pjsip_uri*)pjsip_uri_get_uri(to_hdr->uri); impu = PJUtils::public_id_from_uri(to_uri); // Get the private identity from the Authentication header, or generate // a default if there is no Authentication header or no username in the // header. pjsip_authorization_hdr* auth_hdr = (pjsip_authorization_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_AUTHORIZATION, NULL); if ((auth_hdr != NULL) && (auth_hdr->credential.digest.username.slen != 0)) { // Get the IMPI from the username. impi = PJUtils::pj_str_to_string(&auth_hdr->credential.digest.username); } else { // Create a default IMPI from the IMPU by removing the sip: prefix. impi = impu.substr(4); } // Get the visted network identification if present. If not, homestead will // default it. pjsip_generic_string_hdr* vn_hdr = (pjsip_generic_string_hdr*)pjsip_msg_find_hdr_by_name(msg, &STR_P_V_N_I, NULL); if (vn_hdr != NULL) { visited_network = PJUtils::pj_str_to_string(&vn_hdr->hvalue); } else if (PJSIP_URI_SCHEME_IS_SIP(to_uri) || PJSIP_URI_SCHEME_IS_SIPS(to_uri)) { // Use the domain of the IMPU as the visited network. visited_network = PJUtils::pj_str_to_string(&((pjsip_sip_uri*)to_uri)->host); } // Work out what authorization type to use by looking at the expiry // values in the request. (Use a default of 1 because if there is no // expires header or expires values in the contact headers this will // be a registration not a deregistration.) auth_type = (PJUtils::max_expires(msg, 1) > 0) ? "REG" : "DEREG"; // Create an UAR router to handle the HSS interactions and S-CSCF // selection. _router = (ICSCFRouter*)new ICSCFUARouter(((ICSCFProxy*)_proxy)->_hss, ((ICSCFProxy*)_proxy)->_scscf_selector, trail(), _acr, impi, impu, visited_network, auth_type); } else { // Non-register request. LOG_DEBUG("I-CSCF initialize transaction for non-REGISTER request"); // Check for a route header containing the orig parameter; pjsip_route_hdr* route = rdata->msg_info.route; std::string impu; if ((route != NULL) && (pjsip_param_find(&((pjsip_sip_uri*)route->name_addr.uri)->other_param, &STR_ORIG) != NULL)) { // Originating request. LOG_DEBUG("Originating request"); _case = SessionCase::ORIGINATING; impu = PJUtils::public_id_from_uri(PJUtils::orig_served_user(msg)); } else { // Terminating request. LOG_DEBUG("Terminating request"); _case = SessionCase::TERMINATING; impu = PJUtils::public_id_from_uri(PJUtils::term_served_user(msg)); } // Create an LIR router to handle the HSS interactions and S-CSCF // selection. _router = (ICSCFRouter*)new ICSCFLIRouter(((ICSCFProxy*)_proxy)->_hss, ((ICSCFProxy*)_proxy)->_scscf_selector, trail(), _acr, impu, (_case == SessionCase::ORIGINATING)); } // Pass the received request to the ACR. _acr->rx_request(rdata->msg_info.msg, rdata->pkt_info.timestamp); // Record whether or not this is an in-dialog request. This is needed // to determine whether or not to send interim ACRs on provisional // responses. _in_dialog = (rdata->msg_info.msg->line.req.method.id != PJSIP_BYE_METHOD) && (rdata->msg_info.to->tag.slen != 0); return status; }
void MementoAppServerTsx::on_initial_request(pjsip_msg* req) { TRC_DEBUG("Memento processing an initial request of type %s", (req->line.req.method.id == PJSIP_INVITE_METHOD) ? "INVITE" : "BYE"); // Get the current time time_t rawtime; time(&rawtime); tm* start_time = localtime(&rawtime); _start_time_xml = create_formatted_timestamp(start_time, XML_PATTERN); _start_time_cassandra = create_formatted_timestamp(start_time, TIMESTAMP_PATTERN); // Is the call originating or terminating? std::string served_user; pjsip_routing_hdr* psu_hdr = (pjsip_routing_hdr*) pjsip_msg_find_hdr_by_name(req, &P_SERVED_USER, NULL); if (psu_hdr != NULL) { pjsip_uri* uri = (pjsip_uri*)pjsip_uri_get_uri(&psu_hdr->name_addr); served_user = uri_to_string(PJSIP_URI_IN_ROUTING_HDR, uri); pjsip_param* sescase = pjsip_param_find(&psu_hdr->other_param, &SESCASE); if ((sescase != NULL) && (pj_stricmp(&sescase->value, &ORIG) == 0)) { TRC_DEBUG("Request is originating"); _outgoing = true; } } // Get the caller, callee and impu values if (_outgoing) { // Get the callee's URI amd name from the To header. _callee_uri = uri_to_string(PJSIP_URI_IN_FROMTO_HDR, (pjsip_uri*)pjsip_uri_get_uri(PJSIP_MSG_TO_HDR(req)->uri)); _callee_name = pj_str_to_string(&((pjsip_name_addr*) (PJSIP_MSG_TO_HDR(req)->uri))->display); // Get the caller's URI and name from the P-Asserted Identity header. If // this is missing, use the From header. pjsip_routing_hdr* asserted_id = (pjsip_routing_hdr*) pjsip_msg_find_hdr_by_name(req, &P_ASSERTED_IDENTITY, NULL); if (asserted_id != NULL) { _caller_uri = uri_to_string(PJSIP_URI_IN_FROMTO_HDR, (pjsip_uri*)pjsip_uri_get_uri(&asserted_id->name_addr)); _caller_name = pj_str_to_string(&asserted_id->name_addr.display); } else { TRC_WARNING("INVITE missing P-Asserted-Identity"); send_request(req); return; } // Set the IMPU equal to the caller's URI _impu = _caller_uri; } else { // Get the callee's URI from the request URI. There can be no name value. _callee_uri = uri_to_string(PJSIP_URI_IN_FROMTO_HDR, req->line.req.uri); // Get the caller's URI and name from the From header. _caller_uri = uri_to_string(PJSIP_URI_IN_FROMTO_HDR, (pjsip_uri*)pjsip_uri_get_uri(PJSIP_MSG_FROM_HDR(req)->uri)); _caller_name = pj_str_to_string(&((pjsip_name_addr*) (PJSIP_MSG_FROM_HDR(req)->uri))->display); // Set the IMPU equal to the callee's URI _impu = _callee_uri; } // Add a unique ID containing the IMPU to the record route header. // This has the format: // <YYYYMMDDHHMMSS>_<unique_id>_<base64 encoded impu>.memento.<home domain> _unique_id = std::to_string(Utils::generate_unique_integer(0,0)); std::string encoded_impu = base64_encode(reinterpret_cast<const unsigned char*>(_impu.c_str()), _impu.length()); std::string dialog_id = std::string(_start_time_cassandra). append("_"). append(_unique_id). append("_"). append(encoded_impu); add_to_dialog(dialog_id); send_request(req); }
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; }
void registrar_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event) { if (((bool)tsx->mod_data[mod_registrar.id] == DEFAULT_HANDLING_SESSION_TERMINATED) && (event->type == PJSIP_EVENT_RX_MSG) && ((tsx->status_code == 408) || ((tsx->status_code >= 500) && (tsx->status_code < 600)))) { // Can't create an AS response in UT // LCOV_EXCL_START LOG_INFO("REGISTER transaction failed with code %d", tsx->status_code); std::string aor = PJUtils::uri_to_string(PJSIP_URI_IN_FROMTO_HDR, (pjsip_uri*)pjsip_uri_get_uri(PJSIP_MSG_TO_HDR(tsx->last_tx->msg)->uri)); // 3GPP TS 24.229 V12.0.0 (2013-03) 5.4.1.7 specifies that an AS failure where SESSION_TERMINATED // is set means that we should deregister "the currently registered public user identity" - i.e. all bindings RegistrationUtils::network_initiated_deregistration(ifchandler, store, aor, "*"); // LCOV_EXCL_STOP } }
void ICSCFSproutletRegTsx::on_rx_initial_request(pjsip_msg* req) { // Create an ACR for this transaction. _acr = _icscf->get_acr(trail()); _acr->rx_request(req); TRC_DEBUG("I-CSCF initialize transaction for REGISTER request"); // Extract relevant fields from the message std::string impu; std::string impi; std::string visited_network; std::string auth_type; // Get the public identity from the To: header. pjsip_to_hdr* to_hdr = PJSIP_MSG_TO_HDR(req); pjsip_uri* to_uri = (pjsip_uri*)pjsip_uri_get_uri(to_hdr->uri); impu = PJUtils::public_id_from_uri(to_uri); SAS::Event reg_event(trail(), SASEvent::ICSCF_RCVD_REGISTER, 0); reg_event.add_var_param(impu); SAS::report_event(reg_event); // Get the private identity from the Authentication header, or generate // a default if there is no Authentication header or no username in the // header. pjsip_authorization_hdr* auth_hdr = (pjsip_authorization_hdr*)pjsip_msg_find_hdr(req, PJSIP_H_AUTHORIZATION, NULL); if ((auth_hdr != NULL) && (auth_hdr->credential.digest.username.slen != 0)) { // Get the IMPI from the username. impi = PJUtils::pj_str_to_string(&auth_hdr->credential.digest.username); } else { // Create a default IMPI from the IMPU by removing the sip: prefix. impi = impu.substr(4); } // Get the visited network identification if present. If not, warn // (because a spec-compliant P-CSCF should default it) and use a // sensible default. pjsip_generic_string_hdr* vn_hdr = (pjsip_generic_string_hdr*)pjsip_msg_find_hdr_by_name(req, &STR_P_V_N_I, NULL); if (vn_hdr != NULL) { visited_network = PJUtils::pj_str_to_string(&vn_hdr->hvalue); } else if (PJSIP_URI_SCHEME_IS_SIP(to_uri) || PJSIP_URI_SCHEME_IS_SIPS(to_uri)) { // Use the domain of the IMPU as the visited network. visited_network = PJUtils::pj_str_to_string(&((pjsip_sip_uri*)to_uri)->host); TRC_WARNING("No P-Visited-Network-ID in REGISTER - using %s as a default", visited_network.c_str()); } // Work out what authorization type to use by looking at the expiry // values in the request. (Use a default of 1 because if there is no // expires header or expires values in the contact headers this will // be a registration not a deregistration.) auth_type = PJUtils::is_deregistration(req) ? "DEREG" : "REG"; // Remove any Route headers present on the request as we're re-routing the // message. pj_str_t route_hdr_name = pj_str((char *)"Route"); PJUtils::remove_hdr(req, &route_hdr_name); // Check if this is an emergency registration. This is true if any of the // contact headers in the message contain the "sos" parameter. bool emergency = false; pjsip_contact_hdr* contact_hdr = (pjsip_contact_hdr*)pjsip_msg_find_hdr(req, PJSIP_H_CONTACT, NULL); while (contact_hdr != NULL) { if (PJUtils::is_emergency_registration(contact_hdr)) { emergency = true; break; } contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(req, PJSIP_H_CONTACT, contact_hdr->next); } // Create an UAR router to handle the HSS interactions and S-CSCF // selection. _router = (ICSCFRouter*)new ICSCFUARouter(_icscf->get_hss_connection(), _icscf->get_scscf_selector(), trail(), _acr, _icscf->port(), impi, impu, visited_network, auth_type, emergency); // We have a router, query it for an S-CSCF to use. pjsip_sip_uri* scscf_sip_uri = NULL; std::string dummy_wildcard; pjsip_status_code status_code = (pjsip_status_code)_router->get_scscf(get_pool(req), scscf_sip_uri, dummy_wildcard); if (status_code == PJSIP_SC_OK) { TRC_DEBUG("Found SCSCF for REGISTER"); req->line.req.uri = (pjsip_uri*)scscf_sip_uri; send_request(req); } else { pjsip_msg* rsp = create_response(req, status_code); send_response(rsp); free_msg(req); } }
static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata) { char local_buf[256]; char remote_buf[256]; char *uuid; struct hepv3_capture_info *capture_info; pjsip_cid_hdr *cid_hdr; pjsip_from_hdr *from_hdr; pjsip_to_hdr *to_hdr; capture_info = hepv3_create_capture_info(tdata->buf.start, (size_t)(tdata->buf.cur - tdata->buf.start)); if (!capture_info) { return PJ_SUCCESS; } if (!(tdata->tp_info.transport->flag & PJSIP_TRANSPORT_RELIABLE)) { pjsip_tpmgr_fla2_param prm; /* Attempt to determine what IP address will we send this packet out of */ pjsip_tpmgr_fla2_param_default(&prm); prm.tp_type = tdata->tp_info.transport->key.type; pj_strset2(&prm.dst_host, tdata->tp_info.dst_name); prm.local_if = PJ_TRUE; /* If we can't get the local address use what we have already */ if (pjsip_tpmgr_find_local_addr2(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), tdata->pool, &prm) != PJ_SUCCESS) { pj_sockaddr_print(&tdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3); } else { if (prm.tp_type & PJSIP_TRANSPORT_IPV6) { snprintf(local_buf, sizeof(local_buf), "[%.*s]:%hu", (int)pj_strlen(&prm.ret_addr), pj_strbuf(&prm.ret_addr), prm.ret_port); } else { snprintf(local_buf, sizeof(local_buf), "%.*s:%hu", (int)pj_strlen(&prm.ret_addr), pj_strbuf(&prm.ret_addr), prm.ret_port); } } } else { /* For reliable transports they can only ever come from the transport * local address. */ pj_sockaddr_print(&tdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3); } pj_sockaddr_print(&tdata->tp_info.dst_addr, remote_buf, sizeof(remote_buf), 3); cid_hdr = PJSIP_MSG_CID_HDR(tdata->msg); from_hdr = PJSIP_MSG_FROM_HDR(tdata->msg); to_hdr = PJSIP_MSG_TO_HDR(tdata->msg); uuid = assign_uuid(&cid_hdr->id, &to_hdr->tag, &from_hdr->tag); if (!uuid) { ao2_ref(capture_info, -1); return PJ_SUCCESS; } ast_sockaddr_parse(&capture_info->src_addr, local_buf, PARSE_PORT_REQUIRE); ast_sockaddr_parse(&capture_info->dst_addr, remote_buf, PARSE_PORT_REQUIRE); capture_info->protocol_id = transport_to_protocol_id(tdata->tp_info.transport); capture_info->capture_time = ast_tvnow(); capture_info->capture_type = HEPV3_CAPTURE_TYPE_SIP; capture_info->uuid = uuid; capture_info->zipped = 0; hepv3_send_packet(capture_info); return PJ_SUCCESS; }
// Determine whether this request should be challenged (and SAS log appropriately). static pj_bool_t needs_authentication(pjsip_rx_data* rdata, SAS::TrailId trail) { if (rdata->tp_info.transport->local_name.port != stack_data.scscf_port) { TRC_DEBUG("Request does not need authentication - not on S-CSCF port"); // Request not received on S-CSCF port, so don't authenticate it. SAS::Event event(trail, SASEvent::AUTHENTICATION_NOT_NEEDED_NOT_SCSCF_PORT, 0); SAS::report_event(event); return PJ_FALSE; } if (rdata->msg_info.msg->line.req.method.id == PJSIP_REGISTER_METHOD) { // 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) { SAS::Event event(trail, SASEvent::AUTHENTICATION_NOT_NEEDED_EMERGENCY_REGISTER, 0); SAS::report_event(event); 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) { // There is an authorization header, so check for the integrity-protected // indication. TRC_DEBUG("Authorization header in request"); pjsip_param* integrity = pjsip_param_find(&auth_hdr->credential.digest.other_param, &STR_INTEGRITY_PROTECTED); if ((integrity != NULL) && ((pj_stricmp(&integrity->value, &STR_TLS_YES) == 0) || (pj_stricmp(&integrity->value, &STR_IP_ASSOC_YES) == 0))) { // The integrity protected indicator is included and set to tls-yes or // ip-assoc-yes. This indicates the client has already been authenticated // so we will accept this REGISTER even if there is a challenge response. // (Values of tls-pending or ip-assoc-pending indicate the challenge // should be checked.) TRC_INFO("SIP Digest authenticated request integrity protected by edge proxy"); SAS::Event event(trail, SASEvent::AUTHENTICATION_NOT_NEEDED_INTEGRITY_PROTECTED, 0); SAS::report_event(event); return PJ_FALSE; } else if ((integrity != NULL) && (pj_stricmp(&integrity->value, &STR_YES) == 0) && (auth_hdr->credential.digest.response.slen == 0)) { // The integrity protected indicator is include and set to yes. This // indicates that AKA authentication is in use and the REGISTER was // received on an integrity protected channel, so we will let the // request through if there is no challenge response, but must check // the challenge response if included. TRC_INFO("AKA authenticated request integrity protected by edge proxy"); SAS::Event event(trail, SASEvent::AUTHENTICATION_NOT_NEEDED_INTEGRITY_PROTECTED, 1); SAS::report_event(event); return PJ_FALSE; } } return PJ_TRUE; } else { if (PJSIP_MSG_TO_HDR(rdata->msg_info.msg)->tag.slen != 0) { // This is an in-dialog request which needs no authentication. return PJ_FALSE; } // Check to see if we should authenticate this non-REGISTER message - this if (non_register_auth_mode == NonRegisterAuthentication::NEVER) { // Configured to never authenticate non-REGISTER requests. SAS::Event event(trail, SASEvent::AUTHENTICATION_NOT_NEEDED_NEVER_AUTH_NON_REG, 0); SAS::report_event(event); return PJ_FALSE; } else if (non_register_auth_mode == NonRegisterAuthentication::IF_PROXY_AUTHORIZATION_PRESENT) { // Only authenticate the request if it has a Proxy-Authorization header. pjsip_proxy_authorization_hdr* auth_hdr = (pjsip_proxy_authorization_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_PROXY_AUTHORIZATION, NULL); if (auth_hdr != NULL) { // Edge proxy has explicitly asked us to authenticate this non-REGISTER // message SAS::Event event(trail, SASEvent::AUTHENTICATION_NEEDED_PROXY_AUTHORIZATION, 0); SAS::report_event(event); return PJ_TRUE; } else { // No Proxy-Authorization header - this indicates the P-CSCF trusts this // message so we don't need to perform further authentication. SAS::Event event(trail, SASEvent::AUTHENTICATION_NOT_NEEDED_PROXY_AUTHORIZATION, 0); SAS::report_event(event); return PJ_FALSE; } } else { // Unrecognized authentication mode - should never happen. LCOV_EXCL_START assert(!"Unrecognized authentication mode"); return PJ_FALSE; // LCOV_EXCL_STOP } } }