[Pkg-voip-commits] [asterisk] 01/01: Patches ASTERISK-20658 and AST-2013-007

tzafrir at debian.org tzafrir at debian.org
Tue Dec 17 16:03:49 UTC 2013


This is an automated email from the git hooks/post-receive script.

tzafrir pushed a commit to branch wheezy
in repository asterisk.

commit f532a9ba2f544c122a7b3e48371a4a3aef59b8b2
Author: Tzafrir Cohen <tzafrir at debian.org>
Date:   Tue Dec 17 17:52:04 2013 +0200

    Patches ASTERISK-20658 and AST-2013-007
    
    * Add security fix patch AST-2013-007
    * In order to simplify applying it cleanly on func_realtime I ran into
      ASTERISK-20658 which may be worth applying as well.
---
 debian/changelog              |   2 +
 debian/patches/AST-2013-007   | 884 ++++++++++++++++++++++++++++++++++++++++++
 debian/patches/ASTERISK-20658 |  86 ++++
 debian/patches/series         |   2 +
 4 files changed, 974 insertions(+)

diff --git a/debian/changelog b/debian/changelog
index 612656f..9d16a6c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,6 +1,8 @@
 asterisk (1:1.8.13.1~dfsg-3+deb7u2) UNRELEASED; urgency=high
 
   * Patch AST-2013-006: fixes a buffer overflow in app_sms.
+  * Patch ASTERISK-20658: fixes potential crash with asterisk-realtime
+  * Patch AST-2013-007: guards access to code execution from remote interfaces
 
  -- Tzafrir Cohen <tzafrir at debian.org>  Tue, 17 Dec 2013 02:05:19 +0200
 
