mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-11-03 20:38:59 +00:00 
			
		
		
		
	Found via `codespell -q 3 -S "./CREDITS" -L abd,asent,atleast,childrens,contentn,crypted,dne,durationm,exten,inout,leapyear,nd,oclock,offsetp,ot,parm,parms,requestor,ser,slanguage,slin,thirdparty,varn,varns,ues`
		
			
				
	
	
		
			739 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			739 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Asterisk -- An open source telephony toolkit.
 | 
						|
 *
 | 
						|
 * Copyright (C) 2015, CFWare, LLC
 | 
						|
 *
 | 
						|
 * Corey Farrell <git@cfware.com>
 | 
						|
 *
 | 
						|
 * See http://www.asterisk.org for more information about
 | 
						|
 * the Asterisk project. Please do not directly contact
 | 
						|
 * any of the maintainers of this project for assistance;
 | 
						|
 * the project provides a web site, mailing lists and IRC
 | 
						|
 * channels for your use.
 | 
						|
 *
 | 
						|
 * This program is free software, distributed under the terms of
 | 
						|
 * the GNU General Public License Version 2. See the LICENSE file
 | 
						|
 * at the top of the source tree.
 | 
						|
 */
 | 
						|
 | 
						|
/*! \file
 | 
						|
 *
 | 
						|
 * \brief Custom function management routines.
 | 
						|
 *
 | 
						|
 * \author Corey Farrell <git@cfware.com>
 | 
						|
 */
 | 
						|
 | 
						|
/*** MODULEINFO
 | 
						|
	<support_level>core</support_level>
 | 
						|
 ***/
 | 
						|
 | 
						|
#include "asterisk.h"
 | 
						|
 | 
						|
#include "asterisk/_private.h"
 | 
						|
#include "asterisk/cli.h"
 | 
						|
#include "asterisk/linkedlists.h"
 | 
						|
#include "asterisk/module.h"
 | 
						|
#include "asterisk/pbx.h"
 | 
						|
#include "asterisk/term.h"
 | 
						|
#include "asterisk/threadstorage.h"
 | 
						|
#include "asterisk/xmldoc.h"
 | 
						|
#include "pbx_private.h"
 | 
						|
 | 
						|
/*!
 | 
						|
 * \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 Registered functions container.
 | 
						|
 *
 | 
						|
 * It is sorted by function name.
 | 
						|
 */
 | 
						|
static AST_RWLIST_HEAD_STATIC(acf_root, ast_custom_function);
 | 
						|
 | 
						|
