コード例 #1
0
void ICSCFSproutletTsx::on_rx_cancel(int status_code, pjsip_msg* cancel_req)
{
  // If this is cancelling a terminating INVITE then check whether we need to
  // update our session establishment stats.
  if (!_originating &&
      (_req_type == PJSIP_INVITE_METHOD) &&
      (!_session_set_up))
  {
    // Session has failed to establish but for a reason that is not the fault
    // of the network.
    _icscf->_session_establishment_tbl->increment_attempts();
    _icscf->_session_establishment_tbl->increment_failures();
    _icscf->_session_establishment_network_tbl->increment_attempts();
    _icscf->_session_establishment_network_tbl->increment_successes();

    // Set _session_set_up to true so that we don't count this session again
    // when we receive the subsequent final response.
    _session_set_up = true;
  }

  if ((status_code == PJSIP_SC_REQUEST_TERMINATED) &&
      (cancel_req != NULL))
  {
    // Create and send an ACR for the CANCEL request.
    ACR* acr = _icscf->get_acr(trail());

    // @TODO - timestamp from request.
    acr->rx_request(cancel_req);
    acr->send();

    delete acr;
  }
}
コード例 #2
0
// LCOV_EXCL_START - TODO add to UTs
void BGCFSproutletTsx::on_rx_cancel(int status_code, pjsip_msg* cancel_req)
{
  if ((status_code == PJSIP_SC_REQUEST_TERMINATED) &&
      (cancel_req != NULL))
  {
    // Create and send an ACR for the CANCEL request.
    ACR* acr = _bgcf->get_acr(trail());

    // @TODO - timestamp from request.
    acr->rx_request(cancel_req);
    acr->send();

    delete acr;
  }
}
コード例 #3
0
void SubscriptionSproutletTsx::process_subscription_request(pjsip_msg* req)
{
  SAS::TrailId trail_id = trail();

  // Get the URI from the To header and check it is a SIP or SIPS URI.
  pjsip_uri* uri = (pjsip_uri*)pjsip_uri_get_uri(PJSIP_MSG_TO_HDR(req)->uri);

  if ((!PJSIP_URI_SCHEME_IS_SIP(uri)) && (!PJSIP_URI_SCHEME_IS_TEL(uri)))
  {
    // Reject a non-SIP/TEL URI with 404 Not Found (RFC3261 isn't clear
    // whether 404 is the right status code - it says 404 should be used if
    // the AoR isn't valid for the domain in the RequestURI).
    TRC_DEBUG("Rejecting subscribe request using invalid URI scheme");

    SAS::Event event(trail_id, SASEvent::SUBSCRIBE_FAILED_EARLY_URLSCHEME, 0);
    SAS::report_event(event);

    pjsip_msg* rsp = create_response(req, PJSIP_SC_NOT_FOUND);
    send_response(rsp);
    free_msg(req);
    return;
  }

  // Check if the contact header is present. If it isn't, we want to abort
  // processing before going any further, to avoid unnecessary work.
  pjsip_contact_hdr* contact_hdr =
             (pjsip_contact_hdr*)pjsip_msg_find_hdr(req, PJSIP_H_CONTACT, NULL);

  if (contact_hdr == NULL)
  {
    TRC_DEBUG("Unable to parse contact header from request. "
              "Aborting processing");
    pjsip_msg* rsp = create_response(req, PJSIP_SC_BAD_REQUEST);
    send_response(rsp);
    free_msg(req);
    return;
  }

  // Check if this is a subscription request from a binding that was emergency
  // registered.
  bool emergency_subscription = false;

  while (contact_hdr != NULL)
  {
    emergency_subscription = PJUtils::is_emergency_registration(contact_hdr);

    if (!emergency_subscription)
    {
      break;
    }

    contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(req,
                                                          PJSIP_H_CONTACT,
                                                          contact_hdr->next);
  }

  if (emergency_subscription)
  {
    // Reject a subscription with a Contact header containing a contact address
    // that's been registered for emergency service.
    TRC_DEBUG("Rejecting subscribe request from emergency registration");

    SAS::Event event(trail_id, SASEvent::SUBSCRIBE_FAILED_EARLY_EMERGENCY, 0);
    SAS::report_event(event);

    // Allow-Events is a mandatory header on 489 responses.
    pjsip_msg* rsp = create_response(req, PJSIP_SC_BAD_EVENT);
    pjsip_generic_string_hdr* allow_events_hdr =
                           pjsip_generic_string_hdr_create(get_pool(rsp),
                                                           &STR_ALLOW_EVENTS,
                                                           &STR_REG);
    pjsip_msg_add_hdr(rsp, (pjsip_hdr*)allow_events_hdr);
    send_response(rsp);
    free_msg(req);
    return;
  }

  // At this point we are going to attempt to process the subscribe.
  // Canonicalize the public ID from the URI in the To header.
  std::string public_id = PJUtils::public_id_from_uri(uri);

  TRC_DEBUG("Process SUBSCRIBE for public ID %s", public_id.c_str());

  // Get the call identifier from the headers.
  std::string cid = PJUtils::pj_str_to_string(&PJSIP_MSG_CID_HDR(req)->id);

  // Add SAS markers to the trail attached to the message so the trail
  // becomes searchable.
  SAS::Event event(trail_id, SASEvent::SUBSCRIBE_START, 0);
  event.add_var_param(public_id);
  SAS::report_event(event);

  // Create an ACR for the request. The node role is always considered
  // originating for SUBSCRIBE requests.
  ACR* acr = _subscription->_acr_factory->get_acr(trail_id,
                                                  ACR::CALLING_PARTY,
                                                  ACR::NODE_ROLE_ORIGINATING);
  acr->rx_request(req);

  // Work out the expiry time of the subscription.
  pjsip_expires_hdr* expires =
             (pjsip_expires_hdr*)pjsip_msg_find_hdr(req, PJSIP_H_EXPIRES, NULL);
  int expiry = (expires != NULL) ?
                expires->ivalue :
                SubscriptionSproutlet::DEFAULT_SUBSCRIPTION_EXPIRES;

  if (expiry > _subscription->_max_expires)
  {
    // Expiry is too long, set it to the maximum.
    TRC_DEBUG("Requested expiry (%d) is too long, setting to the maximum (%d)",
              expiry, _subscription->_max_expires);
    expiry = _subscription->_max_expires;
  }

  // Create a subscription object from the request that we can pass down to
  // be set into/updated in the different stores
  Subscription* new_subscription = create_subscription(req, expiry);
  HSSConnection::irs_info irs_info;
  HTTPCode rc;

  // Update or remove the subscription in the subscriber manager depending on
  // the expiry time.
  if (expiry != 0)
  {
    TRC_DEBUG("Adding/updating the subscription with ID %s",
              new_subscription->get_id().c_str());

    Subscriptions new_subscriptions;
    new_subscriptions.insert(std::make_pair(new_subscription->get_id(), new_subscription));
    rc = _subscription->_sm->update_subscriptions(
                   public_id,
                   new_subscriptions,
                   irs_info,
                   trail_id);
  }
  else
  {
    TRC_DEBUG("Removing the subscription with ID %s",
              new_subscription->get_id().c_str());

    rc = _subscription->_sm->remove_subscriptions(public_id,
                                                  {new_subscription->get_id()},
                                                  irs_info,
                                                  trail_id);
  }

  pjsip_status_code st_code =
                 determine_sm_sip_response(rc, irs_info._regstate, "SUBSCRIBE");

  pjsip_msg* rsp = create_response(req, st_code);

  if (st_code == PJSIP_SC_OK)
  {
    // The subscribe was successful. SAS log, and add headers to the response.
    TRC_DEBUG("The subscribe has been successful");

    SAS::Event sub_accepted(trail_id, SASEvent::SUBSCRIBE_ACCEPTED, 0);
    SAS::report_event(sub_accepted);

    // Add expires headers
    pjsip_expires_hdr* expires_hdr = pjsip_expires_hdr_create(get_pool(rsp),
                                                              expiry);
    pjsip_msg_add_hdr(rsp, (pjsip_hdr*)expires_hdr);

    // Add the contact header
    pjsip_contact_hdr* contact_hdr = pjsip_contact_hdr_create(get_pool(rsp));
    pjsip_name_addr* contact_uri = pjsip_name_addr_create(get_pool(rsp));
    contact_uri->uri = (pjsip_uri*)stack_data.scscf_uri;
    contact_hdr->uri = (pjsip_uri*)contact_uri;
    pjsip_msg_add_hdr(rsp, (pjsip_hdr*)contact_hdr);

    // Add a P-Charging-Function-Addresses header to the successful SUBSCRIBE
    // response containing the charging addresses returned by the HSS.
    PJUtils::add_pcfa_header(rsp,
                             get_pool(rsp),
                             irs_info._ccfs,
                             irs_info._ecfs,
                             false);

  }
  else if (st_code == PJSIP_SC_TEMPORARILY_UNAVAILABLE)
  {
    // A 480 response means that the subscriber wasn't registered. SAS log
    // this.
    TRC_DEBUG("The subscribe has failed as the subscriber (%s) wasn't registered",
              public_id.c_str());

    SAS::Event event(trail_id, SASEvent::SUBSCRIBE_FAILED_EARLY_NOT_REG, 0);
    SAS::report_event(event);
  }
  else
  {
    // The subscribe was unsuccessful.
    TRC_DEBUG("The subscribe failed with return code %d", st_code);

    SAS::Event sub_failed(trail_id, SASEvent::SUBSCRIBE_FAILED, 0);
    sub_failed.add_var_param(public_id);
    SAS::report_event(sub_failed);
  }

  // Add the to tag to the response (even if the subscribe was rejected).
  pjsip_to_hdr *to = (pjsip_to_hdr*) pjsip_msg_find_hdr(rsp,
                                                        PJSIP_H_TO,
                                                        NULL);
  pj_strdup2(get_pool(rsp), &to->tag, new_subscription->_to_tag.c_str());

  // Pass the response to the ACR.
  acr->tx_response(rsp);

  // Send the response.
  send_response(rsp);

  // Send the ACR and delete it.
  acr->send();
  delete acr; acr = NULL;

  delete new_subscription; new_subscription = NULL;
  free_msg(req);
}
コード例 #4
0
ファイル: authentication.cpp プロジェクト: gfraysse/sprout
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;
}