static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, const char *name, pj_size_t initial_size, pj_size_t increment_sz, pj_pool_callback *callback) { pj_caching_pool *cp = (pj_caching_pool*)pf; pj_pool_t *pool; int idx; PJ_CHECK_STACK(); pj_lock_acquire(cp->lock); /* Use pool factory's policy when callback is NULL */ if (callback == NULL) { callback = pf->policy.callback; } /* Search the suitable size for the pool. * We'll just do linear search to the size array, as the array size itself * is only a few elements. Binary search I suspect will be less efficient * for this purpose. */ if (initial_size <= pool_sizes[START_SIZE]) { for (idx=START_SIZE-1; idx >= 0 && pool_sizes[idx] >= initial_size; --idx) ; ++idx; } else { for (idx=START_SIZE+1; idx < PJ_CACHING_POOL_ARRAY_SIZE && pool_sizes[idx] < initial_size; ++idx) ; } /* Check whether there's a pool in the list. */ if (idx==PJ_CACHING_POOL_ARRAY_SIZE || pj_list_empty(&cp->free_list[idx])) { /* No pool is available. */ /* Set minimum size. */ if (idx < PJ_CACHING_POOL_ARRAY_SIZE) initial_size = pool_sizes[idx]; /* Create new pool */ pool = pj_pool_create_int(&cp->factory, name, initial_size, increment_sz, callback); if (!pool) { pj_lock_release(cp->lock); return NULL; } } else { /* Get one pool from the list. */ pool = (pj_pool_t*) cp->free_list[idx].next; pj_list_erase(pool); /* Initialize the pool. */ pj_pool_init_int(pool, name, increment_sz, callback); /* Update pool manager's free capacity. */ if (cp->capacity > pj_pool_get_capacity(pool)) { cp->capacity -= pj_pool_get_capacity(pool); } else { cp->capacity = 0; } PJ_LOG(6, (pool->obj_name, "pool reused, size=%u", pool->capacity)); } /* Put in used list. */ pj_list_insert_before( &cp->used_list, pool ); /* Mark factory data */ pool->factory_data = (void*) (pj_ssize_t) idx; /* Increment used count. */ ++cp->used_count; pj_lock_release(cp->lock); return pool; }
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); } }