/branches/12:1-403347
Index: configure
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: UPGRADE.txt
===================================================================
--- UPGRADE.txt	(https://origsvn.digium.com/svn/asterisk/branches/12)	(revision 403347)
+++ UPGRADE.txt	(working copy)
@@ -354,6 +354,17 @@
    - BLINDTRANSFER/ATTENDEDTRANSFER events now report the peer as NULL and
      additional information in the extra string field.
 
+Dialplan Functions:
+
+ - 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; ARI's /channels/{channelId}/variable resource; etc.) is
+   disallowed by default. Execution from external sources may be enabled by
+   setting 'live_dangerously' to 'yes' in the [options] section of
+   asterisk.conf. SHELL(), LOCK(), TRYLOCK(), and UNLOCK() 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).
+
 Dialplan:
  - All channel and global variable names are evaluated in a case-sensitive
    manner. In previous versions of Asterisk, variables created and evaluated in
Index: funcs/func_env.c
===================================================================
--- funcs/func_env.c	(https://origsvn.digium.com/svn/asterisk/branches/12)	(revision 403347)
+++ funcs/func_env.c	(working copy)
@@ -1221,6 +1221,7 @@
 	.name = "STAT",
 	.read = stat_read,
 	.read_max = 12,
+	.dangerous_read = 1,
 };
 
 static struct ast_custom_function file_function = {
@@ -1227,6 +1228,8 @@
 	.name = "FILE",
 	.read2 = file_read,
 	.write = file_write,
+	.dangerous_read = 1,
+	.dangerous_write = 1,
 };
 
 static struct ast_custom_function file_count_line_function = {
@@ -1233,6 +1236,7 @@
 	.name = "FILE_COUNT_LINE",
 	.read2 = file_count_line,
 	.read_max = 12,
+	.dangerous_read = 1,
 };
 
 static struct ast_custom_function file_format_function = {
@@ -1239,6 +1243,7 @@
 	.name = "FILE_FORMAT",
 	.read2 = file_format,
 	.read_max = 2,
+	.dangerous_read = 1,
 };
 
 static int unload_module(void)
Index: funcs/func_shell.c
===================================================================
--- funcs/func_shell.c	(https://origsvn.digium.com/svn/asterisk/branches/12)	(revision 403347)
+++ funcs/func_shell.c	(working copy)
@@ -88,11 +88,17 @@
 		</syntax>
 		<description>
 			<para>Collects the output generated by a command executed by the system shell</para>
-			<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></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>The <literal>SHELL</literal> function is considered dangerous,
+				and can only be executed directly from external interfaces (AMI, ARI, etc.)
+				if <literal>live_dangerously</literal> is set in asterisk.conf.
+				Such usage is discouraged, since it allows for privilege escalation.</para>
+			</note>
 		</description>
 
 	</function>
@@ -100,6 +106,7 @@
 static struct ast_custom_function shell_function = {
 	.name = "SHELL",
 	.read = shell_helper,
+	.dangerous_read = 1
 };
 
 static int unload_module(void)
Index: funcs/func_db.c
===================================================================
--- funcs/func_db.c	(https://origsvn.digium.com/svn/asterisk/branches/12)	(revision 403347)
+++ funcs/func_db.c	(working copy)
@@ -311,10 +311,19 @@
 	return 0;
 }
 
+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,
+	.dangerous_read = 1,
+	.write = function_db_delete_write,
 };
 
 static int unload_module(void)
Index: funcs/func_lock.c
===================================================================
--- funcs/func_lock.c	(https://origsvn.digium.com/svn/asterisk/branches/12)	(revision 403347)
+++ funcs/func_lock.c	(working copy)
@@ -448,6 +448,7 @@
 	.name = "LOCK",
 	.read = lock_read,
 	.read_max = 2,
+	.dangerous_read = 1,
 };
 
 static struct ast_custom_function trylock_function = {
@@ -454,6 +455,7 @@
 	.name = "TRYLOCK",
 	.read = trylock_read,
 	.read_max = 2,
+	.dangerous_read = 1,
 };
 
 static struct ast_custom_function unlock_function = {
@@ -460,6 +462,7 @@
 	.name = "UNLOCK",
 	.read = unlock_read,
 	.read_max = 2,
+	.dangerous_read = 1,
 };
 
 static int unload_module(void)
