summaryrefslogtreecommitdiff
path: root/res
diff options
context:
space:
mode:
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_exten_state.c2
-rw-r--r--res/res_pjsip_mwi.c2
-rw-r--r--res/res_pjsip_outbound_publish.c2
-rw-r--r--res/res_pjsip_publish_asterisk.c2
-rw-r--r--res/res_pjsip_pubsub.c43
-rw-r--r--res/res_pjsip_registrar.c129
12 files changed, 760 insertions, 94 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_exten_state.c b/res/res_pjsip_exten_state.c
index e84a25c6a..6b4e758d3 100644
--- a/res/res_pjsip_exten_state.c
+++ b/res/res_pjsip_exten_state.c
@@ -812,5 +812,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Extension State
.support_level = AST_MODULE_SUPPORT_CORE,
.load = load_module,
.unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND + 5,
);
diff --git a/res/res_pjsip_mwi.c b/res/res_pjsip_mwi.c
index f9bfc1904..9eba335b5 100644
--- a/res/res_pjsip_mwi.c
+++ b/res/res_pjsip_mwi.c
@@ -1231,5 +1231,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP MWI resource",
.load = load_module,
.unload = unload_module,
.reload = reload,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND + 5,
);
diff --git a/res/res_pjsip_outbound_publish.c b/res/res_pjsip_outbound_publish.c
index 5b27bdf0d..0fe2e3e5d 100644
--- a/res/res_pjsip_outbound_publish.c
+++ b/res/res_pjsip_outbound_publish.c
@@ -389,7 +389,7 @@ static void sip_outbound_publish_synchronize(struct ast_sip_event_publisher_hand
} else {
state->client->started = 1;
}
- } else if (state->client->started && !handler && removed && !strcmp(publish->event, removed->event_name)) {
+ } else if (!handler && removed && !strcmp(publish->event, removed->event_name)) {
/* If the publisher client has been started but it is going away stop it */
removed->stop_publishing(state->client);
state->client->started = 0;
diff --git a/res/res_pjsip_publish_asterisk.c b/res/res_pjsip_publish_asterisk.c
index 50d4cc387..3218b0a0c 100644
--- a/res/res_pjsip_publish_asterisk.c
+++ b/res/res_pjsip_publish_asterisk.c
@@ -926,5 +926,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Asterisk Event
.load = load_module,
.reload = reload_module,
.unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND + 5,
);
diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c
index dca2e3109..755a154a5 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -2541,20 +2541,28 @@ void ast_sip_unregister_subscription_handler(struct ast_sip_subscription_handler
AST_RWLIST_TRAVERSE_SAFE_END;
}
-static struct ast_sip_pubsub_body_generator *find_body_generator_type_subtype(const char *content_type,
- const char *content_subtype)
+static struct ast_sip_pubsub_body_generator *find_body_generator_type_subtype_nolock(const char *type, const char *subtype)
{
- struct ast_sip_pubsub_body_generator *iter;
- SCOPED_LOCK(lock, &body_generators, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK);
+ struct ast_sip_pubsub_body_generator *gen;
- AST_LIST_TRAVERSE(&body_generators, iter, list) {
- if (!strcmp(iter->type, content_type) &&
- !strcmp(iter->subtype, content_subtype)) {
+ AST_LIST_TRAVERSE(&body_generators, gen, list) {
+ if (!strcmp(gen->type, type)
+ && !strcmp(gen->subtype, subtype)) {
break;
}
- };
+ }
- return iter;
+ return gen;
+}
+
+static struct ast_sip_pubsub_body_generator *find_body_generator_type_subtype(const char *type, const char *subtype)
+{
+ struct ast_sip_pubsub_body_generator *gen;
+
+ AST_RWLIST_RDLOCK(&body_generators);
+ gen = find_body_generator_type_subtype_nolock(type, subtype);
+ AST_RWLIST_UNLOCK(&body_generators);
+ return gen;
}
static struct ast_sip_pubsub_body_generator *find_body_generator_accept(const char *accept)
@@ -3097,14 +3105,14 @@ int ast_sip_pubsub_register_body_generator(struct ast_sip_pubsub_body_generator
pj_str_t accept;
pj_size_t accept_len;
- existing = find_body_generator_type_subtype(generator->type, generator->subtype);
+ AST_RWLIST_WRLOCK(&body_generators);
+ existing = find_body_generator_type_subtype_nolock(generator->type, generator->subtype);
if (existing) {
- ast_log(LOG_WARNING, "Cannot register body generator of %s/%s."
- "One is already registered.\n", generator->type, generator->subtype);
+ AST_RWLIST_UNLOCK(&body_generators);
+ ast_log(LOG_WARNING, "A body generator for %s/%s is already registered.\n",
+ generator->type, generator->subtype);
return -1;
}
-
- AST_RWLIST_WRLOCK(&body_generators);
AST_LIST_INSERT_HEAD(&body_generators, generator, list);
AST_RWLIST_UNLOCK(&body_generators);
@@ -3184,14 +3192,15 @@ int ast_sip_pubsub_generate_body_content(const char *type, const char *subtype,
}
if (strcmp(data->body_type, generator->body_type)) {
- ast_log(LOG_WARNING, "Body generator does not accept the type of data provided\n");
+ ast_log(LOG_WARNING, "%s/%s body generator does not accept the type of data provided\n",
+ type, subtype);
return -1;
}
body = generator->allocate_body(data->body_data);
if (!body) {
- ast_log(LOG_WARNING, "Unable to allocate a NOTIFY body of type %s/%s\n",
- type, subtype);
+ ast_log(LOG_WARNING, "%s/%s body generator could not to allocate a body\n",
+ type, subtype);
return -1;
}
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;
}