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

master
Andrew Kelley 2017-09-26 02:42:06 -04:00
parent cba4a9ad4a
commit fd2d502e41
4 changed files with 95 additions and 10 deletions

View File

@ -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) %%

View File

@ -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;

View File

@ -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,
};
}

View File

@ -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)
}