[asterisk-commits] eliel: trunk r160062 - in /trunk: ./ configs/ include/asterisk/ main/
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Mon Dec 1 12:52:14 CST 2008
Author: eliel
Date: Mon Dec 1 12:52:14 2008
New Revision: 160062
URL: http://svn.digium.com/view/asterisk?view=rev&rev=160062
Log:
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
Added:
trunk/configs/cli_permissions.conf.sample
- copied unchanged from r160061, team/eliel/cli-permissions/configs/cli_permissions.conf.sample
Modified:
trunk/CHANGES
trunk/configure
trunk/configure.ac
trunk/include/asterisk/_private.h
trunk/include/asterisk/autoconfig.h.in
trunk/include/asterisk/cli.h
trunk/main/asterisk.c
trunk/main/cli.c
Modified: trunk/CHANGES
URL: http://svn.digium.com/view/asterisk/trunk/CHANGES?view=diff&rev=160062&r1=160061&r2=160062
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Mon Dec 1 12:52:14 2008
@@ -402,6 +402,12 @@
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 settings"
* Added 'core show channels count' CLI command.
Modified: trunk/configure.ac
URL: http://svn.digium.com/view/asterisk/trunk/configure.ac?view=diff&rev=160062&r1=160061&r2=160062
==============================================================================
--- trunk/configure.ac (original)
+++ trunk/configure.ac Mon Dec 1 12:52:14 2008
@@ -336,7 +336,7 @@
AC_FUNC_STRTOD
AC_FUNC_UTIME_NULL
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])
Modified: trunk/include/asterisk/_private.h
URL: http://svn.digium.com/view/asterisk/trunk/include/asterisk/_private.h?view=diff&rev=160062&r1=160061&r2=160062
==============================================================================
--- trunk/include/asterisk/_private.h (original)
+++ trunk/include/asterisk/_private.h Mon Dec 1 12:52:14 2008
@@ -24,6 +24,7 @@
int astdb_init(void); /*!< Provided by db.c */
void ast_channels_init(void); /*!< Provided by channel.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 */
void dnsmgr_start_refresh(void); /*!< Provided by dnsmgr.c */
int dnsmgr_reload(void); /*!< Provided by dnsmgr.c */
Modified: trunk/include/asterisk/autoconfig.h.in
URL: http://svn.digium.com/view/asterisk/trunk/include/asterisk/autoconfig.h.in?view=diff&rev=160062&r1=160061&r2=160062
==============================================================================
--- trunk/include/asterisk/autoconfig.h.in (original)
+++ trunk/include/asterisk/autoconfig.h.in Mon Dec 1 12:52:14 2008
@@ -321,6 +321,9 @@
/* Define to 1 if you have the `getpagesize' function. */
#undef HAVE_GETPAGESIZE
+
+/* Define to 1 if you have the `getpeereid' function. */
+#undef HAVE_GETPEEREID
/* Define to 1 if you have the `gettimeofday' function. */
#undef HAVE_GETTIMEOFDAY
Modified: trunk/include/asterisk/cli.h
URL: http://svn.digium.com/view/asterisk/trunk/include/asterisk/cli.h?view=diff&rev=160062&r1=160061&r2=160062
==============================================================================
--- trunk/include/asterisk/cli.h (original)
+++ trunk/include/asterisk/cli.h Mon Dec 1 12:52:14 2008
@@ -31,6 +31,10 @@
void ast_cli(int fd, const char *fmt, ...)
__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_SHOWUSAGE 1
@@ -191,23 +195,35 @@
/*!
* \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 s incoming string
* \retval 0 on success
* \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
* 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 size is the total size of the string
* \param s incoming string
* \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
* \param e which cli entry to register.
Modified: trunk/main/asterisk.c
URL: http://svn.digium.com/view/asterisk/trunk/main/asterisk.c?view=diff&rev=160062&r1=160061&r2=160062
==============================================================================
--- trunk/main/asterisk.c (original)
+++ trunk/main/asterisk.c Mon Dec 1 12:52:14 2008
@@ -176,6 +176,8 @@
int p[2]; /*!< Pipe */
pthread_t t; /*!< Thread of handler */
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 */
};
@@ -988,6 +990,48 @@
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)
{
struct console *con = vconsole;
@@ -1015,19 +1059,19 @@
continue;
}
if (fds[0].revents) {
- res = read(con->fd, tmp, sizeof(tmp));
+ res = read_credentials(con->fd, tmp, sizeof(tmp), con);
if (res < 1) {
break;
}
tmp[res] = 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;
}
- 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) {
- res = read(con->p[0], tmp, sizeof(tmp));
+ res = read_credentials(con->p[0], tmp, sizeof(tmp), con);
if (res < 1) {
ast_log(LOG_ERROR, "read returned %d\n", res);
break;
@@ -1072,8 +1116,19 @@
if (errno != EINTR)
ast_log(LOG_WARNING, "Accept returned %d: %s\n", s, strerror(errno));
} else {
- for (x = 0; x < AST_MAX_CONNECTS; x++) {
- if (consoles[x].fd < 0) {
+#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++) {
+ if (consoles[x].fd >= 0) {
+ continue;
+ }
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, consoles[x].p)) {
ast_log(LOG_ERROR, "Unable to create pipe: %s\n", strerror(errno));
consoles[x].fd = -1;
@@ -1085,6 +1140,10 @@
fcntl(consoles[x].p[1], F_SETFL, flags | O_NONBLOCK);
consoles[x].fd = s;
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])) {
ast_log(LOG_ERROR, "Unable to spawn thread to handle connection: %s\n", strerror(errno));
close(consoles[x].p[0]);
@@ -1095,13 +1154,13 @@
}
break;
}
+ if (x >= AST_MAX_CONNECTS) {
+ fdprint(s, "No more connections allowed\n");
+ ast_log(LOG_WARNING, "No more connections allowed\n");
+ close(s);
+ } else if (consoles[x].fd > -1)
+ ast_verb(3, "Remote UNIX connection\n");
}
- if (x >= AST_MAX_CONNECTS) {
- fdprint(s, "No more connections allowed\n");
- ast_log(LOG_WARNING, "No more connections allowed\n");
- close(s);
- } else if (consoles[x].fd > -1)
- ast_verb(3, "Remote UNIX connection\n");
}
}
return NULL;
@@ -3350,6 +3409,9 @@
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
* conflict between issuing a module reload from manager and
* registering manager actions. This will cause reversed locking
Modified: trunk/main/cli.c
URL: http://svn.digium.com/view/asterisk/trunk/main/cli.c?view=diff&rev=160062&r1=160061&r2=160062
==============================================================================
--- trunk/main/cli.c (original)
+++ trunk/main/cli.c Mon Dec 1 12:52:14 2008
@@ -33,6 +33,8 @@
#include <signal.h>
#include <ctype.h>
#include <regex.h>
+#include <pwd.h>
+#include <grp.h>
#include "asterisk/cli.h"
#include "asterisk/linkedlists.h"
@@ -46,6 +48,35 @@
#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
*/
struct ast_debug_file {
@@ -115,6 +146,74 @@
AST_RWLIST_UNLOCK(&verbose_files);
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);
@@ -477,6 +576,15 @@
if (x > 0 || out->used == 0) /* if there is nothing, print 0 seconds */
ast_str_append(&out, 0, "%d second%s ", x, ESS(x));
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)
@@ -750,6 +858,146 @@
ast_channel_unlock(c);
} else
ast_cli(a->fd, "%s is not a known channel\n", a->argv[3]);
+ 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 at 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;
}
@@ -1176,6 +1424,12 @@
AST_CLI_DEFINE(handle_showuptime, "Show uptime information"),
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,19 +1459,149 @@
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. */
void ast_builtins_init(void)
{
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);
- }
}
/*!
@@ -1795,12 +2179,13 @@
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];
struct ast_cli_entry *e;
int x;
char *duplicate = parse_args(s, &x, args + 1, AST_MAX_ARGS, NULL);
+ char tmp[AST_MAX_ARGS + 1];
char *retval = NULL;
struct ast_cli_args a = {
.fd = fd, .argc = x, .argv = args+1 };
@@ -1820,6 +2205,15 @@
ast_cli(fd, "No such command '%s' (type 'core show help %s' for other possible commands)\n", s, find_best(args + 1));
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.
* Remember that the array returned by parse_args is NULL-terminated.
@@ -1840,7 +2234,7 @@
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];
int x, y = 0, count = 0;
@@ -1849,7 +2243,7 @@
cmd[y] = s[x];
y++;
if (s[x] == '\0') {
- ast_cli_command(fd, cmd);
+ ast_cli_command_full(uid, gid, fd, cmd);
y = 0;
count++;
}
More information about the asterisk-commits
mailing list