From a25a630659c49e5e5cddacdaa06a0bd323f7c9a1 Mon Sep 17 00:00:00 2001 From: Kevin Harwell Date: Tue, 2 Jul 2013 17:06:06 +0000 Subject: New SIP Channel driver: Always Auth Reject If no matching endpoint is found for the incoming request Asterisk will respond with a 401 Unauthorized (rejecting the request), but will first challenge if no authorization creditials are given. Changes also included moving ACL options into a new global 'security' configuration section in res_sip.conf. (closes issue ASTERISK-21433) Reported by: Matt Jordan Review: https://reviewboard.asterisk.org/r/2554/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@393442 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- res/res_sip.c | 2 + res/res_sip.exports.in | 2 + res/res_sip/config_auth.c | 2 + res/res_sip/config_security.c | 88 +++++++++++++++++++++++++++++ res/res_sip/sip_configuration.c | 12 ++++ res/res_sip/sip_distributor.c | 76 +++++++++++++++++++++---- res/res_sip_acl.c | 82 ++++----------------------- res/res_sip_authenticator_digest.c | 42 +++++++++----- res/res_sip_outbound_authenticator_digest.c | 3 + 9 files changed, 212 insertions(+), 97 deletions(-) create mode 100644 res/res_sip/config_security.c (limited to 'res') diff --git a/res/res_sip.c b/res/res_sip.c index ebbf596de..61e8e34be 100644 --- a/res/res_sip.c +++ b/res/res_sip.c @@ -1519,6 +1519,7 @@ static int load_module(void) return AST_MODULE_LOAD_SUCCESS; error: + ast_sip_destroy_distributor(); ast_res_sip_destroy_configuration(); if (monitor_thread) { stop_monitor_thread(); @@ -1561,6 +1562,7 @@ static int unload_pjsip(void *data) static int unload_module(void) { + ast_sip_destroy_distributor(); ast_res_sip_destroy_configuration(); if (monitor_thread) { stop_monitor_thread(); diff --git a/res/res_sip.exports.in b/res/res_sip.exports.in index f557300f9..fb94f1ef2 100644 --- a/res/res_sip.exports.in +++ b/res/res_sip.exports.in @@ -53,6 +53,8 @@ LINKER_SYMBOL_PREFIXast_sip_retrieve_auths; LINKER_SYMBOL_PREFIXast_sip_cleanup_auths; LINKER_SYMBOL_PREFIXast_sip_is_content_type; + LINKER_SYMBOL_PREFIXast_sip_get_artificial_endpoint; + LINKER_SYMBOL_PREFIXast_sip_get_artificial_auth; LINKER_SYMBOL_PREFIXast_sip_report_invalid_endpoint; LINKER_SYMBOL_PREFIXast_sip_report_failed_acl; LINKER_SYMBOL_PREFIXast_sip_report_auth_failed_challenge_response; diff --git a/res/res_sip/config_auth.c b/res/res_sip/config_auth.c index 47ff42deb..bffb96c39 100644 --- a/res/res_sip/config_auth.c +++ b/res/res_sip/config_auth.c @@ -87,6 +87,8 @@ static int auth_apply(const struct ast_sorcery *sorcery, void *obj) res = -1; } break; + case AST_SIP_AUTH_TYPE_ARTIFICIAL: + break; } return res; diff --git a/res/res_sip/config_security.c b/res/res_sip/config_security.c new file mode 100644 index 000000000..b813bdbb2 --- /dev/null +++ b/res/res_sip/config_security.c @@ -0,0 +1,88 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson + * Kevin Harwell + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*** MODULEINFO + pjproject + res_sip + core + ***/ +#include "asterisk.h" + +#include + +#include "asterisk/res_sip.h" +#include "asterisk/logger.h" +#include "asterisk/sorcery.h" +#include "asterisk/acl.h" + +static int acl_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_security *security = obj; + int error = 0; + int ignore; + if (!strncmp(var->name, "contact", 7)) { + ast_append_acl(var->name + 7, var->value, &security->contact_acl, &error, &ignore); + } else { + ast_append_acl(var->name, var->value, &security->acl, &error, &ignore); + } + + return error; +} + +static void security_destroy(void *obj) +{ + struct ast_sip_security *security = obj; + security->acl = ast_free_acl_list(security->acl); + security->contact_acl = ast_free_acl_list(security->contact_acl); +} + +static void *security_alloc(const char *name) +{ + struct ast_sip_security *security = + ast_sorcery_generic_alloc(sizeof(*security), security_destroy); + + if (!security) { + return NULL; + } + + return security; +} + +int ast_sip_initialize_sorcery_security(struct ast_sorcery *sorcery) +{ + ast_sorcery_apply_default(sorcery, SIP_SORCERY_SECURITY_TYPE, + "config", "res_sip.conf,criteria=type=security"); + + if (ast_sorcery_object_register(sorcery, SIP_SORCERY_SECURITY_TYPE, + security_alloc, NULL, NULL)) { + + ast_log(LOG_ERROR, "Failed to register SIP %s object with sorcery\n", + SIP_SORCERY_SECURITY_TYPE); + return -1; + } + + ast_sorcery_object_field_register(sorcery, SIP_SORCERY_SECURITY_TYPE, "type", "", OPT_NOOP_T, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_SECURITY_TYPE, "permit", "", acl_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_SECURITY_TYPE, "deny", "", acl_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_SECURITY_TYPE, "acl", "", acl_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_SECURITY_TYPE, "contactpermit", "", acl_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_SECURITY_TYPE, "contactdeny", "", acl_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_SECURITY_TYPE, "contactacl", "", acl_handler, NULL, 0, 0); + return 0; +} diff --git a/res/res_sip/sip_configuration.c b/res/res_sip/sip_configuration.c index 11d478897..ca4660acb 100644 --- a/res/res_sip/sip_configuration.c +++ b/res/res_sip/sip_configuration.c @@ -281,6 +281,11 @@ static int timers_handler(const struct aco_option *opt, struct ast_variable *var static void destroy_auths(const char **auths, size_t num_auths) { int i; + + if (!auths) { + return; + } + for (i = 0; i < num_auths; ++i) { ast_free((char *) auths[i]); } @@ -684,6 +689,13 @@ int ast_res_sip_initialize_configuration(void) return -1; } + if (ast_sip_initialize_sorcery_security(sip_sorcery)) { + ast_log(LOG_ERROR, "Failed to register SIP security support\n"); + ast_sorcery_unref(sip_sorcery); + sip_sorcery = NULL; + return -1; + } + ast_sorcery_load(sip_sorcery); return 0; diff --git a/res/res_sip/sip_distributor.c b/res/res_sip/sip_distributor.c index db36b6182..6ddc0e70a 100644 --- a/res/res_sip/sip_distributor.c +++ b/res/res_sip/sip_distributor.c @@ -59,7 +59,7 @@ void ast_sip_dialog_set_serializer(pjsip_dialog *dlg, struct ast_taskprocessor * { struct distributor_dialog_data *dist; SCOPED_LOCK(lock, dlg, pjsip_dlg_inc_lock, pjsip_dlg_dec_lock); - + dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id); if (!dist) { dist = distributor_dialog_data_alloc(dlg); @@ -71,7 +71,7 @@ void ast_sip_dialog_set_endpoint(pjsip_dialog *dlg, struct ast_sip_endpoint *end { struct distributor_dialog_data *dist; SCOPED_LOCK(lock, dlg, pjsip_dlg_inc_lock, pjsip_dlg_dec_lock); - + dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id); if (!dist) { dist = distributor_dialog_data_alloc(dlg); @@ -125,6 +125,48 @@ static pjsip_module endpoint_mod = { .on_rx_request = endpoint_lookup, }; +static struct ast_sip_auth *artificial_auth; + +static int create_artificial_auth(void) +{ + if (!(artificial_auth = ast_sorcery_alloc( + ast_sip_get_sorcery(), SIP_SORCERY_AUTH_TYPE, "artificial"))) { + ast_log(LOG_ERROR, "Unable to create artificial auth\n"); + return -1; + } + + ast_string_field_set(artificial_auth, realm, "asterisk"); + ast_string_field_set(artificial_auth, auth_user, ""); + ast_string_field_set(artificial_auth, auth_pass, ""); + artificial_auth->type = AST_SIP_AUTH_TYPE_ARTIFICIAL; + return 0; +} + +struct ast_sip_auth *ast_sip_get_artificial_auth(void) +{ + ao2_ref(artificial_auth, +1); + return artificial_auth; +} + +static struct ast_sip_endpoint *artificial_endpoint; + +static int create_artificial_endpoint(void) +{ + if (!(artificial_endpoint = ast_sorcery_alloc( + ast_sip_get_sorcery(), "endpoint", NULL))) { + return -1; + } + + artificial_endpoint->num_inbound_auths = 1; + return 0; +} + +struct ast_sip_endpoint *ast_sip_get_artificial_endpoint(void) +{ + ao2_ref(artificial_endpoint, +1); + return artificial_endpoint; +} + static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata) { struct ast_sip_endpoint *endpoint; @@ -143,11 +185,12 @@ static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata) char name[AST_UUID_STR_LEN] = ""; pjsip_uri *from = rdata->msg_info.from->uri; - /* XXX When we do an alwaysauthreject-like option, we'll need to take that into account - * for this response. Either that, or have a pseudo-endpoint to pass along so that authentication - * will fail - */ - pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); + /* always use an artificial endpoint - per discussion no reason + to have "alwaysauthreject" as an option. It is felt using it + was a bug fix and it is not needed since we are not worried about + breaking old stuff and we really don't want to enable the discovery + of SIP accounts */ + endpoint = ast_sip_get_artificial_endpoint(); if (PJSIP_URI_SCHEME_IS_SIP(from) || PJSIP_URI_SCHEME_IS_SIPS(from)) { pjsip_sip_uri *sip_from = pjsip_uri_get_uri(from); @@ -155,7 +198,6 @@ static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata) } ast_sip_report_invalid_endpoint(name, rdata); - return PJ_TRUE; } rdata->endpt_info.mod_data[endpoint_mod.id] = endpoint; return PJ_FALSE; @@ -183,8 +225,7 @@ static pj_bool_t authenticate(pjsip_rx_data *rdata) return PJ_FALSE; case AST_SIP_AUTHENTICATION_FAILED: ast_sip_report_auth_failed_challenge_response(endpoint, rdata); - pjsip_tx_data_dec_ref(tdata); - pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); + pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL); return PJ_TRUE; case AST_SIP_AUTHENTICATION_ERROR: ast_sip_report_auth_failed_challenge_response(endpoint, rdata); @@ -240,6 +281,10 @@ struct ast_sip_endpoint *ast_pjsip_rdata_get_endpoint(pjsip_rx_data *rdata) int ast_sip_initialize_distributor(void) { + if (create_artificial_endpoint() || create_artificial_auth()) { + return -1; + } + if (ast_sip_register_service(&distributor_mod)) { return -1; } @@ -249,5 +294,16 @@ int ast_sip_initialize_distributor(void) if (ast_sip_register_service(&auth_mod)) { return -1; } + return 0; } + +void ast_sip_destroy_distributor(void) +{ + ast_sip_unregister_service(&distributor_mod); + ast_sip_unregister_service(&endpoint_mod); + ast_sip_unregister_service(&auth_mod); + + ao2_cleanup(artificial_auth); + ao2_cleanup(artificial_endpoint); +} diff --git a/res/res_sip_acl.c b/res/res_sip_acl.c index 057ae96f7..7c9e43af1 100644 --- a/res/res_sip_acl.c +++ b/res/res_sip_acl.c @@ -77,19 +77,13 @@ List of IP-domains to allow access from - Must be of type 'acl'. + Must be of type 'security'. ***/ -struct sip_acl { - SORCERY_OBJECT(details); - struct ast_acl_list *acl; - struct ast_acl_list *contact_acl; -}; - static int apply_acl(pjsip_rx_data *rdata, struct ast_acl_list *acl) { struct ast_sockaddr addr; @@ -161,10 +155,11 @@ static int apply_contact_acl(pjsip_rx_data *rdata, struct ast_acl_list *contact_ static int check_acls(void *obj, void *arg, int flags) { - struct sip_acl *acl = obj; + struct ast_sip_security *security = obj; pjsip_rx_data *rdata = arg; - if (apply_acl(rdata, acl->acl) || apply_contact_acl(rdata, acl->contact_acl)) { + if (apply_acl(rdata, security->acl) || + apply_contact_acl(rdata, security->contact_acl)) { return CMP_MATCH | CMP_STOP; } return 0; @@ -172,22 +167,17 @@ static int check_acls(void *obj, void *arg, int flags) static pj_bool_t acl_on_rx_msg(pjsip_rx_data *rdata) { - int forbidden = 0; - struct ao2_container *acls = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "acl", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL); - struct sip_acl *matched_acl; + RAII_VAR(struct ao2_container *, acls, ast_sorcery_retrieve_by_fields( + ast_sip_get_sorcery(), SIP_SORCERY_SECURITY_TYPE, + AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL), ao2_cleanup); + RAII_VAR(struct ast_sip_security *, matched_acl, NULL, ao2_cleanup); + if (!acls) { ast_log(LOG_ERROR, "Unable to retrieve ACL sorcery data\n"); return PJ_FALSE; } - matched_acl = ao2_callback(acls, 0, check_acls, rdata); - if (matched_acl) { - forbidden = 1; - ao2_ref(matched_acl, -1); - } - ao2_ref(acls, -1); - - if (forbidden) { + if ((matched_acl = ao2_callback(acls, 0, check_acls, rdata))) { if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); } @@ -204,60 +194,8 @@ static pjsip_module acl_module = { .on_rx_request = acl_on_rx_msg, }; -static int acl_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) -{ - struct sip_acl *acl = obj; - int error; - int ignore; - if (!strncmp(var->name, "contact", 7)) { - ast_append_acl(var->name + 7, var->value, &acl->contact_acl, &error, &ignore); - } else { - ast_append_acl(var->name, var->value, &acl->acl, &error, &ignore); - } - return error; -} - -static void sip_acl_destructor(void *obj) -{ - struct sip_acl *acl = obj; - acl->acl = ast_free_acl_list(acl->acl); - acl->contact_acl = ast_free_acl_list(acl->contact_acl); -} - -static void *sip_acl_alloc(const char *name) -{ - struct sip_acl *acl = ast_sorcery_generic_alloc(sizeof(*acl), sip_acl_destructor); - if (!acl) { - return NULL; - } - return acl; -} - -static int load_acls(void) -{ - ast_sorcery_apply_default(ast_sip_get_sorcery(), "acl", "config", "res_sip.conf,criteria=type=acl"); - if (ast_sorcery_object_register(ast_sip_get_sorcery(), "acl", sip_acl_alloc, NULL, NULL)) { - ast_log(LOG_ERROR, "Failed to register SIP ACL object with sorcery\n"); - return -1; - } - ast_sorcery_object_field_register(ast_sip_get_sorcery(), "acl", "type", "", OPT_NOOP_T, 0, 0); - ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "acl", "permit", "", acl_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "acl", "deny", "", acl_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "acl", "acl", "", acl_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "acl", "contactpermit", "", acl_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "acl", "contactdeny", "", acl_handler, NULL, 0, 0); - ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "acl", "contactacl", "", acl_handler, NULL, 0, 0); - - /* XXX Is there a more selective way to do this? (i.e. Just reload a specific object type?) */ - ast_sorcery_reload(ast_sip_get_sorcery()); - return 0; -} - static int load_module(void) { - if (load_acls()) { - return AST_MODULE_LOAD_DECLINE; - } ast_sip_register_service(&acl_module); return AST_MODULE_LOAD_SUCCESS; } diff --git a/res/res_sip_authenticator_digest.c b/res/res_sip_authenticator_digest.c index 499342309..8e3ff6de7 100644 --- a/res/res_sip_authenticator_digest.c +++ b/res/res_sip_authenticator_digest.c @@ -149,6 +149,10 @@ static pj_status_t digest_lookup(pj_pool_t *pool, const pj_str_t *realm, return PJSIP_SC_FORBIDDEN; } + if (auth->type == AST_SIP_AUTH_TYPE_ARTIFICIAL) { + return PJSIP_SC_FORBIDDEN; + } + if (pj_strcmp2(realm, auth->realm)) { return PJSIP_SC_FORBIDDEN; } @@ -268,12 +272,12 @@ static int find_challenge(const pjsip_rx_data *rdata, const struct ast_sip_auth /*! * \brief Common code for initializing a pjsip_auth_srv */ -static void setup_auth_srv(pj_pool_t *pool, pjsip_auth_srv *auth_server, const struct ast_sip_auth *auth) +static void setup_auth_srv(pj_pool_t *pool, pjsip_auth_srv *auth_server, const char *realm) { - pj_str_t realm; - pj_cstr(&realm, auth->realm); + pj_str_t realm_str; + pj_cstr(&realm_str, realm); - pjsip_auth_srv_init(pool, auth_server, &realm, digest_lookup, 0); + pjsip_auth_srv_init(pool, auth_server, &realm_str, digest_lookup, 0); } /*! @@ -311,7 +315,7 @@ static int verify(struct ast_sip_auth *auth, pjsip_rx_data *rdata, pj_pool_t *po stale = 1; } - setup_auth_srv(pool, &auth_server, auth); + setup_auth_srv(pool, &auth_server, auth->realm); store_auth(auth); @@ -332,12 +336,12 @@ static int verify(struct ast_sip_auth *auth, pjsip_rx_data *rdata, pj_pool_t *po /*! * \brief astobj2 callback for adding digest challenges to responses * - * \param auth The ast_aip_auth to build a challenge from + * \param realm An auth's realm to build a challenge from * \param tdata The response to add the challenge to * \param rdata The request the challenge is in response to * \param is_stale Indicates whether nonce on incoming request was stale */ -static void challenge(const struct ast_sip_auth *auth, pjsip_tx_data *tdata, const pjsip_rx_data *rdata, int is_stale) +static void challenge(const char *realm, pjsip_tx_data *tdata, const pjsip_rx_data *rdata, int is_stale) { pj_str_t qop; pj_str_t pj_nonce; @@ -347,9 +351,9 @@ static void challenge(const struct ast_sip_auth *auth, pjsip_tx_data *tdata, con time_t timestamp = time(NULL); snprintf(time_buf, sizeof(time_buf), "%d", (int) timestamp); - build_nonce(&nonce, time_buf, rdata, auth->realm); + build_nonce(&nonce, time_buf, rdata, realm); - setup_auth_srv(tdata->pool, &auth_server, auth); + setup_auth_srv(tdata->pool, &auth_server, realm); pj_cstr(&pj_nonce, ast_str_buffer(nonce)); pj_cstr(&qop, "auth"); @@ -362,22 +366,30 @@ static void challenge(const struct ast_sip_auth *auth, pjsip_tx_data *tdata, con * This function will check an incoming message against configured authentication * options. If \b any of the incoming Authorization headers result in successful * authentication, then authentication is considered successful. - * + * * \see ast_sip_check_authentication */ static enum ast_sip_check_auth_result digest_check_auth(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, pjsip_tx_data *tdata) { - struct ast_sip_auth **auths = ast_alloca(endpoint->num_inbound_auths * sizeof(*auths)); - enum digest_verify_result *verify_res = ast_alloca(endpoint->num_inbound_auths * sizeof(*verify_res)); + struct ast_sip_auth **auths; + enum digest_verify_result *verify_res; enum ast_sip_check_auth_result res; int i; + RAII_VAR(struct ast_sip_endpoint *, artificial_endpoint, + ast_sip_get_artificial_endpoint(), ao2_cleanup); + + auths = ast_alloca(endpoint->num_inbound_auths * sizeof(*auths)); + verify_res = ast_alloca(endpoint->num_inbound_auths * sizeof(*verify_res)); + if (!auths) { return AST_SIP_AUTHENTICATION_ERROR; } - if (ast_sip_retrieve_auths(endpoint->sip_inbound_auths, endpoint->num_inbound_auths, auths)) { + if (endpoint == artificial_endpoint) { + auths[0] = ast_sip_get_artificial_auth(); + } else if (ast_sip_retrieve_auths(endpoint->sip_inbound_auths, endpoint->num_inbound_auths, auths)) { res = AST_SIP_AUTHENTICATION_ERROR; goto cleanup; } @@ -391,9 +403,9 @@ static enum ast_sip_check_auth_result digest_check_auth(struct ast_sip_endpoint } for (i = 0; i < endpoint->num_inbound_auths; ++i) { - challenge(auths[i], tdata, rdata, verify_res[i] == AUTH_STALE); + challenge(auths[i]->realm, tdata, rdata, verify_res[i] == AUTH_STALE); } - + res = AST_SIP_AUTHENTICATION_CHALLENGE; cleanup: diff --git a/res/res_sip_outbound_authenticator_digest.c b/res/res_sip_outbound_authenticator_digest.c index d4ce7d66a..13dc1f21b 100644 --- a/res/res_sip_outbound_authenticator_digest.c +++ b/res/res_sip_outbound_authenticator_digest.c @@ -56,6 +56,9 @@ static int set_outbound_authentication_credentials(pjsip_auth_clt_sess *auth_ses pj_cstr(&auth_creds[i].data, auths[i]->md5_creds); auth_creds[i].data_type = PJSIP_CRED_DATA_DIGEST; break; + case AST_SIP_AUTH_TYPE_ARTIFICIAL: + ast_log(LOG_ERROR, "Trying to set artificial outbound auth credentials shouldn't happen.\n"); + break; } } -- cgit v1.2.3