Asterisk - The Open Source Telephony Project  21.4.1
Data Structures | Functions | Variables
pbx_functions.c File Reference

Custom function management routines. More...

#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"

Go to the source code of this file.

Data Structures

struct  acf_root
 Registered functions container. More...
 

Functions

int __ast_custom_function_register (struct ast_custom_function *acf, struct ast_module *mod)
 Register a custom function.
 
int __ast_custom_function_register_escalating (struct ast_custom_function *acf, enum ast_custom_function_escalation escalation, struct ast_module *mod)
 Register a custom function which requires escalated privileges. More...
 
static void __init_thread_inhibit_escalations_tl (void)
 A thread local indicating whether the current thread can run 'dangerous' dialplan functions.
 
static int acf_retrieve_docs (struct ast_custom_function *acf)
 
struct ast_custom_functionast_custom_function_find (const char *name)
 
static struct ast_custom_functionast_custom_function_find_nolock (const char *name)
 
int ast_custom_function_unregister (struct ast_custom_function *acf)
 Unregister a custom function.
 
int ast_func_read (struct ast_channel *chan, const char *function, char *workspace, size_t len)
 executes a read operation on a function More...
 
int ast_func_read2 (struct ast_channel *chan, const char *function, struct ast_str **str, ssize_t maxlen)
 executes a read operation on a function More...
 
int ast_func_write (struct ast_channel *chan, const char *function, const char *value)
 executes a write operation on a function More...
 
int ast_thread_inhibit_escalations (void)
 Inhibit (in the current thread) the execution of dialplan functions which cause privilege escalations. If pbx_live_dangerously() has been called, this function has no effect. More...
 
int ast_thread_inhibit_escalations_swap (int inhibit)
 Swap the current thread escalation inhibit setting. More...
 
static char * complete_functions (const char *word, int pos, int state)
 
static char * func_args (char *function)
 return a pointer to the arguments of the function, and terminates the function name with '\0'
 
