Bug#732355: asterisk: Two Asterisk security issues

Tzafrir Cohen tzafrir.cohen at xorcom.com
Fri Dec 20 13:14:00 UTC 2013


On Tue, Dec 17, 2013 at 06:17:09PM +0100, Moritz Muehlenhoff wrote:
> On Tue, Dec 17, 2013 at 05:55:14PM +0200, Tzafrir Cohen wrote:
> > On Tue, Dec 17, 2013 at 07:33:53AM +0100, Moritz Muehlenhoff wrote:
> > > Package: asterisk
> > > Severity: grave
> > > Tags: security
> > > 
> > > Hi,
> > > please see
> > > http://downloads.asterisk.org/pub/security/AST-2013-006.html and
> > > http://downloads.asterisk.org/pub/security/AST-2013-007.html
> > 
> > Looking at them. At first glance: both of them also affect 1.6.2 from
> > old-stable. AST-2013-007 introduces a new configuration item and we have
> > to see what the sane default for it should be.
> 
> I think we should follow upstream and keep live_dangerously activated
> We can add a note to the advisory what setting must be tweaked.

Attached are debdiffs for oldstable and stable uploads. I couldn't find
CVE entries.

I added an extra bug fix to help me patch the issue, for a bug that is
marginally a remote crash bug:
https://issues.asterisk.org/jira/browse/ASTERISK-20658
(Asterisk Realtime means getting some of Asterisk's configuration from a
database)


More on AST-2013-007:

(maybe shorten it a bit?)

Asterisk employs in its dialplan and varois other places a syntax for
varable expantion: ${VAR} expands the value of ${VAR}. Similarly there
are also some functions that use a similar syntax: ${RANDOM(5)} or 
${CUT(20-30-40,-,2)}. Some are more potent, however such as SHELL
(run a shell command and return the output).

The variables were primarily meant for the Asterisk dialplan, but may be
accessed through several other interfaces. For instance, the AMI
(Asterisk Manager Interface) provides a GetVar command. This will also
expand functions.

With the fix for AST-2013-007, a new knob was added in order to allow
the system adminitrator to disable expantion of "dangerous" functions
(such as SHELL()) from any interface which is not the dialplan. In
Stable and Oldstable this knob is disabled by default. To enable it add
the following line to the section '[options]' in
/etc/asterisk/asterisk.conf (and restart asterisk)

  live_dangerously = no

-- 
               Tzafrir Cohen
icq#16849755              jabber:tzafrir.cohen at xorcom.com
+972-50-7952406           mailto:tzafrir.cohen at xorcom.com
http://www.xorcom.com
-------------- next part --------------
diff -Nru asterisk-1.8.13.1~dfsg/debian/changelog asterisk-1.8.13.1~dfsg/debian/changelog
--- asterisk-1.8.13.1~dfsg/debian/changelog	2013-08-29 22:02:00.000000000 +0300
+++ asterisk-1.8.13.1~dfsg/debian/changelog	2013-12-19 13:16:41.000000000 +0200
@@ -1,3 +1,12 @@
+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
+    - but patch out the change in asterisk.conf.
+
+ -- Tzafrir Cohen <tzafrir at debian.org>  Tue, 17 Dec 2013 02:05:19 +0200
+
 asterisk (1:1.8.13.1~dfsg-3+deb7u1) stable-security; urgency=high
 
   * Patch AST-2013-004 (CVE-2013-5641): chan_sip: crash in ACK to SDP
diff -Nru asterisk-1.8.13.1~dfsg/debian/control asterisk-1.8.13.1~dfsg/debian/control
--- asterisk-1.8.13.1~dfsg/debian/control	2013-08-29 21:57:28.000000000 +0300
+++ asterisk-1.8.13.1~dfsg/debian/control	2013-12-19 13:15:43.000000000 +0200
@@ -21,6 +21,8 @@
  autotools-dev,
  autoconf,
  automake,