diff --git a/debian/patches/AST-2013-007 b/debian/patches/AST-2013-007
new file mode 100644
index 0000000..3137df0
--- /dev/null
+++ b/debian/patches/AST-2013-007
@@ -0,0 +1,884 @@
+From: "David M. Lee" <dlee at digium.com>
+Date: Mon, 16 Dec 2013 16:36:52 +0000
+Bug: https://issues.asterisk.org/jira/browse/ASTERISK-22905
+Subject: Inhibit execution of privilege escalating functions
+Origin: http://svnview.digium.com/svn/asterisk?view=rev&rev=403913
+
+This patch allows individual dialplan functions to be marked as
+'dangerous', to inhibit their execution from external sources.
+
+A 'dangerous' function is one which results in a privilege escalation.
+For example, if one were to read the channel variable SHELL(rm -rf /)
+Bad Things(TM) could happen; even if the external source has only read
+permissions.
+
+Execution from external sources may be enabled by setting
+'live_dangerously' to 'yes' in the [options] section of asterisk.conf.
+Although doing so is not recommended.
+
+Review: http://reviewboard.digium.internal/r/432/
+
+---
+ README-SERIOUSLY.bestpractices.txt |   24 ++++
+ UPGRADE.txt                        |    8 ++
+ configs/asterisk.conf.sample       |    6 +
+ funcs/func_db.c                    |   20 ++-
+ funcs/func_env.c                   |   28 +++-
+ funcs/func_lock.c                  |   21 ++-
+ funcs/func_realtime.c              |   62 ++++++---
+ funcs/func_shell.c                 |   18 ++-
+ include/asterisk/pbx.h             |   54 ++++++++
+ main/asterisk.c                    |    5 +
+ main/pbx.c                         |  254 +++++++++++++++++++++++++++++++++++-
+ main/tcptls.c                      |   11 ++
+ 12 files changed, 473 insertions(+), 38 deletions(-)
+
+diff --git a/README-SERIOUSLY.bestpractices.txt b/README-SERIOUSLY.bestpractices.txt
+index b470fd6..281d0d3 100644
+--- a/README-SERIOUSLY.bestpractices.txt
++++ b/README-SERIOUSLY.bestpractices.txt
+@@ -26,6 +26,9 @@ Sections
+ * Manager Class Authorizations:
+         Recognizing potential issues with certain classes of authorization
+ 
++* Avoid Privilege Escalations:
++        Disable the ability to execute functions that may escalate privileges
++
+ ----------------
+ Additional Links
+ ----------------
+@@ -344,3 +347,24 @@ same as the class authorization "system".  Good system configuration, such as
+ not running Asterisk as root, can prevent serious problems from arising when
+ allowing external connections to originate calls into Asterisk.
+ 
++===========================
++Avoid Privilege Escalations
++===========================
++
++External control protocols, such as Manager, often have the ability to get and
++set channel variables; which allows the execution of dialplan functions.
++
++Dialplan functions within Asterisk are incredibly powerful, which is wonderful
++for building applications using Asterisk. But during the read or write
++execution, certain diaplan functions do much more. For example, reading the
++SHELL() function can execute arbitrary commands on the system Asterisk is
++running on. Writing to the FILE() function can change any file that Asterisk has
++write access to.
++
++When these functions are executed from an external protocol, that execution
++could result in a privilege escalation. Asterisk can inhibit the execution of
++these functions, if live_dangerously in the [options] section of asterisk.conf
++is set to no.
++
++For backwards compatibility, live_dangerously defaults to yes, and must be
++explicitly set to no to enable this privilege escalation protection.
+diff --git a/UPGRADE.txt b/UPGRADE.txt
+index c28be5a..408a3fa 100644
+--- a/UPGRADE.txt
++++ b/UPGRADE.txt
+@@ -27,6 +27,15 @@ from 1.8.23.0 to 1.8.24.0:
+ ===
+ ===========================================================
+ 
++from 1.8.13.1~dfsg-3+deb7u1 to 1.8.13.1~dfsg-3+deb7u2 (backported from 1.8.24.1):
++* Certain dialplan functions have been marked as 'dangerous', and may only be
++  executed from the dialplan. Execution from extenal sources (AMI's GetVar and
++  SetVar actions; etc.) may be inhibited by setting live_dangerously in the
++  [options] section of asterisk.conf to no. SHELL(), channel locking, and direct
++  file read/write functions are marked as dangerous. DB_DELETE() and
++  REALTIME_DESTROY() are marked as dangerous for reads, but can now safely
++  accept writes (which ignore the provided value).
++
+ From 1.8.11 to 1.8.12:
+ * In AEL dialplans, the "h" extension will now be inherited from prior
+   calling contexts, just as it had in 1.4.  If you have created an AEL
+diff --git a/configs/asterisk.conf.sample b/configs/asterisk.conf.sample
+index 618ecea..5fe70e1 100644
+--- a/configs/asterisk.conf.sample
++++ b/configs/asterisk.conf.sample
+@@ -73,6 +73,12 @@ documentation_language = en_US	; Set the language you want documentation
+ ;lockconfdir = no		; Protect the directory containing the
+ 				; configuration files (/etc/asterisk) with a
+ 				; lock.
++;live_dangerously = no		; Enable the execution of 'dangerous' dialplan
++				; functions from external sources (AMI,
++				; etc.) These functions (such as SHELL) are
++				; considered dangerous because they can allow
++				; privilege escalation.
++				; Default yes, for backward compatability.
+ 
+ ; Changing the following lines may compromise your security.
+ ;[files]
+diff --git a/funcs/func_db.c b/funcs/func_db.c
+index 8bd4d58..282a962 100644
+--- a/funcs/func_db.c
++++ b/funcs/func_db.c
+@@ -97,6 +97,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+ 			<para>This function will retrieve a value from the Asterisk database
+ 			and then remove that key from the database. <variable>DB_RESULT</variable>
+ 			will be set to the key's value if it exists.</para>
++			<note>
++				<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
++				is set to <literal>no</literal>, this function can only be read from the
++				dialplan, and not directly from external protocols. It can, however, be
++				executed as a write operation (<literal>DB_DELETE(family, key)=ignored</literal>)</para>
++			</note>
+ 		</description>
+ 		<see-also>
+ 			<ref type="application">DBdel</ref>
+@@ -243,10 +249,22 @@ static int function_db_delete(struct ast_channel *chan, const char *cmd,
+ 	return 0;
+ }
+ 
++/*!
++ * \brief Wrapper to execute DB_DELETE from a write operation. Allows execution
++ * even if live_dangerously is disabled.
++ */
++static int function_db_delete_write(struct ast_channel *chan, const char *cmd, char *parse,
++	const char *value)
++{
++	/* Throwaway to hold the result from the read */
++	char buf[128];
++	return function_db_delete(chan, cmd, parse, buf, sizeof(buf));
++}
+ 
+ static struct ast_custom_function db_delete_function = {
+ 	.name = "DB_DELETE",
+ 	.read = function_db_delete,
++	.write = function_db_delete_write,
+ };
+ 
+ static int unload_module(void)
+@@ -266,7 +284,7 @@ static int load_module(void)
+ 
+ 	res |= ast_custom_function_register(&db_function);
+ 	res |= ast_custom_function_register(&db_exists_function);
+-	res |= ast_custom_function_register(&db_delete_function);
++	res |= ast_custom_function_register_escalating(&db_delete_function, AST_CFE_READ);
+ 
+ 	return res;
+ }
+diff --git a/funcs/func_env.c b/funcs/func_env.c
+index cbc80e8..7fc1823 100644
+--- a/funcs/func_env.c
++++ b/funcs/func_env.c
+@@ -71,6 +71,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+ 			<parameter name="filename" required="true" />
+ 		</syntax>
+ 		<description>
++			<note>
++				<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
++				is set to <literal>no</literal>, this function can only be executed from the
++				dialplan, and not directly from external protocols.</para>
++			</note>
+ 		</description>
+ 	</function>
+ 	<function name="FILE" language="en_US">
+@@ -167,6 +172,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+ 			<para>    Set(FILE(/tmp/foo.txt,-1,,l)=bar)</para>
+ 			<para>    ; Append "bar" to the file with a newline</para>
+ 			<para>    Set(FILE(/tmp/foo.txt,,,al)=bar)</para>
++			<note>
++				<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
++				is set to <literal>no</literal>, this function can only be executed from the
++				dialplan, and not directly from external protocols.</para>
++			</note>
+ 		</description>
+ 		<see-also>
+ 			<ref type="function">FILE_COUNT_LINE</ref>
+@@ -197,6 +207,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+ 		</syntax>
+ 		<description>
+ 			<para>Returns the number of lines, or <literal>-1</literal> on error.</para>
++			<note>
++				<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
++				is set to <literal>no</literal>, this function can only be executed from the
++				dialplan, and not directly from external protocols.</para>
++			</note>
+ 		</description>
+ 		<see-also>
+ 			<ref type="function">FILE</ref>
+@@ -216,6 +231,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+ 			<para>'d' - DOS "\r\n" format</para>
+ 			<para>'m' - Macintosh "\r" format</para>
+ 			<para>'x' - Cannot be determined</para>
++			<note>
++				<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
++				is set to <literal>no</literal>, this function can only be executed from the
++				dialplan, and not directly from external protocols.</para>
++			</note>
+ 		</description>
+ 		<see-also>
+ 			<ref type="function">FILE</ref>
+@@ -1258,10 +1278,10 @@ static int load_module(void)
+ 	int res = 0;
+ 
+ 	res |= ast_custom_function_register(&env_function);
+-	res |= ast_custom_function_register(&stat_function);
+-	res |= ast_custom_function_register(&file_function);
+-	res |= ast_custom_function_register(&file_count_line_function);
+-	res |= ast_custom_function_register(&file_format_function);
++	res |= ast_custom_function_register_escalating(&stat_function, AST_CFE_READ);
++	res |= ast_custom_function_register_escalating(&file_function, AST_CFE_BOTH);
++	res |= ast_custom_function_register_escalating(&file_count_line_function, AST_CFE_READ);
++	res |= ast_custom_function_register_escalating(&file_format_function, AST_CFE_READ);
+ 
+ 	return res;
+ }
+diff --git a/funcs/func_lock.c b/funcs/func_lock.c
+index 92d5776..40deb20 100644
+--- a/funcs/func_lock.c
++++ b/funcs/func_lock.c
+@@ -59,6 +59,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+ 			Returns <literal>1</literal> if the lock was obtained or <literal>0</literal> on error.</para>
+ 			<note><para>To avoid the possibility of a deadlock, LOCK will only attempt to
+ 			obtain the lock for 3 seconds if the channel already has another lock.</para></note>
++			<note>
++				<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
++				is set to <literal>no</literal>, this function can only be executed from the
++				dialplan, and not directly from external protocols.</para>
++			</note>
+ 		</description>
+ 	</function>
+ 	<function name="TRYLOCK" language="en_US">
+@@ -72,6 +77,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+ 			<para>Attempts to grab a named lock exclusively, and prevents other channels
+ 			from obtaining the same lock.  Returns <literal>1</literal> if the lock was 
+ 			available or <literal>0</literal> otherwise.</para>
++			<note>
++				<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
++				is set to <literal>no</literal>, this function can only be executed from the
++				dialplan, and not directly from external protocols.</para>
++			</note>
+ 		</description>
+ 	</function>
+ 	<function name="UNLOCK" language="en_US">
+@@ -86,6 +96,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+ 			had a lock or <literal>0</literal> otherwise.</para>
+ 			<note><para>It is generally unnecessary to unlock in a hangup routine, as any locks 
+ 			held are automatically freed when the channel is destroyed.</para></note>
++			<note>
++				<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
++				is set to <literal>no</literal>, this function can only be executed from the
++				dialplan, and not directly from external protocols.</para>
++			</note>
+ 		</description>
+ 	</function>
+  ***/
+@@ -502,9 +517,9 @@ static int unload_module(void)
+ 
+ static int load_module(void)
+ {
+-	int res = ast_custom_function_register(&lock_function);
+-	res |= ast_custom_function_register(&trylock_function);
+-	res |= ast_custom_function_register(&unlock_function);
++	int res = ast_custom_function_register_escalating(&lock_function, AST_CFE_READ);
++	res |= ast_custom_function_register_escalating(&trylock_function, AST_CFE_READ);
++	res |= ast_custom_function_register_escalating(&unlock_function, AST_CFE_READ);
+ 	ast_pthread_create_background(&broker_tid, NULL, lock_broker, NULL);
+ 	return res;
+ }
+index 6d7338c..8e0d2c6 100644
+--- a/funcs/func_realtime.c
++++ b/funcs/func_realtime.c
+@@ -115,6 +115,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+ 		<description>
+ 			<para>This function acts in the same way as REALTIME(....) does, except that
+ 			it destroys the matched record in the RT engine.</para>
++			<note>
++				<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
++				is set to <literal>no</literal>, this function can only be read from the
++				dialplan, and not directly from external protocols. It can, however, be
++				executed as a write operation (<literal>REALTIME_DESTROY(family, fieldmatch)=ignored</literal>)</para>
++			</note>
+ 		</description>
+ 		<see-also>
+ 			<ref type="function">REALTIME</ref>
+@@ -439,28 +445,32 @@ static int function_realtime_readdestroy(struct ast_channel *chan, const char *c
+ 		return -1;
+ 	}
+ 
+-	resultslen = 0;
+-	n = 0;
+-	for (var = head; var; n++, var = var->next)
+-		resultslen += strlen(var->name) + strlen(var->value);
+-	/* add space for delimiters and final '\0' */
+-	resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
+-
+-	if (resultslen > len) {
+-		/* Unfortunately this does mean that we cannot destroy the row
+-		 * anymore. But OTOH, we're not destroying someones data without
+-		 * giving him the chance to look at it. */
+-		ast_log(LOG_WARNING, "Failed to fetch/destroy. Realtime data is too large: need %zu, have %zu.\n", resultslen, len);
+-		return -1;
+-	}
++	if (len > 0) {
++		resultslen = 0;
++		n = 0;
++		for (var = head; var; n++, var = var->next) {
++			resultslen += strlen(var->name) + strlen(var->value);
++		}
++		/* add space for delimiters and final '\0' */
++		resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
++
++		if (resultslen > len) {
++			/* Unfortunately this does mean that we cannot destroy
++			 * the row anymore. But OTOH, we're not destroying
++			 * someones data without giving him the chance to look
++			 * at it. */
++			ast_log(LOG_WARNING, "Failed to fetch/destroy. Realtime data is too large: need %zu, have %zu.\n", resultslen, len);
++			return -1;
++		}
+ 
+-	/* len is going to be sensible, so we don't need to check for stack
+-	 * overflows here. */
+-	out = ast_str_alloca(resultslen);
+-	for (var = head; var; var = var->next) {
+-		ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
++		/* len is going to be sensible, so we don't need to check for
++		 * stack overflows here. */
++		out = ast_str_alloca(resultslen);
++		for (var = head; var; var = var->next) {
++			ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
++		}
++		ast_copy_string(buf, ast_str_buffer(out), len);
+ 	}
+-	ast_copy_string(buf, ast_str_buffer(out), len);
+ 
+ 	ast_destroy_realtime(args.family, args.fieldmatch, args.value, SENTINEL);
+ 	ast_variables_destroy(head);
+@@ -471,6 +481,15 @@ static int function_realtime_readdestroy(struct ast_channel *chan, const char *c
+ 	return 0;
+ }
+ 
++/*!
++ * \brief Wrapper to execute REALTIME_DESTROY from a write operation. Allows
++ * execution even if live_dangerously is disabled.
++ */
++static int function_realtime_writedestroy(struct ast_channel *chan, const char *cmd, char *data, const char *value)
++{
++	return function_realtime_readdestroy(chan, cmd, data, NULL, 0);
++}
++
+ static struct ast_custom_function realtime_function = {
+ 	.name = "REALTIME",
+ 	.read = function_realtime_read,
+@@ -496,6 +515,7 @@ static struct ast_custom_function realtime_store_function = {
+ static struct ast_custom_function realtime_destroy_function = {
+ 	.name = "REALTIME_DESTROY",
+ 	.read = function_realtime_readdestroy,
++	.write = function_realtime_writedestroy,
+ };
+ 
+ static int unload_module(void)
+@@ -514,7 +534,7 @@ static int load_module(void)
+ 	int res = 0;
+ 	res |= ast_custom_function_register(&realtime_function);
+ 	res |= ast_custom_function_register(&realtime_store_function);
+-	res |= ast_custom_function_register(&realtime_destroy_function);
++	res |= ast_custom_function_register_escalating(&realtime_destroy_function, AST_CFE_READ);
+ 	res |= ast_custom_function_register(&realtimefield_function);
+ 	res |= ast_custom_function_register(&realtimehash_function);
+ 	return res;
+diff --git a/funcs/func_shell.c b/funcs/func_shell.c
+index bad10b3..e403efc 100644
+--- a/funcs/func_shell.c
++++ b/funcs/func_shell.c
+@@ -88,12 +88,17 @@ static int shell_helper(struct ast_channel *chan, const char *cmd, char *data,
+ 		</syntax>
+ 		<description>
+ 			<para>Returns the value from a system command</para>
+-			<para>Example:  <literal>Set(foo=${SHELL(echo \bar\)})</literal></para>
+-			<note><para>When using the SHELL() dialplan function, your \SHELL\ is /bin/sh,
+-			which may differ as to the underlying shell, depending upon your production
+-			platform.  Also keep in mind that if you are using a common path, you should
+-			be mindful of race conditions that could result from two calls running
+-			SHELL() simultaneously.</para></note>
++			<para>Example:  <literal>Set(foo=${SHELL(echo bar)})</literal></para>
++			<note>
++				<para>The command supplied to this function will be executed by the
++				system's shell, typically specified in the SHELL environment variable. There
++				are many different system shells available with somewhat different behaviors,
++				so the output generated by this function may vary between platforms.</para>
++
++				<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
++				is set to <literal>no</literal>, this function can only be executed from the
++				dialplan, and not directly from external protocols.</para>
++			</note>
+ 		</description>
+  
+ 	</function>
+@@ -109,7 +115,7 @@ static int unload_module(void)
+ 
+ static int load_module(void)
+ {
+-	return ast_custom_function_register(&shell_function);
++	return ast_custom_function_register_escalating(&shell_function, AST_CFE_READ);
+ }
+ 
+ AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Returns the output of a shell command");
+diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h
+index 52710e8..2531413 100644
+--- a/include/asterisk/pbx.h
++++ b/include/asterisk/pbx.h
+@@ -1149,16 +1149,44 @@ struct ast_custom_function* ast_custom_function_find(const char *name);
+ int ast_custom_function_unregister(struct ast_custom_function *acf);
+ 
+ /*!
++ * \brief Description of the ways in which a function may escalate privileges.
++ */
++enum ast_custom_function_escalation {
++	AST_CFE_NONE,
++	AST_CFE_READ,
++	AST_CFE_WRITE,
++	AST_CFE_BOTH,
++};
++
++/*!
+  * \brief Register a custom function
+  */
+ #define ast_custom_function_register(acf) __ast_custom_function_register(acf, ast_module_info->self)
+ 
+ /*!
++ * \brief Register a custom function which requires escalated privileges.
++ *
++ * Examples would be SHELL() (for which a read needs permission to execute
++ * arbitrary code) or FILE() (for which write needs permission to change files
++ * on the filesystem).
++ */
++#define ast_custom_function_register_escalating(acf, escalation) __ast_custom_function_register_escalating(acf, escalation, ast_module_info->self)
++
++/*!
+  * \brief Register a custom function
+  */
+ int __ast_custom_function_register(struct ast_custom_function *acf, struct ast_module *mod);
+ 
+ /*!
++ * \brief Register a custom function which requires escalated privileges.
++ *
++ * Examples would be SHELL() (for which a read needs permission to execute
++ * arbitrary code) or FILE() (for which write needs permission to change files
++ * on the filesystem).
++ */
++int __ast_custom_function_register_escalating(struct ast_custom_function *acf, enum ast_custom_function_escalation escalation, struct ast_module *mod);
++
++/*!
+  * \brief Retrieve the number of active calls
+  */
+ int ast_active_calls(void);
+@@ -1271,6 +1299,32 @@ unsigned int ast_hashtab_hash_contexts(const void *obj);
+  */
+ char *ast_complete_applications(const char *line, const char *word, int state);
+ 
++/*!
++ * \brief Enable/disable the execution of 'dangerous' functions from external
++ * protocols (AMI, etc.).
++ *
++ * These dialplan functions (such as \c SHELL) provide an opportunity for
++ * privilege escalation. They are okay to invoke from the dialplan, but external
++ * protocols with permission controls should not normally invoke them.
++ *
++ * This function can globally enable/disable the execution of dangerous
++ * functions from external protocols.
++ *
++ * \param new_live_dangerously If true, enable the execution of escalating
++ * functions from external protocols.
++ */
++void pbx_live_dangerously(int new_live_dangerously);
++
++/*!
++ * \brief Inhibit (in the current thread) the execution of dialplan functions
++ * which cause privilege escalations. If pbx_live_dangerously() has been
++ * called, this function has no effect.
++ *
++ * \return 0 if successfuly marked current thread.
++ * \return Non-zero if marking current thread failed.
++ */
++int ast_thread_inhibit_escalations(void);
++
+ #if defined(__cplusplus) || defined(c_plusplus)
+ }
+ #endif
+diff --git a/main/asterisk.c b/main/asterisk.c
+index fe4088b..c563b70 100644
+--- a/main/asterisk.c
++++ b/main/asterisk.c
+@@ -3036,6 +3036,8 @@ static void ast_readconfig(void)
+ 		unsigned int dbdir:1;
+ 		unsigned int keydir:1;
+ 	} found = { 0, 0 };
++	/* Default to true for backward compatibility */
++	int live_dangerously = 1;
+ 
+ 	if (ast_opt_override_config) {
+ 		cfg = ast_config_load2(ast_config_AST_CONFIG_FILE, "" /* core, can't reload */, config_flags);
+@@ -3245,8 +3247,11 @@ static void ast_readconfig(void)
+ 			ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_HIDE_CONSOLE_CONNECT);
+ 		} else if (!strcasecmp(v->name, "lockconfdir")) {
+ 			ast_set2_flag(&ast_options, ast_true(v->value),	AST_OPT_FLAG_LOCK_CONFIG_DIR);
++		} else if (!strcasecmp(v->name, "live_dangerously")) {
++			live_dangerously = ast_true(v->value);
+ 		}
+ 	}
++	pbx_live_dangerously(live_dangerously);
+ 	for (v = ast_variable_browse(cfg, "compat"); v; v = v->next) {
+ 		float version;
+ 		if (sscanf(v->value, "%30f", &version) != 1) {
+diff --git a/main/pbx.c b/main/pbx.c
+index 996ac25..e370ae7 100644
+--- a/main/pbx.c
++++ b/main/pbx.c
+@@ -821,6 +821,17 @@ static struct ast_taskprocessor *device_state_tps;
+ 
+ AST_THREADSTORAGE(switch_data);
+ AST_THREADSTORAGE(extensionstate_buf);
++/*!
++ * \brief A thread local indicating whether the current thread can run
++ * 'dangerous' dialplan functions.
++ */
++AST_THREADSTORAGE(thread_inhibit_escalations_tl);
++
++/*!
++ * \brief Set to true (non-zero) to globally allow all dangerous dialplan
++ * functions to run.
++ */
++static int live_dangerously;
+ 
+ /*!
+    \brief ast_exten: An extension
+@@ -1175,6 +1186,19 @@ static int totalcalls;
+ 
+ static AST_RWLIST_HEAD_STATIC(acf_root, ast_custom_function);
+ 
++/*!
++ * \brief Extra information for an \ref ast_custom_function holding privilege
++ * escalation information. Kept in a separate structure for ABI compatibility.
++ */
++struct ast_custom_escalating_function {
++	AST_RWLIST_ENTRY(ast_custom_escalating_function) list;
++	const struct ast_custom_function *acf;
++	unsigned int read_escalates:1;
++	unsigned int write_escalates:1;
++};
++
++static AST_RWLIST_HEAD_STATIC(escalation_root, ast_custom_escalating_function);
++
+ /*! \brief Declaration of builtin applications */
+ static struct pbx_builtin {
+ 	char name[AST_MAX_APP];
+@@ -3748,6 +3772,7 @@ struct ast_custom_function *ast_custom_function_find(const char *name)
+ int ast_custom_function_unregister(struct ast_custom_function *acf)
+ {
+ 	struct ast_custom_function *cur;
++	struct ast_custom_escalating_function *cur_escalation;
+ 
+ 	if (!acf) {
+ 		return -1;
+@@ -3764,9 +3789,64 @@ int ast_custom_function_unregister(struct ast_custom_function *acf)
+ 	}
+ 	AST_RWLIST_UNLOCK(&acf_root);
+ 
++	/* Remove from the escalation list */
++	AST_RWLIST_WRLOCK(&escalation_root);
++	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&escalation_root, cur_escalation, list) {
++		if (cur_escalation->acf == acf) {
++			AST_RWLIST_REMOVE_CURRENT(list);
++			break;
++		}
++	}
++	AST_RWLIST_TRAVERSE_SAFE_END;
++	AST_RWLIST_UNLOCK(&escalation_root);
++
+ 	return cur ? 0 : -1;
+ }
+ 
++/*!
++ * \brief Returns true if given custom function escalates privileges on read.
++ *
++ * \param acf Custom function to query.
++ * \return True (non-zero) if reads escalate privileges.
++ * \return False (zero) if reads just read.
++ */
++static int read_escalates(const struct ast_custom_function *acf) {
++	int res = 0;
++	struct ast_custom_escalating_function *cur_escalation;
++
++	AST_RWLIST_RDLOCK(&escalation_root);
++	AST_RWLIST_TRAVERSE(&escalation_root, cur_escalation, list) {
++		if (cur_escalation->acf == acf) {
++			res = cur_escalation->read_escalates;
++			break;
++		}
++	}
++	AST_RWLIST_UNLOCK(&escalation_root);
++	return res;
++}
++
++/*!
++ * \brief Returns true if given custom function escalates privileges on write.
++ *
++ * \param acf Custom function to query.
++ * \return True (non-zero) if writes escalate privileges.
++ * \return False (zero) if writes just write.
++ */
++static int write_escalates(const struct ast_custom_function *acf) {
++	int res = 0;
++	struct ast_custom_escalating_function *cur_escalation;
++
++	AST_RWLIST_RDLOCK(&escalation_root);
++	AST_RWLIST_TRAVERSE(&escalation_root, cur_escalation, list) {
++		if (cur_escalation->acf == acf) {
++			res = cur_escalation->write_escalates;
++			break;
++		}
++	}
++	AST_RWLIST_UNLOCK(&escalation_root);
++	return res;
++}
++
+ /*! \internal
+  *  \brief Retrieve the XML documentation of a specified ast_custom_function,
+  *         and populate ast_custom_function string fields.
+@@ -3868,6 +3948,50 @@ int __ast_custom_function_register(struct ast_custom_function *acf, struct ast_m
+ 	return 0;
+ }
+ 
++int __ast_custom_function_register_escalating(struct ast_custom_function *acf, enum ast_custom_function_escalation escalation, struct ast_module *mod)
++{
++	struct ast_custom_escalating_function *acf_escalation = NULL;
++	int res;
++
++	res = __ast_custom_function_register(acf, mod);
++	if (res != 0) {
++		return -1;
++	}
++
++	if (escalation == AST_CFE_NONE) {
++		/* No escalations; no need to do anything else */
++		return 0;
++	}
++
++	acf_escalation = ast_calloc(1, sizeof(*acf_escalation));
++	if (!acf_escalation) {
++		ast_custom_function_unregister(acf);
++		return -1;
++	}
++
++	acf_escalation->acf = acf;
++	switch (escalation) {
++	case AST_CFE_NONE:
++		break;
++	case AST_CFE_READ:
++		acf_escalation->read_escalates = 1;
++		break;
++	case AST_CFE_WRITE:
++		acf_escalation->write_escalates = 1;
++		break;
++	case AST_CFE_BOTH:
++		acf_escalation->read_escalates = 1;
++		acf_escalation->write_escalates = 1;
++		break;
++	}
++
++	AST_RWLIST_WRLOCK(&escalation_root);
++	AST_RWLIST_INSERT_TAIL(&escalation_root, acf_escalation, list);
++	AST_RWLIST_UNLOCK(&escalation_root);
++
++	return 0;
++}
++
+ /*! \brief return a pointer to the arguments of the function,
+  * and terminates the function name with '\\0'
+  */
+@@ -3889,6 +4013,124 @@ static char *func_args(char *function)
+ 	return args;
+ }
+ 
++void pbx_live_dangerously(int new_live_dangerously)
++{
++	if (new_live_dangerously && !live_dangerously) {
++		ast_log(LOG_WARNING, "Privilege escalation protection disabled!\n"
++			"See https://wiki.asterisk.org/wiki/x/1gKfAQ for more details.\n");
++	}
++
++	if (!new_live_dangerously && live_dangerously) {
++		ast_log(LOG_NOTICE, "Privilege escalation protection enabled.\n");
++	}
++	live_dangerously = new_live_dangerously;
++}
++
++int ast_thread_inhibit_escalations(void)
++{
++	int *thread_inhibit_escalations;
++
++	thread_inhibit_escalations = ast_threadstorage_get(
++		&thread_inhibit_escalations_tl, sizeof(*thread_inhibit_escalations));
++
++	if (thread_inhibit_escalations == NULL) {
++		ast_log(LOG_ERROR, "Error inhibiting privilege escalations for current thread\n");
++		return -1;
++	}
++
++	*thread_inhibit_escalations = 1;
++	return 0;
++}
++
++/*!
++ * \brief Indicates whether the current thread inhibits the execution of
++ * dangerous functions.
++ *
++ * \return True (non-zero) if dangerous function execution is inhibited.
++ * \return False (zero) if dangerous function execution is allowed.
++ */
++static int thread_inhibits_escalations(void)
++{
++	int *thread_inhibit_escalations;
++
++	thread_inhibit_escalations = ast_threadstorage_get(
++		&thread_inhibit_escalations_tl, sizeof(*thread_inhibit_escalations));
++
++	if (thread_inhibit_escalations == NULL) {
++		ast_log(LOG_ERROR, "Error checking thread's ability to run dangerous functions\n");
++		/* On error, assume that we are inhibiting */
++		return 1;
++	}
++
++	return *thread_inhibit_escalations;
++}
++
++/*!
++ * \brief Determines whether execution of a custom function's read function
++ * is allowed.
++ *
++ * \param acfptr Custom function to check
++ * \return True (non-zero) if reading is allowed.
++ * \return False (zero) if reading is not allowed.
++ */
++static int is_read_allowed(struct ast_custom_function *acfptr)
++{
++	if (!acfptr) {
++		return 1;
++	}
++
++	if (!read_escalates(acfptr)) {
++		return 1;
++	}
++
++	if (!thread_inhibits_escalations()) {
++		return 1;
++	}
++
++	if (live_dangerously) {
++		/* Global setting overrides the thread's preference */
++		ast_debug(2, "Reading %s from a dangerous context\n",
++			acfptr->name);
++		return 1;
++	}
++
++	/* We have no reason to allow this function to execute */
++	return 0;
++}
++
++/*!
++ * \brief Determines whether execution of a custom function's write function
++ * is allowed.
++ *
++ * \param acfptr Custom function to check
++ * \return True (non-zero) if writing is allowed.
++ * \return False (zero) if writing is not allowed.
++ */
++static int is_write_allowed(struct ast_custom_function *acfptr)
++{
++	if (!acfptr) {
++		return 1;
++	}
++
++	if (!write_escalates(acfptr)) {
++		return 1;
++	}
++
++	if (!thread_inhibits_escalations()) {
++		return 1;
++	}
++
++	if (live_dangerously) {
++		/* Global setting overrides the thread's preference */
++		ast_debug(2, "Writing %s from a dangerous context\n",
++			acfptr->name);
++		return 1;
++	}
++
++	/* We have no reason to allow this function to execute */
++	return 0;
++}
++
+ int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len)
+ {
+ 	char *copy = ast_strdupa(function);
+@@ -3901,6 +4143,8 @@ int ast_func_read(struct ast_channel *chan, const char *function, char *workspac
+ 		ast_log(LOG_ERROR, "Function %s not registered\n", copy);
+ 	} else if (!acfptr->read && !acfptr->read2) {
+ 		ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
++	} else if (!is_read_allowed(acfptr)) {
++		ast_log(LOG_ERROR, "Dangerous function %s read blocked\n", copy);
+ 	} else if (acfptr->read) {
+ 		if (acfptr->mod) {
+ 			u = __ast_module_user_add(acfptr->mod, chan);
+@@ -3938,6 +4182,8 @@ int ast_func_read2(struct ast_channel *chan, const char *function, struct ast_st
+ 		ast_log(LOG_ERROR, "Function %s not registered\n", copy);
+ 	} else if (!acfptr->read && !acfptr->read2) {
+ 		ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
++	} else if (!is_read_allowed(acfptr)) {
++		ast_log(LOG_ERROR, "Dangerous function %s read blocked\n", copy);
+ 	} else {
+ 		if (acfptr->mod) {
+ 			u = __ast_module_user_add(acfptr->mod, chan);
+@@ -3977,11 +4223,13 @@ int ast_func_write(struct ast_channel *chan, const char *function, const char *v
+ 	char *args = func_args(copy);
+ 	struct ast_custom_function *acfptr = ast_custom_function_find(copy);
+ 
+-	if (acfptr == NULL)
++	if (acfptr == NULL) {
+ 		ast_log(LOG_ERROR, "Function %s not registered\n", copy);
+-	else if (!acfptr->write)
++	} else if (!acfptr->write) {
+ 		ast_log(LOG_ERROR, "Function %s cannot be written to\n", copy);
+-	else {
++	} else if (!is_write_allowed(acfptr)) {
++		ast_log(LOG_ERROR, "Dangerous function %s write blocked\n", copy);
++	} else {
+ 		int res;
+ 		struct ast_module_user *u = NULL;
+ 		if (acfptr->mod)
+diff --git a/main/tcptls.c b/main/tcptls.c
+index c25f63f..32931b9 100644
+--- a/main/tcptls.c
++++ b/main/tcptls.c
+@@ -48,6 +48,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+ #include "asterisk/options.h"
+ #include "asterisk/manager.h"
+ #include "asterisk/astobj2.h"
++#include "asterisk/pbx.h"
+ 
+ /*! \brief
+  * replacement read/write functions for SSL support.
+@@ -161,6 +162,16 @@ static void *handle_tcptls_connection(void *data)
+ 	char err[256];
+ #endif
+ 
++	/* TCP/TLS connections are associated with external protocols, and
++	 * should not be allowed to execute 'dangerous' functions. This may
++	 * need to be pushed down into the individual protocol handlers, but
++	 * this seems like a good general policy.
++	 */
++	if (ast_thread_inhibit_escalations()) {
++		ast_log(LOG_ERROR, "Failed to inhibit privilege escalations; killing connection\n");
++		return NULL;
++	}
++
+ 	/*
+ 	* open a FILE * as appropriate.
+ 	*/
+-- 
+1.7.10.4
+
diff --git a/debian/patches/ASTERISK-20658 b/debian/patches/ASTERISK-20658
new file mode 100644
index 0000000..e0093a3
--- /dev/null
+++ b/debian/patches/ASTERISK-20658
@@ -0,0 +1,86 @@
+From: Matthew Jordan <mjordan at digium.com>
+Date: Wed, 2 Jan 2013 21:48:57 +0000
+Bug: https://issues.asterisk.org/jira/browse/ASTERISK-20658
+Origin: http://svnview.digium.com/svn/asterisk?view=rev&rev=378375
+Subject: Prevent crashes from occurring when reading from data sources with large values
+
+When reading configuration data from an Asterisk .conf file or when pulling
+data from an Asterisk RealTime backend, Asterisk was copying the data on the
+stack for manipulation. Unfortunately, it is possible to read configuration
+data or realtime data from some data source that provides a large blob of
+characters. This could potentially cause a crash via a stack overflow.
+
+This patch prevents large sets of data from being read from an ARA backend or
+from an Asterisk conf file.
+
+Reported by: wdoekes
+Tested by: wdoekes, mmichelson
+patches:
+ * issueA20658_dont_process_overlong_config_lines.patch uploaded by wdoekes (license 5674)
+ * issueA20658_func_realtime_limit.patch uploaded by wdoekes (license 5674)
+
+---
+ funcs/func_realtime.c |   17 +++++++++++++++++
+ main/config.c         |   11 +++++++++++
+ 2 files changed, 28 insertions(+)
+
+diff --git a/funcs/func_realtime.c b/funcs/func_realtime.c
+index a1b6d20..6d7338c 100644
+--- a/funcs/func_realtime.c
++++ b/funcs/func_realtime.c
+@@ -219,6 +219,13 @@ static int function_realtime_read(struct ast_channel *chan, const char *cmd, cha
+ 	/* add space for delimiters and final '\0' */
+ 	resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
+ 
++	if (resultslen > len) {
++		ast_log(LOG_WARNING, "Failed to fetch. Realtime data is too large: need %zu, have %zu.\n", resultslen, len);
++		return -1;
++	}
++
++	/* len is going to be sensible, so we don't need to check for stack
++	 * overflows here. */
+ 	out = ast_str_alloca(resultslen);
+ 	for (var = head; var; var = var->next)
+ 		ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
+@@ -439,6 +446,16 @@ static int function_realtime_readdestroy(struct ast_channel *chan, const char *c
+ 	/* add space for delimiters and final '\0' */
+ 	resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
+ 
++	if (resultslen > len) {
++		/* Unfortunately this does mean that we cannot destroy the row
++		 * anymore. But OTOH, we're not destroying someones data without
++		 * giving him the chance to look at it. */
++		ast_log(LOG_WARNING, "Failed to fetch/destroy. Realtime data is too large: need %zu, have %zu.\n", resultslen, len);
++		return -1;
++	}
++
++	/* len is going to be sensible, so we don't need to check for stack
++	 * overflows here. */
+ 	out = ast_str_alloca(resultslen);
+ 	for (var = head; var; var = var->next) {
+ 		ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
+diff --git a/main/config.c b/main/config.c
+index 4a71c1f..f90cfc1 100644
+--- a/main/config.c
++++ b/main/config.c
+@@ -1524,6 +1524,17 @@ static struct ast_config *config_text_file_load(const char *database, const char
+ 		while (!feof(f)) {
+ 			lineno++;
+ 			if (fgets(buf, sizeof(buf), f)) {
++				/* Skip lines that are too long */
++				if (strlen(buf) == sizeof(buf) - 1 && buf[sizeof(buf) - 1] != '\n') {
++					ast_log(LOG_WARNING, "Line %d too long, skipping. It begins with: %.32s...\n", lineno, buf);
++					while (fgets(buf, sizeof(buf), f)) {
++						if (strlen(buf) != sizeof(buf) - 1 || buf[sizeof(buf) - 1] == '\n') {
++							break;
++						}
++					}
++					continue;
++				}
++
+ 				if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && ast_str_strlen(lline_buffer)) {
+ 					CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
+ 					ast_str_reset(lline_buffer);        /* erase the lline buffer */
+-- 
+1.7.10.4
+
diff --git a/debian/patches/series b/debian/patches/series
index 3501811..2d21bde 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -37,3 +37,5 @@ fix_xmpp_19532
 AST-2013-004
 AST-2013-005
 AST-2013-006
+ASTERISK-20658
+AST-2013-007

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-voip/asterisk.git



More information about the Pkg-voip-commits mailing list