systemd-257-23
Resolves: RHEL-106795, RHEL-143036, RHEL-143034, RHEL-53112, RHEL-95219
This commit is contained in:
parent
bf06933ed4
commit
363d20f6ec
@ -0,0 +1,84 @@
|
||||
From 53118a0bc2f4855cb90ba24c674c3c6044ea6e3d Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
|
||||
Date: Wed, 30 Jul 2025 11:33:35 +0200
|
||||
Subject: [PATCH] test-journal-dump: dump the headers of journal files
|
||||
|
||||
We have journal_file_print_header(), but it's not exposed anywhere in
|
||||
a way that it is easy to call.
|
||||
|
||||
(cherry picked from commit 58ecb72bcf5b2f25057d58fe52ce9717c0d07382)
|
||||
|
||||
Resolves: RHEL-106795
|
||||
---
|
||||
src/libsystemd/meson.build | 4 ++
|
||||
src/libsystemd/sd-journal/test-journal-dump.c | 46 +++++++++++++++++++
|
||||
2 files changed, 50 insertions(+)
|
||||
create mode 100644 src/libsystemd/sd-journal/test-journal-dump.c
|
||||
|
||||
diff --git a/src/libsystemd/meson.build b/src/libsystemd/meson.build
|
||||
index fa28aa09fb..e5c8275fb0 100644
|
||||
--- a/src/libsystemd/meson.build
|
||||
+++ b/src/libsystemd/meson.build
|
||||
@@ -291,6 +291,10 @@ libsystemd_tests += [
|
||||
'sources' : files('sd-journal/test-journal-append.c'),
|
||||
'type' : 'manual',
|
||||
},
|
||||
+ {
|
||||
+ 'sources' : files('sd-journal/test-journal-dump.c'),
|
||||
+ 'type' : 'manual',
|
||||
+ },
|
||||
{
|
||||
'sources' : files('sd-journal/test-journal-verify.c'),
|
||||
'timeout' : 90,
|
||||
diff --git a/src/libsystemd/sd-journal/test-journal-dump.c b/src/libsystemd/sd-journal/test-journal-dump.c
|
||||
new file mode 100644
|
||||
index 0000000000..bac2fb9a66
|
||||
--- /dev/null
|
||||
+++ b/src/libsystemd/sd-journal/test-journal-dump.c
|
||||
@@ -0,0 +1,46 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+
|
||||
+#include "errno-util.h"
|
||||
+#include "journal-file.h"
|
||||
+#include "log.h"
|
||||
+#include "main-func.h"
|
||||
+#include "pager.h"
|
||||
+#include "strv.h"
|
||||
+
|
||||
+static int run(int argc, char *argv[]) {
|
||||
+ int r = 0;
|
||||
+ unsigned n = 0;
|
||||
+
|
||||
+ _cleanup_(mmap_cache_unrefp) MMapCache *m = mmap_cache_new();
|
||||
+ assert_se(m);
|
||||
+
|
||||
+ pager_open(/* flags= */ 0);
|
||||
+
|
||||
+ STRV_FOREACH(s, strv_skip(argv, 1)) {
|
||||
+ JournalFile *f = NULL;
|
||||
+
|
||||
+ int k = journal_file_open(
|
||||
+ /* fd= */ -EBADF,
|
||||
+ *s,
|
||||
+ O_RDONLY,
|
||||
+ /* file_flags= */ 0,
|
||||
+ 0666,
|
||||
+ /* compress_threshold_bytes= */ UINT64_MAX,
|
||||
+ /* metrics= */ NULL,
|
||||
+ m,
|
||||
+ /* template= */ NULL,
|
||||
+ &f);
|
||||
+ if (k < 0)
|
||||
+ RET_GATHER(r, log_error_errno(k, "Failed to open %s, continuing: %m", *s));
|
||||
+
|
||||
+ if (n++ > 0)
|
||||
+ puts("");
|
||||
+
|
||||
+ journal_file_print_header(f);
|
||||
+ journal_file_close(f);
|
||||
+ }
|
||||
+
|
||||
+ return r;
|
||||
+}
|
||||
+
|
||||
+DEFINE_MAIN_FUNCTION(run);
|
||||
@ -0,0 +1,94 @@
|
||||
From f9594f04c3a37ad8dab8f62bbeff67adc2e5ca44 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
|
||||
Date: Wed, 30 Jul 2025 11:39:35 +0200
|
||||
Subject: [PATCH] journal: store counts, not byte sizes, in table size
|
||||
constants
|
||||
|
||||
It's easier to think about the size in "objects", not bytes. Let's convert to
|
||||
bytes at the last moment.
|
||||
|
||||
Also drop some of the pointless size suffixes. In general, it's the size of the
|
||||
variable that matters, not the constant that is written to it.
|
||||
|
||||
No functional change.
|
||||
|
||||
(cherry picked from commit fbbcd0edefd2921b117eebfeab8eefc3d26bd0c2)
|
||||
|
||||
Resolves: RHEL-106795
|
||||
---
|
||||
src/libsystemd/sd-journal/journal-file.c | 27 ++++++++++++------------
|
||||
1 file changed, 13 insertions(+), 14 deletions(-)
|
||||
|
||||
diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c
|
||||
index 7e941edb19..791c1e8eaf 100644
|
||||
--- a/src/libsystemd/sd-journal/journal-file.c
|
||||
+++ b/src/libsystemd/sd-journal/journal-file.c
|
||||
@@ -42,11 +42,11 @@
|
||||
#include "user-util.h"
|
||||
#include "xattr-util.h"
|
||||
|
||||
-#define DEFAULT_DATA_HASH_TABLE_SIZE (2047ULL*sizeof(HashItem))
|
||||
-#define DEFAULT_FIELD_HASH_TABLE_SIZE (333ULL*sizeof(HashItem))
|
||||
+#define DEFAULT_DATA_HASH_TABLE_SIZE 2047U
|
||||
+#define DEFAULT_FIELD_HASH_TABLE_SIZE 333U
|
||||
|
||||
-#define DEFAULT_COMPRESS_THRESHOLD (512ULL)
|
||||
-#define MIN_COMPRESS_THRESHOLD (8ULL)
|
||||
+#define DEFAULT_COMPRESS_THRESHOLD 512U
|
||||
+#define MIN_COMPRESS_THRESHOLD 8U
|
||||
|
||||
/* This is the minimum journal file size */
|
||||
#define JOURNAL_FILE_SIZE_MIN (512 * U64_KB) /* 512 KiB */
|
||||
@@ -1281,15 +1281,14 @@ static int journal_file_setup_data_hash_table(JournalFile *f) {
|
||||
beyond 75% fill level. Calculate the hash table size for
|
||||
the maximum file size based on these metrics. */
|
||||
|
||||
- s = (f->metrics.max_size * 4 / 768 / 3) * sizeof(HashItem);
|
||||
- if (s < DEFAULT_DATA_HASH_TABLE_SIZE)
|
||||
- s = DEFAULT_DATA_HASH_TABLE_SIZE;
|
||||
+ s = MAX(f->metrics.max_size * 4 / 768 / 3,
|
||||
+ DEFAULT_DATA_HASH_TABLE_SIZE);
|
||||
|
||||
- log_debug("Reserving %"PRIu64" entries in data hash table.", s / sizeof(HashItem));
|
||||
+ log_debug("Reserving %"PRIu64" entries in data hash table.", s);
|
||||
|
||||
r = journal_file_append_object(f,
|
||||
OBJECT_DATA_HASH_TABLE,
|
||||
- offsetof(Object, hash_table.items) + s,
|
||||
+ offsetof(Object, hash_table.items) + s * sizeof(HashItem),
|
||||
&o, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -1297,7 +1296,7 @@ static int journal_file_setup_data_hash_table(JournalFile *f) {
|
||||
memzero(o->hash_table.items, s);
|
||||
|
||||
f->header->data_hash_table_offset = htole64(p + offsetof(Object, hash_table.items));
|
||||
- f->header->data_hash_table_size = htole64(s);
|
||||
+ f->header->data_hash_table_size = htole64(s * sizeof(HashItem));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1314,19 +1313,19 @@ static int journal_file_setup_field_hash_table(JournalFile *f) {
|
||||
* number should grow very slowly only */
|
||||
|
||||
s = DEFAULT_FIELD_HASH_TABLE_SIZE;
|
||||
- log_debug("Reserving %"PRIu64" entries in field hash table.", s / sizeof(HashItem));
|
||||
+ log_debug("Reserving %"PRIu64" entries in field hash table.", s);
|
||||
|
||||
r = journal_file_append_object(f,
|
||||
OBJECT_FIELD_HASH_TABLE,
|
||||
- offsetof(Object, hash_table.items) + s,
|
||||
+ offsetof(Object, hash_table.items) + s * sizeof(HashItem),
|
||||
&o, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
- memzero(o->hash_table.items, s);
|
||||
+ memzero(o->hash_table.items, s * sizeof(HashItem));
|
||||
|
||||
f->header->field_hash_table_offset = htole64(p + offsetof(Object, hash_table.items));
|
||||
- f->header->field_hash_table_size = htole64(s);
|
||||
+ f->header->field_hash_table_size = htole64(s * sizeof(HashItem));
|
||||
|
||||
return 0;
|
||||
}
|
||||
243
0577-journal-treble-field-hash-table-size.patch
Normal file
243
0577-journal-treble-field-hash-table-size.patch
Normal file
@ -0,0 +1,243 @@
|
||||
From 1aea0cf1757ce56a47f915aa89043fd5415d1f58 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
|
||||
Date: Wed, 30 Jul 2025 11:52:26 +0200
|
||||
Subject: [PATCH] journal: treble field hash table size
|
||||
|
||||
As discussed in https://github.com/systemd/systemd/issues/38399, "ordinary"
|
||||
systems can have the field table with a large number of values, causing journal
|
||||
rotation to occur early. For example, audit generates a log of fields:
|
||||
$ journalctl --fields | rg -c '^_?AUDIT'
|
||||
114
|
||||
|
||||
It seems that the "structured log" capabilities of the journal are being use
|
||||
more than in the past. Looking at some journal files on my system, it seems
|
||||
the field hash table field is quite high in many cases:
|
||||
$ build/test-journal-dump /var/log/journal/*/* | rg 'table fill'
|
||||
Data hash table fill: 15.1%
|
||||
Field hash table fill: 69.1%
|
||||
Data hash table fill: 4.9%
|
||||
Field hash table fill: 32.4%
|
||||
Data hash table fill: 10.2%
|
||||
Field hash table fill: 34.2%
|
||||
Data hash table fill: 9.9%
|
||||
Field hash table fill: 37.2%
|
||||
Data hash table fill: 26.8%
|
||||
Field hash table fill: 21.9%
|
||||
Data hash table fill: 35.6%
|
||||
Field hash table fill: 22.8%
|
||||
Data hash table fill: 25.5%
|
||||
Field hash table fill: 54.1%
|
||||
Data hash table fill: 3.4%
|
||||
Field hash table fill: 43.8%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 70.3%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 63.1%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 74.2%
|
||||
Data hash table fill: 35.6%
|
||||
Field hash table fill: 43.2%
|
||||
Data hash table fill: 35.5%
|
||||
Field hash table fill: 75.4%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 59.8%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 56.5%
|
||||
Data hash table fill: 16.9%
|
||||
Field hash table fill: 76.3%
|
||||
Data hash table fill: 18.1%
|
||||
Field hash table fill: 76.9%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 42.0%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 22.8%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 22.8%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 22.8%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 22.8%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 32.1%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 21.9%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 21.9%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 21.9%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 22.8%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 22.8%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 21.9%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 22.5%
|
||||
Data hash table fill: 9.6%
|
||||
Field hash table fill: 53.8%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 22.2%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 22.2%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 22.2%
|
||||
Data hash table fill: 35.6%
|
||||
Field hash table fill: 75.1%
|
||||
Data hash table fill: 33.6%
|
||||
Field hash table fill: 50.2%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 26.7%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 25.8%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 29.1%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 25.8%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 31.8%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 18.9%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 22.2%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 20.1%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 29.1%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 30.9%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 28.5%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 28.5%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 25.8%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 25.2%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 39.3%
|
||||
Data hash table fill: 50.2%
|
||||
Field hash table fill: 75.1%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 61.9%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 56.5%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 58.6%
|
||||
Data hash table fill: 48.9%
|
||||
Field hash table fill: 79.6%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 71.5%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 60.1%
|
||||
Data hash table fill: 31.4%
|
||||
Field hash table fill: 75.7%
|
||||
Data hash table fill: 27.0%
|
||||
Field hash table fill: 69.4%
|
||||
Data hash table fill: 28.9%
|
||||
Field hash table fill: 76.6%
|
||||
Data hash table fill: 60.2%
|
||||
Field hash table fill: 79.9%
|
||||
Data hash table fill: 8.8%
|
||||
Field hash table fill: 78.7%
|
||||
Data hash table fill: 5.8%
|
||||
Field hash table fill: 61.3%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 64.0%
|
||||
Data hash table fill: 61.4%
|
||||
Field hash table fill: 63.4%
|
||||
Data hash table fill: 29.7%
|
||||
Field hash table fill: 61.9%
|
||||
Data hash table fill: 18.9%
|
||||
Field hash table fill: 30.9%
|
||||
Data hash table fill: 1.4%
|
||||
Field hash table fill: 22.2%
|
||||
Data hash table fill: 0.4%
|
||||
Field hash table fill: 13.5%
|
||||
Data hash table fill: 2.6%
|
||||
Field hash table fill: 37.5%
|
||||
Data hash table fill: 1.3%
|
||||
Field hash table fill: 23.4%
|
||||
Data hash table fill: 0.6%
|
||||
Field hash table fill: 15.3%
|
||||
Data hash table fill: 18.7%
|
||||
Field hash table fill: 33.9%
|
||||
Data hash table fill: 7.4%
|
||||
Field hash table fill: 37.5%
|
||||
Data hash table fill: 20.2%
|
||||
Field hash table fill: 44.1%
|
||||
Data hash table fill: 1.3%
|
||||
Field hash table fill: 33.0%
|
||||
Data hash table fill: 75.0%
|
||||
Field hash table fill: 19.2%
|
||||
Data hash table fill: 42.2%
|
||||
Field hash table fill: 23.4%
|
||||
Data hash table fill: 1.6%
|
||||
Field hash table fill: 87.1%
|
||||
Data hash table fill: 0.1%
|
||||
Field hash table fill: 98.8%
|
||||
Data hash table fill: 0.2%
|
||||
Field hash table fill: 128.8%
|
||||
Data hash table fill: 15.4%
|
||||
Field hash table fill: 31.2%
|
||||
Data hash table fill: 7.4%
|
||||
Field hash table fill: 22.5%
|
||||
Data hash table fill: 10.5%
|
||||
Field hash table fill: 38.7%
|
||||
Data hash table fill: 2.8%
|
||||
Field hash table fill: 18.0%
|
||||
Data hash table fill: 1.5%
|
||||
Field hash table fill: 15.9%
|
||||
Data hash table fill: 0.0%
|
||||
Field hash table fill: 7.5%
|
||||
Data hash table fill: 0.1%
|
||||
Field hash table fill: 12.0%
|
||||
Data hash table fill: 0.2%
|
||||
Field hash table fill: 10.8%
|
||||
Data hash table fill: 0.2%
|
||||
Field hash table fill: 15.6%
|
||||
Data hash table fill: 0.1%
|
||||
Field hash table fill: 11.7%
|
||||
Data hash table fill: 0.1%
|
||||
Field hash table fill: 12.0%
|
||||
Data hash table fill: 0.0%
|
||||
Field hash table fill: 6.6%
|
||||
Data hash table fill: 1.4%
|
||||
Field hash table fill: 18.0%
|
||||
Data hash table fill: 0.7%
|
||||
Field hash table fill: 16.8%
|
||||
Data hash table fill: 1.1%
|
||||
Field hash table fill: 18.0%
|
||||
Data hash table fill: 0.2%
|
||||
Field hash table fill: 10.8%
|
||||
Data hash table fill: 0.1%
|
||||
Field hash table fill: 10.8%
|
||||
Data hash table fill: 0.4%
|
||||
Field hash table fill: 11.1%
|
||||
|
||||
Since filling of the field hash table to 75% normally causes file rotation,
|
||||
let's double the default to make rotation happen less often.
|
||||
We'll use 11kB more for the hash table, which should be fine, considering
|
||||
that journal files are usually at least 8 MB.
|
||||
|
||||
Closes https://github.com/systemd/systemd/issues/38399.
|
||||
|
||||
(cherry picked from commit e8962d77ac5a10926d0216246cfb5ad5dc25a533)
|
||||
|
||||
Resolves: RHEL-106795
|
||||
---
|
||||
src/libsystemd/sd-journal/journal-file.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c
|
||||
index 791c1e8eaf..290b8af73e 100644
|
||||
--- a/src/libsystemd/sd-journal/journal-file.c
|
||||
+++ b/src/libsystemd/sd-journal/journal-file.c
|
||||
@@ -43,7 +43,7 @@
|
||||
#include "xattr-util.h"
|
||||
|
||||
#define DEFAULT_DATA_HASH_TABLE_SIZE 2047U
|
||||
-#define DEFAULT_FIELD_HASH_TABLE_SIZE 333U
|
||||
+#define DEFAULT_FIELD_HASH_TABLE_SIZE 1023U
|
||||
|
||||
#define DEFAULT_COMPRESS_THRESHOLD 512U
|
||||
#define MIN_COMPRESS_THRESHOLD 8U
|
||||
375
0578-nspawn-move-uid-shift-chown-code-into-shared.patch
Normal file
375
0578-nspawn-move-uid-shift-chown-code-into-shared.patch
Normal file
@ -0,0 +1,375 @@
|
||||
From f9eb81c4c764d4d0c5a0a9265f805be6f575f02d Mon Sep 17 00:00:00 2001
|
||||
From: Lennart Poettering <lennart@poettering.net>
|
||||
Date: Mon, 11 Nov 2024 20:58:30 +0100
|
||||
Subject: [PATCH] nspawn: move uid shift/chown() code into shared/
|
||||
|
||||
(cherry picked from commit b1fb2d971c810e0bdf9ff0ae567a1c6c230e4e5d)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
src/nspawn/meson.build | 7 -
|
||||
src/nspawn/nspawn-def.h | 9 --
|
||||
src/nspawn/nspawn.c | 2 +-
|
||||
src/shared/meson.build | 1 +
|
||||
.../nspawn-patch-uid.c => shared/shift-uid.c} | 141 ++++++------------
|
||||
.../nspawn-patch-uid.h => shared/shift-uid.h} | 0
|
||||
src/test/meson.build | 5 +
|
||||
.../test-shift-uid.c} | 2 +-
|
||||
8 files changed, 52 insertions(+), 115 deletions(-)
|
||||
delete mode 100644 src/nspawn/nspawn-def.h
|
||||
rename src/{nspawn/nspawn-patch-uid.c => shared/shift-uid.c} (82%)
|
||||
rename src/{nspawn/nspawn-patch-uid.h => shared/shift-uid.h} (100%)
|
||||
rename src/{nspawn/test-patch-uid.c => test/test-shift-uid.c} (97%)
|
||||
|
||||
diff --git a/src/nspawn/meson.build b/src/nspawn/meson.build
|
||||
index 2a913b156d..26a7e7bfb9 100644
|
||||
--- a/src/nspawn/meson.build
|
||||
+++ b/src/nspawn/meson.build
|
||||
@@ -7,7 +7,6 @@ libnspawn_core_sources = files(
|
||||
'nspawn-mount.c',
|
||||
'nspawn-network.c',
|
||||
'nspawn-oci.c',
|
||||
- 'nspawn-patch-uid.c',
|
||||
'nspawn-register.c',
|
||||
'nspawn-seccomp.c',
|
||||
'nspawn-settings.c',
|
||||
@@ -63,12 +62,6 @@ executables += [
|
||||
nspawn_test_template + {
|
||||
'sources' : files('test-nspawn-util.c'),
|
||||
},
|
||||
- test_template + {
|
||||
- 'sources' : files('test-patch-uid.c'),
|
||||
- 'link_with' : nspawn_libs,
|
||||
- 'dependencies' : libacl,
|
||||
- 'type' : 'manual',
|
||||
- },
|
||||
nspawn_fuzz_template + {
|
||||
'sources' : files('fuzz-nspawn-settings.c'),
|
||||
},
|
||||
diff --git a/src/nspawn/nspawn-def.h b/src/nspawn/nspawn-def.h
|
||||
deleted file mode 100644
|
||||
index 32a20aabd5..0000000000
|
||||
--- a/src/nspawn/nspawn-def.h
|
||||
+++ /dev/null
|
||||
@@ -1,9 +0,0 @@
|
||||
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
-#pragma once
|
||||
-
|
||||
-#include <sys/types.h>
|
||||
-
|
||||
-/* While we are chmod()ing a directory tree, we set the top-level UID base to this "busy" base, so that we can always
|
||||
- * recognize trees we are were chmod()ing recursively and got interrupted in */
|
||||
-#define UID_BUSY_BASE ((uid_t) UINT32_C(0xFFFE0000))
|
||||
-#define UID_BUSY_MASK ((uid_t) UINT32_C(0xFFFF0000))
|
||||
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
|
||||
index 6f90f2f418..724639df5c 100644
|
||||
--- a/src/nspawn/nspawn.c
|
||||
+++ b/src/nspawn/nspawn.c
|
||||
@@ -73,7 +73,6 @@
|
||||
#include "nspawn-mount.h"
|
||||
#include "nspawn-network.h"
|
||||
#include "nspawn-oci.h"
|
||||
-#include "nspawn-patch-uid.h"
|
||||
#include "nspawn-register.h"
|
||||
#include "nspawn-seccomp.h"
|
||||
#include "nspawn-settings.h"
|
||||
@@ -96,6 +95,7 @@
|
||||
#include "rlimit-util.h"
|
||||
#include "rm-rf.h"
|
||||
#include "seccomp-util.h"
|
||||
+#include "shift-uid.h"
|
||||
#include "signal-util.h"
|
||||
#include "socket-util.h"
|
||||
#include "stat-util.h"
|
||||
diff --git a/src/shared/meson.build b/src/shared/meson.build
|
||||
index af9ef74b32..4684b7709d 100644
|
||||
--- a/src/shared/meson.build
|
||||
+++ b/src/shared/meson.build
|
||||
@@ -158,6 +158,7 @@ shared_sources = files(
|
||||
'selinux-util.c',
|
||||
'serialize.c',
|
||||
'service-util.c',
|
||||
+ 'shift-uid.c',
|
||||
'sleep-config.c',
|
||||
'smack-util.c',
|
||||
'smbios11.c',
|
||||
diff --git a/src/nspawn/nspawn-patch-uid.c b/src/shared/shift-uid.c
|
||||
similarity index 82%
|
||||
rename from src/nspawn/nspawn-patch-uid.c
|
||||
rename to src/shared/shift-uid.c
|
||||
index b8918a2315..435ba46c8e 100644
|
||||
--- a/src/nspawn/nspawn-patch-uid.c
|
||||
+++ b/src/shared/shift-uid.c
|
||||
@@ -1,24 +1,21 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
-#include <fcntl.h>
|
||||
#include <sys/statvfs.h>
|
||||
-#include <sys/vfs.h>
|
||||
-#include <unistd.h>
|
||||
|
||||
#include "acl-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
-#include "fs-util.h"
|
||||
#include "missing_magic.h"
|
||||
-#include "nspawn-def.h"
|
||||
-#include "nspawn-patch-uid.h"
|
||||
+#include "shift-uid.h"
|
||||
#include "stat-util.h"
|
||||
-#include "stdio-util.h"
|
||||
-#include "string-util.h"
|
||||
-#include "strv.h"
|
||||
#include "user-util.h"
|
||||
|
||||
+/* While we are chmod()ing a directory tree, we set the top-level UID base to this "busy" base, so that we can always
|
||||
+ * recognize trees we are were chmod()ing recursively and got interrupted in */
|
||||
+#define UID_BUSY_BASE ((uid_t) UINT32_C(0xFFFE0000))
|
||||
+#define UID_BUSY_MASK ((uid_t) UINT32_C(0xFFFF0000))
|
||||
+
|
||||
#if HAVE_ACL
|
||||
|
||||
static int get_acl(int fd, const char *name, acl_type_t type, acl_t *ret) {
|
||||
@@ -286,7 +283,7 @@ static int is_fs_fully_userns_compatible(const struct statfs *sfs) {
|
||||
F_TYPE_EQUAL(sfs->f_type, SYSFS_MAGIC);
|
||||
}
|
||||
|
||||
-static int recurse_fd(int fd, bool donate_fd, const struct stat *st, uid_t shift, bool is_toplevel) {
|
||||
+static int recurse_fd(int fd, const struct stat *st, uid_t shift, bool is_toplevel) {
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
bool changed = false;
|
||||
struct statfs sfs;
|
||||
@@ -303,10 +300,10 @@ static int recurse_fd(int fd, bool donate_fd, const struct stat *st, uid_t shift
|
||||
|
||||
r = is_fs_fully_userns_compatible(&sfs);
|
||||
if (r < 0)
|
||||
- goto finish;
|
||||
+ return r;
|
||||
if (r > 0) {
|
||||
r = 0; /* don't recurse */
|
||||
- goto finish;
|
||||
+ return r;
|
||||
}
|
||||
|
||||
/* Also, if we hit a read-only file system, then don't bother, skip the whole subtree */
|
||||
@@ -315,56 +312,36 @@ static int recurse_fd(int fd, bool donate_fd, const struct stat *st, uid_t shift
|
||||
goto read_only;
|
||||
|
||||
if (S_ISDIR(st->st_mode)) {
|
||||
- if (!donate_fd) {
|
||||
- int copy;
|
||||
-
|
||||
- copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
|
||||
- if (copy < 0) {
|
||||
- r = -errno;
|
||||
- goto finish;
|
||||
- }
|
||||
-
|
||||
- fd = copy;
|
||||
- donate_fd = true;
|
||||
- }
|
||||
-
|
||||
d = take_fdopendir(&fd);
|
||||
- if (!d) {
|
||||
- r = -errno;
|
||||
- goto finish;
|
||||
- }
|
||||
+ if (!d)
|
||||
+ return -errno;
|
||||
|
||||
- FOREACH_DIRENT_ALL(de, d, r = -errno; goto finish) {
|
||||
+ FOREACH_DIRENT_ALL(de, d, return -errno) {
|
||||
struct stat fst;
|
||||
|
||||
if (dot_or_dot_dot(de->d_name))
|
||||
continue;
|
||||
|
||||
- if (fstatat(dirfd(d), de->d_name, &fst, AT_SYMLINK_NOFOLLOW) < 0) {
|
||||
- r = -errno;
|
||||
- goto finish;
|
||||
- }
|
||||
+ if (fstatat(dirfd(d), de->d_name, &fst, AT_SYMLINK_NOFOLLOW) < 0)
|
||||
+ return -errno;
|
||||
|
||||
if (S_ISDIR(fst.st_mode)) {
|
||||
int subdir_fd;
|
||||
|
||||
subdir_fd = openat(dirfd(d), de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
|
||||
- if (subdir_fd < 0) {
|
||||
- r = -errno;
|
||||
- goto finish;
|
||||
-
|
||||
- }
|
||||
+ if (subdir_fd < 0)
|
||||
+ return -errno;
|
||||
|
||||
- r = recurse_fd(subdir_fd, true, &fst, shift, false);
|
||||
+ r = recurse_fd(subdir_fd, &fst, shift, false);
|
||||
if (r < 0)
|
||||
- goto finish;
|
||||
+ return r;
|
||||
if (r > 0)
|
||||
changed = true;
|
||||
|
||||
} else {
|
||||
r = patch_fd(dirfd(d), de->d_name, &fst, shift);
|
||||
if (r < 0)
|
||||
- goto finish;
|
||||
+ return r;
|
||||
if (r > 0)
|
||||
changed = true;
|
||||
}
|
||||
@@ -380,8 +357,7 @@ static int recurse_fd(int fd, bool donate_fd, const struct stat *st, uid_t shift
|
||||
if (r > 0)
|
||||
changed = true;
|
||||
|
||||
- r = changed;
|
||||
- goto finish;
|
||||
+ return changed;
|
||||
|
||||
read_only:
|
||||
if (!is_toplevel) {
|
||||
@@ -393,51 +369,41 @@ read_only:
|
||||
r = changed;
|
||||
}
|
||||
|
||||
-finish:
|
||||
- if (donate_fd)
|
||||
- safe_close(fd);
|
||||
-
|
||||
return r;
|
||||
}
|
||||
|
||||
-static int fd_patch_uid_internal(int fd, bool donate_fd, uid_t shift, uid_t range) {
|
||||
+int path_patch_uid(const char *path, uid_t shift, uid_t range) {
|
||||
+ _cleanup_close_ int fd = -EBADF;
|
||||
struct stat st;
|
||||
- int r;
|
||||
|
||||
- assert(fd >= 0);
|
||||
+ assert(path);
|
||||
+
|
||||
+ fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
|
||||
+ if (fd < 0)
|
||||
+ return -errno;
|
||||
|
||||
/* Recursively adjusts the UID/GIDs of all files of a directory tree. This is used to automatically fix up an
|
||||
* OS tree to the used user namespace UID range. Note that this automatic adjustment only works for UID ranges
|
||||
* following the concept that the upper 16-bit of a UID identify the container, and the lower 16-bit are the actual
|
||||
* UID within the container. */
|
||||
|
||||
- if ((shift & 0xFFFF) != 0) {
|
||||
- /* We only support containers where the shift starts at a 2^16 boundary */
|
||||
- r = -EOPNOTSUPP;
|
||||
- goto finish;
|
||||
- }
|
||||
+ /* We only support containers where the shift starts at a 2^16 boundary */
|
||||
+ if ((shift & 0xFFFF) != 0)
|
||||
+ return -EOPNOTSUPP;
|
||||
|
||||
- if (shift == UID_BUSY_BASE) {
|
||||
- r = -EINVAL;
|
||||
- goto finish;
|
||||
- }
|
||||
+ if (shift == UID_BUSY_BASE)
|
||||
+ return -EINVAL;
|
||||
|
||||
- if (range != 0x10000) {
|
||||
- /* We only support containers with 16-bit UID ranges for the patching logic */
|
||||
- r = -EOPNOTSUPP;
|
||||
- goto finish;
|
||||
- }
|
||||
+ /* We only support containers with 16-bit UID ranges for the patching logic */
|
||||
+ if (range != 0x10000)
|
||||
+ return -EOPNOTSUPP;
|
||||
|
||||
- if (fstat(fd, &st) < 0) {
|
||||
- r = -errno;
|
||||
- goto finish;
|
||||
- }
|
||||
+ if (fstat(fd, &st) < 0)
|
||||
+ return -errno;
|
||||
|
||||
- if ((uint32_t) st.st_uid >> 16 != (uint32_t) st.st_gid >> 16) {
|
||||
- /* We only support containers where the uid/gid container ID match */
|
||||
- r = -EBADE;
|
||||
- goto finish;
|
||||
- }
|
||||
+ /* We only support containers where the uid/gid container ID match */
|
||||
+ if ((uint32_t) st.st_uid >> 16 != (uint32_t) st.st_gid >> 16)
|
||||
+ return -EBADE;
|
||||
|
||||
/* Try to detect if the range is already right. Of course, this a pretty drastic optimization, as we assume
|
||||
* that if the top-level dir has the right upper 16-bit assigned, then everything below will have too... */
|
||||
@@ -448,30 +414,11 @@ static int fd_patch_uid_internal(int fd, bool donate_fd, uid_t shift, uid_t rang
|
||||
* range. Should we be interrupted in the middle of our work, we'll see it owned by this user and will start
|
||||
* chown()ing it again, unconditionally, as the busy UID is not a valid UID we'd everpick for ourselves. */
|
||||
|
||||
- if ((st.st_uid & UID_BUSY_MASK) != UID_BUSY_BASE) {
|
||||
+ if ((st.st_uid & UID_BUSY_MASK) != UID_BUSY_BASE)
|
||||
if (fchown(fd,
|
||||
UID_BUSY_BASE | (st.st_uid & ~UID_BUSY_MASK),
|
||||
- (gid_t) UID_BUSY_BASE | (st.st_gid & ~(gid_t) UID_BUSY_MASK)) < 0) {
|
||||
- r = -errno;
|
||||
- goto finish;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- return recurse_fd(fd, donate_fd, &st, shift, true);
|
||||
-
|
||||
-finish:
|
||||
- if (donate_fd)
|
||||
- safe_close(fd);
|
||||
-
|
||||
- return r;
|
||||
-}
|
||||
-
|
||||
-int path_patch_uid(const char *path, uid_t shift, uid_t range) {
|
||||
- int fd;
|
||||
-
|
||||
- fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
|
||||
- if (fd < 0)
|
||||
- return -errno;
|
||||
+ (gid_t) UID_BUSY_BASE | (st.st_gid & ~(gid_t) UID_BUSY_MASK)) < 0)
|
||||
+ return -errno;
|
||||
|
||||
- return fd_patch_uid_internal(fd, true, shift, range);
|
||||
+ return recurse_fd(TAKE_FD(fd), &st, shift, true);
|
||||
}
|
||||
diff --git a/src/nspawn/nspawn-patch-uid.h b/src/shared/shift-uid.h
|
||||
similarity index 100%
|
||||
rename from src/nspawn/nspawn-patch-uid.h
|
||||
rename to src/shared/shift-uid.h
|
||||
diff --git a/src/test/meson.build b/src/test/meson.build
|
||||
index d8135c226c..5dd9e3b8bd 100644
|
||||
--- a/src/test/meson.build
|
||||
+++ b/src/test/meson.build
|
||||
@@ -386,6 +386,11 @@ executables += [
|
||||
'sources' : files('test-parse-util.c'),
|
||||
'dependencies' : libm,
|
||||
},
|
||||
+ test_template + {
|
||||
+ 'sources' : files('test-shift-uid.c'),
|
||||
+ 'dependencies' : libacl,
|
||||
+ 'type' : 'manual',
|
||||
+ },
|
||||
test_template + {
|
||||
'sources' : files('test-process-util.c'),
|
||||
'dependencies' : threads,
|
||||
diff --git a/src/nspawn/test-patch-uid.c b/src/test/test-shift-uid.c
|
||||
similarity index 97%
|
||||
rename from src/nspawn/test-patch-uid.c
|
||||
rename to src/test/test-shift-uid.c
|
||||
index f8f44b0b0b..b1e272c79f 100644
|
||||
--- a/src/nspawn/test-patch-uid.c
|
||||
+++ b/src/test/test-shift-uid.c
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "log.h"
|
||||
-#include "nspawn-patch-uid.h"
|
||||
+#include "shift-uid.h"
|
||||
#include "user-util.h"
|
||||
#include "string-util.h"
|
||||
#include "tests.h"
|
||||
319
0579-user-classification-add-new-foreign-UID-range.patch
Normal file
319
0579-user-classification-add-new-foreign-UID-range.patch
Normal file
@ -0,0 +1,319 @@
|
||||
From 69b8c163b4785b80195d109333776a416b9c4318 Mon Sep 17 00:00:00 2001
|
||||
From: Lennart Poettering <lennart@poettering.net>
|
||||
Date: Fri, 8 Nov 2024 12:14:16 +0100
|
||||
Subject: [PATCH] user-classification: add new "foreign" UID range
|
||||
|
||||
This makes the UID range configurable via build time options, but of
|
||||
course it really shouldn't be changed. The default range I picked is
|
||||
outside even of IPAs current (ridiculously large) allocation ranges,
|
||||
hence hopefully minimizes conflicts.
|
||||
|
||||
(cherry picked from commit ec0c10fc9db6459d78f0b3970a0f7a34c88e6db3)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
docs/UIDS-GIDS.md | 60 ++++++++++++++++++++--------------
|
||||
docs/USER_RECORD.md | 7 ++--
|
||||
meson.build | 4 +++
|
||||
meson_options.txt | 2 ++
|
||||
src/basic/uid-classification.c | 2 +-
|
||||
src/basic/uid-classification.h | 12 +++++++
|
||||
src/core/systemd.pc.in | 2 ++
|
||||
src/dissect/dissect.c | 2 +-
|
||||
src/shared/group-record.c | 3 ++
|
||||
src/shared/user-record.c | 4 +++
|
||||
src/shared/user-record.h | 1 +
|
||||
src/userdb/userdbctl.c | 7 ++++
|
||||
12 files changed, 78 insertions(+), 28 deletions(-)
|
||||
|
||||
diff --git a/docs/UIDS-GIDS.md b/docs/UIDS-GIDS.md
|
||||
index 09488e2a78..35018b77a4 100644
|
||||
--- a/docs/UIDS-GIDS.md
|
||||
+++ b/docs/UIDS-GIDS.md
|
||||
@@ -129,10 +129,18 @@ possible.
|
||||
erroneously considers UIDs signed integers, and hence can't deal with values above 2^31.
|
||||
The `systemd-machined.service` service will synthesize user database records for all UIDs assigned to a running container from this range.
|
||||
|
||||
-Note for both allocation ranges: when a UID allocation takes place NSS is
|
||||
-checked for collisions first, and a different UID is picked if an entry is found.
|
||||
-Thus, the user database is used as synchronization mechanism to ensure
|
||||
-exclusive ownership of UIDs and UID ranges.
|
||||
+4. 2147352576…2147418111 → UID range used for foreign OS images. For various
|
||||
+ usecases (primarily: containers) it makes sense to make foreign OS images
|
||||
+ available locally whose UID/GID ownerships do not make sense in the local
|
||||
+ context but only within the OS image itself. This 64K UID range can be used
|
||||
+ to have a clearly defined ownership even on the host, that can be mapped via
|
||||
+ idmapped mount to a dynamic runtime UID range as needed. (These numbers in
|
||||
+ hexadecimal are 0x7FFE0000…0x7FFEFFFF.)
|
||||
+
|
||||
+Note for the `DynamicUser=` and the `systemd-nspawn` allocation ranges: when a
|
||||
+UID allocation takes place NSS is checked for collisions first, and a different
|
||||
+UID is picked if an entry is found. Thus, the user database is used as
|
||||
+synchronization mechanism to ensure exclusive ownership of UIDs and UID ranges.
|
||||
To ensure compatibility with other subsystems allocating from the same ranges it is hence essential that they
|
||||
ensure that whatever they pick shows up in the user/group databases, either by
|
||||
providing an NSS module, or by adding entries directly to `/etc/passwd` and `/etc/group`.
|
||||
@@ -157,6 +165,8 @@ $ pkg-config --variable=container_uid_base_min systemd
|
||||
524288
|
||||
$ pkg-config --variable=container_uid_base_max systemd
|
||||
1878982656
|
||||
+$ pkg-config --variable=foreign_uid_base systemd
|
||||
+2147352576
|
||||
```
|
||||
|
||||
(Note that the latter encodes the maximum UID *base* `systemd-nspawn` might
|
||||
@@ -164,7 +174,7 @@ pick — given that 64K UIDs are assigned to each container according to this
|
||||
allocation logic, the maximum UID used for this range is hence
|
||||
1878982656+65535=1879048191.)
|
||||
|
||||
-Systemd has compile-time default for these boundaries.
|
||||
+systemd has compile-time default for these boundaries.
|
||||
Using those defaults is recommended.
|
||||
It will nevertheless query `/etc/login.defs` at runtime, when compiled with `-Dcompat-mutable-uid-boundaries=true` and that file is present.
|
||||
Support for this is considered only a compatibility feature and should not be
|
||||
@@ -244,25 +254,27 @@ i.e. somewhere below `/var/` or similar.
|
||||
|
||||
## Summary
|
||||
|
||||
-| UID/GID | Purpose | Defined By | Listed in |
|
||||
-|-----------------------|-----------------------|---------------|-------------------------------|
|
||||
-| 0 | `root` user | Linux | `/etc/passwd` + `nss-systemd` |
|
||||
-| 1…4 | System users | Distributions | `/etc/passwd` |
|
||||
-| 5 | `tty` group | `systemd` | `/etc/passwd` |
|
||||
-| 6…999 | System users | Distributions | `/etc/passwd` |
|
||||
-| 1000…60000 | Regular users | Distributions | `/etc/passwd` + LDAP/NIS/… |
|
||||
-| 60001…60513 | Human users (homed) | `systemd` | `nss-systemd` |
|
||||
-| 60514…60577 | Host users mapped into containers | `systemd` | `systemd-nspawn` |
|
||||
-| 60578…61183 | Unused | | |
|
||||
-| 61184…65519 | Dynamic service users | `systemd` | `nss-systemd` |
|
||||
-| 65520…65533 | Unused | | |
|
||||
-| 65534 | `nobody` user | Linux | `/etc/passwd` + `nss-systemd` |
|
||||
-| 65535 | 16-bit `(uid_t) -1` | Linux | |
|
||||
-| 65536…524287 | Unused | | |
|
||||
-| 524288…1879048191 | Container UID ranges | `systemd` | `nss-systemd` |
|
||||
-| 1879048192…2147483647 | Unused | | |
|
||||
-| 2147483648…4294967294 | HIC SVNT LEONES | | |
|
||||
-| 4294967295 | 32-bit `(uid_t) -1` | Linux | |
|
||||
+| UID/GID | Same in Hexadecimal | How Many | Purpose | Defined By | Listed in |
|
||||
+|----------------------:|----------------------:|-----------:|:----------------------------------|:--------------|:------------------------------|
|
||||
+| 0 | 0x00000000 | 1 | `root` user | Linux | `/etc/passwd` + `nss-systemd` |
|
||||
+| 1…4 | 0x00000001…0x00000004 | 4 | System users | Distributions | `/etc/passwd` |
|
||||
+| 5 | 0x00000005 | 1 | `tty` group | `systemd` | `/etc/passwd` |
|
||||
+| 6…999 | 0x00000006…0x000003E7 | 994 | System users | Distributions | `/etc/passwd` |
|
||||
+| 1000…60000 | 0x000003E8…0x00001770 | 59000 | Regular users | Distributions | `/etc/passwd` + LDAP/NIS/… |
|
||||
+| 60001…60513 | 0x0000EA61…0x0000EC61 | 513 | Human users (homed) | `systemd` | `nss-systemd` |
|
||||
+| 60514…60577 | 0x0000EC62…0x0000ECA1 | 64 | Host users mapped into containers | `systemd` | `systemd-nspawn` |
|
||||
+| 60578…61183 | 0x0000ECA2…0x0000EEFF | 606 | *unused* | | |
|
||||
+| 61184…65519 | 0x0000EF00…0x0000FFEF | 4336 | Dynamic service users | `systemd` | `nss-systemd` |
|
||||
+| 65520…65533 | 0x0000FFF0…0x0000FFFD | 13 | *unused* | | |
|
||||
+| 65534 | 0x0000FFFE | 1 | `nobody` user | Linux | `/etc/passwd` + `nss-systemd` |
|
||||
+| 65535 | 0x0000FFFF | 1 | 16-bit `(uid_t) -1` | Linux | |
|
||||
+| 65536…524287 | 0x00010000…0x0007FFFF | 458752 | *unused* | | |
|
||||
+| 524288…1879048191 | 0x00080000…0x6FFFFFFF | 1878523904 | Container UID ranges | `systemd` | `nss-systemd` |
|
||||
+| 1879048192…2147352575 | 0x70000000…0x7FFDFFFF | 1879048192 | *unused* | | |
|
||||
+| 2147352576…2147418111 | 0x7FFE0000…0x7FFEFFFF | 65536 | Foreign UID range | `systemd` | `nss-systemd` |
|
||||
+| 2147418112…2147483647 | 0x7FFF0000…0x7FFFFFFF | 65536 | *unused* | | |
|
||||
+| 2147483648…4294967294 | 0x80000000…0xFFFFFFFE | 2147483647 | *HIC SVNT LEONES* | | |
|
||||
+| 4294967295 | 0xFFFFFFFF | 1 | 32-bit `(uid_t) -1` | Linux | |
|
||||
|
||||
Note that "Unused" in the table above doesn't mean that these ranges are really unused.
|
||||
It just means that these ranges have no well-established
|
||||
diff --git a/docs/USER_RECORD.md b/docs/USER_RECORD.md
|
||||
index 5babc70f65..a8e02b2c5e 100644
|
||||
--- a/docs/USER_RECORD.md
|
||||
+++ b/docs/USER_RECORD.md
|
||||
@@ -267,14 +267,17 @@ It's probably wise to use a location string processable by geo-location subsyste
|
||||
Example: `Berlin, Germany` or `Basement, Room 3a`.
|
||||
|
||||
`disposition` → A string, one of `intrinsic`, `system`, `dynamic`, `regular`,
|
||||
-`container`, `reserved`. If specified clarifies the disposition of the user,
|
||||
+`container`, `foreign`, `reserved`. If specified clarifies the disposition of the user,
|
||||
i.e. the context it is defined in.
|
||||
For regular, "human" users this should be `regular`, for system users (i.e. users that system services run under, and similar) this should be `system`.
|
||||
The `intrinsic` disposition should be used only for the two users that have special meaning to the OS kernel itself,
|
||||
i.e. the `root` and `nobody` users.
|
||||
The `container` string should be used for users that are used by an OS container, and hence will show up in `ps` listings
|
||||
and such, but are only defined in container context.
|
||||
-Finally `reserved` should be used for any users outside of these use-cases.
|
||||
+The `foreign` string should be used for users from UID ranges which are used
|
||||
+for OS images from foreign systems, i.e. where local resolution would not make
|
||||
+sense.
|
||||
+Finally, `reserved` should be used for any users outside of these use-cases.
|
||||
Note that this property is entirely optional and applications are assumed to be able to derive the
|
||||
disposition of a user automatically from a record even in absence of this
|
||||
field, based on other fields, for example the numeric UID. By setting this
|
||||
diff --git a/meson.build b/meson.build
|
||||
index 873d70f8d3..fa39da2d38 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -887,6 +887,9 @@ container_uid_base_max = get_option('container-uid-base-max')
|
||||
conf.set('CONTAINER_UID_BASE_MIN', container_uid_base_min)
|
||||
conf.set('CONTAINER_UID_BASE_MAX', container_uid_base_max)
|
||||
|
||||
+foreign_uid_base = get_option('foreign-uid-base')
|
||||
+conf.set('FOREIGN_UID_BASE', foreign_uid_base)
|
||||
+
|
||||
nobody_user = get_option('nobody-user')
|
||||
nobody_group = get_option('nobody-group')
|
||||
|
||||
@@ -3011,6 +3014,7 @@ summary({
|
||||
conf.get('SYSTEM_ALLOC_GID_MIN')),
|
||||
'dynamic UIDs' : '@0@…@1@'.format(dynamic_uid_min, dynamic_uid_max),
|
||||
'container UID bases' : '@0@…@1@'.format(container_uid_base_min, container_uid_base_max),
|
||||
+ 'foreign UID base' : '@0@'.format(foreign_uid_base),
|
||||
'static UID/GID allocations' : ' '.join(static_ugids),
|
||||
'/dev/kvm access mode' : get_option('dev-kvm-mode'),
|
||||
'render group access mode' : get_option('group-render-mode'),
|
||||
diff --git a/meson_options.txt b/meson_options.txt
|
||||
index 78b7c5fe30..df2bacdd3d 100644
|
||||
--- a/meson_options.txt
|
||||
+++ b/meson_options.txt
|
||||
@@ -273,6 +273,8 @@ option('container-uid-base-min', type : 'integer', value : 0x00080000,
|
||||
description : 'minimum container UID base')
|
||||
option('container-uid-base-max', type : 'integer', value : 0x6FFF0000,
|
||||
description : 'maximum container UID base')
|
||||
+option('foreign-uid-base', type : 'integer', value : 0x7FFE0000,
|
||||
+ description : 'foreign OS image UID base')
|
||||
option('adm-group', type : 'boolean',
|
||||
description : 'the ACL for adm group should be added')
|
||||
option('wheel-group', type : 'boolean',
|
||||
diff --git a/src/basic/uid-classification.c b/src/basic/uid-classification.c
|
||||
index 2c8b06c0d3..1295db01ae 100644
|
||||
--- a/src/basic/uid-classification.c
|
||||
+++ b/src/basic/uid-classification.c
|
||||
@@ -127,5 +127,5 @@ bool uid_for_system_journal(uid_t uid) {
|
||||
|
||||
/* Returns true if the specified UID shall get its data stored in the system journal. */
|
||||
|
||||
- return uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY;
|
||||
+ return uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY || uid_is_foreign(uid);
|
||||
}
|
||||
diff --git a/src/basic/uid-classification.h b/src/basic/uid-classification.h
|
||||
index 0932123d5c..2d76be5f04 100644
|
||||
--- a/src/basic/uid-classification.h
|
||||
+++ b/src/basic/uid-classification.h
|
||||
@@ -12,6 +12,10 @@ assert_cc((CONTAINER_UID_BASE_MAX & 0xFFFFU) == 0);
|
||||
#define CONTAINER_UID_MIN (CONTAINER_UID_BASE_MIN)
|
||||
#define CONTAINER_UID_MAX (CONTAINER_UID_BASE_MAX + 0xFFFFU)
|
||||
|
||||
+assert_cc((FOREIGN_UID_BASE & 0xFFFFU) == 0);
|
||||
+#define FOREIGN_UID_MIN (FOREIGN_UID_BASE)
|
||||
+#define FOREIGN_UID_MAX (FOREIGN_UID_BASE + 0xFFFFU)
|
||||
+
|
||||
bool uid_is_system(uid_t uid);
|
||||
bool gid_is_system(gid_t gid);
|
||||
|
||||
@@ -31,6 +35,14 @@ static inline bool gid_is_container(gid_t gid) {
|
||||
return uid_is_container((uid_t) gid);
|
||||
}
|
||||
|
||||
+static inline bool uid_is_foreign(uid_t uid) {
|
||||
+ return FOREIGN_UID_MIN <= uid && uid <= FOREIGN_UID_MAX;
|
||||
+}
|
||||
+
|
||||
+static inline bool gid_is_foreign(gid_t gid) {
|
||||
+ return uid_is_foreign((uid_t) gid);
|
||||
+}
|
||||
+
|
||||
typedef struct UGIDAllocationRange {
|
||||
uid_t system_alloc_uid_min;
|
||||
uid_t system_uid_max;
|
||||
diff --git a/src/core/systemd.pc.in b/src/core/systemd.pc.in
|
||||
index f3b85b0190..8d044dd7ad 100644
|
||||
--- a/src/core/systemd.pc.in
|
||||
+++ b/src/core/systemd.pc.in
|
||||
@@ -102,6 +102,8 @@ containeruidbasemin=${container_uid_base_min}
|
||||
container_uid_base_max={{CONTAINER_UID_BASE_MAX}}
|
||||
containeruidbasemax=${container_uid_base_max}
|
||||
|
||||
+foreign_uid_base={{FOREIGN_UID_BASE}}
|
||||
+
|
||||
Name: systemd
|
||||
Description: systemd System and Service Manager
|
||||
URL: {{PROJECT_URL}}
|
||||
diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c
|
||||
index 78a830d574..84e836027a 100644
|
||||
--- a/src/dissect/dissect.c
|
||||
+++ b/src/dissect/dissect.c
|
||||
@@ -1172,7 +1172,7 @@ static const char *pick_color_for_uid_gid(uid_t uid) {
|
||||
return ansi_normal(); /* files in disk images are typically owned by root and other system users, no issue there */
|
||||
if (uid_is_dynamic(uid))
|
||||
return ansi_highlight_red(); /* files should never be owned persistently by dynamic users, and there are just no excuses */
|
||||
- if (uid_is_container(uid))
|
||||
+ if (uid_is_container(uid) || uid_is_foreign(uid))
|
||||
return ansi_highlight_cyan();
|
||||
|
||||
return ansi_highlight();
|
||||
diff --git a/src/shared/group-record.c b/src/shared/group-record.c
|
||||
index e4a4eca99c..dcabdf634f 100644
|
||||
--- a/src/shared/group-record.c
|
||||
+++ b/src/shared/group-record.c
|
||||
@@ -302,6 +302,9 @@ UserDisposition group_record_disposition(GroupRecord *h) {
|
||||
if (gid_is_container(h->gid))
|
||||
return USER_CONTAINER;
|
||||
|
||||
+ if (gid_is_foreign(h->gid))
|
||||
+ return USER_FOREIGN;
|
||||
+
|
||||
if (h->gid > INT32_MAX)
|
||||
return USER_RESERVED;
|
||||
|
||||
diff --git a/src/shared/user-record.c b/src/shared/user-record.c
|
||||
index 8617c70aef..75605f248b 100644
|
||||
--- a/src/shared/user-record.c
|
||||
+++ b/src/shared/user-record.c
|
||||
@@ -1994,6 +1994,9 @@ UserDisposition user_record_disposition(UserRecord *h) {
|
||||
if (uid_is_container(h->uid))
|
||||
return USER_CONTAINER;
|
||||
|
||||
+ if (uid_is_foreign(h->uid))
|
||||
+ return USER_FOREIGN;
|
||||
+
|
||||
if (h->uid > INT32_MAX)
|
||||
return USER_RESERVED;
|
||||
|
||||
@@ -2736,6 +2739,7 @@ static const char* const user_disposition_table[_USER_DISPOSITION_MAX] = {
|
||||
[USER_DYNAMIC] = "dynamic",
|
||||
[USER_REGULAR] = "regular",
|
||||
[USER_CONTAINER] = "container",
|
||||
+ [USER_FOREIGN] = "foreign",
|
||||
[USER_RESERVED] = "reserved",
|
||||
};
|
||||
|
||||
diff --git a/src/shared/user-record.h b/src/shared/user-record.h
|
||||
index f8c7454f21..d80a46130a 100644
|
||||
--- a/src/shared/user-record.h
|
||||
+++ b/src/shared/user-record.h
|
||||
@@ -17,6 +17,7 @@ typedef enum UserDisposition {
|
||||
USER_DYNAMIC, /* dynamically allocated users for system services */
|
||||
USER_REGULAR, /* regular (typically human users) */
|
||||
USER_CONTAINER, /* UID ranges allocated for container uses */
|
||||
+ USER_FOREIGN, /* UID range allocated for foreign OS images */
|
||||
USER_RESERVED, /* Range above 2^31 */
|
||||
_USER_DISPOSITION_MAX,
|
||||
_USER_DISPOSITION_INVALID = -EINVAL,
|
||||
diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c
|
||||
index 0bb458eb15..f029c801a1 100644
|
||||
--- a/src/userdb/userdbctl.c
|
||||
+++ b/src/userdb/userdbctl.c
|
||||
@@ -62,6 +62,7 @@ static const char *user_disposition_to_color(UserDisposition d) {
|
||||
return ansi_green();
|
||||
|
||||
case USER_CONTAINER:
|
||||
+ case USER_FOREIGN:
|
||||
return ansi_cyan();
|
||||
|
||||
case USER_RESERVED:
|
||||
@@ -171,6 +172,12 @@ static const struct {
|
||||
.name = "container",
|
||||
.disposition = USER_CONTAINER,
|
||||
},
|
||||
+ {
|
||||
+ .first = FOREIGN_UID_MIN,
|
||||
+ .last = FOREIGN_UID_MAX,
|
||||
+ .name = "foreign",
|
||||
+ .disposition = USER_FOREIGN,
|
||||
+ },
|
||||
#if ENABLE_HOMED
|
||||
{
|
||||
.first = HOME_UID_MIN,
|
||||
388
0580-userdb-synthesize-stub-user-records-for-the-foreign-.patch
Normal file
388
0580-userdb-synthesize-stub-user-records-for-the-foreign-.patch
Normal file
@ -0,0 +1,388 @@
|
||||
From de19e6fd05fe14ef0cf661abc0a087318019ed6b Mon Sep 17 00:00:00 2001
|
||||
From: Lennart Poettering <lennart@poettering.net>
|
||||
Date: Tue, 12 Nov 2024 17:04:11 +0100
|
||||
Subject: [PATCH] userdb: synthesize stub user records for the foreign UID
|
||||
|
||||
(cherry picked from commit 44eb6b81db77216e4f34e1de37eda133d7db6945)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
man/userdbctl.xml | 6 +-
|
||||
src/nspawn/nspawn-bind-user.c | 4 +-
|
||||
src/nss-systemd/nss-systemd.c | 12 ++--
|
||||
src/shared/userdb.c | 127 +++++++++++++++++++++++++++++++---
|
||||
src/shared/userdb.h | 19 ++---
|
||||
src/userdb/userdbctl.c | 4 +-
|
||||
6 files changed, 142 insertions(+), 30 deletions(-)
|
||||
|
||||
diff --git a/man/userdbctl.xml b/man/userdbctl.xml
|
||||
index fb66ee2c0a..22d7da4d12 100644
|
||||
--- a/man/userdbctl.xml
|
||||
+++ b/man/userdbctl.xml
|
||||
@@ -136,9 +136,9 @@
|
||||
<term><option>--synthesize=<replaceable>BOOL</replaceable></option></term>
|
||||
|
||||
<listitem><para>Controls whether to synthesize records for the root and nobody users/groups if they
|
||||
- aren't defined otherwise. By default (or <literal>yes</literal>) such records are implicitly
|
||||
- synthesized if otherwise missing since they have special significance to the OS. When
|
||||
- <literal>no</literal> this synthesizing is turned off.</para>
|
||||
+ are not defined otherwise, as well as the user/groups for the "foreign" UID range. By default (or with
|
||||
+ <literal>yes</literal>), such records are implicitly synthesized if otherwise missing since they have
|
||||
+ special significance to the OS. When <literal>no</literal>, this synthesizing is turned off.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v245"/></listitem>
|
||||
</varlistentry>
|
||||
diff --git a/src/nspawn/nspawn-bind-user.c b/src/nspawn/nspawn-bind-user.c
|
||||
index d64a89f161..749accdce8 100644
|
||||
--- a/src/nspawn/nspawn-bind-user.c
|
||||
+++ b/src/nspawn/nspawn-bind-user.c
|
||||
@@ -231,7 +231,7 @@ int bind_user_prepare(
|
||||
_cleanup_(group_record_unrefp) GroupRecord *g = NULL, *cg = NULL;
|
||||
_cleanup_free_ char *sm = NULL, *sd = NULL;
|
||||
|
||||
- r = userdb_by_name(*n, USERDB_DONT_SYNTHESIZE, &u);
|
||||
+ r = userdb_by_name(*n, USERDB_DONT_SYNTHESIZE_INTRINSIC|USERDB_DONT_SYNTHESIZE_FOREIGN, &u);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to resolve user '%s': %m", *n);
|
||||
|
||||
@@ -252,7 +252,7 @@ int bind_user_prepare(
|
||||
if (u->uid >= uid_shift && u->uid < uid_shift + uid_range)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID of user '%s' to map is already in container UID range, refusing.", u->user_name);
|
||||
|
||||
- r = groupdb_by_gid(u->gid, USERDB_DONT_SYNTHESIZE, &g);
|
||||
+ r = groupdb_by_gid(u->gid, USERDB_DONT_SYNTHESIZE_INTRINSIC|USERDB_DONT_SYNTHESIZE_FOREIGN, &g);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to resolve group of user '%s': %m", u->user_name);
|
||||
|
||||
diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c
|
||||
index 8e8d4cf1cb..d686d920fc 100644
|
||||
--- a/src/nss-systemd/nss-systemd.c
|
||||
+++ b/src/nss-systemd/nss-systemd.c
|
||||
@@ -615,7 +615,7 @@ enum nss_status _nss_systemd_setpwent(int stayopen) {
|
||||
* (think: LDAP/NIS type situations), and our synthesizing of root/nobody is a robustness fallback
|
||||
* only, which matters for getpwnam()/getpwuid() primarily, which are the main NSS entrypoints to the
|
||||
* user database. */
|
||||
- r = userdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE, &getpwent_data.iterator);
|
||||
+ r = userdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE_INTRINSIC | USERDB_DONT_SYNTHESIZE_FOREIGN, &getpwent_data.iterator);
|
||||
return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -634,8 +634,8 @@ enum nss_status _nss_systemd_setgrent(int stayopen) {
|
||||
getgrent_data.iterator = userdb_iterator_free(getgrent_data.iterator);
|
||||
getgrent_data.by_membership = false;
|
||||
|
||||
- /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE here */
|
||||
- r = groupdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE, &getgrent_data.iterator);
|
||||
+ /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE_INTRINSIC here */
|
||||
+ r = groupdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE_INTRINSIC | USERDB_DONT_SYNTHESIZE_FOREIGN, &getgrent_data.iterator);
|
||||
return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -654,8 +654,8 @@ enum nss_status _nss_systemd_setspent(int stayopen) {
|
||||
getspent_data.iterator = userdb_iterator_free(getspent_data.iterator);
|
||||
getspent_data.by_membership = false;
|
||||
|
||||
- /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE here */
|
||||
- r = userdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE, &getspent_data.iterator);
|
||||
+ /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE_INTRINSIC here */
|
||||
+ r = userdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE_INTRINSIC | USERDB_DONT_SYNTHESIZE_FOREIGN, &getspent_data.iterator);
|
||||
return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -675,7 +675,7 @@ enum nss_status _nss_systemd_setsgent(int stayopen) {
|
||||
getsgent_data.by_membership = false;
|
||||
|
||||
/* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE here */
|
||||
- r = groupdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE, &getsgent_data.iterator);
|
||||
+ r = groupdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE_INTRINSIC | USERDB_DONT_SYNTHESIZE_FOREIGN, &getsgent_data.iterator);
|
||||
return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
diff --git a/src/shared/userdb.c b/src/shared/userdb.c
|
||||
index 29acebae19..1154906fd5 100644
|
||||
--- a/src/shared/userdb.c
|
||||
+++ b/src/shared/userdb.c
|
||||
@@ -16,10 +16,11 @@
|
||||
#include "set.h"
|
||||
#include "socket-util.h"
|
||||
#include "strv.h"
|
||||
+#include "uid-classification.h"
|
||||
#include "user-record-nss.h"
|
||||
#include "user-util.h"
|
||||
-#include "userdb-dropin.h"
|
||||
#include "userdb.h"
|
||||
+#include "userdb-dropin.h"
|
||||
|
||||
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(link_hash_ops, void, trivial_hash_func, trivial_compare_func, sd_varlink, sd_varlink_unref);
|
||||
|
||||
@@ -116,8 +117,8 @@ static UserDBIterator* userdb_iterator_new(LookupWhat what, UserDBFlags flags) {
|
||||
*i = (UserDBIterator) {
|
||||
.what = what,
|
||||
.flags = flags,
|
||||
- .synthesize_root = !FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE),
|
||||
- .synthesize_nobody = !FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE),
|
||||
+ .synthesize_root = !FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE_INTRINSIC),
|
||||
+ .synthesize_nobody = !FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE_INTRINSIC),
|
||||
};
|
||||
|
||||
return i;
|
||||
@@ -434,7 +435,7 @@ static int userdb_start_query(
|
||||
}
|
||||
|
||||
/* First, let's talk to the multiplexer, if we can */
|
||||
- if ((flags & (USERDB_AVOID_MULTIPLEXER|USERDB_EXCLUDE_DYNAMIC_USER|USERDB_EXCLUDE_NSS|USERDB_EXCLUDE_DROPIN|USERDB_DONT_SYNTHESIZE)) == 0 &&
|
||||
+ if ((flags & (USERDB_AVOID_MULTIPLEXER|USERDB_EXCLUDE_DYNAMIC_USER|USERDB_EXCLUDE_NSS|USERDB_EXCLUDE_DROPIN|USERDB_DONT_SYNTHESIZE_INTRINSIC|USERDB_DONT_SYNTHESIZE_FOREIGN)) == 0 &&
|
||||
!strv_contains(except, "io.systemd.Multiplexer") &&
|
||||
(!only || strv_contains(only, "io.systemd.Multiplexer"))) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *patched_query = sd_json_variant_ref(query);
|
||||
@@ -617,6 +618,63 @@ static int synthetic_nobody_user_build(UserRecord **ret) {
|
||||
SD_JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("intrinsic"))));
|
||||
}
|
||||
|
||||
+static int synthetic_foreign_user_build(uid_t foreign_uid, UserRecord **ret) {
|
||||
+ assert(ret);
|
||||
+
|
||||
+ if (!uid_is_valid(foreign_uid))
|
||||
+ return -ESRCH;
|
||||
+ if (foreign_uid > 0xFFFF)
|
||||
+ return -ESRCH;
|
||||
+
|
||||
+ _cleanup_free_ char *un = NULL;
|
||||
+ if (asprintf(&un, "foreign-" UID_FMT, foreign_uid) < 0)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ _cleanup_free_ char *rn = NULL;
|
||||
+ if (asprintf(&rn, "Foreign System Image UID " UID_FMT, foreign_uid) < 0)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ return user_record_build(
|
||||
+ ret,
|
||||
+ SD_JSON_BUILD_OBJECT(
|
||||
+ SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(un)),
|
||||
+ SD_JSON_BUILD_PAIR("realName", SD_JSON_BUILD_STRING(rn)),
|
||||
+ SD_JSON_BUILD_PAIR("uid", SD_JSON_BUILD_UNSIGNED(FOREIGN_UID_BASE + foreign_uid)),
|
||||
+ SD_JSON_BUILD_PAIR("gid", SD_JSON_BUILD_UNSIGNED(FOREIGN_UID_BASE + foreign_uid)),
|
||||
+ SD_JSON_BUILD_PAIR("shell", JSON_BUILD_CONST_STRING(NOLOGIN)),
|
||||
+ SD_JSON_BUILD_PAIR("locked", SD_JSON_BUILD_BOOLEAN(true)),
|
||||
+ SD_JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("foreign"))));
|
||||
+}
|
||||
+
|
||||
+static int user_name_foreign_extract_uid(const char *name, uid_t *ret_uid) {
|
||||
+ int r;
|
||||
+
|
||||
+ assert(name);
|
||||
+ assert(ret_uid);
|
||||
+
|
||||
+ /* Parses the inner UID from a user name of the foreign UID range, in the form "foreign-NNN". Returns
|
||||
+ * > 0 if that worked, 0 if it didn't. */
|
||||
+
|
||||
+ const char *e = startswith(name, "foreign-");
|
||||
+ if (!e)
|
||||
+ goto nomatch;
|
||||
+
|
||||
+ uid_t uid;
|
||||
+ r = parse_uid(e, &uid);
|
||||
+ if (r < 0)
|
||||
+ goto nomatch;
|
||||
+
|
||||
+ if (uid > 0xFFFF)
|
||||
+ goto nomatch;
|
||||
+
|
||||
+ *ret_uid = uid;
|
||||
+ return 1;
|
||||
+
|
||||
+nomatch:
|
||||
+ *ret_uid = UID_INVALID;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret) {
|
||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *query = NULL;
|
||||
@@ -658,7 +716,7 @@ int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret) {
|
||||
}
|
||||
}
|
||||
|
||||
- if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE)) {
|
||||
+ if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE_INTRINSIC)) {
|
||||
if (streq(name, "root"))
|
||||
return synthetic_root_user_build(ret);
|
||||
|
||||
@@ -666,6 +724,16 @@ int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret) {
|
||||
return synthetic_nobody_user_build(ret);
|
||||
}
|
||||
|
||||
+ if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE_FOREIGN)) {
|
||||
+ uid_t foreign_uid;
|
||||
+ r = user_name_foreign_extract_uid(name, &foreign_uid);
|
||||
+ if (r < 0)
|
||||
+ return r;
|
||||
+ if (r > 0)
|
||||
+ return synthetic_foreign_user_build(foreign_uid, ret);
|
||||
+ r = -ESRCH;
|
||||
+ }
|
||||
+
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -708,7 +776,7 @@ int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret) {
|
||||
}
|
||||
}
|
||||
|
||||
- if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE)) {
|
||||
+ if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE_INTRINSIC)) {
|
||||
if (uid == 0)
|
||||
return synthetic_root_user_build(ret);
|
||||
|
||||
@@ -716,6 +784,9 @@ int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret) {
|
||||
return synthetic_nobody_user_build(ret);
|
||||
}
|
||||
|
||||
+ if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE_FOREIGN) && uid_is_foreign(uid))
|
||||
+ return synthetic_foreign_user_build(uid - FOREIGN_UID_BASE, ret);
|
||||
+
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -751,6 +822,8 @@ int userdb_all(UserDBFlags flags, UserDBIterator **ret) {
|
||||
log_debug_errno(r, "Failed to find user drop-ins, ignoring: %m");
|
||||
}
|
||||
|
||||
+ /* Note that we do not enumerate the foreign users, since those would be just 64K of noise */
|
||||
+
|
||||
/* propagate IPC error, but only if there are no drop-ins */
|
||||
if (qr < 0 &&
|
||||
!iterator->nss_iterating &&
|
||||
@@ -889,6 +962,31 @@ static int synthetic_nobody_group_build(GroupRecord **ret) {
|
||||
SD_JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("intrinsic"))));
|
||||
}
|
||||
|
||||
+static int synthetic_foreign_group_build(gid_t foreign_gid, GroupRecord **ret) {
|
||||
+ assert(ret);
|
||||
+
|
||||
+ if (!gid_is_valid(foreign_gid))
|
||||
+ return -ESRCH;
|
||||
+ if (foreign_gid > 0xFFFF)
|
||||
+ return -ESRCH;
|
||||
+
|
||||
+ _cleanup_free_ char *gn = NULL;
|
||||
+ if (asprintf(&gn, "foreign-" GID_FMT, foreign_gid) < 0)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ _cleanup_free_ char *d = NULL;
|
||||
+ if (asprintf(&d, "Foreign System Image GID " GID_FMT, foreign_gid) < 0)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ return group_record_build(
|
||||
+ ret,
|
||||
+ SD_JSON_BUILD_OBJECT(
|
||||
+ SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(gn)),
|
||||
+ SD_JSON_BUILD_PAIR("description", SD_JSON_BUILD_STRING(d)),
|
||||
+ SD_JSON_BUILD_PAIR("gid", SD_JSON_BUILD_UNSIGNED(FOREIGN_UID_BASE + foreign_gid)),
|
||||
+ SD_JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("foreign"))));
|
||||
+}
|
||||
+
|
||||
int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret) {
|
||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *query = NULL;
|
||||
@@ -927,7 +1025,7 @@ int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret) {
|
||||
}
|
||||
}
|
||||
|
||||
- if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE)) {
|
||||
+ if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE_INTRINSIC)) {
|
||||
if (streq(name, "root"))
|
||||
return synthetic_root_group_build(ret);
|
||||
|
||||
@@ -935,6 +1033,16 @@ int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret) {
|
||||
return synthetic_nobody_group_build(ret);
|
||||
}
|
||||
|
||||
+ if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE_FOREIGN)) {
|
||||
+ uid_t foreign_gid;
|
||||
+ r = user_name_foreign_extract_uid(name, &foreign_gid); /* Same for UID + GID */
|
||||
+ if (r < 0)
|
||||
+ return r;
|
||||
+ if (r > 0)
|
||||
+ return synthetic_foreign_group_build(foreign_gid, ret);
|
||||
+ r = -ESRCH;
|
||||
+ }
|
||||
+
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -976,7 +1084,7 @@ int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret) {
|
||||
}
|
||||
}
|
||||
|
||||
- if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE)) {
|
||||
+ if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE_INTRINSIC)) {
|
||||
if (gid == 0)
|
||||
return synthetic_root_group_build(ret);
|
||||
|
||||
@@ -984,6 +1092,9 @@ int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret) {
|
||||
return synthetic_nobody_group_build(ret);
|
||||
}
|
||||
|
||||
+ if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE_FOREIGN) && gid_is_foreign(gid))
|
||||
+ return synthetic_foreign_group_build(gid - FOREIGN_UID_BASE, ret);
|
||||
+
|
||||
return r;
|
||||
}
|
||||
|
||||
diff --git a/src/shared/userdb.h b/src/shared/userdb.h
|
||||
index 75eb4b2dce..daf87fb5cf 100644
|
||||
--- a/src/shared/userdb.h
|
||||
+++ b/src/shared/userdb.h
|
||||
@@ -16,19 +16,20 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(UserDBIterator*, userdb_iterator_free);
|
||||
|
||||
typedef enum UserDBFlags {
|
||||
/* The main sources */
|
||||
- USERDB_EXCLUDE_NSS = 1 << 0, /* don't do client-side nor server-side NSS */
|
||||
- USERDB_EXCLUDE_VARLINK = 1 << 1, /* don't talk to any varlink services */
|
||||
- USERDB_EXCLUDE_DROPIN = 1 << 2, /* don't load drop-in user/group definitions */
|
||||
+ USERDB_EXCLUDE_NSS = 1 << 0, /* don't do client-side nor server-side NSS */
|
||||
+ USERDB_EXCLUDE_VARLINK = 1 << 1, /* don't talk to any varlink services */
|
||||
+ USERDB_EXCLUDE_DROPIN = 1 << 2, /* don't load drop-in user/group definitions */
|
||||
|
||||
/* Modifications */
|
||||
- USERDB_SUPPRESS_SHADOW = 1 << 3, /* don't do client-side shadow calls (server side might happen though) */
|
||||
- USERDB_EXCLUDE_DYNAMIC_USER = 1 << 4, /* exclude looking up in io.systemd.DynamicUser */
|
||||
- USERDB_AVOID_MULTIPLEXER = 1 << 5, /* exclude looking up via io.systemd.Multiplexer */
|
||||
- USERDB_DONT_SYNTHESIZE = 1 << 6, /* don't synthesize root/nobody */
|
||||
+ USERDB_SUPPRESS_SHADOW = 1 << 3, /* don't do client-side shadow calls (server side might happen though) */
|
||||
+ USERDB_EXCLUDE_DYNAMIC_USER = 1 << 4, /* exclude looking up in io.systemd.DynamicUser */
|
||||
+ USERDB_AVOID_MULTIPLEXER = 1 << 5, /* exclude looking up via io.systemd.Multiplexer */
|
||||
+ USERDB_DONT_SYNTHESIZE_INTRINSIC = 1 << 6, /* don't synthesize root/nobody */
|
||||
+ USERDB_DONT_SYNTHESIZE_FOREIGN = 1 << 7, /* don't synthesize foreign UID records */
|
||||
|
||||
/* Combinations */
|
||||
- USERDB_NSS_ONLY = USERDB_EXCLUDE_VARLINK|USERDB_EXCLUDE_DROPIN|USERDB_DONT_SYNTHESIZE,
|
||||
- USERDB_DROPIN_ONLY = USERDB_EXCLUDE_NSS|USERDB_EXCLUDE_VARLINK|USERDB_DONT_SYNTHESIZE,
|
||||
+ USERDB_NSS_ONLY = USERDB_EXCLUDE_VARLINK|USERDB_EXCLUDE_DROPIN|USERDB_DONT_SYNTHESIZE_INTRINSIC|USERDB_DONT_SYNTHESIZE_FOREIGN,
|
||||
+ USERDB_DROPIN_ONLY = USERDB_EXCLUDE_NSS|USERDB_EXCLUDE_VARLINK|USERDB_DONT_SYNTHESIZE_INTRINSIC|USERDB_DONT_SYNTHESIZE_FOREIGN,
|
||||
} UserDBFlags;
|
||||
|
||||
/* Well-known errors we'll return here:
|
||||
diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c
|
||||
index f029c801a1..eac7636ace 100644
|
||||
--- a/src/userdb/userdbctl.c
|
||||
+++ b/src/userdb/userdbctl.c
|
||||
@@ -1381,7 +1381,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
- arg_userdb_flags |= USERDB_EXCLUDE_NSS|USERDB_DONT_SYNTHESIZE;
|
||||
+ arg_userdb_flags |= USERDB_EXCLUDE_NSS|USERDB_DONT_SYNTHESIZE_INTRINSIC|USERDB_DONT_SYNTHESIZE_FOREIGN;
|
||||
break;
|
||||
|
||||
case ARG_WITH_NSS:
|
||||
@@ -1413,7 +1413,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
- SET_FLAG(arg_userdb_flags, USERDB_DONT_SYNTHESIZE, !r);
|
||||
+ SET_FLAG(arg_userdb_flags, USERDB_DONT_SYNTHESIZE_INTRINSIC|USERDB_DONT_SYNTHESIZE_FOREIGN, !r);
|
||||
break;
|
||||
|
||||
case ARG_MULTIPLEXER:
|
||||
199
0581-dissect-add-new-shift-command.patch
Normal file
199
0581-dissect-add-new-shift-command.patch
Normal file
@ -0,0 +1,199 @@
|
||||
From 9bda47d0ed718f7f7fe928dc0098ca13f139834e Mon Sep 17 00:00:00 2001
|
||||
From: Lennart Poettering <lennart@poettering.net>
|
||||
Date: Tue, 12 Nov 2024 09:44:48 +0100
|
||||
Subject: [PATCH] dissect: add new --shift command
|
||||
|
||||
(cherry picked from commit 55e4946f9ca75c35e87ff7f0c0d871e0d80e8ca0)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
man/systemd-dissect.xml | 24 ++++++++++++++++++++
|
||||
src/dissect/dissect.c | 49 +++++++++++++++++++++++++++++++++++++++--
|
||||
2 files changed, 71 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/man/systemd-dissect.xml b/man/systemd-dissect.xml
|
||||
index b65a2c39f1..7ff1be31bd 100644
|
||||
--- a/man/systemd-dissect.xml
|
||||
+++ b/man/systemd-dissect.xml
|
||||
@@ -62,6 +62,9 @@
|
||||
<cmdsynopsis>
|
||||
<command>systemd-dissect</command> <arg choice="opt" rep="repeat">OPTIONS</arg> <arg>--validate</arg> <arg choice="plain"><replaceable>IMAGE</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
+ <cmdsynopsis>
|
||||
+ <command>systemd-dissect</command> <arg choice="opt" rep="repeat">OPTIONS</arg> <arg>--shift</arg> <arg choice="plain"><replaceable>IMAGE</replaceable></arg> <arg choice="plain"><replaceable>UIDBASE</replaceable></arg>
|
||||
+ </cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
@@ -350,6 +353,27 @@
|
||||
<xi:include href="version-info.xml" xpointer="v254"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
+ <varlistentry>
|
||||
+ <term><option>--shift</option></term>
|
||||
+
|
||||
+ <listitem><para>Recursively iterates through all inodes of the specified image and shifts the UIDs
|
||||
+ and GIDs the inodes are owned by into the specified UID range. Takes an image path and a UID base as
|
||||
+ parameter. The UID base can be specified numerically (in which case it must be a multiple of 65536,
|
||||
+ and either 0 or within the container or foreign UID range, as per <ulink
|
||||
+ url="https://systemd.io/UIDS-GIDS/">Users, Groups, UIDs and GIDs on systemd Systems</ulink>), or as
|
||||
+ the symbolic identifier <literal>foreign</literal> which is shorthand to the foreign UID base. This
|
||||
+ command is useful for preparing directory container images for unprivileged use. Note that this
|
||||
+ command is intended for images that use the 16bit UIDs/GIDs range only, and it always ignores the
|
||||
+ upper 16bit of the current UID/GID ownership, combining the lower 16 bit with the target UID
|
||||
+ base.</para>
|
||||
+
|
||||
+ <para>Use <command>systemd-dissect --shift /some/container/tree foreign</command> to shift a
|
||||
+ container image into the foreign UID range, or <command>systemd-dissect --shift /some/container/tree
|
||||
+ 0</command> to shift it to host UID range.</para>
|
||||
+
|
||||
+ <xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
+ </varlistentry>
|
||||
+
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
<xi:include href="standard-options.xml" xpointer="version" />
|
||||
</variablelist>
|
||||
diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c
|
||||
index 84e836027a..d3d3b3b195 100644
|
||||
--- a/src/dissect/dissect.c
|
||||
+++ b/src/dissect/dissect.c
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "process-util.h"
|
||||
#include "recurse-dir.h"
|
||||
#include "sha256.h"
|
||||
+#include "shift-uid.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
@@ -68,6 +69,7 @@ static enum {
|
||||
ACTION_DISCOVER,
|
||||
ACTION_VALIDATE,
|
||||
ACTION_MAKE_ARCHIVE,
|
||||
+ ACTION_SHIFT,
|
||||
} arg_action = ACTION_DISSECT;
|
||||
static char *arg_image = NULL;
|
||||
static char *arg_root = NULL;
|
||||
@@ -95,6 +97,7 @@ static char *arg_loop_ref = NULL;
|
||||
static ImagePolicy *arg_image_policy = NULL;
|
||||
static bool arg_mtree_hash = true;
|
||||
static bool arg_via_service = false;
|
||||
+static uid_t arg_uid_base = UID_INVALID;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
|
||||
@@ -127,6 +130,7 @@ static int help(void) {
|
||||
"%1$s [OPTIONS...] --make-archive IMAGE [TARGET]\n"
|
||||
"%1$s [OPTIONS...] --discover\n"
|
||||
"%1$s [OPTIONS...] --validate IMAGE\n"
|
||||
+ "%1$s [OPTIONS...] --shift IMAGE UIDBASE\n"
|
||||
"\n%5$sDissect a Discoverable Disk Image (DDI).%6$s\n\n"
|
||||
"%3$sOptions:%4$s\n"
|
||||
" --no-pager Do not pipe output into a pager\n"
|
||||
@@ -169,6 +173,7 @@ static int help(void) {
|
||||
" --make-archive Convert the DDI to an archive file\n"
|
||||
" --discover Discover DDIs in well known directories\n"
|
||||
" --validate Validate image and image policy\n"
|
||||
+ " --shift Shift UID range to selected base\n"
|
||||
"\nSee the %2$s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link,
|
||||
@@ -274,6 +279,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_VALIDATE,
|
||||
ARG_MTREE_HASH,
|
||||
ARG_MAKE_ARCHIVE,
|
||||
+ ARG_SHIFT,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -307,6 +313,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "validate", no_argument, NULL, ARG_VALIDATE },
|
||||
{ "mtree-hash", required_argument, NULL, ARG_MTREE_HASH },
|
||||
{ "make-archive", no_argument, NULL, ARG_MAKE_ARCHIVE },
|
||||
+ { "shift", no_argument, NULL, ARG_SHIFT },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -539,6 +546,9 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_action = ACTION_MAKE_ARCHIVE;
|
||||
break;
|
||||
|
||||
+ case ARG_SHIFT:
|
||||
+ arg_action = ACTION_SHIFT;
|
||||
+ break;
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -704,6 +714,33 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_flags &= ~(DISSECT_IMAGE_PIN_PARTITION_DEVICES|DISSECT_IMAGE_ADD_PARTITION_DEVICES);
|
||||
break;
|
||||
|
||||
+ case ACTION_SHIFT:
|
||||
+ if (optind + 2 != argc)
|
||||
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
+ "Expected an image path and a UID base as only argument.");
|
||||
+
|
||||
+ r = parse_image_path_argument(argv[optind], &arg_root, &arg_image);
|
||||
+ if (r < 0)
|
||||
+ return r;
|
||||
+
|
||||
+ if (streq(argv[optind + 1], "foreign"))
|
||||
+ arg_uid_base = FOREIGN_UID_BASE;
|
||||
+ else {
|
||||
+ r = parse_uid(argv[optind + 1], &arg_uid_base);
|
||||
+ if (r < 0)
|
||||
+ return log_error_errno(r, "Failed to parse UID base: %s", argv[optind + 1]);
|
||||
+
|
||||
+ if ((arg_uid_base & 0xFFFF) != 0)
|
||||
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected UID base not a multiple of 64K: " UID_FMT, arg_uid_base);
|
||||
+ if (arg_uid_base != 0 &&
|
||||
+ !uid_is_container(arg_uid_base) &&
|
||||
+ !uid_is_foreign(arg_uid_base))
|
||||
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected UID range is not in the container range, nor the foreign one, refusing.");
|
||||
+ }
|
||||
+
|
||||
+ arg_flags |= DISSECT_IMAGE_REQUIRE_ROOT;
|
||||
+ break;
|
||||
+
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
@@ -1417,7 +1454,7 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
|
||||
const char *root;
|
||||
int r;
|
||||
|
||||
- assert(IN_SET(arg_action, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO, ACTION_MAKE_ARCHIVE));
|
||||
+ assert(IN_SET(arg_action, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO, ACTION_MAKE_ARCHIVE, ACTION_SHIFT));
|
||||
|
||||
if (arg_image) {
|
||||
assert(m);
|
||||
@@ -1673,6 +1710,13 @@ static int action_list_or_mtree_or_copy_or_make_archive(DissectedImage *m, LoopD
|
||||
#endif
|
||||
}
|
||||
|
||||
+ case ACTION_SHIFT:
|
||||
+ r = path_patch_uid(root, arg_uid_base, 0x10000);
|
||||
+ if (r < 0)
|
||||
+ return log_error_errno(r, "Failed to shift UID base: %m");
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
@@ -2082,7 +2126,7 @@ static int run(int argc, char *argv[]) {
|
||||
else
|
||||
r = loop_device_make_by_path(arg_image, open_flags, /* sector_size= */ UINT32_MAX, loop_flags, LOCK_SH, &d);
|
||||
if (r < 0) {
|
||||
- if (!ERRNO_IS_PRIVILEGE(r) || !IN_SET(arg_action, ACTION_DISSECT, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO))
|
||||
+ if (!ERRNO_IS_PRIVILEGE(r) || !IN_SET(arg_action, ACTION_DISSECT, ACTION_LIST, ACTION_MTREE, ACTION_COPY_FROM, ACTION_COPY_TO, ACTION_SHIFT))
|
||||
return log_error_errno(r, "Failed to set up loopback device for %s: %m", arg_image);
|
||||
|
||||
log_debug_errno(r, "Lacking permissions to set up loopback block device for %s, using service: %m", arg_image);
|
||||
@@ -2167,6 +2211,7 @@ static int run(int argc, char *argv[]) {
|
||||
case ACTION_COPY_FROM:
|
||||
case ACTION_COPY_TO:
|
||||
case ACTION_MAKE_ARCHIVE:
|
||||
+ case ACTION_SHIFT:
|
||||
return action_list_or_mtree_or_copy_or_make_archive(m, d, userns_fd);
|
||||
|
||||
case ACTION_WITH:
|
||||
@ -0,0 +1,95 @@
|
||||
From 269ed9b1c475c27f7b96713d1927f89616c4a465 Mon Sep 17 00:00:00 2001
|
||||
From: Lennart Poettering <lennart@poettering.net>
|
||||
Date: Tue, 14 Jan 2025 18:28:27 +0100
|
||||
Subject: [PATCH] userdb: optionally parse numeric UIDs/GIDs where a username
|
||||
is expected
|
||||
|
||||
Let's move this logic from userdbctl into generic code so that we can
|
||||
use it elsewhere.
|
||||
|
||||
(cherry picked from commit cc7300fc5868f6d47f3f47076100b574bf54e58d)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
src/shared/userdb.c | 14 ++++++++++++++
|
||||
src/shared/userdb.h | 2 ++
|
||||
src/userdb/userdbctl.c | 12 ++----------
|
||||
3 files changed, 18 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/src/shared/userdb.c b/src/shared/userdb.c
|
||||
index 1154906fd5..a1da514884 100644
|
||||
--- a/src/shared/userdb.c
|
||||
+++ b/src/shared/userdb.c
|
||||
@@ -680,6 +680,13 @@ int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *query = NULL;
|
||||
int r;
|
||||
|
||||
+ if (FLAGS_SET(flags, USERDB_PARSE_NUMERIC)) {
|
||||
+ uid_t uid;
|
||||
+
|
||||
+ if (parse_uid(name, &uid) >= 0)
|
||||
+ return userdb_by_uid(uid, flags, ret);
|
||||
+ }
|
||||
+
|
||||
if (!valid_user_group_name(name, VALID_USER_RELAX))
|
||||
return -EINVAL;
|
||||
|
||||
@@ -992,6 +999,13 @@ int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *query = NULL;
|
||||
int r;
|
||||
|
||||
+ if (FLAGS_SET(flags, USERDB_PARSE_NUMERIC)) {
|
||||
+ gid_t gid;
|
||||
+
|
||||
+ if (parse_gid(name, &gid) >= 0)
|
||||
+ return groupdb_by_gid(gid, flags, ret);
|
||||
+ }
|
||||
+
|
||||
if (!valid_user_group_name(name, VALID_USER_RELAX))
|
||||
return -EINVAL;
|
||||
|
||||
diff --git a/src/shared/userdb.h b/src/shared/userdb.h
|
||||
index daf87fb5cf..9bb47efbfe 100644
|
||||
--- a/src/shared/userdb.h
|
||||
+++ b/src/shared/userdb.h
|
||||
@@ -30,6 +30,8 @@ typedef enum UserDBFlags {
|
||||
/* Combinations */
|
||||
USERDB_NSS_ONLY = USERDB_EXCLUDE_VARLINK|USERDB_EXCLUDE_DROPIN|USERDB_DONT_SYNTHESIZE_INTRINSIC|USERDB_DONT_SYNTHESIZE_FOREIGN,
|
||||
USERDB_DROPIN_ONLY = USERDB_EXCLUDE_NSS|USERDB_EXCLUDE_VARLINK|USERDB_DONT_SYNTHESIZE_INTRINSIC|USERDB_DONT_SYNTHESIZE_FOREIGN,
|
||||
+
|
||||
+ USERDB_PARSE_NUMERIC = 1 << 8, /* if a numeric UID is specified as name, parse it and look up by UID/GID */
|
||||
} UserDBFlags;
|
||||
|
||||
/* Well-known errors we'll return here:
|
||||
diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c
|
||||
index eac7636ace..4525de3a46 100644
|
||||
--- a/src/userdb/userdbctl.c
|
||||
+++ b/src/userdb/userdbctl.c
|
||||
@@ -422,12 +422,8 @@ static int display_user(int argc, char *argv[], void *userdata) {
|
||||
} else if (argc > 1 && !arg_fuzzy)
|
||||
STRV_FOREACH(i, argv + 1) {
|
||||
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
|
||||
- uid_t uid;
|
||||
|
||||
- if (parse_uid(*i, &uid) >= 0)
|
||||
- r = userdb_by_uid(uid, arg_userdb_flags, &ur);
|
||||
- else
|
||||
- r = userdb_by_name(*i, arg_userdb_flags, &ur);
|
||||
+ r = userdb_by_name(*i, arg_userdb_flags|USERDB_PARSE_NUMERIC, &ur);
|
||||
if (r < 0) {
|
||||
if (r == -ESRCH)
|
||||
log_error_errno(r, "User %s does not exist.", *i);
|
||||
@@ -774,12 +770,8 @@ static int display_group(int argc, char *argv[], void *userdata) {
|
||||
} else if (argc > 1 && !arg_fuzzy)
|
||||
STRV_FOREACH(i, argv + 1) {
|
||||
_cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
|
||||
- gid_t gid;
|
||||
|
||||
- if (parse_gid(*i, &gid) >= 0)
|
||||
- r = groupdb_by_gid(gid, arg_userdb_flags, &gr);
|
||||
- else
|
||||
- r = groupdb_by_name(*i, arg_userdb_flags, &gr);
|
||||
+ r = groupdb_by_name(*i, arg_userdb_flags|USERDB_PARSE_NUMERIC, &gr);
|
||||
if (r < 0) {
|
||||
if (r == -ESRCH)
|
||||
log_error_errno(r, "Group %s does not exist.", *i);
|
||||
128
0583-userdbd-separate-parameter-structure-of-GetMembershi.patch
Normal file
128
0583-userdbd-separate-parameter-structure-of-GetMembershi.patch
Normal file
@ -0,0 +1,128 @@
|
||||
From 0bb32ee43fd41ed3914a9400bd2e2074e34f0f4b Mon Sep 17 00:00:00 2001
|
||||
From: Lennart Poettering <lennart@poettering.net>
|
||||
Date: Tue, 21 Jan 2025 23:28:12 +0100
|
||||
Subject: [PATCH] userdbd: separate parameter structure of GetMemberships()
|
||||
varlink call from the GetUserRecord() one
|
||||
|
||||
The GetUserRecord() and GetMemberships() have quite different arguments,
|
||||
hence let's use separate structures for both.
|
||||
|
||||
This makes sense on its own, since it makes the structures a bit
|
||||
smaller, but is also preparation for a later commit that adds a bunch of
|
||||
new fields to one of the structs but not the other.
|
||||
|
||||
(cherry picked from commit 45e587d822e60f643c0cf69584d7ef1ff03818a5)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
src/userdb/userwork.c | 41 +++++++++++++++++++++++------------------
|
||||
1 file changed, 23 insertions(+), 18 deletions(-)
|
||||
|
||||
diff --git a/src/userdb/userwork.c b/src/userdb/userwork.c
|
||||
index dce60e2ebd..c8fef87326 100644
|
||||
--- a/src/userdb/userwork.c
|
||||
+++ b/src/userdb/userwork.c
|
||||
@@ -29,8 +29,7 @@
|
||||
#define LISTEN_IDLE_USEC (90 * USEC_PER_SEC)
|
||||
|
||||
typedef struct LookupParameters {
|
||||
- const char *user_name;
|
||||
- const char *group_name;
|
||||
+ const char *name;
|
||||
union {
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
@@ -135,9 +134,9 @@ static int userdb_flags_from_service(sd_varlink *link, const char *service, User
|
||||
static int vl_method_get_user_record(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
- { "uid", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 },
|
||||
- { "userName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, user_name), SD_JSON_RELAX },
|
||||
- { "service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
|
||||
+ { "uid", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 },
|
||||
+ { "userName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, name), SD_JSON_RELAX },
|
||||
+ { "service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -162,8 +161,8 @@ static int vl_method_get_user_record(sd_varlink *link, sd_json_variant *paramete
|
||||
|
||||
if (uid_is_valid(p.uid))
|
||||
r = userdb_by_uid(p.uid, userdb_flags, &hr);
|
||||
- else if (p.user_name)
|
||||
- r = userdb_by_name(p.user_name, userdb_flags, &hr);
|
||||
+ else if (p.name)
|
||||
+ r = userdb_by_name(p.name, userdb_flags, &hr);
|
||||
else {
|
||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *last = NULL;
|
||||
@@ -215,7 +214,7 @@ static int vl_method_get_user_record(sd_varlink *link, sd_json_variant *paramete
|
||||
}
|
||||
|
||||
if ((uid_is_valid(p.uid) && hr->uid != p.uid) ||
|
||||
- (p.user_name && !user_record_matches_user_name(hr, p.user_name)))
|
||||
+ (p.name && !user_record_matches_user_name(hr, p.name)))
|
||||
return sd_varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
|
||||
|
||||
r = build_user_json(link, hr, &v);
|
||||
@@ -272,9 +271,9 @@ static int build_group_json(sd_varlink *link, GroupRecord *gr, sd_json_variant *
|
||||
static int vl_method_get_group_record(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
- { "gid", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 },
|
||||
- { "groupName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, group_name), SD_JSON_RELAX },
|
||||
- { "service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
|
||||
+ { "gid", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 },
|
||||
+ { "groupName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, name), SD_JSON_RELAX },
|
||||
+ { "service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -298,8 +297,8 @@ static int vl_method_get_group_record(sd_varlink *link, sd_json_variant *paramet
|
||||
|
||||
if (gid_is_valid(p.gid))
|
||||
r = groupdb_by_gid(p.gid, userdb_flags, &g);
|
||||
- else if (p.group_name)
|
||||
- r = groupdb_by_name(p.group_name, userdb_flags, &g);
|
||||
+ else if (p.name)
|
||||
+ r = groupdb_by_name(p.name, userdb_flags, &g);
|
||||
else {
|
||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *last = NULL;
|
||||
@@ -345,7 +344,7 @@ static int vl_method_get_group_record(sd_varlink *link, sd_json_variant *paramet
|
||||
}
|
||||
|
||||
if ((uid_is_valid(p.gid) && g->gid != p.gid) ||
|
||||
- (p.group_name && !group_record_matches_group_name(g, p.group_name)))
|
||||
+ (p.name && !group_record_matches_group_name(g, p.name)))
|
||||
return sd_varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
|
||||
|
||||
r = build_group_json(link, g, &v);
|
||||
@@ -355,17 +354,23 @@ static int vl_method_get_group_record(sd_varlink *link, sd_json_variant *paramet
|
||||
return sd_varlink_reply(link, v);
|
||||
}
|
||||
|
||||
+typedef struct MembershipLookupParameters {
|
||||
+ const char *user_name;
|
||||
+ const char *group_name;
|
||||
+ const char *service;
|
||||
+} MembershipLookupParameters;
|
||||
+
|
||||
static int vl_method_get_memberships(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
- { "userName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, user_name), SD_JSON_RELAX },
|
||||
- { "groupName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, group_name), SD_JSON_RELAX },
|
||||
- { "service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
|
||||
+ { "userName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(MembershipLookupParameters, user_name), SD_JSON_RELAX },
|
||||
+ { "groupName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(MembershipLookupParameters, group_name), SD_JSON_RELAX },
|
||||
+ { "service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MembershipLookupParameters, service), 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
_cleanup_free_ char *last_user_name = NULL, *last_group_name = NULL;
|
||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||
- LookupParameters p = {};
|
||||
+ MembershipLookupParameters p = {};
|
||||
UserDBFlags userdb_flags;
|
||||
int r;
|
||||
|
||||
@ -0,0 +1,84 @@
|
||||
From fcf21e465c847084f198b4ca2659acc011410b81 Mon Sep 17 00:00:00 2001
|
||||
From: Lennart Poettering <lennart@poettering.net>
|
||||
Date: Wed, 22 Jan 2025 16:34:16 +0100
|
||||
Subject: [PATCH] userdb: move setting of 'service' varlink parameter into
|
||||
userdb_connect()
|
||||
|
||||
We currently set this at two distinct places right before calling
|
||||
userdb_connect(). let's do this inside of userdb_connect() instead, and
|
||||
derive it directly from the socket path.
|
||||
|
||||
This doesn't change behaviour but simplifies things a bit.
|
||||
|
||||
(cherry picked from commit 6a43f0a73c33cb417a218de89858fde006214db7)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
src/shared/userdb.c | 31 +++++++++++++++----------------
|
||||
1 file changed, 15 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/src/shared/userdb.c b/src/shared/userdb.c
|
||||
index a1da514884..32f851b0f3 100644
|
||||
--- a/src/shared/userdb.c
|
||||
+++ b/src/shared/userdb.c
|
||||
@@ -384,10 +384,21 @@ static int userdb_connect(
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to bind reply callback: %m");
|
||||
|
||||
+ _cleanup_free_ char *service = NULL;
|
||||
+ r = path_extract_filename(path, &service);
|
||||
+ if (r < 0)
|
||||
+ return log_debug_errno(r, "Failed to extract service name from socket path: %m");
|
||||
+ assert(r != O_DIRECTORY);
|
||||
+
|
||||
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *patched_query = sd_json_variant_ref(query);
|
||||
+ r = sd_json_variant_set_field_string(&patched_query, "service", service);
|
||||
+ if (r < 0)
|
||||
+ return log_debug_errno(r, "Unable to set service JSON field: %m");
|
||||
+
|
||||
if (more)
|
||||
- r = sd_varlink_observe(vl, method, query);
|
||||
+ r = sd_varlink_observe(vl, method, patched_query);
|
||||
else
|
||||
- r = sd_varlink_invoke(vl, method, query);
|
||||
+ r = sd_varlink_invoke(vl, method, patched_query);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to invoke varlink method: %m");
|
||||
|
||||
@@ -438,13 +449,7 @@ static int userdb_start_query(
|
||||
if ((flags & (USERDB_AVOID_MULTIPLEXER|USERDB_EXCLUDE_DYNAMIC_USER|USERDB_EXCLUDE_NSS|USERDB_EXCLUDE_DROPIN|USERDB_DONT_SYNTHESIZE_INTRINSIC|USERDB_DONT_SYNTHESIZE_FOREIGN)) == 0 &&
|
||||
!strv_contains(except, "io.systemd.Multiplexer") &&
|
||||
(!only || strv_contains(only, "io.systemd.Multiplexer"))) {
|
||||
- _cleanup_(sd_json_variant_unrefp) sd_json_variant *patched_query = sd_json_variant_ref(query);
|
||||
-
|
||||
- r = sd_json_variant_set_field_string(&patched_query, "service", "io.systemd.Multiplexer");
|
||||
- if (r < 0)
|
||||
- return log_debug_errno(r, "Unable to set service JSON field: %m");
|
||||
-
|
||||
- r = userdb_connect(iterator, "/run/systemd/userdb/io.systemd.Multiplexer", method, more, patched_query);
|
||||
+ r = userdb_connect(iterator, "/run/systemd/userdb/io.systemd.Multiplexer", method, more, query);
|
||||
if (r >= 0) {
|
||||
iterator->nss_covered = true; /* The multiplexer does NSS */
|
||||
iterator->dropin_covered = true; /* It also handles drop-in stuff */
|
||||
@@ -461,7 +466,6 @@ static int userdb_start_query(
|
||||
}
|
||||
|
||||
FOREACH_DIRENT(de, d, return -errno) {
|
||||
- _cleanup_(sd_json_variant_unrefp) sd_json_variant *patched_query = NULL;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
bool is_nss, is_dropin;
|
||||
|
||||
@@ -495,12 +499,7 @@ static int userdb_start_query(
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
- patched_query = sd_json_variant_ref(query);
|
||||
- r = sd_json_variant_set_field_string(&patched_query, "service", de->d_name);
|
||||
- if (r < 0)
|
||||
- return log_debug_errno(r, "Unable to set service JSON field: %m");
|
||||
-
|
||||
- r = userdb_connect(iterator, p, method, more, patched_query);
|
||||
+ r = userdb_connect(iterator, p, method, more, query);
|
||||
if (is_nss && r >= 0) /* Turn off fallback NSS + dropin if we found the NSS/dropin service
|
||||
* and could connect to it */
|
||||
iterator->nss_covered = true;
|
||||
@ -0,0 +1,44 @@
|
||||
From 10ab6be5caf19592ebf487ff8075d5e0c17825b4 Mon Sep 17 00:00:00 2001
|
||||
From: Lennart Poettering <lennart@poettering.net>
|
||||
Date: Wed, 22 Jan 2025 16:24:17 +0100
|
||||
Subject: [PATCH] user-record: make a NULL UserDBMatch be equivalent to no
|
||||
filtering
|
||||
|
||||
(cherry picked from commit 83eabe102a91d4c4ac0080c31252cd60476af7ca)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
src/shared/group-record.c | 4 +++-
|
||||
src/shared/user-record.c | 4 +++-
|
||||
2 files changed, 6 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/shared/group-record.c b/src/shared/group-record.c
|
||||
index dcabdf634f..07bce7056d 100644
|
||||
--- a/src/shared/group-record.c
|
||||
+++ b/src/shared/group-record.c
|
||||
@@ -345,7 +345,9 @@ bool group_record_matches_group_name(const GroupRecord *g, const char *group_nam
|
||||
|
||||
int group_record_match(GroupRecord *h, const UserDBMatch *match) {
|
||||
assert(h);
|
||||
- assert(match);
|
||||
+
|
||||
+ if (!match)
|
||||
+ return true;
|
||||
|
||||
if (h->gid < match->gid_min || h->gid > match->gid_max)
|
||||
return false;
|
||||
diff --git a/src/shared/user-record.c b/src/shared/user-record.c
|
||||
index 75605f248b..bbf5c71849 100644
|
||||
--- a/src/shared/user-record.c
|
||||
+++ b/src/shared/user-record.c
|
||||
@@ -2693,7 +2693,9 @@ bool user_name_fuzzy_match(const char *names[], size_t n_names, char **matches)
|
||||
|
||||
int user_record_match(UserRecord *u, const UserDBMatch *match) {
|
||||
assert(u);
|
||||
- assert(match);
|
||||
+
|
||||
+ if (!match)
|
||||
+ return true;
|
||||
|
||||
if (u->uid < match->uid_min || u->uid > match->uid_max)
|
||||
return false;
|
||||
175
0586-sd-varlink-add-sd_varlink_get_description-call.patch
Normal file
175
0586-sd-varlink-add-sd_varlink_get_description-call.patch
Normal file
@ -0,0 +1,175 @@
|
||||
From e627c841a9e701c675a984e4ff01556c72b44baa Mon Sep 17 00:00:00 2001
|
||||
From: Lennart Poettering <lennart@poettering.net>
|
||||
Date: Wed, 22 Jan 2025 16:13:59 +0100
|
||||
Subject: [PATCH] sd-varlink: add sd_varlink_get_description() call
|
||||
|
||||
(cherry picked from commit 25c24619db829a589dbb4cd53ec3ccf9e2b28aa8)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
man/rules/meson.build | 1 +
|
||||
man/sd_varlink_set_description.xml | 101 +++++++++++++++++++++++++
|
||||
src/libsystemd/libsystemd.sym | 1 +
|
||||
src/libsystemd/sd-varlink/sd-varlink.c | 6 ++
|
||||
src/systemd/sd-varlink.h | 1 +
|
||||
5 files changed, 110 insertions(+)
|
||||
create mode 100644 man/sd_varlink_set_description.xml
|
||||
|
||||
diff --git a/man/rules/meson.build b/man/rules/meson.build
|
||||
index e76cb0223b..a4767eae59 100644
|
||||
--- a/man/rules/meson.build
|
||||
+++ b/man/rules/meson.build
|
||||
@@ -889,6 +889,7 @@ manpages = [
|
||||
'sd_uid_get_sessions',
|
||||
'sd_uid_is_on_seat'],
|
||||
'HAVE_PAM'],
|
||||
+ ['sd_varlink_set_description', '3', ['sd_varlink_get_description'], ''],
|
||||
['sd_watchdog_enabled', '3', [], ''],
|
||||
['shutdown', '8', [], ''],
|
||||
['smbios-type-11', '7', [], ''],
|
||||
diff --git a/man/sd_varlink_set_description.xml b/man/sd_varlink_set_description.xml
|
||||
new file mode 100644
|
||||
index 0000000000..e64cd8a5ac
|
||||
--- /dev/null
|
||||
+++ b/man/sd_varlink_set_description.xml
|
||||
@@ -0,0 +1,101 @@
|
||||
+<?xml version='1.0'?>
|
||||
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
+ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||||
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
|
||||
+
|
||||
+<refentry id="sd_varlink_set_description" xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
+
|
||||
+ <refentryinfo>
|
||||
+ <title>sd_varlink_set_description</title>
|
||||
+ <productname>systemd</productname>
|
||||
+ </refentryinfo>
|
||||
+
|
||||
+ <refmeta>
|
||||
+ <refentrytitle>sd_varlink_set_description</refentrytitle>
|
||||
+ <manvolnum>3</manvolnum>
|
||||
+ </refmeta>
|
||||
+
|
||||
+ <refnamediv>
|
||||
+ <refname>sd_varlink_set_description</refname>
|
||||
+ <refname>sd_varlink_get_description</refname>
|
||||
+
|
||||
+ <refpurpose>Set or query description of a Varlink connection object</refpurpose>
|
||||
+ </refnamediv>
|
||||
+
|
||||
+ <refsynopsisdiv>
|
||||
+ <funcsynopsis>
|
||||
+ <funcsynopsisinfo>#include <systemd/sd-link.h></funcsynopsisinfo>
|
||||
+
|
||||
+ <funcprototype>
|
||||
+ <funcdef>int <function>sd_varlink_set_description</function></funcdef>
|
||||
+ <paramdef>sd_varlink *<parameter>link</parameter></paramdef>
|
||||
+ <paramdef>const char *<parameter>description</parameter></paramdef>
|
||||
+ </funcprototype>
|
||||
+
|
||||
+ <funcprototype>
|
||||
+ <funcdef>const char* <function>sd_varlink_get_description</function></funcdef>
|
||||
+ <paramdef>sd_varlink *<parameter>link</parameter></paramdef>
|
||||
+ </funcprototype>
|
||||
+ </funcsynopsis>
|
||||
+ </refsynopsisdiv>
|
||||
+
|
||||
+ <refsect1>
|
||||
+ <title>Description</title>
|
||||
+
|
||||
+ <para><function>sd_varlink_set_description()</function> sets the description string that is used in
|
||||
+ logging to the specified string. The string is copied internally and freed when the Varlink connection
|
||||
+ object is deallocated. The <parameter>description</parameter> argument may be <constant>NULL</constant>,
|
||||
+ in which case the description is unset.</para>
|
||||
+
|
||||
+ <para><function>sd_varlink_get_description()</function> returns a description string for the specified
|
||||
+ Varlink connection. This string may have been previously set with
|
||||
+ <function>sd_varlink_set_description()</function>. If not set this way, a default string or
|
||||
+ <constant>NULL</constant> may be returned, depending how the connection was allocated and set up.</para>
|
||||
+ </refsect1>
|
||||
+
|
||||
+ <refsect1>
|
||||
+ <title>Return Value</title>
|
||||
+
|
||||
+ <para>On success, <function>sd_varlink_set_description()</function> returns a non-negative integer. On
|
||||
+ failure, it returns a negative errno-style error code. <function>sd_varlink_get_description()</function>
|
||||
+ returns either <constant>NULL</constant> or a pointer to the description string.</para>
|
||||
+
|
||||
+ <refsect2>
|
||||
+ <title>Errors</title>
|
||||
+
|
||||
+ <para>Returned errors may indicate the following problems:</para>
|
||||
+
|
||||
+ <variablelist>
|
||||
+ <varlistentry>
|
||||
+ <term><constant>-EINVAL</constant></term>
|
||||
+
|
||||
+ <listitem><para>An argument is invalid.</para></listitem>
|
||||
+ </varlistentry>
|
||||
+
|
||||
+ <varlistentry>
|
||||
+ <term><constant>-ENOMEM</constant></term>
|
||||
+
|
||||
+ <listitem><para>Memory allocation failed.</para></listitem>
|
||||
+ </varlistentry>
|
||||
+ </variablelist>
|
||||
+ </refsect2>
|
||||
+ </refsect1>
|
||||
+
|
||||
+ <xi:include href="libsystemd-pkgconfig.xml" />
|
||||
+
|
||||
+ <refsect1>
|
||||
+ <title>History</title>
|
||||
+ <para><function>sd_varlink_set_description()</function> was added in version 257.</para>
|
||||
+ <para><function>sd_varlink_get_description()</function> was added in version 258.</para>
|
||||
+ </refsect1>
|
||||
+
|
||||
+ <refsect1>
|
||||
+ <title>See Also</title>
|
||||
+
|
||||
+ <para><simplelist type="inline">
|
||||
+ <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
+ <member><citerefentry><refentrytitle>sd-varlink</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
|
||||
+ </simplelist></para>
|
||||
+ </refsect1>
|
||||
+
|
||||
+</refentry>
|
||||
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
|
||||
index 4dbe4e3c76..00ebc710be 100644
|
||||
--- a/src/libsystemd/libsystemd.sym
|
||||
+++ b/src/libsystemd/libsystemd.sym
|
||||
@@ -1063,4 +1063,5 @@ global:
|
||||
LIBSYSTEMD_258 {
|
||||
global:
|
||||
sd_varlink_get_current_method;
|
||||
+ sd_varlink_get_description;
|
||||
} LIBSYSTEMD_257;
|
||||
diff --git a/src/libsystemd/sd-varlink/sd-varlink.c b/src/libsystemd/sd-varlink/sd-varlink.c
|
||||
index 5f3b17199d..0b9bf48363 100644
|
||||
--- a/src/libsystemd/sd-varlink/sd-varlink.c
|
||||
+++ b/src/libsystemd/sd-varlink/sd-varlink.c
|
||||
@@ -2879,6 +2879,12 @@ _public_ int sd_varlink_set_description(sd_varlink *v, const char *description)
|
||||
return free_and_strdup(&v->description, description);
|
||||
}
|
||||
|
||||
+_public_ const char* sd_varlink_get_description(sd_varlink *v) {
|
||||
+ assert_return(v, NULL);
|
||||
+
|
||||
+ return v->description;
|
||||
+}
|
||||
+
|
||||
static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
sd_varlink *v = ASSERT_PTR(userdata);
|
||||
|
||||
diff --git a/src/systemd/sd-varlink.h b/src/systemd/sd-varlink.h
|
||||
index 9401d417b2..7d4da21b0f 100644
|
||||
--- a/src/systemd/sd-varlink.h
|
||||
+++ b/src/systemd/sd-varlink.h
|
||||
@@ -210,6 +210,7 @@ int sd_varlink_set_relative_timeout(sd_varlink *v, uint64_t usec);
|
||||
sd_varlink_server* sd_varlink_get_server(sd_varlink *v);
|
||||
|
||||
int sd_varlink_set_description(sd_varlink *v, const char *d);
|
||||
+const char* sd_varlink_get_description(sd_varlink *v);
|
||||
|
||||
/* Automatically mark the parameters part of incoming messages as security sensitive */
|
||||
int sd_varlink_set_input_sensitive(sd_varlink *v);
|
||||
@ -0,0 +1,78 @@
|
||||
From bcb78a95d52b54a3efd662a9e04f5a6f98f8d36e Mon Sep 17 00:00:00 2001
|
||||
From: Lennart Poettering <lennart@poettering.net>
|
||||
Date: Tue, 21 Jan 2025 12:53:02 +0100
|
||||
Subject: [PATCH] user-record: add helper for dispatching a disposition mask
|
||||
|
||||
(cherry picked from commit be093d457ffb9b8448c184fd83d49cfd98b91c96)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
src/shared/user-record.c | 34 ++++++++++++++++++++++++++++++++++
|
||||
src/shared/user-record.h | 2 ++
|
||||
2 files changed, 36 insertions(+)
|
||||
|
||||
diff --git a/src/shared/user-record.c b/src/shared/user-record.c
|
||||
index bbf5c71849..e984f6d24c 100644
|
||||
--- a/src/shared/user-record.c
|
||||
+++ b/src/shared/user-record.c
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <sys/mount.h>
|
||||
|
||||
+#include "bitfield.h"
|
||||
#include "cap-list.h"
|
||||
#include "cgroup-util.h"
|
||||
#include "dns-domain.h"
|
||||
@@ -2724,6 +2725,39 @@ int user_record_match(UserRecord *u, const UserDBMatch *match) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+int json_dispatch_dispositions_mask(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
+ uint64_t *mask = ASSERT_PTR(userdata);
|
||||
+
|
||||
+ if (sd_json_variant_is_null(variant)) {
|
||||
+ *mask = UINT64_MAX;
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ if (!sd_json_variant_is_array(variant))
|
||||
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
|
||||
+
|
||||
+ uint64_t m = 0;
|
||||
+ for (size_t i = 0; i < sd_json_variant_elements(variant); i++) {
|
||||
+ sd_json_variant *e;
|
||||
+ const char *a;
|
||||
+
|
||||
+ e = sd_json_variant_by_index(variant, i);
|
||||
+ if (!sd_json_variant_is_string(e))
|
||||
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name));
|
||||
+
|
||||
+ assert_se(a = sd_json_variant_string(e));
|
||||
+
|
||||
+ UserDisposition d = user_disposition_from_string(a);
|
||||
+ if (d < 0)
|
||||
+ return json_log(e, flags, d, "JSON field '%s' contains an invalid user disposition type: %s", strna(name), a);
|
||||
+
|
||||
+ m |= INDEX_TO_MASK(uint64_t, d);
|
||||
+ }
|
||||
+
|
||||
+ *mask = m;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static const char* const user_storage_table[_USER_STORAGE_MAX] = {
|
||||
[USER_CLASSIC] = "classic",
|
||||
[USER_LUKS] = "luks",
|
||||
diff --git a/src/shared/user-record.h b/src/shared/user-record.h
|
||||
index d80a46130a..a188dc0d0b 100644
|
||||
--- a/src/shared/user-record.h
|
||||
+++ b/src/shared/user-record.h
|
||||
@@ -493,6 +493,8 @@ int user_record_match(UserRecord *u, const UserDBMatch *match);
|
||||
|
||||
bool user_record_matches_user_name(const UserRecord *u, const char *username);
|
||||
|
||||
+int json_dispatch_dispositions_mask(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
+
|
||||
const char* user_storage_to_string(UserStorage t) _const_;
|
||||
UserStorage user_storage_from_string(const char *s) _pure_;
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
From a02347043677bb5218bdd4483fefae891da352d8 Mon Sep 17 00:00:00 2001
|
||||
From: Lennart Poettering <lennart@poettering.net>
|
||||
Date: Fri, 24 Jan 2025 22:15:29 +0100
|
||||
Subject: [PATCH] =?UTF-8?q?user-record:=20rename=20USER=5FDISPOSITION=5FMA?=
|
||||
=?UTF-8?q?SK=5FMAX=20=E2=86=92=20USER=5FDISPOSITION=5FMASK=5FALL?=
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
On request by yuwata.
|
||||
|
||||
(cherry picked from commit 27cce1f1efb564f30563eb0e3d37264bed3b093c)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
src/shared/user-record.h | 2 +-
|
||||
src/userdb/userdbctl.c | 2 +-
|
||||
2 files changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/shared/user-record.h b/src/shared/user-record.h
|
||||
index a188dc0d0b..5ecc443d22 100644
|
||||
--- a/src/shared/user-record.h
|
||||
+++ b/src/shared/user-record.h
|
||||
@@ -486,7 +486,7 @@ typedef struct UserDBMatch {
|
||||
};
|
||||
} UserDBMatch;
|
||||
|
||||
-#define USER_DISPOSITION_MASK_MAX ((UINT64_C(1) << _USER_DISPOSITION_MAX) - UINT64_C(1))
|
||||
+#define USER_DISPOSITION_MASK_ALL ((UINT64_C(1) << _USER_DISPOSITION_MAX) - UINT64_C(1))
|
||||
|
||||
bool user_name_fuzzy_match(const char *names[], size_t n_names, char **matches);
|
||||
int user_record_match(UserRecord *u, const UserDBMatch *match);
|
||||
diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c
|
||||
index 4525de3a46..2c3b274828 100644
|
||||
--- a/src/userdb/userdbctl.c
|
||||
+++ b/src/userdb/userdbctl.c
|
||||
@@ -1510,7 +1510,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
/* If not mask was specified, use the all bits on mask */
|
||||
if (arg_disposition_mask == UINT64_MAX)
|
||||
- arg_disposition_mask = USER_DISPOSITION_MASK_MAX;
|
||||
+ arg_disposition_mask = USER_DISPOSITION_MASK_ALL;
|
||||
|
||||
if (arg_from_file)
|
||||
arg_boundaries = false;
|
||||
@ -0,0 +1,55 @@
|
||||
From 14dbb7e8f0a7640bbffe6d8152e4a998a86297d8 Mon Sep 17 00:00:00 2001
|
||||
From: Lennart Poettering <lennart@poettering.net>
|
||||
Date: Wed, 22 Jan 2025 16:26:53 +0100
|
||||
Subject: [PATCH] user-record: add some helpers for working with UserDBMatch
|
||||
|
||||
(cherry picked from commit d6db229ffc94981339a6a6e41c2172b0f962c6f6)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
src/shared/user-record.h | 24 ++++++++++++++++++++++++
|
||||
1 file changed, 24 insertions(+)
|
||||
|
||||
diff --git a/src/shared/user-record.h b/src/shared/user-record.h
|
||||
index 5ecc443d22..91402f38b9 100644
|
||||
--- a/src/shared/user-record.h
|
||||
+++ b/src/shared/user-record.h
|
||||
@@ -9,7 +9,9 @@
|
||||
|
||||
#include "hashmap.h"
|
||||
#include "missing_resource.h"
|
||||
+#include "strv.h"
|
||||
#include "time-util.h"
|
||||
+#include "user-util.h"
|
||||
|
||||
typedef enum UserDisposition {
|
||||
USER_INTRINSIC, /* root and nobody */
|
||||
@@ -488,6 +490,28 @@ typedef struct UserDBMatch {
|
||||
|
||||
#define USER_DISPOSITION_MASK_ALL ((UINT64_C(1) << _USER_DISPOSITION_MAX) - UINT64_C(1))
|
||||
|
||||
+#define USERDB_MATCH_NULL \
|
||||
+ (UserDBMatch) { \
|
||||
+ .disposition_mask = USER_DISPOSITION_MASK_ALL, \
|
||||
+ .uid_min = 0, \
|
||||
+ .uid_max = UID_INVALID-1, \
|
||||
+ }
|
||||
+
|
||||
+static inline bool userdb_match_is_set(const UserDBMatch *match) {
|
||||
+ if (!match)
|
||||
+ return false;
|
||||
+
|
||||
+ return !strv_isempty(match->fuzzy_names) ||
|
||||
+ !FLAGS_SET(match->disposition_mask, USER_DISPOSITION_MASK_ALL) ||
|
||||
+ match->uid_min > 0 ||
|
||||
+ match->uid_max < UID_INVALID-1;
|
||||
+}
|
||||
+
|
||||
+static inline void userdb_match_done(UserDBMatch *match) {
|
||||
+ assert(match);
|
||||
+ strv_free(match->fuzzy_names);
|
||||
+}
|
||||
+
|
||||
bool user_name_fuzzy_match(const char *names[], size_t n_names, char **matches);
|
||||
int user_record_match(UserRecord *u, const UserDBMatch *match);
|
||||
|
||||
143
0590-varlink-add-new-calls-for-server-side-user-record-fi.patch
Normal file
143
0590-varlink-add-new-calls-for-server-side-user-record-fi.patch
Normal file
@ -0,0 +1,143 @@
|
||||
From 56df03dca7a70c341ec30fec489e2ee32531906b Mon Sep 17 00:00:00 2001
|
||||
From: Lennart Poettering <lennart@poettering.net>
|
||||
Date: Wed, 22 Jan 2025 16:27:14 +0100
|
||||
Subject: [PATCH] varlink: add new calls for server-side user record filtering
|
||||
to varlink IDL + to spec
|
||||
|
||||
This is preparation for adding server side filtering to the userdb
|
||||
logic: it adds some fields for this to the userdb varlink API. This only
|
||||
adds the IDL for it, no client will use it for now, no server implement
|
||||
it. That's added in later commits.
|
||||
|
||||
(cherry picked from commit 1ff1e0e01b8cddea89aad88671069527b981a9a2)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
docs/USER_GROUP_API.md | 43 ++++++++++++++++++++
|
||||
src/shared/varlink-io.systemd.UserDatabase.c | 19 +++++++++
|
||||
2 files changed, 62 insertions(+)
|
||||
|
||||
diff --git a/docs/USER_GROUP_API.md b/docs/USER_GROUP_API.md
|
||||
index 033c2a1b98..0beb3bb912 100644
|
||||
--- a/docs/USER_GROUP_API.md
|
||||
+++ b/docs/USER_GROUP_API.md
|
||||
@@ -161,6 +161,10 @@ interface io.systemd.UserDatabase
|
||||
method GetUserRecord(
|
||||
uid : ?int,
|
||||
userName : ?string,
|
||||
+ fuzzyNames: ?[]string,
|
||||
+ dispositionMask: ?[]string,
|
||||
+ uidMin: ?int,
|
||||
+ uidMax: ?int,
|
||||
service : string
|
||||
) -> (
|
||||
record : object,
|
||||
@@ -170,6 +174,10 @@ method GetUserRecord(
|
||||
method GetGroupRecord(
|
||||
gid : ?int,
|
||||
groupName : ?string,
|
||||
+ fuzzyNames: ?[]string,
|
||||
+ dispositionMask: ?[]string,
|
||||
+ gidMin: ?int,
|
||||
+ gidMax: ?int,
|
||||
service : string
|
||||
) -> (
|
||||
record : object,
|
||||
@@ -189,6 +197,7 @@ error NoRecordFound()
|
||||
error BadService()
|
||||
error ServiceNotAvailable()
|
||||
error ConflictingRecordFound()
|
||||
+error NonMatchingRecordFound()
|
||||
error EnumerationNotSupported()
|
||||
```
|
||||
|
||||
@@ -203,6 +212,40 @@ If neither of the two parameters are set the whole user database is enumerated.
|
||||
In this case the method call needs to be made with `more` set, so that multiple method call replies may be generated as
|
||||
effect, each carrying one user record.
|
||||
|
||||
+The `fuzzyNames`, `dispositionMask`, `uidMin`, `uidMax` fields permit
|
||||
+*additional* filtering of the returned set of user records. The `fuzzyNames`
|
||||
+parameter shall be one or more strings that shall be searched for in "fuzzy"
|
||||
+way. What specifically this means is left for the backend to decide, but
|
||||
+typically this should result in substring or string proximity matching of the
|
||||
+primary user name, the real name of the record and possibly other fields that
|
||||
+carry identifying information for the user. The `dispositionMask` field shall
|
||||
+be one of more user record `disposition` strings. If specified only user
|
||||
+records matching one of the specified dispositions should be enumerated. The
|
||||
+`uidMin` and `uidMax` fields specify a minimum and maximum value for the UID of
|
||||
+returned records. Inline searching for `uid` and `userName` support for
|
||||
+filtering with these four additional parameters is optional, and clients are
|
||||
+expected to be able to do client-side filtering in case the parameters are not
|
||||
+supported by a service. The service should return the usual `InvalidParameter`
|
||||
+error for the relevant parameter if one is passed and it does not support
|
||||
+it. If a request is made specifying `uid` or `userName` and a suitable record
|
||||
+is found, but the specified filter via `fuzzyNames`, `dispositionMask`,
|
||||
+`uidMin`, or `uidMax` does not match, a `NonMatchingRecordFound` error should
|
||||
+be returned.
|
||||
+
|
||||
+Or to say this differently: the *primary search keys* are
|
||||
+`userName`/`groupName` and `uid`/`gid` and the *secondary search filters* are
|
||||
+`fuzzyNames`, `dispositionMask`, `uidMin`, `uidMax`. If no entry matching
|
||||
+either of the primary search keys are found `NoRecordFound()` is returned. If
|
||||
+one is found that matches one but not the other primary search key
|
||||
+`ConflictingRecordFound()` is returned. If an entry is found that matches the
|
||||
+primary search key, but not the secondary match filters
|
||||
+`NonMatchingRecordFound()` is returned. Finally, if an entry is found that
|
||||
+matches both the primary search keys and the secondary search filters, they are
|
||||
+returned as successful response. Note that both the primary search keys and the
|
||||
+secondary search filters are optional, it is possible to use both, use one of
|
||||
+the two, or the other of the two, or neither (the latter for a complete dump of
|
||||
+the database).
|
||||
+
|
||||
The `service` parameter is mandatory and should be set to the service name
|
||||
being talked to (i.e. to the same name as the `AF_UNIX` socket path, with the
|
||||
`/run/systemd/userdb/` prefix removed). This is useful to allow implementation
|
||||
diff --git a/src/shared/varlink-io.systemd.UserDatabase.c b/src/shared/varlink-io.systemd.UserDatabase.c
|
||||
index a9484484e3..b2157298af 100644
|
||||
--- a/src/shared/varlink-io.systemd.UserDatabase.c
|
||||
+++ b/src/shared/varlink-io.systemd.UserDatabase.c
|
||||
@@ -7,6 +7,14 @@ static SD_VARLINK_DEFINE_METHOD_FULL(
|
||||
SD_VARLINK_SUPPORTS_MORE,
|
||||
SD_VARLINK_DEFINE_INPUT(uid, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_INPUT(userName, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
+ SD_VARLINK_FIELD_COMMENT("Names to search for in a fuzzy fashion."),
|
||||
+ SD_VARLINK_DEFINE_INPUT(fuzzyNames, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY),
|
||||
+ SD_VARLINK_FIELD_COMMENT("User dispositions to limit search by."),
|
||||
+ SD_VARLINK_DEFINE_INPUT(dispositionMask, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY),
|
||||
+ SD_VARLINK_FIELD_COMMENT("Minimum UID to restrict search too."),
|
||||
+ SD_VARLINK_DEFINE_INPUT(uidMin, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
+ SD_VARLINK_FIELD_COMMENT("Maximum UID to restrict search too."),
|
||||
+ SD_VARLINK_DEFINE_INPUT(uidMax, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_INPUT(service, SD_VARLINK_STRING, 0),
|
||||
SD_VARLINK_DEFINE_OUTPUT(record, SD_VARLINK_OBJECT, 0),
|
||||
SD_VARLINK_DEFINE_OUTPUT(incomplete, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE));
|
||||
@@ -16,6 +24,14 @@ static SD_VARLINK_DEFINE_METHOD_FULL(
|
||||
SD_VARLINK_SUPPORTS_MORE,
|
||||
SD_VARLINK_DEFINE_INPUT(gid, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_INPUT(groupName, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
+ SD_VARLINK_FIELD_COMMENT("Additional names to search for in a fuzzy fashion."),
|
||||
+ SD_VARLINK_DEFINE_INPUT(fuzzyNames, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY),
|
||||
+ SD_VARLINK_FIELD_COMMENT("Group dispositions to limit search by."),
|
||||
+ SD_VARLINK_DEFINE_INPUT(dispositionMask, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY),
|
||||
+ SD_VARLINK_FIELD_COMMENT("Minimum GID to restrict search too."),
|
||||
+ SD_VARLINK_DEFINE_INPUT(gidMin, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
+ SD_VARLINK_FIELD_COMMENT("Maximum GID to restrict search too."),
|
||||
+ SD_VARLINK_DEFINE_INPUT(gidMax, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_INPUT(service, SD_VARLINK_STRING, 0),
|
||||
SD_VARLINK_DEFINE_OUTPUT(record, SD_VARLINK_OBJECT, 0),
|
||||
SD_VARLINK_DEFINE_OUTPUT(incomplete, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE));
|
||||
@@ -34,6 +50,7 @@ static SD_VARLINK_DEFINE_ERROR(BadService);
|
||||
static SD_VARLINK_DEFINE_ERROR(ServiceNotAvailable);
|
||||
static SD_VARLINK_DEFINE_ERROR(ConflictingRecordFound);
|
||||
static SD_VARLINK_DEFINE_ERROR(EnumerationNotSupported);
|
||||
+static SD_VARLINK_DEFINE_ERROR(NonMatchingRecordFound);
|
||||
|
||||
/* As per https://systemd.io/USER_GROUP_API/ */
|
||||
SD_VARLINK_DEFINE_INTERFACE(
|
||||
@@ -46,4 +63,6 @@ SD_VARLINK_DEFINE_INTERFACE(
|
||||
&vl_error_BadService,
|
||||
&vl_error_ServiceNotAvailable,
|
||||
&vl_error_ConflictingRecordFound,
|
||||
+ SD_VARLINK_SYMBOL_COMMENT("Error indicating that there's a user record matching the primary UID/GID or user/group, but that doesn't match the additional specified matches."),
|
||||
+ &vl_error_NonMatchingRecordFound,
|
||||
&vl_error_EnumerationNotSupported);
|
||||
1361
0591-userdb-move-UserDBMatch-handling-from-userdbctl-into.patch
Normal file
1361
0591-userdb-move-UserDBMatch-handling-from-userdbctl-into.patch
Normal file
File diff suppressed because it is too large
Load Diff
162
0592-userdbd-implement-server-side-filtering-in-the-Multi.patch
Normal file
162
0592-userdbd-implement-server-side-filtering-in-the-Multi.patch
Normal file
@ -0,0 +1,162 @@
|
||||
From 9b5c0f5201194158377da6a3366fa87743395b9b Mon Sep 17 00:00:00 2001
|
||||
From: Lennart Poettering <lennart@poettering.net>
|
||||
Date: Wed, 22 Jan 2025 16:45:52 +0100
|
||||
Subject: [PATCH] userdbd: implement server side filtering in the Multiplexer
|
||||
API
|
||||
|
||||
This impelements server side filtering in userdbd's multiplexer logic.
|
||||
Note thta this means that even if some backend doesn't support it
|
||||
natively the multiplexer will deal with it and apply the filtering as
|
||||
necessary.
|
||||
|
||||
(cherry picked from commit 5ec4933dd5c38d09ed6169cbb22c79ca3be217ce)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
src/userdb/userwork.c | 53 ++++++++++++++++++++++++++++++-------------
|
||||
1 file changed, 37 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/src/userdb/userwork.c b/src/userdb/userwork.c
|
||||
index da115ec6e5..b030fae84c 100644
|
||||
--- a/src/userdb/userwork.c
|
||||
+++ b/src/userdb/userwork.c
|
||||
@@ -35,8 +35,15 @@ typedef struct LookupParameters {
|
||||
gid_t gid;
|
||||
};
|
||||
const char *service;
|
||||
+ UserDBMatch match;
|
||||
} LookupParameters;
|
||||
|
||||
+static void lookup_parameters_done(LookupParameters *p) {
|
||||
+ assert(p);
|
||||
+
|
||||
+ userdb_match_done(&p->match);
|
||||
+}
|
||||
+
|
||||
static int add_nss_service(sd_json_variant **v) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *status = NULL, *z = NULL;
|
||||
sd_id128_t mid;
|
||||
@@ -134,16 +141,21 @@ static int userdb_flags_from_service(sd_varlink *link, const char *service, User
|
||||
static int vl_method_get_user_record(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
- { "uid", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 },
|
||||
- { "userName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, name), SD_JSON_RELAX },
|
||||
- { "service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
|
||||
+ { "uid", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 },
|
||||
+ { "userName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, name), SD_JSON_RELAX },
|
||||
+ { "service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
|
||||
+ { "fuzzyNames", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(LookupParameters, match.fuzzy_names), 0 },
|
||||
+ { "dispositionMask", SD_JSON_VARIANT_ARRAY, json_dispatch_dispositions_mask, offsetof(LookupParameters, match.disposition_mask), 0 },
|
||||
+ { "uidMin", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(LookupParameters, match.uid_min), 0 },
|
||||
+ { "uidMax", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(LookupParameters, match.uid_max), 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
_cleanup_(user_record_unrefp) UserRecord *hr = NULL;
|
||||
- LookupParameters p = {
|
||||
+ _cleanup_(lookup_parameters_done) LookupParameters p = {
|
||||
.uid = UID_INVALID,
|
||||
+ .match = USERDB_MATCH_NULL,
|
||||
};
|
||||
UserDBFlags userdb_flags;
|
||||
int r;
|
||||
@@ -160,14 +172,14 @@ static int vl_method_get_user_record(sd_varlink *link, sd_json_variant *paramete
|
||||
return r;
|
||||
|
||||
if (uid_is_valid(p.uid))
|
||||
- r = userdb_by_uid(p.uid, /* match= */ NULL, userdb_flags, &hr);
|
||||
+ r = userdb_by_uid(p.uid, &p.match, userdb_flags, &hr);
|
||||
else if (p.name)
|
||||
- r = userdb_by_name(p.name, /* match= */ NULL, userdb_flags, &hr);
|
||||
+ r = userdb_by_name(p.name, &p.match, userdb_flags, &hr);
|
||||
else {
|
||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *last = NULL;
|
||||
|
||||
- r = userdb_all(/* match= */ NULL, userdb_flags, &iterator);
|
||||
+ r = userdb_all(&p.match, userdb_flags, &iterator);
|
||||
if (IN_SET(r, -ESRCH, -ENOLINK))
|
||||
/* We turn off Varlink lookups in various cases (e.g. in case we only enable DropIn
|
||||
* backend) — this might make userdb_all return ENOLINK (which indicates that varlink
|
||||
@@ -182,7 +194,7 @@ static int vl_method_get_user_record(sd_varlink *link, sd_json_variant *paramete
|
||||
for (;;) {
|
||||
_cleanup_(user_record_unrefp) UserRecord *z = NULL;
|
||||
|
||||
- r = userdb_iterator_get(iterator, /* match= */ NULL, &z);
|
||||
+ r = userdb_iterator_get(iterator, &p.match, &z);
|
||||
if (r == -ESRCH)
|
||||
break;
|
||||
if (r < 0)
|
||||
@@ -208,6 +220,8 @@ static int vl_method_get_user_record(sd_varlink *link, sd_json_variant *paramete
|
||||
}
|
||||
if (r == -ESRCH)
|
||||
return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
|
||||
+ if (r == -ENOEXEC)
|
||||
+ return sd_varlink_error(link, "io.systemd.UserDatabase.NonMatchingRecordFound", NULL);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "User lookup failed abnormally: %m");
|
||||
return sd_varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
|
||||
@@ -271,16 +285,21 @@ static int build_group_json(sd_varlink *link, GroupRecord *gr, sd_json_variant *
|
||||
static int vl_method_get_group_record(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
- { "gid", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 },
|
||||
- { "groupName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, name), SD_JSON_RELAX },
|
||||
- { "service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
|
||||
+ { "gid", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 },
|
||||
+ { "groupName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, name), SD_JSON_RELAX },
|
||||
+ { "service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
|
||||
+ { "fuzzyNames", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(LookupParameters, match.fuzzy_names), 0 },
|
||||
+ { "dispositionMask", SD_JSON_VARIANT_ARRAY, json_dispatch_dispositions_mask, offsetof(LookupParameters, match.disposition_mask), 0 },
|
||||
+ { "gidMin", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(LookupParameters, match.gid_min), 0 },
|
||||
+ { "gidMax", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(LookupParameters, match.gid_max), 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
_cleanup_(group_record_unrefp) GroupRecord *g = NULL;
|
||||
- LookupParameters p = {
|
||||
+ _cleanup_(lookup_parameters_done) LookupParameters p = {
|
||||
.gid = GID_INVALID,
|
||||
+ .match = USERDB_MATCH_NULL,
|
||||
};
|
||||
UserDBFlags userdb_flags;
|
||||
int r;
|
||||
@@ -296,14 +315,14 @@ static int vl_method_get_group_record(sd_varlink *link, sd_json_variant *paramet
|
||||
return r;
|
||||
|
||||
if (gid_is_valid(p.gid))
|
||||
- r = groupdb_by_gid(p.gid, /* match= */ NULL, userdb_flags, &g);
|
||||
+ r = groupdb_by_gid(p.gid, &p.match, userdb_flags, &g);
|
||||
else if (p.name)
|
||||
- r = groupdb_by_name(p.name, /* match= */ NULL, userdb_flags, &g);
|
||||
+ r = groupdb_by_name(p.name, &p.match, userdb_flags, &g);
|
||||
else {
|
||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *last = NULL;
|
||||
|
||||
- r = groupdb_all(/* match= */ NULL, userdb_flags, &iterator);
|
||||
+ r = groupdb_all(&p.match, userdb_flags, &iterator);
|
||||
if (IN_SET(r, -ESRCH, -ENOLINK))
|
||||
return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
|
||||
if (r < 0)
|
||||
@@ -312,7 +331,7 @@ static int vl_method_get_group_record(sd_varlink *link, sd_json_variant *paramet
|
||||
for (;;) {
|
||||
_cleanup_(group_record_unrefp) GroupRecord *z = NULL;
|
||||
|
||||
- r = groupdb_iterator_get(iterator, /* match= */ NULL, &z);
|
||||
+ r = groupdb_iterator_get(iterator, &p.match, &z);
|
||||
if (r == -ESRCH)
|
||||
break;
|
||||
if (r < 0)
|
||||
@@ -338,6 +357,8 @@ static int vl_method_get_group_record(sd_varlink *link, sd_json_variant *paramet
|
||||
}
|
||||
if (r == -ESRCH)
|
||||
return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
|
||||
+ if (r == -ENOEXEC)
|
||||
+ return sd_varlink_error(link, "io.systemd.UserDatabase.NonMatchingRecordFound", NULL);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Group lookup failed abnormally: %m");
|
||||
return sd_varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
|
||||
108
0593-homectl-port-has_regular_user-acquire_group_list-to-.patch
Normal file
108
0593-homectl-port-has_regular_user-acquire_group_list-to-.patch
Normal file
@ -0,0 +1,108 @@
|
||||
From b81ac039917b08d298ae03d59a449ae7aee0df75 Mon Sep 17 00:00:00 2001
|
||||
From: Lennart Poettering <lennart@poettering.net>
|
||||
Date: Wed, 22 Jan 2025 16:53:01 +0100
|
||||
Subject: [PATCH] homectl: port has_regular_user() + acquire_group_list() to
|
||||
use server-side filtering
|
||||
|
||||
(cherry picked from commit dea3dd66495f8d12977cb1603322a85d155057f3)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
src/home/homectl.c | 42 +++++++++++++++++++++---------------------
|
||||
1 file changed, 21 insertions(+), 21 deletions(-)
|
||||
|
||||
diff --git a/src/home/homectl.c b/src/home/homectl.c
|
||||
index 46a2a4c806..c11e6e0fdc 100644
|
||||
--- a/src/home/homectl.c
|
||||
+++ b/src/home/homectl.c
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "sd-bus.h"
|
||||
|
||||
#include "ask-password-api.h"
|
||||
+#include "bitfield.h"
|
||||
#include "build.h"
|
||||
#include "bus-common-errors.h"
|
||||
#include "bus-error.h"
|
||||
@@ -2393,36 +2394,35 @@ static int create_from_credentials(void) {
|
||||
|
||||
static int has_regular_user(void) {
|
||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||
+ UserDBMatch match = USERDB_MATCH_NULL;
|
||||
int r;
|
||||
|
||||
- r = userdb_all(/* match= */ NULL, USERDB_SUPPRESS_SHADOW, &iterator);
|
||||
+ match.disposition_mask = INDEX_TO_MASK(uint64_t, USER_REGULAR);
|
||||
+
|
||||
+ r = userdb_all(&match, USERDB_SUPPRESS_SHADOW, &iterator);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create user enumerator: %m");
|
||||
|
||||
- for (;;) {
|
||||
- _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
|
||||
-
|
||||
- r = userdb_iterator_get(iterator, /* match= */ NULL, &ur);
|
||||
- if (r == -ESRCH)
|
||||
- break;
|
||||
- if (r < 0)
|
||||
- return log_error_errno(r, "Failed to enumerate users: %m");
|
||||
-
|
||||
- if (user_record_disposition(ur) == USER_REGULAR)
|
||||
- return true;
|
||||
- }
|
||||
+ r = userdb_iterator_get(iterator, &match, /* ret= */ NULL);
|
||||
+ if (r == -ESRCH)
|
||||
+ return false;
|
||||
+ if (r < 0)
|
||||
+ return log_error_errno(r, "Failed to enumerate users: %m");
|
||||
|
||||
- return false;
|
||||
+ return true;
|
||||
}
|
||||
|
||||
static int acquire_group_list(char ***ret) {
|
||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||
_cleanup_strv_free_ char **groups = NULL;
|
||||
+ UserDBMatch match = USERDB_MATCH_NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
- r = groupdb_all(/* match= */ NULL, USERDB_SUPPRESS_SHADOW|USERDB_EXCLUDE_DYNAMIC_USER, &iterator);
|
||||
+ match.disposition_mask = INDEXES_TO_MASK(uint64_t, USER_REGULAR, USER_SYSTEM);
|
||||
+
|
||||
+ r = groupdb_all(&match, USERDB_SUPPRESS_SHADOW, &iterator);
|
||||
if (r == -ENOLINK)
|
||||
log_debug_errno(r, "No groups found. (Didn't check via Varlink.)");
|
||||
else if (r == -ESRCH)
|
||||
@@ -2433,25 +2433,25 @@ static int acquire_group_list(char ***ret) {
|
||||
for (;;) {
|
||||
_cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
|
||||
|
||||
- r = groupdb_iterator_get(iterator, /* match= */ NULL, &gr);
|
||||
+ r = groupdb_iterator_get(iterator, &match, &gr);
|
||||
if (r == -ESRCH)
|
||||
break;
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed acquire next group: %m");
|
||||
|
||||
- if (!IN_SET(group_record_disposition(gr), USER_REGULAR, USER_SYSTEM))
|
||||
- continue;
|
||||
-
|
||||
if (group_record_disposition(gr) == USER_REGULAR) {
|
||||
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
|
||||
|
||||
/* Filter groups here that belong to a specific user, and are named like them */
|
||||
|
||||
- r = userdb_by_name(gr->group_name, /* match= */ NULL, USERDB_SUPPRESS_SHADOW|USERDB_EXCLUDE_DYNAMIC_USER, &ur);
|
||||
+ UserDBMatch user_match = USERDB_MATCH_NULL;
|
||||
+ user_match.disposition_mask = INDEX_TO_MASK(uint64_t, USER_REGULAR);
|
||||
+
|
||||
+ r = userdb_by_name(gr->group_name, &user_match, USERDB_SUPPRESS_SHADOW, &ur);
|
||||
if (r < 0 && r != -ESRCH)
|
||||
return log_debug_errno(r, "Failed to check if matching user exists for group '%s': %m", gr->group_name);
|
||||
|
||||
- if (r >= 0 && ur->gid == gr->gid && user_record_disposition(ur) == USER_REGULAR)
|
||||
+ if (r >= 0 && ur->gid == gr->gid)
|
||||
continue;
|
||||
}
|
||||
|
||||
26
0594-update-TODO.patch
Normal file
26
0594-update-TODO.patch
Normal file
@ -0,0 +1,26 @@
|
||||
From bdbfe972548a78d6ac81986ccb70f54142fdd770 Mon Sep 17 00:00:00 2001
|
||||
From: Lennart Poettering <lennart@poettering.net>
|
||||
Date: Wed, 22 Jan 2025 23:27:54 +0100
|
||||
Subject: [PATCH] update TODO
|
||||
|
||||
(cherry picked from commit 35121184993fa7c9937a5c9654db3e64d5cb0815)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
TODO | 3 ---
|
||||
1 file changed, 3 deletions(-)
|
||||
|
||||
diff --git a/TODO b/TODO
|
||||
index a20eb2c61e..d6c2cef2f1 100644
|
||||
--- a/TODO
|
||||
+++ b/TODO
|
||||
@@ -1601,9 +1601,6 @@ Features:
|
||||
* add growvol and makevol options for /etc/crypttab, similar to
|
||||
x-systemd.growfs and x-systemd-makefs.
|
||||
|
||||
-* userdb: allow username prefix searches in varlink API, allow realname and
|
||||
- realname substr searches in varlink API
|
||||
-
|
||||
* userdb: allow uid/gid range checks
|
||||
|
||||
* userdb: allow existence checks
|
||||
57
0595-JSON-User-Group-records-Add-properties-for-UUIDs.patch
Normal file
57
0595-JSON-User-Group-records-Add-properties-for-UUIDs.patch
Normal file
@ -0,0 +1,57 @@
|
||||
From 6fe4e2e64df52c985acd6ccf02cc69415fbef116 Mon Sep 17 00:00:00 2001
|
||||
From: Erin Shepherd <erin.shepherd@e43.eu>
|
||||
Date: Sat, 5 Apr 2025 21:40:08 +0200
|
||||
Subject: [PATCH] JSON User/Group records: Add properties for UUIDs
|
||||
|
||||
It is useful to have stable and unique identifiers for a security principal.
|
||||
The majority of identitiy management systems in use with Unix systems today
|
||||
(e.g. Active Directory objectGUID, FreeIPA ipaUniqueID, Kanidm UUIDs) assign
|
||||
each account and group a unique UUID and exposing that to applications allows
|
||||
them to refer to accounts in a stable manner.
|
||||
|
||||
This change does not implement user or group lookup by UUID; that is left for
|
||||
a later PR.
|
||||
|
||||
(cherry picked from commit 800afbbcd7f11255b7fc0ab3948861b27be96eb8)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
docs/GROUP_RECORD.md | 6 ++++++
|
||||
docs/USER_RECORD.md | 7 +++++++
|
||||
2 files changed, 13 insertions(+)
|
||||
|
||||
diff --git a/docs/GROUP_RECORD.md b/docs/GROUP_RECORD.md
|
||||
index c055e49d43..add1a0d786 100644
|
||||
--- a/docs/GROUP_RECORD.md
|
||||
+++ b/docs/GROUP_RECORD.md
|
||||
@@ -20,6 +20,12 @@ they carry some identical (or at least very similar) fields.
|
||||
Matches the `gr_name` field of UNIX/glibc NSS `struct group`,
|
||||
or the shadow structure `struct sgrp`'s `sg_namp` field.
|
||||
|
||||
+`uuid` -> A string containing a lowercase UUID that identifies this group.
|
||||
+The same considerations apply to this field as they do to the corresponding field of user records.
|
||||
+Users and groups MUST NOT share the same UUID unless they are semantically
|
||||
+the same security principal e.g. if a system synthesizes a single-user group from
|
||||
+user records to be the user's primary group.
|
||||
+
|
||||
`realm` → The "realm" the group belongs to, conceptually identical to the same field of user records.
|
||||
A string in DNS domain name syntax.
|
||||
|
||||
diff --git a/docs/USER_RECORD.md b/docs/USER_RECORD.md
|
||||
index a8e02b2c5e..350ca76649 100644
|
||||
--- a/docs/USER_RECORD.md
|
||||
+++ b/docs/USER_RECORD.md
|
||||
@@ -234,6 +234,13 @@ retrievable and resolvable under every name listed here, pretty much everywhere
|
||||
the primary user name is. If logging in is attempted via an alias name it
|
||||
should be normalized to the primary name.
|
||||
|
||||
+`uuid` -> A string containing a lowercase UUID that identifies this user.
|
||||
+The UUID should be assigned to the user at creation, be the same across multiple machines,
|
||||
+and never change (even if the user's username, realm or other identifying attributes change).
|
||||
+When the user database is backed by Microsoft Active Directory, this field should contain
|
||||
+he value from the [objectGUID](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-ada3/937eb5c6-f6b3-4652-a276-5d6bb8979658)
|
||||
+attribute. The same UUID can be retrieved via `mbr_uid_to_uuid` on macOS.
|
||||
+
|
||||
`blobDirectory` → The absolute path to a world-readable copy of the user's blob
|
||||
directory. See [Blob Directories](/USER_RECORD_BLOB_DIRS) for more details.
|
||||
|
||||
@ -0,0 +1,89 @@
|
||||
From eee22e76c180f9afb5401c8c5e9dcee55cd70c43 Mon Sep 17 00:00:00 2001
|
||||
From: Erin Shepherd <erin.shepherd@e43.eu>
|
||||
Date: Tue, 8 Apr 2025 12:07:46 +0000
|
||||
Subject: [PATCH] userdb: add support for printing the UUID from user and group
|
||||
records
|
||||
|
||||
(cherry picked from commit 60cd0cc77a6312a260ed06e73e62d8de942dcb79)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
src/shared/group-record.c | 1 +
|
||||
src/shared/group-record.h | 1 +
|
||||
src/shared/user-record-show.c | 6 ++++++
|
||||
src/shared/user-record.c | 1 +
|
||||
src/shared/user-record.h | 1 +
|
||||
5 files changed, 10 insertions(+)
|
||||
|
||||
diff --git a/src/shared/group-record.c b/src/shared/group-record.c
|
||||
index 07bce7056d..99e1148447 100644
|
||||
--- a/src/shared/group-record.c
|
||||
+++ b/src/shared/group-record.c
|
||||
@@ -173,6 +173,7 @@ int group_record_load(
|
||||
static const sd_json_dispatch_field group_dispatch_table[] = {
|
||||
{ "groupName", SD_JSON_VARIANT_STRING, json_dispatch_user_group_name, offsetof(GroupRecord, group_name), SD_JSON_RELAX },
|
||||
{ "realm", SD_JSON_VARIANT_STRING, json_dispatch_realm, offsetof(GroupRecord, realm), 0 },
|
||||
+ { "uuid", SD_JSON_VARIANT_STRING, sd_json_dispatch_id128, offsetof(GroupRecord, uuid), 0 },
|
||||
{ "description", SD_JSON_VARIANT_STRING, json_dispatch_gecos, offsetof(GroupRecord, description), 0 },
|
||||
{ "disposition", SD_JSON_VARIANT_STRING, json_dispatch_user_disposition, offsetof(GroupRecord, disposition), 0 },
|
||||
{ "service", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(GroupRecord, service), SD_JSON_STRICT },
|
||||
diff --git a/src/shared/group-record.h b/src/shared/group-record.h
|
||||
index 5705fe2511..1a81b006ce 100644
|
||||
--- a/src/shared/group-record.h
|
||||
+++ b/src/shared/group-record.h
|
||||
@@ -13,6 +13,7 @@ typedef struct GroupRecord {
|
||||
char *group_name;
|
||||
char *realm;
|
||||
char *group_name_and_realm_auto;
|
||||
+ sd_id128_t uuid;
|
||||
|
||||
char *description;
|
||||
|
||||
diff --git a/src/shared/user-record-show.c b/src/shared/user-record-show.c
|
||||
index 754895c787..dab75d1f79 100644
|
||||
--- a/src/shared/user-record-show.c
|
||||
+++ b/src/shared/user-record-show.c
|
||||
@@ -239,6 +239,9 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
|
||||
}
|
||||
}
|
||||
|
||||
+ if (!sd_id128_is_null(hr->uuid))
|
||||
+ printf(" UUID: " SD_ID128_UUID_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(hr->uuid));
|
||||
+
|
||||
if (hr->real_name && !streq(hr->real_name, hr->user_name))
|
||||
printf(" Real Name: %s\n", hr->real_name);
|
||||
|
||||
@@ -644,6 +647,9 @@ void group_record_show(GroupRecord *gr, bool show_full_user_info) {
|
||||
if (gid_is_valid(gr->gid))
|
||||
printf(" GID: " GID_FMT "\n", gr->gid);
|
||||
|
||||
+ if (!sd_id128_is_null(gr->uuid))
|
||||
+ printf(" UUID: " SD_ID128_UUID_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(gr->uuid));
|
||||
+
|
||||
if (show_full_user_info) {
|
||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||
|
||||
diff --git a/src/shared/user-record.c b/src/shared/user-record.c
|
||||
index e984f6d24c..3b2194a9de 100644
|
||||
--- a/src/shared/user-record.c
|
||||
+++ b/src/shared/user-record.c
|
||||
@@ -1541,6 +1541,7 @@ int user_record_load(UserRecord *h, sd_json_variant *v, UserRecordLoadFlags load
|
||||
{ "userName", SD_JSON_VARIANT_STRING, json_dispatch_user_group_name, offsetof(UserRecord, user_name), SD_JSON_RELAX },
|
||||
{ "aliases", SD_JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(UserRecord, aliases), SD_JSON_RELAX },
|
||||
{ "realm", SD_JSON_VARIANT_STRING, json_dispatch_realm, offsetof(UserRecord, realm), 0 },
|
||||
+ { "uuid", SD_JSON_VARIANT_STRING, sd_json_dispatch_id128, offsetof(UserRecord, uuid), 0 },
|
||||
{ "blobDirectory", SD_JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, blob_directory), SD_JSON_STRICT },
|
||||
{ "blobManifest", SD_JSON_VARIANT_OBJECT, dispatch_blob_manifest, offsetof(UserRecord, blob_manifest), 0 },
|
||||
{ "realName", SD_JSON_VARIANT_STRING, json_dispatch_gecos, offsetof(UserRecord, real_name), 0 },
|
||||
diff --git a/src/shared/user-record.h b/src/shared/user-record.h
|
||||
index 91402f38b9..a7a3f5e924 100644
|
||||
--- a/src/shared/user-record.h
|
||||
+++ b/src/shared/user-record.h
|
||||
@@ -242,6 +242,7 @@ typedef struct UserRecord {
|
||||
char *realm;
|
||||
char *user_name_and_realm_auto; /* the user_name field concatenated with '@' and the realm, if the latter is defined */
|
||||
char **aliases;
|
||||
+ sd_id128_t uuid;
|
||||
char *real_name;
|
||||
char *email_address;
|
||||
char *password_hint;
|
||||
213
0597-userdb-add-support-for-looking-up-users-or-groups-by.patch
Normal file
213
0597-userdb-add-support-for-looking-up-users-or-groups-by.patch
Normal file
@ -0,0 +1,213 @@
|
||||
From 6f5da55f271c36fbc55a6e5f343375b25978c2ed Mon Sep 17 00:00:00 2001
|
||||
From: Erin Shepherd <erin.shepherd@e43.eu>
|
||||
Date: Fri, 11 Apr 2025 19:18:32 +0000
|
||||
Subject: [PATCH] userdb: add support for looking up users or groups by uuid.
|
||||
|
||||
This propagates the UUID lookup parameter through the API permitting
|
||||
lookups to be done by uuid.
|
||||
|
||||
(cherry picked from commit 52874bb763cf97e453ef49a9f71db7c4cc6c1322)
|
||||
|
||||
Resolves: RHEL-143036
|
||||
---
|
||||
docs/USER_GROUP_API.md | 11 +++++++----
|
||||
src/shared/user-record.c | 3 +++
|
||||
src/shared/user-record.h | 5 ++++-
|
||||
src/shared/userdb.c | 33 +++++++++++++++++----------------
|
||||
src/userdb/userwork.c | 2 ++
|
||||
5 files changed, 33 insertions(+), 21 deletions(-)
|
||||
|
||||
diff --git a/docs/USER_GROUP_API.md b/docs/USER_GROUP_API.md
|
||||
index 0beb3bb912..3e273cccb0 100644
|
||||
--- a/docs/USER_GROUP_API.md
|
||||
+++ b/docs/USER_GROUP_API.md
|
||||
@@ -165,6 +165,7 @@ method GetUserRecord(
|
||||
dispositionMask: ?[]string,
|
||||
uidMin: ?int,
|
||||
uidMax: ?int,
|
||||
+ uuid: ?string,
|
||||
service : string
|
||||
) -> (
|
||||
record : object,
|
||||
@@ -178,6 +179,7 @@ method GetGroupRecord(
|
||||
dispositionMask: ?[]string,
|
||||
gidMin: ?int,
|
||||
gidMax: ?int,
|
||||
+ uuid: ?string,
|
||||
service : string
|
||||
) -> (
|
||||
record : object,
|
||||
@@ -212,7 +214,7 @@ If neither of the two parameters are set the whole user database is enumerated.
|
||||
In this case the method call needs to be made with `more` set, so that multiple method call replies may be generated as
|
||||
effect, each carrying one user record.
|
||||
|
||||
-The `fuzzyNames`, `dispositionMask`, `uidMin`, `uidMax` fields permit
|
||||
+The `fuzzyNames`, `dispositionMask`, `uidMin`, `uidMax` and `uuid` fields permit
|
||||
*additional* filtering of the returned set of user records. The `fuzzyNames`
|
||||
parameter shall be one or more strings that shall be searched for in "fuzzy"
|
||||
way. What specifically this means is left for the backend to decide, but
|
||||
@@ -222,19 +224,20 @@ carry identifying information for the user. The `dispositionMask` field shall
|
||||
be one of more user record `disposition` strings. If specified only user
|
||||
records matching one of the specified dispositions should be enumerated. The
|
||||
`uidMin` and `uidMax` fields specify a minimum and maximum value for the UID of
|
||||
-returned records. Inline searching for `uid` and `userName` support for
|
||||
+returned records. The `uuid` field specifies to search for the user record associated
|
||||
+with the specified UUID. Inline searching for `uid` and `userName` support for
|
||||
filtering with these four additional parameters is optional, and clients are
|
||||
expected to be able to do client-side filtering in case the parameters are not
|
||||
supported by a service. The service should return the usual `InvalidParameter`
|
||||
error for the relevant parameter if one is passed and it does not support
|
||||
it. If a request is made specifying `uid` or `userName` and a suitable record
|
||||
is found, but the specified filter via `fuzzyNames`, `dispositionMask`,
|
||||
-`uidMin`, or `uidMax` does not match, a `NonMatchingRecordFound` error should
|
||||
+`uidMin`, `uidMax` or `uuid` does not match, a `NonMatchingRecordFound` error should
|
||||
be returned.
|
||||
|
||||
Or to say this differently: the *primary search keys* are
|
||||
`userName`/`groupName` and `uid`/`gid` and the *secondary search filters* are
|
||||
-`fuzzyNames`, `dispositionMask`, `uidMin`, `uidMax`. If no entry matching
|
||||
+`fuzzyNames`, `dispositionMask`, `uidMin`, `uidMax`, `uuid`. If no entry matching
|
||||
either of the primary search keys are found `NoRecordFound()` is returned. If
|
||||
one is found that matches one but not the other primary search key
|
||||
`ConflictingRecordFound()` is returned. If an entry is found that matches the
|
||||
diff --git a/src/shared/user-record.c b/src/shared/user-record.c
|
||||
index 3b2194a9de..342a1c9c4f 100644
|
||||
--- a/src/shared/user-record.c
|
||||
+++ b/src/shared/user-record.c
|
||||
@@ -2705,6 +2705,9 @@ int user_record_match(UserRecord *u, const UserDBMatch *match) {
|
||||
if (!FLAGS_SET(match->disposition_mask, UINT64_C(1) << user_record_disposition(u)))
|
||||
return false;
|
||||
|
||||
+ if (!sd_id128_is_null(match->uuid) && !sd_id128_equal(match->uuid, u->uuid))
|
||||
+ return false;
|
||||
+
|
||||
if (!strv_isempty(match->fuzzy_names)) {
|
||||
|
||||
/* Note this array of names is sparse, i.e. various entries listed in it will be
|
||||
diff --git a/src/shared/user-record.h b/src/shared/user-record.h
|
||||
index a7a3f5e924..a3d8f4eb07 100644
|
||||
--- a/src/shared/user-record.h
|
||||
+++ b/src/shared/user-record.h
|
||||
@@ -487,6 +487,7 @@ typedef struct UserDBMatch {
|
||||
uid_t uid_max;
|
||||
gid_t gid_max;
|
||||
};
|
||||
+ sd_id128_t uuid;
|
||||
} UserDBMatch;
|
||||
|
||||
#define USER_DISPOSITION_MASK_ALL ((UINT64_C(1) << _USER_DISPOSITION_MAX) - UINT64_C(1))
|
||||
@@ -496,6 +497,7 @@ typedef struct UserDBMatch {
|
||||
.disposition_mask = USER_DISPOSITION_MASK_ALL, \
|
||||
.uid_min = 0, \
|
||||
.uid_max = UID_INVALID-1, \
|
||||
+ .uuid = SD_ID128_NULL, \
|
||||
}
|
||||
|
||||
static inline bool userdb_match_is_set(const UserDBMatch *match) {
|
||||
@@ -505,7 +507,8 @@ static inline bool userdb_match_is_set(const UserDBMatch *match) {
|
||||
return !strv_isempty(match->fuzzy_names) ||
|
||||
!FLAGS_SET(match->disposition_mask, USER_DISPOSITION_MASK_ALL) ||
|
||||
match->uid_min > 0 ||
|
||||
- match->uid_max < UID_INVALID-1;
|
||||
+ match->uid_max < UID_INVALID-1 ||
|
||||
+ !sd_id128_is_null(match->uuid);
|
||||
}
|
||||
|
||||
static inline void userdb_match_done(UserDBMatch *match) {
|
||||
diff --git a/src/shared/userdb.c b/src/shared/userdb.c
|
||||
index ac505285bb..638fc5e9af 100644
|
||||
--- a/src/shared/userdb.c
|
||||
+++ b/src/shared/userdb.c
|
||||
@@ -770,27 +770,29 @@ nomatch:
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int query_append_disposition_mask(sd_json_variant **query, uint64_t mask) {
|
||||
+static int query_append_common(sd_json_variant **query, const UserDBMatch *match) {
|
||||
int r;
|
||||
+ _cleanup_strv_free_ char **dispositions = NULL;
|
||||
|
||||
assert(query);
|
||||
|
||||
- if (FLAGS_SET(mask, USER_DISPOSITION_MASK_ALL))
|
||||
- return 0;
|
||||
-
|
||||
- _cleanup_strv_free_ char **dispositions = NULL;
|
||||
- for (UserDisposition d = 0; d < _USER_DISPOSITION_MAX; d++) {
|
||||
- if (!BITS_SET(mask, d))
|
||||
- continue;
|
||||
+ uint64_t mask = match->disposition_mask;
|
||||
+ if (FLAGS_SET(mask, USER_DISPOSITION_MASK_ALL)) {
|
||||
+ for (UserDisposition d = 0; d < _USER_DISPOSITION_MAX; d++) {
|
||||
+ if (!BIT_SET(mask, d))
|
||||
+ continue;
|
||||
|
||||
- r = strv_extend(&dispositions, user_disposition_to_string(d));
|
||||
- if (r < 0)
|
||||
- return r;
|
||||
+ r = strv_extend(&dispositions, user_disposition_to_string(d));
|
||||
+ if (r < 0)
|
||||
+ return r;
|
||||
+ }
|
||||
}
|
||||
|
||||
return sd_json_variant_merge_objectbo(
|
||||
query,
|
||||
- SD_JSON_BUILD_PAIR_STRV("dispositionMask", dispositions));
|
||||
+ SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(match->fuzzy_names), "fuzzyNames", SD_JSON_BUILD_STRV(match->fuzzy_names)),
|
||||
+ SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(match->uuid), "uuid", SD_JSON_BUILD_UUID(match->uuid)),
|
||||
+ SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(dispositions), "dispositionMask", SD_JSON_BUILD_STRV(dispositions)));
|
||||
}
|
||||
|
||||
static int query_append_uid_match(sd_json_variant **query, const UserDBMatch *match) {
|
||||
@@ -803,13 +805,12 @@ static int query_append_uid_match(sd_json_variant **query, const UserDBMatch *ma
|
||||
|
||||
r = sd_json_variant_merge_objectbo(
|
||||
query,
|
||||
- SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(match->fuzzy_names), "fuzzyNames", SD_JSON_BUILD_STRV(match->fuzzy_names)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(match->uid_min > 0, "uidMin", SD_JSON_BUILD_UNSIGNED(match->uid_min)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(match->uid_max < UID_INVALID-1, "uidMax", SD_JSON_BUILD_UNSIGNED(match->uid_max)));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
- return query_append_disposition_mask(query, match->disposition_mask);
|
||||
+ return query_append_common(query, match);
|
||||
}
|
||||
|
||||
static int userdb_by_name_fallbacks(
|
||||
@@ -1248,13 +1249,13 @@ static int query_append_gid_match(sd_json_variant **query, const UserDBMatch *ma
|
||||
|
||||
r = sd_json_variant_merge_objectbo(
|
||||
query,
|
||||
- SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(match->fuzzy_names), "fuzzyNames", SD_JSON_BUILD_STRV(match->fuzzy_names)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(match->gid_min > 0, "gidMin", SD_JSON_BUILD_UNSIGNED(match->gid_min)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(match->gid_max < GID_INVALID-1, "gidMax", SD_JSON_BUILD_UNSIGNED(match->gid_max)));
|
||||
+
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
- return query_append_disposition_mask(query, match->disposition_mask);
|
||||
+ return query_append_common(query, match);
|
||||
}
|
||||
|
||||
static int groupdb_by_name_fallbacks(
|
||||
diff --git a/src/userdb/userwork.c b/src/userdb/userwork.c
|
||||
index b030fae84c..d079af4dcb 100644
|
||||
--- a/src/userdb/userwork.c
|
||||
+++ b/src/userdb/userwork.c
|
||||
@@ -148,6 +148,7 @@ static int vl_method_get_user_record(sd_varlink *link, sd_json_variant *paramete
|
||||
{ "dispositionMask", SD_JSON_VARIANT_ARRAY, json_dispatch_dispositions_mask, offsetof(LookupParameters, match.disposition_mask), 0 },
|
||||
{ "uidMin", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(LookupParameters, match.uid_min), 0 },
|
||||
{ "uidMax", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(LookupParameters, match.uid_max), 0 },
|
||||
+ { "uuid", SD_JSON_VARIANT_STRING, sd_json_dispatch_id128, offsetof(LookupParameters, match.uuid), 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -292,6 +293,7 @@ static int vl_method_get_group_record(sd_varlink *link, sd_json_variant *paramet
|
||||
{ "dispositionMask", SD_JSON_VARIANT_ARRAY, json_dispatch_dispositions_mask, offsetof(LookupParameters, match.disposition_mask), 0 },
|
||||
{ "gidMin", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(LookupParameters, match.gid_min), 0 },
|
||||
{ "gidMax", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(LookupParameters, match.gid_max), 0 },
|
||||
+ { "uuid", SD_JSON_VARIANT_STRING, sd_json_dispatch_id128, offsetof(LookupParameters, match.uuid), 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
111
0598-userdbctl-add-uuid-filtering-option.patch
Normal file
111
0598-userdbctl-add-uuid-filtering-option.patch
Normal file
@ -0,0 +1,111 @@
|
||||
From 9a92c83a44c68511b52302058a95de89239cbf15 Mon Sep 17 00:00:00 2001
|
||||
From: Erin Shepherd <erin.shepherd@e43.eu>
|
||||
Date: Mon, 21 Jul 2025 19:29:53 +0000
|
||||
Subject: [PATCH] userdbctl: add --uuid filtering option
|
||||
|
||||
This uses the new UUID-based filtering logic inside the userdb library
|
||||
to return just the requested user/group record
|
||||
|
||||
(cherry picked from commit 466562c69b75cec197176f556b940a43bb8350f2)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
man/userdbctl.xml | 9 +++++++++
|
||||
src/userdb/userdbctl.c | 15 +++++++++++++--
|
||||
2 files changed, 22 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/man/userdbctl.xml b/man/userdbctl.xml
|
||||
index 22d7da4d12..110b1c0f35 100644
|
||||
--- a/man/userdbctl.xml
|
||||
+++ b/man/userdbctl.xml
|
||||
@@ -225,6 +225,15 @@
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
+ <varlistentry>
|
||||
+ <term><option>--uuid=</option></term>
|
||||
+
|
||||
+ <listitem><para>When used with the <command>user</command> or <command>group</command> command,
|
||||
+ filters the output to the record with the specified UUID. If unspecified, no UUID-based filtering is applied.</para>
|
||||
+
|
||||
+ <xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
+ </varlistentry>
|
||||
+
|
||||
<varlistentry>
|
||||
<term><option>--boundaries=</option></term>
|
||||
|
||||
diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c
|
||||
index 074d9103af..cb9d2e1897 100644
|
||||
--- a/src/userdb/userdbctl.c
|
||||
+++ b/src/userdb/userdbctl.c
|
||||
@@ -42,6 +42,7 @@ static bool arg_chain = false;
|
||||
static uint64_t arg_disposition_mask = UINT64_MAX;
|
||||
static uid_t arg_uid_min = 0;
|
||||
static uid_t arg_uid_max = UID_INVALID-1;
|
||||
+static sd_id128_t arg_uuid = SD_ID128_NULL;
|
||||
static bool arg_fuzzy = false;
|
||||
static bool arg_boundaries = true;
|
||||
static sd_json_variant *arg_from_file = NULL;
|
||||
@@ -381,7 +382,7 @@ static int display_user(int argc, char *argv[], void *userdata) {
|
||||
int ret = 0, r;
|
||||
|
||||
if (arg_output < 0)
|
||||
- arg_output = arg_from_file || (argc > 1 && !arg_fuzzy) ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
|
||||
+ arg_output = arg_from_file || (argc > 1 && !arg_fuzzy) || !sd_id128_is_null(arg_uuid) ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
|
||||
|
||||
if (arg_output == OUTPUT_TABLE) {
|
||||
table = table_new(" ", "name", "disposition", "uid", "gid", "realname", "home", "shell", "order");
|
||||
@@ -401,6 +402,7 @@ static int display_user(int argc, char *argv[], void *userdata) {
|
||||
.disposition_mask = arg_disposition_mask,
|
||||
.uid_min = arg_uid_min,
|
||||
.uid_max = arg_uid_max,
|
||||
+ .uuid = arg_uuid,
|
||||
};
|
||||
|
||||
if (arg_from_file) {
|
||||
@@ -723,7 +725,7 @@ static int display_group(int argc, char *argv[], void *userdata) {
|
||||
int ret = 0, r;
|
||||
|
||||
if (arg_output < 0)
|
||||
- arg_output = arg_from_file || (argc > 1 && !arg_fuzzy) ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
|
||||
+ arg_output = arg_from_file || (argc > 1 && !arg_fuzzy) || !sd_id128_is_null(arg_uuid) ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
|
||||
|
||||
if (arg_output == OUTPUT_TABLE) {
|
||||
table = table_new(" ", "name", "disposition", "gid", "description", "order");
|
||||
@@ -742,6 +744,7 @@ static int display_group(int argc, char *argv[], void *userdata) {
|
||||
.disposition_mask = arg_disposition_mask,
|
||||
.gid_min = arg_uid_min,
|
||||
.gid_max = arg_uid_max,
|
||||
+ .uuid = arg_uuid,
|
||||
};
|
||||
|
||||
if (arg_from_file) {
|
||||
@@ -1235,6 +1238,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_CHAIN,
|
||||
ARG_UID_MIN,
|
||||
ARG_UID_MAX,
|
||||
+ ARG_UUID,
|
||||
ARG_DISPOSITION,
|
||||
ARG_BOUNDARIES,
|
||||
};
|
||||
@@ -1255,6 +1259,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "chain", no_argument, NULL, ARG_CHAIN },
|
||||
{ "uid-min", required_argument, NULL, ARG_UID_MIN },
|
||||
{ "uid-max", required_argument, NULL, ARG_UID_MAX },
|
||||
+ { "uuid", required_argument, NULL, ARG_UUID },
|
||||
{ "fuzzy", no_argument, NULL, 'z' },
|
||||
{ "disposition", required_argument, NULL, ARG_DISPOSITION },
|
||||
{ "boundaries", required_argument, NULL, ARG_BOUNDARIES },
|
||||
@@ -1450,6 +1455,12 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return log_error_errno(r, "Failed to parse --uid-max= value: %s", optarg);
|
||||
break;
|
||||
|
||||
+ case ARG_UUID:
|
||||
+ r = sd_id128_from_string(optarg, &arg_uuid);
|
||||
+ if (r < 0)
|
||||
+ return log_error_errno(r, "Failed to parse --uuid= value: %s", optarg);
|
||||
+ break;
|
||||
+
|
||||
case 'z':
|
||||
arg_fuzzy = true;
|
||||
break;
|
||||
26
0599-userdbctl-add-missing-uuid-to-help-text.patch
Normal file
26
0599-userdbctl-add-missing-uuid-to-help-text.patch
Normal file
@ -0,0 +1,26 @@
|
||||
From 09282345b595d817725be5333baa08e16ce1f8de Mon Sep 17 00:00:00 2001
|
||||
From: Lennart Poettering <lennart@poettering.net>
|
||||
Date: Sat, 20 Dec 2025 06:33:55 +0100
|
||||
Subject: [PATCH] userdbctl: add missing --uuid= to --help text
|
||||
|
||||
Follow-up for: 466562c69b75cec197176f556b940a43bb8350f2
|
||||
|
||||
(cherry picked from commit f42ac2477258987cf6982c19b05cb46fabca16c9)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
src/userdb/userdbctl.c | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c
|
||||
index cb9d2e1897..4c304cc656 100644
|
||||
--- a/src/userdb/userdbctl.c
|
||||
+++ b/src/userdb/userdbctl.c
|
||||
@@ -1205,6 +1205,7 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" --chain Chain another command\n"
|
||||
" --uid-min=ID Filter by minimum UID/GID (default 0)\n"
|
||||
" --uid-max=ID Filter by maximum UID/GID (default 4294967294)\n"
|
||||
+ " --uuid=UUID Filter by UUID\n"
|
||||
" -z --fuzzy Do a fuzzy name search\n"
|
||||
" --disposition=VALUE Filter by disposition\n"
|
||||
" -I Equivalent to --disposition=intrinsic\n"
|
||||
27
0600-userdb-fix-typo.patch
Normal file
27
0600-userdb-fix-typo.patch
Normal file
@ -0,0 +1,27 @@
|
||||
From d4761084258e69b0f02d5802635ff943607b7625 Mon Sep 17 00:00:00 2001
|
||||
From: Yu Watanabe <watanabe.yu+github@gmail.com>
|
||||
Date: Sat, 1 Feb 2025 14:09:03 +0900
|
||||
Subject: [PATCH] userdb: fix typo
|
||||
|
||||
Follow-up for 7419291670dd4066594350cce585031f60bc4f0a.
|
||||
|
||||
(cherry picked from commit 546e6cb2e3e949e7162e4cf45cce8a6b26db175d)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
src/shared/userdb.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/shared/userdb.c b/src/shared/userdb.c
|
||||
index 638fc5e9af..d4eecbd001 100644
|
||||
--- a/src/shared/userdb.c
|
||||
+++ b/src/shared/userdb.c
|
||||
@@ -196,7 +196,7 @@ static int userdb_maybe_restart_query(
|
||||
NULL
|
||||
};
|
||||
|
||||
- /* Figure out if the reported error indicates any of the suppressable fields are at fault, and that
|
||||
+ /* Figure out if the reported error indicates any of the suppressible fields are at fault, and that
|
||||
* our query actually included them */
|
||||
bool restart = false;
|
||||
STRV_FOREACH(f, fields) {
|
||||
27
0601-man-userdbctl-fixup-version-info.patch
Normal file
27
0601-man-userdbctl-fixup-version-info.patch
Normal file
@ -0,0 +1,27 @@
|
||||
From f1e64d4b3583e78e4fbbee9f21e663974d0af1fe Mon Sep 17 00:00:00 2001
|
||||
From: Mike Yuan <me@yhndnzj.com>
|
||||
Date: Wed, 8 Oct 2025 22:17:38 +0200
|
||||
Subject: [PATCH] man/userdbctl: fixup version info
|
||||
|
||||
Follow-up for 466562c69b75cec197176f556b940a43bb8350f2
|
||||
|
||||
(cherry picked from commit fd99d9d1cb53f32dea46f1770446db729c316304)
|
||||
|
||||
Related: RHEL-143036
|
||||
---
|
||||
man/userdbctl.xml | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/man/userdbctl.xml b/man/userdbctl.xml
|
||||
index 110b1c0f35..4da9762ab0 100644
|
||||
--- a/man/userdbctl.xml
|
||||
+++ b/man/userdbctl.xml
|
||||
@@ -231,7 +231,7 @@
|
||||
<listitem><para>When used with the <command>user</command> or <command>group</command> command,
|
||||
filters the output to the record with the specified UUID. If unspecified, no UUID-based filtering is applied.</para>
|
||||
|
||||
- <xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
+ <xi:include href="version-info.xml" xpointer="v259"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
@ -0,0 +1,80 @@
|
||||
From 41d06bdfa06e1a230661a4281b4003cb53ba3a71 Mon Sep 17 00:00:00 2001
|
||||
From: Lennart Poettering <lennart@poettering.net>
|
||||
Date: Wed, 19 Feb 2025 17:15:56 +0100
|
||||
Subject: [PATCH] user-record: add a concept of inverting per-host matching
|
||||
sections in user record
|
||||
|
||||
Sometimes it is useful to apply options on all hosts except some. Add a
|
||||
simple concept for that.
|
||||
|
||||
(cherry picked from commit ce94761debfab321d608e5c4ea876d7bc7f65097)
|
||||
|
||||
Resolves: RHEL-143034
|
||||
---
|
||||
src/shared/user-record.c | 24 ++++++++++++++++++++++++
|
||||
1 file changed, 24 insertions(+)
|
||||
|
||||
diff --git a/src/shared/user-record.c b/src/shared/user-record.c
|
||||
index 342a1c9c4f..bfea52427a 100644
|
||||
--- a/src/shared/user-record.c
|
||||
+++ b/src/shared/user-record.c
|
||||
@@ -1087,6 +1087,8 @@ int per_machine_id_match(sd_json_variant *ids, sd_json_dispatch_flags_t flags) {
|
||||
sd_id128_t mid;
|
||||
int r;
|
||||
|
||||
+ assert(ids);
|
||||
+
|
||||
r = sd_id128_get_machine(&mid);
|
||||
if (r < 0)
|
||||
return json_log(ids, flags, r, "Failed to acquire machine ID: %m");
|
||||
@@ -1135,6 +1137,8 @@ int per_machine_hostname_match(sd_json_variant *hns, sd_json_dispatch_flags_t fl
|
||||
_cleanup_free_ char *hn = NULL;
|
||||
int r;
|
||||
|
||||
+ assert(hns);
|
||||
+
|
||||
r = gethostname_strict(&hn);
|
||||
if (r == -ENXIO) {
|
||||
json_log(hns, flags, r, "No hostname set, not matching perMachine hostname record: %m");
|
||||
@@ -1182,6 +1186,15 @@ int per_machine_match(sd_json_variant *entry, sd_json_dispatch_flags_t flags) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+ m = sd_json_variant_by_key(entry, "matchNotMachineId");
|
||||
+ if (m) {
|
||||
+ r = per_machine_id_match(m, flags);
|
||||
+ if (r < 0)
|
||||
+ return r;
|
||||
+ if (r == 0)
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
m = sd_json_variant_by_key(entry, "matchHostname");
|
||||
if (m) {
|
||||
r = per_machine_hostname_match(m, flags);
|
||||
@@ -1191,6 +1204,15 @@ int per_machine_match(sd_json_variant *entry, sd_json_dispatch_flags_t flags) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+ m = sd_json_variant_by_key(entry, "matchNotHostname");
|
||||
+ if (m) {
|
||||
+ r = per_machine_hostname_match(m, flags);
|
||||
+ if (r < 0)
|
||||
+ return r;
|
||||
+ if (r == 0)
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1198,7 +1220,9 @@ static int dispatch_per_machine(const char *name, sd_json_variant *variant, sd_j
|
||||
|
||||
static const sd_json_dispatch_field per_machine_dispatch_table[] = {
|
||||
{ "matchMachineId", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
|
||||
+ { "matchNotMachineId", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
|
||||
{ "matchHostname", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
|
||||
+ { "matchNotHostname", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
|
||||
{ "blobDirectory", SD_JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, blob_directory), SD_JSON_STRICT },
|
||||
{ "blobManifest", SD_JSON_VARIANT_OBJECT, dispatch_blob_manifest, offsetof(UserRecord, blob_manifest), 0 },
|
||||
{ "iconName", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(UserRecord, icon_name), SD_JSON_STRICT },
|
||||
647
0603-homectl-add-interface-for-controlling-storage-for-ne.patch
Normal file
647
0603-homectl-add-interface-for-controlling-storage-for-ne.patch
Normal file
@ -0,0 +1,647 @@
|
||||
From 9df0f85a5a9a27a135f8caab544827932170c585 Mon Sep 17 00:00:00 2001
|
||||
From: Lennart Poettering <lennart@poettering.net>
|
||||
Date: Wed, 19 Feb 2025 21:56:54 +0100
|
||||
Subject: [PATCH] homectl: add interface for controlling storage for negative
|
||||
machine ID matches
|
||||
|
||||
(cherry picked from commit 0e1ede4b4b6d1ce6b5b6cda5f803e4f1b5aa4a03)
|
||||
|
||||
Related: RHEL-143034
|
||||
---
|
||||
docs/USER_RECORD.md | 17 +++-
|
||||
man/homectl.xml | 34 +++++++
|
||||
shell-completion/bash/homectl | 5 +-
|
||||
src/home/homectl.c | 181 +++++++++++++++++++++++-----------
|
||||
4 files changed, 172 insertions(+), 65 deletions(-)
|
||||
|
||||
diff --git a/docs/USER_RECORD.md b/docs/USER_RECORD.md
|
||||
index 350ca76649..90675dd532 100644
|
||||
--- a/docs/USER_RECORD.md
|
||||
+++ b/docs/USER_RECORD.md
|
||||
@@ -758,14 +758,23 @@ If any of the specified IDs match the system's local machine ID
|
||||
(As a special case, if only a single machine ID is listed this field may be a single
|
||||
string rather than an array of strings.)
|
||||
|
||||
+`matchNotMachineId` → Similar to `matchMachineId` but implements the inverse
|
||||
+match: this section only applies if the local machine ID does *not* match any
|
||||
+of the listed IDs.
|
||||
+
|
||||
`matchHostname` → An array of strings that are valid hostnames.
|
||||
If any of the specified hostnames match the system's local hostname, the fields in this object are honored.
|
||||
-If both `matchHostname` and `matchMachineId` are used within the same array entry, the object is honored when either match succeeds,
|
||||
-i.e. the two match types are combined in OR, not in AND.
|
||||
(As a special case, if only a single hostname is listed this field may be a single string rather than an array of strings.)
|
||||
|
||||
-These two are the only two fields specific to this section.
|
||||
-All other fields that may be used in this section are identical to the equally named ones in the
|
||||
+`matchNotHostname` → Similar to `matchHostname`, but implement the inverse
|
||||
+match, as above.
|
||||
+
|
||||
+If any of these four fields are used within the same array entry, the object is
|
||||
+honored when either match succeeds, i.e. the match types are combined in OR,
|
||||
+not in AND.
|
||||
+
|
||||
+These four are the only fields specific to this section. All other fields that
|
||||
+may be used in this section are identical to the equally named ones in the
|
||||
`regular` section (i.e. at the top-level object). Specifically, these are:
|
||||
|
||||
`blobDirectory`, `blobManifest`, `iconName`, `location`, `shell`, `umask`, `environment`, `timeZone`,
|
||||
diff --git a/man/homectl.xml b/man/homectl.xml
|
||||
index 282066c4fa..d08972c421 100644
|
||||
--- a/man/homectl.xml
|
||||
+++ b/man/homectl.xml
|
||||
@@ -179,6 +179,40 @@
|
||||
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
+ <varlistentry>
|
||||
+ <term><option>--match=</option></term>
|
||||
+ <term><option>-A</option></term>
|
||||
+ <term><option>-N</option></term>
|
||||
+ <term><option>-T</option></term>
|
||||
+
|
||||
+ <listitem><para>Takes one of <literal>this</literal>, <literal>other</literal>,
|
||||
+ <literal>any</literal> or <literal>auto</literal>. Some user record settings can be defined to match
|
||||
+ only specific machines, or all machines but one, or all machines. With this switch it is possibly to
|
||||
+ control to which machines to apply the settings appearing on the command line after it. If
|
||||
+ <literal>this</literal> is specified the setting will only apply to the local system (positive
|
||||
+ match), if <literal>other</literal> it will apply to all but the local system (negative match), if
|
||||
+ <literal>any</literal> it will apply to all systems (unless there's a matching positive or negative
|
||||
+ per-machine setting). If <literal>auto</literal> returns to the default logic: whether a setting
|
||||
+ applies by default to the local system or all systems depends on the option in question.</para>
|
||||
+
|
||||
+ <para>Note that only some user record settings can be conditioned like this. This option has no
|
||||
+ effect on the others and is ignored there. This option may appear multiple times in a single command
|
||||
+ line to apply settings conditioned by different matches to the same user record. See <ulink
|
||||
+ url="https://systemd.io/USER_RECORD">JSON User Records</ulink> for details on which settings may be
|
||||
+ used with such per-machine matching and which ones may not.</para>
|
||||
+
|
||||
+ <para><option>-A</option> is a shortcut for <option>--match=any</option>, <option>-T</option> is
|
||||
+ short for <option>--match=this</option> and <option>-N</option> is short for
|
||||
+ <option>--match=other</option>.</para>
|
||||
+
|
||||
+ <para>Here's an example call that sets the storage field to <literal>luks</literal> on the local
|
||||
+ system, but to <literal>cifs</literal> on all others:</para>
|
||||
+
|
||||
+ <programlisting># homectl update lennart -T --storage=luks -N --storage=cifs</programlisting>
|
||||
+
|
||||
+ <xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
+ </varlistentry>
|
||||
+
|
||||
<xi:include href="user-system-options.xml" xpointer="host" />
|
||||
<xi:include href="user-system-options.xml" xpointer="machine" />
|
||||
|
||||
diff --git a/shell-completion/bash/homectl b/shell-completion/bash/homectl
|
||||
index 5e2235bc3b..c8fbf8c553 100644
|
||||
--- a/shell-completion/bash/homectl
|
||||
+++ b/shell-completion/bash/homectl
|
||||
@@ -41,7 +41,7 @@ _homectl() {
|
||||
local -A OPTS=(
|
||||
[STANDALONE]='-h --help --version
|
||||
--no-pager --no-legend --no-ask-password
|
||||
- -j -E -P'
|
||||
+ -j -E -P -A -N -T'
|
||||
[ARG]=' -H --host
|
||||
-M --machine
|
||||
--identity
|
||||
@@ -112,7 +112,8 @@ _homectl() {
|
||||
--avatar
|
||||
--login-background
|
||||
--session-launcher
|
||||
- --session-type'
|
||||
+ --session-type
|
||||
+ --match'
|
||||
)
|
||||
|
||||
if __contains_word "$prev" ${OPTS[ARG]}; then
|
||||
diff --git a/src/home/homectl.c b/src/home/homectl.c
|
||||
index c11e6e0fdc..6d5aec1602 100644
|
||||
--- a/src/home/homectl.c
|
||||
+++ b/src/home/homectl.c
|
||||
@@ -68,6 +68,7 @@ static const char *arg_identity = NULL;
|
||||
static sd_json_variant *arg_identity_extra = NULL;
|
||||
static sd_json_variant *arg_identity_extra_privileged = NULL;
|
||||
static sd_json_variant *arg_identity_extra_this_machine = NULL;
|
||||
+static sd_json_variant *arg_identity_extra_other_machines = NULL;
|
||||
static sd_json_variant *arg_identity_extra_rlimits = NULL;
|
||||
static char **arg_identity_filter = NULL; /* this one is also applied to 'privileged' and 'thisMachine' subobjects */
|
||||
static char **arg_identity_filter_rlimits = NULL;
|
||||
@@ -99,6 +100,7 @@ static Hashmap *arg_blob_files = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_identity_extra, sd_json_variant_unrefp);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_this_machine, sd_json_variant_unrefp);
|
||||
+STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_other_machines, sd_json_variant_unrefp);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_privileged, sd_json_variant_unrefp);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_rlimits, sd_json_variant_unrefp);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_identity_filter, strv_freep);
|
||||
@@ -116,6 +118,7 @@ static bool identity_properties_specified(void) {
|
||||
!sd_json_variant_is_blank_object(arg_identity_extra) ||
|
||||
!sd_json_variant_is_blank_object(arg_identity_extra_privileged) ||
|
||||
!sd_json_variant_is_blank_object(arg_identity_extra_this_machine) ||
|
||||
+ !sd_json_variant_is_blank_object(arg_identity_extra_other_machines) ||
|
||||
!sd_json_variant_is_blank_object(arg_identity_extra_rlimits) ||
|
||||
!strv_isempty(arg_identity_filter) ||
|
||||
!strv_isempty(arg_identity_filter_rlimits) ||
|
||||
@@ -917,7 +920,7 @@ static int apply_identity_changes(sd_json_variant **_v) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to merge identities: %m");
|
||||
|
||||
- if (arg_identity_extra_this_machine || !strv_isempty(arg_identity_filter)) {
|
||||
+ if (arg_identity_extra_this_machine || arg_identity_extra_other_machines || !strv_isempty(arg_identity_filter)) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *per_machine = NULL, *mmid = NULL;
|
||||
sd_id128_t mid;
|
||||
|
||||
@@ -931,7 +934,7 @@ static int apply_identity_changes(sd_json_variant **_v) {
|
||||
|
||||
per_machine = sd_json_variant_ref(sd_json_variant_by_key(v, "perMachine"));
|
||||
if (per_machine) {
|
||||
- _cleanup_(sd_json_variant_unrefp) sd_json_variant *npm = NULL, *add = NULL;
|
||||
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *npm = NULL, *positive = NULL, *negative = NULL;
|
||||
_cleanup_free_ sd_json_variant **array = NULL;
|
||||
sd_json_variant *z;
|
||||
size_t i = 0;
|
||||
@@ -939,7 +942,7 @@ static int apply_identity_changes(sd_json_variant **_v) {
|
||||
if (!sd_json_variant_is_array(per_machine))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "perMachine field is not an array, refusing.");
|
||||
|
||||
- array = new(sd_json_variant*, sd_json_variant_elements(per_machine) + 1);
|
||||
+ array = new(sd_json_variant*, sd_json_variant_elements(per_machine) + 2);
|
||||
if (!array)
|
||||
return log_oom();
|
||||
|
||||
@@ -952,31 +955,41 @@ static int apply_identity_changes(sd_json_variant **_v) {
|
||||
array[i++] = z;
|
||||
|
||||
u = sd_json_variant_by_key(z, "matchMachineId");
|
||||
- if (!u)
|
||||
- continue;
|
||||
-
|
||||
- if (!sd_json_variant_equal(u, mmid))
|
||||
- continue;
|
||||
+ if (u && sd_json_variant_equal(u, mmid))
|
||||
+ r = sd_json_variant_merge_object(&positive, z);
|
||||
+ else {
|
||||
+ u = sd_json_variant_by_key(z, "matchNotMachineId");
|
||||
+ if (!u || !sd_json_variant_equal(u, mmid))
|
||||
+ continue;
|
||||
|
||||
- r = sd_json_variant_merge_object(&add, z);
|
||||
+ r = sd_json_variant_merge_object(&negative, z);
|
||||
+ }
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to merge perMachine entry: %m");
|
||||
|
||||
i--;
|
||||
}
|
||||
|
||||
- r = sd_json_variant_filter(&add, arg_identity_filter);
|
||||
+ r = sd_json_variant_filter(&positive, arg_identity_filter);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to filter perMachine: %m");
|
||||
|
||||
- r = sd_json_variant_merge_object(&add, arg_identity_extra_this_machine);
|
||||
+ r = sd_json_variant_filter(&negative, arg_identity_filter);
|
||||
+ if (r < 0)
|
||||
+ return log_error_errno(r, "Failed to filter perMachine: %m");
|
||||
+
|
||||
+ r = sd_json_variant_merge_object(&positive, arg_identity_extra_this_machine);
|
||||
+ if (r < 0)
|
||||
+ return log_error_errno(r, "Failed to merge in perMachine fields: %m");
|
||||
+
|
||||
+ r = sd_json_variant_merge_object(&negative, arg_identity_extra_other_machines);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to merge in perMachine fields: %m");
|
||||
|
||||
if (arg_identity_filter_rlimits || arg_identity_extra_rlimits) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *rlv = NULL;
|
||||
|
||||
- rlv = sd_json_variant_ref(sd_json_variant_by_key(add, "resourceLimits"));
|
||||
+ rlv = sd_json_variant_ref(sd_json_variant_by_key(positive, "resourceLimits"));
|
||||
|
||||
r = sd_json_variant_filter(&rlv, arg_identity_filter_rlimits);
|
||||
if (r < 0)
|
||||
@@ -987,22 +1000,30 @@ static int apply_identity_changes(sd_json_variant **_v) {
|
||||
return log_error_errno(r, "Failed to set resource limits: %m");
|
||||
|
||||
if (sd_json_variant_is_blank_object(rlv)) {
|
||||
- r = sd_json_variant_filter(&add, STRV_MAKE("resourceLimits"));
|
||||
+ r = sd_json_variant_filter(&positive, STRV_MAKE("resourceLimits"));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to drop resource limits field from identity: %m");
|
||||
} else {
|
||||
- r = sd_json_variant_set_field(&add, "resourceLimits", rlv);
|
||||
+ r = sd_json_variant_set_field(&positive, "resourceLimits", rlv);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to update resource limits of identity: %m");
|
||||
}
|
||||
}
|
||||
|
||||
- if (!sd_json_variant_is_blank_object(add)) {
|
||||
- r = sd_json_variant_set_field(&add, "matchMachineId", mmid);
|
||||
+ if (!sd_json_variant_is_blank_object(positive)) {
|
||||
+ r = sd_json_variant_set_field(&positive, "matchMachineId", mmid);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set matchMachineId field: %m");
|
||||
|
||||
- array[i++] = add;
|
||||
+ array[i++] = positive;
|
||||
+ }
|
||||
+
|
||||
+ if (!sd_json_variant_is_blank_object(negative)) {
|
||||
+ r = sd_json_variant_set_field(&negative, "matchNotMachineId", mmid);
|
||||
+ if (r < 0)
|
||||
+ return log_error_errno(r, "Failed to set matchNotMachineId field: %m");
|
||||
+
|
||||
+ array[i++] = negative;
|
||||
}
|
||||
|
||||
r = sd_json_variant_new_array(&npm, array, i);
|
||||
@@ -1012,21 +1033,34 @@ static int apply_identity_changes(sd_json_variant **_v) {
|
||||
sd_json_variant_unref(per_machine);
|
||||
per_machine = TAKE_PTR(npm);
|
||||
} else {
|
||||
- _cleanup_(sd_json_variant_unrefp) sd_json_variant *item = sd_json_variant_ref(arg_identity_extra_this_machine);
|
||||
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *positive = sd_json_variant_ref(arg_identity_extra_this_machine),
|
||||
+ *negative = sd_json_variant_ref(arg_identity_extra_other_machines);
|
||||
|
||||
if (arg_identity_extra_rlimits) {
|
||||
- r = sd_json_variant_set_field(&item, "resourceLimits", arg_identity_extra_rlimits);
|
||||
+ r = sd_json_variant_set_field(&positive, "resourceLimits", arg_identity_extra_rlimits);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to update resource limits of identity: %m");
|
||||
}
|
||||
|
||||
- r = sd_json_variant_set_field(&item, "matchMachineId", mmid);
|
||||
- if (r < 0)
|
||||
- return log_error_errno(r, "Failed to set matchMachineId field: %m");
|
||||
+ if (positive) {
|
||||
+ r = sd_json_variant_set_field(&positive, "matchMachineId", mmid);
|
||||
+ if (r < 0)
|
||||
+ return log_error_errno(r, "Failed to set matchMachineId field: %m");
|
||||
|
||||
- r = sd_json_variant_append_array(&per_machine, item);
|
||||
- if (r < 0)
|
||||
- return log_error_errno(r, "Failed to append to perMachine array: %m");
|
||||
+ r = sd_json_variant_append_array(&per_machine, positive);
|
||||
+ if (r < 0)
|
||||
+ return log_error_errno(r, "Failed to append to perMachine array: %m");
|
||||
+ }
|
||||
+
|
||||
+ if (negative) {
|
||||
+ r = sd_json_variant_set_field(&negative, "matchNotMachineId", mmid);
|
||||
+ if (r < 0)
|
||||
+ return log_error_errno(r, "Failed to set matchNotMachineId field: %m");
|
||||
+
|
||||
+ r = sd_json_variant_append_array(&per_machine, negative);
|
||||
+ if (r < 0)
|
||||
+ return log_error_errno(r, "Failed to append to perMachine array: %m");
|
||||
+ }
|
||||
}
|
||||
|
||||
r = sd_json_variant_set_field(&v, "perMachine", per_machine);
|
||||
@@ -2968,6 +3002,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_PROMPT_NEW_USER,
|
||||
ARG_AVATAR,
|
||||
ARG_LOGIN_BACKGROUND,
|
||||
+ ARG_MATCH,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -3068,18 +3103,24 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "blob", required_argument, NULL, 'b' },
|
||||
{ "avatar", required_argument, NULL, ARG_AVATAR },
|
||||
{ "login-background", required_argument, NULL, ARG_LOGIN_BACKGROUND },
|
||||
+ { "match", required_argument, NULL, ARG_MATCH },
|
||||
{}
|
||||
};
|
||||
|
||||
int r;
|
||||
|
||||
+ /* This points to one of arg_identity_extra, arg_identity_extra_this_machine,
|
||||
+ * arg_identity_extra_other_machines, in order to redirect changes on the next property being set to
|
||||
+ * this part of the identity, instead of the default. */
|
||||
+ sd_json_variant **match_identity = NULL;
|
||||
+
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
for (;;) {
|
||||
int c;
|
||||
|
||||
- c = getopt_long(argc, argv, "hH:M:I:c:d:u:G:k:s:e:b:jPE", options, NULL);
|
||||
+ c = getopt_long(argc, argv, "hH:M:I:c:d:u:G:k:s:e:b:jPENAT", options, NULL);
|
||||
if (c < 0)
|
||||
break;
|
||||
|
||||
@@ -3133,7 +3174,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (!valid_gecos(optarg))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Real name '%s' not a valid GECOS field.", optarg);
|
||||
|
||||
- r = sd_json_variant_set_field_string(&arg_identity_extra, "realName", optarg);
|
||||
+ r = sd_json_variant_set_field_string(match_identity ?: &arg_identity_extra, "realName", optarg);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set realName field: %m");
|
||||
|
||||
@@ -3263,7 +3304,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
- r = sd_json_variant_set_field_string(&arg_identity_extra, field, optarg);
|
||||
+ r = sd_json_variant_set_field_string(match_identity ?: &arg_identity_extra, field, optarg);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set %s field: %m", field);
|
||||
|
||||
@@ -3283,7 +3324,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to validate CIFS service name: %s", optarg);
|
||||
|
||||
- r = sd_json_variant_set_field_string(&arg_identity_extra, "cifsService", optarg);
|
||||
+ r = sd_json_variant_set_field_string(match_identity ?: &arg_identity_extra, "cifsService", optarg);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set cifsService field: %m");
|
||||
|
||||
@@ -3319,7 +3360,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse nice level: %s", optarg);
|
||||
|
||||
- r = sd_json_variant_set_field_integer(&arg_identity_extra, "niceLevel", nc);
|
||||
+ r = sd_json_variant_set_field_integer(match_identity ?: &arg_identity_extra, "niceLevel", nc);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set niceLevel field: %m");
|
||||
|
||||
@@ -3443,7 +3484,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
- r = sd_json_variant_set_field_string(&arg_identity_extra_this_machine, field, v);
|
||||
+ r = sd_json_variant_set_field_string(match_identity ?: &arg_identity_extra_this_machine, field, v);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set %s field: %m", v);
|
||||
|
||||
@@ -3462,7 +3503,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (!valid_shell(optarg))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Shell '%s' not valid.", optarg);
|
||||
|
||||
- r = sd_json_variant_set_field_string(&arg_identity_extra, "shell", optarg);
|
||||
+ r = sd_json_variant_set_field_string(match_identity ?: &arg_identity_extra, "shell", optarg);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set shell field: %m");
|
||||
|
||||
@@ -3481,7 +3522,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
- e = sd_json_variant_by_key(arg_identity_extra, "environment");
|
||||
+ e = sd_json_variant_by_key(match_identity ? *match_identity: arg_identity_extra, "environment");
|
||||
if (e) {
|
||||
r = sd_json_variant_strv(e, &l);
|
||||
if (r < 0)
|
||||
@@ -3498,7 +3539,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate environment list JSON: %m");
|
||||
|
||||
- r = sd_json_variant_set_field(&arg_identity_extra, "environment", ne);
|
||||
+ r = sd_json_variant_set_field(match_identity ?: &arg_identity_extra, "environment", ne);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set environment list: %m");
|
||||
|
||||
@@ -3506,7 +3547,6 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
case ARG_TIMEZONE:
|
||||
-
|
||||
if (isempty(optarg)) {
|
||||
r = drop_from_identity("timeZone");
|
||||
if (r < 0)
|
||||
@@ -3518,7 +3558,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (!timezone_is_valid(optarg, LOG_DEBUG))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Timezone '%s' is not valid.", optarg);
|
||||
|
||||
- r = sd_json_variant_set_field_string(&arg_identity_extra, "timeZone", optarg);
|
||||
+ r = sd_json_variant_set_field_string(match_identity ?: &arg_identity_extra, "timeZone", optarg);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set timezone field: %m");
|
||||
|
||||
@@ -3598,7 +3638,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s boolean: %m", field);
|
||||
|
||||
- r = sd_json_variant_set_field_boolean(&arg_identity_extra, field, r > 0);
|
||||
+ r = sd_json_variant_set_field_boolean(match_identity ?: &arg_identity_extra, field, r > 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set %s field: %m", field);
|
||||
|
||||
@@ -3634,7 +3674,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
- r = sd_json_variant_set_field_unsigned(&arg_identity_extra_this_machine, "diskSize", arg_disk_size);
|
||||
+ r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra_this_machine, "diskSize", arg_disk_size);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set diskSize field: %m");
|
||||
|
||||
@@ -3647,7 +3687,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
- r = sd_json_variant_set_field_unsigned(&arg_identity_extra_this_machine, "diskSizeRelative", arg_disk_size_relative);
|
||||
+ r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra_this_machine, "diskSizeRelative", arg_disk_size_relative);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set diskSizeRelative field: %m");
|
||||
|
||||
@@ -3655,7 +3695,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
/* Automatically turn off the rebalance logic if user configured a size explicitly */
|
||||
- r = sd_json_variant_set_field_unsigned(&arg_identity_extra_this_machine, "rebalanceWeight", REBALANCE_WEIGHT_OFF);
|
||||
+ r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra_this_machine, "rebalanceWeight", REBALANCE_WEIGHT_OFF);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set rebalanceWeight field: %m");
|
||||
|
||||
@@ -3696,7 +3736,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse --luks-discard= parameter: %s", optarg);
|
||||
|
||||
- r = sd_json_variant_set_field_boolean(&arg_identity_extra, "luksDiscard", r);
|
||||
+ r = sd_json_variant_set_field_boolean(match_identity ?: &arg_identity_extra, "luksDiscard", r);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set discard field: %m");
|
||||
|
||||
@@ -3715,7 +3755,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse --luks-offline-discard= parameter: %s", optarg);
|
||||
|
||||
- r = sd_json_variant_set_field_boolean(&arg_identity_extra, "luksOfflineDiscard", r);
|
||||
+ r = sd_json_variant_set_field_boolean(match_identity ?: &arg_identity_extra, "luksOfflineDiscard", r);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set offline discard field: %m");
|
||||
|
||||
@@ -3744,7 +3784,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s parameter: %s", field, optarg);
|
||||
|
||||
- r = sd_json_variant_set_field_unsigned(&arg_identity_extra, field, n);
|
||||
+ r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra, field, n);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set %s field: %m", field);
|
||||
|
||||
@@ -3766,7 +3806,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
- r = sd_json_variant_set_field_unsigned(&arg_identity_extra, "luksSectorSize", ss);
|
||||
+ r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra, "luksSectorSize", ss);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set sector size field: %m");
|
||||
|
||||
@@ -3788,7 +3828,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse umask: %m");
|
||||
|
||||
- r = sd_json_variant_set_field_integer(&arg_identity_extra, "umask", m);
|
||||
+ r = sd_json_variant_set_field_integer(match_identity ?: &arg_identity_extra, "umask", m);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set umask field: %m");
|
||||
|
||||
@@ -3900,7 +3940,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s parameter: %m", field);
|
||||
|
||||
- r = sd_json_variant_set_field_unsigned(&arg_identity_extra, field, n);
|
||||
+ r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra, field, n);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set %s field: %m", field);
|
||||
break;
|
||||
@@ -3933,7 +3973,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s parameter: %m", field);
|
||||
|
||||
- r = sd_json_variant_set_field_unsigned(&arg_identity_extra, field, n);
|
||||
+ r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra, field, n);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set %s field: %m", field);
|
||||
break;
|
||||
@@ -3968,9 +4008,9 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Parameter for %s field not valid: %s", field, optarg);
|
||||
|
||||
r = sd_json_variant_set_field_string(
|
||||
- IN_SET(c, ARG_STORAGE, ARG_FS_TYPE) ?
|
||||
- &arg_identity_extra_this_machine :
|
||||
- &arg_identity_extra, field, optarg);
|
||||
+ match_identity ?: (IN_SET(c, ARG_STORAGE, ARG_FS_TYPE) ?
|
||||
+ &arg_identity_extra_this_machine :
|
||||
+ &arg_identity_extra), field, optarg);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set %s field: %m", field);
|
||||
|
||||
@@ -4001,7 +4041,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s field: %s", field, optarg);
|
||||
|
||||
- r = sd_json_variant_set_field_unsigned(&arg_identity_extra, field, t);
|
||||
+ r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra, field, t);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set %s field: %m", field);
|
||||
|
||||
@@ -4050,7 +4090,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create group list JSON: %m");
|
||||
|
||||
- r = sd_json_variant_set_field(&arg_identity_extra, "memberOf", mo);
|
||||
+ r = sd_json_variant_set_field(match_identity ?: &arg_identity_extra, "memberOf", mo);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to update group list: %m");
|
||||
}
|
||||
@@ -4072,7 +4112,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse --tasks-max= parameter: %s", optarg);
|
||||
|
||||
- r = sd_json_variant_set_field_unsigned(&arg_identity_extra, "tasksMax", u);
|
||||
+ r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra, "tasksMax", u);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set tasksMax field: %m");
|
||||
|
||||
@@ -4102,7 +4142,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s parameter: %s", field, optarg);
|
||||
|
||||
- r = sd_json_variant_set_field_unsigned(&arg_identity_extra_this_machine, field, u);
|
||||
+ r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra_this_machine, field, u);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set %s field: %m", field);
|
||||
|
||||
@@ -4131,7 +4171,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (!CGROUP_WEIGHT_IS_OK(u))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Weight %" PRIu64 " is out of valid weight range.", u);
|
||||
|
||||
- r = sd_json_variant_set_field_unsigned(&arg_identity_extra, field, u);
|
||||
+ r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra, field, u);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set %s field: %m", field);
|
||||
|
||||
@@ -4262,7 +4302,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse --auto-resize-mode= argument: %s", optarg);
|
||||
|
||||
- r = sd_json_variant_set_field_string(&arg_identity_extra, "autoResizeMode", auto_resize_mode_to_string(r));
|
||||
+ r = sd_json_variant_set_field_string(match_identity ?: &arg_identity_extra, "autoResizeMode", auto_resize_mode_to_string(r));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set autoResizeMode field: %m");
|
||||
|
||||
@@ -4296,7 +4336,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return r;
|
||||
|
||||
/* Add to main identity */
|
||||
- r = sd_json_variant_set_field_unsigned(&arg_identity_extra, "rebalanceWeight", u);
|
||||
+ r = sd_json_variant_set_field_unsigned(match_identity ?: &arg_identity_extra, "rebalanceWeight", u);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set rebalanceWeight field: %m");
|
||||
|
||||
@@ -4363,7 +4403,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
- r = sd_json_variant_set_field_boolean(&arg_identity_extra, "dropCaches", r);
|
||||
+ r = sd_json_variant_set_field_boolean(match_identity ?: &arg_identity_extra, "dropCaches", r);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set drop caches field: %m");
|
||||
|
||||
@@ -4417,7 +4457,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (capability_set_to_strv(updated, &l) < 0)
|
||||
return log_oom();
|
||||
|
||||
- r = sd_json_variant_set_field_strv(&arg_identity_extra, field, l);
|
||||
+ r = sd_json_variant_set_field_strv(match_identity ?: &arg_identity_extra, field, l);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set %s field: %m", field);
|
||||
|
||||
@@ -4501,6 +4541,29 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
+ case ARG_MATCH:
|
||||
+ if (streq(optarg, "any"))
|
||||
+ match_identity = &arg_identity_extra;
|
||||
+ else if (streq(optarg, "this"))
|
||||
+ match_identity = &arg_identity_extra_this_machine;
|
||||
+ else if (streq(optarg, "other"))
|
||||
+ match_identity = &arg_identity_extra_other_machines;
|
||||
+ else if (streq(optarg, "auto"))
|
||||
+ match_identity = NULL;
|
||||
+ else
|
||||
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--machine= argument not understood. Refusing.");
|
||||
+ break;
|
||||
+
|
||||
+ case 'A':
|
||||
+ match_identity = &arg_identity_extra;
|
||||
+ break;
|
||||
+ case 'T':
|
||||
+ match_identity = &arg_identity_extra_this_machine;
|
||||
+ break;
|
||||
+ case 'N':
|
||||
+ match_identity = &arg_identity_extra_other_machines;
|
||||
+ break;
|
||||
+
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
From 119cca5054f04e1afefc15c626ed1b2353fdb080 Mon Sep 17 00:00:00 2001
|
||||
From: Lennart Poettering <lennart@poettering.net>
|
||||
Date: Mon, 19 May 2025 12:14:25 +0200
|
||||
Subject: [PATCH] logind: also save pidfdid as part of session state, even if
|
||||
we don't parse it
|
||||
|
||||
(cherry picked from commit f01d8658a3a57d05a5156aefd32d8137c3ee3996)
|
||||
|
||||
Related: RHEL-53112
|
||||
---
|
||||
src/login/logind-session.c | 6 +++++-
|
||||
1 file changed, 5 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
|
||||
index aa2420d5dd..b28dd716d7 100644
|
||||
--- a/src/login/logind-session.c
|
||||
+++ b/src/login/logind-session.c
|
||||
@@ -398,8 +398,12 @@ int session_save(Session *s) {
|
||||
if (!s->vtnr)
|
||||
fprintf(f, "POSITION=%u\n", s->position);
|
||||
|
||||
- if (pidref_is_set(&s->leader))
|
||||
+ if (pidref_is_set(&s->leader)) {
|
||||
fprintf(f, "LEADER="PID_FMT"\n", s->leader.pid);
|
||||
+ (void) pidref_acquire_pidfd_id(&s->leader);
|
||||
+ if (s->leader.fd_id != 0)
|
||||
+ fprintf(f, "LEADER_PIDFDID=%" PRIu64 "\n", s->leader.fd_id);
|
||||
+ }
|
||||
|
||||
if (audit_session_is_valid(s->audit_id))
|
||||
fprintf(f, "AUDIT=%"PRIu32"\n", s->audit_id);
|
||||
179
0605-logind-support-deserializing-session-leader-through-.patch
Normal file
179
0605-logind-support-deserializing-session-leader-through-.patch
Normal file
@ -0,0 +1,179 @@
|
||||
From a19c25b8b5edbfa9a47f6648862cdea6938ee455 Mon Sep 17 00:00:00 2001
|
||||
From: Mike Yuan <me@yhndnzj.com>
|
||||
Date: Fri, 24 Oct 2025 23:09:50 +0200
|
||||
Subject: [PATCH] logind: support deserializing session leader through pidfdid
|
||||
|
||||
People make weird assumptions around state preservation and
|
||||
expect logind to be stoppable. While this is realistically
|
||||
not OK we can probably improve things a little.
|
||||
|
||||
This complements f01d8658a3a57d05a5156aefd32d8137c3ee3996 and
|
||||
adds support for deserializing the LEADER_PIDFDID= field.
|
||||
We still prioritize pidfd if got one from fdstore (as with
|
||||
service_notify_message_parse_new_pid() in pid1), but otherwise
|
||||
this should make logind restart more robust when fdstore
|
||||
gets spuriously cleared.
|
||||
|
||||
Fixes #39437
|
||||
|
||||
(cherry picked from commit 45eea629e3b3a640bf6a5cd13f4c73c86b426b11)
|
||||
|
||||
Resolves: RHEL-53112
|
||||
---
|
||||
src/login/logind-session.c | 62 +++++++++++++++++++++++++++++++-------
|
||||
src/login/logind.c | 24 ++++++++++-----
|
||||
2 files changed, 68 insertions(+), 18 deletions(-)
|
||||
|
||||
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
|
||||
index b28dd716d7..a81841611c 100644
|
||||
--- a/src/login/logind-session.c
|
||||
+++ b/src/login/logind-session.c
|
||||
@@ -469,6 +469,46 @@ static int session_load_devices(Session *s, const char *devices) {
|
||||
return r;
|
||||
}
|
||||
|
||||
+static int session_load_leader(Session *s, uint64_t pidfdid) {
|
||||
+ _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
|
||||
+ int r;
|
||||
+
|
||||
+ assert(s);
|
||||
+ assert(pid_is_valid(s->deserialized_pid));
|
||||
+ assert(!pidref_is_set(&s->leader));
|
||||
+
|
||||
+ if (pidfdid == 0 && s->leader_fd_saved)
|
||||
+ /* We have no pidfd id for stable reference, but the pidfd has been submitted to fdstore.
|
||||
+ * manager_enumerate_fds() will dispatch the leader fd for us later. */
|
||||
+ return 0;
|
||||
+
|
||||
+ r = pidref_set_pid(&pidref, s->deserialized_pid);
|
||||
+ if (r < 0)
|
||||
+ return log_error_errno(r, "Failed to deserialize leader PID for session '%s': %m", s->id);
|
||||
+ if (pidref.fd < 0)
|
||||
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||||
+ "Failed to acquire pidfd for session leader '" PID_FMT "', refusing.",
|
||||
+ pidref.pid);
|
||||
+
|
||||
+ if (pidfdid > 0) {
|
||||
+ r = pidref_acquire_pidfd_id(&pidref);
|
||||
+ if (r < 0)
|
||||
+ return log_error_errno(r, "Failed to acquire pidfd id of deserialized leader '" PID_FMT "': %m",
|
||||
+ pidref.pid);
|
||||
+
|
||||
+ if (pidref.fd_id != pidfdid)
|
||||
+ return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
|
||||
+ "Deserialized pidfd id for process " PID_FMT " (%" PRIu64 ") doesn't match with the current one (%" PRIu64 "), refusing.",
|
||||
+ pidref.pid, pidfdid, pidref.fd_id);
|
||||
+ }
|
||||
+
|
||||
+ r = session_set_leader_consume(s, TAKE_PIDREF(pidref));
|
||||
+ if (r < 0)
|
||||
+ return log_error_errno(r, "Failed to set leader PID for session '%s': %m", s->id);
|
||||
+
|
||||
+ return 1;
|
||||
+}
|
||||
+
|
||||
int session_load(Session *s) {
|
||||
_cleanup_free_ char *remote = NULL,
|
||||
*seat = NULL,
|
||||
@@ -478,6 +518,7 @@ int session_load(Session *s) {
|
||||
*position = NULL,
|
||||
*leader_pid = NULL,
|
||||
*leader_fd_saved = NULL,
|
||||
+ *leader_pidfdid = NULL,
|
||||
*type = NULL,
|
||||
*original_type = NULL,
|
||||
*class = NULL,
|
||||
@@ -511,6 +552,7 @@ int session_load(Session *s) {
|
||||
"POSITION", &position,
|
||||
"LEADER", &leader_pid,
|
||||
"LEADER_FD_SAVED", &leader_fd_saved,
|
||||
+ "LEADER_PIDFDID", &leader_pidfdid,
|
||||
"TYPE", &type,
|
||||
"ORIGINAL_TYPE", &original_type,
|
||||
"CLASS", &class,
|
||||
@@ -662,8 +704,6 @@ int session_load(Session *s) {
|
||||
}
|
||||
|
||||
if (leader_pid) {
|
||||
- assert(!pidref_is_set(&s->leader));
|
||||
-
|
||||
r = parse_pid(leader_pid, &s->deserialized_pid);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse LEADER=%s: %m", leader_pid);
|
||||
@@ -673,19 +713,19 @@ int session_load(Session *s) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse LEADER_FD_SAVED=%s: %m", leader_fd_saved);
|
||||
s->leader_fd_saved = r > 0;
|
||||
-
|
||||
- if (s->leader_fd_saved)
|
||||
- /* The leader fd will be acquired from fdstore later */
|
||||
- return 0;
|
||||
}
|
||||
|
||||
- _cleanup_(pidref_done) PidRef p = PIDREF_NULL;
|
||||
+ uint64_t pidfdid;
|
||||
+ if (leader_pidfdid) {
|
||||
+ r = safe_atou64(leader_pidfdid, &pidfdid);
|
||||
+ if (r < 0)
|
||||
+ return log_error_errno(r, "Failed to parse LEADER_PIDFDID=%s: %m", leader_pidfdid);
|
||||
+ } else
|
||||
+ pidfdid = 0;
|
||||
|
||||
- r = pidref_set_pid(&p, s->deserialized_pid);
|
||||
- if (r >= 0)
|
||||
- r = session_set_leader_consume(s, TAKE_PIDREF(p));
|
||||
+ r = session_load_leader(s, pidfdid);
|
||||
if (r < 0)
|
||||
- log_warning_errno(r, "Failed to set leader PID for session '%s': %m", s->id);
|
||||
+ return r;
|
||||
}
|
||||
|
||||
return r;
|
||||
diff --git a/src/login/logind.c b/src/login/logind.c
|
||||
index 8dc1781edf..a7f75f0904 100644
|
||||
--- a/src/login/logind.c
|
||||
+++ b/src/login/logind.c
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "selinux-util.h"
|
||||
#include "service-util.h"
|
||||
#include "signal-util.h"
|
||||
+#include "stat-util.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
#include "udev-util.h"
|
||||
@@ -442,19 +443,28 @@ static int deliver_session_leader_fd_consume(Session *s, const char *fdname, int
|
||||
assert(fdname);
|
||||
assert(fd >= 0);
|
||||
|
||||
- if (!pid_is_valid(s->deserialized_pid)) {
|
||||
+ /* Already deserialized via pidfd id? */
|
||||
+ if (pidref_is_set(&s->leader)) {
|
||||
+ assert(s->leader.pid == s->deserialized_pid);
|
||||
+ assert(s->leader.fd >= 0);
|
||||
+
|
||||
+ r = fd_inode_same(fd, s->leader.fd);
|
||||
+ if (r < 0)
|
||||
+ return log_warning_errno(r, "Failed to compare pidfd with deserialized leader for session '%s': %m",
|
||||
+ s->id);
|
||||
+ if (r > 0)
|
||||
+ return 0;
|
||||
+
|
||||
+ log_warning("Got leader pidfd for session '%s' which mismatches with the deserialized process, resetting with pidfd.",
|
||||
+ s->id);
|
||||
+
|
||||
+ } else if (!pid_is_valid(s->deserialized_pid)) {
|
||||
r = log_warning_errno(SYNTHETIC_ERRNO(EOWNERDEAD),
|
||||
"Got leader pidfd for session '%s', but LEADER= is not set, refusing.",
|
||||
s->id);
|
||||
goto fail_close;
|
||||
}
|
||||
|
||||
- if (!s->leader_fd_saved)
|
||||
- log_warning("Got leader pidfd for session '%s', but not recorded in session state, proceeding anyway.",
|
||||
- s->id);
|
||||
- else
|
||||
- assert(!pidref_is_set(&s->leader));
|
||||
-
|
||||
r = pidref_set_pidfd_take(&leader_fdstore, fd);
|
||||
if (r < 0) {
|
||||
if (r == -ESRCH)
|
||||
@ -0,0 +1,36 @@
|
||||
From 969bae840bf3c9be3697ca00e3ece8ec32a80a31 Mon Sep 17 00:00:00 2001
|
||||
From: Mike Yuan <me@yhndnzj.com>
|
||||
Date: Fri, 24 Oct 2025 23:40:12 +0200
|
||||
Subject: [PATCH] TEST-35-LOGIN: test coldplug without fdstore on kernels with
|
||||
pidfd id
|
||||
|
||||
(cherry picked from commit ebb730b96ded7674994235340c0d4a39af174f52)
|
||||
|
||||
Related: RHEL-53112
|
||||
---
|
||||
test/units/TEST-35-LOGIN.sh | 8 +++++---
|
||||
1 file changed, 5 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/test/units/TEST-35-LOGIN.sh b/test/units/TEST-35-LOGIN.sh
|
||||
index 80320e32e1..755249140e 100755
|
||||
--- a/test/units/TEST-35-LOGIN.sh
|
||||
+++ b/test/units/TEST-35-LOGIN.sh
|
||||
@@ -32,13 +32,15 @@ Environment=SYSTEMD_LOG_LEVEL=debug
|
||||
EOF
|
||||
|
||||
# We test "coldplug" (completely stop and start logind) here. So we need to preserve
|
||||
- # the fdstore, which might contain session leader pidfds. This is extremely rare use case
|
||||
- # and shall not be considered fully supported.
|
||||
+ # the fdstore, which might contain session leader pidfds, but only if pidfd id isn't
|
||||
+ # a thing. This is extremely rare use case and shall not be considered fully supported.
|
||||
# See also: https://github.com/systemd/systemd/pull/30610#discussion_r1440507850
|
||||
- systemctl edit --runtime --stdin systemd-logind.service --drop-in=fdstore-preserve.conf <<EOF
|
||||
+ if systemd-analyze compare-versions "$(uname -r)" lt 6.9; then
|
||||
+ systemctl edit --runtime --stdin systemd-logind.service --drop-in=fdstore-preserve.conf <<EOF
|
||||
[Service]
|
||||
FileDescriptorStorePreserve=yes
|
||||
EOF
|
||||
+ fi
|
||||
|
||||
systemctl restart systemd-logind.service
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
From aa181fefdd039d74a20ccce431f12f6b5fa9b637 Mon Sep 17 00:00:00 2001
|
||||
From: Mike Yuan <me@yhndnzj.com>
|
||||
Date: Thu, 6 Nov 2025 19:31:18 +0100
|
||||
Subject: [PATCH] logind: fix potential fd leak in
|
||||
deliver_session_leader_fd_consume()
|
||||
|
||||
Follow-up for 45eea629e3b3a640bf6a5cd13f4c73c86b426b11
|
||||
|
||||
(cherry picked from commit c54112bdee1d32934e688961ca53e81ffff0c99a)
|
||||
|
||||
Related: RHEL-53112
|
||||
---
|
||||
src/login/logind.c | 7 ++++---
|
||||
1 file changed, 4 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/src/login/logind.c b/src/login/logind.c
|
||||
index a7f75f0904..afa9b1d42c 100644
|
||||
--- a/src/login/logind.c
|
||||
+++ b/src/login/logind.c
|
||||
@@ -435,13 +435,13 @@ static int deliver_session_device_fd(Session *s, const char *fdname, int fd, dev
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int deliver_session_leader_fd_consume(Session *s, const char *fdname, int fd) {
|
||||
+static int deliver_session_leader_fd_consume(Session *s, const char *fdname, int fd_consume) {
|
||||
+ _cleanup_close_ int fd = ASSERT_FD(fd_consume);
|
||||
_cleanup_(pidref_done) PidRef leader_fdstore = PIDREF_NULL;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(fdname);
|
||||
- assert(fd >= 0);
|
||||
|
||||
/* Already deserialized via pidfd id? */
|
||||
if (pidref_is_set(&s->leader)) {
|
||||
@@ -473,6 +473,7 @@ static int deliver_session_leader_fd_consume(Session *s, const char *fdname, int
|
||||
log_warning_errno(r, "Failed to create reference to leader of session '%s': %m", s->id);
|
||||
goto fail_close;
|
||||
}
|
||||
+ TAKE_FD(fd);
|
||||
|
||||
if (leader_fdstore.pid != s->deserialized_pid)
|
||||
log_warning("Leader from pidfd (" PID_FMT ") doesn't match with LEADER=" PID_FMT " for session '%s', proceeding anyway.",
|
||||
@@ -485,7 +486,7 @@ static int deliver_session_leader_fd_consume(Session *s, const char *fdname, int
|
||||
return 0;
|
||||
|
||||
fail_close:
|
||||
- close_and_notify_warn(fd, fdname);
|
||||
+ close_and_notify_warn(TAKE_FD(fd), fdname);
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
From b19c1dbe3962ba716555284136a23fb0b8da9e3a Mon Sep 17 00:00:00 2001
|
||||
From: Michal Sekletar <msekleta@redhat.com>
|
||||
Date: Fri, 14 Mar 2025 09:16:17 +0100
|
||||
Subject: [PATCH] Revert "coredump: lock down EnterNamespace= mount even more"
|
||||
|
||||
This reverts commit 4c9c8b8d09eff18df71ba4aa910df4201f9890a0.
|
||||
|
||||
Reverted change broke EnterNamespace= completely. For example, libdw
|
||||
tries to access libc in /lib64 which points to usr/lib64 and that fails.
|
||||
Similarly for binaries, we need to be able to resolve /bin to usr/bin
|
||||
and /sbin to usr/sbin at the very least.
|
||||
|
||||
(cherry picked from commit 8f8148cb08bf9f2c0e1f7fe6a5e6eb383115957b)
|
||||
|
||||
Related: RHEL-95219
|
||||
---
|
||||
src/coredump/coredump.c | 11 +++++++----
|
||||
1 file changed, 7 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
|
||||
index ea4f8805f4..9665b3fe3e 100644
|
||||
--- a/src/coredump/coredump.c
|
||||
+++ b/src/coredump/coredump.c
|
||||
@@ -832,10 +832,13 @@ static int attach_mount_tree(int mount_tree_fd) {
|
||||
return log_warning_errno(r, "Failed to create directory: %m");
|
||||
|
||||
r = mount_setattr(mount_tree_fd, "", AT_EMPTY_PATH,
|
||||
- &(struct mount_attr) {
|
||||
- .attr_set = MOUNT_ATTR_RDONLY|MOUNT_ATTR_NOSUID|MOUNT_ATTR_NODEV|MOUNT_ATTR_NOEXEC|MOUNT_ATTR_NOSYMFOLLOW,
|
||||
- .propagation = MS_SLAVE,
|
||||
- }, sizeof(struct mount_attr));
|
||||
+ &(struct mount_attr) {
|
||||
+ /* MOUNT_ATTR_NOSYMFOLLOW is left out on purpose to allow libdwfl to resolve symlinks.
|
||||
+ * libdwfl will use openat2() with RESOLVE_IN_ROOT so there is no risk of symlink escape.
|
||||
+ * https://sourceware.org/git/?p=elfutils.git;a=patch;h=06f0520f9a78b07c11c343181d552791dd630346 */
|
||||
+ .attr_set = MOUNT_ATTR_RDONLY|MOUNT_ATTR_NOSUID|MOUNT_ATTR_NODEV|MOUNT_ATTR_NOEXEC,
|
||||
+ .propagation = MS_SLAVE,
|
||||
+ }, sizeof(struct mount_attr));
|
||||
if (r < 0)
|
||||
return log_warning_errno(errno, "Failed to change properties of mount tree: %m");
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
From 0f03838ff135c027c249e8212f51957a4109cfc0 Mon Sep 17 00:00:00 2001
|
||||
From: Michal Sekletar <msekleta@redhat.com>
|
||||
Date: Mon, 9 Jun 2025 15:22:33 +0200
|
||||
Subject: [PATCH] coredump: add compat support for
|
||||
SYSTEMD_COREDUMP_ALLOW_NAMESPACE_CHANGE
|
||||
|
||||
Set SYSTEMD_COREDUMP_ALLOW_NAMESPACE_CHANGE is an equivalent to
|
||||
EnterNamespace=yes in the configuration file.
|
||||
|
||||
This compat support will be removed in RHEL-11.
|
||||
|
||||
rhel-only: policy
|
||||
|
||||
Related: RHEL-95219
|
||||
---
|
||||
src/coredump/coredump.c | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
|
||||
index 9665b3fe3e..0c51f62728 100644
|
||||
--- a/src/coredump/coredump.c
|
||||
+++ b/src/coredump/coredump.c
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "coredump-vacuum.h"
|
||||
#include "dirent-util.h"
|
||||
#include "elf-util.h"
|
||||
+#include "env-util.h"
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
@@ -1812,6 +1813,9 @@ static int acquire_pid_mount_tree_fd(const Context *context, int *ret_fd) {
|
||||
assert(context);
|
||||
assert(ret_fd);
|
||||
|
||||
+ if (getenv_bool("SYSTEMD_COREDUMP_ALLOW_NAMESPACE_CHANGE") > 0)
|
||||
+ arg_enter_namespace = true;
|
||||
+
|
||||
if (!arg_enter_namespace) {
|
||||
*ret_fd = -EHOSTDOWN;
|
||||
log_debug("EnterNamespace=no so we won't use mount tree of the crashed process for generating backtrace.");
|
||||
74
systemd.spec
74
systemd.spec
@ -48,7 +48,7 @@ Url: https://systemd.io
|
||||
# Allow users to specify the version and release when building the rpm by
|
||||
# setting the %%version_override and %%release_override macros.
|
||||
Version: %{?version_override}%{!?version_override:257}
|
||||
Release: 22%{?dist}
|
||||
Release: 23%{?dist}
|
||||
|
||||
%global stable %(c="%version"; [ "$c" = "${c#*.*}" ]; echo $?)
|
||||
|
||||
@ -684,6 +684,41 @@ Patch0571: 0571-userdbctl-optionally-show-user-group-data-from-JSON-.patch
|
||||
Patch0572: 0572-man-fix-typo.patch
|
||||
Patch0573: 0573-test-fix-test-with-Dnetworkd-false.patch
|
||||
Patch0574: 0574-ci-run-apt-get-update-before-running-mkosi.patch
|
||||
Patch0575: 0575-test-journal-dump-dump-the-headers-of-journal-files.patch
|
||||
Patch0576: 0576-journal-store-counts-not-byte-sizes-in-table-size-co.patch
|
||||
Patch0577: 0577-journal-treble-field-hash-table-size.patch
|
||||
Patch0578: 0578-nspawn-move-uid-shift-chown-code-into-shared.patch
|
||||
Patch0579: 0579-user-classification-add-new-foreign-UID-range.patch
|
||||
Patch0580: 0580-userdb-synthesize-stub-user-records-for-the-foreign-.patch
|
||||
Patch0581: 0581-dissect-add-new-shift-command.patch
|
||||
Patch0582: 0582-userdb-optionally-parse-numeric-UIDs-GIDs-where-a-us.patch
|
||||
Patch0583: 0583-userdbd-separate-parameter-structure-of-GetMembershi.patch
|
||||
Patch0584: 0584-userdb-move-setting-of-service-varlink-parameter-int.patch
|
||||
Patch0585: 0585-user-record-make-a-NULL-UserDBMatch-be-equivalent-to.patch
|
||||
Patch0586: 0586-sd-varlink-add-sd_varlink_get_description-call.patch
|
||||
Patch0587: 0587-user-record-add-helper-for-dispatching-a-disposition.patch
|
||||
Patch0588: 0588-user-record-rename-USER_DISPOSITION_MASK_MAX-USER_DI.patch
|
||||
Patch0589: 0589-user-record-add-some-helpers-for-working-with-UserDB.patch
|
||||
Patch0590: 0590-varlink-add-new-calls-for-server-side-user-record-fi.patch
|
||||
Patch0591: 0591-userdb-move-UserDBMatch-handling-from-userdbctl-into.patch
|
||||
Patch0592: 0592-userdbd-implement-server-side-filtering-in-the-Multi.patch
|
||||
Patch0593: 0593-homectl-port-has_regular_user-acquire_group_list-to-.patch
|
||||
Patch0594: 0594-update-TODO.patch
|
||||
Patch0595: 0595-JSON-User-Group-records-Add-properties-for-UUIDs.patch
|
||||
Patch0596: 0596-userdb-add-support-for-printing-the-UUID-from-user-a.patch
|
||||
Patch0597: 0597-userdb-add-support-for-looking-up-users-or-groups-by.patch
|
||||
Patch0598: 0598-userdbctl-add-uuid-filtering-option.patch
|
||||
Patch0599: 0599-userdbctl-add-missing-uuid-to-help-text.patch
|
||||
Patch0600: 0600-userdb-fix-typo.patch
|
||||
Patch0601: 0601-man-userdbctl-fixup-version-info.patch
|
||||
Patch0602: 0602-user-record-add-a-concept-of-inverting-per-host-matc.patch
|
||||
Patch0603: 0603-homectl-add-interface-for-controlling-storage-for-ne.patch
|
||||
Patch0604: 0604-logind-also-save-pidfdid-as-part-of-session-state-ev.patch
|
||||
Patch0605: 0605-logind-support-deserializing-session-leader-through-.patch
|
||||
Patch0606: 0606-TEST-35-LOGIN-test-coldplug-without-fdstore-on-kerne.patch
|
||||
Patch0607: 0607-logind-fix-potential-fd-leak-in-deliver_session_lead.patch
|
||||
Patch0608: 0608-Revert-coredump-lock-down-EnterNamespace-mount-even-.patch
|
||||
Patch0609: 0609-coredump-add-compat-support-for-SYSTEMD_COREDUMP_ALL.patch
|
||||
|
||||
# Downstream-only patches (9000–9999)
|
||||
%endif
|
||||
@ -1635,6 +1670,43 @@ rm -f .file-list-*
|
||||
rm -f %{name}.lang
|
||||
|
||||
%changelog
|
||||
* Tue Feb 17 2026 systemd maintenance team <systemd-maint@redhat.com> - 257-23
|
||||
- test-journal-dump: dump the headers of journal files (RHEL-106795)
|
||||
- journal: store counts, not byte sizes, in table size constants (RHEL-106795)
|
||||
- journal: treble field hash table size (RHEL-106795)
|
||||
- nspawn: move uid shift/chown() code into shared/ (RHEL-143036)
|
||||
- user-classification: add new "foreign" UID range (RHEL-143036)
|
||||
- userdb: synthesize stub user records for the foreign UID (RHEL-143036)
|
||||
- dissect: add new --shift command (RHEL-143036)
|
||||
- userdb: optionally parse numeric UIDs/GIDs where a username is expected (RHEL-143036)
|
||||
- userdbd: separate parameter structure of GetMemberships() varlink call from the GetUserRecord() one (RHEL-143036)
|
||||
- userdb: move setting of 'service' varlink parameter into userdb_connect() (RHEL-143036)
|
||||
- user-record: make a NULL UserDBMatch be equivalent to no filtering (RHEL-143036)
|
||||
- sd-varlink: add sd_varlink_get_description() call (RHEL-143036)
|
||||
- user-record: add helper for dispatching a disposition mask (RHEL-143036)
|
||||
- user-record: rename USER_DISPOSITION_MASK_MAX → USER_DISPOSITION_MASK_ALL (RHEL-143036)
|
||||
- user-record: add some helpers for working with UserDBMatch (RHEL-143036)
|
||||
- varlink: add new calls for server-side user record filtering to varlink IDL + to spec (RHEL-143036)
|
||||
- userdb: move UserDBMatch handling from userdbctl into generic userdb code to allow it to be done server side (RHEL-143036)
|
||||
- userdbd: implement server side filtering in the Multiplexer API (RHEL-143036)
|
||||
- homectl: port has_regular_user() + acquire_group_list() to use server-side filtering (RHEL-143036)
|
||||
- update TODO (RHEL-143036)
|
||||
- JSON User/Group records: Add properties for UUIDs (RHEL-143036)
|
||||
- userdb: add support for printing the UUID from user and group records (RHEL-143036)
|
||||
- userdb: add support for looking up users or groups by uuid. (RHEL-143036)
|
||||
- userdbctl: add --uuid filtering option (RHEL-143036)
|
||||
- userdbctl: add missing --uuid= to --help text (RHEL-143036)
|
||||
- userdb: fix typo (RHEL-143036)
|
||||
- man/userdbctl: fixup version info (RHEL-143036)
|
||||
- user-record: add a concept of inverting per-host matching sections in user record (RHEL-143034)
|
||||
- homectl: add interface for controlling storage for negative machine ID matches (RHEL-143034)
|
||||
- logind: also save pidfdid as part of session state, even if we don't parse it (RHEL-53112)
|
||||
- logind: support deserializing session leader through pidfdid (RHEL-53112)
|
||||
- TEST-35-LOGIN: test coldplug without fdstore on kernels with pidfd id (RHEL-53112)
|
||||
- logind: fix potential fd leak in deliver_session_leader_fd_consume() (RHEL-53112)
|
||||
- Revert "coredump: lock down EnterNamespace= mount even more" (RHEL-95219)
|
||||
- coredump: add compat support for SYSTEMD_COREDUMP_ALLOW_NAMESPACE_CHANGE (RHEL-95219)
|
||||
|
||||
* Fri Feb 06 2026 systemd maintenance team <systemd-maint@redhat.com> - 257-22
|
||||
- fstab-generator: fix options in systemd.mount-extra= arg (RHEL-125822)
|
||||
- hwdb: Add Accelerometer mount matrix for Irbis TW43 (RHEL-72702)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user