mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-30 10:33:13 +00:00
Introduce CLI permissions.
Based on cli_permissions.conf configuration file, we are able to permit or deny cli commands based on some patterns and the local user and group running rasterisk. (Sorry if I missed some of the testers). Reviewboard: http://reviewboard.digium.com/r/11/ (closes issue #11123) Reported by: eliel Tested by: eliel, IgorG, Laureano, otherwiseguy, mvanbaak git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@160062 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
6
CHANGES
6
CHANGES
@@ -402,6 +402,12 @@ Dialplan functions
|
|||||||
|
|
||||||
CLI Changes
|
CLI Changes
|
||||||
-----------
|
-----------
|
||||||
|
* Added CLI permissions, config file: cli_permissions.conf
|
||||||
|
default is to allow all commands for every local user/group.
|
||||||
|
Also this new feature added three new CLI commands:
|
||||||
|
- cli check permissions {<username>|@<groupname>|<username>@<groupname>} [<command>]
|
||||||
|
- cli reload permissions
|
||||||
|
- cli show permissions
|
||||||
* New CLI command "core show hint" (usage: core show hint <exten>)
|
* New CLI command "core show hint" (usage: core show hint <exten>)
|
||||||
* New CLI command "core show settings"
|
* New CLI command "core show settings"
|
||||||
* Added 'core show channels count' CLI command.
|
* Added 'core show channels count' CLI command.
|
||||||
|
82
configs/cli_permissions.conf.sample
Normal file
82
configs/cli_permissions.conf.sample
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
;
|
||||||
|
; CLI permissions configuration example for Asterisk
|
||||||
|
;
|
||||||
|
; All the users that you want to connect with asterisk using
|
||||||
|
; rasterisk, should have write/read access to the
|
||||||
|
; asterisk socket (asterisk.ctl). You could change the permissions
|
||||||
|
; of this file in 'asterisk.conf' config parameter: 'astctlpermissions' (0666)
|
||||||
|
; found on the [files] section.
|
||||||
|
;
|
||||||
|
; general options:
|
||||||
|
;
|
||||||
|
; default_perm = permit | deny
|
||||||
|
; This is the default permissions to apply for a user that
|
||||||
|
; does not has a permissions definided.
|
||||||
|
;
|
||||||
|
; user options:
|
||||||
|
; permit = <command name> | all ; allow the user to run 'command' |
|
||||||
|
; ; allow the user to run 'all' the commands
|
||||||
|
; deny = <command name> | all ; disallow the user to run 'command' |
|
||||||
|
; ; disallow the user to run 'all' commands.
|
||||||
|
;
|
||||||
|
|
||||||
|
[general]
|
||||||
|
|
||||||
|
default_perm=permit ; To leave asterisk working as normal
|
||||||
|
; we should set this parameter to 'permit'
|
||||||
|
;
|
||||||
|
; Follows the per-users permissions configs.
|
||||||
|
;
|
||||||
|
; This list is read in the sequence that is being written, so
|
||||||
|
; In this example the user 'eliel' is allow to run only the following
|
||||||
|
; commands:
|
||||||
|
; sip show peer
|
||||||
|
; core set debug
|
||||||
|
; core set verbose
|
||||||
|
; If the user is not specified, the default_perm option will be apply to
|
||||||
|
; every command.
|
||||||
|
;
|
||||||
|
; Notice that you can also use regular expressions to allow or deny access to a
|
||||||
|
; certain command like: 'core show application D*'. In this example the user will be
|
||||||
|
; allowed to view the documentation for all the applications starting with 'D'.
|
||||||
|
; Another regular expression could be: 'channel originate SIP/[0-9]* extension *'
|
||||||
|
; allowing the user to use 'channel originate' on a sip channel and with the 'extension'
|
||||||
|
; parameter and avoiding the use of the 'application' parameter.
|
||||||
|
;
|
||||||
|
; We can also use the templates syntax:
|
||||||
|
; [supportTemplate](!)
|
||||||
|
; deny=all
|
||||||
|
; permit=sip show ; all commands starting with 'sip show' will be allowed
|
||||||
|
; permit=core show
|
||||||
|
;
|
||||||
|
; You can specify permissions for a local group instead of a user,
|
||||||
|
; just put a '@' and we will know that is a group.
|
||||||
|
; IMPORTANT NOTE: Users permissions overwrite group permissions.
|
||||||
|
;
|
||||||
|
;[@adm]
|
||||||
|
;deny=all
|
||||||
|
;permit=sip
|
||||||
|
;permit=core
|
||||||
|
;
|
||||||
|
;
|
||||||
|
;[eliel]
|
||||||
|
;deny=all
|
||||||
|
;permit=sip show peer
|
||||||
|
;deny=sip show peers
|
||||||
|
;permit=core set
|
||||||
|
;
|
||||||
|
;
|
||||||
|
;User 'tommy' inherits from template 'supportTemplate':
|
||||||
|
; deny=all
|
||||||
|
; permit=sip show
|
||||||
|
; permit=core show
|
||||||
|
;[tommy](supportTemplate)
|
||||||
|
;permit=core set debug
|
||||||
|
;permit=dialplan show
|
||||||
|
;
|
||||||
|
;
|
||||||
|
;[mark]
|
||||||
|
;deny=all
|
||||||
|
;permit=all
|
||||||
|
;
|
||||||
|
;
|
5
configure
vendored
5
configure
vendored
@@ -1,5 +1,5 @@
|
|||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
# From configure.ac Revision: 159631 .
|
# From configure.ac Revision: 159818 .
|
||||||
# Guess values for system-dependent variables and create Makefiles.
|
# Guess values for system-dependent variables and create Makefiles.
|
||||||
# Generated by GNU Autoconf 2.61 for asterisk 1.6.
|
# Generated by GNU Autoconf 2.61 for asterisk 1.6.
|
||||||
#
|
#
|
||||||
@@ -15639,7 +15639,8 @@ done
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
for ac_func in asprintf atexit dup2 endpwent ftruncate getcwd gethostbyname gethostname getloadavg gettimeofday ioperm inet_ntoa isascii localtime_r memchr memmove memset mkdir munmap putenv re_comp regcomp select setenv socket strcasecmp strcasestr strchr strcspn strdup strerror strlcat strlcpy strncasecmp strndup strnlen strrchr strsep strspn strstr strtol strtoq unsetenv utime vasprintf
|
|
||||||
|
for ac_func in asprintf atexit dup2 endpwent ftruncate getcwd gethostbyname gethostname getloadavg gettimeofday ioperm inet_ntoa isascii localtime_r memchr memmove memset mkdir munmap putenv re_comp regcomp select setenv socket strcasecmp strcasestr strchr strcspn strdup strerror strlcat strlcpy strncasecmp strndup strnlen strrchr strsep strspn strstr strtol strtoq unsetenv utime vasprintf getpeereid
|
||||||
do
|
do
|
||||||
as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
|
as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
|
||||||
{ echo "$as_me:$LINENO: checking for $ac_func" >&5
|
{ echo "$as_me:$LINENO: checking for $ac_func" >&5
|
||||||
|
@@ -336,7 +336,7 @@ AC_FUNC_STRNLEN
|
|||||||
AC_FUNC_STRTOD
|
AC_FUNC_STRTOD
|
||||||
AC_FUNC_UTIME_NULL
|
AC_FUNC_UTIME_NULL
|
||||||
AC_FUNC_VPRINTF
|
AC_FUNC_VPRINTF
|
||||||
AC_CHECK_FUNCS([asprintf atexit dup2 endpwent ftruncate getcwd gethostbyname gethostname getloadavg gettimeofday ioperm inet_ntoa isascii localtime_r memchr memmove memset mkdir munmap putenv re_comp regcomp select setenv socket strcasecmp strcasestr strchr strcspn strdup strerror strlcat strlcpy strncasecmp strndup strnlen strrchr strsep strspn strstr strtol strtoq unsetenv utime vasprintf])
|
AC_CHECK_FUNCS([asprintf atexit dup2 endpwent ftruncate getcwd gethostbyname gethostname getloadavg gettimeofday ioperm inet_ntoa isascii localtime_r memchr memmove memset mkdir munmap putenv re_comp regcomp select setenv socket strcasecmp strcasestr strchr strcspn strdup strerror strlcat strlcpy strncasecmp strndup strnlen strrchr strsep strspn strstr strtol strtoq unsetenv utime vasprintf getpeereid])
|
||||||
|
|
||||||
AC_CHECK_FUNCS([glob])
|
AC_CHECK_FUNCS([glob])
|
||||||
|
|
||||||
|
@@ -24,6 +24,7 @@ int ast_term_init(void); /*!< Provided by term.c */
|
|||||||
int astdb_init(void); /*!< Provided by db.c */
|
int astdb_init(void); /*!< Provided by db.c */
|
||||||
void ast_channels_init(void); /*!< Provided by channel.c */
|
void ast_channels_init(void); /*!< Provided by channel.c */
|
||||||
void ast_builtins_init(void); /*!< Provided by cli.c */
|
void ast_builtins_init(void); /*!< Provided by cli.c */
|
||||||
|
int ast_cli_perms_init(int reload); /*!< Provided by cli.c */
|
||||||
int dnsmgr_init(void); /*!< Provided by dnsmgr.c */
|
int dnsmgr_init(void); /*!< Provided by dnsmgr.c */
|
||||||
void dnsmgr_start_refresh(void); /*!< Provided by dnsmgr.c */
|
void dnsmgr_start_refresh(void); /*!< Provided by dnsmgr.c */
|
||||||
int dnsmgr_reload(void); /*!< Provided by dnsmgr.c */
|
int dnsmgr_reload(void); /*!< Provided by dnsmgr.c */
|
||||||
|
@@ -322,6 +322,9 @@
|
|||||||
/* Define to 1 if you have the `getpagesize' function. */
|
/* Define to 1 if you have the `getpagesize' function. */
|
||||||
#undef HAVE_GETPAGESIZE
|
#undef HAVE_GETPAGESIZE
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `getpeereid' function. */
|
||||||
|
#undef HAVE_GETPEEREID
|
||||||
|
|
||||||
/* Define to 1 if you have the `gettimeofday' function. */
|
/* Define to 1 if you have the `gettimeofday' function. */
|
||||||
#undef HAVE_GETTIMEOFDAY
|
#undef HAVE_GETTIMEOFDAY
|
||||||
|
|
||||||
|
@@ -32,6 +32,10 @@ extern "C" {
|
|||||||
void ast_cli(int fd, const char *fmt, ...)
|
void ast_cli(int fd, const char *fmt, ...)
|
||||||
__attribute__((format(printf, 2, 3)));
|
__attribute__((format(printf, 2, 3)));
|
||||||
|
|
||||||
|
/* dont check permissions while passing this option as a 'uid'
|
||||||
|
* to the cli_has_permissions() function. */
|
||||||
|
#define CLI_NO_PERMS -1
|
||||||
|
|
||||||
#define RESULT_SUCCESS 0
|
#define RESULT_SUCCESS 0
|
||||||
#define RESULT_SHOWUSAGE 1
|
#define RESULT_SHOWUSAGE 1
|
||||||
#define RESULT_FAILURE 2
|
#define RESULT_FAILURE 2
|
||||||
@@ -191,23 +195,35 @@ char *ast_cli_complete(const char *word, char *const choices[], int pos);
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Interprets a command
|
* \brief Interprets a command
|
||||||
* Interpret a command s, sending output to fd
|
* Interpret a command s, sending output to fd if uid:gid has permissions
|
||||||
|
* to run this command. uid = CLI_NO_PERMS to avoid checking user permissions
|
||||||
|
* gid = CLI_NO_PERMS to avoid checking group permissions.
|
||||||
|
* \param uid User ID that is trying to run the command.
|
||||||
|
* \param gid Group ID that is trying to run the command.
|
||||||
* \param fd pipe
|
* \param fd pipe
|
||||||
* \param s incoming string
|
* \param s incoming string
|
||||||
* \retval 0 on success
|
* \retval 0 on success
|
||||||
* \retval -1 on failure
|
* \retval -1 on failure
|
||||||
*/
|
*/
|
||||||
int ast_cli_command(int fd, const char *s);
|
int ast_cli_command_full(int uid, int gid, int fd, const char *s);
|
||||||
|
|
||||||
|
#define ast_cli_command(fd,s) ast_cli_command_full(CLI_NO_PERMS, CLI_NO_PERMS, fd, s)
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Executes multiple CLI commands
|
* \brief Executes multiple CLI commands
|
||||||
* Interpret strings separated by NULL and execute each one, sending output to fd
|
* Interpret strings separated by NULL and execute each one, sending output to fd
|
||||||
|
* if uid has permissions, uid = CLI_NO_PERMS to avoid checking users permissions.
|
||||||
|
* gid = CLI_NO_PERMS to avoid checking group permissions.
|
||||||
|
* \param uid User ID that is trying to run the command.
|
||||||
|
* \param gid Group ID that is trying to run the command.
|
||||||
* \param fd pipe
|
* \param fd pipe
|
||||||
* \param size is the total size of the string
|
* \param size is the total size of the string
|
||||||
* \param s incoming string
|
* \param s incoming string
|
||||||
* \retval number of commands executed
|
* \retval number of commands executed
|
||||||
*/
|
*/
|
||||||
int ast_cli_command_multiple(int fd, size_t size, const char *s);
|
int ast_cli_command_multiple_full(int uid, int gid, int fd, size_t size, const char *s);
|
||||||
|
|
||||||
|
#define ast_cli_command_multiple(fd,size,s) ast_cli_command_multiple_full(CLI_NO_PERMS, CLI_NO_PERMS, fd, size, s)
|
||||||
|
|
||||||
/*! \brief Registers a command or an array of commands
|
/*! \brief Registers a command or an array of commands
|
||||||
* \param e which cli entry to register.
|
* \param e which cli entry to register.
|
||||||
|
@@ -176,6 +176,8 @@ struct console {
|
|||||||
int p[2]; /*!< Pipe */
|
int p[2]; /*!< Pipe */
|
||||||
pthread_t t; /*!< Thread of handler */
|
pthread_t t; /*!< Thread of handler */
|
||||||
int mute; /*!< Is the console muted for logs */
|
int mute; /*!< Is the console muted for logs */
|
||||||
|
int uid; /*!< Remote user ID. */
|
||||||
|
int gid; /*!< Remote group ID. */
|
||||||
int levels[NUMLOGLEVELS]; /*!< Which log levels are enabled for the console */
|
int levels[NUMLOGLEVELS]; /*!< Which log levels are enabled for the console */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -988,6 +990,48 @@ static void network_verboser(const char *s)
|
|||||||
|
|
||||||
static pthread_t lthread;
|
static pthread_t lthread;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief read() function supporting the reception of user credentials.
|
||||||
|
*
|
||||||
|
* \param fd Socket file descriptor.
|
||||||
|
* \param buffer Receive buffer.
|
||||||
|
* \param size 'buffer' size.
|
||||||
|
* \param con Console structure to set received credentials
|
||||||
|
* \retval -1 on error
|
||||||
|
* \retval the number of bytes received on success.
|
||||||
|
*/
|
||||||
|
static int read_credentials(int fd, char *buffer, size_t size, struct console *con)
|
||||||
|
{
|
||||||
|
#if defined(SO_PEERCRED)
|
||||||
|
struct ucred cred;
|
||||||
|
socklen_t len = sizeof(cred);
|
||||||
|
#endif
|
||||||
|
int result, uid, gid;
|
||||||
|
|
||||||
|
result = read(fd, buffer, size);
|
||||||
|
if (result < 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(SO_PEERCRED)
|
||||||
|
if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
uid = cred.uid;
|
||||||
|
gid = cred.gid;
|
||||||
|
#elif defined(HAVE_GETPEEREID)
|
||||||
|
if (getpeereid(fd, &uid, &gid)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return result;
|
||||||
|
#endif
|
||||||
|
con->uid = uid;
|
||||||
|
con->gid = gid;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static void *netconsole(void *vconsole)
|
static void *netconsole(void *vconsole)
|
||||||
{
|
{
|
||||||
struct console *con = vconsole;
|
struct console *con = vconsole;
|
||||||
@@ -1015,19 +1059,19 @@ static void *netconsole(void *vconsole)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (fds[0].revents) {
|
if (fds[0].revents) {
|
||||||
res = read(con->fd, tmp, sizeof(tmp));
|
res = read_credentials(con->fd, tmp, sizeof(tmp), con);
|
||||||
if (res < 1) {
|
if (res < 1) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tmp[res] = 0;
|
tmp[res] = 0;
|
||||||
if (strncmp(tmp, "cli quit after ", 15) == 0) {
|
if (strncmp(tmp, "cli quit after ", 15) == 0) {
|
||||||
ast_cli_command_multiple(con->fd, res - 15, tmp + 15);
|
ast_cli_command_multiple_full(con->uid, con->gid, con->fd, res - 15, tmp + 15);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ast_cli_command_multiple(con->fd, res, tmp);
|
ast_cli_command_multiple_full(con->uid, con->gid, con->fd, res, tmp);
|
||||||
}
|
}
|
||||||
if (fds[1].revents) {
|
if (fds[1].revents) {
|
||||||
res = read(con->p[0], tmp, sizeof(tmp));
|
res = read_credentials(con->p[0], tmp, sizeof(tmp), con);
|
||||||
if (res < 1) {
|
if (res < 1) {
|
||||||
ast_log(LOG_ERROR, "read returned %d\n", res);
|
ast_log(LOG_ERROR, "read returned %d\n", res);
|
||||||
break;
|
break;
|
||||||
@@ -1072,8 +1116,19 @@ static void *listener(void *unused)
|
|||||||
if (errno != EINTR)
|
if (errno != EINTR)
|
||||||
ast_log(LOG_WARNING, "Accept returned %d: %s\n", s, strerror(errno));
|
ast_log(LOG_WARNING, "Accept returned %d: %s\n", s, strerror(errno));
|
||||||
} else {
|
} else {
|
||||||
|
#if !defined(SO_PASSCRED)
|
||||||
|
{
|
||||||
|
#else
|
||||||
|
int sckopt = 1;
|
||||||
|
/* turn on socket credentials passing. */
|
||||||
|
if (setsockopt(s, SOL_SOCKET, SO_PASSCRED, &sckopt, sizeof(sckopt)) < 0) {
|
||||||
|
ast_log(LOG_WARNING, "Unable to turn on socket credentials passing\n");
|
||||||
|
} else {
|
||||||
|
#endif
|
||||||
for (x = 0; x < AST_MAX_CONNECTS; x++) {
|
for (x = 0; x < AST_MAX_CONNECTS; x++) {
|
||||||
if (consoles[x].fd < 0) {
|
if (consoles[x].fd >= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, consoles[x].p)) {
|
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, consoles[x].p)) {
|
||||||
ast_log(LOG_ERROR, "Unable to create pipe: %s\n", strerror(errno));
|
ast_log(LOG_ERROR, "Unable to create pipe: %s\n", strerror(errno));
|
||||||
consoles[x].fd = -1;
|
consoles[x].fd = -1;
|
||||||
@@ -1085,6 +1140,10 @@ static void *listener(void *unused)
|
|||||||
fcntl(consoles[x].p[1], F_SETFL, flags | O_NONBLOCK);
|
fcntl(consoles[x].p[1], F_SETFL, flags | O_NONBLOCK);
|
||||||
consoles[x].fd = s;
|
consoles[x].fd = s;
|
||||||
consoles[x].mute = 1; /* Default is muted, we will un-mute if necessary */
|
consoles[x].mute = 1; /* Default is muted, we will un-mute if necessary */
|
||||||
|
/* Default uid and gid to -2, so then in cli.c/cli_has_permissions() we will be able
|
||||||
|
to know if the user didn't send the credentials. */
|
||||||
|
consoles[x].uid = -2;
|
||||||
|
consoles[x].gid = -2;
|
||||||
if (ast_pthread_create_detached_background(&consoles[x].t, NULL, netconsole, &consoles[x])) {
|
if (ast_pthread_create_detached_background(&consoles[x].t, NULL, netconsole, &consoles[x])) {
|
||||||
ast_log(LOG_ERROR, "Unable to spawn thread to handle connection: %s\n", strerror(errno));
|
ast_log(LOG_ERROR, "Unable to spawn thread to handle connection: %s\n", strerror(errno));
|
||||||
close(consoles[x].p[0]);
|
close(consoles[x].p[0]);
|
||||||
@@ -1095,7 +1154,6 @@ static void *listener(void *unused)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (x >= AST_MAX_CONNECTS) {
|
if (x >= AST_MAX_CONNECTS) {
|
||||||
fdprint(s, "No more connections allowed\n");
|
fdprint(s, "No more connections allowed\n");
|
||||||
ast_log(LOG_WARNING, "No more connections allowed\n");
|
ast_log(LOG_WARNING, "No more connections allowed\n");
|
||||||
@@ -1104,6 +1162,7 @@ static void *listener(void *unused)
|
|||||||
ast_verb(3, "Remote UNIX connection\n");
|
ast_verb(3, "Remote UNIX connection\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3350,6 +3409,9 @@ int main(int argc, char *argv[])
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* loads the cli_permissoins.conf file needed to implement cli restrictions. */
|
||||||
|
ast_cli_perms_init(0);
|
||||||
|
|
||||||
/* AMI is initialized after loading modules because of a potential
|
/* AMI is initialized after loading modules because of a potential
|
||||||
* conflict between issuing a module reload from manager and
|
* conflict between issuing a module reload from manager and
|
||||||
* registering manager actions. This will cause reversed locking
|
* registering manager actions. This will cause reversed locking
|
||||||
|
418
main/cli.c
418
main/cli.c
@@ -33,6 +33,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <regex.h>
|
#include <regex.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <grp.h>
|
||||||
|
|
||||||
#include "asterisk/cli.h"
|
#include "asterisk/cli.h"
|
||||||
#include "asterisk/linkedlists.h"
|
#include "asterisk/linkedlists.h"
|
||||||
@@ -45,6 +47,35 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||||||
#include "editline/readline/readline.h"
|
#include "editline/readline/readline.h"
|
||||||
#include "asterisk/threadstorage.h"
|
#include "asterisk/threadstorage.h"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief List of restrictions per user.
|
||||||
|
*/
|
||||||
|
struct cli_perm {
|
||||||
|
unsigned int permit:1; /*!< 1=Permit 0=Deny */
|
||||||
|
char *command; /*!< Command name (to apply restrictions) */
|
||||||
|
AST_LIST_ENTRY(cli_perm) list;
|
||||||
|
};
|
||||||
|
|
||||||
|
AST_LIST_HEAD_NOLOCK(cli_perm_head, cli_perm);
|
||||||
|
|
||||||
|
/*! \brief list of users to apply restrictions. */
|
||||||
|
struct usergroup_cli_perm {
|
||||||
|
int uid; /*!< User ID (-1 disabled) */
|
||||||
|
int gid; /*!< Group ID (-1 disabled) */
|
||||||
|
struct cli_perm_head *perms; /*!< List of permissions. */
|
||||||
|
AST_LIST_ENTRY(usergroup_cli_perm) list;/*!< List mechanics */
|
||||||
|
};
|
||||||
|
/*! \brief CLI permissions config file. */
|
||||||
|
static const char perms_config[] = "cli_permissions.conf";
|
||||||
|
/*! \brief Default permissions value 1=Permit 0=Deny */
|
||||||
|
static int cli_default_perm = 1;
|
||||||
|
|
||||||
|
/*! \brief mutex used to prevent a user from running the 'cli reload permissions' command while
|
||||||
|
* it is already running. */
|
||||||
|
AST_MUTEX_DEFINE_STATIC(permsconfiglock);
|
||||||
|
/*! \brief List of users and permissions. */
|
||||||
|
AST_RWLIST_HEAD_STATIC(cli_perms, usergroup_cli_perm);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief map a debug or verbose value to a filename
|
* \brief map a debug or verbose value to a filename
|
||||||
*/
|
*/
|
||||||
@@ -117,6 +148,74 @@ unsigned int ast_verbose_get_by_file(const char *file)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! \internal
|
||||||
|
* \brief Check if the user with 'uid' and 'gid' is allow to execute 'command',
|
||||||
|
* if command starts with '_' then not check permissions, just permit
|
||||||
|
* to run the 'command'.
|
||||||
|
* if uid == -1 or gid == -1 do not check permissions.
|
||||||
|
* if uid == -2 and gid == -2 is because rasterisk client didn't send
|
||||||
|
* the credentials, so the cli_default_perm will be applied.
|
||||||
|
* \param uid User ID.
|
||||||
|
* \param gid Group ID.
|
||||||
|
* \param command Command name to check permissions.
|
||||||
|
* \retval 1 if has permission
|
||||||
|
* \retval 0 if it is not allowed.
|
||||||
|
*/
|
||||||
|
static int cli_has_permissions(int uid, int gid, const char *command)
|
||||||
|
{
|
||||||
|
struct usergroup_cli_perm *user_perm;
|
||||||
|
struct cli_perm *perm;
|
||||||
|
/* set to the default permissions general option. */
|
||||||
|
int isallowg = cli_default_perm, isallowu = -1, ispattern;
|
||||||
|
regex_t regexbuf;
|
||||||
|
|
||||||
|
/* if uid == -1 or gid == -1 do not check permissions.
|
||||||
|
if uid == -2 and gid == -2 is because rasterisk client didn't send
|
||||||
|
the credentials, so the cli_default_perm will be applied. */
|
||||||
|
if ((uid == CLI_NO_PERMS && gid == CLI_NO_PERMS) || command[0] == '_') {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gid < 0 && uid < 0) {
|
||||||
|
return cli_default_perm;
|
||||||
|
}
|
||||||
|
|
||||||
|
AST_RWLIST_RDLOCK(&cli_perms);
|
||||||
|
AST_LIST_TRAVERSE(&cli_perms, user_perm, list) {
|
||||||
|
if (user_perm->gid != gid && user_perm->uid != uid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
AST_LIST_TRAVERSE(user_perm->perms, perm, list) {
|
||||||
|
if (strcasecmp(perm->command, "all") && strncasecmp(perm->command, command, strlen(perm->command))) {
|
||||||
|
/* if the perm->command is a pattern, check it against command. */
|
||||||
|
ispattern = !regcomp(®exbuf, perm->command, REG_EXTENDED | REG_NOSUB | REG_ICASE);
|
||||||
|
if (ispattern && regexec(®exbuf, command, 0, NULL, 0)) {
|
||||||
|
regfree(®exbuf);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!ispattern) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
regfree(®exbuf);
|
||||||
|
}
|
||||||
|
if (user_perm->uid == uid) {
|
||||||
|
/* this is a user definition. */
|
||||||
|
isallowu = perm->permit;
|
||||||
|
} else {
|
||||||
|
/* otherwise is a group definition. */
|
||||||
|
isallowg = perm->permit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AST_RWLIST_UNLOCK(&cli_perms);
|
||||||
|
if (isallowu > -1) {
|
||||||
|
/* user definition override group definition. */
|
||||||
|
isallowg = isallowu;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isallowg;
|
||||||
|
}
|
||||||
|
|
||||||
static AST_RWLIST_HEAD_STATIC(helpers, ast_cli_entry);
|
static AST_RWLIST_HEAD_STATIC(helpers, ast_cli_entry);
|
||||||
|
|
||||||
static char *complete_fn(const char *word, int state)
|
static char *complete_fn(const char *word, int state)
|
||||||
@@ -479,6 +578,15 @@ static void print_uptimestr(int fd, struct timeval timeval, const char *prefix,
|
|||||||
ast_cli(fd, "%s: %s\n", prefix, out->str);
|
ast_cli(fd, "%s: %s\n", prefix, out->str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct ast_cli_entry *cli_next(struct ast_cli_entry *e)
|
||||||
|
{
|
||||||
|
if (e) {
|
||||||
|
return AST_LIST_NEXT(e, list);
|
||||||
|
} else {
|
||||||
|
return AST_LIST_FIRST(&helpers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static char * handle_showuptime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
static char * handle_showuptime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||||
{
|
{
|
||||||
struct timeval curtime = ast_tvnow();
|
struct timeval curtime = ast_tvnow();
|
||||||
@@ -753,6 +861,146 @@ static char *handle_softhangup(struct ast_cli_entry *e, int cmd, struct ast_cli_
|
|||||||
return CLI_SUCCESS;
|
return CLI_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! \brief handles CLI command 'cli show permissions' */
|
||||||
|
static char *handle_cli_show_permissions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||||
|
{
|
||||||
|
struct usergroup_cli_perm *cp;
|
||||||
|
struct cli_perm *perm;
|
||||||
|
struct passwd *pw = NULL;
|
||||||
|
struct group *gr = NULL;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case CLI_INIT:
|
||||||
|
e->command = "cli show permissions";
|
||||||
|
e->usage =
|
||||||
|
"Usage: cli show permissions\n"
|
||||||
|
" Shows CLI configured permissions.\n";
|
||||||
|
return NULL;
|
||||||
|
case CLI_GENERATE:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
AST_RWLIST_RDLOCK(&cli_perms);
|
||||||
|
AST_LIST_TRAVERSE(&cli_perms, cp, list) {
|
||||||
|
if (cp->uid >= 0) {
|
||||||
|
pw = getpwuid(cp->uid);
|
||||||
|
if (pw) {
|
||||||
|
ast_cli(a->fd, "user: %s [uid=%d]\n", pw->pw_name, cp->uid);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gr = getgrgid(cp->gid);
|
||||||
|
if (gr) {
|
||||||
|
ast_cli(a->fd, "group: %s [gid=%d]\n", gr->gr_name, cp->gid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast_cli(a->fd, "Permissions:\n");
|
||||||
|
if (cp->perms) {
|
||||||
|
AST_LIST_TRAVERSE(cp->perms, perm, list) {
|
||||||
|
ast_cli(a->fd, "\t%s -> %s\n", perm->permit ? "permit" : "deny", perm->command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast_cli(a->fd, "\n");
|
||||||
|
}
|
||||||
|
AST_RWLIST_UNLOCK(&cli_perms);
|
||||||
|
|
||||||
|
return CLI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief handles CLI command 'cli reload permissions' */
|
||||||
|
static char *handle_cli_reload_permissions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||||
|
{
|
||||||
|
switch (cmd) {
|
||||||
|
case CLI_INIT:
|
||||||
|
e->command = "cli reload permissions";
|
||||||
|
e->usage =
|
||||||
|
"Usage: cli reload permissions\n"
|
||||||
|
" Reload the 'cli_permissions.conf' file.\n";
|
||||||
|
return NULL;
|
||||||
|
case CLI_GENERATE:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_cli_perms_init(1);
|
||||||
|
|
||||||
|
return CLI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief handles CLI command 'cli check permissions' */
|
||||||
|
static char *handle_cli_check_permissions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||||
|
{
|
||||||
|
struct passwd *pw = NULL;
|
||||||
|
struct group *gr;
|
||||||
|
int gid = -1, uid = -1;
|
||||||
|
char command[AST_MAX_ARGS] = "";
|
||||||
|
struct ast_cli_entry *ce = NULL;
|
||||||
|
int found = 0;
|
||||||
|
char *group, *tmp;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case CLI_INIT:
|
||||||
|
e->command = "cli check permissions";
|
||||||
|
e->usage =
|
||||||
|
"Usage: cli check permissions {<username>|@<groupname>|<username>@<groupname>} [<command>]\n"
|
||||||
|
" Check permissions config for a user@group or list the allowed commands for the specified user.\n"
|
||||||
|
" The username or the groupname may be omitted.\n";
|
||||||
|
return NULL;
|
||||||
|
case CLI_GENERATE:
|
||||||
|
if (a->pos >= 4) {
|
||||||
|
return ast_cli_generator(a->line + strlen("cli check permissions") + strlen(a->argv[3]) + 1, a->word, a->n);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a->argc < 4) {
|
||||||
|
return CLI_SHOWUSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = ast_strdupa(a->argv[3]);
|
||||||
|
group = strchr(tmp, '@');
|
||||||
|
if (group) {
|
||||||
|
gr = getgrnam(&group[1]);
|
||||||
|
if (!gr) {
|
||||||
|
ast_cli(a->fd, "Unknown group '%s'\n", &group[1]);
|
||||||
|
return CLI_FAILURE;
|
||||||
|
}
|
||||||
|
group[0] = '\0';
|
||||||
|
gid = gr->gr_gid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!group && ast_strlen_zero(tmp)) {
|
||||||
|
ast_cli(a->fd, "You didn't supply a username\n");
|
||||||
|
} else if (!ast_strlen_zero(tmp) && !(pw = getpwnam(tmp))) {
|
||||||
|
ast_cli(a->fd, "Unknown user '%s'\n", tmp);
|
||||||
|
return CLI_FAILURE;
|
||||||
|
} else if (pw) {
|
||||||
|
uid = pw->pw_uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a->argc == 4) {
|
||||||
|
while ((ce = cli_next(ce))) {
|
||||||
|
/* Hide commands that start with '_' */
|
||||||
|
if (ce->_full_cmd[0] == '_') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cli_has_permissions(uid, gid, ce->_full_cmd)) {
|
||||||
|
ast_cli(a->fd, "%30.30s %s\n", ce->_full_cmd, S_OR(ce->summary, "<no description available>"));
|
||||||
|
found++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
ast_cli(a->fd, "You are not allowed to run any command on Asterisk\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ast_join(command, sizeof(command), a->argv + 4);
|
||||||
|
ast_cli(a->fd, "%s '%s%s%s' is %s to run command: '%s'\n", uid >= 0 ? "User" : "Group", tmp,
|
||||||
|
group && uid >= 0 ? "@" : "",
|
||||||
|
group ? &group[1] : "",
|
||||||
|
cli_has_permissions(uid, gid, command) ? "allowed" : "not allowed", command);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CLI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
static char *__ast_cli_generator(const char *text, const char *word, int state, int lock);
|
static char *__ast_cli_generator(const char *text, const char *word, int state, int lock);
|
||||||
|
|
||||||
static char *handle_commandmatchesarray(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
static char *handle_commandmatchesarray(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||||
@@ -1176,6 +1424,12 @@ static struct ast_cli_entry cli_cli[] = {
|
|||||||
AST_CLI_DEFINE(handle_showuptime, "Show uptime information"),
|
AST_CLI_DEFINE(handle_showuptime, "Show uptime information"),
|
||||||
|
|
||||||
AST_CLI_DEFINE(handle_softhangup, "Request a hangup on a given channel"),
|
AST_CLI_DEFINE(handle_softhangup, "Request a hangup on a given channel"),
|
||||||
|
|
||||||
|
AST_CLI_DEFINE(handle_cli_reload_permissions, "Reload CLI permissions config"),
|
||||||
|
|
||||||
|
AST_CLI_DEFINE(handle_cli_show_permissions, "Show CLI permissions"),
|
||||||
|
|
||||||
|
AST_CLI_DEFINE(handle_cli_check_permissions, "Try a permissions config for a user"),
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -1205,21 +1459,151 @@ static int set_full_cmd(struct ast_cli_entry *e)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! \brief cleanup (free) cli_perms linkedlist. */
|
||||||
|
static void destroy_user_perms (void)
|
||||||
|
{
|
||||||
|
struct cli_perm *perm;
|
||||||
|
struct usergroup_cli_perm *user_perm;
|
||||||
|
|
||||||
|
AST_RWLIST_WRLOCK(&cli_perms);
|
||||||
|
while ((user_perm = AST_LIST_REMOVE_HEAD(&cli_perms, list))) {
|
||||||
|
while ((perm = AST_LIST_REMOVE_HEAD(user_perm->perms, list))) {
|
||||||
|
ast_free(perm->command);
|
||||||
|
ast_free(perm);
|
||||||
|
}
|
||||||
|
ast_free(user_perm);
|
||||||
|
}
|
||||||
|
AST_RWLIST_UNLOCK(&cli_perms);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ast_cli_perms_init(int reload) {
|
||||||
|
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
|
||||||
|
struct ast_config *cfg;
|
||||||
|
char *cat = NULL;
|
||||||
|
struct ast_variable *v;
|
||||||
|
struct usergroup_cli_perm *user_group, *cp_entry;
|
||||||
|
struct cli_perm *perm = NULL;
|
||||||
|
struct passwd *pw;
|
||||||
|
struct group *gr;
|
||||||
|
|
||||||
|
if (ast_mutex_trylock(&permsconfiglock)) {
|
||||||
|
ast_log(LOG_NOTICE, "You must wait until last 'cli reload permissions' command finish\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg = ast_config_load2(perms_config, "" /* core, can't reload */, config_flags);
|
||||||
|
if (!cfg) {
|
||||||
|
ast_log (LOG_WARNING, "No cli permissions file found (%s)\n", perms_config);
|
||||||
|
ast_mutex_unlock(&permsconfiglock);
|
||||||
|
return 1;
|
||||||
|
} else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
|
||||||
|
ast_mutex_unlock(&permsconfiglock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* free current structures. */
|
||||||
|
destroy_user_perms();
|
||||||
|
|
||||||
|
while ((cat = ast_category_browse(cfg, cat))) {
|
||||||
|
if (!strcasecmp(cat, "general")) {
|
||||||
|
/* General options */
|
||||||
|
for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
|
||||||
|
if (!strcasecmp(v->name, "default_perm")) {
|
||||||
|
cli_default_perm = (!strcasecmp(v->value, "permit")) ? 1: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* users or groups */
|
||||||
|
gr = NULL, pw = NULL;
|
||||||
|
if (cat[0] == '@') {
|
||||||
|
/* This is a group */
|
||||||
|
gr = getgrnam(&cat[1]);
|
||||||
|
if (!gr) {
|
||||||
|
ast_log (LOG_WARNING, "Unknown group '%s'\n", &cat[1]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* This is a user */
|
||||||
|
pw = getpwnam(cat);
|
||||||
|
if (!pw) {
|
||||||
|
ast_log (LOG_WARNING, "Unknown user '%s'\n", cat);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
user_group = NULL;
|
||||||
|
/* Check for duplicates */
|
||||||
|
AST_RWLIST_WRLOCK(&cli_perms);
|
||||||
|
AST_LIST_TRAVERSE(&cli_perms, cp_entry, list) {
|
||||||
|
if ((pw && cp_entry->uid == pw->pw_uid) || (gr && cp_entry->gid == gr->gr_gid)) {
|
||||||
|
/* if it is duplicated, just added this new settings, to
|
||||||
|
the current list. */
|
||||||
|
user_group = cp_entry;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AST_RWLIST_UNLOCK(&cli_perms);
|
||||||
|
|
||||||
|
if (!user_group) {
|
||||||
|
/* alloc space for the new user config. */
|
||||||
|
user_group = ast_calloc(1, sizeof(*user_group));
|
||||||
|
if (!user_group) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
user_group->uid = (pw ? pw->pw_uid : -1);
|
||||||
|
user_group->gid = (gr ? gr->gr_gid : -1);
|
||||||
|
user_group->perms = ast_calloc(1, sizeof(*user_group->perms));
|
||||||
|
if (!user_group->perms) {
|
||||||
|
ast_free(user_group);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
|
||||||
|
if (ast_strlen_zero(v->value)) {
|
||||||
|
/* we need to check this condition cause it could break security. */
|
||||||
|
ast_log(LOG_WARNING, "Empty permit/deny option in user '%s'\n", cat);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcasecmp(v->name, "permit")) {
|
||||||
|
perm = ast_calloc(1, sizeof(*perm));
|
||||||
|
if (perm) {
|
||||||
|
perm->permit = 1;
|
||||||
|
perm->command = ast_strdup(v->value);
|
||||||
|
}
|
||||||
|
} else if (!strcasecmp(v->name, "deny")) {
|
||||||
|
perm = ast_calloc(1, sizeof(*perm));
|
||||||
|
if (perm) {
|
||||||
|
perm->permit = 0;
|
||||||
|
perm->command = ast_strdup(v->value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* up to now, only 'permit' and 'deny' are possible values. */
|
||||||
|
ast_log(LOG_WARNING, "Unknown '%s' option\n", v->name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (perm) {
|
||||||
|
/* Added the permission to the user's list. */
|
||||||
|
AST_LIST_INSERT_TAIL(user_group->perms, perm, list);
|
||||||
|
perm = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AST_RWLIST_WRLOCK(&cli_perms);
|
||||||
|
AST_RWLIST_INSERT_TAIL(&cli_perms, user_group, list);
|
||||||
|
AST_RWLIST_UNLOCK(&cli_perms);
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_config_destroy(cfg);
|
||||||
|
ast_mutex_unlock(&permsconfiglock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*! \brief initialize the _full_cmd string in * each of the builtins. */
|
/*! \brief initialize the _full_cmd string in * each of the builtins. */
|
||||||
void ast_builtins_init(void)
|
void ast_builtins_init(void)
|
||||||
{
|
{
|
||||||
ast_cli_register_multiple(cli_cli, sizeof(cli_cli) / sizeof(struct ast_cli_entry));
|
ast_cli_register_multiple(cli_cli, sizeof(cli_cli) / sizeof(struct ast_cli_entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct ast_cli_entry *cli_next(struct ast_cli_entry *e)
|
|
||||||
{
|
|
||||||
if (e) {
|
|
||||||
return AST_LIST_NEXT(e, list);
|
|
||||||
} else {
|
|
||||||
return AST_LIST_FIRST(&helpers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* match a word in the CLI entry.
|
* match a word in the CLI entry.
|
||||||
* returns -1 on mismatch, 0 on match of an optional word,
|
* returns -1 on mismatch, 0 on match of an optional word,
|
||||||
@@ -1795,12 +2179,13 @@ char *ast_cli_generator(const char *text, const char *word, int state)
|
|||||||
return __ast_cli_generator(text, word, state, 1);
|
return __ast_cli_generator(text, word, state, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ast_cli_command(int fd, const char *s)
|
int ast_cli_command_full(int uid, int gid, int fd, const char *s)
|
||||||
{
|
{
|
||||||
char *args[AST_MAX_ARGS + 1];
|
char *args[AST_MAX_ARGS + 1];
|
||||||
struct ast_cli_entry *e;
|
struct ast_cli_entry *e;
|
||||||
int x;
|
int x;
|
||||||
char *duplicate = parse_args(s, &x, args + 1, AST_MAX_ARGS, NULL);
|
char *duplicate = parse_args(s, &x, args + 1, AST_MAX_ARGS, NULL);
|
||||||
|
char tmp[AST_MAX_ARGS + 1];
|
||||||
char *retval = NULL;
|
char *retval = NULL;
|
||||||
struct ast_cli_args a = {
|
struct ast_cli_args a = {
|
||||||
.fd = fd, .argc = x, .argv = args+1 };
|
.fd = fd, .argc = x, .argv = args+1 };
|
||||||
@@ -1820,6 +2205,15 @@ int ast_cli_command(int fd, const char *s)
|
|||||||
ast_cli(fd, "No such command '%s' (type 'core show help %s' for other possible commands)\n", s, find_best(args + 1));
|
ast_cli(fd, "No such command '%s' (type 'core show help %s' for other possible commands)\n", s, find_best(args + 1));
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ast_join(tmp, sizeof(tmp), args + 1);
|
||||||
|
/* Check if the user has rights to run this command. */
|
||||||
|
if (!cli_has_permissions(uid, gid, tmp)) {
|
||||||
|
ast_cli(fd, "You don't have permissions to run '%s' command\n", tmp);
|
||||||
|
ast_free(duplicate);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Within the handler, argv[-1] contains a pointer to the ast_cli_entry.
|
* Within the handler, argv[-1] contains a pointer to the ast_cli_entry.
|
||||||
* Remember that the array returned by parse_args is NULL-terminated.
|
* Remember that the array returned by parse_args is NULL-terminated.
|
||||||
@@ -1840,7 +2234,7 @@ done:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ast_cli_command_multiple(int fd, size_t size, const char *s)
|
int ast_cli_command_multiple_full(int uid, int gid, int fd, size_t size, const char *s)
|
||||||
{
|
{
|
||||||
char cmd[512];
|
char cmd[512];
|
||||||
int x, y = 0, count = 0;
|
int x, y = 0, count = 0;
|
||||||
@@ -1849,7 +2243,7 @@ int ast_cli_command_multiple(int fd, size_t size, const char *s)
|
|||||||
cmd[y] = s[x];
|
cmd[y] = s[x];
|
||||||
y++;
|
y++;
|
||||||
if (s[x] == '\0') {
|
if (s[x] == '\0') {
|
||||||
ast_cli_command(fd, cmd);
|
ast_cli_command_full(uid, gid, fd, cmd);
|
||||||
y = 0;
|
y = 0;
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user