raster pushed a commit to branch master.

http://git.enlightenment.org/core/enlightenment.git/commit/?id=5cc02d6467458276a54b61f609e090c24c8b7e78

commit 5cc02d6467458276a54b61f609e090c24c8b7e78
Author: Carsten Haitzler (Rasterman) <[email protected]>
Date:   Thu Jan 16 23:05:02 2020 +0000

    Bring back e_sys just for mount, unmount and eject
    
    This reverts commit a43869cdc096413d5994b5011e57bc7d753d4885.
    
    I still need this for efm... i forgot... :( So bring back the bits we
    need until it's no longer needed...
---
 data/etc/meson.build        |  23 ++
 data/etc/sysactions.conf.in |  77 +++++
 src/bin/e_sys_l2ping.c      | 182 ++++++++++++
 src/bin/e_sys_main.c        | 711 ++++++++++++++++++++++++++++++++++++++++++++
 src/bin/meson.build         |  11 +
 5 files changed, 1004 insertions(+)

diff --git a/data/etc/meson.build b/data/etc/meson.build
index eec92a26c..726d29a88 100644
--- a/data/etc/meson.build
+++ b/data/etc/meson.build
@@ -1,3 +1,26 @@
+MOUNT     = '/bin/mount'
+UMOUNT    = '/bin/umount'
+EJECT     = '/usr/bin/eject'
+
+if host_machine.system().contains('bsd') == true
+  MOUNT     = '/sbin/mount'
+  UMOUNT    = '/sbin/umount'
+  EJECT     = '/usr/sbin/cdcontrol eject'
+endif
+
+sysactions = configuration_data()
+sysactions.set('MOUNT'    , MOUNT)
+sysactions.set('UMOUNT'   , UMOUNT)
+sysactions.set('EJECT'    , EJECT)
+
+if get_option('install-sysactions')
+  configure_file(input        : 'sysactions.conf.in',
+                 output       : 'sysactions.conf',
+                 install_dir  : join_paths(dir_sysconf, 'enlightenment'),
+                 configuration: sysactions
+                )
+endif
+
 if get_option('install-enlightenment-menu')
   install_data('e-applications.menu',
                install_dir: join_paths(dir_sysconf, 'xdg/menus')
diff --git a/data/etc/sysactions.conf.in b/data/etc/sysactions.conf.in
new file mode 100644
index 000000000..5ba74a24a
--- /dev/null
+++ b/data/etc/sysactions.conf.in
@@ -0,0 +1,77 @@
+# ENLIGHTENMENT SYSTEM ACTIONS CONFIGURATION
+#
+# This is a system configuration for allowing or denying certain users or
+# groups to be able to do certain actions that involve system restricted
+# actions such as halt, reboot, suspend, hibernate etc.
+# 
+# This file is read in order from top to bottom - the first rule to MATCH
+# will be used for a user or a group, and nothing after that is read.
+#
+# You must put all the ACTION definitons BEFORE user and group rule matches.
+# Any action definitons after a rule match has been found will be ignored.
+# This allows actions to be re-defined for different user groups, so matches
+# so the command for an action can change for matches to the rules later on.
+# 
+# Any user or group NOT matched by an allow or a deny will be ALLOWED to
+# perform the action by default (system administrators should be aware of
+# this and implement whatever policies they see fit). Generally speaking
+# a user of a workstation, desktop or laptop is intended to have such abilities
+# to perform these actions, thus the default of allow. For multi-user systems
+# the system administrator is considered capable enough to restrict what they
+# see they need to.
+# 
+# A WARNING to admins: do NOT allow access for users to this system remotely
+# UNLESS you fully trust them or you have locked down permissions to 
halt/reboot
+# suspend etc. here first. You have been warned.
+#
+# FORMAT:
+#
+# action:   nam       command to run
+#
+# user:  username  allow: halt reboot suspend hibernate
+# group: groupname deny:  *
+# group: *         deny:  *
+# user:  *         allow: suspend
+# user:  billy     allow: halt reboot
+# group: staff     deny:  halt suspend hibernate
+#
+# etc.
+#
+# user and group name can use glob matches (* == all for example) like the
+# shell. as can action names allowed or denied. 
+
+action:   /bin/mount     @MOUNT@
+action:   /bin/umount    @UMOUNT@
+action:   /usr/bin/eject @EJECT@
+
+# root is allowed to do anything - but it needs to be here explicitly anyway
+user:     root      allow: *
+# members of operator, staff and admin groups should be able to do all
+group:    operator  allow: *
+group:    staff     allow: *
+group:    admin     allow: *
+group:    sys       allow: *
+group:    wheel     allow: *
+group:    adm       allow: *
+# common "user" groups for "console users" on desktops/laptops
+group:    dialout   allow: *
+group:    disk      allow: *
+group:    adm       allow: *
+group:    cdrom     allow: *
+group:    floppy    allow: *
+group:    audio     allow: *
+group:    dip       allow: *
+group:    plugdev   allow: *
+group:    netdev    allow: *
+group:    bluetooth allow: *
+group:    video     allow: *
+group:    voice     allow: *
+group:    fax       allow: *
+group:    tty       allow: *
+# put in a list of other users and groups here that are allowed or denied etc.
+# e.g.
+# user:     myuser     allow:  *
+# user:     another    allow:  suspend hibernate
+#
+# deny everyone else by default
+user:     *         deny:  *
diff --git a/src/bin/e_sys_l2ping.c b/src/bin/e_sys_l2ping.c
new file mode 100644
index 000000000..4805f8171
--- /dev/null
+++ b/src/bin/e_sys_l2ping.c
@@ -0,0 +1,182 @@
+#include "config.h"
+
+#include <Ecore.h>
+
+#ifdef HAVE_BLUETOOTH
+#include <unistd.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <sys/select.h>
+#include <fcntl.h>
+#endif
+
+#define MAX_SZ 500
+
+double
+e_sys_l2ping(const char *bluetooth_mac, int timeout_ms)
+{
+#ifdef HAVE_BLUETOOTH
+   char send_buf[L2CAP_CMD_HDR_SIZE + MAX_SZ];
+   char recv_buf[L2CAP_CMD_HDR_SIZE + MAX_SZ];
+   char tmp[18];
+   bdaddr_t bdaddr;
+   l2cap_cmd_hdr *send_cmd;
+   l2cap_cmd_hdr *recv_cmd;
+   struct sockaddr_l2 addr;
+   socklen_t optlen;
+   double start;
+   int fd, err, size, i;
+   fd_set rfds, wfds, exfds;
+   struct timeval tv;
+
+   // Create socket
+   fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_NONBLOCK, BTPROTO_L2CAP);
+   if (fd < 0)
+     {
+        perror("Can't create socket");
+        return -1;
+     }
+
+   // Bind to local address
+   memset(&addr, 0, sizeof(addr));
+   addr.l2_family = AF_BLUETOOTH;
+   bacpy(&bdaddr, BDADDR_ANY);
+   bacpy(&addr.l2_bdaddr, &bdaddr);
+   if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+     {
+        perror("Can't bind socket");
+        close(fd);
+        return -1;
+     }
+
+   if (fcntl(fd, F_SETFL, O_NONBLOCK) != 0)
+     {
+        perror("Can't set socket to non-blocking... continue");
+     }
+
+   // Connect to remote device
+   memset(&addr, 0, sizeof(addr));
+   addr.l2_family = AF_BLUETOOTH;
+   str2ba(bluetooth_mac, &addr.l2_bdaddr);
+   if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
+     {
+        perror("Can't bind connect socket");
+        close(fd);
+        return -1;
+     }
+
+   FD_ZERO(&rfds);
+   FD_ZERO(&wfds);
+   FD_ZERO(&exfds);
+   FD_SET(fd, &wfds);
+   tv.tv_sec = timeout_ms / 1000;
+   tv.tv_usec = (timeout_ms % 1000) * 1000;
+   start = ecore_time_get();
+   err = select(fd + 1, &rfds, &wfds, &exfds, &tv);
+   if (err == 0)
+     {
+        fprintf(stderr, "Connect timeout %s\n", bluetooth_mac);
+        return -1;
+     }
+   // adjust timeout by how long we waited to connect
+   timeout_ms -= (ecore_time_get() - start) * 1000;
+   if (timeout_ms < 1) timeout_ms = 1;
+
+   // Get local address
+   memset(&addr, 0, sizeof(addr));
+   optlen = sizeof(addr);
+   if (getsockname(fd, (struct sockaddr *)&addr, &optlen) < 0)
+     {
+        perror("Can't get local address");
+        return -1;
+     }
+
+   ba2str(&addr.l2_bdaddr, tmp);
+
+   size = 44; // use std 44 byte ping size, but no more than MAX_SZ
+
+   send_cmd = (l2cap_cmd_hdr *)send_buf;
+   send_cmd->ident = 200;
+   send_cmd->len = htobs(size);
+   send_cmd->code = L2CAP_ECHO_REQ;
+   // init buffer with some content
+   for (i = 0; i < size; i++)
+     {
+        // ABCDEF....WXYZABCEF... up to "size" chars
+        send_buf[L2CAP_CMD_HDR_SIZE + i] = 'A' + (i % 26);
+     }
+   // get our time just before a send
+   start = ecore_time_get();
+   // send the ping
+   if (send(fd, send_buf, L2CAP_CMD_HDR_SIZE + size, 0) <= 0)
+     {
+        perror("Send failed");
+        return -1;
+     }
+
+   do
+     {
+        FD_ZERO(&rfds);
+        FD_ZERO(&wfds);
+        FD_ZERO(&exfds);
+        FD_SET(fd, &rfds);
+        tv.tv_sec = timeout_ms / 1000;
+        tv.tv_usec = (timeout_ms % 1000) * 1000;
+        err = select(fd + 1, &rfds, &wfds, &exfds, &tv);
+        if (err < 0)
+          {
+             perror("Select failed");
+             return -1;
+          }
+        else if (err == 0)
+          {
+             fprintf(stderr, "Select timeout %s\n", bluetooth_mac);
+             return -1;
+          }
+        err = recv(fd, recv_buf, L2CAP_CMD_HDR_SIZE + size, 0);
+        if (err == 0)
+          {
+             fprintf(stderr, "Disconnect %s\n", bluetooth_mac);
+             return -1;
+          }
+        else if (err < 0)
+          {
+             perror("Recv failed");
+             return -1;
+          }
+
+        recv_cmd = (l2cap_cmd_hdr *)recv_buf;
+        recv_cmd->len = btohs(recv_cmd->len);
+        // we only want the 200 ident response packets
+        if (recv_cmd->ident != 200) continue;
+        if (recv_cmd->code == L2CAP_COMMAND_REJ)
+          {
+             fprintf(stderr, "Peer %s doesn't do echo\n", bluetooth_mac);
+             return -1;
+          }
+        if (recv_cmd->len != size)
+          {
+             fprintf(stderr, "Size %i echo for %s does not match %i\n",
+                     recv_cmd->len, bluetooth_mac, size);
+             return -1;
+          }
+        if (memcmp(send_buf + L2CAP_CMD_HDR_SIZE,
+                   recv_buf + L2CAP_CMD_HDR_SIZE, size) != 0)
+          {
+             fprintf(stderr, "Echo response for %s data does not match sent 
data\n", bluetooth_mac);
+             return -1;
+          }
+        fprintf(stderr, "Device %s responded\n", bluetooth_mac);
+        break;
+     }
+   while (1);
+
+   close(fd);
+   // time it took to send and get our response
+   return ecore_time_get() - start;
+#else
+   (void) bluetooth_mac;
+   fprintf(stderr, "e_sys_l2ping nop mac=%s timeout=%ims\n", bluetooth_mac, 
timeout_ms);
+   return -1;
+#endif
+}
diff --git a/src/bin/e_sys_main.c b/src/bin/e_sys_main.c
new file mode 100644
index 000000000..abc2c3bff
--- /dev/null
+++ b/src/bin/e_sys_main.c
@@ -0,0 +1,711 @@
+#include "config.h"
+
+#define __USE_MISC
+#define _SVID_SOURCE
+#define _DEFAULT_SOURCE
+
+#ifdef HAVE_FEATURES_H
+# include <features.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_ENVIRON
+# define _GNU_SOURCE 1
+#endif
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <pwd.h>
+#include <grp.h>
+#include <fnmatch.h>
+#include <ctype.h>
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+#include <Eina.h>
+
+#ifdef HAVE_ENVIRON
+extern char **environ;
+#endif
+
+/* local subsystem functions */
+#ifdef HAVE_EEZE_MOUNT
+static Eina_Bool mountopts_check(const char *opts);
+static Eina_Bool mount_args_check(int argc, char **argv, const char *action);
+#endif
+static int       auth_action_ok(char *a,
+                                gid_t gid,
+                                gid_t *gl,
+                                int gn,
+                                gid_t egid);
+static int       auth_etc_enlightenment_sysactions(char *a,
+                                                   char *u,
+                                                   char **g);
+static void      auth_etc_enlightenment_sysactions_perm(char *path);
+static char     *get_word(char *s,
+                          char *d);
+/* local subsystem globals */
+static Eina_Hash *actions = NULL;
+static uid_t uid = -1;
+
+/* externally accessible functions */
+int
+main(int argc,
+     char **argv)
+{
+   int i, gn;
+   int test = 0;
+   char *action = NULL, *cmd;
+#ifdef HAVE_EEZE_MOUNT
+   Eina_Bool mnt = EINA_FALSE;
+   const char *act = NULL;
+#endif
+   gid_t gid, gl[65536], egid;
+
+   for (i = 1; i < argc; i++)
+     {
+        if ((!strcmp(argv[i], "-h")) ||
+            (!strcmp(argv[i], "-help")) ||
+            (!strcmp(argv[i], "--help")))
+          {
+             printf(
+               "This is an internal tool for Enlightenment.\n"
+               "do not use it.\n"
+               );
+             exit(0);
+          }
+     }
+   if (argc >= 3)
+     {
+        if ((argc == 3) && (!strcmp(argv[1], "-t")))
+          {
+             test = 1;
+             action = argv[2];
+          }
+#ifdef HAVE_EEZE_MOUNT
+        else
+          {
+             const char *s;
+
+             s = strrchr(argv[1], '/');
+             if ((!s) || (!s[1])) exit(1);  /* eeze always uses complete path 
*/
+             s++;
+             if (strcmp(s, "mount") && strcmp(s, "umount") && strcmp(s, 
"eject")) exit(1);
+             mnt = EINA_TRUE;
+             act = s;
+             action = argv[1];
+          }
+#endif
+     }
+   else if (argc == 2)
+     {
+        action = argv[1];
+     }
+   else
+     {
+        exit(1);
+     }
+   if (!action) exit(1);
+
+   eina_init();
+
+   uid = getuid();
+   gid = getgid();
+   egid = getegid();
+   gn = getgroups(65536, gl);
+   if (gn < 0)
+     {
+        printf("ERROR: MEMBER OF MORE THAN 65536 GROUPS\n");
+        exit(3);
+     }
+   if (setuid(0) != 0)
+     {
+        printf("ERROR: UNABLE TO ASSUME ROOT PRIVILEGES\n");
+        exit(5);
+     }
+   if (setgid(0) != 0)
+     {
+        printf("ERROR: UNABLE TO ASSUME ROOT GROUP PRIVILEGES\n");
+        exit(7);
+     }
+
+   if (!auth_action_ok(action, gid, gl, gn, egid))
+     {
+        printf("ERROR: ACTION NOT ALLOWED: %s\n", action);
+        exit(10);
+     }
+   /* we can add more levels of auth here */
+
+   /* when mounting, this will match the exact path to the exe,
+    * as required in sysactions.conf
+    * this is intentionally pedantic for security
+    */
+   cmd = eina_hash_find(actions, action);
+   if (!cmd)
+     {
+        printf("ERROR: UNDEFINED ACTION: %s\n", action);
+        exit(20);
+     }
+
+   /* sanitize environment */
+#ifdef HAVE_UNSETENV
+# define NOENV(x) unsetenv(x)
+   /* pass 1 - just nuke known dangerous env vars brutally if possible via
+    * unsetenv(). if you don't have unsetenv... there's pass 2 and 3 */
+   NOENV("IFS");
+   NOENV("CDPATH");
+   NOENV("LOCALDOMAIN");
+   NOENV("RES_OPTIONS");
+   NOENV("HOSTALIASES");
+   NOENV("NLSPATH");
+   NOENV("PATH_LOCALE");
+   NOENV("COLORTERM");
+   NOENV("LANG");
+   NOENV("LANGUAGE");
+   NOENV("LINGUAS");
+   NOENV("TERM");
+   NOENV("LD_PRELOAD");
+   NOENV("LD_LIBRARY_PATH");
+   NOENV("SHLIB_PATH");
+   NOENV("LIBPATH");
+   NOENV("AUTHSTATE");
+   NOENV("DYLD_*");
+   NOENV("KRB_CONF*");
+   NOENV("KRBCONFDIR");
+   NOENV("KRBTKFILE");
+   NOENV("KRB5_CONFIG*");
+   NOENV("KRB5_KTNAME");
+   NOENV("VAR_ACE");
+   NOENV("USR_ACE");
+   NOENV("DLC_ACE");
+   NOENV("TERMINFO");
+   NOENV("TERMINFO_DIRS");
+   NOENV("TERMPATH");
+   NOENV("TERMCAP");
+   NOENV("ENV");
+   NOENV("BASH_ENV");
+   NOENV("PS4");
+   NOENV("GLOBIGNORE");
+   NOENV("SHELLOPTS");
+   NOENV("JAVA_TOOL_OPTIONS");
+   NOENV("PERLIO_DEBUG");
+   NOENV("PERLLIB");
+   NOENV("PERL5LIB");
+   NOENV("PERL5OPT");
+   NOENV("PERL5DB");
+   NOENV("FPATH");
+   NOENV("NULLCMD");
+   NOENV("READNULLCMD");
+   NOENV("ZDOTDIR");
+   NOENV("TMPPREFIX");
+   NOENV("PYTHONPATH");
+   NOENV("PYTHONHOME");
+   NOENV("PYTHONINSPECT");
+   NOENV("RUBYLIB");
+   NOENV("RUBYOPT");
+# ifdef HAVE_ENVIRON
+   if (environ)
+     {
+        int again;
+        char *tmp, *p;
+
+        /* go over environment array again and again... safely */
+        do
+          {
+             again = 0;
+
+             /* walk through and find first entry that we don't like */
+             for (i = 0; environ[i]; i++)
+               {
+                  /* if it begins with any of these, it's possibly nasty */
+                  if ((!strncmp(environ[i], "LD_", 3)) ||
+                      (!strncmp(environ[i], "_RLD_", 5)) ||
+                      (!strncmp(environ[i], "LC_", 3)) ||
+                      (!strncmp(environ[i], "LDR_", 3)))
+                    {
+                       /* unset it */
+                       tmp = strdup(environ[i]);
+                       if (!tmp) abort();
+                       p = strchr(tmp, '=');
+                       if (!p) abort();
+                       *p = 0;
+                       NOENV(tmp);
+                       free(tmp);
+                       /* and mark our do to try again from the start in case
+                        * unsetenv changes environ ptr */
+                       again = 1;
+                       break;
+                    }
+               }
+          }
+        while (again);
+     }
+# endif
+#endif
+
+   /* pass 2 - clear entire environment so it doesn't exist at all. if you
+    * can't do this... you're possibly in trouble... but the worst is still
+    * fixed in pass 3 */
+#ifdef HAVE_CLEARENV
+   clearenv();
+#else
+# ifdef HAVE_ENVIRON
+   environ = NULL;
+# endif
+#endif
+
+   /* pass 3 - set path and ifs to minimal defaults */
+   putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin");
+   putenv("IFS= \t\n");
+
+   if ((!test)
+#ifdef HAVE_EEZE_MOUNT
+       && (!mnt)
+#endif
+       )
+     return system(cmd);
+#ifdef HAVE_EEZE_MOUNT
+   if (mnt)
+     {
+        int ret = 0;
+        const char *mp = NULL;
+        Eina_Strbuf *buf = NULL;
+
+        if (!act) exit(40);
+        if (!mount_args_check(argc, argv, act)) exit(40);
+        /* all options are deemed safe at this point, so away we go! */
+        if (!strcmp(act, "mount"))
+          {
+             struct stat s;
+
+             mp = argv[5];
+             if (stat("/media", &s))
+               {
+                  mode_t um;
+
+                  um = umask(0);
+                  if (mkdir("/media", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | 
S_IXOTH))
+                    {
+                       printf("ERROR: COULD NOT CREATE DIRECTORY /media\n");
+                       exit(40);
+                    }
+                  umask(um);
+               }
+             else if (!S_ISDIR(s.st_mode))
+               {
+                  printf("ERROR: NOT A DIRECTORY: /media\n");
+                  exit(40);
+               }
+
+             if (stat(mp, &s))
+               {
+                  mode_t um;
+
+                  um = umask(0);
+                  if (mkdir(mp, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | 
S_IXOTH))
+                    {
+                       printf("ERROR: COULD NOT CREATE DIRECTORY %s\n", mp);
+                       exit(40);
+                    }
+                  umask(um);
+               }
+             else if (!S_ISDIR(s.st_mode))
+               {
+                  printf("ERROR: NOT A DIRECTORY: %s\n", mp);
+                  exit(40);
+               }
+          }
+        buf = eina_strbuf_new();
+        if (!buf) exit(30);
+        for (i = 1; i < argc; i++)
+          eina_strbuf_append_printf(buf, "%s ", argv[i]);
+        ret = system(eina_strbuf_string_get(buf));
+        if ((!strcmp(act, "umount")) && (!ret))
+          {
+             Eina_Iterator *it;
+             char path[PATH_MAX];
+             const char *s;
+             struct stat st;
+             Eina_Bool rm = EINA_TRUE;
+
+             mp = strrchr(argv[2], '/');
+             if (!mp) return ret;
+             snprintf(path, sizeof(path), "/media%s", mp);
+             if (stat(path, &st)) return ret;
+             if (!S_ISDIR(st.st_mode)) return ret;
+             it = eina_file_ls(path);
+             EINA_ITERATOR_FOREACH(it, s)
+               {
+                  /* don't delete any directories with files in them */
+                  rm = EINA_FALSE;
+                  eina_stringshare_del(s);
+               }
+             if (rm)
+               {
+                  if (rmdir(path))
+                    printf("ERROR: COULD NOT UNLINK MOUNT POINT %s\n", path);
+               }
+          }
+        return ret;
+     }
+#endif
+   eina_shutdown();
+
+   return 0;
+}
+
+/* local subsystem functions */
+#ifdef HAVE_EEZE_MOUNT
+static Eina_Bool
+mountopts_check(const char *opts)
+{
+   char buf[64];
+   const char *p;
+   char *end;
+   unsigned long muid;
+   Eina_Bool nosuid, nodev, noexec, nuid;
+
+   nosuid = nodev = noexec = nuid = EINA_FALSE;
+
+   /* these are the only possible options which can be present here; check 
them strictly */
+   if (eina_strlcpy(buf, opts, sizeof(buf)) >= sizeof(buf)) return EINA_FALSE;
+   for (p = buf; p && p[1]; p = strchr(p + 1, ','))
+     {
+        if (p[0] == ',') p++;
+#define CMP(OPT) \
+  if (!strncmp(p, OPT, sizeof(OPT) - 1))
+
+        CMP("nosuid,")
+        {
+           nosuid = EINA_TRUE;
+           continue;
+        }
+        CMP("nodev,")
+        {
+           nodev = EINA_TRUE;
+           continue;
+        }
+        CMP("noexec,")
+        {
+           noexec = EINA_TRUE;
+           continue;
+        }
+        CMP("utf8,") continue;
+        CMP("utf8=0,") continue;
+        CMP("utf8=1,") continue;
+        CMP("iocharset=utf8,") continue;
+        CMP("uid=")
+        {
+           p += 4;
+           errno = 0;
+           muid = strtoul(p, &end, 10);
+           if (muid == ULONG_MAX) return EINA_FALSE;
+           if (errno) return EINA_FALSE;
+           if (end[0] != ',') return EINA_FALSE;
+           if (muid != uid) return EINA_FALSE;
+           nuid = EINA_TRUE;
+           continue;
+        }
+        return EINA_FALSE;
+     }
+   if ((!nosuid) || (!nodev) || (!noexec) || (!nuid)) return EINA_FALSE;
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+check_uuid(const char *uuid)
+{
+   const char *p;
+
+   for (p = uuid; p[0]; p++)
+     if ((!isalnum(*p)) && (*p != '-')) return EINA_FALSE;
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+mount_args_check(int argc, char **argv, const char *action)
+{
+   Eina_Bool opts = EINA_FALSE;
+   struct stat st;
+   const char *node;
+   char buf[PATH_MAX];
+
+   if (!strcmp(action, "mount"))
+     {
+        /* will ALWAYS be:
+           /path/to/mount -o nosuid,uid=XYZ,[utf8,] UUID=XXXX-XXXX[-XXXX-XXXX] 
/media/$devnode
+         */
+        if (argc != 6) return EINA_FALSE;
+        if (argv[2][0] == '-')
+          {
+             /* disallow any -options other than -o */
+             if (strcmp(argv[2], "-o")) return EINA_FALSE;
+             opts = mountopts_check(argv[3]);
+          }
+        if (!opts) return EINA_FALSE;
+        if (!strncmp(argv[4], "UUID=", sizeof("UUID=") - 1))
+          {
+             if (!check_uuid(argv[4] + 5)) return EINA_FALSE;
+          }
+        else
+          {
+             if (strncmp(argv[4], "/dev/", 5)) return EINA_FALSE;
+             if (stat(argv[4], &st)) return EINA_FALSE;
+          }
+
+        node = strrchr(argv[5], '/');
+        if (!node) return EINA_FALSE;
+        if (!node[1]) return EINA_FALSE;
+        if (node - argv[5] != 6) return EINA_FALSE;
+        snprintf(buf, sizeof(buf), "/dev%s", node);
+        if (stat(buf, &st)) return EINA_FALSE;
+     }
+   else if (!strcmp(action, "umount"))
+     {
+        /* will ALWAYS be:
+           /path/to/umount /dev/$devnode
+         */
+        if (argc != 3) return EINA_FALSE;
+        if (strncmp(argv[2], "/dev/", 5)) return EINA_FALSE;
+        if (stat(argv[2], &st)) return EINA_FALSE;
+        node = strrchr(argv[2], '/');
+        if (!node) return EINA_FALSE;
+        if (!node[1]) return EINA_FALSE;
+        if (node - argv[2] != 4) return EINA_FALSE;
+        /* this is good, but it prevents umounting user-mounted removable 
media;
+         * need to figure out a better way...
+         *
+           snprintf(buf, sizeof(buf), "/media%s", node);
+           if (stat(buf, &st)) return EINA_FALSE;
+           if (!S_ISDIR(st.st_mode)) return EINA_FALSE;
+         */
+     }
+   else if (!strcmp(action, "eject"))
+     {
+        /* will ALWAYS be:
+           /path/to/eject /dev/$devnode
+         */
+        if (argc != 3) return EINA_FALSE;
+        if (strncmp(argv[2], "/dev/", 5)) return EINA_FALSE;
+        if (stat(argv[2], &st)) return EINA_FALSE;
+        node = strrchr(argv[2], '/');
+        if (!node) return EINA_FALSE;
+        if (!node[1]) return EINA_FALSE;
+        if (node - argv[2] != 4) return EINA_FALSE;
+     }
+   else return EINA_FALSE;
+   return EINA_TRUE;
+}
+
+#endif
+
+static int
+auth_action_ok(char *a,
+               gid_t gid,
+               gid_t *gl,
+               int gn,
+               gid_t egid)
+{
+   struct passwd *pw;
+   struct group *gp;
+   char *usr = NULL, **grp, *g;
+   int ret, i, j;
+
+   pw = getpwuid(uid);
+   if (!pw) return 0;
+   usr = pw->pw_name;
+   if (!usr) return 0;
+   grp = alloca(sizeof(char *) * (gn + 1 + 1));
+   j = 0;
+   gp = getgrgid(gid);
+   if (gp)
+     {
+        grp[j] = gp->gr_name;
+        j++;
+     }
+   for (i = 0; i < gn; i++)
+     {
+        if (gl[i] != egid)
+          {
+             gp = getgrgid(gl[i]);
+             if (gp)
+               {
+                  g = alloca(strlen(gp->gr_name) + 1);
+                  strcpy(g, gp->gr_name);
+                  grp[j] = g;
+                  j++;
+               }
+          }
+     }
+   grp[j] = NULL;
+   /* first stage - check:
+    * PREFIX/etc/enlightenment/sysactions.conf
+    */
+   ret = auth_etc_enlightenment_sysactions(a, usr, grp);
+   if (ret == 1) return 1;
+   else if (ret == -1)
+     return 0;
+   /* the DEFAULT - allow */
+   return 1;
+}
+
+static int
+auth_etc_enlightenment_sysactions(char *a,
+                                  char *u,
+                                  char **g)
+{
+   FILE *f;
+   char file[4096], buf[4096], id[4096], ugname[4096], perm[4096], act[4096];
+   char *p, *pp, *s, **gp;
+   int ok = 0;
+   size_t len, line = 0;
+   int allow = 0;
+   int deny = 0;
+
+   snprintf(file, sizeof(file), "/etc/enlightenment/sysactions.conf");
+   f = fopen(file, "r");
+   if (!f)
+     {
+        snprintf(file, sizeof(file), PACKAGE_SYSCONF_DIR 
"/enlightenment/sysactions.conf");
+        f = fopen(file, "r");
+        if (!f) return 0;
+     }
+
+   auth_etc_enlightenment_sysactions_perm(file);
+
+   while (fgets(buf, sizeof(buf), f))
+     {
+        line++;
+        len = strlen(buf);
+        if (len < 1) continue;
+        if (buf[len - 1] == '\n') buf[len - 1] = 0;
+        /* format:
+         *
+         * # comment
+         * user:  username  [allow:|deny:] halt reboot ...
+         * group: groupname [allow:|deny:] suspend ...
+         */
+        if (buf[0] == '#') continue;
+        p = buf;
+        p = get_word(p, id);
+        p = get_word(p, ugname);
+        pp = p;
+        p = get_word(p, perm);
+        allow = 0;
+        deny = 0;
+        if (!strcmp(id, "user:"))
+          {
+             if (!fnmatch(ugname, u, 0))
+               {
+                  if (!strcmp(perm, "allow:")) allow = 1;
+                  else if (!strcmp(perm, "deny:"))
+                    deny = 1;
+                  else
+                    goto malformed;
+               }
+             else
+               continue;
+          }
+        else if (!strcmp(id, "group:"))
+          {
+             Eina_Bool matched = EINA_FALSE;
+
+             for (gp = g; *gp; gp++)
+               {
+                  if (!fnmatch(ugname, *gp, 0))
+                    {
+                       matched = EINA_TRUE;
+                       if (!strcmp(perm, "allow:")) allow = 1;
+                       else if (!strcmp(perm, "deny:"))
+                         deny = 1;
+                       else
+                         goto malformed;
+                    }
+               }
+             if (!matched) continue;
+          }
+        else if (!strcmp(id, "action:"))
+          {
+             while ((*pp) && (isspace(*pp)))
+               pp++;
+             s = eina_hash_find(actions, ugname);
+             if (s) eina_hash_del(actions, ugname, s);
+             if (!actions) actions = eina_hash_string_superfast_new(free);
+             eina_hash_add(actions, ugname, strdup(pp));
+             continue;
+          }
+        else if (id[0] == 0)
+          continue;
+        else
+          goto malformed;
+
+        for (;; )
+          {
+             p = get_word(p, act);
+             if (act[0] == 0) break;
+             if (!fnmatch(act, a, 0))
+               {
+                  if (allow) ok = 1;
+                  else if (deny)
+                    ok = -1;
+                  goto done;
+               }
+          }
+
+        continue;
+malformed:
+        printf("WARNING: %s:%zu\n"
+               "LINE: '%s'\n"
+               "MALFORMED LINE. SKIPPED.\n",
+               file, line, buf);
+     }
+done:
+   fclose(f);
+   return ok;
+}
+
+static void
+auth_etc_enlightenment_sysactions_perm(char *path)
+{
+   struct stat st;
+   if (stat(path, &st) == -1)
+     return;
+
+   if ((st.st_mode & S_IWGRP) || (st.st_mode & S_IXGRP) ||
+       (st.st_mode & S_IWOTH) || (st.st_mode & S_IXOTH))
+     {
+        printf("ERROR: CONFIGURATION FILE HAS BAD PERMISSIONS (writable by 
group and/or others)\n");
+        exit(10);
+     }
+}
+
+static char *
+get_word(char *s,
+         char *d)
+{
+   char *p1, *p2;
+
+   p1 = s;
+   p2 = d;
+   while (*p1)
+     {
+        if (p2 == d)
+          {
+             if (isspace(*p1))
+               {
+                  p1++;
+                  continue;
+               }
+          }
+        if (isspace(*p1)) break;
+        *p2 = *p1;
+        p1++;
+        p2++;
+     }
+   *p2 = 0;
+   return p1;
+}
+
diff --git a/src/bin/meson.build b/src/bin/meson.build
index f17bbb881..1de73fe21 100644
--- a/src/bin/meson.build
+++ b/src/bin/meson.build
@@ -535,6 +535,17 @@ executable('enlightenment_alert',
            install            : true
           )
 
+executable('enlightenment_sys',
+           [ 'e_sys_main.c' ],
+           include_directories: include_directories('../..'),
+           dependencies       : [dep_eina, dep_ecore, dep_bluez],
+           c_args             : suid_cflags,
+           link_args          : suid_ldflags,
+           install_dir        : dir_e_utils,
+           install            : true
+          )
+suid_exes += join_paths(dir_e_utils, 'enlightenment_sys')
+
 executable('enlightenment_ckpasswd',
            'e_ckpasswd_main.c',
            include_directories: include_directories('../..'),

-- 


Reply via email to