diff --git a/build.zig b/build.zig index d13b1da..8cd88d3 100644 --- a/build.zig +++ b/build.zig @@ -54,6 +54,7 @@ pub fn build(b: *std.Build) !void { const gid = b.option(u32, "gid", "gid to run as") orelse 1001; const groups = b.option([]const u8, "groups", "list of supplemental groups") orelse "1001"; const username = b.option([]const u8, "username", "username to run as") orelse "github"; + const homedir = b.option([]const u8, "homedir", "home directory of user") orelse "/github/home"; const tail = tail: { const tail = b.option([]const u8, "tail", "real tail binary") orelse try find(b, "tail"); if (!std.mem.eql(u8, std.fs.path.basename(tail), "coreutils")) break :tail tail; @@ -62,6 +63,12 @@ pub fn build(b: *std.Build) !void { }; const nix = b.option([]const u8, "nix", "real nix binary") orelse try find(b, "nix"); const bash = b.option([]const u8, "bash", "real bash binary") orelse try find(b, "bash"); + const sh = sh: { + const sh = b.option([]const u8, "sh", "real sh binary") orelse try find(b, "sh"); + if (!std.mem.eql(u8, std.fs.path.basename(sh), "sh")) break :sh sh; + const dir = std.fs.path.dirname(sh) orelse break :sh sh; + break :sh try std.fs.path.join(b.allocator, &.{ dir, "sh" }); + }; const options = b.addOptions(); options.addOption(u32, "uid", uid); @@ -76,9 +83,11 @@ pub fn build(b: *std.Build) !void { break :groups list.items; }); options.addOption([]const u8, "username", username); + options.addOption([]const u8, "homedir", homedir); options.addOption([]const u8, "tail", tail); options.addOption([]const u8, "nix", nix); options.addOption([]const u8, "bash", bash); + options.addOption([]const u8, "sh", sh); const execas_exe = b.addExecutable(.{ .name = b.fmt("execas-{d}", .{uid}), @@ -135,9 +144,30 @@ pub fn build(b: *std.Build) !void { b.installArtifact(bash_exe); const bash_tests = b.addTest(.{ - .root_module = tail_exe.root_module, + .root_module = bash_exe.root_module, }); const run_bash_tests = b.addRunArtifact(bash_tests); test_step.dependOn(&run_bash_tests.step); + + const sh_exe = b.addExecutable(.{ + .name = "sh", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/sh.zig"), + .target = target, + .optimize = optimize, + }), + .use_llvm = false, + .use_lld = false, + }); + sh_exe.root_module.addOptions("options", options); + + b.installArtifact(sh_exe); + + const sh_tests = b.addTest(.{ + .root_module = sh_exe.root_module, + }); + + const run_sh_tests = b.addRunArtifact(sh_tests); + test_step.dependOn(&run_sh_tests.step); } diff --git a/flake.nix b/flake.nix index 7722a84..ed17e58 100644 --- a/flake.nix +++ b/flake.nix @@ -498,6 +498,7 @@ ] ); username = "github"; + homedir = "/github/home"; zig = zig.packages.${pkgs.stdenv.hostPlatform.system}.master; }; in diff --git a/package.nix b/package.nix index e9c6c73..3b2e913 100644 --- a/package.nix +++ b/package.nix @@ -8,6 +8,7 @@ uid, gid, username, + homedir, groups, coreutils-full, bashInteractive, @@ -25,9 +26,11 @@ stdenv.mkDerivation (finalAttrs: { "-Dgid=${toString gid}" "-Dgroups=${groups}" "-Dusername=${username}" + "-Dhomedir=${homedir}" "-Dtail=${lib.getExe' coreutils-full "tail"}" "-Dnix=${lib.getExe' nix "nix"}" "-Dbash=${lib.getExe' bashInteractive "bash"}" + "-Dsh=${lib.getExe' bashInteractive "sh"}" ]; meta = { mainProgram = "execas-${toString uid}"; diff --git a/src/bash.zig b/src/bash.zig index eeea97e..25f40b5 100644 --- a/src/bash.zig +++ b/src/bash.zig @@ -10,7 +10,7 @@ pub fn main(init: std.process.Init) !void { const arena: std.mem.Allocator = init.arena.allocator(); const io = init.io; - var environ_map = try lib.fixupEnvironMap(arena, init.environ_map); + var environ_map = try lib.fixupEnvironMap(arena, init.environ_map, .user); defer environ_map.deinit(); var argv: std.ArrayList([]const u8) = .empty; @@ -26,7 +26,7 @@ pub fn main(init: std.process.Init) !void { try argv.append(arena, arg); } - try lib.setUID(); + try lib.switchToUser(); const err = std.process.replace(io, .{ .argv = argv.items, diff --git a/src/lib.zig b/src/lib.zig index 9f30dbf..a9cee89 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -4,9 +4,9 @@ const std = @import("std"); pub const fixupEnvironMap = @import("lib/env.zig").fixupEnvironMap; -pub const setUID = @import("lib/setuid.zig").setUID; +pub const switchToUser = @import("lib/switchtouser.zig").switchToUser; test { _ = @import("lib/env.zig"); - _ = @import("lib/setuid.zig"); + _ = @import("lib/switchtouser.zig"); } diff --git a/src/lib/env.zig b/src/lib/env.zig index 8dc0750..474f9da 100644 --- a/src/lib/env.zig +++ b/src/lib/env.zig @@ -4,7 +4,11 @@ const std = @import("std"); const options = @import("options"); -pub fn fixupEnvironMap(alloc: std.mem.Allocator, old: *const std.process.Environ.Map) (std.mem.Allocator.Error || std.Io.Writer.Error)!std.process.Environ.Map { +pub fn fixupEnvironMap( + alloc: std.mem.Allocator, + old: *const std.process.Environ.Map, + user: enum { root, user }, +) (std.mem.Allocator.Error || std.Io.Writer.Error)!std.process.Environ.Map { var new = try old.clone(alloc); errdefer new.deinit(); @@ -28,7 +32,16 @@ pub fn fixupEnvironMap(alloc: std.mem.Allocator, old: *const std.process.Environ try new.put("PATH", writer.written()); } - try new.put("USER", options.username); + switch (user) { + .root => { + try new.put("USER", "root"); + try new.put("HOME", "/root"); + }, + .user => { + try new.put("USER", options.username); + try new.put("HOME", "/github/home"); + }, + } return new; } diff --git a/src/lib/setuid.zig b/src/lib/switchtouser.zig similarity index 97% rename from src/lib/setuid.zig rename to src/lib/switchtouser.zig index 95b59c1..df4b15b 100644 --- a/src/lib/setuid.zig +++ b/src/lib/switchtouser.zig @@ -4,7 +4,7 @@ const std = @import("std"); const options = @import("options"); -pub fn setUID() !void { +pub fn switchToUser() !void { { const rc = std.os.linux.setgroups(options.groups.len, options.groups.ptr); switch (std.os.linux.errno(rc)) { diff --git a/src/sh.zig b/src/sh.zig new file mode 100644 index 0000000..a11339a --- /dev/null +++ b/src/sh.zig @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: © 2023 Jeffrey C. Ollie +// SPDX-License-Identifier: MIT + +const std = @import("std"); +const options = @import("options"); + +const lib = @import("lib.zig"); + +pub fn main(init: std.process.Init) !u8 { + const arena: std.mem.Allocator = init.arena.allocator(); + const io = init.io; + + var environ_map = try lib.fixupEnvironMap(arena, init.environ_map, .user); + defer environ_map.deinit(); + + var argv: std.ArrayList([]const u8) = .empty; + defer argv.deinit(arena); + + try argv.append(arena, options.sh); + + var it = try init.minimal.args.iterateAllocator(arena); + defer it.deinit(); + + _ = it.next(); + while (it.next()) |arg| { + try argv.append(arena, arg); + } + + try lib.switchToUser(); + + const err = std.process.replace(io, .{ + .argv = argv.items, + .environ_map = &environ_map, + }); + + std.debug.print("unable to execute: {t}\n", .{err}); + + return 127; +} diff --git a/src/tail.zig b/src/tail.zig index 44effa9..762401c 100644 --- a/src/tail.zig +++ b/src/tail.zig @@ -10,9 +10,6 @@ pub fn main(init: std.process.Init) !void { const arena: std.mem.Allocator = init.arena.allocator(); const io = init.io; - var environ_map = try lib.fixupEnvironMap(arena, init.environ_map); - defer environ_map.deinit(); - nix: { if (!std.mem.eql(u8, init.environ_map.get("CI") orelse break :nix, "true")) break :nix; if (!std.mem.eql(u8, init.environ_map.get("GITHUB_ACTIONS") orelse break :nix, "true")) break :nix; @@ -25,6 +22,9 @@ pub fn main(init: std.process.Init) !void { if (!std.mem.eql(u8, it.next() orelse break :nix, "/dev/null")) break :nix; if (it.next() != null) break :nix; + var environ_map = try lib.fixupEnvironMap(arena, init.environ_map, .root); + defer environ_map.deinit(); + cwd: { var dir = std.Io.Dir.openDirAbsolute(io, "/", .{}) catch break :cwd; defer dir.close(io); @@ -35,7 +35,6 @@ pub fn main(init: std.process.Init) !void { .argv = &.{ options.nix, "daemon", - "--trusted", }, .environ_map = &environ_map, }); @@ -44,6 +43,9 @@ pub fn main(init: std.process.Init) !void { return; } + var environ_map = try lib.fixupEnvironMap(arena, init.environ_map, .user); + defer environ_map.deinit(); + var argv: std.ArrayList([]const u8) = .empty; defer argv.deinit(arena); @@ -57,7 +59,7 @@ pub fn main(init: std.process.Init) !void { try argv.append(arena, arg); } - try lib.setUID(); + try lib.switchToUser(); const err = std.process.replace(io, .{ .argv = argv.items,