static char * handle_show_function (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static char * handle_show_functions (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static int is_read_allowed (struct ast_custom_function *acfptr)
 Determines whether execution of a custom function's read function is allowed. More...
 
static int is_write_allowed (struct ast_custom_function *acfptr)
 Determines whether execution of a custom function's write function is allowed. More...
 
int load_pbx_functions_cli (void)
 
void pbx_live_dangerously (int new_live_dangerously)
 Enable/disable the execution of 'dangerous' functions from external protocols (AMI, etc.). More...
 
static int read_escalates (const struct ast_custom_function *acf)
 Returns true if given custom function escalates privileges on read. More...
 
static int thread_inhibits_escalations (void)
 Indicates whether the current thread inhibits the execution of dangerous functions. More...
 
static void unload_pbx_functions_cli (void)
 
static int write_escalates (const struct ast_custom_function *acf)
 Returns true if given custom function escalates privileges on write. More...
 

Variables

static struct ast_cli_entry acf_cli []
 
static struct acf_root acf_root = { .first = NULL, .last = NULL, .lock = { PTHREAD_RWLOCK_INITIALIZER , NULL, {1, 0} } , }
 
static int live_dangerously
 Set to true (non-zero) to globally allow all dangerous dialplan functions to run.
 
static struct ast_threadstorage thread_inhibit_escalations_tl = { .once = PTHREAD_ONCE_INIT , .key_init = __init_thread_inhibit_escalations_tl , .custom_init = NULL , }
 

Detailed Description

Custom function management routines.

Author
Corey Farrell git@c.nosp@m.fwar.nosp@m.e.com

Definition in file pbx_functions.c.

Function Documentation

int __ast_custom_function_register_escalating ( struct ast_custom_function acf,
enum ast_custom_function_escalation  escalation,
struct ast_module mod 
)

Register a custom function which requires escalated privileges.

Examples would be SHELL() (for which a read needs permission to execute arbitrary code) or FILE() (for which write needs permission to change files on the filesystem).

Definition at line 418 of file pbx_functions.c.

References __ast_custom_function_register(), ast_custom_function::read_escalates, and ast_custom_function::write_escalates.

419 {
420  int res;
421 
422  res = __ast_custom_function_register(acf, mod);
423  if (res != 0) {
424  return -1;
425  }
426 
427  switch (escalation) {
428  case AST_CFE_NONE:
429  break;
430  case AST_CFE_READ:
431  acf->read_escalates = 1;
432  break;
433  case AST_CFE_WRITE:
434  acf->write_escalates = 1;
435  break;
436  case AST_CFE_BOTH:
437  acf->read_escalates = 1;
438  acf->write_escalates = 1;
439  break;
440  }
441 
442  return 0;
443 }
int __ast_custom_function_register(struct ast_custom_function *acf, struct ast_module *mod)
Register a custom function.
unsigned int write_escalates
Definition: pbx.h:147
unsigned int read_escalates
Definition: pbx.h:143
int ast_func_read ( struct ast_channel chan,
const char *  function,
char *  workspace,
size_t  len 
)

executes a read operation on a function

Parameters
chanChannel to execute on
functionData containing the function call string (will be modified)
workspaceA pointer to safe memory to use for a return value
lenthe number of bytes in workspace

This application executes a function in read mode on a given channel.

Return values
0success
non-zerofailure

Definition at line 599 of file pbx_functions.c.

References ast_copy_string(), ast_str_buffer(), ast_str_create, ast_str_size(), ast_strdupa, copy(), func_args(), is_read_allowed(), ast_custom_function::mod, ast_custom_function::read, and ast_custom_function::read2.

Referenced by handle_eval_function(), lua_get_variable_value(), and pbx_substitute_variables_helper_full_location().

600 {
601  char *copy = ast_strdupa(function);
602  char *args = func_args(copy);
603  struct ast_custom_function *acfptr = ast_custom_function_find(copy);
604  int res;
605  struct ast_module_user *u = NULL;
606 
607  if (acfptr == NULL) {
608  ast_log(LOG_ERROR, "Function %s not registered\n", copy);
609  } else if (!acfptr->read && !acfptr->read2) {
610  ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
611  } else if (!is_read_allowed(acfptr)) {
612  ast_log(LOG_ERROR, "Dangerous function %s read blocked\n", copy);
613  } else if (acfptr->read) {
614  if (acfptr->mod) {
615  u = __ast_module_user_add(acfptr->mod, chan);
616  }
617  res = acfptr->read(chan, copy, args, workspace, len);
618  if (acfptr->mod && u) {
619  __ast_module_user_remove(acfptr->mod, u);
620  }
621 
622  return res;
623  } else {
624  struct ast_str *str = ast_str_create(16);
625 
626  if (acfptr->mod) {
627  u = __ast_module_user_add(acfptr->mod, chan);
628  }
629  res = acfptr->read2(chan, copy, args, &str, 0);
630  if (acfptr->mod && u) {
631  __ast_module_user_remove(acfptr->mod, u);
632  }
633  ast_copy_string(workspace, ast_str_buffer(str), len > ast_str_size(str) ? ast_str_size(str) : len);
634  ast_free(str);
635 
636  return res;
637  }
638 
639  return -1;
640 }
size_t ast_str_size(const struct ast_str *buf)
Returns the current maximum length (without reallocation) of the current buffer.
Definition: strings.h:742
static int is_read_allowed(struct ast_custom_function *acfptr)
Determines whether execution of a custom function's read function is allowed.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
static int copy(char *infile, char *outfile)
Utility function to copy a file.
struct ast_module * mod
Definition: pbx.h:142
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
static char * func_args(char *function)
return a pointer to the arguments of the function, and terminates the function name with '\0' ...
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
ast_acf_read2_fn_t read2
Definition: pbx.h:137
Support for dynamic strings.
Definition: strings.h:623
ast_acf_read_fn_t read
Definition: pbx.h:129
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
int ast_func_read2 ( struct ast_channel chan,
const char *  function,
struct ast_str **  str,
ssize_t  maxlen 
)

executes a read operation on a function

Parameters
chanChannel to execute on
functionData containing the function call string (will be modified)
strA dynamic string buffer into which to place the result.
maxlen<0 if the dynamic buffer should not grow; >0 if the dynamic buffer should be limited to that number of bytes; 0 if the dynamic buffer has no upper limit

This application executes a function in read mode on a given channel.

Return values
0success
non-zerofailure

Definition at line 642 of file pbx_functions.c.

References ast_str_buffer(), ast_str_reset(), ast_str_size(), ast_str_update(), ast_strdupa, copy(), func_args(), is_read_allowed(), ast_custom_function::mod, ast_custom_function::read, ast_custom_function::read2, and ast_custom_function::read_max.

Referenced by ast_ari_channels_get_channel_var(), and ast_str_substitute_variables_full2().

643 {
644  char *copy = ast_strdupa(function);
645  char *args = func_args(copy);
646  struct ast_custom_function *acfptr = ast_custom_function_find(copy);
647  int res;
648  struct ast_module_user *u = NULL;
649 
650  if (acfptr == NULL) {
651  ast_log(LOG_ERROR, "Function %s not registered\n", copy);
652  } else if (!acfptr->read && !acfptr->read2) {
653  ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
654  } else if (!is_read_allowed(acfptr)) {
655  ast_log(LOG_ERROR, "Dangerous function %s read blocked\n", copy);
656  } else {
657  if (acfptr->mod) {
658  u = __ast_module_user_add(acfptr->mod, chan);
659  }
660  ast_str_reset(*str);
661  if (acfptr->read2) {
662  /* ast_str enabled */
663  res = acfptr->read2(chan, copy, args, str, maxlen);
664  } else {
665  /* Legacy function pointer, allocate buffer for result */
666  int maxsize = ast_str_size(*str);
667 
668  if (maxlen > -1) {
669  if (maxlen == 0) {
670  if (acfptr->read_max) {
671  maxsize = acfptr->read_max;
672  } else {
673  maxsize = VAR_BUF_SIZE;
674  }
675  } else {
676  maxsize = maxlen;
677  }
678  ast_str_make_space(str, maxsize);
679  }
680  res = acfptr->read(chan, copy, args, ast_str_buffer(*str), maxsize);
681  ast_str_update(*str); /* Manually set the string length */
682  }
683  if (acfptr->mod && u) {
684  __ast_module_user_remove(acfptr->mod, u);
685  }
686 
687  return res;
688  }
689 
690  return -1;
691 }
size_t ast_str_size(const struct ast_str *buf)
Returns the current maximum length (without reallocation) of the current buffer.
Definition: strings.h:742
static int is_read_allowed(struct ast_custom_function *acfptr)
Determines whether execution of a custom function's read function is allowed.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
static int copy(char *infile, char *outfile)
Utility function to copy a file.
struct ast_module * mod
Definition: pbx.h:142
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
static char * func_args(char *function)
return a pointer to the arguments of the function, and terminates the function name with '\0' ...
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
size_t read_max
Definition: pbx.h:139
ast_acf_read2_fn_t read2
Definition: pbx.h:137
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:693
ast_acf_read_fn_t read
Definition: pbx.h:129
void ast_str_update(struct ast_str *buf)
Update the length of the buffer, after using ast_str merely as a buffer.
Definition: strings.h:703
int ast_func_write ( struct ast_channel chan,
const char *  function,
const char *  value 
)

executes a write operation on a function

Parameters
chanChannel to execute on
functionData containing the function call string (will be modified)
valueA value parameter to pass for writing

This application executes a function in write mode on a given channel.

Return values
0success
non-zerofailure

Definition at line 693 of file pbx_functions.c.

References ast_strdupa, copy(), func_args(), is_write_allowed(), ast_custom_function::mod, and ast_custom_function::write.

Referenced by ast_channel_hangupcause_hash_set(), confbridge_exec(), pbx_builtin_pushvar_helper(), and pbx_builtin_setvar_helper().

694 {
695  char *copy = ast_strdupa(function);
696  char *args = func_args(copy);
697  struct ast_custom_function *acfptr = ast_custom_function_find(copy);
698 
699  if (acfptr == NULL) {
700  ast_log(LOG_ERROR, "Function %s not registered\n", copy);
701  } else if (!acfptr->write) {
702  ast_log(LOG_ERROR, "Function %s cannot be written to\n", copy);
703  } else if (!is_write_allowed(acfptr)) {
704  ast_log(LOG_ERROR, "Dangerous function %s write blocked\n", copy);
705  } else {
706  int res;
707  struct ast_module_user *u = NULL;
708 
709  if (acfptr->mod) {
710  u = __ast_module_user_add(acfptr->mod, chan);
711  }
712  res = acfptr->write(chan, copy, args, value);
713  if (acfptr->mod && u) {
714  __ast_module_user_remove(acfptr->mod, u);
715  }
716 
717  return res;
718  }
719 
720  return -1;
721 }
static int copy(char *infile, char *outfile)
Utility function to copy a file.
struct ast_module * mod
Definition: pbx.h:142
static int is_write_allowed(struct ast_custom_function *acfptr)
Determines whether execution of a custom function's write function is allowed.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
static char * func_args(char *function)
return a pointer to the arguments of the function, and terminates the function name with '\0' ...
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
ast_acf_write_fn_t write
Definition: pbx.h:141
int ast_thread_inhibit_escalations ( void  )

Inhibit (in the current thread) the execution of dialplan functions which cause privilege escalations. If pbx_live_dangerously() has been called, this function has no effect.

Returns
0 if successfuly marked current thread.
Non-zero if marking current thread failed.

Definition at line 479 of file pbx_functions.c.

References ast_threadstorage_get().

Referenced by ast_add_extension2_lockopt(), and handle_tcptls_connection().

480 {
481  int *thread_inhibit_escalations;
482 
483  thread_inhibit_escalations = ast_threadstorage_get(
484  &thread_inhibit_escalations_tl, sizeof(*thread_inhibit_escalations));
485  if (thread_inhibit_escalations == NULL) {
486  ast_log(LOG_ERROR, "Error inhibiting privilege escalations for current thread\n");
487  return -1;
488  }
489 
490  *thread_inhibit_escalations = 1;
491  return 0;
492 }
void * ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size)
Retrieve thread storage.
int ast_thread_inhibit_escalations_swap ( int  inhibit)

Swap the current thread escalation inhibit setting.

Since
11.24.0
Parameters
inhibitNew setting. Non-zero to inhibit.
Return values
1if dangerous function execution was inhibited.
0if dangerous function execution was allowed.
-1on error.

Definition at line 494 of file pbx_functions.c.

References ast_threadstorage_get().

Referenced by ast_add_extension2_lockopt().

495 {
496  int *thread_inhibit_escalations;
497  int orig;
498 
499  thread_inhibit_escalations = ast_threadstorage_get(
500  &thread_inhibit_escalations_tl, sizeof(*thread_inhibit_escalations));
501  if (thread_inhibit_escalations == NULL) {
502  ast_log(LOG_ERROR, "Error swapping privilege escalations inhibit for current thread\n");
503  return -1;
504  }
505 
506  orig = *thread_inhibit_escalations;
507  *thread_inhibit_escalations = !!inhibit;
508  return orig;
509 }
void * ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size)
Retrieve thread storage.
static int is_read_allowed ( struct ast_custom_function acfptr)
static

