summaryrefslogtreecommitdiff
path: root/res/res_pjsip_registrar.c
diff options
context:
space:
mode:
authorGeorge Joseph <george.joseph@fairview5.com>2016-03-07 17:34:31 -0700
committerGeorge Joseph <gjoseph@digium.com>2016-04-27 16:33:51 -0500
commit4ebf9a938daed520ba627dd0f17f4f3c65245ab2 (patch)
tree31abd9fa94ed622b1d5c2713df81ff1fe9c4c65f /res/res_pjsip_registrar.c
parentf3248017633bc8d556bfbb3e81ce56ca67f2d822 (diff)
res_pjsip: Add ability to identify by Authorization username
A feature of chan_sip that service providers relied upon was the ability to identify by the Authorization username. This is most often used when customers have a PBX that needs to register rather than identify by IP address. From my own experiance, this is pretty common with small businesses who otherwise don't need a static IP. In this scenario, a register from the customer's PBX may succeed because From will usually contain the PBXs account id but an INVITE will contain the caller id. With nothing recognizable in From, the service provider's Asterisk can never match to an endpoint and the INVITE just stays unauthorized. The fixes: A new value "auth_username" has been added to endpoint/identify_by that will use the username and digest fields in the Authorization header instead of username and domain in the the From header to match an endpoint, or the To header to match an aor. This code as added to res_pjsip_endpoint_identifier_user rather than creating a new module. Although identify_by was always a comma-separated list, there was only 1 choice so order wasn't preserved. So to keep the order, a vector was added to the end of ast_sip_endpoint. This is only used by res_pjsip_registrar to find the aor. The res_pjsip_endpoint_identifier_* modules are called in globals/endpoint_identifier_order. Along the way, the logic in res_pjsip_registrar was corrected to match most-specific to least-specific as res_pjsip_endpoint_identifier_user does. The order is: username@domain username@domain_alias username Auth by username does present 1 problem however, the first INVITE won't have an Authorization header so the distributor, not finding a match on anything, sends a securty_alert. It still sends a 401 with a challenge so the next INVITE will have the Authorization header and presumably succeed. As a result though, that first security alert is actually a false alarm. To address this, a new feature has been added to pjsip_distributor that keeps track of unidentified requests and only sends the security alert if a configurable number of unidentified requests come from the same IP in a configurable amout of time. Those configuration options have been added to the global config object. This feature is only used when auth_username is enabled. Finally, default_realm was added to the globals object to replace the hard coded "asterisk" used when an endpoint is not yet identified. The testsuite tests all pass but new tests are forthcoming for this new feature. ASTERISK-25835 #close Reported-by: Ross Beer Change-Id: I30ba62d208e6f63439600916fcd1c08a365ed69d
Diffstat (limited to 'res/res_pjsip_registrar.c')
-rw-r--r--res/res_pjsip_registrar.c129
1 files changed, 98 insertions, 31 deletions
diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c
index 8edd6ee43..ae8440aab 100644
--- a/res/res_pjsip_registrar.c
+++ b/res/res_pjsip_registrar.c
@@ -657,6 +657,65 @@ static int rx_task(void *data)
return res;
}
+static int match_aor(const char *aor_name, const char *id)
+{
+ if (ast_strlen_zero(aor_name)) {
+ return 0;
+ }
+
+ if (!strcmp(aor_name, id)) {
+ ast_debug(3, "Matched id '%s' to aor '%s'\n", id, aor_name);
+ return 1;
+ }
+
+ return 0;
+}
+
+static char *find_aor_name(const char *username, const char *domain, const char *aors)
+{
+ char *configured_aors;
+ char *aor_name;
+ char *id_domain;
+ struct ast_sip_domain_alias *alias;
+
+ id_domain = ast_alloca(strlen(username) + strlen(domain) + 2);
+ sprintf(id_domain, "%s@%s", username, domain);
+
+ /* Look for exact match on username@domain */
+ configured_aors = ast_strdupa(aors);
+ while ((aor_name = ast_strip(strsep(&configured_aors, ",")))) {
+ if (match_aor(aor_name, id_domain)) {
+ return ast_strdup(aor_name);
+ }
+ }
+
+ /* If there's a domain alias, look for exact match on username@domain_alias */
+ alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain);
+ if (alias) {
+ char *id_domain_alias = ast_alloca(strlen(username) + strlen(alias->domain) + 2);
+
+ sprintf(id_domain, "%s@%s", username, alias->domain);
+ ao2_cleanup(alias);
+
+ configured_aors = ast_strdupa(aors);
+ while ((aor_name = ast_strip(strsep(&configured_aors, ",")))) {
+ if (match_aor(aor_name, id_domain_alias)) {
+ return ast_strdup(aor_name);
+ }
+ }
+ }
+
+ /* Look for exact match on username only */
+ configured_aors = ast_strdupa(aors);
+ while ((aor_name = ast_strip(strsep(&configured_aors, ",")))) {
+ if (match_aor(aor_name, username)) {
+ return ast_strdup(aor_name);
+ }
+ }
+
+ return NULL;
+}
+
static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata)
{
RAII_VAR(struct serializer *, ser, NULL, ao2_cleanup);
@@ -665,10 +724,10 @@ static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata)
RAII_VAR(struct ast_sip_endpoint *, endpoint,
ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup);
RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
- pjsip_sip_uri *uri;
- char *domain_name;
- char *configured_aors, *aor_name;
- RAII_VAR(struct ast_str *, id, NULL, ast_free);
+ char *domain_name = NULL;
+ char *username = NULL;
+ RAII_VAR(char *, aor_name, NULL, ast_free);
+ int i;
if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method) || !endpoint) {
return PJ_FALSE;
@@ -689,38 +748,46 @@ static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata)
return PJ_TRUE;
}
- uri = pjsip_uri_get_uri(rdata->msg_info.to->uri);
- domain_name = ast_alloca(uri->host.slen + 1);
- ast_copy_pj_str(domain_name, &uri->host, uri->host.slen + 1);
+ for (i = 0; i < AST_VECTOR_SIZE(&endpoint->ident_method_order); i++) {
+ pjsip_sip_uri *uri;
+ pjsip_authorization_hdr *header = NULL;
- configured_aors = ast_strdupa(endpoint->aors);
+ switch (AST_VECTOR_GET(&endpoint->ident_method_order, i)) {
+ case AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME :
+ uri = pjsip_uri_get_uri(rdata->msg_info.to->uri);
- /* Iterate the configured AORs to see if the user or the user+domain match */
- while ((aor_name = ast_strip(strsep(&configured_aors, ",")))) {
- struct ast_sip_domain_alias *alias = NULL;
+ domain_name = ast_alloca(uri->host.slen + 1);
+ ast_copy_pj_str(domain_name, &uri->host, uri->host.slen + 1);
+ username = ast_alloca(uri->user.slen + 1);
+ ast_copy_pj_str(username, &uri->user, uri->user.slen + 1);
- if (ast_strlen_zero(aor_name)) {
- continue;
- }
-
- if (!pj_strcmp2(&uri->user, aor_name)) {
+ aor_name = find_aor_name(username, domain_name, endpoint->aors);
+ if (aor_name) {
+ ast_debug(3, "Matched aor '%s' by To username\n", aor_name);
+ }
break;
+ case AST_SIP_ENDPOINT_IDENTIFY_BY_AUTH_USERNAME :
+ while ((header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION,
+ header ? header->next : NULL))) {
+ if (header && !pj_stricmp2(&header->scheme, "digest")) {
+ username = ast_alloca(header->credential.digest.username.slen + 1);
+ ast_copy_pj_str(username, &header->credential.digest.username, header->credential.digest.username.slen + 1);
+ domain_name = ast_alloca(header->credential.digest.realm.slen + 1);
+ ast_copy_pj_str(domain_name, &header->credential.digest.realm, header->credential.digest.realm.slen + 1);
+
+ aor_name = find_aor_name(username, domain_name, endpoint->aors);
+ if (aor_name) {
+ ast_debug(3, "Matched aor '%s' by Authentication username\n", aor_name);
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ continue;
}
- if (!id && !(id = ast_str_create(uri->user.slen + uri->host.slen + 2))) {
- return PJ_TRUE;
- }
-
- ast_str_set(&id, 0, "%.*s@", (int)uri->user.slen, uri->user.ptr);
- if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) {
- ast_str_append(&id, 0, "%s", alias->domain);
- ao2_cleanup(alias);
- } else {
- ast_str_append(&id, 0, "%s", domain_name);
- }
-
- if (!strcmp(aor_name, ast_str_buffer(id))) {
- ast_free(id);
+ if (aor_name) {
break;
}
}
@@ -729,7 +796,7 @@ static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata)
/* The provided AOR name was not found (be it within the configuration or sorcery itself) */
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL);
ast_sip_report_req_no_support(endpoint, rdata, "registrar_requested_aor_not_found");
- ast_log(LOG_WARNING, "AOR '%.*s' not found for endpoint '%s'\n", (int)uri->user.slen, uri->user.ptr, ast_sorcery_object_get_id(endpoint));
+ ast_log(LOG_WARNING, "AOR '%s' not found for endpoint '%s'\n", username, ast_sorcery_object_get_id(endpoint));
return PJ_TRUE;
}