mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-12 15:45:18 +00:00
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 git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/12@403959 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
190
main/pbx.c
190
main/pbx.c
@@ -832,6 +832,17 @@ struct ast_app;
|
||||
|
||||
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
|
||||
@@ -3988,6 +3999,28 @@ int ast_custom_function_unregister(struct ast_custom_function *acf)
|
||||
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) {
|
||||
return acf->read_escalates;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \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) {
|
||||
return acf->write_escalates;
|
||||
}
|
||||
|
||||
/*! \internal
|
||||
* \brief Retrieve the XML documentation of a specified ast_custom_function,
|
||||
* and populate ast_custom_function string fields.
|
||||
@@ -4086,6 +4119,33 @@ 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)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = __ast_custom_function_register(acf, mod);
|
||||
if (res != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (escalation) {
|
||||
case AST_CFE_NONE:
|
||||
break;
|
||||
case AST_CFE_READ:
|
||||
acf->read_escalates = 1;
|
||||
break;
|
||||
case AST_CFE_WRITE:
|
||||
acf->write_escalates = 1;
|
||||
break;
|
||||
case AST_CFE_BOTH:
|
||||
acf->read_escalates = 1;
|
||||
acf->write_escalates = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief return a pointer to the arguments of the function,
|
||||
* and terminates the function name with '\\0'
|
||||
*/
|
||||
@@ -4107,6 +4167,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);
|
||||
@@ -4119,6 +4297,8 @@ int ast_func_read(struct ast_channel *chan, const char *function, char *workspac
|
||||
ast_log(LOG_ERROR, "Function %s not registered\n", copy);
|
||||
} else if (!acfptr->read && !acfptr->read2) {
|
||||
ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
|
||||
} else if (!is_read_allowed(acfptr)) {
|
||||
ast_log(LOG_ERROR, "Dangerous function %s read blocked\n", copy);
|
||||
} else if (acfptr->read) {
|
||||
if (acfptr->mod) {
|
||||
u = __ast_module_user_add(acfptr->mod, chan);
|
||||
@@ -4156,6 +4336,8 @@ int ast_func_read2(struct ast_channel *chan, const char *function, struct ast_st
|
||||
ast_log(LOG_ERROR, "Function %s not registered\n", copy);
|
||||
} else if (!acfptr->read && !acfptr->read2) {
|
||||
ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
|
||||
} else if (!is_read_allowed(acfptr)) {
|
||||
ast_log(LOG_ERROR, "Dangerous function %s read blocked\n", copy);
|
||||
} else {
|
||||
if (acfptr->mod) {
|
||||
u = __ast_module_user_add(acfptr->mod, chan);
|
||||
@@ -4195,11 +4377,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)
|
||||
|
Reference in New Issue
Block a user