Determines whether execution of a custom function's read function is allowed.

Parameters
acfptrCustom function to check
Returns
True (non-zero) if reading is allowed.
False (zero) if reading is not allowed.

Definition at line 541 of file pbx_functions.c.

References ast_debug, live_dangerously, ast_custom_function::name, read_escalates(), and thread_inhibits_escalations().

Referenced by ast_func_read(), and ast_func_read2().

542 {
543  if (!acfptr) {
544  return 1;
545  }
546 
547  if (!read_escalates(acfptr)) {
548  return 1;
549  }
550 
552  return 1;
553  }
554 
555  if (live_dangerously) {
556  /* Global setting overrides the thread's preference */
557  ast_debug(2, "Reading %s from a dangerous context\n",
558  acfptr->name);
559  return 1;
560  }
561 
562  /* We have no reason to allow this function to execute */
563  return 0;
564 }
const char * name
Definition: pbx.h:119
static int live_dangerously
Set to true (non-zero) to globally allow all dangerous dialplan functions to run. ...
Definition: pbx_functions.c:46
static int thread_inhibits_escalations(void)
Indicates whether the current thread inhibits the execution of dangerous functions.
#define ast_debug(level,...)
Log a DEBUG message.
static int read_escalates(const struct ast_custom_function *acf)
Returns true if given custom function escalates privileges on read.
static int is_write_allowed ( struct ast_custom_function acfptr)
static

