Support subordinate user and group ID ranges on enterprise set-ups
On enterprise FreeIPA set-ups, the subordinate user and group IDs are
provided by SSSD's sss plugin for the GNU Name Service Switch (or NSS)
functionality of the GNU C Library. They are not listed in /etc/subuid
and /etc/subgid. Therefore, its necessary to use libsubid.so to check
the subordinate ID ranges.
The CGO interaction with libsubid.so is loosely based on 'readSubid' in
github.com/containers/storage/pkg/idtools [1].
However, unlike 'readSubid', this code considers the absence of any
range (ie., nRanges == 0) to be an error as well.
More importantly, this code uses dlopen(3) and friends to dynamically
load the symbols from libsubid.so, instead of linking to libsubid.so at
build-time and having the dependency noted in the /usr/bin/toolbox
binary. This is done because libsubid.so itself depends on several
other shared libraries, and indirect dependencies can't be influenced
by the RUNPATH [2] embedded in the /usr/bin/toolbox binary [3]. Hence,
when the binary is used inside Toolbx containers (eg., as the entry
point), those indirect dependencies won't be picked from the host's
runtime against which the binary was built. This can render the binary
useless due to ABI compatibility issues. Using dlopen(3) avoids this
problem, especially because libsubid.so is only used when running on the
host.
Care was taken to not load and link libsubid.so twice to separately
validate the subordinate ID ranges for the user and the group. Note
that libsubid_init() must be passed a FILE pointer for logging.
Otherwise, it will create it's own for logging, and there's no way to
close it during dlclose(3).
Version 4 of the libsubid.so API/ABI [4] was released in Shadow 4.10,
which is newer than the versions shipped on RHEL 8 and Debian 10 [5],
and even that newer version had some problems [6]. Therefore, support
for older versions, with the relevant workarounds, is necessary.
Fortunately, the oldest that needs to be support is Shadow 4.9 because
that's when libsubid.so was introduced [7].
Note that SUBID_ABI_VERSION was only introduced with version 4 of the
libsubid.so API/ABI released in Shadow 4.10 [8]. The first release of
libsubid.so in Shadow 4.9 already had an ABI version of 3.0.0 [9], since
it was bumped a few times during development, so that's what's assumed
when SUBID_ABI_VERSION is absent.
This code doesn't set the public variables Prog and shadow_logfd that
older Shadow versions used to expect for logging, because from Shadow
4.9 onwards there's a separate function [4,10] to specify these. This
can be changed if there are libsubid.so versions in the wild that really
do need those public variables to be set.
Finally, ISO C99 is required because of the use of <stdbool.h> in the
libsubid.so API.
Some changes by Debarshi Ray.
[1] https://github.com/containers/storage/blob/main/pkg/idtools/idtools_supported.go
[2] https://man7.org/linux/man-pages/man8/ld.so.8.html
[3] Commit 6063eb27b9
https://github.com/containers/toolbox/issues/821
[4] Shadow commit 32f641b207f6ddff
https://github.com/shadow-maint/shadow/commit/32f641b207f6ddff
https://github.com/shadow-maint/shadow/issues/443
[5] https://packages.debian.org/source/buster/shadow
[6] Shadow commit 79157cbad87f42cd
https://github.com/shadow-maint/shadow/commit/79157cbad87f42cd
https://github.com/shadow-maint/shadow/issues/465
[7] Shadow commit 0a7888b1fad613a0
https://github.com/shadow-maint/shadow/commit/0a7888b1fad613a0
https://github.com/shadow-maint/shadow/issues/154
[8] Shadow commit 0c9f64140852e8d5
https://github.com/shadow-maint/shadow/commit/0c9f64140852e8d5
https://github.com/shadow-maint/shadow/pull/449
[9] Shadow commit 3d670ba7ed58f910
https://github.com/shadow-maint/shadow/commit/3d670ba7ed58f910
https://github.com/shadow-maint/shadow/issues/339
[10] Shadow commit 2b22a6909dba60d
https://github.com/shadow-maint/shadow/commit/2b22a6909dba60d
https://github.com/shadow-maint/shadow/issues/325
https://github.com/containers/toolbox/issues/1074
Signed-off-by: Martin Jackson <martjack@redhat.com>
This commit is contained in:
parent
e3b4b5ec24
commit
ca8007c192
8 changed files with 212 additions and 44 deletions
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
)
|
||||
|
||||
|
|
62
src/pkg/utils/libsubid-wrappers.c
Normal file
62
src/pkg/utils/libsubid-wrappers.c
Normal file
|
@ -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 <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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;
|
||||
}
|
31
src/pkg/utils/libsubid-wrappers.h
Normal file
31
src/pkg/utils/libsubid-wrappers.h
Normal file
|
@ -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 <shadow/subid.h>
|
||||
|
||||
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);
|
104
src/pkg/utils/utils_cgo.go
Normal file
104
src/pkg/utils/utils_cgo.go
Normal file
|
@ -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 <stdlib.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <shadow/subid.h>
|
||||
|
||||
#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
|
||||
}
|
Loading…
Reference in a new issue