Index: funcs/func_realtime.c
===================================================================
--- funcs/func_realtime.c	(https://origsvn.digium.com/svn/asterisk/branches/12)	(revision 403347)
+++ funcs/func_realtime.c	(working copy)
@@ -439,28 +439,32 @@
 		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);
@@ -471,6 +475,11 @@
 	return 0;
 }
 
+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 +505,8 @@
 static struct ast_custom_function realtime_destroy_function = {
 	.name = "REALTIME_DESTROY",
 	.read = function_realtime_readdestroy,
+	.dangerous_read = 1,
+	.write = function_realtime_writedestroy,
 };
 
 static int unload_module(void)
Index: include/asterisk/pbx.h
===================================================================
--- include/asterisk/pbx.h	(https://origsvn.digium.com/svn/asterisk/branches/12)	(revision 403347)
+++ include/asterisk/pbx.h	(working copy)
@@ -140,6 +140,14 @@
 	/*! Write function, if write is supported */
 	ast_acf_write_fn_t write;	/*!< Write function, if write is supported */
 	struct ast_module *mod;         /*!< Module this custom function belongs to */
+	int dangerous_read:1;           /*!< The read function is to be considered
+					 * 'dangerous', and should not be run directly
+					 * from external interfaces (AMI, ARI, etc.)
+					 * \since 12 */
+	int dangerous_write:1;          /*!< The write function is to be considerd
+					 * 'dangerous', and should not be run directly
+					 * from external interfaces (AMI, ARI, etc.)
+					 * \since 12 */
 	AST_RWLIST_ENTRY(ast_custom_function) acflist;
 };
 
@@ -1489,6 +1497,23 @@
  */
 char *ast_complete_applications(const char *line, const char *word, int state);
 
+/*!
+ * \brief Enable the execution of 'dangerous' functions from external sources
+ * (ARI, 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
+ * sources with permission controls should not normally invoke them.
+ *
+ * Calling this function removes this safeguard.
+ */
+void pbx_enable_dangerous_functions(void);
+
+/*!
+ * \brief Enable the execution of dangerous functions for the current thread.
+ */
+void ast_thread_disable_dangerous_functions(void);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
Index: main/pbx.c
===================================================================
--- main/pbx.c	(https://origsvn.digium.com/svn/asterisk/branches/12)	(revision 403347)
+++ main/pbx.c	(working copy)
@@ -832,8 +832,19 @@
 
 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_disable_dangerous_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
 	The dialplan is saved as a linked list with each context
 	having it's own linked list of extensions - one item per
@@ -4109,6 +4120,59 @@
 	return args;
 }
 