Determines whether execution of a custom function's write function is allowed.

Parameters
acfptrCustom function to check
Returns
True (non-zero) if writing is allowed.
False (zero) if writing is not allowed.

Definition at line 574 of file pbx_functions.c.

References ast_debug, live_dangerously, ast_custom_function::name, thread_inhibits_escalations(), and write_escalates().

Referenced by ast_func_write().

575 {
576  if (!acfptr) {
577  return 1;
578  }
579 
580  if (!write_escalates(acfptr)) {
581  return 1;
582  }
583 
585  return 1;
586  }
587 
588  if (live_dangerously) {
589  /* Global setting overrides the thread's preference */
590  ast_debug(2, "Writing %s from a dangerous context\n",
591  acfptr->name);
592  return 1;
593  }
594 
595  /* We have no reason to allow this function to execute */
596  return 0;
597 }
const char * name
Definition: pbx.h:119
static int live_dangerously
Set to true (non-zero) to globally allow all dangerous dialplan functions to run. ...
Definition: pbx_functions.c:46
static int thread_inhibits_escalations(void)
Indicates whether the current thread inhibits the execution of dangerous functions.
#define ast_debug(level,...)
Log a DEBUG message.
static int write_escalates(const struct ast_custom_function *acf)
Returns true if given custom function escalates privileges on write.
int load_pbx_functions_cli ( void  )

