Implement run_as wrappers for mkdir/mkdir_recursive/open
[lttng-tools.git] / lttng-sessiond / utils.c
index 0da5642109fcc5ddf7eeced026a0d441cb685594..1e08de77d4ca2854611c6a7372a769a9c7bdeb5e 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/wait.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <fcntl.h>
 
 #include <lttngerr.h>
 
 #include "utils.h"
 
+struct mkdir_data {
+       const char *path;
+       mode_t mode;
+};
+
+struct open_data {
+       const char *path;
+       int flags;
+       mode_t mode;
+};
+
 /*
  * Write to writable pipe used to notify a thread.
  */
@@ -58,12 +71,19 @@ const char *get_home_dir(void)
 /*
  * Create recursively directory using the FULL path.
  */
-int mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid)
+static
+int _mkdir_recursive(void *_data)
 {
-       int ret;
+       struct mkdir_data *data = _data;
+       const char *path;
        char *p, tmp[PATH_MAX];
+       struct stat statbuf;
+       mode_t mode;
        size_t len;
-       mode_t old_umask;
+       int ret;
+
+       path = data->path;
+       mode = data->mode;
 
        ret = snprintf(tmp, sizeof(tmp), "%s", path);
        if (ret < 0) {
@@ -76,27 +96,18 @@ int mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid)
                tmp[len - 1] = 0;
        }
 
-       old_umask = umask(0);
        for (p = tmp + 1; *p; p++) {
                if (*p == '/') {
                        *p = 0;
-                       ret = mkdir(tmp, mode);
+                       ret = stat(tmp, &statbuf);
                        if (ret < 0) {
-                               if (!(errno == EEXIST)) {
-                                       PERROR("mkdir recursive");
-                                       ret = -errno;
-                                       goto umask_error;
-                               }
-                       } else if (ret == 0) {
-                               /*
-                                * We created the directory. Set its ownership to the
-                                * user/group specified.
-                                */
-                               ret = chown(tmp, uid, gid);
+                               ret = mkdir(tmp, mode);
                                if (ret < 0) {
-                                       PERROR("chown in mkdir recursive");
-                                       ret = -errno;
-                                       goto umask_error;
+                                       if (!(errno == EEXIST)) {
+                                               PERROR("mkdir recursive");
+                                               ret = -errno;
+                                               goto error;
+                                       }
                                }
                        }
                        *p = '/';
@@ -111,21 +122,113 @@ int mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid)
                } else {
                        ret = 0;
                }
-       } else if (ret == 0) {
+       }
+
+error:
+       return ret;
+}
+
+static
+int _mkdir(void *_data)
+{
+       struct mkdir_data *data = _data;
+       return mkdir(data->path, data->mode);
+}
+
+static
+int _open(void *_data)
+{
+       struct open_data *data = _data;
+       return open(data->path, data->flags, data->mode);
+}
+
+static
+int run_as(int (*cmd)(void *data), void *data, uid_t uid, gid_t gid)
+{
+       int ret = 0;
+       pid_t pid;
+
+       /*
+        * If we are non-root, we can only deal with our own uid.
+        */
+       if (geteuid() != 0) {
+               if (uid != geteuid()) {
+                       ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)",
+                               uid, geteuid());
+                       return -EPERM;
+               }
+               return (*cmd)(data);
+       }
+
+       pid = fork();
+       if (pid > 0) {
+               int status;
+
                /*
-                * We created the directory. Set its ownership to the user/group
-                * specified.
+                * Parent: wait for child to return, in which case the
+                * shared memory map will have been created.
                 */
-               ret = chown(tmp, uid, gid);
+               pid = wait(&status);
+               if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+                       ret = -1;
+                       goto end;
+               }
+               goto end;
+       } else if (pid == 0) {
+               /* Child */
+               setegid(gid);
                if (ret < 0) {
-                       PERROR("chown in mkdir recursive");
-                       ret = -errno;
-                       goto umask_error;
+                       perror("setegid");
+                       exit(EXIT_FAILURE);
                }
+               ret = seteuid(uid);
+               if (ret < 0) {
+                       perror("seteuid");
+                       exit(EXIT_FAILURE);
+               }
+               umask(0);
+               ret = (*cmd)(data);
+               if (!ret)
+                       exit(EXIT_SUCCESS);
+               else
+                       exit(EXIT_FAILURE);
+       } else {
+               return -1;
        }
-
-umask_error:
-       umask(old_umask);
-error:
+end:
        return ret;
 }
+
+int mkdir_recursive_run_as(const char *path, mode_t mode, uid_t uid, gid_t gid)
+{
+       struct mkdir_data data;
+
+       DBG3("mkdir() recursive %s with mode %d for uid %d and gid %d",
+                       path, mode, uid, gid);
+       data.path = path;
+       data.mode = mode;
+       return run_as(_mkdir_recursive, &data, uid, gid);
+}
+
+int mkdir_run_as(const char *path, mode_t mode, uid_t uid, gid_t gid)
+{
+       struct mkdir_data data;
+
+       DBG3("mkdir() %s with mode %d for uid %d and gid %d",
+                       path, mode, uid, gid);
+       data.path = path;
+       data.mode = mode;
+       return run_as(_mkdir, &data, uid, gid);
+}
+
+int open_run_as(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid)
+{
+       struct open_data data;
+
+       DBG3("open() %s with flags %d mode %d for uid %d and gid %d",
+                       path, flags, mode, uid, gid);
+       data.path = path;
+       data.flags = flags;
+       data.mode = mode;
+       return run_as(_open, &data, uid, gid);
+}
This page took 0.028258 seconds and 4 git commands to generate.