tar-util: squash high UIDs in user mode
This commit is contained in:
@@ -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
@@ -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,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);
|
||||
|
||||
Reference in New Issue
Block a user