diff options
Diffstat (limited to 'src/unistd/setxid.c')
-rw-r--r-- | src/unistd/setxid.c | 49 |
1 files changed, 49 insertions, 0 deletions
diff --git a/src/unistd/setxid.c b/src/unistd/setxid.c new file mode 100644 index 00000000..d3bfaf62 --- /dev/null +++ b/src/unistd/setxid.c @@ -0,0 +1,49 @@ +#include <unistd.h> +#include <errno.h> +#include <sys/resource.h> +#include "syscall.h" +#include "libc.h" + +struct ctx { + int id, eid, sid; + int nr, rlim, err; +}; + +/* We jump through hoops to eliminate the possibility of partial failures. */ + +static void do_setxid(void *p) +{ + struct ctx *c = p; + if (c->err) return; + if (c->rlim && c->id >= 0 && c->id != getuid()) { + struct rlimit inf = { RLIM_INFINITY, RLIM_INFINITY }, old; + getrlimit(RLIMIT_NPROC, &old); + if (setrlimit(RLIMIT_NPROC, &inf) && libc.threads_minus_1) { + c->err = errno; + return; + } + if (__syscall(c->nr, c->id, c->eid, c->sid)) + c->err = errno; + setrlimit(RLIMIT_NPROC, &old); + return; + } + if (__syscall(c->nr, c->id, c->eid, c->sid)) + c->err = errno; +} + +int __setxid(int nr, int id, int eid, int sid) +{ + struct ctx c = { .nr = nr, .id = id, .eid = eid, .sid = sid }; + switch (nr) { + case SYS_setuid: + case SYS_setreuid: + case SYS_setresuid: + c.rlim = 1; + } + __synccall(do_setxid, &c); + if (c.err) { + errno = c.err; + return -1; + } + return 0; +} |