Commit fd2d502e41
Changed files (4)
std/os/child_process.zig
@@ -40,6 +40,9 @@ pub const ChildProcess = struct {
/// Set to change the user id when spawning the child process.
pub uid: ?u32,
+ /// Set to change the group id when spawning the child process.
+ pub gid: ?u32,
+
/// Set to change the current working directory when spawning the child process.
pub cwd: ?[]const u8,
@@ -77,6 +80,7 @@ pub const ChildProcess = struct {
.env_map = null,
.cwd = null,
.uid = null,
+ .gid = null,
.stdin = null,
.stdout = null,
.stderr = null,
@@ -89,7 +93,9 @@ pub const ChildProcess = struct {
}
pub fn setUserName(self: &ChildProcess, name: []const u8) -> %void {
- self.uid = %return os.getUserId(name);
+ const user_info = %return os.getUserInfo(name);
+ self.uid = user_info.uid;
+ self.gid = user_info.gid;
}
/// onTerm can be called before `spawn` returns.
@@ -294,7 +300,11 @@ pub const ChildProcess = struct {
}
if (self.uid) |uid| {
- os.posix_setuid(uid) %% |err| forkChildErrReport(err_pipe[1], err);
+ os.posix_setreuid(uid, uid) %% |err| forkChildErrReport(err_pipe[1], err);
+ }
+
+ if (self.gid) |gid| {
+ os.posix_setregid(gid, gid) %% |err| forkChildErrReport(err_pipe[1], err);
}
os.posixExecve(self.argv, env_map, self.allocator) %%
std/os/get_user_id.zig
@@ -3,10 +3,15 @@ const Os = builtin.Os;
const os = @import("index.zig");
const io = @import("../io.zig");
+pub const UserInfo = struct {
+ uid: u32,
+ gid: u32,
+};
+
/// POSIX function which gets a uid from username.
-pub fn getUserId(name: []const u8) -> %u32 {
+pub fn getUserInfo(name: []const u8) -> %UserInfo {
return switch (builtin.os) {
- Os.linux, Os.darwin, Os.macosx, Os.ios => posixGetUserId(name),
+ Os.linux, Os.darwin, Os.macosx, Os.ios => posixGetUserInfo(name),
else => @compileError("Unsupported OS"),
};
}
@@ -15,13 +20,17 @@ const State = enum {
Start,
WaitForNextLine,
SkipPassword,
- ReadId,
+ ReadUserId,
+ ReadGroupId,
};
error UserNotFound;
error CorruptPasswordFile;
-pub fn posixGetUserId(name: []const u8) -> %u32 {
+// TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else
+// like NIS, AD, etc. See `man nss` or look at an strace for `id myuser`.
+
+pub fn posixGetUserInfo(name: []const u8) -> %UserInfo {
var in_stream = %return io.InStream.open("/etc/passwd", null);
defer in_stream.close();
@@ -29,6 +38,7 @@ pub fn posixGetUserId(name: []const u8) -> %u32 {
var name_index: usize = 0;
var state = State.Start;
var uid: u32 = 0;
+ var gid: u32 = 0;
while (true) {
const amt_read = %return in_stream.read(buf[0..]);
@@ -56,12 +66,15 @@ pub fn posixGetUserId(name: []const u8) -> %u32 {
State.SkipPassword => switch (byte) {
'\n' => return error.CorruptPasswordFile,
':' => {
- state = State.ReadId;
+ state = State.ReadUserId;
},
else => continue,
},
- State.ReadId => switch (byte) {
- '\n', ':' => return uid,
+ State.ReadUserId => switch (byte) {
+ ':' => {
+ state = State.ReadGroupId;
+ },
+ '\n' => return error.CorruptPasswordFile,
else => {
const digit = switch (byte) {
'0' ... '9' => byte - '0',
@@ -71,6 +84,22 @@ pub fn posixGetUserId(name: []const u8) -> %u32 {
if (@addWithOverflow(u32, uid, digit, &uid)) return error.CorruptPasswordFile;
},
},
+ State.ReadGroupId => switch (byte) {
+ '\n', ':' => {
+ return UserInfo {
+ .uid = uid,
+ .gid = gid,
+ };
+ },
+ else => {
+ const digit = switch (byte) {
+ '0' ... '9' => byte - '0',
+ else => return error.CorruptPasswordFile,
+ };
+ if (@mulWithOverflow(u32, gid, 10, &gid)) return error.CorruptPasswordFile;
+ if (@addWithOverflow(u32, gid, digit, &gid)) return error.CorruptPasswordFile;
+ },
+ },
}
}
if (amt_read < buf.len) return error.UserNotFound;
std/os/index.zig
@@ -20,7 +20,8 @@ pub const line_sep = switch (builtin.os) {
pub const page_size = 4 * 1024;
-pub const getUserId = @import("get_user_id.zig").getUserId;
+pub const UserInfo = @import("get_user_id.zig").UserInfo;
+pub const getUserInfo = @import("get_user_id.zig").getUserInfo;
const debug = @import("../debug.zig");
const assert = debug.assert;
@@ -999,3 +1000,36 @@ pub fn posix_setuid(uid: u32) -> %void {
else => error.Unexpected,
};
}
+
+pub fn posix_setreuid(ruid: u32, euid: u32) -> %void {
+ const err = posix.getErrno(posix.setreuid(ruid, euid));
+ if (err == 0) return;
+ return switch (err) {
+ posix.EAGAIN => error.ResourceLimitReached,
+ posix.EINVAL => error.InvalidUserId,
+ posix.EPERM => error.PermissionDenied,
+ else => error.Unexpected,
+ };
+}
+
+pub fn posix_setgid(gid: u32) -> %void {
+ const err = posix.getErrno(posix.setgid(gid));
+ if (err == 0) return;
+ return switch (err) {
+ posix.EAGAIN => error.ResourceLimitReached,
+ posix.EINVAL => error.InvalidUserId,
+ posix.EPERM => error.PermissionDenied,
+ else => error.Unexpected,
+ };
+}
+
+pub fn posix_setregid(rgid: u32, egid: u32) -> %void {
+ const err = posix.getErrno(posix.setregid(rgid, egid));
+ if (err == 0) return;
+ return switch (err) {
+ posix.EAGAIN => error.ResourceLimitReached,
+ posix.EINVAL => error.InvalidUserId,
+ posix.EPERM => error.PermissionDenied,
+ else => error.Unexpected,
+ };
+}
std/os/linux.zig
@@ -484,6 +484,18 @@ pub fn setuid(uid: u32) -> usize {
arch.syscall1(arch.SYS_setuid, uid)
}
+pub fn setgid(gid: u32) -> usize {
+ arch.syscall1(arch.SYS_setgid, gid)
+}
+
+pub fn setreuid(ruid: u32, euid: u32) -> usize {
+ arch.syscall2(arch.SYS_setreuid, ruid, euid)
+}
+
+pub fn setregid(rgid: u32, egid: u32) -> usize {
+ arch.syscall2(arch.SYS_setregid, rgid, egid)
+}
+
pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) -> usize {
arch.syscall4(arch.SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8)
}