tar-util: squash high UIDs in user mode

This commit is contained in:
Lennart Poettering
2025-11-04 14:12:39 +01:00
parent 4ded7f7a43
commit 78a738f4cf
3 changed files with 72 additions and 32 deletions
+6 -2
View File
@@ -35,7 +35,9 @@ int import_fork_tar_x(int tree_fd, int userns_fd, PidRef *ret_pid) {
if (r < 0)
return r;
TarFlags flags = mac_selinux_use() ? TAR_SELINUX : 0;
TarFlags flags =
(userns_fd >= 0 ? TAR_SQUASH_UIDS_ABOVE_64K : 0) |
(mac_selinux_use() ? TAR_SELINUX : 0);
_cleanup_close_pair_ int pipefd[2] = EBADF_PAIR;
if (pipe2(pipefd, O_CLOEXEC) < 0)
@@ -100,7 +102,9 @@ int import_fork_tar_c(int tree_fd, int userns_fd, PidRef *ret_pid) {
if (r < 0)
return r;
TarFlags flags = mac_selinux_use() ? TAR_SELINUX : 0;
TarFlags flags =
(userns_fd >= 0 ? TAR_SQUASH_UIDS_ABOVE_64K : 0) |
(mac_selinux_use() ? TAR_SELINUX : 0);
_cleanup_close_pair_ int pipefd[2] = EBADF_PAIR;
if (pipe2(pipefd, O_CLOEXEC) < 0)
+64 -29
View File
@@ -18,6 +18,7 @@
#include "iovec-util.h"
#include "libarchive-util.h"
#include "mountpoint-util.h"
#include "nsresource.h"
#include "nulstr-util.h"
#include "path-util.h"
#include "recurse-dir.h"
@@ -422,7 +423,8 @@ static int archive_entry_pathname_safe(struct archive_entry *entry, const char *
static int archive_entry_read_acl(
struct archive_entry *entry,
acl_type_t ntype,
acl_t *acl) {
acl_t *acl,
TarFlags flags) {
int r;
@@ -501,6 +503,13 @@ static int archive_entry_read_acl(
if (IN_SET(ntag, ACL_USER, ACL_GROUP)) {
id_t id = qual;
/* Suppress ACL entries for invalid UIDs/GIDS */
if (!uid_is_valid(id))
continue;
/* Suppress ACL entries for UIDs/GIDs to squash */
if (FLAGS_SET(flags, TAR_SQUASH_UIDS_ABOVE_64K) && id >= NSRESOURCE_UIDS_64K)
continue;
if (sym_acl_set_qualifier(e, &id) < 0)
return log_error_errno(errno, "Failed to set ACL entry qualifier: %m");
@@ -532,6 +541,24 @@ static int archive_entry_read_acl(
return 0;
}
static uid_t maybe_squash_uid(uid_t uid, TarFlags flags) {
if (FLAGS_SET(flags, TAR_SQUASH_UIDS_ABOVE_64K) &&
uid_is_valid(uid) &&
uid >= NSRESOURCE_UIDS_64K)
return UID_NOBODY;
return uid;
}
static uid_t maybe_squash_gid(uid_t gid, TarFlags flags) {
if (FLAGS_SET(flags, TAR_SQUASH_UIDS_ABOVE_64K) &&
gid_is_valid(gid) &&
gid >= NSRESOURCE_UIDS_64K)
return GID_NOBODY;
return gid;
}
static int archive_entry_read_stat(
struct archive_entry *entry,
mode_t *filetype,
@@ -565,9 +592,9 @@ static int archive_entry_read_stat(
sym_archive_entry_mtime_nsec(entry),
};
if (uid && sym_archive_entry_uid_is_set(entry))
*uid = sym_archive_entry_uid(entry);
*uid = maybe_squash_uid(sym_archive_entry_uid(entry), flags);
if (gid && sym_archive_entry_gid_is_set(entry))
*gid = sym_archive_entry_gid(entry);
*gid = maybe_squash_gid(sym_archive_entry_gid(entry), flags);
if (fflags) {
unsigned long fs = 0, fc = 0;
@@ -617,13 +644,13 @@ static int archive_entry_read_stat(
}
if (acl_access) {
r = archive_entry_read_acl(entry, ACL_TYPE_ACCESS, acl_access);
r = archive_entry_read_acl(entry, ACL_TYPE_ACCESS, acl_access, flags);
if (r < 0)
return r;
}
if (acl_default) {
r = archive_entry_read_acl(entry, ACL_TYPE_DEFAULT, acl_default);
r = archive_entry_read_acl(entry, ACL_TYPE_DEFAULT, acl_default, flags);
if (r < 0)
return r;
}
@@ -1095,7 +1122,8 @@ static int archive_generate_sparse(struct archive_entry *entry, int fd) {
static int archive_write_acl(
struct archive_entry *entry,
acl_type_t ntype,
acl_t acl) {
acl_t acl,
TarFlags flags) {
int r;
assert(entry);
@@ -1134,6 +1162,7 @@ static int archive_write_acl(
int tag = ntag >= 0 && ntag <= (acl_tag_t) ELEMENTSOF(tag_map) ? tag_map[ntag] : ACL_UNDEFINED_TAG;
bool skip = false;
id_t qualifier = UID_INVALID;
if (IN_SET(ntag, ACL_USER, ACL_GROUP)) {
id_t *q = sym_acl_get_qualifier(e);
@@ -1142,31 +1171,37 @@ static int archive_write_acl(
qualifier = *q;
sym_acl_free(q);
/* Suppress invalid UIDs or those that shall be squashed */
skip = !(uid_is_valid(qualifier) &&
(!FLAGS_SET(flags, TAR_SQUASH_UIDS_ABOVE_64K) || qualifier < NSRESOURCE_UIDS_64K));
}
acl_permset_t p;
if (sym_acl_get_permset(e, &p) < 0)
return log_error_errno(errno, "Failed to get ACL entry permission set: %m");
if (!skip) {
acl_permset_t p;
if (sym_acl_get_permset(e, &p) < 0)
return log_error_errno(errno, "Failed to get ACL entry permission set: %m");
int permset = 0;
r = sym_acl_get_perm(p, ACL_READ);
if (r < 0)
return log_error_errno(r, "Failed to get ACL entry read bit: %m");
SET_FLAG(permset, ARCHIVE_ENTRY_ACL_READ, r);
int permset = 0;
r = sym_acl_get_perm(p, ACL_READ);
if (r < 0)
return log_error_errno(r, "Failed to get ACL entry read bit: %m");
SET_FLAG(permset, ARCHIVE_ENTRY_ACL_READ, r);
r = sym_acl_get_perm(p, ACL_WRITE);
if (r < 0)
return log_error_errno(r, "Failed to get ACL entry write bit: %m");
SET_FLAG(permset, ARCHIVE_ENTRY_ACL_WRITE, r);
r = sym_acl_get_perm(p, ACL_WRITE);
if (r < 0)
return log_error_errno(r, "Failed to get ACL entry write bit: %m");
SET_FLAG(permset, ARCHIVE_ENTRY_ACL_WRITE, r);
r = sym_acl_get_perm(p, ACL_EXECUTE);
if (r < 0)
return log_error_errno(r, "Failed to get ACL entry execute bit: %m");
SET_FLAG(permset, ARCHIVE_ENTRY_ACL_EXECUTE, r);
r = sym_acl_get_perm(p, ACL_EXECUTE);
if (r < 0)
return log_error_errno(r, "Failed to get ACL entry execute bit: %m");
SET_FLAG(permset, ARCHIVE_ENTRY_ACL_EXECUTE, r);
r = sym_archive_entry_acl_add_entry(entry, type, permset, tag, qualifier, /* name= */ NULL);
if (r != ARCHIVE_OK)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to add ACL entry.");
r = sym_archive_entry_acl_add_entry(entry, type, permset, tag, qualifier, /* name= */ NULL);
if (r != ARCHIVE_OK)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to add ACL entry.");
}
r = sym_acl_get_entry(acl, ACL_NEXT_ENTRY, &e);
}
@@ -1223,9 +1258,9 @@ static int archive_item(
sym_archive_entry_set_perm(entry, sx->stx_mode);
if (FLAGS_SET(sx->stx_mask, STATX_UID))
sym_archive_entry_set_uid(entry, sx->stx_uid);
sym_archive_entry_set_uid(entry, maybe_squash_uid(sx->stx_uid, d->flags));
if (FLAGS_SET(sx->stx_mask, STATX_GID))
sym_archive_entry_set_gid(entry, sx->stx_gid);
sym_archive_entry_set_gid(entry, maybe_squash_gid(sx->stx_gid, d->flags));
if (S_ISREG(sx->stx_mode)) {
if (!FLAGS_SET(sx->stx_mask, STATX_SIZE))
@@ -1273,7 +1308,7 @@ static int archive_item(
if (!acl)
return log_error_errno(errno, "Failed read access ACLs of '%s': %m", path);
archive_write_acl(entry, ACL_TYPE_ACCESS, acl);
archive_write_acl(entry, ACL_TYPE_ACCESS, acl, d->flags);
if (S_ISDIR(sx->stx_mode)) {
sym_acl_free(acl);
@@ -1282,7 +1317,7 @@ static int archive_item(
if (!acl)
return log_error_errno(errno, "Failed to read default ACLs of '%s': %m", path);
archive_write_acl(entry, ACL_TYPE_DEFAULT, acl);
archive_write_acl(entry, ACL_TYPE_DEFAULT, acl, d->flags);
}
}
}
+2 -1
View File
@@ -2,7 +2,8 @@
#pragma once
typedef enum TarFlags {
TAR_SELINUX = 1 << 0,
TAR_SELINUX = 1 << 0, /* Include SELinux xattr in tarball, or unpack it */
TAR_SQUASH_UIDS_ABOVE_64K = 1 << 1, /* Squash UIDs/GIDs above 64K when packing/unpacking to the nobody user */
} TarFlags;
int tar_x(int input_fd, int tree_fd, TarFlags flags);