diff options
author | Andrew G. Morgan <morgan@kernel.org> | 2020-12-05 19:35:33 -0800 |
---|---|---|
committer | Andrew G. Morgan <morgan@kernel.org> | 2020-12-07 20:27:32 -0800 |
commit | 6e985f8b53ee9cddfc7466c8b8beeb187cc707d0 (patch) | |
tree | 56576aa61ef9c86b77461f0001035a08c40e3fce /goapps/gowns | |
parent | 21253638811cdf0f2719e26a61bd914d090d4f28 (diff) | |
download | libcap2-6e985f8b53ee9cddfc7466c8b8beeb187cc707d0.tar.gz |
Clean up gowns invocation for shell etc.
Also, simplify how to set a range of uids/gids from the commandline.
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
Diffstat (limited to 'goapps/gowns')
-rw-r--r-- | goapps/gowns/gowns.go | 114 |
1 files changed, 86 insertions, 28 deletions
diff --git a/goapps/gowns/gowns.go b/goapps/gowns/gowns.go index 9c77a6e..e82ba07 100644 --- a/goapps/gowns/gowns.go +++ b/goapps/gowns/gowns.go @@ -24,21 +24,57 @@ type nsDetail struct { } var ( - cmd = flag.String("cmd", "/bin/bash", "simple space separated command") - uid = flag.Int("uid", -1, "uid of the hosting user") gid = flag.Int("gid", -1, "gid of the hosting user") iab = flag.String("iab", "", "IAB string for inheritable capabilities") mode = flag.String("mode", "", "force a libcap mode (capsh --modes for list)") - ns = flag.Bool("ns", false, "enable namespace features") - uidBase = flag.Int("uid-base", 65536, "base for mapped NS UIDs 1...") - uids = flag.Int("uids", 100, "number of UIDs to map (req. CAP_SETUID)") - gidBase = flag.Int("gid-base", 65536, "base for mapped NS GIDs 1...") - gids = flag.Int("gids", 100, "number of GIDs to map (req. CAP_SETGID)") - debug = flag.Bool("verbose", false, "more verbose output") + ns = flag.Bool("ns", false, "enable user namespace features") + uids = flag.String("uids", "", "comma separated UID ranges to map contiguously (req. CAP_SETUID)") + gids = flag.String("gids", "", "comma separated GID ranges to map contiguously (req. CAP_SETGID)") + + shell = flag.String("shell", "/bin/bash", "shell to be launched") + debug = flag.Bool("verbose", false, "more verbose output") ) +// r holds a base and count for a contiguous range. +type r struct { + base, count int +} + +// ranges unpacks numerical ranges. +func ranges(s string) []r { + if s == "" { + return nil + } + var rs []r + for _, n := range strings.Split(s, ",") { + var base, upper int + if _, err := fmt.Sscanf(n, "%d-%d", &base, &upper); err == nil { + if upper < base { + log.Fatalf("invalid range: [%d-%d]", base, upper) + } + rs = append(rs, r{ + base: base, + count: 1 + upper - base, + }) + } else if _, err := fmt.Sscanf(n, "%d", &base); err == nil { + rs = append(rs, r{ + base: base, + count: 1, + }) + } else { + log.Fatalf("unable to parse range [%s]", n) + } + } + return rs +} + +// restart launches the program again with the remaining arguments. +func restart() { + log.Fatalf("failed to restart: flags: %q %q", os.Args[0], flag.Args()[1:]) +} + // errUnableToSetup is how nsSetup fails. var errUnableToSetup = errors.New("data was not in supported format") @@ -65,13 +101,17 @@ func nsSetup(pa *syscall.ProcAttr, data interface{}) error { }) if able, err := have.GetFlag(cap.Effective, cap.SETUID); err != nil { log.Fatalf("cap package SETUID error: %v", err) - } else if able && *uids > 1 { - sys.UidMappings = append(pa.Sys.UidMappings, - syscall.SysProcIDMap{ - ContainerID: 1, - HostID: *uidBase, - Size: *uids - 1, - }) + } else if able { + base := 1 + for _, next := range ranges(*uids) { + sys.UidMappings = append(pa.Sys.UidMappings, + syscall.SysProcIDMap{ + ContainerID: base, + HostID: next.base, + Size: next.count, + }) + base += next.count + } } sys.GidMappings = append(pa.Sys.GidMappings, @@ -82,13 +122,17 @@ func nsSetup(pa *syscall.ProcAttr, data interface{}) error { }) if able, err := have.GetFlag(cap.Effective, cap.SETGID); err != nil { log.Fatalf("cap package SETGID error: %v", err) - } else if able && *gids > 1 { - sys.GidMappings = append(pa.Sys.GidMappings, - syscall.SysProcIDMap{ - ContainerID: 1, - HostID: *gidBase, - Size: *gids - 1, - }) + } else if able { + base := 1 + for _, next := range ranges(*gids) { + sys.GidMappings = append(pa.Sys.GidMappings, + syscall.SysProcIDMap{ + ContainerID: base, + HostID: next.base, + Size: next.count, + }) + base += next.count + } } return nil } @@ -101,12 +145,22 @@ func main() { gid: syscall.Getgid(), } - args := strings.Split(*cmd, " ") - if len(args) == 0 { - log.Fatal("--cmd cannot be empty") + unparsed := flag.Args() + + arg0 := *shell + skip := 0 + var w *cap.Launcher + if len(unparsed) > 0 { + switch unparsed[0] { + case "==": + arg0 = os.Args[0] + skip++ + } } - w := cap.NewLauncher(args[0], args, nil) + + w = cap.NewLauncher(arg0, append([]string{arg0}, unparsed[skip:]...), nil) if *ns { + // Include the namespace setup callback with the launcher. w.Callback(nsSetup) } @@ -154,8 +208,12 @@ func main() { log.Fatalf("privilege assertion failed: %v", err) } - if *ns && *debug { - fmt.Println("launching:", detail.uid, "-> root ...") + if *debug { + if *ns { + fmt.Println("launching:", detail.uid, "-> root ...") + } else { + fmt.Println("launching without namespace") + } } pid, err := w.Launch(detail) |