Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

shared/idmap: Make get_userns_fd configure the userns #13522

Merged
merged 2 commits into from
Jun 1, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 85 additions & 19 deletions lxd/idmap/shift_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,19 +279,78 @@ static int get_userns_fd_cb(void *data)

static int get_userns_fd(void)
{
int ret;
int userns_fd = -EBADF;
int file_fd = -EBADF;
pid_t pid;
char path[256];

// Create the namespace.
pid = do_clone(get_userns_fd_cb, NULL, CLONE_NEWUSER);
if (pid < 0)
return -errno;
goto err;

// Fetch a reference.
snprintf(path, sizeof(path), "/proc/%d/ns/user", pid);
ret = open(path, O_RDONLY | O_CLOEXEC);
userns_fd = open(path, O_RDONLY | O_CLOEXEC);
if (userns_fd < 0)
goto err_process;

// Setup uid_map
snprintf(path, sizeof(path), "/proc/%d/uid_map", pid);
file_fd = openat(AT_FDCWD, path, O_WRONLY);
if (file_fd < 0)
goto err_process;

if (write(file_fd, "0 0 1", 5) != 5)
goto err_process;

if (close(file_fd) < 0) {
file_fd = -EBADF;
goto err_process;
}

// Setup setgroups
snprintf(path, sizeof(path), "/proc/%d/setgroups", pid);
file_fd = openat(AT_FDCWD, path, O_WRONLY);
if (file_fd < 0)
goto err_process;

if (write(file_fd, "deny", 4) != 4)
goto err_process;

if (close(file_fd) < 0) {
file_fd = -EBADF;
goto err_process;
}

// Setup gid_map
snprintf(path, sizeof(path), "/proc/%d/gid_map", pid);
file_fd = openat(AT_FDCWD, path, O_WRONLY);
if (file_fd < 0)
goto err_process;

if (write(file_fd, "0 0 1", 5) != 5)
goto err_process;

if (close(file_fd) < 0) {
file_fd = -EBADF;
goto err_process;
}

// Kill the temporary process.
kill(pid, SIGKILL);
wait_for_pid(pid);

return userns_fd;

err_process:
kill(pid, SIGKILL);
wait_for_pid(pid);
return ret;

err:
close(userns_fd);
close(file_fd);
return -1;
}

static int create_detached_idmapped_mount(const char *path, const char *fstype)
Expand Down Expand Up @@ -335,6 +394,7 @@ static int create_detached_idmapped_mount(const char *path, const char *fstype)
if (ret < 0)
return -errno;

close(fd_userns);
return 0;
}
*/
Expand Down Expand Up @@ -401,21 +461,21 @@ func SetCaps(path string, caps []byte, uid int64) error {
}

