diff --git a/playbooks/dependencies.yaml b/playbooks/dependencies.yaml index 2aa51111b..1d99607bb 100644 --- a/playbooks/dependencies.yaml +++ b/playbooks/dependencies.yaml @@ -31,6 +31,7 @@ - ninja-build - openssl - podman + - shadow-utils-subid-devel - skopeo - systemd - udisks2 diff --git a/src/cmd/root.go b/src/cmd/root.go index 1d9adaefe..62a60a050 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -16,8 +16,26 @@ package cmd +/* +#cgo LDFLAGS: -l subid +#include +#include +#include +const char *Prog = "storage"; +FILE *shadow_logfd = NULL; +struct subid_range get_range(struct subid_range *ranges, int i) +{ + shadow_logfd = stderr; + return ranges[i]; +} +#if !defined(SUBID_ABI_MAJOR) || (SUBID_ABI_MAJOR < 4) +# define subid_get_uid_ranges get_subuid_ranges +# define subid_get_gid_ranges get_subgid_ranges +#endif +*/ +import "C" + import ( - "bufio" "errors" "fmt" "io/ioutil" @@ -26,6 +44,7 @@ import ( "path/filepath" "strings" "syscall" + "unsafe" "github.com/containers/toolbox/pkg/podman" "github.com/containers/toolbox/pkg/utils" @@ -66,6 +85,13 @@ type exitError struct { err error } +type subIDRange struct { + Start int + Length int +} + +type ranges []subIDRange + func (e *exitError) Error() string { if e.err != nil { return e.err.Error() @@ -141,15 +167,15 @@ func preRun(cmd *cobra.Command, args []string) error { logrus.Debugf("Running on a cgroups v%d host", cgroupsVersion) if currentUser.Uid != "0" { - logrus.Debugf("Checking if /etc/subgid and /etc/subuid have entries for user %s", + logrus.Debugf("Checking for subuid and subgid have entries for user %s", currentUser.Username) - if _, err := validateSubIDFile("/etc/subuid"); err != nil { - return newSubIDFileError() + if _, err := validateSubIDRange(currentUser.Username, true); err != nil { + return newSubIDError() } - if _, err := validateSubIDFile("/etc/subgid"); err != nil { - return newSubIDFileError() + if _, err := validateSubIDRange(currentUser.Username, false); err != nil { + return newSubIDError() } } } @@ -319,9 +345,9 @@ func migrate() error { return nil } -func newSubIDFileError() error { +func newSubIDError() error { var builder strings.Builder - fmt.Fprintf(&builder, "/etc/subgid and /etc/subuid don't have entries for user %s\n", currentUser.Username) + fmt.Fprintf(&builder, "Missing subuid and/or subgid entries for user %s\n", currentUser.Username) fmt.Fprintf(&builder, "See the podman(1), subgid(5), subuid(5) and usermod(8) manuals for more\n") fmt.Fprintf(&builder, "information.") @@ -392,28 +418,65 @@ func setUpLoggers() error { return nil } -func validateSubIDFile(path string) (bool, error) { - logrus.Debugf("Validating sub-ID file %s", path) +func validateSubIDRange(username string, isUser bool) (ranges, error) { + var ret ranges - file, err := os.Open(path) - if err != nil { - logrus.Debugf("Validating sub-ID file: failed to open %s: %s", path, err) - return false, fmt.Errorf("failed to open %s", path) + queryType := "" + + if isUser { + queryType = "subuid" + } else { + queryType = "subgid" } - scanner := bufio.NewScanner(file) - scanner.Split(bufio.ScanLines) + uidstr := "" - prefixes := []string{currentUser.Username + ":", currentUser.Uid + ":"} + if username == "ALL" { + return nil, errors.New("username ALL not supported") + } - for scanner.Scan() { - line := scanner.Text() - for _, prefix := range prefixes { - if strings.HasPrefix(line, prefix) { - return true, nil - } + if u, err := user.Lookup(username); err == nil { + uidstr = u.Uid + } + + cUsername := C.CString(username) + defer C.free(unsafe.Pointer(cUsername)) + + cuidstr := C.CString(uidstr) + defer C.free(unsafe.Pointer(cuidstr)) + + var nRanges C.int + var cRanges *C.struct_subid_range + if isUser { + nRanges = C.subid_get_uid_ranges(cUsername, &cRanges) + if nRanges <= 0 { + nRanges = C.subid_get_uid_ranges(cuidstr, &cRanges) + } + } else { + nRanges = C.subid_get_gid_ranges(cUsername, &cRanges) + if nRanges <= 0 { + nRanges = C.subid_get_gid_ranges(cuidstr, &cRanges) } } + if nRanges <= 0 { + return nil, errors.New("cannot read subids") + } + defer C.free(unsafe.Pointer(cRanges)) - return false, fmt.Errorf("failed to find an entry for user %s in %s", currentUser.Username, path) + for i := 0; i < int(nRanges); i++ { + r := C.get_range(cRanges, C.int(i)) + newRange := subIDRange{ + Start: int(r.start), + Length: int(r.count), + } + ret = append(ret, newRange) + + logrus.Debugf("Found %s range %d for %s: start [%d] length [%d]", + queryType, + i, + username, + r.start, + r.count) + } + return ret, nil }