static char *handle_show_functions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 | 
						|
{
 | 
						|
	struct ast_custom_function *acf;
 | 
						|
	int count_acf = 0;
 | 
						|
	int like = 0;
 | 
						|
 | 
						|
	switch (cmd) {
 | 
						|
	case CLI_INIT:
 | 
						|
		e->command = "core show functions [like]";
 | 
						|
		e->usage =
 | 
						|
			"Usage: core show functions [like <text>]\n"
 | 
						|
			"       List builtin functions, optionally only those matching a given string\n";
 | 
						|
		return NULL;
 | 
						|
	case CLI_GENERATE:
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	if (a->argc == 5 && (!strcmp(a->argv[3], "like")) ) {
 | 
						|
		like = 1;
 | 
						|
	} else if (a->argc != 3) {
 | 
						|
		return CLI_SHOWUSAGE;
 | 
						|
	}
 | 
						|
 | 
						|
	ast_cli(a->fd, "%s Custom Functions:\n"
 | 
						|
		"--------------------------------------------------------------------------------\n",
 | 
						|
		like ? "Matching" : "Installed");
 | 
						|
 | 
						|
	AST_RWLIST_RDLOCK(&acf_root);
 | 
						|
	AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) {
 | 
						|
		if (!like || strstr(acf->name, a->argv[4])) {
 | 
						|
			count_acf++;
 | 
						|
			ast_cli(a->fd, "%-20.20s  %-35.35s  %s\n",
 | 
						|
				S_OR(acf->name, ""),
 | 
						|
				S_OR(acf->syntax, ""),
 | 
						|
				S_OR(acf->synopsis, ""));
 | 
						|
		}
 | 
						|
	}
 | 
						|
	AST_RWLIST_UNLOCK(&acf_root);
 | 
						|
 | 
						|
	ast_cli(a->fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
 | 
						|
 | 
						|
	return CLI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
static char *complete_functions(const char *word, int pos, int state)
 | 
						|
{
 | 
						|
	struct ast_custom_function *cur;
 | 
						|
	char *ret = NULL;
 | 
						|
	int which = 0;
 | 
						|
	int wordlen;
 | 
						|
	int cmp;
 | 
						|
 | 
						|
	if (pos != 3) {
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	wordlen = strlen(word);
 | 
						|
	AST_RWLIST_RDLOCK(&acf_root);
 | 
						|
	AST_RWLIST_TRAVERSE(&acf_root, cur, acflist) {
 | 
						|
		/*
 | 
						|
		 * Do a case-insensitive search for convenience in this
 | 
						|
		 * 'complete' function.
 | 
						|
		 *
 | 
						|
		 * We must search the entire container because the functions are
 | 
						|
		 * sorted and normally found case sensitively.
 | 
						|
		 */
 | 
						|
		cmp = strncasecmp(word, cur->name, wordlen);
 | 
						|
		if (!cmp) {
 | 
						|
			/* Found match. */
 | 
						|
			if (++which <= state) {
 | 
						|
				/* Not enough matches. */
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			ret = ast_strdup(cur->name);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	AST_RWLIST_UNLOCK(&acf_root);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static char *handle_show_function(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 | 
						|
{
 | 
						|
	struct ast_custom_function *acf;
 | 
						|
	/* Maximum number of characters added by terminal coloring is 22 */
 | 
						|
	char *synopsis = NULL, *since = NULL, *description = NULL, *syntax = NULL, *arguments = NULL, *seealso = NULL;
 | 
						|
	char *rtn = CLI_SUCCESS;
 | 
						|
 | 
						|
	switch (cmd) {
 | 
						|
	case CLI_INIT:
 | 
						|
		e->command = "core show function";
 | 
						|
		e->usage =
 | 
						|
			"Usage: core show function <function>\n"
 | 
						|
			"       Describe a particular dialplan function.\n";
 | 
						|
		return NULL;
 | 
						|
	case CLI_GENERATE:
 | 
						|
		return complete_functions(a->word, a->pos, a->n);
 | 
						|
	}
 | 
						|
 | 
						|
	if (a->argc != 4) {
 | 
						|
		return CLI_SHOWUSAGE;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!(acf = ast_custom_function_find(a->argv[3]))) {
 | 
						|
		ast_cli(a->fd, "No function by that name registered.\n");
 | 
						|
 | 
						|
		return CLI_FAILURE;
 | 
						|
	}
 | 
						|
 | 
						|
#ifdef AST_XML_DOCS
 | 
						|
	if (acf->docsrc == AST_XML_DOC) {
 | 
						|
		synopsis = ast_xmldoc_printable(S_OR(acf->synopsis, "Not available"), 1);
 | 
						|
		since = ast_xmldoc_printable(S_OR(acf->since, "Not available"), 1);
 | 
						|
		description = ast_xmldoc_printable(S_OR(acf->desc, "Not available"), 1);
 | 
						|
		syntax = ast_xmldoc_printable(S_OR(acf->syntax, "Not available"), 1);
 | 
						|
		arguments = ast_xmldoc_printable(S_OR(acf->arguments, "Not available"), 1);
 | 
						|
		seealso = ast_xmldoc_printable(S_OR(acf->seealso, "Not available"), 1);
 | 
						|
	} else
 | 
						|
#endif
 | 
						|
	{
 | 
						|
		synopsis = ast_strdup(S_OR(acf->synopsis, "Not Available"));
 | 
						|
		since = ast_strdup(S_OR(acf->since, "Not Available"));
 | 
						|
		description = ast_strdup(S_OR(acf->desc, "Not Available"));
 | 
						|
		syntax = ast_strdup(S_OR(acf->syntax, "Not Available"));
 | 
						|
		arguments = ast_strdup(S_OR(acf->arguments, "Not Available"));
 | 
						|
		seealso = ast_strdup(S_OR(acf->seealso, "Not Available"));
 | 
						|
	}
 | 
						|
		/* check allocated memory. */
 | 
						|
	if (!synopsis || !since || !description || !syntax || !arguments || !seealso) {
 | 
						|
		rtn = CLI_FAILURE;
 | 
						|
		goto free_docs;
 | 
						|
	}
 | 
						|
 | 
						|
	ast_cli(a->fd, "\n"
 | 
						|
		"%s  -= Info about Function '%s' =- %s\n\n"
 | 
						|
		COLORIZE_FMT "\n"
 | 
						|
		"%s\n\n"
 | 
						|
		COLORIZE_FMT "\n"
 | 
						|
		"%s\n\n"
 | 
						|
		COLORIZE_FMT "\n"
 | 
						|
		"%s\n\n"
 | 
						|
		COLORIZE_FMT "\n"
 | 
						|
		"%s\n\n"
 | 
						|
		COLORIZE_FMT "\n"
 | 
						|
		"%s\n\n"
 | 
						|
		COLORIZE_FMT "\n"
 | 
						|
		"%s\n\n",
 | 
						|
		ast_term_color(COLOR_MAGENTA, 0), acf->name, ast_term_reset(),
 | 
						|
		COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"), synopsis,
 | 
						|
		COLORIZE(COLOR_MAGENTA, 0, "[Since]"), since,
 | 
						|
		COLORIZE(COLOR_MAGENTA, 0, "[Description]"), description,
 | 
						|
		COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"), syntax,
 | 
						|
		COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"), arguments,
 | 
						|
		COLORIZE(COLOR_MAGENTA, 0, "[See Also]"), seealso
 | 
						|
		);
 | 
						|
 | 
						|
free_docs:
 | 
						|
	ast_free(synopsis);
 | 
						|
	ast_free(since);
 | 
						|
	ast_free(description);
 | 
						|
	ast_free(syntax);
 | 
						|
	ast_free(arguments);
 | 
						|
	ast_free(seealso);
 | 
						|
 | 
						|
	return rtn;
 | 
						|
}
 | 
						|
 | 
						|
static struct ast_custom_function *ast_custom_function_find_nolock(const char *name)
 | 
						|
{
 | 
						|
	struct ast_custom_function *cur;
 | 
						|
	int cmp;
 | 
						|
 | 
						|
	AST_RWLIST_TRAVERSE(&acf_root, cur, acflist) {
 | 
						|
		cmp = strcmp(name, cur->name);
 | 
						|
		if (cmp > 0) {
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		if (!cmp) {
 | 
						|
			/* Found it. */
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		/* Not in container. */
 | 
						|
		cur = NULL;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	return cur;
 | 
						|
}
 | 
						|
 | 
						|
struct ast_custom_function *ast_custom_function_find(const char *name)
 | 
						|
{
 | 
						|
	struct ast_custom_function *acf;
 | 
						|
 | 
						|
	AST_RWLIST_RDLOCK(&acf_root);
 | 
						|
	acf = ast_custom_function_find_nolock(name);
 | 
						|
	AST_RWLIST_UNLOCK(&acf_root);
 | 
						|
 | 
						|
	return acf;
 | 
						|
}
 | 
						|
 | 
						|
int ast_custom_function_unregister(struct ast_custom_function *acf)
 | 
						|
{
 | 
						|
	struct ast_custom_function *cur;
 | 
						|
 | 
						|
	if (!acf) {
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	AST_RWLIST_WRLOCK(&acf_root);
 | 
						|
	cur = AST_RWLIST_REMOVE(&acf_root, acf, acflist);
 | 
						|
	if (cur) {
 | 
						|
#ifdef AST_XML_DOCS
 | 
						|
		if (cur->docsrc == AST_XML_DOC) {
 | 
						|
			ast_string_field_free_memory(acf);
 | 
						|
		}
 | 
						|
#endif
 | 
						|
		ast_verb(5, "Unregistered custom function %s\n", cur->name);
 | 
						|
	}
 | 
						|
	AST_RWLIST_UNLOCK(&acf_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)
 | 
						|
{
 | 
						|
	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.
 | 
						|
 *  \param acf ast_custom_function structure with empty 'desc' and 'synopsis'
 | 
						|
 *             but with a function 'name'.
 | 
						|
 *  \retval -1 On error.
 | 
						|
 *  \retval 0 On success.
 | 
						|
 */
 | 
						|
static int acf_retrieve_docs(struct ast_custom_function *acf)
 | 
						|
{
 | 
						|
#ifdef AST_XML_DOCS
 | 
						|
	char *tmpxml;
 | 
						|
 | 
						|
	/* Let's try to find it in the Documentation XML */
 | 
						|
	if (!ast_strlen_zero(acf->desc) || !ast_strlen_zero(acf->synopsis)) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	if (ast_string_field_init(acf, 128)) {
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	if (ast_string_field_init_extended(acf, since)) {
 | 
						|
		ast_string_field_free_memory(acf);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	/* load synopsis */
 | 
						|
	tmpxml = ast_xmldoc_build_synopsis("function", acf->name, ast_module_name(acf->mod));
 | 
						|
	ast_string_field_set(acf, synopsis, tmpxml);
 | 
						|
	ast_free(tmpxml);
 | 
						|
 | 
						|
	/* load since */
 | 
						|
	tmpxml = ast_xmldoc_build_since("function", acf->name, ast_module_name(acf->mod));
 | 
						|
	ast_string_field_set(acf, since, tmpxml);
 | 
						|
	ast_free(tmpxml);
 | 
						|
 | 
						|
	/* load description */
 | 
						|
	tmpxml = ast_xmldoc_build_description("function", acf->name, ast_module_name(acf->mod));
 | 
						|
	ast_string_field_set(acf, desc, tmpxml);
 | 
						|
	ast_free(tmpxml);
 | 
						|
 | 
						|
	/* load syntax */
 | 
						|
	tmpxml = ast_xmldoc_build_syntax("function", acf->name, ast_module_name(acf->mod));
 | 
						|
	ast_string_field_set(acf, syntax, tmpxml);
 | 
						|
	ast_free(tmpxml);
 | 
						|
 | 
						|
	/* load arguments */
 | 
						|
	tmpxml = ast_xmldoc_build_arguments("function", acf->name, ast_module_name(acf->mod));
 | 
						|
	ast_string_field_set(acf, arguments, tmpxml);
 | 
						|
	ast_free(tmpxml);
 | 
						|
 | 
						|
	/* load seealso */
 | 
						|
	tmpxml = ast_xmldoc_build_seealso("function", acf->name, ast_module_name(acf->mod));
 | 
						|
	ast_string_field_set(acf, seealso, tmpxml);
 | 
						|
	ast_free(tmpxml);
 | 
						|
 | 
						|
	acf->docsrc = AST_XML_DOC;
 | 
						|
#endif
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int __ast_custom_function_register(struct ast_custom_function *acf, struct ast_module *mod)
 | 
						|
{
 | 
						|
	struct ast_custom_function *cur;
 | 
						|
 | 
						|
	if (!acf) {
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	acf->mod = mod;
 | 
						|
#ifdef AST_XML_DOCS
 | 
						|
	acf->docsrc = AST_STATIC_DOC;
 | 
						|
#endif
 | 
						|
 | 
						|
	if (acf_retrieve_docs(acf)) {
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	AST_RWLIST_WRLOCK(&acf_root);
 | 
						|
 | 
						|
	cur = ast_custom_function_find_nolock(acf->name);
 | 
						|
	if (cur) {
 | 
						|
		ast_log(LOG_ERROR, "Function %s already registered.\n", acf->name);
 | 
						|
		AST_RWLIST_UNLOCK(&acf_root);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Store in alphabetical order */
 | 
						|
	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
 | 
						|
		if (strcmp(acf->name, cur->name) < 0) {
 | 
						|
			AST_RWLIST_INSERT_BEFORE_CURRENT(acf, acflist);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	AST_RWLIST_TRAVERSE_SAFE_END;
 | 
						|
	if (!cur) {
 | 
						|
		AST_RWLIST_INSERT_TAIL(&acf_root, acf, acflist);
 | 
						|
	}
 | 
						|
 | 
						|
	AST_RWLIST_UNLOCK(&acf_root);
 | 
						|
 | 
						|
	ast_verb(5, "Registered custom function '" COLORIZE_FMT "'\n", COLORIZE(COLOR_BRCYAN, 0, acf->name));
 | 
						|
 | 
						|
	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'
 | 
						|
 */
 | 
						|
static char *func_args(char *function)
 | 
						|
{
 | 
						|
	char *args = strchr(function, '(');
 | 
						|
 | 
						|
	if (!args) {
 | 
						|
		ast_log(LOG_WARNING, "Function '%s' doesn't contain parentheses.  Assuming null argument.\n", function);
 | 
						|
	} else {
 | 
						|
		char *p;
 | 
						|
		*args++ = '\0';
 | 
						|
		if ((p = strrchr(args, ')'))) {
 | 
						|
			*p = '\0';
 | 
						|
		} else {
 | 
						|
			ast_log(LOG_WARNING, "Can't find trailing parenthesis for function '%s(%s'?\n", function, args);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	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://docs.asterisk.org/Configuration/Dialplan/Privilege-Escalations-with-Dialplan-Functions/ 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;
 | 
						|
}
 | 
						|
 | 
						|
int ast_thread_inhibit_escalations_swap(int inhibit)
 | 
						|
{
 | 
						|
	int *thread_inhibit_escalations;
 | 
						|
	int orig;
 | 
						|
 | 
						|
	thread_inhibit_escalations = ast_threadstorage_get(
 | 
						|
		&thread_inhibit_escalations_tl, sizeof(*thread_inhibit_escalations));
 | 
						|
	if (thread_inhibit_escalations == NULL) {
 | 
						|
		ast_log(LOG_ERROR, "Error swapping privilege escalations inhibit for current thread\n");
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	orig = *thread_inhibit_escalations;
 | 
						|
	*thread_inhibit_escalations = !!inhibit;
 | 
						|
	return orig;
 | 
						|
}
 | 
						|
 | 
						|
/*!
 | 
						|
 * \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);
 | 
						|
	char *args = func_args(copy);
 | 
						|
	struct ast_custom_function *acfptr = ast_custom_function_find(copy);
 | 
						|
	int res;
 | 
						|
	struct ast_module_user *u = NULL;
 | 
						|
 | 
						|
	if (acfptr == NULL) {
 | 
						|
		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);
 | 
						|
		}
 | 
						|
		res = acfptr->read(chan, copy, args, workspace, len);
 | 
						|
		if (acfptr->mod && u) {
 | 
						|
			__ast_module_user_remove(acfptr->mod, u);
 | 
						|
		}
 | 
						|
 | 
						|
		return res;
 | 
						|
	} else {
 | 
						|
		struct ast_str *str = ast_str_create(16);
 | 
						|
 | 
						|
		if (acfptr->mod) {
 | 
						|
			u = __ast_module_user_add(acfptr->mod, chan);
 | 
						|
		}
 | 
						|
		res = acfptr->read2(chan, copy, args, &str, 0);
 | 
						|
		if (acfptr->mod && u) {
 | 
						|
			__ast_module_user_remove(acfptr->mod, u);
 | 
						|
		}
 | 
						|
		ast_copy_string(workspace, ast_str_buffer(str), len > ast_str_size(str) ? ast_str_size(str) : len);
 | 
						|
		ast_free(str);
 | 
						|
 | 
						|
		return res;
 | 
						|
	}
 | 
						|
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
int ast_func_read2(struct ast_channel *chan, const char *function, struct ast_str **str, ssize_t maxlen)
 | 
						|
{
 | 
						|
	char *copy = ast_strdupa(function);
 | 
						|
	char *args = func_args(copy);
 | 
						|
	struct ast_custom_function *acfptr = ast_custom_function_find(copy);
 | 
						|
	int res;
 | 
						|
	struct ast_module_user *u = NULL;
 | 
						|
 | 
						|
	if (acfptr == NULL) {
 | 
						|
		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);
 | 
						|
		}
 | 
						|
		ast_str_reset(*str);
 | 
						|
		if (acfptr->read2) {
 | 
						|
			/* ast_str enabled */
 | 
						|
			res = acfptr->read2(chan, copy, args, str, maxlen);
 | 
						|
		} else {
 | 
						|
			/* Legacy function pointer, allocate buffer for result */
 | 
						|
			int maxsize = ast_str_size(*str);
 | 
						|
 | 
						|
			if (maxlen > -1) {
 | 
						|
				if (maxlen == 0) {
 | 
						|
					if (acfptr->read_max) {
 | 
						|
						maxsize = acfptr->read_max;
 | 
						|
					} else {
 | 
						|
						maxsize = VAR_BUF_SIZE;
 | 
						|
					}
 | 
						|
				} else {
 | 
						|
					maxsize = maxlen;
 | 
						|
				}
 | 
						|
				ast_str_make_space(str, maxsize);
 | 
						|
			}
 | 
						|
			res = acfptr->read(chan, copy, args, ast_str_buffer(*str), maxsize);
 | 
						|
			ast_str_update(*str); /* Manually set the string length */
 | 
						|
		}
 | 
						|
		if (acfptr->mod && u) {
 | 
						|
			__ast_module_user_remove(acfptr->mod, u);
 | 
						|
		}
 | 
						|
 | 
						|
		return res;
 | 
						|
	}
 | 
						|
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
 | 
						|
{
 | 
						|
	char *copy = ast_strdupa(function);
 | 
						|
	char *args = func_args(copy);
 | 
						|
	struct ast_custom_function *acfptr = ast_custom_function_find(copy);
 | 
						|
 | 
						|
	if (acfptr == NULL) {
 | 
						|
		ast_log(LOG_ERROR, "Function %s not registered\n", copy);
 | 
						|
	} else if (!acfptr->write) {
 | 
						|
		ast_log(LOG_ERROR, "Function %s cannot be written to\n", copy);
 | 
						|
	} 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) {
 | 
						|
			u = __ast_module_user_add(acfptr->mod, chan);
 | 
						|
		}
 | 
						|
		res = acfptr->write(chan, copy, args, value);
 | 
						|
		if (acfptr->mod && u) {
 | 
						|
			__ast_module_user_remove(acfptr->mod, u);
 | 
						|
		}
 | 
						|
 | 
						|
		return res;
 | 
						|
	}
 | 
						|
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
static struct ast_cli_entry acf_cli[] = {
 | 
						|
	AST_CLI_DEFINE(handle_show_functions, "Shows registered dialplan functions"),
 | 
						|
	AST_CLI_DEFINE(handle_show_function, "Describe a specific dialplan function"),
 | 
						|
};
 | 
						|
 | 
						|
static void unload_pbx_functions_cli(void)
 | 
						|
{
 | 
						|
	ast_cli_unregister_multiple(acf_cli, ARRAY_LEN(acf_cli));
 | 
						|
}
 | 
						|
 | 
						|
int load_pbx_functions_cli(void)
 | 
						|
{
 | 
						|
	ast_cli_register_multiple(acf_cli, ARRAY_LEN(acf_cli));
 | 
						|
	ast_register_cleanup(unload_pbx_functions_cli);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 |