summaryrefslogtreecommitdiff
path: root/res
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
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')
-rw-r--r--res/res_pjsip.c71
-rw-r--r--res/res_pjsip/config_global.c57
-rw-r--r--res/res_pjsip/pjsip_configuration.c56
-rw-r--r--res/res_pjsip/pjsip_distributor.c358
-rw-r--r--res/res_pjsip_authenticator_digest.c21
-rw-r--r--res/res_pjsip_endpoint_identifier_user.c111
-rw-r--r--res/res_pjsip_registrar.c129
7 files changed, 730 insertions, 73 deletions
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index daab0af3f..6890e637b 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -252,18 +252,35 @@
<configOption name="identify_by" default="username,location">
<synopsis>Way(s) for Endpoint to be identified</synopsis>
<description><para>
- An endpoint can be identified in multiple ways. Currently, the only supported
- option is <literal>username</literal>, which matches the endpoint based on the
- username in the From header.
+ Endpoints and aors can be identified in multiple ways. Currently, the supported
+ options are <literal>username</literal>, which matches the endpoint or aor id based on
+ the username and domain in the From header (or To header for aors), and
+ <literal>auth_username</literal>, which matches the endpoint or aor id based on the
+ username and realm in the Authentication header. In all cases, if an exact match
+ on both username and domain/realm fails, the match will be retried with just the username.
</para>
+ <note><para>
+ Identification by auth_username has some security considerations because an
+ Authentication header is not present on the first message of a dialog when
+ digest authentication is used. The client can't generate it until the server
+ sends the challenge in a 401 response. Since Asterisk normally sends a security
+ event when an incoming request can't be matched to an endpoint, using auth_username
+ requires that the security event be deferred until a request is received with
+ the Authentication header and only generated if the username doesn't result in a
+ match. This may result in a delay before an attack is recognized. You can control
+ how many unmatched requests are received from a single ip address before a security
+ event is generated using the unidentified_request parameters in the "global"
+ configuration object.
+ </para></note>
<note><para>Endpoints can also be identified by IP address; however, that method
of identification is not handled by this configuration option. See the documentation
for the <literal>identify</literal> configuration section for more details on that
- method of endpoint identification. If this option is set to <literal>username</literal>
- and an <literal>identify</literal> configuration section exists for the endpoint, then
- the endpoint can be identified in multiple ways.</para></note>
+ method of endpoint identification. If this option is set and an <literal>identify</literal>
+ configuration section exists for the endpoint, then the endpoint can be identified in
+ multiple ways.</para></note>
<enumlist>
<enum name="username" />
+ <enum name="auth_username" />
</enumlist>
</description>
</configOption>
@@ -1304,6 +1321,24 @@
<synopsis>The maximum amount of time from startup that qualifies should be attempted on all contacts.
If greater than the qualify_frequency for an aor, qualify_frequency will be used instead.</synopsis>
</configOption>
+ <configOption name="unidentified_request_period" default="5">
+ <synopsis>The number of seconds over which to accumulate unidentified requests.</synopsis>
+ <description><para>
+ If <literal>unidentified_request_count</literal> unidentified requests are received
+ during <literal>unidentified_request_period</literal>, a security event will be generated.
+ </para></description>
+ </configOption>
+ <configOption name="unidentified_request_count" default="5">
+ <synopsis>The number of unidentified requests from a single IP to allow.</synopsis>
+ <description><para>
+ If <literal>unidentified_request_count</literal> unidentified requests are received
+ during <literal>unidentified_request_period</literal>, a security event will be generated.
+ </para></description>
+ </configOption>
+ <configOption name="unidentified_request_prune_interval" default="30">
+ <synopsis>The interval at which unidentified requests are older than
+ twice the unidentified_request_period are pruned.</synopsis>
+ </configOption>
<configOption name="type">
<synopsis>Must be of type 'global'.</synopsis>
</configOption>
@@ -1327,13 +1362,35 @@
<configOption name="endpoint_identifier_order" default="ip,username,anonymous">
<synopsis>The order by which endpoint identifiers are processed and checked.
Identifier names are usually derived from and can be found in the endpoint
- identifier module itself (res_pjsip_endpoint_identifier_*)</synopsis>
+ identifier module itself (res_pjsip_endpoint_identifier_*).
+ You can use the CLI command "pjsip show identifiers" to see the
+ identifiers currently available.</synopsis>
+ <description>
+ <note><para>
+ One of the identifiers is "auth_username" which matches on the username in
+ an Authentication header. This method has some security considerations because an
+ Authentication header is not present on the first message of a dialog when
+ digest authentication is used. The client can't generate it until the server
+ sends the challenge in a 401 response. Since Asterisk normally sends a security
+ event when an incoming request can't be matched to an endpoint, using auth_username
+ requires that the security event be deferred until a request is received with
+ the Authentication header and only generated if the username doesn't result in a
+ match. This may result in a delay before an attack is recognized. You can control
+ how many unmatched requests are received from a single ip address before a security
+ event is generated using the unidentified_request parameters.
+ </para></note>
+ </description>
</configOption>
<configOption name="default_from_user" default="asterisk">
<synopsis>When Asterisk generates an outgoing SIP request, the From header username will be
set to this value if there is no better option (such as CallerID) to be
used.</synopsis>
</configOption>
+ <configOption name="default_realm" default="asterisk">
+ <synopsis>When Asterisk generates an challenge, the digest will be
+ set to this value if there is no better option (such as auth/realm) to be
+ used.</synopsis>
+ </configOption>
</configObject>
</configFile>
</configInfo>
diff --git a/res/res_pjsip/config_global.c b/res/res_pjsip/config_global.c
index ccfec5435..6bb688804 100644
--- a/res/res_pjsip/config_global.c
+++ b/res/res_pjsip/config_global.c
@@ -35,10 +35,14 @@
#define DEFAULT_ENDPOINT_IDENTIFIER_ORDER "ip,username,anonymous"
#define DEFAULT_MAX_INITIAL_QUALIFY_TIME 0
#define DEFAULT_FROM_USER "asterisk"
+#define DEFAULT_REALM "asterisk"
#define DEFAULT_REGCONTEXT ""
#define DEFAULT_CONTACT_EXPIRATION_CHECK_INTERVAL 30
#define DEFAULT_DISABLE_MULTI_DOMAIN 0
#define DEFAULT_VOICEMAIL_EXTENSION ""
+#define DEFAULT_UNIDENTIFIED_REQUEST_COUNT 5
+#define DEFAULT_UNIDENTIFIED_REQUEST_PERIOD 5
+#define DEFAULT_UNIDENTIFIED_REQUEST_PRUNE_INTERVAL 30
static char default_useragent[256];
@@ -56,6 +60,8 @@ struct global_config {
AST_STRING_FIELD(default_from_user);
/*! Default voicemail extension */
AST_STRING_FIELD(default_voicemail_extension);
+ /*! Realm to use in challenges before an endpoint is identified */
+ AST_STRING_FIELD(default_realm);
);
/* Value to put in Max-Forwards header */
unsigned int max_forwards;
@@ -67,6 +73,12 @@ struct global_config {
unsigned int contact_expiration_check_interval;
/*! Nonzero to disable multi domain support */
unsigned int disable_multi_domain;
+ /* The maximum number of unidentified requests per source IP address before a security event is logged */
+ unsigned int unidentified_request_count;
+ /* The period during which unidentified requests are accumulated */
+ unsigned int unidentified_request_period;
+ /* Interval at which expired unidentifed requests will be pruned */
+ unsigned int unidentified_request_prune_interval;
};
static void global_destructor(void *obj)
@@ -255,6 +267,40 @@ unsigned int ast_sip_get_max_initial_qualify_time(void)
return time;
}
+void ast_sip_get_unidentified_request_thresholds(unsigned int *count, unsigned int *period,
+ unsigned int *prune_interval)
+{
+ struct global_config *cfg;
+
+ cfg = get_global_cfg();
+ if (!cfg) {
+ *count = DEFAULT_UNIDENTIFIED_REQUEST_COUNT;
+ *period = DEFAULT_UNIDENTIFIED_REQUEST_PERIOD;
+ *prune_interval = DEFAULT_UNIDENTIFIED_REQUEST_PRUNE_INTERVAL;
+ return;
+ }
+
+ *count = cfg->unidentified_request_count;
+ *period = cfg->unidentified_request_period;
+ *prune_interval = cfg->unidentified_request_prune_interval;
+
+ ao2_ref(cfg, -1);
+ return;
+}
+
+void ast_sip_get_default_realm(char *realm, size_t size)
+{
+ struct global_config *cfg;
+
+ cfg = get_global_cfg();
+ if (!cfg) {
+ ast_copy_string(realm, DEFAULT_REALM, size);
+ } else {
+ ast_copy_string(realm, cfg->default_realm, size);
+ ao2_ref(cfg, -1);
+ }
+}
+
void ast_sip_get_default_from_user(char *from_user, size_t size)
{
struct global_config *cfg;
@@ -393,6 +439,17 @@ int ast_sip_initialize_sorcery_global(void)
OPT_UINT_T, 0, FLDSET(struct global_config, contact_expiration_check_interval));
ast_sorcery_object_field_register(sorcery, "global", "disable_multi_domain", "no",
OPT_BOOL_T, 1, FLDSET(struct global_config, disable_multi_domain));
+ ast_sorcery_object_field_register(sorcery, "global", "unidentified_request_count",
+ __stringify(DEFAULT_UNIDENTIFIED_REQUEST_COUNT),
+ OPT_UINT_T, 0, FLDSET(struct global_config, unidentified_request_count));
+ ast_sorcery_object_field_register(sorcery, "global", "unidentified_request_period",
+ __stringify(DEFAULT_UNIDENTIFIED_REQUEST_PERIOD),
+ OPT_UINT_T, 0, FLDSET(struct global_config, unidentified_request_period));
+ ast_sorcery_object_field_register(sorcery, "global", "unidentified_request_prune_interval",
+ __stringify(DEFAULT_UNIDENTIFIED_REQUEST_PRUNE_INTERVAL),
+ OPT_UINT_T, 0, FLDSET(struct global_config, unidentified_request_prune_interval));
+ ast_sorcery_object_field_register(sorcery, "global", "default_realm", DEFAULT_REALM,
+ OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, default_realm));
if (ast_sorcery_instance_observer_add(sorcery, &observer_callbacks_global)) {
return -1;
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index baa5063e4..c370ab75f 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -476,6 +476,16 @@ static int ident_handler(const struct aco_option *opt, struct ast_variable *var,
struct ast_sip_endpoint *endpoint = obj;
char *idents = ast_strdupa(var->value);
char *val;
+ enum ast_sip_endpoint_identifier_type method;
+
+ /*
+ * If there's already something in the vector when we get here,
+ * it's the default value so we need to clean it out.
+ */
+ if (AST_VECTOR_SIZE(&endpoint->ident_method_order)) {
+ AST_VECTOR_RESET(&endpoint->ident_method_order, AST_VECTOR_ELEM_CLEANUP_NOOP);
+ endpoint->ident_method = 0;
+ }
while ((val = ast_strip(strsep(&idents, ",")))) {
if (ast_strlen_zero(val)) {
@@ -483,27 +493,55 @@ static int ident_handler(const struct aco_option *opt, struct ast_variable *var,
}
if (!strcasecmp(val, "username")) {
- endpoint->ident_method |= AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME;
+ method = AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME;
+ } else if (!strcasecmp(val, "auth_username")) {
+ method = AST_SIP_ENDPOINT_IDENTIFY_BY_AUTH_USERNAME;
} else {
ast_log(LOG_ERROR, "Unrecognized identification method %s specified for endpoint %s\n",
val, ast_sorcery_object_get_id(endpoint));
+ AST_VECTOR_RESET(&endpoint->ident_method_order, AST_VECTOR_ELEM_CLEANUP_NOOP);
+ endpoint->ident_method = 0;
return -1;
}
+
+ endpoint->ident_method |= method;
+ AST_VECTOR_APPEND(&endpoint->ident_method_order, method);
}
+
return 0;
}
static int ident_to_str(const void *obj, const intptr_t *args, char **buf)
{
const struct ast_sip_endpoint *endpoint = obj;
- switch (endpoint->ident_method) {
- case AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME :
- *buf = "username"; break;
- default:
+ int methods;
+ char *method;
+ int i;
+ int j = 0;
+
+ methods = AST_VECTOR_SIZE(&endpoint->ident_method_order);
+ if (!methods) {
return 0;
}
- *buf = ast_strdup(*buf);
+ if (!(*buf = ast_calloc(MAX_OBJECT_FIELD, sizeof(char)))) {
+ return -1;
+ }
+
+ for (i = 0; i < methods; i++) {
+ switch (AST_VECTOR_GET(&endpoint->ident_method_order, i)) {
+ case AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME :
+ method = "username";
+ break;
+ case AST_SIP_ENDPOINT_IDENTIFY_BY_AUTH_USERNAME :
+ method = "auth_username";
+ break;
+ default:
+ continue;
+ }
+ j = sprintf(*buf + j, "%s%s", method, i < methods - 1 ? "," : "");
+ }
+
return 0;
}
@@ -1849,6 +1887,7 @@ static void endpoint_destructor(void* obj)
endpoint->pickup.named_pickupgroups = ast_unref_namedgroups(endpoint->pickup.named_pickupgroups);
ao2_cleanup(endpoint->persistent);
ast_variables_destroy(endpoint->channel_vars);
+ AST_VECTOR_FREE(&endpoint->ident_method_order);
}
static int init_subscription_configuration(struct ast_sip_endpoint_subscription_configuration *subscription)
@@ -1893,6 +1932,11 @@ void *ast_sip_endpoint_alloc(const char *name)
return NULL;
}
ast_party_id_init(&endpoint->id.self);
+
+ if (AST_VECTOR_INIT(&endpoint->ident_method_order, 1)) {
+ return NULL;
+ }
+
return endpoint;
}
diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c
index 834ca10d3..cbe955728 100644
--- a/res/res_pjsip/pjsip_distributor.c
+++ b/res/res_pjsip/pjsip_distributor.c
@@ -24,6 +24,7 @@
#include "include/res_pjsip_private.h"
#include "asterisk/taskprocessor.h"
#include "asterisk/threadpool.h"
+#include "asterisk/res_pjsip_cli.h"
static int distribute(void *data);
static pj_bool_t distributor(pjsip_rx_data *rdata);
@@ -37,6 +38,26 @@ static pjsip_module distributor_mod = {
.on_rx_response = distributor,
};
+struct ast_sched_context *prune_context;
+
+/* From the auth/realm realtime column size */
+#define MAX_REALM_LENGTH 40
+static char default_realm[MAX_REALM_LENGTH + 1];
+
+#define DEFAULT_SUSPECTS_BUCKETS 53
+
+static struct ao2_container *unidentified_requests;
+static unsigned int unidentified_count;
+static unsigned int unidentified_period;
+static unsigned int unidentified_prune_interval;
+static int using_auth_username;
+
+struct unidentified_request{
+ struct timeval first_seen;
+ int count;
+ char src_name[];
+};
+
/*!
* \internal
* \brief Record the task's serializer name on the tdata structure.
@@ -322,7 +343,7 @@ static int create_artificial_auth(void)
return -1;
}
- ast_string_field_set(artificial_auth, realm, "asterisk");
+ ast_string_field_set(artificial_auth, realm, default_realm);
ast_string_field_set(artificial_auth, auth_user, "");
ast_string_field_set(artificial_auth, auth_pass, "");
artificial_auth->type = AST_SIP_AUTH_TYPE_ARTIFICIAL;
@@ -359,27 +380,65 @@ struct ast_sip_endpoint *ast_sip_get_artificial_endpoint(void)
return artificial_endpoint;
}
-static void log_unidentified_request(pjsip_rx_data *rdata)
+static void log_unidentified_request(pjsip_rx_data *rdata, unsigned int count, unsigned int period)
{
char from_buf[PJSIP_MAX_URL_SIZE];
char callid_buf[PJSIP_MAX_URL_SIZE];
pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->msg_info.from->uri, from_buf, PJSIP_MAX_URL_SIZE);
ast_copy_pj_str(callid_buf, &rdata->msg_info.cid->id, PJSIP_MAX_URL_SIZE);
- ast_log(LOG_NOTICE, "Request from '%s' failed for '%s:%d' (callid: %s) - No matching endpoint found\n",
- from_buf, rdata->pkt_info.src_name, rdata->pkt_info.src_port, callid_buf);
+ if (count) {
+ ast_log(LOG_NOTICE, "Request from '%s' failed for '%s:%d' (callid: %s) - No matching endpoint found"
+ " after %u tries in %.3f ms\n",
+ from_buf, rdata->pkt_info.src_name, rdata->pkt_info.src_port, callid_buf, count, period / 1000.0);
+ } else {
+ ast_log(LOG_NOTICE, "Request from '%s' failed for '%s:%d' (callid: %s) - No matching endpoint found",
+ from_buf, rdata->pkt_info.src_name, rdata->pkt_info.src_port, callid_buf);
+ }
+}
+
+static void check_endpoint(pjsip_rx_data *rdata, struct unidentified_request *unid,
+ const char *name)
+{
+ int64_t ms = ast_tvdiff_ms(ast_tvnow(), unid->first_seen);
+
+ ao2_wrlock(unid);
+ unid->count++;
+
+ if (ms < (unidentified_period * 1000) && unid->count >= unidentified_count) {
+ log_unidentified_request(rdata, unid->count, ms);
+ ast_sip_report_invalid_endpoint(name, rdata);
+ }
+ ao2_unlock(unid);
}
static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata)
{
struct ast_sip_endpoint *endpoint;
+ struct unidentified_request *unid;
int is_ack = rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD;
endpoint = rdata->endpt_info.mod_data[endpoint_mod.id];
if (endpoint) {
+ /*
+ * ao2_find with OBJ_UNLINK always write locks the container before even searching
+ * for the object. Since the majority case is that the object won't be found, do
+ * the find without OBJ_UNLINK to prevent the unnecessary write lock, then unlink
+ * if needed.
+ */
+ if ((unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY))) {
+ ao2_unlink(unidentified_requests, unid);
+ ao2_ref(unid, -1);
+ }
return PJ_FALSE;
}
endpoint = ast_sip_identify_endpoint(rdata);
+ if (endpoint) {
+ if ((unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY))) {
+ ao2_unlink(unidentified_requests, unid);
+ ao2_ref(unid, -1);
+ }
+ }
if (!endpoint && !is_ack) {
char name[AST_UUID_STR_LEN] = "";
@@ -397,8 +456,32 @@ static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata)
ast_copy_pj_str(name, &sip_from->user, sizeof(name));
}
- log_unidentified_request(rdata);
- ast_sip_report_invalid_endpoint(name, rdata);
+ if ((unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY))) {
+ check_endpoint(rdata, unid, name);
+ ao2_ref(unid, -1);
+ } else if (using_auth_username) {
+ ao2_wrlock(unidentified_requests);
+ /* The check again with the write lock held allows us to eliminate the DUPS_REPLACE and sort_fn */
+ if ((unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY | OBJ_NOLOCK))) {
+ check_endpoint(rdata, unid, name);
+ } else {
+ unid = ao2_alloc_options(sizeof(*unid) + strlen(rdata->pkt_info.src_name) + 1, NULL,
+ AO2_ALLOC_OPT_LOCK_RWLOCK);
+ if (!unid) {
+ ao2_unlock(unidentified_requests);
+ return PJ_TRUE;
+ }
+ strcpy(unid->src_name, rdata->pkt_info.src_name); /* Safe */
+ unid->first_seen = ast_tvnow();
+ unid->count = 1;
+ ao2_link_flags(unidentified_requests, unid, OBJ_NOLOCK);
+ }
+ ao2_ref(unid, -1);
+ ao2_unlock(unidentified_requests);
+ } else {
+ log_unidentified_request(rdata, 0, 0);
+ ast_sip_report_invalid_endpoint(name, rdata);
+ }
}
rdata->endpt_info.mod_data[endpoint_mod.id] = endpoint;
return PJ_FALSE;
@@ -413,6 +496,8 @@ static pj_bool_t authenticate(pjsip_rx_data *rdata)
if (!is_ack && ast_sip_requires_authentication(endpoint, rdata)) {
pjsip_tx_data *tdata;
+ struct unidentified_request *unid;
+
pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, 401, NULL, &tdata);
switch (ast_sip_check_authentication(endpoint, rdata, tdata)) {
case AST_SIP_AUTHENTICATION_CHALLENGE:
@@ -421,6 +506,11 @@ static pj_bool_t authenticate(pjsip_rx_data *rdata)
pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL);
return PJ_TRUE;
case AST_SIP_AUTHENTICATION_SUCCESS:
+ /* See note in endpoint_lookup about not holding an unnecessary write lock */
+ if ((unid = ao2_find(unidentified_requests, rdata->pkt_info.src_name, OBJ_SEARCH_KEY))) {
+ ao2_unlink(unidentified_requests, unid);
+ ao2_ref(unid, -1);
+ }
ast_sip_report_auth_success(endpoint, rdata);
pjsip_tx_data_dec_ref(tdata);
return PJ_FALSE;
@@ -480,31 +570,287 @@ struct ast_sip_endpoint *ast_pjsip_rdata_get_endpoint(pjsip_rx_data *rdata)
return endpoint;
}
+static int suspects_sort(const void *obj, const void *arg, int flags)
+{
+ const struct unidentified_request *object_left = obj;
+ const struct unidentified_request *object_right = arg;
+ const char *right_key = arg;
+ int cmp;
+
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_OBJECT:
+ right_key = object_right->src_name;
+ /* Fall through */
+ case OBJ_SEARCH_KEY:
+ cmp = strcmp(object_left->src_name, right_key);
+ break;
+ case OBJ_SEARCH_PARTIAL_KEY:
+ cmp = strncmp(object_left->src_name, right_key, strlen(right_key));
+ break;
+ default:
+ cmp = 0;
+ break;
+ }
+ return cmp;
+}
+
+static int suspects_compare(void *obj, void *arg, int flags)
+{
+ const struct unidentified_request *object_left = obj;
+ const struct unidentified_request *object_right = arg;
+ const char *right_key = arg;
+ int cmp = 0;
+
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_OBJECT:
+ right_key = object_right->src_name;
+ /* Fall through */
+ case OBJ_SEARCH_KEY:
+ if (strcmp(object_left->src_name, right_key) == 0) {
+ cmp = CMP_MATCH | CMP_STOP;
+ }
+ break;
+ case OBJ_SEARCH_PARTIAL_KEY:
+ if (strncmp(object_left->src_name, right_key, strlen(right_key)) == 0) {
+ cmp = CMP_MATCH;
+ }
+ break;
+ default:
+ cmp = 0;
+ break;
+ }
+ return cmp;
+}
+
+static int suspects_hash(const void *obj, int flags) {
+ const struct unidentified_request *object_left = obj;
+
+ if (flags & OBJ_SEARCH_OBJECT) {
+ return ast_str_hash(object_left->src_name);
+ } else if (flags & OBJ_SEARCH_KEY) {
+ return ast_str_hash(obj);
+ }
+ return -1;
+}
+
+static struct ao2_container *cli_unid_get_container(const char *regex)
+{
+ struct ao2_container *s_container;
+
+ s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
+ suspects_sort, suspects_compare);
+ if (!s_container) {
+ return NULL;
+ }
+
+ if (ao2_container_dup(s_container, unidentified_requests, 0)) {
+ ao2_ref(s_container, -1);
+ return NULL;
+ }
+
+ return s_container;
+}
+
+static int cli_unid_iterate(void *container, ao2_callback_fn callback, void *args)
+{
+ ao2_callback(container, 0, callback, args);
+
+ return 0;
+}
+
+static void *cli_unid_retrieve_by_id(const char *id)
+{
+ return ao2_find(unidentified_requests, id, OBJ_SEARCH_KEY);
+}
+
+static const char *cli_unid_get_id(const void *obj)
+{
+ const struct unidentified_request *unid = obj;
+
+ return unid->src_name;
+}
+
+static int cli_unid_print_header(void *obj, void *arg, int flags)
+{
+ struct ast_sip_cli_context *context = arg;
+ RAII_VAR(struct ast_sip_cli_formatter_entry *, formatter_entry, NULL, ao2_cleanup);
+
+ int indent = CLI_INDENT_TO_SPACES(context->indent_level);
+ int filler = CLI_LAST_TABSTOP - indent - 7;
+
+ ast_assert(context->output_buffer != NULL);
+
+ ast_str_append(&context->output_buffer, 0,
+ "%*s: <IP Address%*.*s> <Count> <Age(sec)>\n",
+ indent, "Request", filler, filler, CLI_HEADER_FILLER);
+
+ return 0;
+}
+static int cli_unid_print_body(void *obj, void *arg, int flags)
+{
+ struct unidentified_request *unid = obj;
+ struct ast_sip_cli_context *context = arg;
+ int indent;
+ int flexwidth;
+ int64_t ms = ast_tvdiff_ms(ast_tvnow(), unid->first_seen);
+
+ ast_assert(context->output_buffer != NULL);
+
+ indent = CLI_INDENT_TO_SPACES(context->indent_level);
+ flexwidth = CLI_LAST_TABSTOP - 4;
+
+ ast_str_append(&context->output_buffer, 0, "%*s: %-*.*s %7d %10.3f\n",
+ indent,
+ "Request",
+ flexwidth, flexwidth,
+ unid->src_name, unid->count, ms / 1000.0);
+
+ return 0;
+}
+
+static struct ast_cli_entry cli_commands[] = {
+ AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "Show PJSIP Unidentified Requests",
+ .command = "pjsip show unidentified_requests",
+ .usage = "Usage: pjsip show unidentified_requests\n"
+ " Show the PJSIP Unidentified Requests\n"),
+};
+
+struct ast_sip_cli_formatter_entry *unid_formatter;
+
+static int expire_requests(void *object, void *arg, int flags)
+{
+ struct unidentified_request *unid = object;
+ int *maxage = arg;
+ int64_t ms = ast_tvdiff_ms(ast_tvnow(), unid->first_seen);
+
+ if (ms > (*maxage) * 2 * 1000) {
+ return CMP_MATCH;
+ }
+
+ return 0;
+}
+
+static int prune_task(const void *data)
+{
+ unsigned int maxage;
+
+ ast_sip_get_unidentified_request_thresholds(&unidentified_count, &unidentified_period, &unidentified_prune_interval);
+ maxage = unidentified_period * 2;
+ ao2_callback(unidentified_requests, OBJ_MULTIPLE | OBJ_NODATA | OBJ_UNLINK, expire_requests, &maxage);
+
+ return unidentified_prune_interval * 1000;
+}
+
+static int clean_task(const void *data)
+{
+ return 0;
+}
+
+static void global_loaded(const char *object_type)
+{
+ char *identifier_order = ast_sip_get_endpoint_identifier_order();
+ char *io_copy = ast_strdupa(identifier_order);
+ char *identify_method;
+
+ ast_free(identifier_order);
+ using_auth_username = 0;
+ while ((identify_method = ast_strip(strsep(&io_copy, ",")))) {
+ if (!strcmp(identify_method, "auth_username")) {
+ using_auth_username = 1;
+ break;
+ }
+ }
+
+ ast_sip_get_default_realm(default_realm, sizeof(default_realm));
+ ast_sip_get_unidentified_request_thresholds(&unidentified_count, &unidentified_period, &unidentified_prune_interval);
+
+ /* Clean out the old task, if any */
+ ast_sched_clean_by_callback(prune_context, prune_task, clean_task);
+ if (ast_sched_add_variable(prune_context, unidentified_prune_interval * 1000, prune_task, NULL, 1) < 0) {
+ return;
+ }
+}
+
+/*! \brief Observer which is used to update our interval and default_realm when the global setting changes */
+static struct ast_sorcery_observer global_observer = {
+ .loaded = global_loaded,
+};
+
+
int ast_sip_initialize_distributor(void)
{
+ unidentified_requests = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_RWLOCK, 0,
+ DEFAULT_SUSPECTS_BUCKETS, suspects_hash, NULL, suspects_compare);
+ if (!unidentified_requests) {
+ return -1;
+ }
+
+ prune_context = ast_sched_context_create();
+ if (!prune_context) {
+ ast_sip_destroy_distributor();
+ return -1;
+ }
+
+ if (ast_sched_start_thread(prune_context)) {
+ ast_sip_destroy_distributor();
+ return -1;
+ }
+
+ ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &global_observer);
+ ast_sorcery_reload_object(ast_sip_get_sorcery(), "global");
+
if (create_artificial_endpoint() || create_artificial_auth()) {
+ ast_sip_destroy_distributor();
return -1;
}
if (internal_sip_register_service(&distributor_mod)) {
+ ast_sip_destroy_distributor();
return -1;
}
if (internal_sip_register_service(&endpoint_mod)) {
+ ast_sip_destroy_distributor();
return -1;
}
if (internal_sip_register_service(&auth_mod)) {
+ ast_sip_destroy_distributor();
return -1;
}
+ unid_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
+ if (!unid_formatter) {
+ ast_log(LOG_ERROR, "Unable to allocate memory for unid_formatter\n");
+ return -1;
+ }
+ unid_formatter->name = "unidentified_request";
+ unid_formatter->print_header = cli_unid_print_header;
+ unid_formatter->print_body = cli_unid_print_body;
+ unid_formatter->get_container = cli_unid_get_container;
+ unid_formatter->iterate = cli_unid_iterate;
+ unid_formatter->get_id = cli_unid_get_id;
+ unid_formatter->retrieve_by_id = cli_unid_retrieve_by_id;
+ ast_sip_register_cli_formatter(unid_formatter);
+ ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
+
return 0;
}
void ast_sip_destroy_distributor(void)
{
+ ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
+ ast_sip_unregister_cli_formatter(unid_formatter);
+
internal_sip_unregister_service(&distributor_mod);
internal_sip_unregister_service(&endpoint_mod);
internal_sip_unregister_service(&auth_mod);
ao2_cleanup(artificial_auth);
ao2_cleanup(artificial_endpoint);
+ ao2_cleanup(unidentified_requests);
+
+ ast_sorcery_observer_remove(ast_sip_get_sorcery(), "global", &global_observer);
+
+ if (prune_context) {
+ ast_sched_context_destroy(prune_context);
+ }
}
diff --git a/res/res_pjsip_authenticator_digest.c b/res/res_pjsip_authenticator_digest.c
index e610bb804..4a31edfc4 100644
--- a/res/res_pjsip_authenticator_digest.c
+++ b/res/res_pjsip_authenticator_digest.c
@@ -31,6 +31,10 @@
<support_level>core</support_level>
***/
+/* From the auth/realm realtime column size */
+#define MAX_REALM_LENGTH 40
+static char default_realm[MAX_REALM_LENGTH + 1];
+
AO2_GLOBAL_OBJ_STATIC(entity_id);
/*!
@@ -409,7 +413,7 @@ static enum ast_sip_check_auth_result digest_check_auth(struct ast_sip_endpoint
for (i = 0; i < auth_size; ++i) {
if (ast_strlen_zero(auths[i]->realm)) {
- ast_string_field_set(auths[i], realm, "asterisk");
+ ast_string_field_set(auths[i], realm, default_realm);
}
verify_res[i] = verify(auths[i], rdata, tdata->pool);
if (verify_res[i] == AUTH_SUCCESS) {
@@ -456,6 +460,16 @@ static int build_entity_id(void)
return 0;
}
+static void global_loaded(const char *object_type)
+{
+ ast_sip_get_default_realm(default_realm, sizeof(default_realm));
+}
+
+/*! \brief Observer which is used to update our default_realm when the global setting changes */
+static struct ast_sorcery_observer global_observer = {
+ .loaded = global_loaded,
+};
+
static int reload_module(void)
{
if (build_entity_id()) {
@@ -471,6 +485,10 @@ static int load_module(void)
if (build_entity_id()) {
return AST_MODULE_LOAD_DECLINE;
}
+
+ ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &global_observer);
+ ast_sorcery_reload_object(ast_sip_get_sorcery(), "global");
+
if (ast_sip_register_authenticator(&digest_authenticator)) {
ao2_global_obj_release(entity_id);
return AST_MODULE_LOAD_DECLINE;
@@ -480,6 +498,7 @@ static int load_module(void)
static int unload_module(void)
{
+ ast_sorcery_observer_remove(ast_sip_get_sorcery(), "global", &global_observer);
ast_sip_unregister_authenticator(&digest_authenticator);
ao2_global_obj_release(entity_id);
return 0;
diff --git a/res/res_pjsip_endpoint_identifier_user.c b/res/res_pjsip_endpoint_identifier_user.c
index 12af2edb9..172566a9f 100644
--- a/res/res_pjsip_endpoint_identifier_user.c
+++ b/res/res_pjsip_endpoint_identifier_user.c
@@ -29,7 +29,7 @@
#include "asterisk/res_pjsip.h"
#include "asterisk/module.h"
-static int get_endpoint_details(pjsip_rx_data *rdata, char *endpoint, size_t endpoint_size, char *domain, size_t domain_size)
+static int get_from_header(pjsip_rx_data *rdata, char *username, size_t username_size, char *domain, size_t domain_size)
{
pjsip_uri *from = rdata->msg_info.from->uri;
pjsip_sip_uri *sip_from;
@@ -37,11 +37,28 @@ static int get_endpoint_details(pjsip_rx_data *rdata, char *endpoint, size_t end
return -1;
}
sip_from = (pjsip_sip_uri *) pjsip_uri_get_uri(from);
- ast_copy_pj_str(endpoint, &sip_from->user, endpoint_size);
+ ast_copy_pj_str(username, &sip_from->user, username_size);
ast_copy_pj_str(domain, &sip_from->host, domain_size);
return 0;
}
+static pjsip_authorization_hdr *get_auth_header(pjsip_rx_data *rdata, char *username,
+ size_t username_size, char *realm, size_t realm_size, pjsip_authorization_hdr *start)
+{
+ pjsip_authorization_hdr *header;
+
+ header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION, start);
+
+ if (!header || pj_stricmp2(&header->scheme, "digest")) {
+ return NULL;
+ }
+
+ ast_copy_pj_str(username, &header->credential.digest.username, username_size);
+ ast_copy_pj_str(realm, &header->credential.digest.realm, realm_size);
+
+ return header;
+}
+
static int find_transport_state_in_use(void *obj, void *arg, int flags)
{
struct ast_sip_transport_state *transport_state = obj;
@@ -56,34 +73,30 @@ static int find_transport_state_in_use(void *obj, void *arg, int flags)
return 0;
}
-static struct ast_sip_endpoint *username_identify(pjsip_rx_data *rdata)
+static struct ast_sip_endpoint *find_endpoint(pjsip_rx_data *rdata, char *endpoint_name,
+ char *domain_name)
{
- char endpoint_name[64], domain_name[64], id[AST_UUID_STR_LEN];
+ char id[AST_UUID_STR_LEN];
struct ast_sip_endpoint *endpoint;
RAII_VAR(struct ast_sip_domain_alias *, alias, NULL, ao2_cleanup);
RAII_VAR(struct ao2_container *, transport_states, NULL, ao2_cleanup);
RAII_VAR(struct ast_sip_transport_state *, transport_state, NULL, ao2_cleanup);
RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup);
- if (get_endpoint_details(rdata, endpoint_name, sizeof(endpoint_name), domain_name, sizeof(domain_name))) {
- return NULL;
- }
-
if (!ast_sip_get_disable_multi_domain()) {
/* Attempt to find the endpoint given the name and domain provided */
snprintf(id, sizeof(id), "%s@%s", endpoint_name, domain_name);
if ((endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", id))) {
- goto done;
+ return endpoint;
}
/* See if an alias exists for the domain provided */
if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) {
snprintf(id, sizeof(id), "%s@%s", endpoint_name, alias->domain);
if ((endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", id))) {
- goto done;
+ return endpoint;
}
}
-
/* See if the transport this came in on has a provided domain */
if ((transport_states = ast_sip_get_transport_states())
&& (transport_state = ao2_callback(transport_states, 0, find_transport_state_in_use, rdata))
@@ -91,41 +104,95 @@ static struct ast_sip_endpoint *username_identify(pjsip_rx_data *rdata)
&& !ast_strlen_zero(transport->domain)) {
snprintf(id, sizeof(id), "anonymous@%s", transport->domain);
if ((endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", id))) {
- goto done;
+ return endpoint;
}
}
}
/* Fall back to no domain */
- endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", endpoint_name);
+ return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", endpoint_name);
+}
-done:
- if (endpoint) {
- if (!(endpoint->ident_method & AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME)) {
- ao2_ref(endpoint, -1);
- return NULL;
- }
- ast_debug(3, "Retrieved endpoint %s\n", ast_sorcery_object_get_id(endpoint));
- } else {
- ast_debug(3, "Could not identify endpoint by username '%s'\n", endpoint_name);
+static struct ast_sip_endpoint *username_identify(pjsip_rx_data *rdata)
+{
+ char username[64], domain[64];
+ struct ast_sip_endpoint *endpoint;
+
+ if (get_from_header(rdata, username, sizeof(username), domain, sizeof(domain))) {
+ return NULL;
+ }
+ ast_debug(3, "Attempting identify by From username '%s' domain '%s'\n", username, domain);
+
+ endpoint = find_endpoint(rdata, username, domain);
+ if (!endpoint) {
+ ast_debug(3, "Endpoint not found for From username '%s' domain '%s'\n", username, domain);
+ ao2_cleanup(endpoint);
+ return NULL;
+ }
+ if (!(endpoint->ident_method & AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME)) {
+ ast_debug(3, "Endpoint found for '%s' but 'username' method not supported'\n", username);
+ ao2_cleanup(endpoint);
+ return NULL;
}
+ ast_debug(3, "Identified by From username '%s' domain '%s'\n", username, domain);
+
return endpoint;
}
+static struct ast_sip_endpoint *auth_username_identify(pjsip_rx_data *rdata)
+{
+ char username[64], realm[64];
+ struct ast_sip_endpoint *endpoint;
+ pjsip_authorization_hdr *auth_header = NULL;
+
+ while ((auth_header = get_auth_header(rdata, username, sizeof(username), realm, sizeof(realm),
+ auth_header ? auth_header->next : NULL))) {
+ ast_debug(3, "Attempting identify by Authorization username '%s' realm '%s'\n", username,
+ realm);
+
+ endpoint = find_endpoint(rdata, username, realm);
+ if (!endpoint) {
+ ast_debug(3, "Endpoint not found for Authentication username '%s' realm '%s'\n",
+ username, realm);
+ ao2_cleanup(endpoint);
+ continue;
+ }
+ if (!(endpoint->ident_method & AST_SIP_ENDPOINT_IDENTIFY_BY_AUTH_USERNAME)) {
+ ast_debug(3, "Endpoint found for '%s' but 'auth_username' method not supported'\n",
+ username);
+ ao2_cleanup(endpoint);
+ continue;
+ }
+ ast_debug(3, "Identified by Authorization username '%s' realm '%s'\n", username, realm);
+
+ return endpoint;
+ }
+
+ return NULL;
+}
+
+
static struct ast_sip_endpoint_identifier username_identifier = {
.identify_endpoint = username_identify,
};
+static struct ast_sip_endpoint_identifier auth_username_identifier = {
+ .identify_endpoint = auth_username_identify,
+};
+
+
static int load_module(void)
{
CHECK_PJSIP_MODULE_LOADED();
ast_sip_register_endpoint_identifier_with_name(&username_identifier, "username");
+ ast_sip_register_endpoint_identifier_with_name(&auth_username_identifier, "auth_username");
return AST_MODULE_LOAD_SUCCESS;
}
static int unload_module(void)
{
+ ast_sip_unregister_endpoint_identifier(&auth_username_identifier);
ast_sip_unregister_endpoint_identifier(&username_identifier);
return 0;
}
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;
}