// ShiftACL updates uid and gid for file ACLs when entering/exiting a namespace
func ShiftACL(path string, shiftIds func(uid int64, gid int64) (int64, int64)) error {
err := shiftAclType(path, C.ACL_TYPE_ACCESS, shiftIds)
func ShiftACL(path string, shiftIDs func(uid int64, gid int64) (int64, int64)) error {
err := shiftACLType(path, C.ACL_TYPE_ACCESS, shiftIDs)
if err != nil {
return err
}

err = shiftAclType(path, C.ACL_TYPE_DEFAULT, shiftIds)
err = shiftACLType(path, C.ACL_TYPE_DEFAULT, shiftIDs)
if err != nil {
return err
}

return nil
}

func shiftAclType(path string, aclType int, shiftIds func(uid int64, gid int64) (int64, int64)) error {
func shiftACLType(path string, aclType int, shiftIDs func(uid int64, gid int64) (int64, int64)) error {
// Convert the path to something usable with cgo
cpath := C.CString(path)
defer C.free(unsafe.Pointer(cpath))
Expand All @@ -430,12 +490,12 @@ func shiftAclType(path string, aclType int, shiftIds func(uid int64, gid int64)

// Iterate through all ACL entries
update := false
for entryId := C.ACL_FIRST_ENTRY; ; entryId = C.ACL_NEXT_ENTRY {
for entryID := C.ACL_FIRST_ENTRY; ; entryID = C.ACL_NEXT_ENTRY {
var ent C.acl_entry_t
var tag C.acl_tag_t

// Get the ACL entry
ret := C.acl_get_entry(acl, C.int(entryId), &ent)
ret := C.acl_get_entry(acl, C.int(entryID), &ent)
if ret == 0 {
break
} else if ret < 0 {
Expand All @@ -460,15 +520,15 @@ func shiftAclType(path string, aclType int, shiftIds func(uid int64, gid int64)
}

// Shift the value
newId := int64(-1)
newID := int64(-1)
if tag == C.ACL_USER {
newId, _ = shiftIds((int64)(*idp), -1)
newID, _ = shiftIDs((int64)(*idp), -1)
} else {
_, newId = shiftIds(-1, (int64)(*idp))
_, newID = shiftIDs(-1, (int64)(*idp))
}

// Update the new entry with the shifted value
ret = C.acl_set_qualifier(ent, unsafe.Pointer(&newId))
ret = C.acl_set_qualifier(ent, unsafe.Pointer(&newID))
if ret == -1 {
return fmt.Errorf("Failed to set ACL qualifier on %s", path)
}
Expand All @@ -487,6 +547,7 @@ func shiftAclType(path string, aclType int, shiftIds func(uid int64, gid int64)
return nil
}

// SupportsVFS3Fscaps checks if VFS3Fscaps are supported
func SupportsVFS3Fscaps(prefix string) bool {
tmpfile, err := os.CreateTemp(prefix, ".lxd_fcaps_v3_")
if err != nil {
Expand Down Expand Up @@ -523,6 +584,7 @@ func SupportsVFS3Fscaps(prefix string) bool {
return true
}

// UnshiftACL performs an UID/GID unshift on the ACL xattr value in accordance with idmap (set) provided
func UnshiftACL(value string, set *IdmapSet) (string, error) {
if set == nil {
return "", fmt.Errorf("Invalid IdmapSet supplied")
Expand Down Expand Up @@ -551,10 +613,10 @@ func UnshiftACL(value string, set *IdmapSet) (string, error) {
return "", fmt.Errorf("No valid ACLs found")
}

entry_ptr := C.posix_entry_start(unsafe.Pointer(header))
end_entry_ptr := C.posix_entry_end(entry_ptr, C.size_t(count))
for entry_ptr != end_entry_ptr {
entry := (*C.struct_posix_acl_xattr_entry)(entry_ptr)
entryPtr := C.posix_entry_start(unsafe.Pointer(header))
endEntryPtr := C.posix_entry_end(entryPtr, C.size_t(count))
for entryPtr != endEntryPtr {
entry := (*C.struct_posix_acl_xattr_entry)(entryPtr)
switch C.le16_to_native(entry.e_tag) {
case C.ACL_USER:
ouid := int64(C.le32_to_native(entry.e_id))
Expand Down Expand Up @@ -584,14 +646,15 @@ func UnshiftACL(value string, set *IdmapSet) (string, error) {
logger.Debugf("Ignoring unknown ACL type %d", C.le16_to_native(entry.e_tag))
}

entry_ptr = C.posix_entry_next(entry_ptr)
entryPtr = C.posix_entry_next(entryPtr)
}

buf = C.GoBytes(cBuf, C.int(size))

return string(buf), nil
}

// UnshiftCaps performs an UID/GID unshift on the security.capability xattr value in accordance with idmap (set) provided
func UnshiftCaps(value string, set *IdmapSet) (string, error) {
if set == nil {
return "", fmt.Errorf("Invalid IdmapSet supplied")
Expand All @@ -618,13 +681,16 @@ func UnshiftCaps(value string, set *IdmapSet) (string, error) {
return string(buf), nil
}

// IdmapStorageType represents a file system idmapping type
type IdmapStorageType string

// Define IdmapStorageType type values
const (
IdmapStorageNone = "none"
IdmapStorageIdmapped = "idmapped"
)

// CanIdmapMount checks if (fstype) filesystem supports idmapped mounts
func CanIdmapMount(path string, fstype string) bool {
cpath := C.CString(path)
defer C.free(unsafe.Pointer(cpath))
Expand Down
Loading