diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample index de3e8664ed..7b4c7aac1e 100644 --- a/configs/samples/pjsip.conf.sample +++ b/configs/samples/pjsip.conf.sample @@ -927,6 +927,26 @@ ; happens to the call if verification fails; it's up to ; you to determine what to do with the results. ; (default: no) +;allow_unauthenticated_options = + ; By default, chan_pjsip will challenge an incoming + ; OPTIONS request for authentication credentials just + ; as it would an INVITE request. This is consistent + ; with RFC 3261. + ; There are many UAs that use an OPTIONS request as a + ; "ping" and they expect a 200 response indicating that + ; the remote party is up and running without a need to + ; authenticate. + ; Setting allow_unauthenticated_options to 'yes' will + ; instruct chan_pjsip to skip the authentication step + ; when it receives an OPTIONS request for this + ; endpoint. + ; There are security implications to enabling this + ; setting as it can allow information disclosure to + ; occur - specifically, if enabled, an external party + ; could enumerate and find the endpoint name by + ; sending OPTIONS requests and examining the + ; responses. + ; (default: no) ;==========================AUTH SECTION OPTIONS========================= ;[auth] diff --git a/contrib/ast-db-manage/config/versions/c20d6e3992f4_add_allow_unauthenticated_options.py b/contrib/ast-db-manage/config/versions/c20d6e3992f4_add_allow_unauthenticated_options.py new file mode 100644 index 0000000000..deec5325d7 --- /dev/null +++ b/contrib/ast-db-manage/config/versions/c20d6e3992f4_add_allow_unauthenticated_options.py @@ -0,0 +1,29 @@ +"""add allow_unauthenticated_options + +Revision ID: c20d6e3992f4 +Revises: 8915fcc5766f +Create Date: 2021-04-23 13:44:38.296558 + +""" + +# revision identifiers, used by Alembic. +revision = 'c20d6e3992f4' +down_revision = '8915fcc5766f' + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects.postgresql import ENUM + +AST_BOOL_NAME = 'ast_bool_values' +AST_BOOL_VALUES = [ '0', '1', + 'off', 'on', + 'false', 'true', + 'no', 'yes' ] + +def upgrade(): + ast_bool_values = ENUM(*AST_BOOL_VALUES, name=AST_BOOL_NAME, create_type=False) + op.add_column('ps_endpoints', sa.Column('allow_unauthenticated_options', ast_bool_values)) + +def downgrade(): + op.drop_column('ps_endpoints', 'allow_unauthenticated_options') + pass diff --git a/doc/CHANGES-staging/pjsip_endpoint_unauthenticated_options.txt b/doc/CHANGES-staging/pjsip_endpoint_unauthenticated_options.txt new file mode 100644 index 0000000000..9c8d32cb0e --- /dev/null +++ b/doc/CHANGES-staging/pjsip_endpoint_unauthenticated_options.txt @@ -0,0 +1,5 @@ +Subject: res_pjsip + +PJSIP endpoints can now be configured to skip authentication when +handling OPTIONS requests by setting the allow_unauthenticated_options +configuration property to 'yes.' diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index 5e3e439fda..2020ca8782 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -915,6 +915,8 @@ struct ast_sip_endpoint { unsigned int ignore_183_without_sdp; /*! Enable STIR/SHAKEN support on this endpoint */ unsigned int stir_shaken; + /*! Should we authenticate OPTIONS requests per RFC 3261? */ + unsigned int allow_unauthenticated_options; }; /*! URI parameter for symmetric transport */ diff --git a/res/res_pjsip.c b/res/res_pjsip.c index 53198eacf2..775b63f8d1 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -1472,6 +1472,23 @@ INVITEs, an Identity header will be added. + + Skip authentication when receiving OPTIONS requests + + RFC 3261 says that the response to an OPTIONS request MUST be the + same had the request been an INVITE. Some UAs use OPTIONS requests + like a 'ping' and the expectation is that they will return a + 200 OK. + Enabling allow_unauthenticated_options + will skip authentication of OPTIONS requests for the given + endpoint. + There are security implications to enabling this setting as + it can allow information disclosure to occur - specifically, if + enabled, an external party could enumerate and find the endpoint + name by sending OPTIONS requests and examining the + responses. + + Authentication type @@ -3307,6 +3324,12 @@ void ast_sip_unregister_authenticator(struct ast_sip_authenticator *auth) int ast_sip_requires_authentication(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata) { + if (endpoint->allow_unauthenticated_options + && !pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_options_method)) { + ast_debug(3, "Skipping OPTIONS authentication due to endpoint configuration\n"); + return 0; + } + if (!registered_authenticator) { ast_log(LOG_WARNING, "No SIP authenticator registered. Assuming authentication is not required\n"); return 0; diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index 80bad02b37..6d12c6f6ec 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -2153,6 +2153,7 @@ int ast_res_pjsip_initialize_configuration(void) "prefer: pending, operation: intersect, keep: all", codec_prefs_handler, outgoing_answer_codec_prefs_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "stir_shaken", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, stir_shaken)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow_unauthenticated_options", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, allow_unauthenticated_options)); if (ast_sip_initialize_sorcery_transport()) { ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");