Merge branch 'setuid-wrapper-readlink'

This commit is contained in:
Shea Levy 2018-03-07 12:44:21 -05:00
commit 6dab907ebe
No known key found for this signature in database
GPG Key ID: 5C0BD6957D86FE27

View File

@ -1,3 +1,4 @@
#define _GNU_SOURCE
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@ -161,22 +162,34 @@ static int make_caps_ambient(const char *selfPath)
int main(int argc, char * * argv) int main(int argc, char * * argv)
{ {
// I *think* it's safe to assume that a path from a symbolic link // O_PATH | O_NOFOLLOW gives us a fd pointing to the symlink
// should safely fit within the PATH_MAX system limit. Though I'm int selfExe = open("/proc/self/exe", O_PATH | O_CLOEXEC | O_NOFOLLOW);
// not positive it's safe... assert(selfExe != -1);
char selfPath[PATH_MAX]; struct stat st;
int selfPathSize = readlink("/proc/self/exe", selfPath, sizeof(selfPath)); assert(fstat(selfExe, &st) != -1);
size_t selfPathCap = st.st_size + 1;
char *selfPath = malloc(selfPathCap);
assert(selfPath);
int selfPathSize = readlinkat(selfExe, "", selfPath, selfPathCap);
assert(selfPathSize > 0); assert(selfPathSize > 0);
// Assert we have room for the zero byte, this ensures the path // Assert we have room for the zero byte, this ensures the path
// isn't being truncated because it's too big for the buffer. // isn't being truncated because it's too big for the buffer.
// //
// A better way to handle this might be to use something like the // selfPathSize is the number of bytes readlinkat put into the
// whereami library (https://github.com/gpakosz/whereami) or a // buffer, which does *not* append a null byte. selfPathCap is the
// loop that resizes the buffer and re-reads the link if the // capacity of the buffer, which was set to the number of bytes in
// contents are being truncated. // the link contents (again, without the null byte) plus one for
assert(selfPathSize < sizeof(selfPath)); // the null byte.
//
// I don't think it's possible for the link contents to change
// between opening a symlink fd and readlinkat on it, so this is
// probably not necessary. Doubly so since this is /proc/self/exe,
// not a normal symlink. But it's trivial to check.
assert(selfPathSize < selfPathCap);
assert(close(selfExe));
// Set the zero byte since readlink doesn't do that for us. // Set the zero byte since readlink doesn't do that for us.
selfPath[selfPathSize] = '\0'; selfPath[selfPathSize] = '\0';
@ -197,7 +210,6 @@ int main(int argc, char * * argv)
// `selfPath', and not, say, as some other setuid program. That // `selfPath', and not, say, as some other setuid program. That
// is, our effective uid/gid should match the uid/gid of // is, our effective uid/gid should match the uid/gid of
// `selfPath'. // `selfPath'.
struct stat st;
assert(lstat(selfPath, &st) != -1); assert(lstat(selfPath, &st) != -1);
assert(!(st.st_mode & S_ISUID) || (st.st_uid == geteuid())); assert(!(st.st_mode & S_ISUID) || (st.st_uid == geteuid()));
@ -228,6 +240,8 @@ int main(int argc, char * * argv)
// capabilities too! // capabilities too!
make_caps_ambient(selfPath); make_caps_ambient(selfPath);
free(selfPath);
execve(sourceProg, argv, environ); execve(sourceProg, argv, environ);
fprintf(stderr, "%s: cannot run `%s': %s\n", fprintf(stderr, "%s: cannot run `%s': %s\n",