Move hash table to common/ directory
[lttng-tools.git] / librunas / runas.c
index 24a772ccf2f9c253d3d0a0635783c5ab947f6b89..f612ccc7554590a44fdeca167a33f29d4b0ee0d0 100644 (file)
 #include <sys/stat.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <sched.h>
+#include <sys/mman.h>
 
 #include <lttngerr.h>
 
+#define CHILD_STACK_SIZE       10485760
+
+struct run_as_data {
+       int (*cmd)(void *data);
+       void *data;
+       uid_t uid;
+       gid_t gid;
+       int retval_pipe;
+};
+
 struct mkdir_data {
        const char *path;
        mode_t mode;
@@ -115,11 +127,73 @@ int _open(void *_data)
        return open(data->path, data->flags, data->mode);
 }
 
+static
+int child_run_as(void *_data)
+{
+       struct run_as_data *data = _data;
+       size_t writelen, writeleft, index;
+       union {
+               int i;
+               char c[sizeof(int)];
+       } sendret;
+       int ret;
+
+       /*
+        * Child: it is safe to drop egid and euid while sharing the
+        * file descriptors with the parent process, since we do not
+        * drop "uid": therefore, the user we are dropping egid/euid to
+        * cannot attach to this process with, e.g. ptrace, nor map this
+        * process memory.
+        */
+       if (data->gid != getegid()) {
+               ret = setegid(data->gid);
+               if (ret < 0) {
+                       perror("setegid");
+                       return EXIT_FAILURE;
+               }
+       }
+       if (data->uid != geteuid()) {
+               ret = seteuid(data->uid);
+               if (ret < 0) {
+                       perror("seteuid");
+                       return EXIT_FAILURE;
+               }
+       }
+       /*
+        * Also set umask to 0 for mkdir executable bit.
+        */
+       umask(0);
+       sendret.i = (*data->cmd)(data->data);
+       /* send back return value */
+       writeleft = sizeof(sendret);
+       index = 0;
+       do {
+               writelen = write(data->retval_pipe, &sendret.c[index],
+                               writeleft);
+               if (writelen < 0) {
+                       perror("write");
+                       return EXIT_FAILURE;
+               }
+               writeleft -= writelen;
+               index += writelen;
+       } while (writeleft > 0);
+       return EXIT_SUCCESS;
+}
+
 static
 int run_as(int (*cmd)(void *data), void *data, uid_t uid, gid_t gid)
 {
+       struct run_as_data run_as_data;
        int ret = 0;
+       int status;
        pid_t pid;
+       int retval_pipe[2];
+       ssize_t readlen, readleft, index;
+       void *child_stack;
+       union {
+               int i;
+               char c[sizeof(int)];
+       } retval;
 
        /*
         * If we are non-root, we can only deal with our own uid.
@@ -130,46 +204,72 @@ int run_as(int (*cmd)(void *data), void *data, uid_t uid, gid_t gid)
                                uid, geteuid());
                        return -EPERM;
                }
-               return (*cmd)(data);
        }
 
-       pid = fork();
-       if (pid > 0) {
-               int status;
-               /*
-                * Parent: wait for child to return, in which case the
-                * shared memory map will have been created.
-                */
-               pid = wait(&status);
-               if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
-                       ret = -1;
-                       goto end;
-               }
+       ret = pipe(retval_pipe);
+       if (ret < 0) {
+               perror("pipe");
                goto end;
-       } else if (pid == 0) {
-               /* Child */
-               setegid(gid);
-               if (ret < 0) {
-                       perror("setegid");
-                       exit(EXIT_FAILURE);
-               }
-               ret = seteuid(uid);
-               if (ret < 0) {
-                       perror("seteuid");
-                       exit(EXIT_FAILURE);
+       }
+       run_as_data.data = data;
+       run_as_data.cmd = cmd;
+       run_as_data.uid = uid;
+       run_as_data.gid = gid;
+       run_as_data.retval_pipe = retval_pipe[1];       /* write end */
+       child_stack = mmap(NULL, CHILD_STACK_SIZE,
+               PROT_WRITE | PROT_READ,
+               MAP_PRIVATE | MAP_GROWSDOWN | MAP_ANONYMOUS | MAP_STACK,
+               -1, 0);
+       if (child_stack == MAP_FAILED) {
+               perror("mmap");
+               ret = -ENOMEM;
+               goto close_pipe;
+       }
+       /*
+        * Pointing to the middle of the stack to support architectures
+        * where the stack grows up (HPPA).
+        */
+       pid = clone(child_run_as, child_stack + (CHILD_STACK_SIZE / 2),
+               CLONE_FILES | SIGCHLD | CLONE_VM,
+               &run_as_data, NULL);
+       if (pid < 0) {
+               perror("clone");
+               ret = pid;
+               goto unmap_stack;
+       }
+       /* receive return value */
+       readleft = sizeof(retval);
+       index = 0;
+       do {
+               readlen = read(retval_pipe[0], &retval.c[index], readleft);
+               if (readlen < 0) {
+                       perror("read");
+                       ret = -1;
+                       break;
                }
-               umask(0);
-               ret = (*cmd)(data);
-               if (!ret)
-                       exit(EXIT_SUCCESS);
-               else
-                       exit(EXIT_FAILURE);
-       } else {
-               return -1;
+               readleft -= readlen;
+               index += readlen;
+       } while (readleft > 0);
+
+       /*
+        * Parent: wait for child to return, in which case the
+        * shared memory map will have been created.
+        */
+       pid = waitpid(pid, &status, 0);
+       if (pid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+               perror("wait");
+               ret = -1;
        }
+unmap_stack:
+       ret = munmap(child_stack, CHILD_STACK_SIZE);
+       if (ret < 0) {
+               perror("munmap");
+       }
+close_pipe:
+       close(retval_pipe[0]);
+       close(retval_pipe[1]);
 end:
-       return ret;
+       return retval.i;
 }
 
 int mkdir_recursive_run_as(const char *path, mode_t mode, uid_t uid, gid_t gid)
@@ -200,27 +300,12 @@ int mkdir_run_as(const char *path, mode_t mode, uid_t uid, gid_t gid)
  */
 int open_run_as(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid)
 {
-       //struct open_data data;
-       int fd, ret;
+       struct open_data data;
 
-       DBG3("open() %s with flags %d mode %d for uid %d and gid %d",
+       DBG3("open() %s with flags %X mode %d for uid %d and gid %d",
                        path, flags, mode, uid, gid);
-       fd = open(path, flags, mode);
-       if (fd < 0) {
-               perror("open");
-               return fd;
-       }
-       ret = fchown(fd, uid, gid);
-       if (ret < 0) {
-               perror("fchown");
-               close(fd);
-               return ret;
-       }
-       return fd;
-#if 0
        data.path = path;
        data.flags = flags;
        data.mode = mode;
        return run_as(_open, &data, uid, gid);
-#endif
 }
This page took 0.025294 seconds and 4 git commands to generate.