diff --git a/CHANGES b/CHANGES index 68617a441893b7eef6371743eccb41452dd91fdf..683a164ab0b41cd8aa0b0b69bdb4d1104f9e38c3 100644 --- a/CHANGES +++ b/CHANGES @@ -26,6 +26,16 @@ chan_sip headers be retrieved from the REFER message and made accessible to the dialplan in the hash TRANSFER_DATA. +------------------------------------------------------------------------------ +--- Functionality changes from Asterisk 15.1.0 to Asterisk 15.2.0 ------------ +------------------------------------------------------------------------------ + +res_pjsip +------------------ + * The "identify_by" on endpoints can now be set to "ip" to restrict an endpoint + being matched based only on IP address. To ensure no behavior change the + default has been changed to "username,ip". + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 15.0.0 to Asterisk 15.1.0 ------------ ------------------------------------------------------------------------------ diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample index c535aaf04c4402f9e9de5138e22bb83cbd15a8b2..800ff0f444248fc8c8961d937ece17bc3f1fa0a4 100644 --- a/configs/samples/pjsip.conf.sample +++ b/configs/samples/pjsip.conf.sample @@ -634,9 +634,11 @@ ; identified. ; "username": Identify by the From or To username and domain ; "auth_username": Identify by the Authorization username and realm - ; In all cases, if an exact match on username and domain/realm fails, - ; the match will be retried with just the username. - ; (default: "username") + ; "ip": Identify by the source IP address + ; In username and auth_username cases, if an exact match on + ; username and domain/realm fails, the match will be retried + ; with just the username. + ; (default: "username,ip") ;redirect_method=user ; How redirects received from an endpoint are handled ; (default: "user") ;mailboxes= ; NOTIFY the endpoint when state changes for any of the specified mailboxes. diff --git a/contrib/ast-db-manage/config/versions/20abce6d1e3c_add_pjsip_identify_by_ip.py b/contrib/ast-db-manage/config/versions/20abce6d1e3c_add_pjsip_identify_by_ip.py new file mode 100644 index 0000000000000000000000000000000000000000..d457c9233d6d0f3071971cc5d89554eaf6fa97e4 --- /dev/null +++ b/contrib/ast-db-manage/config/versions/20abce6d1e3c_add_pjsip_identify_by_ip.py @@ -0,0 +1,46 @@ +"""add pjsip identify by ip + +Revision ID: 20abce6d1e3c +Revises: a1698e8bb9c5 +Create Date: 2017-10-24 15:44:06.404774 + +""" + +# revision identifiers, used by Alembic. +revision = '20abce6d1e3c' +down_revision = 'a1698e8bb9c5' + +from alembic import op +import sqlalchemy as sa + + +def enum_update(table_name, column_name, enum_name, enum_values): + if op.get_context().bind.dialect.name != 'postgresql': + if op.get_context().bind.dialect.name == 'mssql': + op.drop_constraint('ck_ps_endpoints_identify_by_pjsip_identify_by_values', 'ps_endpoints') + op.alter_column(table_name, column_name, + type_=sa.Enum(*enum_values, name=enum_name)) + return + + # Postgres requires a few more steps + tmp = enum_name + '_tmp' + + op.execute('ALTER TYPE ' + enum_name + ' RENAME TO ' + tmp) + + updated = sa.Enum(*enum_values, name=enum_name) + updated.create(op.get_bind(), checkfirst=False) + + op.execute('ALTER TABLE ' + table_name + ' ALTER COLUMN ' + column_name + + ' TYPE ' + enum_name + ' USING identify_by::text::' + enum_name) + + op.execute('DROP TYPE ' + tmp) + + +def upgrade(): + enum_update('ps_endpoints', 'identify_by', 'pjsip_identify_by_values', + ['username', 'auth_username', 'ip']) + + +def downgrade(): + enum_update('ps_endpoints', 'identify_by', 'pjsip_identify_by_values', + ['username', 'auth_username']) diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index e6ccf0a1d4f0de0a8a23640f7bf22f6face94708..e71eb98d79e74422e56a242e1025a97e6f1f541e 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -437,6 +437,8 @@ enum ast_sip_endpoint_identifier_type { AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME = (1 << 0), /*! Identify based on user name in Auth header first, then From header */ AST_SIP_ENDPOINT_IDENTIFY_BY_AUTH_USERNAME = (1 << 1), + /*! Identify based on source IP address */ + AST_SIP_ENDPOINT_IDENTIFY_BY_IP = (1 << 2), }; AST_VECTOR(ast_sip_identify_by_vector, enum ast_sip_endpoint_identifier_type); diff --git a/res/res_pjsip.c b/res/res_pjsip.c index f81d34ca447a3f1b7a19bf2c8400c05c78da2433..7499ded3e1ceb7769eb7c692a9618038f91cde76 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -268,15 +268,17 @@ <configOption name="ice_support" default="no"> <synopsis>Enable the ICE mechanism to help traverse NAT</synopsis> </configOption> - <configOption name="identify_by" default="username,location"> + <configOption name="identify_by" default="username,ip"> <synopsis>Way(s) for Endpoint to be identified</synopsis> <description><para> 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 + the username and domain in the From header (or To header for aors), <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. + username and realm in the Authentication header, and <literal>ip</literal> which matches + an endpoint based on the source IP address. In the <literal>username</literal> and + <literal>auth_username</literal> 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 @@ -292,14 +294,19 @@ 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 and an <literal>identify</literal> - configuration section exists for the endpoint, then the endpoint can be identified in - multiple ways.</para></note> + of identification is not configured but simply allowed by this configuration option. + See the documentation for the <literal>identify</literal> configuration section for + more details on that method of endpoint identification.</para></note> + <note><para> + This option controls both how an endpoint is matched for incoming traffic and also how + an AoR is determined if a registration occurs. If <literal>ip</literal> is set alone + then incoming registration will not find an AoR and the registration attempt will fail. + If you want to allow incoming registrations to succeed you must set a second identify + method such as <literal>username</literal> in this case.</para></note> <enumlist> <enum name="username" /> <enum name="auth_username" /> + <enum name="ip" /> </enumlist> </description> </configOption> diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index 653cb98ac77807e7e1fb71441f4816ca9d4af57b..6db5b38985ab1c204a3f2b38d2ab49090361f3c2 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -582,8 +582,10 @@ static int ident_handler(const struct aco_option *opt, struct ast_variable *var, if (!strcasecmp(val, "username")) { method = AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME; - } else if (!strcasecmp(val, "auth_username")) { + } else if (!strcasecmp(val, "auth_username")) { method = AST_SIP_ENDPOINT_IDENTIFY_BY_AUTH_USERNAME; + } else if (!strcasecmp(val, "ip")) { + method = AST_SIP_ENDPOINT_IDENTIFY_BY_IP; } else { ast_log(LOG_ERROR, "Unrecognized identification method %s specified for endpoint %s\n", val, ast_sorcery_object_get_id(endpoint)); @@ -628,6 +630,9 @@ static int ident_to_str(const void *obj, const intptr_t *args, char **buf) case AST_SIP_ENDPOINT_IDENTIFY_BY_AUTH_USERNAME : method = "auth_username"; break; + case AST_SIP_ENDPOINT_IDENTIFY_BY_IP : + method = "ip"; + break; default: continue; } @@ -1901,7 +1906,7 @@ int ast_res_pjsip_initialize_configuration(void) ast_sorcery_object_field_register(sip_sorcery, "endpoint", "aors", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, aors)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "media_address", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, media.address)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "bind_rtp_to_media_address", "no", OPT_BOOL_T, 1, STRFLDSET(struct ast_sip_endpoint, media.bind_rtp_to_media_address)); - ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "identify_by", "username", ident_handler, ident_to_str, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "identify_by", "username,ip", ident_handler, ident_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "direct_media", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.direct_media.enabled)); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "direct_media_method", "invite", direct_media_method_handler, direct_media_method_to_str, NULL, 0, 0); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "connected_line_method", "invite", connected_line_method_handler, connected_line_method_to_str, NULL, 0, 0); diff --git a/res/res_pjsip_endpoint_identifier_ip.c b/res/res_pjsip_endpoint_identifier_ip.c index 30bfc2618c5ca4da66d2c4f89a02b891b53d2ea8..8b92cef27e8f8aa3b5db41c4a5caa8f1546a250f 100644 --- a/res/res_pjsip_endpoint_identifier_ip.c +++ b/res/res_pjsip_endpoint_identifier_ip.c @@ -227,7 +227,14 @@ static struct ast_sip_endpoint *ip_identify(pjsip_rx_data *rdata) } endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", match->endpoint_name); + if (endpoint) { + if (!(endpoint->ident_method & AST_SIP_ENDPOINT_IDENTIFY_BY_IP)) { + ast_debug(3, "Endpoint '%s' found for '%s' but 'ip' method not supported'\n", match->endpoint_name, + ast_sockaddr_stringify(&addr)); + ao2_cleanup(endpoint); + return NULL; + } ast_debug(3, "Retrieved endpoint %s\n", ast_sorcery_object_get_id(endpoint)); } else { ast_log(LOG_WARNING, "Identify section '%s' points to endpoint '%s' but endpoint could not be looked up\n",