diff --git a/meson.build b/meson.build index 9c2138e25..9a7b1f3b0 100644 --- a/meson.build +++ b/meson.build @@ -3,6 +3,7 @@ project( 'c', version: '0.0.99.3', license: 'ASL 2.0', + default_options: 'c_std=c99', meson_version: '>= 0.58.0', ) @@ -13,6 +14,8 @@ if not cc.has_argument('-print-file-name=libc.so') error('C compiler does not support the -print-file-name argument.') endif +subid_dep = cc.find_library('subid', has_headers: ['shadow/subid.h']) + go = find_program('go') go_md2man = find_program('go-md2man') podman = find_program('podman') diff --git a/playbooks/dependencies-centos-9-stream.yaml b/playbooks/dependencies-centos-9-stream.yaml index 132567b2d..6b59f72c6 100644 --- a/playbooks/dependencies-centos-9-stream.yaml +++ b/playbooks/dependencies-centos-9-stream.yaml @@ -10,6 +10,7 @@ - ninja-build - openssl - podman + - shadow-utils-subid-devel - skopeo - systemd - udisks2 @@ -50,7 +51,7 @@ chdir: '{{ zuul.project.src_dir }}' - name: Check versions of crucial packages - command: rpm -qa ShellCheck codespell *kernel* *glibc* golang podman conmon containernetworking-plugins containers-common container-selinux crun fuse-overlayfs flatpak-session-helper + command: rpm -qa ShellCheck codespell *kernel* *glibc* golang shadow-utils-subid-devel podman conmon containernetworking-plugins containers-common container-selinux crun fuse-overlayfs flatpak-session-helper - name: Show podman versions command: podman version diff --git a/playbooks/dependencies-fedora.yaml b/playbooks/dependencies-fedora.yaml index b8500a056..62a1dfa48 100644 --- a/playbooks/dependencies-fedora.yaml +++ b/playbooks/dependencies-fedora.yaml @@ -32,6 +32,7 @@ - ninja-build - openssl - podman + - shadow-utils-subid-devel - skopeo - systemd - udisks2 @@ -50,7 +51,7 @@ chdir: '{{ zuul.project.src_dir }}' - name: Check versions of crucial packages - command: rpm -qa ShellCheck codespell *kernel* *glibc* golang podman conmon containernetworking-plugins containers-common container-selinux crun fuse-overlayfs flatpak-session-helper + command: rpm -qa ShellCheck codespell *kernel* *glibc* golang shadow-utils-subid-devel podman conmon containernetworking-plugins containers-common container-selinux crun fuse-overlayfs flatpak-session-helper - name: Show podman versions command: podman version diff --git a/src/cmd/root.go b/src/cmd/root.go index 1d9adaefe..9f403f0eb 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -17,7 +17,6 @@ package cmd import ( - "bufio" "errors" "fmt" "io/ioutil" @@ -141,15 +140,14 @@ 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", - currentUser.Username) + logrus.Debugf("Looking for subuid and subgid ranges for user %s", currentUser.Username) - if _, err := validateSubIDFile("/etc/subuid"); err != nil { - return newSubIDFileError() + if _, err := utils.ValidateSubIDRange(currentUser, true); err != nil { + return newSubIDError() } - if _, err := validateSubIDFile("/etc/subgid"); err != nil { - return newSubIDFileError() + if _, err := utils.ValidateSubIDRange(currentUser, false); err != nil { + return newSubIDError() } } } @@ -319,9 +317,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 ranges 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.") @@ -391,29 +389,3 @@ func setUpLoggers() error { return nil } - -func validateSubIDFile(path string) (bool, error) { - logrus.Debugf("Validating sub-ID file %s", path) - - 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) - } - - scanner := bufio.NewScanner(file) - scanner.Split(bufio.ScanLines) - - prefixes := []string{currentUser.Username + ":", currentUser.Uid + ":"} - - for scanner.Scan() { - line := scanner.Text() - for _, prefix := range prefixes { - if strings.HasPrefix(line, prefix) { - return true, nil - } - } - } - - return false, fmt.Errorf("failed to find an entry for user %s in %s", currentUser.Username, path) -} diff --git a/src/meson.build b/src/meson.build index 88b81d35a..19e1b29be 100644 --- a/src/meson.build +++ b/src/meson.build @@ -22,6 +22,7 @@ sources = files( 'pkg/shell/shell.go', 'pkg/utils/errors.go', 'pkg/utils/utils.go', + 'pkg/utils/utils_cgo.go', 'pkg/version/version.go', ) diff --git a/src/pkg/utils/utils_cgo.go b/src/pkg/utils/utils_cgo.go new file mode 100644 index 000000000..b99561511 --- /dev/null +++ b/src/pkg/utils/utils_cgo.go @@ -0,0 +1,131 @@ +/* + * Copyright © 2019 – 2022 Red Hat Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package utils + +import ( + "errors" + "fmt" + "os/user" + "unsafe" +) + +/* +#cgo LDFLAGS: -ldl + +#include +#include +#include + +#include + +#include + +#if SUBID_ABI_MAJOR < 4 +# define subid_get_uid_ranges get_subuid_ranges +# define subid_get_gid_ranges get_subgid_ranges +#endif + +#define TOOLBOX_STRINGIFY_HELPER(s) #s +#define TOOLBOX_STRINGIFY(s) TOOLBOX_STRINGIFY_HELPER (s) + +typedef bool (*ToolboxSubidInitFunc) (const char *progname, FILE *logfd); +typedef int (*ToolboxSubidGetRangesFunc) (const char *owner, struct subid_range **ranges); + +const char *TOOLBOX_LIBSUBID = "libsubid.so." TOOLBOX_STRINGIFY (SUBID_ABI_VERSION); + +const char *TOOLBOX_LIBSUBID_INIT = "libsubid_init"; +const char *TOOLBOX_SUBID_INIT = "subid_init"; + +const char *TOOLBOX_SUBID_GET_UID_RANGES_SYMBOL = TOOLBOX_STRINGIFY (subid_get_uid_ranges); +const char *TOOLBOX_SUBID_GET_GID_RANGES_SYMBOL = TOOLBOX_STRINGIFY (subid_get_gid_ranges); + +void +toolbox_subid_init (void *subid_init_func) +{ + (* (ToolboxSubidInitFunc) subid_init_func) (NULL, NULL); +} + +int +toolbox_subid_get_id_ranges (void *subid_get_id_ranges_func, const char *owner, struct subid_range **ranges) +{ + int ret_val = 0; + + ret_val = (* (ToolboxSubidGetRangesFunc) subid_get_id_ranges_func) (owner, ranges); + return ret_val; +} +*/ +import "C" + +func ValidateSubIDRange(user *user.User, isUser bool) (bool, error) { + if IsInsideContainer() { + panic("cannot validate subordinate IDs inside container") + } + + if user.Username == "ALL" { + return false, errors.New("username ALL not supported") + } + + libsubid := C.dlopen(C.TOOLBOX_LIBSUBID, C.RTLD_LAZY) + if libsubid == nil { + filename := C.GoString(C.TOOLBOX_LIBSUBID) + return false, fmt.Errorf("cannot dlopen(3) %s", filename) + } + + defer C.dlclose(libsubid) + + subid_init := C.dlsym(libsubid, C.TOOLBOX_SUBID_INIT) + if subid_init == nil { + subid_init = C.dlsym(libsubid, C.TOOLBOX_LIBSUBID_INIT) + } + + if subid_init != nil { + C.toolbox_subid_init(subid_init) + } + + var cSubidGetIDRangesSymbol *C.char + if isUser { + cSubidGetIDRangesSymbol = C.TOOLBOX_SUBID_GET_UID_RANGES_SYMBOL + } else { + cSubidGetIDRangesSymbol = C.TOOLBOX_SUBID_GET_GID_RANGES_SYMBOL + } + + subid_get_id_ranges := C.dlsym(libsubid, cSubidGetIDRangesSymbol) + if subid_get_id_ranges == nil { + subidGetIDRangesSymbol := C.GoString(cSubidGetIDRangesSymbol) + return false, fmt.Errorf("cannot dlsym(3) %s", subidGetIDRangesSymbol) + } + + cUsername := C.CString(user.Username) + defer C.free(unsafe.Pointer(cUsername)) + + var cRanges *C.struct_subid_range + defer C.free(unsafe.Pointer(cRanges)) + + nRanges := C.toolbox_subid_get_id_ranges(subid_get_id_ranges, cUsername, &cRanges) + if nRanges <= 0 { + cUid := C.CString(user.Uid) + defer C.free(unsafe.Pointer(cUid)) + + nRanges = C.toolbox_subid_get_id_ranges(subid_get_id_ranges, cUid, &cRanges) + } + + if nRanges <= 0 { + return false, errors.New("cannot read subids") + } + + return true, nil +}