security: Inhibit execution of privilege escalating functions

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.

Also, the ABI was changed to something more reasonable, since Asterisk
12 does not yet have a public release.

(closes issue ASTERISK-22905)
Review: http://reviewboard.digium.internal/r/432/
........

Merged revisions 403913 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........

Merged revisions 403917 from http://svn.asterisk.org/svn/asterisk/branches/11
........

Merged revisions 403959 from http://svn.asterisk.org/svn/asterisk/branches/12


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@403960 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
David M. Lee
2013-12-16 19:11:51 +00:00
parent 00dcee2a64
commit 744556c01d
12 changed files with 418 additions and 37 deletions

View File

@@ -110,6 +110,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>
@@ -311,10 +317,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)
@@ -335,7 +353,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);
res |= ast_custom_function_register(&db_keys_function);
return res;

View File

@@ -71,6 +71,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<parameter name="filename" required="true" />
</syntax>
<description>
<note>
<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
is set to <literal>no</literal>, this function can only be executed from the
dialplan, and not directly from external protocols.</para>
</note>
</description>
</function>
<function name="FILE" language="en_US">
@@ -167,6 +172,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para> Set(FILE(/tmp/foo.txt,-1,,l)=bar)</para>
<para> ; Append "bar" to the file with a newline</para>
<para> Set(FILE(/tmp/foo.txt,,,al)=bar)</para>
<note>
<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
is set to <literal>no</literal>, this function can only be executed from the
dialplan, and not directly from external protocols.</para>
</note>
</description>
<see-also>
<ref type="function">FILE_COUNT_LINE</ref>
@@ -197,6 +207,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</syntax>
<description>
<para>Returns the number of lines, or <literal>-1</literal> on error.</para>
<note>
<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
is set to <literal>no</literal>, this function can only be executed from the
dialplan, and not directly from external protocols.</para>
</note>
</description>
<see-also>
<ref type="function">FILE</ref>
@@ -216,6 +231,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>'d' - DOS "\r\n" format</para>
<para>'m' - Macintosh "\r" format</para>
<para>'x' - Cannot be determined</para>
<note>
<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
is set to <literal>no</literal>, this function can only be executed from the
dialplan, and not directly from external protocols.</para>
</note>
</description>
<see-also>
<ref type="function">FILE</ref>
@@ -1259,10 +1279,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;
}

View File

@@ -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);
if (ast_pthread_create_background(&broker_tid, NULL, lock_broker, NULL)) {
ast_log(LOG_ERROR, "Failed to start lock broker thread. Unloading func_lock module.\n");

View File

@@ -115,6 +115,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>This function acts in the same way as REALTIME(....) does, except that
it destroys the matched record in the RT engine.</para>
<note>
<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
is set to <literal>no</literal>, this function can only be read from the
dialplan, and not directly from external protocols. It can, however, be
executed as a write operation (<literal>REALTIME_DESTROY(family, fieldmatch)=ignored</literal>)</para>
</note>
</description>
<see-also>
<ref type="function">REALTIME</ref>
@@ -439,28 +445,32 @@ static int function_realtime_readdestroy(struct ast_channel *chan, const char *c
return -1;
}
resultslen = 0;
n = 0;
for (var = head; var; n++, var = var->next)
resultslen += strlen(var->name) + strlen(var->value);
/* add space for delimiters and final '\0' */
resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
if (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 +481,15 @@ static int function_realtime_readdestroy(struct ast_channel *chan, const char *c
return 0;
}
/*!
* \brief Wrapper to execute REALTIME_DESTROY from a write operation. Allows
* execution even if live_dangerously is disabled.
*/
static int function_realtime_writedestroy(struct ast_channel *chan, const char *cmd, char *data, const char *value)
{
return function_realtime_readdestroy(chan, cmd, data, NULL, 0);
}
static struct ast_custom_function realtime_function = {
.name = "REALTIME",
.read = function_realtime_read,
@@ -496,6 +515,7 @@ static struct ast_custom_function realtime_store_function = {
static struct ast_custom_function realtime_destroy_function = {
.name = "REALTIME_DESTROY",
.read = function_realtime_readdestroy,
.write = function_realtime_writedestroy,
};
static int unload_module(void)
@@ -514,7 +534,7 @@ static int load_module(void)
int res = 0;
res |= ast_custom_function_register(&realtime_function);
res |= ast_custom_function_register(&realtime_store_function);
res |= ast_custom_function_register(&realtime_destroy_function);
res |= ast_custom_function_register_escalating(&realtime_destroy_function, AST_CFE_READ);
res |= ast_custom_function_register(&realtimefield_function);
res |= ast_custom_function_register(&realtimehash_function);
return res;

View File

@@ -88,11 +88,17 @@ static int shell_helper(struct ast_channel *chan, const char *cmd, char *data,
</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>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, "Collects the output generated by a command executed by the system shell");