diff --git a/meson.build b/meson.build index 1768bdf07..960d0e1a5 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 68bb2ebce..10db018ad 100644 --- a/playbooks/dependencies-centos-9-stream.yaml +++ b/playbooks/dependencies-centos-9-stream.yaml @@ -11,6 +11,7 @@ - ninja-build - openssl - podman + - shadow-utils-subid-devel - skopeo - systemd - udisks2 @@ -51,7 +52,7 @@ chdir: '{{ zuul.project.src_dir }}' - name: Check versions of crucial packages - command: rpm -qa ShellCheck codespell *kernel* gcc *glibc* golang podman conmon containernetworking-plugins containers-common container-selinux crun fuse-overlayfs flatpak-session-helper + command: rpm -qa ShellCheck codespell *kernel* gcc *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 8a900df76..fe3cf8632 100644 --- a/playbooks/dependencies-fedora.yaml +++ b/playbooks/dependencies-fedora.yaml @@ -33,6 +33,7 @@ - ninja-build - openssl - podman + - shadow-utils-subid-devel - skopeo - systemd - udisks2 @@ -51,7 +52,7 @@ chdir: '{{ zuul.project.src_dir }}' - name: Check versions of crucial packages - command: rpm -qa ShellCheck codespell *kernel* gcc *glibc* golang podman conmon containernetworking-plugins containers-common container-selinux crun fuse-overlayfs flatpak-session-helper + command: rpm -qa ShellCheck codespell *kernel* gcc *glibc* shadow-utils-subid-devel golang 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 ac89a38f6..304b03dcd 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -17,7 +17,6 @@ package cmd import ( - "bufio" "errors" "fmt" "io/ioutil" @@ -141,17 +140,11 @@ 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 sub-GID and sub-UID ranges for user %s", currentUser.Username) - if _, err := validateSubIDFile("/etc/subuid"); err != nil { - logrus.Debugf("Checking sub-ID file /etc/subuid: %s", err) - return newSubIDFileError() - } - - if _, err := validateSubIDFile("/etc/subgid"); err != nil { - logrus.Debugf("Checking sub-ID file /etc/subgid: %s", err) - return newSubIDFileError() + if _, err := utils.ValidateSubIDRanges(currentUser); err != nil { + logrus.Debugf("Looking for sub-GID and sub-UID ranges: %s", err) + return newSubIDError() } } } @@ -321,9 +314,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 subgid and/or subuid 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.") @@ -393,32 +386,3 @@ func setUpLoggers() error { return nil } - -func validateSubIDFile(path string) (bool, error) { - if utils.IsInsideContainer() { - panic("cannot validate sub-IDs inside container") - } - - file, err := os.Open(path) - if err != nil { - return false, fmt.Errorf("failed to open: %w", err) - } - - defer file.Close() - - 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", currentUser.Username) -} diff --git a/src/meson.build b/src/meson.build index 1d2b868b6..f4e85b35a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -21,8 +21,10 @@ sources = files( 'cmd/utils.go', 'pkg/podman/podman.go', 'pkg/shell/shell.go', + 'pkg/utils/libsubid-wrappers.c', 'pkg/utils/errors.go', 'pkg/utils/utils.go', + 'pkg/utils/utils_cgo.go', 'pkg/version/version.go', ) diff --git a/src/pkg/utils/libsubid-wrappers.c b/src/pkg/utils/libsubid-wrappers.c new file mode 100644 index 000000000..e185b64d0 --- /dev/null +++ b/src/pkg/utils/libsubid-wrappers.c @@ -0,0 +1,62 @@ +/* + * Copyright © 2022 – 2023 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. + */ + + +#include +#include + +#include "libsubid-wrappers.h" + +#ifndef SUBID_ABI_VERSION +#define SUBID_ABI_VERSION 3.0.0 +#endif + +#if SUBID_ABI_MAJOR < 4 +#define subid_init libsubid_init +#define subid_get_gid_ranges get_subgid_ranges +#define subid_get_uid_ranges get_subuid_ranges +#endif + +#define TOOLBOX_STRINGIZE_HELPER(s) #s +#define TOOLBOX_STRINGIZE(s) TOOLBOX_STRINGIZE_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_STRINGIZE (SUBID_ABI_VERSION); + +const char *TOOLBOX_SUBID_INIT = TOOLBOX_STRINGIZE (subid_init); + +const char *TOOLBOX_SUBID_GET_GID_RANGES_SYMBOL = TOOLBOX_STRINGIZE (subid_get_gid_ranges); +const char *TOOLBOX_SUBID_GET_UID_RANGES_SYMBOL = TOOLBOX_STRINGIZE (subid_get_uid_ranges); + + +void +toolbox_subid_init (void *subid_init_func) +{ + (* (ToolboxSubidInitFunc) subid_init_func) (NULL, stderr); +} + + +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; +} diff --git a/src/pkg/utils/libsubid-wrappers.h b/src/pkg/utils/libsubid-wrappers.h new file mode 100644 index 000000000..c1dec3555 --- /dev/null +++ b/src/pkg/utils/libsubid-wrappers.h @@ -0,0 +1,31 @@ +/* + * Copyright © 2023 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. + */ + +#pragma once + +#include + +extern const char *TOOLBOX_LIBSUBID; + +extern const char *TOOLBOX_LIBSUBID_INIT; +extern const char *TOOLBOX_SUBID_INIT; + +extern const char *TOOLBOX_SUBID_GET_GID_RANGES_SYMBOL; +extern const char *TOOLBOX_SUBID_GET_UID_RANGES_SYMBOL; + +void toolbox_subid_init (void *subid_init_func); + +int toolbox_subid_get_id_ranges (void *subid_get_id_ranges_func, const char *owner, struct subid_range **ranges); diff --git a/src/pkg/utils/utils_cgo.go b/src/pkg/utils/utils_cgo.go new file mode 100644 index 000000000..96d7b16e6 --- /dev/null +++ b/src/pkg/utils/utils_cgo.go @@ -0,0 +1,104 @@ +/* + * Copyright © 2022 – 2023 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 "libsubid-wrappers.h" +*/ +import "C" + +func validateSubIDRange(user *user.User, libsubid unsafe.Pointer, cSubidGetIDRangesSymbol *C.char) (bool, error) { + 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 +} + +func ValidateSubIDRanges(user *user.User) (bool, error) { + if IsInsideContainer() { + panic("cannot validate subordinate IDs inside container") + } + + if user == nil { + panic("cannot validate subordinate IDs when user is nil") + } + + 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 { + subidInitSymbol := C.GoString(C.TOOLBOX_SUBID_INIT) + return false, fmt.Errorf("cannot dlsym(3) %s", subidInitSymbol) + } + + C.toolbox_subid_init(subid_init) + + if _, err := validateSubIDRange(user, libsubid, C.TOOLBOX_SUBID_GET_GID_RANGES_SYMBOL); err != nil { + return false, err + } + + if _, err := validateSubIDRange(user, libsubid, C.TOOLBOX_SUBID_GET_UID_RANGES_SYMBOL); err != nil { + return false, err + } + + return true, nil +}