diff options
author | Rian Hunter | 2018-10-13 04:45:06 +0200 |
---|---|---|
committer | Karel Zak | 2018-11-30 10:25:04 +0100 |
commit | e101a9eb0fab6725e0a239a92f9b50822c494a3e (patch) | |
tree | 9866e49f670b8054bfe6fe171a98e49ee1e70097 /lib | |
parent | blkid: fix usage() (diff) | |
download | kernel-qcow2-util-linux-e101a9eb0fab6725e0a239a92f9b50822c494a3e.tar.gz kernel-qcow2-util-linux-e101a9eb0fab6725e0a239a92f9b50822c494a3e.tar.xz kernel-qcow2-util-linux-e101a9eb0fab6725e0a239a92f9b50822c494a3e.zip |
lib/canonicalize: do restricted canonicalize in a subprocess
Accessing FUSE mounts require suid/sgid (saved uid) to be equal to the
owner of the mount. If mount is running as a setuid process, swapping
creds by only setting the euid/egid isn't enough to change the
suid/sgid as well. We must do a full setuid()/setgid(), but that
removes our ability to re-assume the identity of the original
euid. The solution is swap creds in a child process, preserving the
creds of the parent.
[kzak@redhat.com: - use switch() rather than if() for fork
- use all-io.h
- close unused pipe[] ends
- be more strict about used types]
Addresses: https://github.com/karelzak/util-linux/pull/705
Co-Author: Karel Zak <kzak@redhat.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/canonicalize.c | 99 |
1 files changed, 77 insertions, 22 deletions
diff --git a/lib/canonicalize.c b/lib/canonicalize.c index f3a2a3af2..e85e0357c 100644 --- a/lib/canonicalize.c +++ b/lib/canonicalize.c @@ -14,9 +14,11 @@ #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> +#include <sys/wait.h> #include "canonicalize.h" #include "pathnames.h" +#include "all-io.h" /* * Converts private "dm-N" names to "/dev/mapper/<name>" @@ -140,39 +142,92 @@ char *canonicalize_path(const char *path) char *canonicalize_path_restricted(const char *path) { - char *canonical, *dmname; - int errsv; - uid_t euid; - gid_t egid; + char *canonical = NULL; + int errsv = 0; + int pipes[2]; + ssize_t len; + pid_t pid; if (!path || !*path) return NULL; - euid = geteuid(); - egid = getegid(); - - /* drop permissions */ - if (setegid(getgid()) < 0 || seteuid(getuid()) < 0) + if (pipe(pipes) != 0) return NULL; - errsv = errno = 0; - - canonical = realpath(path, NULL); - if (!canonical) - errsv = errno; - else if (is_dm_devname(canonical, &dmname)) { - char *dm = canonicalize_dm_name(dmname); - if (dm) { - free(canonical); - canonical = dm; + /* + * To accurately assume identity of getuid() we must use setuid() + * but if we do that, we lose ability to reassume euid of 0, so + * we fork to do the check to keep euid intact. + */ + pid = fork(); + switch (pid) { + case -1: + close(pipes[0]); + close(pipes[1]); + return NULL; /* fork error */ + case 0: + close(pipes[0]); /* close unused end */ + pipes[0] = -1; + errno = 0; + + /* drop permissions */ + if (setgid(getgid()) < 0 || setuid(getuid()) < 0) + canonical = NULL; /* failed */ + else { + char *dmname = NULL; + + canonical = realpath(path, NULL); + if (canonical && is_dm_devname(canonical, &dmname)) { + char *dm = canonicalize_dm_name(dmname); + if (dm) { + free(canonical); + canonical = dm; + } + } } + + len = canonical ? strlen(canonical) : errno ? -errno : -EINVAL; + + /* send lenght or errno */ + write_all(pipes[1], (char *) &len, sizeof(len)); + if (canonical) + write_all(pipes[1], canonical, len); + exit(0); + default: + break; } - /* restore */ - if (setegid(egid) < 0 || seteuid(euid) < 0) { + close(pipes[1]); /* close unused end */ + pipes[1] = -1; + + /* read size or -errno */ + if (read_all(pipes[0], (char *) &len, sizeof(len)) != sizeof(len)) + goto done; + if (len < 0) { + errsv = -len; + goto done; + } + + canonical = malloc(len + 1); + if (!canonical) { + errsv = ENOMEM; + goto done; + } + /* read path */ + if (read_all(pipes[0], canonical, len) != len) { + errsv = errno; + goto done; + } + canonical[len] = '\0'; +done: + if (errsv) { free(canonical); - return NULL; + canonical = NULL; } + close(pipes[0]); + + /* We make a best effort to reap child */ + waitpid(pid, NULL, 0); errno = errsv; return canonical; |