Provided by pbx_functions.c

Definition at line 733 of file pbx_functions.c.

References ast_cli_register_multiple, and ast_register_cleanup().

734 {
735  ast_cli_register_multiple(acf_cli, ARRAY_LEN(acf_cli));
736  ast_register_cleanup(unload_pbx_functions_cli);
737 
738  return 0;
739 }
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
int ast_register_cleanup(void(*func)(void))
Register a function to be executed before Asterisk gracefully exits.
Definition: clicompat.c:19
void pbx_live_dangerously ( int  new_live_dangerously)

Enable/disable the execution of 'dangerous' functions from external protocols (AMI, etc.).

These dialplan functions (such as SHELL) provide an opportunity for privilege escalation. They are okay to invoke from the dialplan, but external protocols with permission controls should not normally invoke them.

This function can globally enable/disable the execution of dangerous functions from external protocols.

Parameters
new_live_dangerouslyIf true, enable the execution of escalating functions from external protocols.

Definition at line 466 of file pbx_functions.c.

References live_dangerously.

467 {
468  if (new_live_dangerously && !live_dangerously) {
469  ast_log(LOG_WARNING, "Privilege escalation protection disabled!\n"
470  "See https://docs.asterisk.org/Configuration/Dialplan/Privilege-Escalations-with-Dialplan-Functions/ for more details.\n");
471  }
472 
473  if (!new_live_dangerously && live_dangerously) {
474  ast_log(LOG_NOTICE, "Privilege escalation protection enabled.\n");
475  }
476  live_dangerously = new_live_dangerously;
477 }
static int live_dangerously
Set to true (non-zero) to globally allow all dangerous dialplan functions to run. ...
Definition: pbx_functions.c:46
static int read_escalates ( const struct ast_custom_function acf)
static

Returns true if given custom function escalates privileges on read.

Parameters
acfCustom function to query.
Returns
True (non-zero) if reads escalate privileges.
False (zero) if reads just read.

Definition at line 303 of file pbx_functions.c.

References ast_custom_function::read_escalates.

Referenced by is_read_allowed().

304 {
305  return acf->read_escalates;
306 }
unsigned int read_escalates
Definition: pbx.h:143
static int thread_inhibits_escalations ( void  )
static

Indicates whether the current thread inhibits the execution of dangerous functions.

Returns
True (non-zero) if dangerous function execution is inhibited.
False (zero) if dangerous function execution is allowed.

Definition at line 518 of file pbx_functions.c.

References ast_threadstorage_get().

Referenced by is_read_allowed(), and is_write_allowed().

519 {
520  int *thread_inhibit_escalations;
521 
522  thread_inhibit_escalations = ast_threadstorage_get(
523  &thread_inhibit_escalations_tl, sizeof(*thread_inhibit_escalations));
524  if (thread_inhibit_escalations == NULL) {
525  ast_log(LOG_ERROR, "Error checking thread's ability to run dangerous functions\n");
526  /* On error, assume that we are inhibiting */
527  return 1;
528  }
529 
530  return *thread_inhibit_escalations;
531 }
void * ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size)
Retrieve thread storage.
static int write_escalates ( const struct ast_custom_function acf)
static

Returns true if given custom function escalates privileges on write.

Parameters
acfCustom function to query.
Returns
True (non-zero) if writes escalate privileges.
False (zero) if writes just write.

Definition at line 315 of file pbx_functions.c.

References ast_custom_function::write_escalates.

Referenced by is_write_allowed().

316 {
317  return acf->write_escalates;
318 }
unsigned int write_escalates
Definition: pbx.h:147

Variable Documentation

struct ast_cli_entry acf_cli[]
static
Initial value:
= {
{ .handler = handle_show_functions , .summary = "Shows registered dialplan functions" ,},
{ .handler = handle_show_function , .summary = "Describe a specific dialplan function" ,},
}

Definition at line 723 of file pbx_functions.c.