Esempio n. 1
0
/*
 * 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;
}
Esempio n. 2
0
void create_challenge(pjsip_authorization_hdr* auth_hdr,
                      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 (auth_hdr != NULL)
  {
    pjsip_param* integrity =
           pjsip_param_find(&auth_hdr->credential.digest.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.
  Json::Value* 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.
    LOG_DEBUG("Valid AV - generate challenge");
    char buf[16];
    pj_str_t random;
    random.ptr = buf;
    random.slen = sizeof(buf);

    LOG_DEBUG("Create WWW-Authenticate header");
    pjsip_www_authenticate_hdr* hdr = pjsip_www_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->isMember("aka"))
    {
      // AKA authentication.
      LOG_DEBUG("Add AKA information");

      SAS::Event event(get_trail(rdata), SASEvent::AUTHENTICATION_CHALLENGE, 0);
      std::string AKA = "AKA";
      event.add_var_param(AKA);
      SAS::report_event(event);

      Json::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;
      nonce = aka["challenge"].asString();
      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 = PJ_FALSE;

      // 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 ck = "\"" + aka["cryptkey"].asString() + "\"";
      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 ik = "\"" + aka["integritykey"].asString() + "\"";
      pj_strdup2(tdata->pool, &ik_param->value, ik.c_str());
      pj_list_insert_before(&hdr->challenge.digest.other_param, ik_param);
    }
    else
    {
      // Digest authentication.
      LOG_DEBUG("Add Digest information");

      SAS::Event event(get_trail(rdata), SASEvent::AUTHENTICATION_CHALLENGE, 0);
      std::string DIGEST = "DIGEST";
      event.add_var_param(DIGEST);
      SAS::report_event(event);

      Json::Value& digest = (*av)["digest"];
      pj_strdup2(tdata->pool, &hdr->challenge.digest.realm, digest["realm"].asCString());
      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, digest["qop"].asCString());
      hdr->challenge.digest.stale = PJ_FALSE;
    }

    // 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) : "";

    (*av)["branch"] = branch;

    // Write the authentication vector (as a JSON string) into the AV store.
    LOG_DEBUG("Write AV to store");
    uint64_t cas = 0;
    bool success = av_store->set_av(impi, nonce, av, cas, get_trail(rdata));
    if (success)
    {
      // 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 +"\"}";
      LOG_DEBUG("Sending %s to Chronos to set AV timer", chronos_body.c_str());
      chronos->send_post(timer_id, 30, "/authentication-timeout", chronos_body, 0);
    }

    delete av;
  }
  else
  {
    std::string error_msg;

    // 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))
    {
      error_msg = "Downstream node is overloaded or unresponsive, unable to get Authentication vector";
      LOG_DEBUG(error_msg.c_str());
      tdata->msg->line.status.code = PJSIP_SC_SERVER_TIMEOUT;
      tdata->msg->line.status.reason = *pjsip_get_status_text(PJSIP_SC_SERVER_TIMEOUT);
    }
    else
    {
      error_msg = "Failed to get Authentication vector";
      LOG_DEBUG(error_msg.c_str());
      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, 0);
    event.add_var_param(error_msg);
    SAS::report_event(event);

    pjsip_tx_data_invalidate_msg(tdata);
  }
}