+void pbx_enable_dangerous_functions(void)
+{
+	ast_log(LOG_WARNING, "Enabling dangerous function execution from external sources!\n");
+	live_dangerously = 1;
+}
+
+void ast_thread_disable_dangerous_functions(void)
+{
+	int *thread_disable_dangerous;
+
+	thread_disable_dangerous = ast_threadstorage_get(
+		&thread_disable_dangerous_tl, sizeof(*thread_disable_dangerous));
+
+	if (thread_disable_dangerous == NULL) {
+		ast_log(LOG_ERROR, "Error enabling thread's ability to run dangerous functions\n");
+		return;
+	}
+
+	*thread_disable_dangerous = 1;
+}
+
+static int dangerous_functions_allowed(struct ast_custom_function *acfptr)
+{
+	int *thread_disable_dangerous;
+
+	if (!acfptr) {
+		return 0;
+	}
+
+	thread_disable_dangerous = ast_threadstorage_get(
+		&thread_disable_dangerous_tl, sizeof(*thread_disable_dangerous));
+
+	if (thread_disable_dangerous == NULL) {
+		ast_log(LOG_ERROR, "Error checking thread's ability to run dangerous functions\n");
+		return 0;
+	}
+
+	if (*thread_disable_dangerous) {
+		if (live_dangerously) {
+			/* Global setting overrides the thread's preference */
+			ast_debug(2, "Executing %s from a dangerous context\n",
+				acfptr->name);
+			return 1;
+		}
+
+		/* Dangerous function not allowed */
+		return 0;
+	}
+
+	/* Dangerous functions enabled for this thread. */
+	return 1;
+}
+
 int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len)
 {
 	char *copy = ast_strdupa(function);
@@ -4121,6 +4185,8 @@
 		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 (acfptr->dangerous_read && !dangerous_functions_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);
@@ -4158,6 +4224,8 @@
 		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 (acfptr->dangerous_read && !dangerous_functions_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);
@@ -4197,11 +4265,13 @@
 	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 (acfptr->dangerous_write && !dangerous_functions_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)
@@ -6450,6 +6520,8 @@
 	 */
 	struct ast_channel *c = data;
 
+	/* From the PBX thread, we execute dialplan. Which is allowed to
+	 * execute dangerous functions */
 	__ast_pbx_run(c, NULL);
 	decrease_call_count();
 
Index: main/asterisk.c
===================================================================
--- main/asterisk.c	(https://origsvn.digium.com/svn/asterisk/branches/12)	(revision 403347)
+++ main/asterisk.c	(working copy)
@@ -3566,6 +3566,10 @@
 					v->value);
 				ast_clear_flag(&ast_options, AST_OPT_FLAG_STDEXTEN_MACRO);
 			}
+		} else if (!strcasecmp(v->name, "live_dangerously")) {
+			if (ast_true(v->value)) {
+				pbx_enable_dangerous_functions();
+			}
 		}
 	}
 	for (v = ast_variable_browse(cfg, "compat"); v; v = v->next) {
Index: main/tcptls.c
===================================================================
--- main/tcptls.c	(https://origsvn.digium.com/svn/asterisk/branches/12)	(revision 403347)
+++ main/tcptls.c	(working copy)
@@ -48,6 +48,7 @@
 #include "asterisk/options.h"
 #include "asterisk/manager.h"
 #include "asterisk/astobj2.h"
+#include "asterisk/pbx.h"
 
 /*! \brief
  * replacement read/write functions for SSL support.
@@ -164,6 +165,13 @@
 	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.
+	 */
+	ast_thread_disable_dangerous_functions();
+
 	/*
 	* open a FILE * as appropriate.
 	*/
Index: configs/asterisk.conf.sample
===================================================================
--- configs/asterisk.conf.sample	(https://origsvn.digium.com/svn/asterisk/branches/12)	(revision 403347)
+++ configs/asterisk.conf.sample	(working copy)
@@ -83,6 +83,12 @@
 				; gosub - Invoke the stdexten using a gosub as
 				;         documented in extensions.conf.sample.
 				; Default gosub.
+;live_dangerously = no		; Enable the execution of 'dangerous' dialplan
+				; functions from external sources (ARI, AMI,
+				; etc.) These functions (such as SHELL) are
+				; considered dangerous because they can allow
+				; privilege escalation.
+				; Default no.
 
 ; Changing the following lines may compromise your security.
 ;[files]
Index: .
===================================================================
--- .	(https://origsvn.digium.com/svn/asterisk/branches/12)	(revision 403347)
+++ .	(working copy)

Property changes on: .
___________________________________________________________________
Added: svnmerge-integrated
## -0,0 +1 ##
+/branches/12:1-403347
\ No newline at end of property
