static ssize_t encrypt_challenge(struct master_config* config, struct udpaddress* remote, struct udpaddress* local, struct cipher_context* oldkey, const char* newkey, char* obuf) { char token[1500]; size_t tlength; tlength = create_challenge(config, remote, local, oldkey, newkey, token); return xencrypt(config, config->keyX, token, tlength, obuf); }
struct cipher_context* verify_challenge(struct master_config* config, struct udpaddress* remote, struct udpaddress* local, struct cipher_context* oldkey, char* in, ssize_t ilength) { char obuf[1500]; char token[1500]; ssize_t tlength, olength; olength = xdecrypt(config, config->keyX, in, ilength, obuf); if (olength < 0) { return 0; } tlength = create_challenge(config, remote, local, oldkey, obuf, token); if (tlength != olength) { return 0; } if (memcmp(token, obuf, tlength) != 0) { return 0; } return cipher_context_create(config->cipher, obuf); }
static void gs_cmd_drop(sourceinfo_t *si, int parc, char *parv[]) { mygroup_t *mg; char *name = parv[0]; char *key = parv[1]; char fullcmd[512]; char key0[80], key1[80]; if (!name) { command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "DROP"); command_fail(si, fault_needmoreparams, _("Syntax: DROP <!group>")); return; } if (*name != '!') { command_fail(si, fault_badparams, STR_INVALID_PARAMS, "DROP"); command_fail(si, fault_badparams, _("Syntax: DROP <!group>")); return; } if (!(mg = mygroup_find(name))) { command_fail(si, fault_nosuch_target, _("Group \2%s\2 does not exist."), name); return; } if (!groupacs_sourceinfo_has_flag(mg, si, GA_FOUNDER)) { command_fail(si, fault_noprivs, _("You are not authorized to execute this command.")); return; } if (si->su != NULL) { if (!key) { create_challenge(si, entity(mg)->name, 0, key0); snprintf(fullcmd, sizeof fullcmd, "/%s%s DROP %s %s", (ircd->uses_rcommand == false) ? "msg " : "", si->service->disp, entity(mg)->name, key0); command_success_nodata(si, _("To avoid accidental use of this command, this operation has to be confirmed. Please confirm by replying with \2%s\2"), fullcmd); return; } /* accept current and previous key */ create_challenge(si, entity(mg)->name, 0, key0); create_challenge(si, entity(mg)->name, 1, key1); if (strcmp(key, key0) && strcmp(key, key1)) { command_fail(si, fault_badparams, _("Invalid key for %s."), "DROP"); return; } } logcommand(si, CMDLOG_REGISTER, "DROP: \2%s\2", entity(mg)->name); remove_group_chanacs(mg); object_unref(mg); command_success_nodata(si, _("The group \2%s\2 has been dropped."), name); return; }
static void cs_cmd_drop(sourceinfo_t *si, int parc, char *parv[]) { mychan_t *mc; char *name = parv[0]; char *key = parv[1]; char fullcmd[512]; char key0[80], key1[80]; if (!name) { command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "DROP"); command_fail(si, fault_needmoreparams, _("Syntax: DROP <#channel>")); return; } if (*name != '#') { command_fail(si, fault_badparams, STR_INVALID_PARAMS, "DROP"); command_fail(si, fault_badparams, _("Syntax: DROP <#channel>")); return; } if (!(mc = mychan_find(name))) { command_fail(si, fault_nosuch_target, _("Channel \2%s\2 is not registered."), name); return; } if (si->c != NULL) { command_fail(si, fault_noprivs, _("For security reasons, you may not drop a channel registration with a fantasy command.")); return; } if (!is_founder(mc, entity(si->smu))) { command_fail(si, fault_noprivs, _("You are not authorized to perform this operation.")); return; } if (metadata_find(mc, "private:close:closer")) { logcommand(si, CMDLOG_REGISTER, "DROP: \2%s\2 failed to drop (closed)", mc->name); command_fail(si, fault_noprivs, _("The channel \2%s\2 is closed; it cannot be dropped."), mc->name); return; } if (mc->flags & MC_HOLD) { command_fail(si, fault_noprivs, _("The channel \2%s\2 is held; it cannot be dropped."), mc->name); return; } if (si->su != NULL) { if (!key) { create_challenge(si, mc->name, 0, key0); snprintf(fullcmd, sizeof fullcmd, "/%s%s DROP %s %s", (ircd->uses_rcommand == false) ? "msg " : "", chansvs.me->disp, mc->name, key0); command_success_nodata(si, _("To avoid accidental use of this command, this operation has to be confirmed. Please confirm by replying with \2%s\2"), fullcmd); return; } /* accept current and previous key */ create_challenge(si, mc->name, 0, key0); create_challenge(si, mc->name, 1, key1); if (strcmp(key, key0) && strcmp(key, key1)) { command_fail(si, fault_badparams, _("Invalid key for %s."), "DROP"); return; } } logcommand(si, CMDLOG_REGISTER, "DROP: \2%s\2", mc->name); hook_call_channel_drop(mc); if (mc->chan != NULL && !(mc->chan->flags & CHAN_LOG)) part(mc->name, chansvs.nick); object_unref(mc); command_success_nodata(si, _("The channel \2%s\2 has been dropped."), name); return; }
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; }