pj_status_t user_lookup(pj_pool_t *pool, const pjsip_auth_lookup_cred_param *param, pjsip_cred_info *cred_info, void* av_param) { const pj_str_t* acc_name = ¶m->acc_name; const pj_str_t* realm = ¶m->realm; const pjsip_rx_data* rdata = param->rdata; SAS::TrailId trail = get_trail(rdata); pj_status_t status = PJSIP_EAUTHACCNOTFOUND; // Get the impi and the nonce. There must be an authorization header otherwise // PJSIP wouldn't have called this method. std::string impi = PJUtils::pj_str_to_string(acc_name); pjsip_authorization_hdr* auth_hdr = (pjsip_authorization_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION, NULL); std::string nonce = PJUtils::pj_str_to_string(&auth_hdr->credential.digest.nonce); // Get the Authentication Vector from the store. Json::Value* av = (Json::Value*)av_param; if (av == NULL) { LOG_WARNING("Received an authentication request for %s with nonce %s, but no matching AV found", impi.c_str(), nonce.c_str()); } if ((av != NULL) && (!verify_auth_vector(av, impi, trail))) { // Authentication vector is badly formed. av = NULL; // LCOV_EXCL_LINE } if (av != NULL) { pj_cstr(&cred_info->scheme, "digest"); pj_strdup(pool, &cred_info->username, acc_name); if (av->isMember("aka")) { // AKA authentication. The response in the AV must be used as a // plain-text password for the MD5 Digest computation. Convert the text // into binary as this is what PJSIP is expecting. std::string response = (*av)["aka"]["response"].asString(); std::string xres; for (size_t ii = 0; ii < response.length(); ii += 2) { xres.push_back((char)(pj_hex_digit_to_val(response[ii]) * 16 + pj_hex_digit_to_val(response[ii+1]))); } cred_info->data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; pj_strdup2(pool, &cred_info->data, xres.c_str()); LOG_DEBUG("Found AKA XRES = %.*s", cred_info->data.slen, cred_info->data.ptr); // Use default realm as it isn't specified in the AV. pj_strdup(pool, &cred_info->realm, realm); status = PJ_SUCCESS; } else if (av->isMember("digest")) { if (pj_strcmp2(realm, (*av)["digest"]["realm"].asCString()) == 0) { // Digest authentication, so ha1 field is hashed password. cred_info->data_type = PJSIP_CRED_DATA_DIGEST; pj_strdup2(pool, &cred_info->data, (*av)["digest"]["ha1"].asCString()); cred_info->realm = *realm; LOG_DEBUG("Found Digest HA1 = %.*s", cred_info->data.slen, cred_info->data.ptr); status = PJ_SUCCESS; } else { // These credentials are for a different realm, so no credentials were // actually provided for us to check. status = PJSIP_EAUTHNOAUTH; } } correlate_branch_from_av(av, trail); } return status; }
HTTPCode AuthTimeoutTask::handle_response(std::string body) { Json::Value json_body; std::string json_str = body; Json::Reader reader; bool parsingSuccessful = reader.parse(json_str.c_str(), json_body); if (!parsingSuccessful) { LOG_ERROR("Failed to read opaque data, %s", reader.getFormattedErrorMessages().c_str()); return HTTP_BAD_REQUEST; } if ((json_body.isMember("impu")) && ((json_body)["impu"].isString())) { _impu = json_body.get("impu", "").asString(); report_sip_all_register_marker(trail(), _impu); } else { LOG_ERROR("IMPU not available in JSON"); return HTTP_BAD_REQUEST; } if ((json_body.isMember("impi")) && ((json_body)["impi"].isString())) { _impi = json_body.get("impi", "").asString(); } else { LOG_ERROR("IMPI not available in JSON"); return HTTP_BAD_REQUEST; } if ((json_body.isMember("nonce")) && ((json_body)["nonce"].isString())) { _nonce = json_body.get("nonce", "").asString(); } else { LOG_ERROR("Nonce not available in JSON"); return HTTP_BAD_REQUEST; } bool success = false; uint64_t cas; Json::Value* av = _cfg->_avstore->get_av(_impi, _nonce, cas, trail()); if (av != NULL) { // Use the original REGISTER's branch parameter for SAS // correlation correlate_branch_from_av(av, trail()); // If authentication completed, we'll have written a marker to // indicate that. Look for it. if (!av->isMember("tombstone")) { LOG_DEBUG("AV for %s:%s has timed out", _impi.c_str(), _nonce.c_str()); // The AUTHENTICATION_TIMEOUT SAR is idempotent, so there's no // problem if Chronos' timer pops twice (e.g. if we have high // latency and these operations take more than 2 seconds). // If either of these operations fail, we return a 500 Internal // Server Error - this will trigger Chronos to try a different // Sprout, which may have better connectivity to Homestead or Memcached. HTTPCode hss_query = _cfg->_hss->update_registration_state(_impu, _impi, HSSConnection::AUTH_TIMEOUT, trail()); if (hss_query == HTTP_OK) { success = true; } } else { SAS::Event event(trail(), SASEvent::AUTHENTICATION_TIMER_POP_IGNORED, 0); SAS::report_event(event); LOG_DEBUG("Tombstone record indicates Authentication Vector has been used successfully - ignoring timer pop"); success = true; } } else { LOG_WARNING("Could not find AV for %s:%s when checking authentication timeout", _impi.c_str(), _nonce.c_str()); // LCOV_EXCL_LINE } delete av; return success ? HTTP_OK : HTTP_SERVER_ERROR; }