From: Mark Michelson Date: Thu, 14 Apr 2016 12:20:46 +0000 (-0500) Subject: AST-2016-004: Fix crash on REGISTER with long URI. X-Git-Tag: certified/13.1-cert5~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e2e8699d003f6d97e7768daa3a7cb82cc7dbc5a6;p=thirdparty%2Fasterisk.git AST-2016-004: Fix crash on REGISTER with long URI. Due to some ignored return values, Asterisk could crash if processing an incoming REGISTER whose contact URI was above a certain length. ASTERISK-25707 #close Reported by George Joseph Patches: 0001-res_pjsip-Validate-that-URIs-don-t-exceed-pjproject-.patch AST-2016-004 Change-Id: Ic4f5e49f1a83fef4951ffeeef8f443a7f6ac15eb --- diff --git a/include/asterisk/res_pjproject.h b/include/asterisk/res_pjproject.h new file mode 100644 index 0000000000..8828b340c8 --- /dev/null +++ b/include/asterisk/res_pjproject.h @@ -0,0 +1,96 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2016, Fairview 5 Engineering, LLC + * + * George Joseph + * + * 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. + */ + +#ifndef _RES_PJPROJECT_H +#define _RES_PJPROJECT_H + +/*! \brief Determines whether the res_pjproject module is loaded */ +#define CHECK_PJPROJECT_MODULE_LOADED() \ + do { \ + if (!ast_module_check("res_pjproject.so")) { \ + return AST_MODULE_LOAD_DECLINE; \ + } \ + } while(0) + +/*! + * \brief Retrieve a pjproject build option + * + * \param option The build option requested + * \param format_string A scanf-style format string to parse the option value into + * \param ... Pointers to variables to receive the values parsed + * + * \retval The number of values parsed + * + * \since 13.8.0 + * + * \note The option requested must be from those returned by pj_dump_config() + * which can be displayed with the 'pjsip show buildopts' CLI command. + * + * Sample Usage: + * \code + * + * int max_hostname; + * + * ast_sip_get_pjproject_buildopt("PJ_MAX_HOSTNAME", "%d", &max_hostname); + * + * \endcode + * + */ +int ast_pjproject_get_buildopt(char *option, char *format_string, ...) __attribute__((format(scanf, 2, 3))); + +/*! + * \brief Begin PJPROJECT log interception for CLI output. + * \since 13.8.0 + * + * \param fd CLI file descriptior to send intercepted output. + * + * \note ast_pjproject_log_intercept_begin() and + * ast_pjproject_log_intercept_end() must always be called + * in pairs. + * + * \return Nothing + */ +void ast_pjproject_log_intercept_begin(int fd); + +/*! + * \brief End PJPROJECT log interception for CLI output. + * \since 13.8.0 + * + * \note ast_pjproject_log_intercept_begin() and + * ast_pjproject_log_intercept_end() must always be called + * in pairs. + * + * \return Nothing + */ +void ast_pjproject_log_intercept_end(void); + +/*! + * \brief Increment the res_pjproject reference count. + * + * This ensures graceful shutdown happens in the proper order. + */ +void ast_pjproject_ref(void); + +/*! + * \brief Decrement the res_pjproject reference count. + * + * This ensures graceful shutdown happens in the proper order. + */ +void ast_pjproject_unref(void); + +#endif /* _RES_PJPROJECT_H */ diff --git a/include/asterisk/vector.h b/include/asterisk/vector.h index 0053d8a325..bf61fda1ee 100644 --- a/include/asterisk/vector.h +++ b/include/asterisk/vector.h @@ -86,6 +86,31 @@ (vec)->current = 0; \ } while (0) +/*! + * \internal + */ +#define __make_room(idx, vec) ({ \ + int res = 0; \ + do { \ + if ((idx) >= (vec)->max) { \ + size_t new_max = ((idx) + 1) * 2; \ + typeof((vec)->elems) new_elems = ast_calloc(1, \ + new_max * sizeof(*new_elems)); \ + if (new_elems) { \ + memcpy(new_elems, (vec)->elems, \ + (vec)->current * sizeof(*new_elems)); \ + ast_free((vec)->elems); \ + (vec)->elems = new_elems; \ + (vec)->max = new_max; \ + } else { \ + res = -1; \ + break; \ + } \ + } \ + } while(0); \ + res; \ +}) + /*! * \brief Append an element to a vector, growing the vector if needed. * @@ -157,6 +182,36 @@ res; \ }) +/*! + * \brief Add an element into a sorted vector + * + * \param vec Sorted vector to add to. + * \param elem Element to insert. + * \param cmp A strcmp compatible compare function. + * + * \return 0 on success. + * \return Non-zero on failure. + * + * \warning Use of this macro on an unsorted vector will produce unpredictable results + */ +#define AST_VECTOR_ADD_SORTED(vec, elem, cmp) ({ \ + int res = 0; \ + size_t __idx = (vec)->current; \ + do { \ + if (__make_room((vec)->current, vec) != 0) { \ + res = -1; \ + break; \ + } \ + while (__idx > 0 && (cmp((vec)->elems[__idx - 1], elem) > 0)) { \ + (vec)->elems[__idx] = (vec)->elems[__idx - 1]; \ + __idx--; \ + } \ + (vec)->elems[__idx] = elem; \ + (vec)->current++; \ + } while (0); \ + res; \ +}) + /*! * \brief Remove an element from a vector by index. * diff --git a/res/res_pjproject.c b/res/res_pjproject.c new file mode 100644 index 0000000000..83940dd27a --- /dev/null +++ b/res/res_pjproject.c @@ -0,0 +1,256 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * David M. Lee, II + * + * 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. + */ + +/*! \file + * + * \brief Bridge PJPROJECT logging to Asterisk logging. + * \author David M. Lee, II + * + * PJPROJECT logging doesn't exactly match Asterisk logging, but mapping the two is + * not too bad. PJPROJECT log levels are identified by a single int. Limits are + * not specified by PJPROJECT, but their implementation used 1 through 6. + * + * The mapping is as follows: + * - 0: LOG_ERROR + * - 1: LOG_ERROR + * - 2: LOG_WARNING + * - 3 and above: equivalent to ast_debug(level, ...) for res_pjproject.so + */ + +/*** MODULEINFO + pjproject + core + ***/ + +#include "asterisk.h" + +#include +#include +#include +#include + +#include "asterisk/logger.h" +#include "asterisk/module.h" +#include "asterisk/cli.h" +#include "asterisk/res_pjproject.h" +#include "asterisk/vector.h" + +static pj_log_func *log_cb_orig; +static unsigned decor_orig; + +static AST_VECTOR(buildopts, char *) buildopts; + +/*! Protection from other log intercept instances. There can be only one at a time. */ +AST_MUTEX_DEFINE_STATIC(pjproject_log_intercept_lock); + +struct pjproject_log_intercept_data { + pthread_t thread; + int fd; +}; + +static struct pjproject_log_intercept_data pjproject_log_intercept = { + .thread = AST_PTHREADT_NULL, + .fd = -1, +}; + +static void log_forwarder(int level, const char *data, int len) +{ + int ast_level; + /* PJPROJECT doesn't provide much in the way of source info */ + const char * log_source = "pjproject"; + int log_line = 0; + const char *log_func = ""; + int mod_level; + + if (pjproject_log_intercept.fd != -1 + && pjproject_log_intercept.thread == pthread_self()) { + /* + * We are handling a CLI command intercepting PJPROJECT + * log output. + */ + ast_cli(pjproject_log_intercept.fd, "%s\n", data); + return; + } + + /* Lower number indicates higher importance */ + switch (level) { + case 0: /* level zero indicates fatal error, according to docs */ + case 1: /* 1 seems to be used for errors */ + ast_level = __LOG_ERROR; + break; + case 2: /* 2 seems to be used for warnings and errors */ + ast_level = __LOG_WARNING; + break; + default: + ast_level = __LOG_DEBUG; + + /* For levels 3 and up, obey the debug level for res_pjproject */ + mod_level = ast_opt_dbg_module ? + ast_debug_get_by_module("res_pjproject") : 0; + if (option_debug < level && mod_level < level) { + return; + } + break; + } + + /* PJPROJECT uses indention to indicate function call depth. We'll prepend + * log statements with a tab so they'll have a better shot at lining + * up */ + ast_log(ast_level, log_source, log_line, log_func, "\t%s\n", data); +} + +static void capture_buildopts_cb(int level, const char *data, int len) +{ + if (strstr(data, "Teluu") || strstr(data, "Dumping")) { + return; + } + + AST_VECTOR_ADD_SORTED(&buildopts, ast_strdup(ast_skip_blanks(data)), strcmp); +} + +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +int ast_pjproject_get_buildopt(char *option, char *format_string, ...) +{ + int res = 0; + char *format_temp; + int i; + + format_temp = ast_alloca(strlen(option) + strlen(" : ") + strlen(format_string) + 1); + sprintf(format_temp, "%s : %s", option, format_string); + + for (i = 0; i < AST_VECTOR_SIZE(&buildopts); i++) { + va_list arg_ptr; + va_start(arg_ptr, format_string); + res = vsscanf(AST_VECTOR_GET(&buildopts, i), format_temp, arg_ptr); + va_end(arg_ptr); + if (res) { + break; + } + } + + return res; +} +#pragma GCC diagnostic warning "-Wformat-nonliteral" + +void ast_pjproject_log_intercept_begin(int fd) +{ + /* Protect from other CLI instances trying to do this at the same time. */ + ast_mutex_lock(&pjproject_log_intercept_lock); + + pjproject_log_intercept.thread = pthread_self(); + pjproject_log_intercept.fd = fd; +} + +void ast_pjproject_log_intercept_end(void) +{ + pjproject_log_intercept.fd = -1; + pjproject_log_intercept.thread = AST_PTHREADT_NULL; + + ast_mutex_unlock(&pjproject_log_intercept_lock); +} + +void ast_pjproject_ref(void) +{ + ast_module_ref(ast_module_info->self); +} + +void ast_pjproject_unref(void) +{ + ast_module_unref(ast_module_info->self); +} + +static char *handle_pjproject_show_buildopts(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + int i; + + switch (cmd) { + case CLI_INIT: + e->command = "pjproject show buildopts"; + e->usage = + "Usage: pjproject show buildopts\n" + " Show the compile time config of the pjproject that Asterisk is\n" + " running against.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + ast_cli(a->fd, "PJPROJECT compile time config currently running against:\n"); + + for (i = 0; i < AST_VECTOR_SIZE(&buildopts); i++) { + ast_cli(a->fd, "%s\n", AST_VECTOR_GET(&buildopts, i)); + } + + return CLI_SUCCESS; +} + +static struct ast_cli_entry pjproject_cli[] = { + AST_CLI_DEFINE(handle_pjproject_show_buildopts, "Show the compiled config of the pjproject in use"), +}; + +static int load_module(void) +{ + ast_debug(3, "Starting PJPROJECT logging to Asterisk logger\n"); + + pj_init(); + + decor_orig = pj_log_get_decor(); + log_cb_orig = pj_log_get_log_func(); + + if (AST_VECTOR_INIT(&buildopts, 64)) { + return AST_MODULE_LOAD_DECLINE; + } + + /* + * On startup, we want to capture the dump once and store it. + */ + pj_log_set_log_func(capture_buildopts_cb); + pj_log_set_decor(0); + pj_dump_config(); + pj_log_set_decor(PJ_LOG_HAS_SENDER | PJ_LOG_HAS_INDENT); + pj_log_set_log_func(log_forwarder); + + ast_cli_register_multiple(pjproject_cli, ARRAY_LEN(pjproject_cli)); + + return AST_MODULE_LOAD_SUCCESS; +} + +#define NOT_EQUALS(a, b) (a != b) + +static int unload_module(void) +{ + ast_cli_unregister_multiple(pjproject_cli, ARRAY_LEN(pjproject_cli)); + pj_log_set_log_func(log_cb_orig); + pj_log_set_decor(decor_orig); + + AST_VECTOR_REMOVE_CMP_UNORDERED(&buildopts, NULL, NOT_EQUALS, ast_free); + AST_VECTOR_FREE(&buildopts); + + ast_debug(3, "Stopped PJPROJECT logging to Asterisk logger\n"); + + pj_shutdown(); + + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJPROJECT Log and Utility Support", + .support_level = AST_MODULE_SUPPORT_CORE, + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_CHANNEL_DEPEND - 6, + ); diff --git a/res/res_pjproject.exports.in b/res/res_pjproject.exports.in new file mode 100644 index 0000000000..81d55db3b0 --- /dev/null +++ b/res/res_pjproject.exports.in @@ -0,0 +1,7 @@ +{ + global: + LINKER_SYMBOL_PREFIXast_pjproject_*; + local: + *; +}; + diff --git a/res/res_pjsip/include/res_pjsip_private.h b/res/res_pjsip/include/res_pjsip_private.h index fa37c8c4be..100f4667c6 100644 --- a/res/res_pjsip/include/res_pjsip_private.h +++ b/res/res_pjsip/include/res_pjsip_private.h @@ -112,4 +112,10 @@ char *ast_sip_global_default_outbound_endpoint(void); int ast_sip_initialize_cli(void); void ast_sip_destroy_cli(void); +/*! + * \internal + * \brief Validate that the uri meets pjproject length restrictions + */ +int ast_sip_validate_uri_length(const char *uri); + #endif /* RES_PJSIP_PRIVATE_H_ */ diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c index 29f186c42e..837ade05bf 100644 --- a/res/res_pjsip/location.c +++ b/res/res_pjsip/location.c @@ -26,6 +26,10 @@ #include "asterisk/sorcery.h" #include "include/res_pjsip_private.h" #include "asterisk/res_pjsip_cli.h" +#include "asterisk/res_pjproject.h" + +static int pj_max_hostname = PJ_MAX_HOSTNAME; +static int pjsip_max_url_size = PJSIP_MAX_URL_SIZE; /*! \brief Destructor for AOR */ static void aor_destroy(void *obj) @@ -266,6 +270,43 @@ static int permanent_uri_sort_fn(const void *obj_left, const void *obj_right, in return cmp; } +int ast_sip_validate_uri_length(const char *contact_uri) +{ + pjsip_uri *uri; + pjsip_sip_uri *sip_uri; + pj_pool_t *pool; + int max_length = pj_max_hostname - 1; + + if (strlen(contact_uri) > pjsip_max_url_size - 1) { + return -1; + } + + if (!(pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "uri validation", 512, 512))) { + ast_log(LOG_ERROR, "Unable to allocate pool for uri validation\n"); + return -1; + } + + if (!(uri = pjsip_parse_uri(pool, (char *)contact_uri, strlen(contact_uri), 0)) || + (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))) { + pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool); + return -1; + } + + sip_uri = pjsip_uri_get_uri(uri); + if (sip_uri->port == 0) { + max_length -= strlen("_sips.tcp."); + } + + if (sip_uri->host.slen > max_length) { + pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool); + return -1; + } + + pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool); + + return 0; +} + /*! \brief Custom handler for permanent URIs */ static int permanent_uri_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { @@ -283,6 +324,11 @@ static int permanent_uri_handler(const struct aco_option *opt, struct ast_variab struct ast_sip_contact *contact; char contact_id[strlen(aor_id) + strlen(contact_uri) + 2 + 1]; + if (ast_sip_validate_uri_length(contact_uri)) { + ast_log(LOG_ERROR, "Contact uri or hostname length exceeds pjproject limit: %s\n", contact_uri); + return -1; + } + if (!aor->permanent_contacts) { aor->permanent_contacts = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, permanent_uri_sort_fn, NULL); @@ -811,6 +857,11 @@ struct ast_sip_cli_formatter_entry *aor_formatter; int ast_sip_initialize_sorcery_location(void) { struct ast_sorcery *sorcery = ast_sip_get_sorcery(); + + ast_pjproject_get_buildopt("PJ_MAX_HOSTNAME", "%d", &pj_max_hostname); + /* As of pjproject 2.4.5, PJSIP_MAX_URL_SIZE isn't exposed yet but we try anyway. */ + ast_pjproject_get_buildopt("PJSIP_MAX_URL_SIZE", "%d", &pjsip_max_url_size); + ast_sorcery_apply_default(sorcery, "contact", "astdb", "registrar"); ast_sorcery_apply_default(sorcery, "aor", "config", "pjsip.conf,criteria=type=aor"); diff --git a/res/res_pjsip_log_forwarder.c b/res/res_pjsip_log_forwarder.c deleted file mode 100644 index 7b095bb1f2..0000000000 --- a/res/res_pjsip_log_forwarder.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2013, Digium, Inc. - * - * David M. Lee, II - * - * 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. - */ - -/*! \file - * - * \brief Bridge PJSIP logging to Asterisk logging. - * \author David M. Lee, II - * - * PJSIP logging doesn't exactly match Asterisk logging, but mapping the two is - * not too bad. PJSIP log levels are identified by a single int. Limits are - * not specified by PJSIP, but their implementation used 1 through 6. - * - * The mapping is as follows: - * - 0: LOG_ERROR - * - 1: LOG_ERROR - * - 2: LOG_WARNING - * - 3 and above: equivalent to ast_debug(level, ...) for res_pjsip.so - */ - -/*** MODULEINFO - pjproject - core - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include - -#include "asterisk/logger.h" -#include "asterisk/module.h" - -static pj_log_func *log_cb_orig; -static unsigned decor_orig; - -static void log_cb(int level, const char *data, int len) -{ - int ast_level; - /* PJSIP doesn't provide much in the way of source info */ - const char * log_source = "pjsip"; - int log_line = 0; - const char *log_func = ""; - int mod_level; - - /* Lower number indicates higher importance */ - switch (level) { - case 0: /* level zero indicates fatal error, according to docs */ - case 1: /* 1 seems to be used for errors */ - ast_level = __LOG_ERROR; - break; - case 2: /* 2 seems to be used for warnings and errors */ - ast_level = __LOG_WARNING; - break; - default: - ast_level = __LOG_DEBUG; - - /* For levels 3 and up, obey the debug level for res_pjsip */ - mod_level = ast_opt_dbg_module ? - ast_debug_get_by_module("res_pjsip") : 0; - if (option_debug < level && mod_level < level) { - return; - } - break; - } - - /* PJSIP uses indention to indicate function call depth. We'll prepend - * log statements with a tab so they'll have a better shot at lining - * up */ - ast_log(ast_level, log_source, log_line, log_func, "\t%s\n", data); -} - -static int load_module(void) -{ - pj_init(); - - decor_orig = pj_log_get_decor(); - log_cb_orig = pj_log_get_log_func(); - - ast_debug(3, "Forwarding PJSIP logger to Asterisk logger\n"); - /* SENDER prepends the source to the log message. This could be a - * filename, object reference, or simply a string - * - * INDENT is assumed to be on by most log statements in PJSIP itself. - */ - pj_log_set_decor(PJ_LOG_HAS_SENDER | PJ_LOG_HAS_INDENT); - pj_log_set_log_func(log_cb); - - return AST_MODULE_LOAD_SUCCESS; -} - -static int unload_module(void) -{ - pj_log_set_log_func(log_cb_orig); - pj_log_set_decor(decor_orig); - - pj_shutdown(); - - return 0; -} - -/* While we don't really export global symbols, we want to load before other - * modules that do */ -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJSIP Log Forwarder", - .support_level = AST_MODULE_SUPPORT_CORE, - .load = load_module, - .unload = unload_module, - .load_pri = AST_MODPRI_CHANNEL_DEPEND - 6, - ); diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c index 8585b19194..3436db4b61 100644 --- a/res/res_pjsip_outbound_registration.c +++ b/res/res_pjsip_outbound_registration.c @@ -1070,10 +1070,26 @@ static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, vo ast_log(LOG_ERROR, "No server URI specified on outbound registration '%s'", ast_sorcery_object_get_id(applied)); return -1; + } else if (ast_sip_validate_uri_length(applied->server_uri)) { + ast_log(LOG_ERROR, "Server URI or hostname length exceeds pjpropject limit '%s'\n", + ast_sorcery_object_get_id(applied)); + return -1; } else if (ast_strlen_zero(applied->client_uri)) { ast_log(LOG_ERROR, "No client URI specified on outbound registration '%s'\n", ast_sorcery_object_get_id(applied)); return -1; + } else if (ast_sip_validate_uri_length(applied->client_uri)) { + ast_log(LOG_ERROR, "Client URI or hostname length exceeds pjpropject limit '%s'\n", + ast_sorcery_object_get_id(applied)); + return -1; + } else if (applied->line && ast_strlen_zero(applied->endpoint)) { + ast_log(LOG_ERROR, "Line support has been enabled on outbound registration '%s' without providing an endpoint\n", + ast_sorcery_object_get_id(applied)); + return -1; + } else if (!ast_strlen_zero(applied->endpoint) && !applied->line) { + ast_log(LOG_ERROR, "An endpoint has been specified on outbound registration '%s' without enabling line support\n", + ast_sorcery_object_get_id(applied)); + return -1; } if (state && can_reuse_registration(state->registration, applied)) { diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c index 595f83463e..a4a75bb7e5 100644 --- a/res/res_pjsip_registrar.c +++ b/res/res_pjsip_registrar.c @@ -18,6 +18,7 @@ /*** MODULEINFO pjproject + res_pjproject res_pjsip core ***/ @@ -32,6 +33,7 @@ #include "asterisk/test.h" #include "asterisk/taskprocessor.h" #include "asterisk/manager.h" +#include "asterisk/res_pjproject.h" #include "res_pjsip/include/res_pjsip_private.h" /*** DOCUMENTATION @@ -51,6 +53,9 @@ ***/ +static int pj_max_hostname = PJ_MAX_HOSTNAME; +static int pjsip_max_url_size = PJSIP_MAX_URL_SIZE; + /*! \brief Internal function which returns the expiration time for a contact */ static int registrar_get_expiration(const struct ast_sip_aor *aor, const pjsip_contact_hdr *contact, const pjsip_rx_data *rdata) { @@ -85,7 +90,7 @@ struct registrar_contact_details { /*! \brief Pool used for parsing URI */ pj_pool_t *pool; /*! \brief URI being looked for */ - pjsip_uri *uri; + pjsip_sip_uri *uri; }; /*! \brief Callback function for finding a contact */ @@ -113,6 +118,7 @@ static int registrar_validate_contacts(const pjsip_rx_data *rdata, struct ao2_co while ((contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next))) { int expiration = registrar_get_expiration(aor, contact, rdata); RAII_VAR(struct ast_sip_contact *, existing, NULL, ao2_cleanup); + char contact_uri[pjsip_max_url_size]; if (contact->star) { /* The expiration MUST be 0 when a '*' contact is used and there must be no other contact */ @@ -134,6 +140,19 @@ static int registrar_validate_contacts(const pjsip_rx_data *rdata, struct ao2_co details.uri = pjsip_uri_get_uri(contact->uri); + /* pjsip_uri_print returns -1 if there's not enough room in the buffer */ + if (pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)) < 0) { + /* If the total length of the uri is greater than pjproject can handle, go no further */ + pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); + return -1; + } + + if (details.uri->host.slen >= pj_max_hostname) { + /* If the length of the hostname is greater than pjproject can handle, go no further */ + pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); + return -1; + } + /* Determine if this is an add, update, or delete for policy enforcement purposes */ if (!(existing = ao2_callback(contacts, 0, registrar_find_contact, &details))) { if (expiration) { @@ -471,7 +490,7 @@ static int rx_task(void *data) /* Iterate each provided Contact header and add, update, or delete */ while ((contact_hdr = pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr ? contact_hdr->next : NULL))) { int expiration; - char contact_uri[PJSIP_MAX_URL_SIZE]; + char contact_uri[pjsip_max_url_size]; RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup); if (contact_hdr->star) { @@ -788,6 +807,12 @@ static int load_module(void) { const pj_str_t STR_REGISTER = { "REGISTER", 8 }; + CHECK_PJPROJECT_MODULE_LOADED(); + + ast_pjproject_get_buildopt("PJ_MAX_HOSTNAME", "%d", &pj_max_hostname); + /* As of pjproject 2.4.5, PJSIP_MAX_URL_SIZE isn't exposed yet but we try anyway. */ + ast_pjproject_get_buildopt("PJSIP_MAX_URL_SIZE", "%d", &pjsip_max_url_size); + CHECK_PJSIP_MODULE_LOADED(); if (!(serializers = ao2_container_alloc(