+ patch,
+ patchutils,
  libnewt-dev,
  libsqlite0-dev | libsqlite-dev,
  libsqlite3-dev,
diff -Nru asterisk-1.8.13.1~dfsg/debian/patches/AST-2013-006 asterisk-1.8.13.1~dfsg/debian/patches/AST-2013-006
--- asterisk-1.8.13.1~dfsg/debian/patches/AST-2013-006	1970-01-01 02:00:00.000000000 +0200
+++ asterisk-1.8.13.1~dfsg/debian/patches/AST-2013-006	2013-12-19 13:06:59.000000000 +0200
@@ -0,0 +1,38 @@
+Subject: app_sms: BufferOverflow when receiving odd length 16 bit message
+From: Scott Griepentrog <sgriepentrog at digium.com>
+Date: Mon, 16 Dec 2013 15:18:56 +0000
+Origin: http://svnview.digium.com/svn/asterisk?view=rev&rev=403853
+Bug: https://issues.asterisk.org/jira/browse/ASTERISK-22590
+
+This patch prevents an infinite loop overwriting memory when
+a message is received into the unpacksms16() function, where
+the length of the message is an odd number of bytes.
+
+---
+ apps/app_sms.c |    3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/apps/app_sms.c b/apps/app_sms.c
+index 08b90d1..75d399a 100644
+--- a/apps/app_sms.c
++++ b/apps/app_sms.c
+@@ -696,7 +696,7 @@ static void unpacksms16(unsigned char *i, unsigned char l, unsigned char *udh, i
+ 	}
+ 	while (l--) {
+ 		int v = *i++;
+-		if (l--) {
++		if (l && l--) {
+ 			v = (v << 8) + *i++;
+ 		}
+ 		*o++ = v;
+@@ -714,6 +714,7 @@ static int unpacksms(unsigned char dcs, unsigned char *i, unsigned char *udh, in
+ 	} else if (is8bit(dcs)) {
+ 		unpacksms8(i, l, udh, udhl, ud, udl, udhi);
+ 	} else {
++		l += l % 2;
+ 		unpacksms16(i, l, udh, udhl, ud, udl, udhi);
+ 	}
+ 	return l + 1;
+-- 
+1.7.10.4
+
diff -Nru asterisk-1.8.13.1~dfsg/debian/patches/AST-2013-007 asterisk-1.8.13.1~dfsg/debian/patches/AST-2013-007
--- asterisk-1.8.13.1~dfsg/debian/patches/AST-2013-007	1970-01-01 02:00:00.000000000 +0200
+++ asterisk-1.8.13.1~dfsg/debian/patches/AST-2013-007	2013-12-20 14:37:04.000000000 +0200
@@ -0,0 +1,857 @@
+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(-)
+
+--- 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"
+ 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.
+--- a/UPGRADE.txt
++++ b/UPGRADE.txt
+@@ -18,6 +18,15 @@
+ ===
+ ===========================================================
+ 
++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
+--- a/configs/asterisk.conf.sample
++++ b/configs/asterisk.conf.sample
+@@ -73,6 +73,12 @@ documentation_language = en_US	; Set the
+ ;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]
+--- a/funcs/func_db.c
++++ b/funcs/func_db.c
+@@ -97,6 +97,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi
+ 			<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
+ 	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;
+ }
+--- a/funcs/func_env.c
++++ b/funcs/func_env.c
+@@ -71,6 +71,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi
+ 			<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__, "$Revisi
+ 			<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__, "$Revisi
+ 		</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__, "$Revisi
+ 			<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;
+ }
+--- a/funcs/func_lock.c
++++ b/funcs/func_lock.c
+@@ -59,6 +59,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi
+ 			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__, "$Revisi
+ 			<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__, "$Revisi
+ 			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>
+  ***/
+@@ -497,9 +512,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;
+ }
+--- a/funcs/func_realtime.c
++++ b/funcs/func_realtime.c
+@@ -115,6 +115,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi
+ 		<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>
+@@ -432,28 +438,32 @@ static int function_realtime_readdestroy
+ 		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 (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;
+-	}
++		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);
+@@ -464,6 +474,15 @@ static int function_realtime_readdestroy
+ 	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,
+@@ -489,6 +508,7 @@ static struct ast_custom_function realti
+ static struct ast_custom_function realtime_destroy_function = {
+ 	.name = "REALTIME_DESTROY",
+ 	.read = function_realtime_readdestroy,
++	.write = function_realtime_writedestroy,
+ };
+ 
+ static int unload_module(void)
+@@ -507,7 +527,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;
+--- a/funcs/func_shell.c
++++ b/funcs/func_shell.c
+@@ -88,12 +88,17 @@ static int shell_helper(struct ast_chann
+ 		</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>
+@@ -110,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");
+--- a/include/asterisk/pbx.h
++++ b/include/asterisk/pbx.h
+@@ -1139,16 +1139,44 @@ struct ast_custom_function* ast_custom_f
+ 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);
+@@ -1261,6 +1289,32 @@ unsigned int ast_hashtab_hash_contexts(c
+  */
+ 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
+--- a/main/asterisk.c
++++ b/main/asterisk.c
+@@ -2959,6 +2959,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);
+@@ -3166,8 +3168,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) {
+--- a/main/pbx.c
++++ b/main/pbx.c
+@@ -805,6 +805,17 @@ static struct ast_taskprocessor *device_
+ 
+ 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
+@@ -1150,6 +1161,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];
+@@ -3499,6 +3523,7 @@ struct ast_custom_function *ast_custom_f
+ 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;
+@@ -3515,9 +3540,64 @@ int ast_custom_function_unregister(struc
+ 	}
+ 	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.
+@@ -3619,6 +3699,50 @@ int __ast_custom_function_register(struc
+ 	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'
+  */
+@@ -3640,6 +3764,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);
+@@ -3652,6 +3894,8 @@ int ast_func_read(struct ast_channel *ch
+ 		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);
+@@ -3689,6 +3933,8 @@ int ast_func_read2(struct ast_channel *c
+ 		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);
+@@ -3728,11 +3974,13 @@ int ast_func_write(struct ast_channel *c
+ 	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)
+--- a/main/tcptls.c
++++ b/main/tcptls.c
+@@ -44,6 +44,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi
+ #include "asterisk/options.h"
+ #include "asterisk/manager.h"
+ #include "asterisk/astobj2.h"
++#include "asterisk/pbx.h"
+ 
+ /*! \brief
+  * replacement read/write functions for SSL support.
+@@ -149,6 +150,16 @@ static void *handle_tcptls_connection(vo
+ 	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.
+ 	*/
diff -Nru asterisk-1.8.13.1~dfsg/debian/patches/ASTERISK-20658 asterisk-1.8.13.1~dfsg/debian/patches/ASTERISK-20658
--- asterisk-1.8.13.1~dfsg/debian/patches/ASTERISK-20658	1970-01-01 02:00:00.000000000 +0200
+++ asterisk-1.8.13.1~dfsg/debian/patches/ASTERISK-20658	2013-12-20 14:36:57.000000000 +0200
@@ -0,0 +1,79 @@
+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(+)
+
+--- a/funcs/func_realtime.c
++++ b/funcs/func_realtime.c
+@@ -219,6 +219,13 @@ static int function_realtime_read(struct
+ 	/* 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);
+@@ -432,6 +439,16 @@ static int function_realtime_readdestroy
+ 	/* 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);
+--- a/main/config.c
++++ b/main/config.c
+@@ -1514,6 +1514,17 @@ static struct ast_config *config_text_fi
+ 		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 */
diff -Nru asterisk-1.8.13.1~dfsg/debian/patches/series asterisk-1.8.13.1~dfsg/debian/patches/series
--- asterisk-1.8.13.1~dfsg/debian/patches/series	2013-08-29 21:57:28.000000000 +0300
+++ asterisk-1.8.13.1~dfsg/debian/patches/series	2013-12-19 13:06:59.000000000 +0200
@@ -36,3 +36,6 @@
 fix_xmpp_19532
 AST-2013-004
 AST-2013-005
+AST-2013-006
+ASTERISK-20658
+AST-2013-007
diff -Nru asterisk-1.8.13.1~dfsg/debian/README.Debian asterisk-1.8.13.1~dfsg/debian/README.Debian
--- asterisk-1.8.13.1~dfsg/debian/README.Debian	2013-08-29 21:57:28.000000000 +0300
+++ asterisk-1.8.13.1~dfsg/debian/README.Debian	2013-12-19 14:05:42.000000000 +0200
@@ -303,6 +303,17 @@
 live/asterisk is a wrapper to that private copy of Asterisk.
 
 
-Enjoy your PBX!
+Live Dangarously
+================
+AST-2013-007 disallows running several functions (such as SHELL) when
+run from remote interfaces (AMI, realtime), unless the user decides to
+"live_dangerously". However, in order to support backward compatibility,
+the stable versions default to the "live_dangerously" setting enabled.
+
+If you want to disable it, add the following line to the section
+'[options]' in /etc/asterisk/asterisk.conf:
+
+live_dangerously = no
 
- -- Lionel Elie Mamane <lmamane at debian.org>, Fri, 29 Jul 2011 19:21:06 +0200
+
+Enjoy your PBX!
diff -Nru asterisk-1.8.13.1~dfsg/debian/rules asterisk-1.8.13.1~dfsg/debian/rules
--- asterisk-1.8.13.1~dfsg/debian/rules	2013-08-29 21:57:28.000000000 +0300
+++ asterisk-1.8.13.1~dfsg/debian/rules	2013-12-19 13:12:18.000000000 +0200
@@ -192,6 +192,11 @@
 	# create a simple config
 	echo "; please read the documentation regarding the Manager Interface (asterisk-doc package)" > \
 		$(CURDIR)/debian/asterisk-config/etc/asterisk/manager.d/README.conf
+	# Unapply the changes in AST-2013-007 to config file asterisk.conf:
+	# (Changes left in the patch as we do want to keep the sample config
+	# files fixed)
+	filterdiff -i '*/configs/asterisk.conf.sample' $(CURDIR)/debian/patches/AST-2013-007 \
+		| patch -R $(CURDIR)/debian/asterisk-config/etc/asterisk/asterisk.conf
 	touch $@
 
 binary: binary-indep binary-arch
-------------- next part --------------
diff -Nru asterisk-1.6.2.9/debian/changelog asterisk-1.6.2.9/debian/changelog
--- asterisk-1.6.2.9/debian/changelog	2013-09-02 11:11:43.000000000 +0300
+++ asterisk-1.6.2.9/debian/changelog	2013-12-19 12:47:36.000000000 +0200
@@ -1,3 +1,11 @@
+asterisk (1:1.6.2.9-2+squeeze12) 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 20:36:21 +0200
+
 asterisk (1:1.6.2.9-2+squeeze11) oldstable-security; urgency=high
 
   * Patch AST-2013-004 (CVE-2013-5641): chan_sip: crash in ACK to SDP
diff -Nru asterisk-1.6.2.9/debian/patches/AST-2013-006 asterisk-1.6.2.9/debian/patches/AST-2013-006
--- asterisk-1.6.2.9/debian/patches/AST-2013-006	1970-01-01 02:00:00.000000000 +0200
+++ asterisk-1.6.2.9/debian/patches/AST-2013-006	2013-12-17 20:34:25.000000000 +0200
@@ -0,0 +1,38 @@
+Subject: app_sms: BufferOverflow when receiving odd length 16 bit message
+From: Scott Griepentrog <sgriepentrog at digium.com>
+Date: Mon, 16 Dec 2013 15:18:56 +0000
+Origin: http://svnview.digium.com/svn/asterisk?view=rev&rev=403853
+Bug: https://issues.asterisk.org/jira/browse/ASTERISK-22590
+
+This patch prevents an infinite loop overwriting memory when
+a message is received into the unpacksms16() function, where
+the length of the message is an odd number of bytes.
+
+---
+ apps/app_sms.c |    3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/apps/app_sms.c b/apps/app_sms.c
+index 08b90d1..75d399a 100644
+--- a/apps/app_sms.c
++++ b/apps/app_sms.c
+@@ -696,7 +696,7 @@ static void unpacksms16(unsigned char *i, unsigned char l, unsigned char *udh, i
+ 	}
+ 	while (l--) {
+ 		int v = *i++;
+-		if (l--) {
++		if (l && l--) {
+ 			v = (v << 8) + *i++;
+ 		}
+ 		*o++ = v;
+@@ -714,6 +714,7 @@ static int unpacksms(unsigned char dcs, unsigned char *i, unsigned char *udh, in
+ 	} else if (is8bit(dcs)) {
+ 		unpacksms8(i, l, udh, udhl, ud, udl, udhi);
+ 	} else {
++		l += l % 2;
+ 		unpacksms16(i, l, udh, udhl, ud, udl, udhi);
+ 	}
+ 	return l + 1;
+-- 
+1.7.10.4
+
diff -Nru asterisk-1.6.2.9/debian/patches/AST-2013-007 asterisk-1.6.2.9/debian/patches/AST-2013-007
--- asterisk-1.6.2.9/debian/patches/AST-2013-007	1970-01-01 02:00:00.000000000 +0200
+++ asterisk-1.6.2.9/debian/patches/AST-2013-007	2013-12-19 12:46:15.000000000 +0200
@@ -0,0 +1,830 @@
+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.6.2.9-2+squeeze11 to 1.6.2.9-2+squeeze12 (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.6.1 to 1.6.2:
+ 
+ * SIP no longer sends the 183 progress message for early media by
+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$")
+ 			</parameter>
+ 		</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>
+  ***/
+@@ -1258,8 +1278,8 @@ 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_escalating(&stat_function, AST_CFE_READ);
++	res |= ast_custom_function_register_escalating(&file_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>
+ 	</function>
+ 	<function name="REALTIME_FIELD" language="en_US">
+@@ -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);
++}
++
+ struct ast_custom_function realtime_function = {
+ 	.name = "REALTIME",
+ 	.read = function_realtime_read,
+@@ -496,6 +515,7 @@ static struct ast_custom_function realtime_store_function = {
+ 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);
+ unsigned int ast_hashtab_hash_contexts(const void *obj);
+ /*! @} */
+ 
++/*!
++ * \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, "sendfullybooted")) {
+ 			ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_SEND_FULLYBOOTED);
++		} 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)
+ 		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 {
+ 		int res;
+ 		struct ast_module_user *u = NULL;
+@@ -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 -Nru asterisk-1.6.2.9/debian/patches/ASTERISK-20658 asterisk-1.6.2.9/debian/patches/ASTERISK-20658
--- asterisk-1.6.2.9/debian/patches/ASTERISK-20658	1970-01-01 02:00:00.000000000 +0200
+++ asterisk-1.6.2.9/debian/patches/ASTERISK-20658	2013-12-19 12:03:19.000000000 +0200
@@ -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 -Nru asterisk-1.6.2.9/debian/patches/series asterisk-1.6.2.9/debian/patches/series
--- asterisk-1.6.2.9/debian/patches/series	2013-09-02 11:11:43.000000000 +0300
+++ asterisk-1.6.2.9/debian/patches/series	2013-12-19 12:03:32.000000000 +0200
@@ -63,3 +63,6 @@
 AST-2012-015
 AST-2013-004
 AST-2013-005
+AST-2013-006
+ASTERISK-20658
+AST-2013-007


More information about the Pkg-voip-maintainers mailing list