static pjsip_hdr* parse_hdr_proxy_authenticate( pjsip_parse_ctx *ctx ) { pjsip_proxy_authenticate_hdr *hdr = pjsip_proxy_authenticate_hdr_create(ctx->pool); int_parse_hdr_authenticate(ctx->scanner, ctx->pool, hdr); return (pjsip_hdr*)hdr; }
static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool, const pjsip_www_authenticate_hdr *rhs) { /* This function also serves Proxy-Authenticate header. */ pjsip_www_authenticate_hdr *hdr; if (rhs->type == PJSIP_H_WWW_AUTHENTICATE) hdr = pjsip_www_authenticate_hdr_create(pool); else hdr = pjsip_proxy_authenticate_hdr_create(pool); pj_strdup(pool, &hdr->scheme, &rhs->scheme); if (pj_stricmp2(&hdr->scheme, "digest") == 0) { pj_strdup(pool, &hdr->challenge.digest.realm, &rhs->challenge.digest.realm); pj_strdup(pool, &hdr->challenge.digest.domain, &rhs->challenge.digest.domain); pj_strdup(pool, &hdr->challenge.digest.nonce, &rhs->challenge.digest.nonce); pj_strdup(pool, &hdr->challenge.digest.opaque, &rhs->challenge.digest.opaque); hdr->challenge.digest.stale = rhs->challenge.digest.stale; pj_strdup(pool, &hdr->challenge.digest.algorithm, &rhs->challenge.digest.algorithm); pj_strdup(pool, &hdr->challenge.digest.qop, &rhs->challenge.digest.qop); pjsip_param_clone(pool, &hdr->challenge.digest.other_param, &rhs->challenge.digest.other_param); } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) { pj_assert(0); return NULL; } else { pj_assert(0); return NULL; } return hdr; }
/* * Add authentication challenge headers to the outgoing response in tdata. * Application may specify its customized nonce and opaque for the challenge, * or can leave the value to NULL to make the function fills them in with * random characters. */ PJ_DEF(pj_status_t) pjsip_auth_srv_challenge( pjsip_auth_srv *auth_srv, const pj_str_t *qop, const pj_str_t *nonce, const pj_str_t *opaque, pj_bool_t stale, pjsip_tx_data *tdata) { pjsip_www_authenticate_hdr *hdr; char nonce_buf[16]; pj_str_t random; PJ_ASSERT_RETURN( auth_srv && tdata, PJ_EINVAL ); random.ptr = nonce_buf; random.slen = sizeof(nonce_buf); /* Create the header. */ if (auth_srv->is_proxy) hdr = pjsip_proxy_authenticate_hdr_create(tdata->pool); else hdr = pjsip_www_authenticate_hdr_create(tdata->pool); /* Initialize header. * Note: only support digest authentication now. */ hdr->scheme = pjsip_DIGEST_STR; hdr->challenge.digest.algorithm = pjsip_MD5_STR; if (nonce) { pj_strdup(tdata->pool, &hdr->challenge.digest.nonce, nonce); } else { pj_create_random_string(nonce_buf, sizeof(nonce_buf)); pj_strdup(tdata->pool, &hdr->challenge.digest.nonce, &random); } if (opaque) { pj_strdup(tdata->pool, &hdr->challenge.digest.opaque, opaque); } else { pj_create_random_string(nonce_buf, sizeof(nonce_buf)); pj_strdup(tdata->pool, &hdr->challenge.digest.opaque, &random); } if (qop) { pj_strdup(tdata->pool, &hdr->challenge.digest.qop, qop); } else { hdr->challenge.digest.qop.slen = 0; } pj_strdup(tdata->pool, &hdr->challenge.digest.realm, &auth_srv->realm); hdr->challenge.digest.stale = stale; pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr); return PJ_SUCCESS; }
void create_challenge(pjsip_digest_credential* credentials, pj_bool_t stale, std::string resync, pjsip_rx_data* rdata, pjsip_tx_data* tdata) { // Get the public and private identities from the request. std::string impi; std::string impu; std::string nonce; PJUtils::get_impi_and_impu(rdata, impi, impu); // Set up the authorization type, following Annex P.4 of TS 33.203. Currently // only support AKA and SIP Digest, so only implement the subset of steps // required to distinguish between the two. std::string auth_type; if (credentials != NULL) { pjsip_param* integrity = pjsip_param_find(&credentials->other_param, &STR_INTEGRITY_PROTECTED); if ((integrity != NULL) && ((pj_stricmp(&integrity->value, &STR_YES) == 0) || (pj_stricmp(&integrity->value, &STR_NO) == 0))) { // Authentication scheme is AKA. auth_type = "aka"; } } // Get the Authentication Vector from the HSS. rapidjson::Document* av = NULL; HTTPCode http_code = hss->get_auth_vector(impi, impu, auth_type, resync, av, get_trail(rdata)); if ((av != NULL) && (!verify_auth_vector(av, impi, get_trail(rdata)))) { // Authentication Vector is badly formed. delete av; av = NULL; } if (av != NULL) { // Retrieved a valid authentication vector, so generate the challenge. TRC_DEBUG("Valid AV - generate challenge"); char buf[16]; pj_str_t random; random.ptr = buf; random.slen = sizeof(buf); pjsip_www_authenticate_hdr* hdr; if (rdata->msg_info.msg->line.req.method.id == PJSIP_REGISTER_METHOD) { TRC_DEBUG("Create WWW-Authenticate header"); hdr = pjsip_www_authenticate_hdr_create(tdata->pool); } else { TRC_DEBUG("Create Proxy-Authenticate header"); hdr = pjsip_proxy_authenticate_hdr_create(tdata->pool); } // Set up common fields for Digest and AKA cases (both are considered // Digest authentication). hdr->scheme = STR_DIGEST; if (av->HasMember("aka")) { // AKA authentication. TRC_DEBUG("Add AKA information"); SAS::Event event(get_trail(rdata), SASEvent::AUTHENTICATION_CHALLENGE_AKA, 0); SAS::report_event(event); rapidjson::Value& aka = (*av)["aka"]; // Use default realm for AKA as not specified in the AV. pj_strdup(tdata->pool, &hdr->challenge.digest.realm, &aka_realm); hdr->challenge.digest.algorithm = STR_AKAV1_MD5; if ((aka.HasMember("challenge")) && (aka["challenge"].IsString())) { nonce = aka["challenge"].GetString(); } pj_strdup2(tdata->pool, &hdr->challenge.digest.nonce, nonce.c_str()); pj_create_random_string(buf, sizeof(buf)); pj_strdup(tdata->pool, &hdr->challenge.digest.opaque, &random); hdr->challenge.digest.qop = STR_AUTH; hdr->challenge.digest.stale = stale; // Add the cryptography key parameter. pjsip_param* ck_param = (pjsip_param*)pj_pool_alloc(tdata->pool, sizeof(pjsip_param)); ck_param->name = STR_CK; std::string cryptkey = ""; if ((aka.HasMember("cryptkey")) && (aka["cryptkey"].IsString())) { cryptkey = aka["cryptkey"].GetString(); } std::string ck = "\"" + cryptkey + "\""; pj_strdup2(tdata->pool, &ck_param->value, ck.c_str()); pj_list_insert_before(&hdr->challenge.digest.other_param, ck_param); // Add the integrity key parameter. pjsip_param* ik_param = (pjsip_param*)pj_pool_alloc(tdata->pool, sizeof(pjsip_param)); ik_param->name = STR_IK; std::string integritykey = ""; if ((aka.HasMember("integritykey")) && (aka["integritykey"].IsString())) { integritykey = aka["integritykey"].GetString(); } std::string ik = "\"" + integritykey + "\""; pj_strdup2(tdata->pool, &ik_param->value, ik.c_str()); pj_list_insert_before(&hdr->challenge.digest.other_param, ik_param); } else { // Digest authentication. TRC_DEBUG("Add Digest information"); SAS::Event event(get_trail(rdata), SASEvent::AUTHENTICATION_CHALLENGE_DIGEST, 0); SAS::report_event(event); rapidjson::Value& digest = (*av)["digest"]; std::string realm = ""; if ((digest.HasMember("realm")) && (digest["realm"].IsString())) { realm = digest["realm"].GetString(); } std::string qop = ""; if ((digest.HasMember("qop")) && (digest["qop"].IsString())) { qop = digest["qop"].GetString(); } pj_strdup2(tdata->pool, &hdr->challenge.digest.realm, realm.c_str()); hdr->challenge.digest.algorithm = STR_MD5; pj_create_random_string(buf, sizeof(buf)); nonce.assign(buf, sizeof(buf)); pj_strdup(tdata->pool, &hdr->challenge.digest.nonce, &random); pj_create_random_string(buf, sizeof(buf)); pj_strdup(tdata->pool, &hdr->challenge.digest.opaque, &random); pj_strdup2(tdata->pool, &hdr->challenge.digest.qop, qop.c_str()); hdr->challenge.digest.stale = stale; } // Add the header to the message. pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr); // Store the branch parameter in memcached for correlation purposes pjsip_via_hdr* via_hdr = (pjsip_via_hdr*)pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_VIA, NULL); std::string branch = (via_hdr != NULL) ? PJUtils::pj_str_to_string(&via_hdr->branch_param) : ""; rapidjson::Value branch_value; branch_value.SetString(branch.c_str(), (*av).GetAllocator()); (*av).AddMember("branch", branch_value, (*av).GetAllocator()); // Write the authentication vector (as a JSON string) into the AV store. TRC_DEBUG("Write AV to store"); uint64_t cas = 0; Store::Status status = av_store->set_av(impi, nonce, av, cas, get_trail(rdata)); if (status == Store::OK) { // We've written the AV into the store, so need to set a Chronos // timer so that an AUTHENTICATION_TIMEOUT SAR is sent to the // HSS when it expires. std::string timer_id; std::string chronos_body = "{\"impi\": \"" + impi + "\", \"impu\": \"" + impu +"\", \"nonce\": \"" + nonce +"\"}"; TRC_DEBUG("Sending %s to Chronos to set AV timer", chronos_body.c_str()); chronos->send_post(timer_id, 30, "/authentication-timeout", chronos_body, get_trail(rdata)); } delete av; } else { // If we couldn't get the AV because a downstream node is overloaded then don't return // a 4xx error to the client. if ((http_code == HTTP_SERVER_UNAVAILABLE) || (http_code == HTTP_GATEWAY_TIMEOUT)) { TRC_DEBUG("Downstream node is overloaded or unresponsive, unable to get Authentication vector"); tdata->msg->line.status.code = PJSIP_SC_SERVER_TIMEOUT; tdata->msg->line.status.reason = *pjsip_get_status_text(PJSIP_SC_SERVER_TIMEOUT); SAS::Event event(get_trail(rdata), SASEvent::AUTHENTICATION_FAILED_OVERLOAD, 0); SAS::report_event(event); } else { TRC_DEBUG("Failed to get Authentication vector"); tdata->msg->line.status.code = PJSIP_SC_FORBIDDEN; tdata->msg->line.status.reason = *pjsip_get_status_text(PJSIP_SC_FORBIDDEN); SAS::Event event(get_trail(rdata), SASEvent::AUTHENTICATION_FAILED_NO_AV, 0); SAS::report_event(event); } pjsip_tx_data_invalidate_msg(tdata); } }