authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-09-26 02:42:06-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-09-26 02:42:06-04:00
logfd2d502e411c45828ebdf1008c5060db8749ef31
treea116b0e692198f52396e426393a9311c8652c221
parentcba4a9ad4a149766c650e3f3d71435cef14867a3

std.os.ChildProcess: ability to set both uid and gid


4 files changed, 95 insertions(+), 10 deletions(-)

std/os/child_process.zig+12-2
......@@ -40,6 +40,9 @@ pub const ChildProcess = struct {
4040 /// Set to change the user id when spawning the child process.
4141 pub uid: ?u32,
4242
43 /// Set to change the group id when spawning the child process.
44 pub gid: ?u32,
45
4346 /// Set to change the current working directory when spawning the child process.
4447 pub cwd: ?[]const u8,
4548
......@@ -77,6 +80,7 @@ pub const ChildProcess = struct {
7780 .env_map = null,
7881 .cwd = null,
7982 .uid = null,
83 .gid = null,
8084 .stdin = null,
8185 .stdout = null,
8286 .stderr = null,
......@@ -89,7 +93,9 @@ pub const ChildProcess = struct {
8993 }
9094
9195 pub fn setUserName(self: &ChildProcess, name: []const u8) -> %void {
92 self.uid = %return os.getUserId(name);
96 const user_info = %return os.getUserInfo(name);
97 self.uid = user_info.uid;
98 self.gid = user_info.gid;
9399 }
94100
95101 /// onTerm can be called before `spawn` returns.
......@@ -294,7 +300,11 @@ pub const ChildProcess = struct {
294300 }
295301
296302 if (self.uid) |uid| {
297 os.posix_setuid(uid) %% |err| forkChildErrReport(err_pipe[1], err);
303 os.posix_setreuid(uid, uid) %% |err| forkChildErrReport(err_pipe[1], err);
304 }
305
306 if (self.gid) |gid| {
307 os.posix_setregid(gid, gid) %% |err| forkChildErrReport(err_pipe[1], err);
298308 }
299309
300310 os.posixExecve(self.argv, env_map, self.allocator) %%
std/os/get_user_id.zig+36-7
......@@ -3,10 +3,15 @@ const Os = builtin.Os;
33const os = @import("index.zig");
44const io = @import("../io.zig");
55
6pub const UserInfo = struct {
7 uid: u32,
8 gid: u32,
9};
10
611/// POSIX function which gets a uid from username.
7pub fn getUserId(name: []const u8) -> %u32 {
12pub fn getUserInfo(name: []const u8) -> %UserInfo {
813 return switch (builtin.os) {
9 Os.linux, Os.darwin, Os.macosx, Os.ios => posixGetUserId(name),
14 Os.linux, Os.darwin, Os.macosx, Os.ios => posixGetUserInfo(name),
1015 else => @compileError("Unsupported OS"),
1116 };
1217}
......@@ -15,13 +20,17 @@ const State = enum {
1520 Start,
1621 WaitForNextLine,
1722 SkipPassword,
18 ReadId,
23 ReadUserId,
24 ReadGroupId,
1925};
2026
2127error UserNotFound;
2228error CorruptPasswordFile;
2329
24pub fn posixGetUserId(name: []const u8) -> %u32 {
30// TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else
31// like NIS, AD, etc. See `man nss` or look at an strace for `id myuser`.
32
33pub fn posixGetUserInfo(name: []const u8) -> %UserInfo {
2534 var in_stream = %return io.InStream.open("/etc/passwd", null);
2635 defer in_stream.close();
2736
......@@ -29,6 +38,7 @@ pub fn posixGetUserId(name: []const u8) -> %u32 {
2938 var name_index: usize = 0;
3039 var state = State.Start;
3140 var uid: u32 = 0;
41 var gid: u32 = 0;
3242
3343 while (true) {
3444 const amt_read = %return in_stream.read(buf[0..]);
......@@ -56,12 +66,15 @@ pub fn posixGetUserId(name: []const u8) -> %u32 {
5666 State.SkipPassword => switch (byte) {
5767 '\n' => return error.CorruptPasswordFile,
5868 ':' => {
59 state = State.ReadId;
69 state = State.ReadUserId;
6070 },
6171 else => continue,
6272 },
63 State.ReadId => switch (byte) {
64 '\n', ':' => return uid,
73 State.ReadUserId => switch (byte) {
74 ':' => {
75 state = State.ReadGroupId;
76 },
77 '\n' => return error.CorruptPasswordFile,
6578 else => {
6679 const digit = switch (byte) {
6780 '0' ... '9' => byte - '0',
......@@ -71,6 +84,22 @@ pub fn posixGetUserId(name: []const u8) -> %u32 {
7184 if (@addWithOverflow(u32, uid, digit, &uid)) return error.CorruptPasswordFile;
7285 },
7386 },
87 State.ReadGroupId => switch (byte) {
88 '\n', ':' => {
89 return UserInfo {
90 .uid = uid,
91 .gid = gid,
92 };
93 },
94 else => {
95 const digit = switch (byte) {
96 '0' ... '9' => byte - '0',
97 else => return error.CorruptPasswordFile,
98 };
99 if (@mulWithOverflow(u32, gid, 10, &gid)) return error.CorruptPasswordFile;
100 if (@addWithOverflow(u32, gid, digit, &gid)) return error.CorruptPasswordFile;
101 },
102 },
74103 }
75104 }
76105 if (amt_read < buf.len) return error.UserNotFound;
std/os/index.zig+35-1
......@@ -20,7 +20,8 @@ pub const line_sep = switch (builtin.os) {
2020
2121pub const page_size = 4 * 1024;
2222
23pub const getUserId = @import("get_user_id.zig").getUserId;
23pub const UserInfo = @import("get_user_id.zig").UserInfo;
24pub const getUserInfo = @import("get_user_id.zig").getUserInfo;
2425
2526const debug = @import("../debug.zig");
2627const assert = debug.assert;
......@@ -999,3 +1000,36 @@ pub fn posix_setuid(uid: u32) -> %void {
9991000 else => error.Unexpected,
10001001 };
10011002}
1003
1004pub fn posix_setreuid(ruid: u32, euid: u32) -> %void {
1005 const err = posix.getErrno(posix.setreuid(ruid, euid));
1006 if (err == 0) return;
1007 return switch (err) {
1008 posix.EAGAIN => error.ResourceLimitReached,
1009 posix.EINVAL => error.InvalidUserId,
1010 posix.EPERM => error.PermissionDenied,
1011 else => error.Unexpected,
1012 };
1013}
1014
1015pub fn posix_setgid(gid: u32) -> %void {
1016 const err = posix.getErrno(posix.setgid(gid));
1017 if (err == 0) return;
1018 return switch (err) {
1019 posix.EAGAIN => error.ResourceLimitReached,
1020 posix.EINVAL => error.InvalidUserId,
1021 posix.EPERM => error.PermissionDenied,
1022 else => error.Unexpected,
1023 };
1024}
1025
1026pub fn posix_setregid(rgid: u32, egid: u32) -> %void {
1027 const err = posix.getErrno(posix.setregid(rgid, egid));
1028 if (err == 0) return;
1029 return switch (err) {
1030 posix.EAGAIN => error.ResourceLimitReached,
1031 posix.EINVAL => error.InvalidUserId,
1032 posix.EPERM => error.PermissionDenied,
1033 else => error.Unexpected,
1034 };
1035}
std/os/linux.zig+12
......@@ -484,6 +484,18 @@ pub fn setuid(uid: u32) -> usize {
484484 arch.syscall1(arch.SYS_setuid, uid)
485485}
486486
487pub fn setgid(gid: u32) -> usize {
488 arch.syscall1(arch.SYS_setgid, gid)
489}
490
491pub fn setreuid(ruid: u32, euid: u32) -> usize {
492 arch.syscall2(arch.SYS_setreuid, ruid, euid)
493}
494
495pub fn setregid(rgid: u32, egid: u32) -> usize {
496 arch.syscall2(arch.SYS_setregid, rgid, egid)
497}
498
487499pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) -> usize {
488500 arch.syscall4(arch.SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8)
489501}