diff options
Diffstat (limited to 'main/pbx.c')
-rw-r--r-- | main/pbx.c | 190 |
1 files changed, 187 insertions, 3 deletions
diff --git a/main/pbx.c b/main/pbx.c index ee472d842..6b9bd5ebf 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -845,6 +845,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 @@ -4001,6 +4012,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. @@ -4099,6 +4132,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' */ @@ -4120,6 +4180,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); @@ -4132,6 +4310,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); @@ -4169,6 +4349,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); @@ -4208,11 +4390,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) |