mirror of
https://github.com/systemd/systemd
synced 2026-03-14 17:14:49 +01:00
Compare commits
No commits in common. "ebf940e1e9837170dddc80c0ae9edb6da3b5f457" and "37ef2fc9f7709c710692081e7e3be94d7b16d9fa" have entirely different histories.
ebf940e1e9
...
37ef2fc9f7
9
TODO
9
TODO
@ -36,15 +36,12 @@ Features:
|
|||||||
* journald: support RFC3164 fully for the incoming syslog transport, see
|
* journald: support RFC3164 fully for the incoming syslog transport, see
|
||||||
https://github.com/systemd/systemd/issues/19251#issuecomment-816601955
|
https://github.com/systemd/systemd/issues/19251#issuecomment-816601955
|
||||||
|
|
||||||
|
* nspawn: support uid mapping bind mounts, as defined available in kernel 5.12,
|
||||||
|
for all our disk image needs
|
||||||
|
|
||||||
* homed: if kernel 5.12 uid mapping mounts exist, use that instead of recursive
|
* homed: if kernel 5.12 uid mapping mounts exist, use that instead of recursive
|
||||||
chowns.
|
chowns.
|
||||||
|
|
||||||
* DynamicUser= + StateDirectory= → use uid mapping mounts, too, in order to
|
|
||||||
make dirs appear under right UID.
|
|
||||||
|
|
||||||
* nspawn: make --bind= work sanely with --private-users when uid mapping mounts
|
|
||||||
are used.
|
|
||||||
|
|
||||||
* cryptsetup: tweak tpm2-device=auto logic, abort quickly if firmware tells us
|
* cryptsetup: tweak tpm2-device=auto logic, abort quickly if firmware tells us
|
||||||
there isn't any TPM2 device anyway. that way, we'll wait for the TPM2 device
|
there isn't any TPM2 device anyway. that way, we'll wait for the TPM2 device
|
||||||
to show up only if registered in LUKS header + the firmware suggests there is
|
to show up only if registered in LUKS header + the firmware suggests there is
|
||||||
|
|||||||
@ -753,10 +753,6 @@ sensor:modalias:acpi:KIOX020A*:dmi:*:svnTECLAST:pnF6Pro:*
|
|||||||
sensor:modalias:acpi:KIOX000A*:dmi:*:svnTECLAST:pnTbooK11:*
|
sensor:modalias:acpi:KIOX000A*:dmi:*:svnTECLAST:pnTbooK11:*
|
||||||
ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
|
ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
|
||||||
|
|
||||||
# Teclast X4 2-in-1 (G4M6)
|
|
||||||
sensor:modalias:acpi:KIOX000A*:dmi:*:svnTECLAST:pnX4:*
|
|
||||||
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
|
|
||||||
|
|
||||||
# Teclast X80 Plus (H5C5)
|
# Teclast X80 Plus (H5C5)
|
||||||
sensor:modalias:acpi:KIOX000A*:dmi:*:svnTECLAST:pnDefaultstring:*
|
sensor:modalias:acpi:KIOX000A*:dmi:*:svnTECLAST:pnDefaultstring:*
|
||||||
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
|
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
|
||||||
|
|||||||
@ -696,41 +696,32 @@
|
|||||||
number of host UIDs/GIDs to assign to the container. If the second parameter is omitted, 65536 UIDs/GIDs are
|
number of host UIDs/GIDs to assign to the container. If the second parameter is omitted, 65536 UIDs/GIDs are
|
||||||
assigned.</para></listitem>
|
assigned.</para></listitem>
|
||||||
|
|
||||||
<listitem><para>If the parameter is <literal>yes</literal>, user namespacing is turned on. The
|
<listitem><para>If the parameter is omitted, or true, user namespacing is turned on. The UID/GID range to
|
||||||
UID/GID range to use is determined automatically from the file ownership of the root directory of
|
use is determined automatically from the file ownership of the root directory of the container's directory
|
||||||
the container's directory tree. To use this option, make sure to prepare the directory tree in
|
tree. To use this option, make sure to prepare the directory tree in advance, and ensure that all files and
|
||||||
advance, and ensure that all files and directories in it are owned by UIDs/GIDs in the range you'd
|
directories in it are owned by UIDs/GIDs in the range you'd like to use. Also, make sure that used file ACLs
|
||||||
like to use. Also, make sure that used file ACLs exclusively reference UIDs/GIDs in the appropriate
|
exclusively reference UIDs/GIDs in the appropriate range. If this mode is used the number of UIDs/GIDs
|
||||||
range. In this mode, the number of UIDs/GIDs assigned to the container is 65536, and the owner
|
assigned to the container for use is 65536, and the UID/GID of the root directory must be a multiple of
|
||||||
UID/GID of the root directory must be a multiple of 65536.</para></listitem>
|
65536.</para></listitem>
|
||||||
|
|
||||||
<listitem><para>If the parameter is <literal>no</literal>, user namespacing is turned off. This is
|
<listitem><para>If the parameter is false, user namespacing is turned off. This is the default.</para>
|
||||||
the default.</para>
|
|
||||||
</listitem>
|
</listitem>
|
||||||
|
|
||||||
<listitem><para>If the parameter is <literal>identity</literal>, user namespacing is employed with
|
<listitem><para>The special value <literal>pick</literal> turns on user namespacing. In this case the UID/GID
|
||||||
an identity mapping for the first 65536 UIDs/GIDs. This is mostly equivalent to
|
range is automatically chosen. As first step, the file owner of the root directory of the container's
|
||||||
<option>--private-users=0:65536</option>. While it does not provide UID/GID isolation, since all
|
directory tree is read, and it is checked that it is currently not used by the system otherwise (in
|
||||||
host and container UIDs/GIDs are chosen identically it does provide process capability isolation,
|
particular, that no other container is using it). If this check is successful, the UID/GID range determined
|
||||||
and hence is often a good choice if proper user namespacing with distinct UID maps is not
|
this way is used, similar to the behavior if "yes" is specified. If the check is not successful (and thus
|
||||||
appropriate.</para></listitem>
|
the UID/GID range indicated in the root directory's file owner is already used elsewhere) a new – currently
|
||||||
|
unused – UID/GID range of 65536 UIDs/GIDs is randomly chosen between the host UID/GIDs of 524288 and
|
||||||
<listitem><para>The special value <literal>pick</literal> turns on user namespacing. In this case
|
1878982656, always starting at a multiple of 65536, and, if possible, consistently hashed from the machine
|
||||||
the UID/GID range is automatically chosen. As first step, the file owner UID/GID of the root
|
name. This setting implies
|
||||||
directory of the container's directory tree is read, and it is checked that no other container is
|
<option>--private-users-chown</option> (see below), which has the effect that the files and directories in
|
||||||
currently using it. If this check is successful, the UID/GID range determined this way is used,
|
the container's directory tree will be owned by the appropriate users of the range picked. Using this option
|
||||||
similar to the behavior if <literal>yes</literal> is specified. If the check is not successful (and
|
makes user namespace behavior fully automatic. Note that the first invocation of a previously unused
|
||||||
thus the UID/GID range indicated in the root directory's file owner is already used elsewhere) a
|
container image might result in picking a new UID/GID range for it, and thus in the (possibly expensive) file
|
||||||
new – currently unused – UID/GID range of 65536 UIDs/GIDs is randomly chosen between the host
|
ownership adjustment operation. However, subsequent invocations of the container will be cheap (unless of
|
||||||
UID/GIDs of 524288 and 1878982656, always starting at a multiple of 65536, and, if possible,
|
course the picked UID/GID range is assigned to a different use by then).</para></listitem>
|
||||||
consistently hashed from the machine name. This setting implies
|
|
||||||
<option>--private-users-ownership=auto</option> (see below), which possibly has the effect that the
|
|
||||||
files and directories in the container's directory tree will be owned by the appropriate users of
|
|
||||||
the range picked. Using this option makes user namespace behavior fully automatic. Note that the
|
|
||||||
first invocation of a previously unused container image might result in picking a new UID/GID range
|
|
||||||
for it, and thus in the (possibly expensive) file ownership adjustment operation. However,
|
|
||||||
subsequent invocations of the container will be cheap (unless of course the picked UID/GID range is
|
|
||||||
assigned to a different use by then).</para></listitem>
|
|
||||||
</orderedlist>
|
</orderedlist>
|
||||||
|
|
||||||
<para>It is recommended to assign at least 65536 UIDs/GIDs to each container, so that the usable UID/GID range in the
|
<para>It is recommended to assign at least 65536 UIDs/GIDs to each container, so that the usable UID/GID range in the
|
||||||
@ -756,44 +747,31 @@
|
|||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><option>--private-users-ownership=</option></term>
|
<term><option>--private-users-chown</option></term>
|
||||||
|
|
||||||
<listitem><para>Controls how to adjust the container image's UIDs and GIDs to match the UID/GID range
|
<listitem><para>If specified, all files and directories in the container's directory tree will be
|
||||||
chosen with <option>--private-users=</option>, see above. Takes one of <literal>off</literal> (to
|
adjusted so that they are owned by the appropriate UIDs/GIDs selected for the container (see above).
|
||||||
leave the image as is), <literal>chown</literal> (to recursively <function>chown()</function> the
|
This operation is potentially expensive, as it involves iterating through the full directory tree of
|
||||||
container's directory tree as needed), <literal>map</literal> (in order to use transparent ID mapping
|
the container. Besides actual file ownership, file ACLs are adjusted as well.</para>
|
||||||
mounts) or <literal>auto</literal> for automatically using <literal>map</literal> where available and
|
|
||||||
<literal>chown</literal> where not.</para>
|
|
||||||
|
|
||||||
<para>If <literal>chown</literal> is selected, all files and directories in the container's directory
|
<para>This option is implied if <option>--private-users=pick</option> is used. This option has no effect if
|
||||||
tree will be adjusted so that they are owned by the appropriate UIDs/GIDs selected for the container
|
user namespacing is not used.</para></listitem>
|
||||||
(see above). This operation is potentially expensive, as it involves iterating through the full
|
|
||||||
directory tree of the container. Besides actual file ownership, file ACLs are adjusted as
|
|
||||||
well.</para>
|
|
||||||
|
|
||||||
<para>Typically <literal>map</literal> is the best choice, since it transparently maps UIDs/GIDs in
|
|
||||||
memory as needed without modifying the image, and without requiring an expensive recursive adjustment
|
|
||||||
operation. However, it is not available for all file systems, currently.</para>
|
|
||||||
|
|
||||||
<para>The <option>--private-users-ownership=auto</option> option is implied if
|
|
||||||
<option>--private-users=pick</option> is used. This option has no effect if user namespacing is not
|
|
||||||
used.</para></listitem>
|
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><option>-U</option></term>
|
<term><option>-U</option></term>
|
||||||
|
|
||||||
<listitem><para>If the kernel supports the user namespaces feature, equivalent to
|
<listitem><para>If the kernel supports the user namespaces feature, equivalent to
|
||||||
<option>--private-users=pick --private-users-ownership=auto</option>, otherwise equivalent to
|
<option>--private-users=pick --private-users-chown</option>, otherwise equivalent to
|
||||||
<option>--private-users=no</option>.</para>
|
<option>--private-users=no</option>.</para>
|
||||||
|
|
||||||
<para>Note that <option>-U</option> is the default if the
|
<para>Note that <option>-U</option> is the default if the
|
||||||
<filename>systemd-nspawn@.service</filename> template unit file is used.</para>
|
<filename>systemd-nspawn@.service</filename> template unit file is used.</para>
|
||||||
|
|
||||||
<para>Note: it is possible to undo the effect of <option>--private-users-ownership=chown</option> (or
|
<para>Note: it is possible to undo the effect of <option>--private-users-chown</option> (or
|
||||||
<option>-U</option>) on the file system by redoing the operation with the first UID of 0:</para>
|
<option>-U</option>) on the file system by redoing the operation with the first UID of 0:</para>
|
||||||
|
|
||||||
<programlisting>systemd-nspawn … --private-users=0 --private-users-ownership=chown</programlisting>
|
<programlisting>systemd-nspawn … --private-users=0 --private-users-chown</programlisting>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
|||||||
@ -452,12 +452,12 @@
|
|||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><varname>PrivateUsersOwnership=</varname></term>
|
<term><varname>PrivateUsersChown=</varname></term>
|
||||||
|
|
||||||
<listitem><para>Configures whether the ownership of the files and directories in the container tree
|
<listitem><para>Configures whether the ownership of the files and directories in the container tree shall be
|
||||||
shall be adjusted to the UID/GID range used, if necessary and user namespacing is enabled. This is
|
adjusted to the UID/GID range used, if necessary and user namespacing is enabled. This is equivalent to the
|
||||||
equivalent to the <option>--private-users-ownership=</option> command line switch. This option is
|
<option>--private-users-chown</option> command line switch. This option is privileged (see
|
||||||
privileged (see above).</para></listitem>
|
above). </para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|||||||
@ -63,14 +63,14 @@ _systemd_nspawn() {
|
|||||||
|
|
||||||
local -A OPTS=(
|
local -A OPTS=(
|
||||||
[STANDALONE]='-h --help --version --private-network -b --boot --read-only -q --quiet --share-system
|
[STANDALONE]='-h --help --version --private-network -b --boot --read-only -q --quiet --share-system
|
||||||
--keep-unit -n --network-veth -j -x --ephemeral -a --as-pid2 -U'
|
--keep-unit -n --network-veth -j -x --ephemeral -a --as-pid2 --private-users-chown -U'
|
||||||
[ARG]='-D --directory -u --user --uuid --capability --drop-capability --link-journal --bind --bind-ro
|
[ARG]='-D --directory -u --user --uuid --capability --drop-capability --link-journal --bind --bind-ro
|
||||||
-M --machine -S --slice -E --setenv -Z --selinux-context -L --selinux-apifs-context
|
-M --machine -S --slice -E --setenv -Z --selinux-context -L --selinux-apifs-context
|
||||||
--register --network-interface --network-bridge --personality -i --image --tmpfs
|
--register --network-interface --network-bridge --personality -i --image --tmpfs
|
||||||
--volatile --network-macvlan --kill-signal --template --notify-ready --root-hash --chdir
|
--volatile --network-macvlan --kill-signal --template --notify-ready --root-hash --chdir
|
||||||
--pivot-root --property --private-users --private-users-ownership --network-namespace-path
|
--pivot-root --property --private-users --network-namespace-path --network-ipvlan
|
||||||
--network-ipvlan --network-veth-extra --network-zone -p --port --system-call-filter --overlay
|
--network-veth-extra --network-zone -p --port --system-call-filter --overlay --overlay-ro
|
||||||
--overlay-ro --settings --rlimit --hostname --no-new-privileges --oom-score-adjust --cpu-affinity
|
--settings --rlimit --hostname --no-new-privileges --oom-score-adjust --cpu-affinity
|
||||||
--resolv-conf --timezone --root-hash-sig'
|
--resolv-conf --timezone --root-hash-sig'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -9,18 +9,3 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int
|
|||||||
int fd_is_ns(int fd, unsigned long nsflag);
|
int fd_is_ns(int fd, unsigned long nsflag);
|
||||||
|
|
||||||
int detach_mount_namespace(void);
|
int detach_mount_namespace(void);
|
||||||
|
|
||||||
static inline bool userns_shift_range_valid(uid_t shift, uid_t range) {
|
|
||||||
/* Checks that the specified userns range makes sense, i.e. contains at least one UID, and the end
|
|
||||||
* doesn't overflow uid_t. */
|
|
||||||
|
|
||||||
assert_cc((uid_t) -1 > 0); /* verify that uid_t is unsigned */
|
|
||||||
|
|
||||||
if (range <= 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (shift > (uid_t) -1 - range)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1306,10 +1306,8 @@ int safe_fork_full(
|
|||||||
saved_ssp = &saved_ss;
|
saved_ssp = &saved_ss;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS)) != 0)
|
if (flags & FORK_NEW_MOUNTNS)
|
||||||
pid = raw_clone(SIGCHLD|
|
pid = raw_clone(SIGCHLD|CLONE_NEWNS);
|
||||||
(FLAGS_SET(flags, FORK_NEW_MOUNTNS) ? CLONE_NEWNS : 0) |
|
|
||||||
(FLAGS_SET(flags, FORK_NEW_USERNS) ? CLONE_NEWUSER : 0));
|
|
||||||
else
|
else
|
||||||
pid = fork();
|
pid = fork();
|
||||||
if (pid < 0)
|
if (pid < 0)
|
||||||
|
|||||||
@ -165,7 +165,6 @@ typedef enum ForkFlags {
|
|||||||
FORK_RLIMIT_NOFILE_SAFE = 1 << 10, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
|
FORK_RLIMIT_NOFILE_SAFE = 1 << 10, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
|
||||||
FORK_STDOUT_TO_STDERR = 1 << 11, /* Make stdout a copy of stderr */
|
FORK_STDOUT_TO_STDERR = 1 << 11, /* Make stdout a copy of stderr */
|
||||||
FORK_FLUSH_STDIO = 1 << 12, /* fflush() stdout (and stderr) before forking */
|
FORK_FLUSH_STDIO = 1 << 12, /* fflush() stdout (and stderr) before forking */
|
||||||
FORK_NEW_USERNS = 1 << 13, /* Run child in its own user namespace */
|
|
||||||
} ForkFlags;
|
} ForkFlags;
|
||||||
|
|
||||||
int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
|
int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
|
||||||
|
|||||||
@ -130,7 +130,7 @@ static inline bool _pure_ in_charset(const char *s, const char* charset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline bool char_is_cc(char p) {
|
static inline bool char_is_cc(char p) {
|
||||||
return (uint8_t) p < ' ' || p == 127;
|
return (p >= 0 && p < ' ') || p == 127;
|
||||||
}
|
}
|
||||||
bool string_has_cc(const char *p, const char *ok) _pure_;
|
bool string_has_cc(const char *p, const char *ok) _pure_;
|
||||||
|
|
||||||
|
|||||||
@ -2170,7 +2170,7 @@ int setup_namespace(
|
|||||||
|
|
||||||
if (root_image) {
|
if (root_image) {
|
||||||
/* A root image is specified, mount it to the right place */
|
/* A root image is specified, mount it to the right place */
|
||||||
r = dissected_image_mount(dissected_image, root, UID_INVALID, UID_INVALID, dissect_image_flags);
|
r = dissected_image_mount(dissected_image, root, UID_INVALID, dissect_image_flags);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_debug_errno(r, "Failed to mount root image: %m");
|
log_debug_errno(r, "Failed to mount root image: %m");
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|||||||
@ -582,7 +582,7 @@ static int action_mount(DissectedImage *m, LoopDevice *d) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = dissected_image_mount_and_warn(m, arg_path, UID_INVALID, UID_INVALID, arg_flags);
|
r = dissected_image_mount_and_warn(m, arg_path, UID_INVALID, arg_flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -628,7 +628,7 @@ static int action_copy(DissectedImage *m, LoopDevice *d) {
|
|||||||
|
|
||||||
created_dir = TAKE_PTR(temp);
|
created_dir = TAKE_PTR(temp);
|
||||||
|
|
||||||
r = dissected_image_mount_and_warn(m, created_dir, UID_INVALID, UID_INVALID, arg_flags);
|
r = dissected_image_mount_and_warn(m, created_dir, UID_INVALID, arg_flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|||||||
@ -176,7 +176,7 @@ int manager_add_user_by_name(
|
|||||||
assert(m);
|
assert(m);
|
||||||
assert(name);
|
assert(name);
|
||||||
|
|
||||||
r = userdb_by_name(name, USERDB_SUPPRESS_SHADOW, &ur);
|
r = userdb_by_name(name, USERDB_AVOID_SHADOW, &ur);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -194,7 +194,7 @@ int manager_add_user_by_uid(
|
|||||||
assert(m);
|
assert(m);
|
||||||
assert(uid_is_valid(uid));
|
assert(uid_is_valid(uid));
|
||||||
|
|
||||||
r = userdb_by_uid(uid, USERDB_SUPPRESS_SHADOW, &ur);
|
r = userdb_by_uid(uid, USERDB_AVOID_SHADOW, &ur);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|||||||
@ -67,8 +67,7 @@ Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0
|
|||||||
Files.Inaccessible, config_parse_inaccessible, 0, 0
|
Files.Inaccessible, config_parse_inaccessible, 0, 0
|
||||||
Files.Overlay, config_parse_overlay, 0, 0
|
Files.Overlay, config_parse_overlay, 0, 0
|
||||||
Files.OverlayReadOnly, config_parse_overlay, 1, 0
|
Files.OverlayReadOnly, config_parse_overlay, 1, 0
|
||||||
Files.PrivateUsersChown, config_parse_userns_chown, 0, offsetof(Settings, userns_ownership)
|
Files.PrivateUsersChown, config_parse_tristate, 0, offsetof(Settings, userns_chown)
|
||||||
Files.PrivateUsersOwnership, config_parse_userns_ownership, 0, offsetof(Settings, userns_ownership)
|
|
||||||
Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network)
|
Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network)
|
||||||
Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces)
|
Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces)
|
||||||
Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, network_macvlan)
|
Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, network_macvlan)
|
||||||
|
|||||||
@ -5,7 +5,6 @@
|
|||||||
#include "conf-parser.h"
|
#include "conf-parser.h"
|
||||||
#include "cpu-set-util.h"
|
#include "cpu-set-util.h"
|
||||||
#include "hostname-util.h"
|
#include "hostname-util.h"
|
||||||
#include "namespace-util.h"
|
|
||||||
#include "nspawn-network.h"
|
#include "nspawn-network.h"
|
||||||
#include "nspawn-settings.h"
|
#include "nspawn-settings.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
@ -34,7 +33,7 @@ Settings *settings_new(void) {
|
|||||||
.timezone = _TIMEZONE_MODE_INVALID,
|
.timezone = _TIMEZONE_MODE_INVALID,
|
||||||
|
|
||||||
.userns_mode = _USER_NAMESPACE_MODE_INVALID,
|
.userns_mode = _USER_NAMESPACE_MODE_INVALID,
|
||||||
.userns_ownership = _USER_NAMESPACE_OWNERSHIP_INVALID,
|
.userns_chown = -1,
|
||||||
.uid_shift = UID_INVALID,
|
.uid_shift = UID_INVALID,
|
||||||
.uid_range = UID_INVALID,
|
.uid_range = UID_INVALID,
|
||||||
|
|
||||||
@ -85,9 +84,12 @@ int settings_load(FILE *f, const char *path, Settings **ret) {
|
|||||||
|
|
||||||
/* Make sure that if userns_mode is set, userns_chown is set to something appropriate, and vice versa. Either
|
/* Make sure that if userns_mode is set, userns_chown is set to something appropriate, and vice versa. Either
|
||||||
* both fields shall be initialized or neither. */
|
* both fields shall be initialized or neither. */
|
||||||
if (s->userns_mode >= 0 && s->userns_ownership < 0)
|
if (s->userns_mode == USER_NAMESPACE_PICK)
|
||||||
s->userns_ownership = s->userns_mode == USER_NAMESPACE_PICK ? USER_NAMESPACE_OWNERSHIP_CHOWN : USER_NAMESPACE_OWNERSHIP_OFF;
|
s->userns_chown = true;
|
||||||
if (s->userns_ownership >= 0 && s->userns_mode < 0)
|
else if (s->userns_mode != _USER_NAMESPACE_MODE_INVALID && s->userns_chown < 0)
|
||||||
|
s->userns_chown = false;
|
||||||
|
|
||||||
|
if (s->userns_chown >= 0 && s->userns_mode == _USER_NAMESPACE_MODE_INVALID)
|
||||||
s->userns_mode = USER_NAMESPACE_NO;
|
s->userns_mode = USER_NAMESPACE_NO;
|
||||||
|
|
||||||
*ret = TAKE_PTR(s);
|
*ret = TAKE_PTR(s);
|
||||||
@ -612,7 +614,7 @@ int config_parse_private_users(
|
|||||||
range++;
|
range++;
|
||||||
|
|
||||||
r = safe_atou32(range, &rn);
|
r = safe_atou32(range, &rn);
|
||||||
if (r < 0) {
|
if (r < 0 || rn <= 0) {
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, r, "UID/GID range invalid, ignoring: %s", range);
|
log_syntax(unit, LOG_WARNING, filename, line, r, "UID/GID range invalid, ignoring: %s", range);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -627,11 +629,6 @@ int config_parse_private_users(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!userns_shift_range_valid(sh, rn)) {
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, 0, "UID/GID shift and range combination invalid, ignoring: %s", range);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
settings->userns_mode = USER_NAMESPACE_FIXED;
|
settings->userns_mode = USER_NAMESPACE_FIXED;
|
||||||
settings->uid_shift = sh;
|
settings->uid_shift = sh;
|
||||||
settings->uid_range = rn;
|
settings->uid_range = rn;
|
||||||
@ -866,44 +863,3 @@ static const char *const timezone_mode_table[_TIMEZONE_MODE_MAX] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(timezone_mode, TimezoneMode, TIMEZONE_AUTO);
|
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(timezone_mode, TimezoneMode, TIMEZONE_AUTO);
|
||||||
|
|
||||||
DEFINE_CONFIG_PARSE_ENUM(config_parse_userns_ownership, user_namespace_ownership, UserNamespaceOwnership, "Failed to parse user namespace ownership mode");
|
|
||||||
|
|
||||||
static const char *const user_namespace_ownership_table[_USER_NAMESPACE_OWNERSHIP_MAX] = {
|
|
||||||
[USER_NAMESPACE_OWNERSHIP_OFF] = "off",
|
|
||||||
[USER_NAMESPACE_OWNERSHIP_CHOWN] = "chown",
|
|
||||||
[USER_NAMESPACE_OWNERSHIP_MAP] = "map",
|
|
||||||
[USER_NAMESPACE_OWNERSHIP_AUTO] = "auto",
|
|
||||||
};
|
|
||||||
|
|
||||||
DEFINE_STRING_TABLE_LOOKUP(user_namespace_ownership, UserNamespaceOwnership);
|
|
||||||
|
|
||||||
int config_parse_userns_chown(
|
|
||||||
const char *unit,
|
|
||||||
const char *filename,
|
|
||||||
unsigned line,
|
|
||||||
const char *section,
|
|
||||||
unsigned section_line,
|
|
||||||
const char *lvalue,
|
|
||||||
int ltype,
|
|
||||||
const char *rvalue,
|
|
||||||
void *data,
|
|
||||||
void *userdata) {
|
|
||||||
|
|
||||||
UserNamespaceOwnership *ownership = data;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(rvalue);
|
|
||||||
assert(ownership);
|
|
||||||
|
|
||||||
/* Compatibility support for UserNamespaceChown=, whose job has been taken over by UserNamespaceOwnership= */
|
|
||||||
|
|
||||||
r = parse_boolean(rvalue);
|
|
||||||
if (r < 0) {
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse user namespace ownership mode, ignoring: %s", rvalue);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
*ownership = r ? USER_NAMESPACE_OWNERSHIP_CHOWN : USER_NAMESPACE_OWNERSHIP_OFF;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -36,15 +36,6 @@ typedef enum UserNamespaceMode {
|
|||||||
_USER_NAMESPACE_MODE_INVALID = -EINVAL,
|
_USER_NAMESPACE_MODE_INVALID = -EINVAL,
|
||||||
} UserNamespaceMode;
|
} UserNamespaceMode;
|
||||||
|
|
||||||
typedef enum UserNamespaceOwnership {
|
|
||||||
USER_NAMESPACE_OWNERSHIP_OFF,
|
|
||||||
USER_NAMESPACE_OWNERSHIP_CHOWN,
|
|
||||||
USER_NAMESPACE_OWNERSHIP_MAP,
|
|
||||||
USER_NAMESPACE_OWNERSHIP_AUTO,
|
|
||||||
_USER_NAMESPACE_OWNERSHIP_MAX,
|
|
||||||
_USER_NAMESPACE_OWNERSHIP_INVALID = -1,
|
|
||||||
} UserNamespaceOwnership;
|
|
||||||
|
|
||||||
typedef enum ResolvConfMode {
|
typedef enum ResolvConfMode {
|
||||||
RESOLV_CONF_OFF,
|
RESOLV_CONF_OFF,
|
||||||
RESOLV_CONF_COPY_HOST, /* /etc/resolv.conf */
|
RESOLV_CONF_COPY_HOST, /* /etc/resolv.conf */
|
||||||
@ -194,7 +185,7 @@ typedef struct Settings {
|
|||||||
VolatileMode volatile_mode;
|
VolatileMode volatile_mode;
|
||||||
CustomMount *custom_mounts;
|
CustomMount *custom_mounts;
|
||||||
size_t n_custom_mounts;
|
size_t n_custom_mounts;
|
||||||
UserNamespaceOwnership userns_ownership;
|
int userns_chown;
|
||||||
|
|
||||||
/* [Network] */
|
/* [Network] */
|
||||||
int private_network;
|
int private_network;
|
||||||
@ -264,8 +255,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_cpu_affinity);
|
|||||||
CONFIG_PARSER_PROTOTYPE(config_parse_resolv_conf);
|
CONFIG_PARSER_PROTOTYPE(config_parse_resolv_conf);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_link_journal);
|
CONFIG_PARSER_PROTOTYPE(config_parse_link_journal);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_timezone);
|
CONFIG_PARSER_PROTOTYPE(config_parse_timezone);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_userns_chown);
|
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_userns_ownership);
|
|
||||||
|
|
||||||
const char *resolv_conf_mode_to_string(ResolvConfMode a) _const_;
|
const char *resolv_conf_mode_to_string(ResolvConfMode a) _const_;
|
||||||
ResolvConfMode resolv_conf_mode_from_string(const char *s) _pure_;
|
ResolvConfMode resolv_conf_mode_from_string(const char *s) _pure_;
|
||||||
@ -273,9 +262,6 @@ ResolvConfMode resolv_conf_mode_from_string(const char *s) _pure_;
|
|||||||
const char *timezone_mode_to_string(TimezoneMode a) _const_;
|
const char *timezone_mode_to_string(TimezoneMode a) _const_;
|
||||||
TimezoneMode timezone_mode_from_string(const char *s) _pure_;
|
TimezoneMode timezone_mode_from_string(const char *s) _pure_;
|
||||||
|
|
||||||
const char *user_namespace_ownership_to_string(UserNamespaceOwnership a) _const_;
|
|
||||||
UserNamespaceOwnership user_namespace_ownership_from_string(const char *s) _pure_;
|
|
||||||
|
|
||||||
int parse_link_journal(const char *s, LinkJournal *ret_mode, bool *ret_try);
|
int parse_link_journal(const char *s, LinkJournal *ret_mode, bool *ret_try);
|
||||||
|
|
||||||
void device_node_array_free(DeviceNode *node, size_t n);
|
void device_node_array_free(DeviceNode *node, size_t n);
|
||||||
|
|||||||
@ -194,7 +194,7 @@ static char **arg_property = NULL;
|
|||||||
static sd_bus_message *arg_property_message = NULL;
|
static sd_bus_message *arg_property_message = NULL;
|
||||||
static UserNamespaceMode arg_userns_mode = USER_NAMESPACE_NO;
|
static UserNamespaceMode arg_userns_mode = USER_NAMESPACE_NO;
|
||||||
static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U;
|
static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U;
|
||||||
static UserNamespaceOwnership arg_userns_ownership = _USER_NAMESPACE_OWNERSHIP_INVALID;
|
static bool arg_userns_chown = false;
|
||||||
static int arg_kill_signal = 0;
|
static int arg_kill_signal = 0;
|
||||||
static CGroupUnified arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_UNKNOWN;
|
static CGroupUnified arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_UNKNOWN;
|
||||||
static SettingsMask arg_settings_mask = 0;
|
static SettingsMask arg_settings_mask = 0;
|
||||||
@ -352,9 +352,7 @@ static int help(void) {
|
|||||||
" -U --private-users=pick Run within user namespace, autoselect UID/GID range\n"
|
" -U --private-users=pick Run within user namespace, autoselect UID/GID range\n"
|
||||||
" --private-users[=UIDBASE[:NUIDS]]\n"
|
" --private-users[=UIDBASE[:NUIDS]]\n"
|
||||||
" Similar, but with user configured UID/GID range\n"
|
" Similar, but with user configured UID/GID range\n"
|
||||||
" --private-users-ownership=MODE\n"
|
" --private-users-chown Adjust OS tree ownership to private UID/GID range\n\n"
|
||||||
" Adjust ('chown') or map ('map') OS tree ownership\n"
|
|
||||||
" to private UID/GID range\n\n"
|
|
||||||
"%3$sNetworking:%4$s\n"
|
"%3$sNetworking:%4$s\n"
|
||||||
" --private-network Disable network in container\n"
|
" --private-network Disable network in container\n"
|
||||||
" --network-interface=INTERFACE\n"
|
" --network-interface=INTERFACE\n"
|
||||||
@ -451,10 +449,10 @@ static int custom_mount_check_all(void) {
|
|||||||
CustomMount *m = &arg_custom_mounts[i];
|
CustomMount *m = &arg_custom_mounts[i];
|
||||||
|
|
||||||
if (path_equal(m->destination, "/") && arg_userns_mode != USER_NAMESPACE_NO) {
|
if (path_equal(m->destination, "/") && arg_userns_mode != USER_NAMESPACE_NO) {
|
||||||
if (arg_userns_ownership != USER_NAMESPACE_OWNERSHIP_OFF)
|
if (arg_userns_chown)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||||
"--private-users-ownership=own not be combined with custom root mounts.");
|
"--private-users-chown may not be combined with custom root mounts.");
|
||||||
if (arg_uid_shift == UID_INVALID)
|
else if (arg_uid_shift == UID_INVALID)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||||
"--private-users with automatic UID shift may not be combined with custom root mounts.");
|
"--private-users with automatic UID shift may not be combined with custom root mounts.");
|
||||||
}
|
}
|
||||||
@ -687,7 +685,6 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
ARG_CHDIR,
|
ARG_CHDIR,
|
||||||
ARG_PIVOT_ROOT,
|
ARG_PIVOT_ROOT,
|
||||||
ARG_PRIVATE_USERS_CHOWN,
|
ARG_PRIVATE_USERS_CHOWN,
|
||||||
ARG_PRIVATE_USERS_OWNERSHIP,
|
|
||||||
ARG_NOTIFY_READY,
|
ARG_NOTIFY_READY,
|
||||||
ARG_ROOT_HASH,
|
ARG_ROOT_HASH,
|
||||||
ARG_ROOT_HASH_SIG,
|
ARG_ROOT_HASH_SIG,
|
||||||
@ -755,8 +752,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
{ "port", required_argument, NULL, 'p' },
|
{ "port", required_argument, NULL, 'p' },
|
||||||
{ "property", required_argument, NULL, ARG_PROPERTY },
|
{ "property", required_argument, NULL, ARG_PROPERTY },
|
||||||
{ "private-users", optional_argument, NULL, ARG_PRIVATE_USERS },
|
{ "private-users", optional_argument, NULL, ARG_PRIVATE_USERS },
|
||||||
{ "private-users-chown", optional_argument, NULL, ARG_PRIVATE_USERS_CHOWN }, /* obsolete */
|
{ "private-users-chown", optional_argument, NULL, ARG_PRIVATE_USERS_CHOWN },
|
||||||
{ "private-users-ownership",required_argument, NULL, ARG_PRIVATE_USERS_OWNERSHIP},
|
|
||||||
{ "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL },
|
{ "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL },
|
||||||
{ "settings", required_argument, NULL, ARG_SETTINGS },
|
{ "settings", required_argument, NULL, ARG_SETTINGS },
|
||||||
{ "chdir", required_argument, NULL, ARG_CHDIR },
|
{ "chdir", required_argument, NULL, ARG_CHDIR },
|
||||||
@ -1199,41 +1195,29 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ARG_PRIVATE_USERS: {
|
case ARG_PRIVATE_USERS: {
|
||||||
int boolean;
|
int boolean = -1;
|
||||||
|
|
||||||
if (!optarg)
|
if (!optarg)
|
||||||
boolean = true;
|
boolean = true;
|
||||||
else if (!in_charset(optarg, DIGITS))
|
else if (!in_charset(optarg, DIGITS))
|
||||||
/* do *not* parse numbers as booleans */
|
/* do *not* parse numbers as booleans */
|
||||||
boolean = parse_boolean(optarg);
|
boolean = parse_boolean(optarg);
|
||||||
else
|
|
||||||
boolean = -1;
|
|
||||||
|
|
||||||
if (boolean == 0) {
|
if (boolean == false) {
|
||||||
/* no: User namespacing off */
|
/* no: User namespacing off */
|
||||||
arg_userns_mode = USER_NAMESPACE_NO;
|
arg_userns_mode = USER_NAMESPACE_NO;
|
||||||
arg_uid_shift = UID_INVALID;
|
arg_uid_shift = UID_INVALID;
|
||||||
arg_uid_range = UINT32_C(0x10000);
|
arg_uid_range = UINT32_C(0x10000);
|
||||||
} else if (boolean > 0) {
|
} else if (boolean == true) {
|
||||||
/* yes: User namespacing on, UID range is read from root dir */
|
/* yes: User namespacing on, UID range is read from root dir */
|
||||||
arg_userns_mode = USER_NAMESPACE_FIXED;
|
arg_userns_mode = USER_NAMESPACE_FIXED;
|
||||||
arg_uid_shift = UID_INVALID;
|
arg_uid_shift = UID_INVALID;
|
||||||
arg_uid_range = UINT32_C(0x10000);
|
arg_uid_range = UINT32_C(0x10000);
|
||||||
} else if (streq(optarg, "pick")) {
|
} else if (streq(optarg, "pick")) {
|
||||||
/* pick: User namespacing on, UID range is picked randomly */
|
/* pick: User namespacing on, UID range is picked randomly */
|
||||||
arg_userns_mode = USER_NAMESPACE_PICK; /* Note that arg_userns_ownership is
|
arg_userns_mode = USER_NAMESPACE_PICK;
|
||||||
* implied by USER_NAMESPACE_PICK
|
|
||||||
* further down. */
|
|
||||||
arg_uid_shift = UID_INVALID;
|
arg_uid_shift = UID_INVALID;
|
||||||
arg_uid_range = UINT32_C(0x10000);
|
arg_uid_range = UINT32_C(0x10000);
|
||||||
|
|
||||||
} else if (streq(optarg, "identity")) {
|
|
||||||
/* identitiy: User namespaces on, UID range is map the 0…0xFFFF range to
|
|
||||||
* itself, i.e. we don't actually map anything, but do take benefit of
|
|
||||||
* isolation of capability sets. */
|
|
||||||
arg_userns_mode = USER_NAMESPACE_FIXED;
|
|
||||||
arg_uid_shift = 0;
|
|
||||||
arg_uid_range = UINT32_C(0x10000);
|
|
||||||
} else {
|
} else {
|
||||||
_cleanup_free_ char *buffer = NULL;
|
_cleanup_free_ char *buffer = NULL;
|
||||||
const char *range, *shift;
|
const char *range, *shift;
|
||||||
@ -1259,20 +1243,19 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
return log_error_errno(r, "Failed to parse UID \"%s\": %m", optarg);
|
return log_error_errno(r, "Failed to parse UID \"%s\": %m", optarg);
|
||||||
|
|
||||||
arg_userns_mode = USER_NAMESPACE_FIXED;
|
arg_userns_mode = USER_NAMESPACE_FIXED;
|
||||||
|
|
||||||
if (!userns_shift_range_valid(arg_uid_shift, arg_uid_range))
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID range cannot be empty or go beyond " UID_FMT ".", UID_INVALID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arg_uid_range <= 0)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"UID range cannot be 0.");
|
||||||
|
|
||||||
arg_settings_mask |= SETTING_USERNS;
|
arg_settings_mask |= SETTING_USERNS;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'U':
|
case 'U':
|
||||||
if (userns_supported()) {
|
if (userns_supported()) {
|
||||||
arg_userns_mode = USER_NAMESPACE_PICK; /* Note that arg_userns_ownership is
|
arg_userns_mode = USER_NAMESPACE_PICK;
|
||||||
* implied by USER_NAMESPACE_PICK
|
|
||||||
* further down. */
|
|
||||||
arg_uid_shift = UID_INVALID;
|
arg_uid_shift = UID_INVALID;
|
||||||
arg_uid_range = UINT32_C(0x10000);
|
arg_uid_range = UINT32_C(0x10000);
|
||||||
|
|
||||||
@ -1282,20 +1265,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ARG_PRIVATE_USERS_CHOWN:
|
case ARG_PRIVATE_USERS_CHOWN:
|
||||||
arg_userns_ownership = USER_NAMESPACE_OWNERSHIP_CHOWN;
|
arg_userns_chown = true;
|
||||||
|
|
||||||
arg_settings_mask |= SETTING_USERNS;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ARG_PRIVATE_USERS_OWNERSHIP:
|
|
||||||
if (streq(optarg, "help")) {
|
|
||||||
DUMP_STRING_TABLE(user_namespace_ownership, UserNamespaceOwnership, _USER_NAMESPACE_OWNERSHIP_MAX);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
arg_userns_ownership = user_namespace_ownership_from_string(optarg);
|
|
||||||
if (arg_userns_ownership < 0)
|
|
||||||
return log_error_errno(arg_userns_ownership, "Cannot parse --user-namespace-ownership= value: %s", optarg);
|
|
||||||
|
|
||||||
arg_settings_mask |= SETTING_USERNS;
|
arg_settings_mask |= SETTING_USERNS;
|
||||||
break;
|
break;
|
||||||
@ -1731,10 +1701,8 @@ static int verify_arguments(void) {
|
|||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--boot cannot be used without namespacing.");
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--boot cannot be used without namespacing.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arg_userns_ownership < 0)
|
if (arg_userns_mode == USER_NAMESPACE_PICK)
|
||||||
arg_userns_ownership =
|
arg_userns_chown = true;
|
||||||
arg_userns_mode == USER_NAMESPACE_PICK ? USER_NAMESPACE_OWNERSHIP_AUTO :
|
|
||||||
USER_NAMESPACE_OWNERSHIP_OFF;
|
|
||||||
|
|
||||||
if (arg_start_mode == START_BOOT && arg_kill_signal <= 0)
|
if (arg_start_mode == START_BOOT && arg_kill_signal <= 0)
|
||||||
arg_kill_signal = SIGRTMIN+3;
|
arg_kill_signal = SIGRTMIN+3;
|
||||||
@ -1768,15 +1736,15 @@ static int verify_arguments(void) {
|
|||||||
if (arg_userns_mode != USER_NAMESPACE_NO && !userns_supported())
|
if (arg_userns_mode != USER_NAMESPACE_NO && !userns_supported())
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--private-users= is not supported, kernel compiled without user namespace support.");
|
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--private-users= is not supported, kernel compiled without user namespace support.");
|
||||||
|
|
||||||
if (arg_userns_ownership == USER_NAMESPACE_OWNERSHIP_CHOWN && arg_read_only)
|
if (arg_userns_chown && arg_read_only)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||||
"--read-only and --private-users-ownership=chown may not be combined.");
|
"--read-only and --private-users-chown may not be combined.");
|
||||||
|
|
||||||
/* We don't support --private-users-ownership=chown together with any of the volatile modes since we
|
/* We don't support --private-users-chown together with any of the volatile modes since we couldn't
|
||||||
* couldn't change the read-only part of the tree (i.e. /usr) anyway, or because it would trigger a
|
* change the read-only part of the tree (i.e. /usr) anyway, or because it would trigger a massive
|
||||||
* massive copy-up (in case of overlay) making the entire exercise pointless. */
|
* copy-up (in case of overlay) making the entire exercise pointless. */
|
||||||
if (arg_userns_ownership == USER_NAMESPACE_OWNERSHIP_CHOWN && arg_volatile_mode != VOLATILE_NO)
|
if (arg_userns_chown && arg_volatile_mode != VOLATILE_NO)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--volatile= and --private-users-ownership=chown may not be combined.");
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--volatile= and --private-users-chown may not be combined.");
|
||||||
|
|
||||||
/* If --network-namespace-path is given with any other network-related option (except --private-network),
|
/* If --network-namespace-path is given with any other network-related option (except --private-network),
|
||||||
* we need to error out, to avoid conflicts between different network options. */
|
* we need to error out, to avoid conflicts between different network options. */
|
||||||
@ -2813,7 +2781,7 @@ static int recursive_chown(const char *directory, uid_t shift, uid_t range) {
|
|||||||
|
|
||||||
assert(directory);
|
assert(directory);
|
||||||
|
|
||||||
if (arg_userns_mode == USER_NAMESPACE_NO || arg_userns_ownership != USER_NAMESPACE_OWNERSHIP_CHOWN)
|
if (arg_userns_mode == USER_NAMESPACE_NO || !arg_userns_chown)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
r = path_patch_uid(directory, arg_uid_shift, arg_uid_range);
|
r = path_patch_uid(directory, arg_uid_shift, arg_uid_range);
|
||||||
@ -3044,6 +3012,7 @@ static int chase_symlinks_and_update(char **p, unsigned flags) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int determine_uid_shift(const char *directory) {
|
static int determine_uid_shift(const char *directory) {
|
||||||
|
int r;
|
||||||
|
|
||||||
if (arg_userns_mode == USER_NAMESPACE_NO) {
|
if (arg_userns_mode == USER_NAMESPACE_NO) {
|
||||||
arg_uid_shift = 0;
|
arg_uid_shift = 0;
|
||||||
@ -3053,9 +3022,8 @@ static int determine_uid_shift(const char *directory) {
|
|||||||
if (arg_uid_shift == UID_INVALID) {
|
if (arg_uid_shift == UID_INVALID) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
/* Read the UID shift off the image. Maybe we can reuse this to avoid chowning. */
|
r = stat(directory, &st);
|
||||||
|
if (r < 0)
|
||||||
if (stat(directory, &st) < 0)
|
|
||||||
return log_error_errno(errno, "Failed to determine UID base of %s: %m", directory);
|
return log_error_errno(errno, "Failed to determine UID base of %s: %m", directory);
|
||||||
|
|
||||||
arg_uid_shift = st.st_uid & UINT32_C(0xffff0000);
|
arg_uid_shift = st.st_uid & UINT32_C(0xffff0000);
|
||||||
@ -3065,22 +3033,11 @@ static int determine_uid_shift(const char *directory) {
|
|||||||
"UID and GID base of %s don't match.", directory);
|
"UID and GID base of %s don't match.", directory);
|
||||||
|
|
||||||
arg_uid_range = UINT32_C(0x10000);
|
arg_uid_range = UINT32_C(0x10000);
|
||||||
|
}
|
||||||
|
|
||||||
if (arg_uid_shift != 0) {
|
if (arg_uid_shift > UID_INVALID - arg_uid_range)
|
||||||
/* If the image is shifted already, then we'll fall back to classic chowning, for
|
|
||||||
* compatibility (and simplicity), or refuse if mapping is explicitly requested. */
|
|
||||||
|
|
||||||
if (arg_userns_ownership == USER_NAMESPACE_OWNERSHIP_AUTO) {
|
|
||||||
log_debug("UID base of %s is non-zero, not using UID mapping.", directory);
|
|
||||||
arg_userns_ownership = USER_NAMESPACE_OWNERSHIP_CHOWN;
|
|
||||||
} else if (arg_userns_ownership == USER_NAMESPACE_OWNERSHIP_MAP)
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||||
"UID base of %s is not zero, UID mapping not supported.", directory);
|
"UID base too high for UID range.");
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!userns_shift_range_valid(arg_uid_shift, arg_uid_range))
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID base too high for UID range.");
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -3570,7 +3527,6 @@ static int outer_child(
|
|||||||
|
|
||||||
_cleanup_strv_free_ char **os_release_pairs = NULL;
|
_cleanup_strv_free_ char **os_release_pairs = NULL;
|
||||||
_cleanup_close_ int fd = -1;
|
_cleanup_close_ int fd = -1;
|
||||||
bool idmap = false;
|
|
||||||
const char *p;
|
const char *p;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
ssize_t l;
|
ssize_t l;
|
||||||
@ -3618,7 +3574,6 @@ static int outer_child(
|
|||||||
dissected_image,
|
dissected_image,
|
||||||
directory,
|
directory,
|
||||||
arg_uid_shift,
|
arg_uid_shift,
|
||||||
arg_uid_range,
|
|
||||||
DISSECT_IMAGE_MOUNT_ROOT_ONLY|
|
DISSECT_IMAGE_MOUNT_ROOT_ONLY|
|
||||||
DISSECT_IMAGE_DISCARD_ON_LOOP|
|
DISSECT_IMAGE_DISCARD_ON_LOOP|
|
||||||
DISSECT_IMAGE_USR_NO_ROOT|
|
DISSECT_IMAGE_USR_NO_ROOT|
|
||||||
@ -3674,32 +3629,6 @@ static int outer_child(
|
|||||||
directory = "/run/systemd/nspawn-root";
|
directory = "/run/systemd/nspawn-root";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arg_userns_mode != USER_NAMESPACE_NO &&
|
|
||||||
IN_SET(arg_userns_ownership, USER_NAMESPACE_OWNERSHIP_MAP, USER_NAMESPACE_OWNERSHIP_AUTO) &&
|
|
||||||
arg_uid_shift != 0) {
|
|
||||||
r = make_mount_point(directory);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
r = remount_idmap(directory, arg_uid_shift, arg_uid_range);
|
|
||||||
if (r == -EINVAL || ERRNO_IS_NOT_SUPPORTED(r)) {
|
|
||||||
/* This might fail because the kernel or file system doesn't support idmapping. We
|
|
||||||
* can't really distinguish this nicely, nor do we have any guarantees about the
|
|
||||||
* error codes we see, could be EOPNOTSUPP or EINVAL. */
|
|
||||||
if (arg_userns_ownership != USER_NAMESPACE_OWNERSHIP_AUTO)
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
|
||||||
"ID mapped mounts are apparently not available, sorry.");
|
|
||||||
|
|
||||||
log_debug("ID mapped mounts are apparently not available on this kernel or for the selected file system, reverting to recursive chown()ing.");
|
|
||||||
arg_userns_ownership = USER_NAMESPACE_OWNERSHIP_CHOWN;
|
|
||||||
} else if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to set up ID mapped mounts: %m");
|
|
||||||
else {
|
|
||||||
log_debug("ID mapped mounts available, making use of them.");
|
|
||||||
idmap = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r = setup_pivot_root(
|
r = setup_pivot_root(
|
||||||
directory,
|
directory,
|
||||||
arg_pivot_root_new,
|
arg_pivot_root_new,
|
||||||
@ -3726,9 +3655,11 @@ static int outer_child(
|
|||||||
return r;
|
return r;
|
||||||
|
|
||||||
/* Make sure we always have a mount that we can move to root later on. */
|
/* Make sure we always have a mount that we can move to root later on. */
|
||||||
r = make_mount_point(directory);
|
if (!path_is_mount_point(directory, NULL, 0)) {
|
||||||
|
r = mount_nofollow_verbose(LOG_ERR, directory, directory, NULL, MS_BIND|MS_REC, NULL);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
if (dissected_image) {
|
if (dissected_image) {
|
||||||
/* Now we know the uid shift, let's now mount everything else that might be in the image. */
|
/* Now we know the uid shift, let's now mount everything else that might be in the image. */
|
||||||
@ -3736,12 +3667,10 @@ static int outer_child(
|
|||||||
dissected_image,
|
dissected_image,
|
||||||
directory,
|
directory,
|
||||||
arg_uid_shift,
|
arg_uid_shift,
|
||||||
arg_uid_range,
|
|
||||||
DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY|
|
DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY|
|
||||||
DISSECT_IMAGE_DISCARD_ON_LOOP|
|
DISSECT_IMAGE_DISCARD_ON_LOOP|
|
||||||
DISSECT_IMAGE_USR_NO_ROOT|
|
DISSECT_IMAGE_USR_NO_ROOT|
|
||||||
(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK|DISSECT_IMAGE_GROWFS)|
|
(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK|DISSECT_IMAGE_GROWFS));
|
||||||
(idmap ? DISSECT_IMAGE_MOUNT_IDMAPPED : 0));
|
|
||||||
if (r == -EUCLEAN)
|
if (r == -EUCLEAN)
|
||||||
return log_error_errno(r, "File system check for image failed: %m");
|
return log_error_errno(r, "File system check for image failed: %m");
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -4297,7 +4226,7 @@ static int merge_settings(Settings *settings, const char *path) {
|
|||||||
arg_userns_mode = settings->userns_mode;
|
arg_userns_mode = settings->userns_mode;
|
||||||
arg_uid_shift = settings->uid_shift;
|
arg_uid_shift = settings->uid_shift;
|
||||||
arg_uid_range = settings->uid_range;
|
arg_uid_range = settings->uid_range;
|
||||||
arg_userns_ownership = settings->userns_ownership;
|
arg_userns_chown = settings->userns_chown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -582,7 +582,7 @@ enum nss_status _nss_systemd_initgroups_dyn(
|
|||||||
/* The group might be defined via traditional NSS only, hence let's do a full look-up without
|
/* The group might be defined via traditional NSS only, hence let's do a full look-up without
|
||||||
* disabling NSS. This means we are operating recursively here. */
|
* disabling NSS. This means we are operating recursively here. */
|
||||||
|
|
||||||
r = groupdb_by_name(group_name, (nss_glue_userdb_flags() & ~USERDB_EXCLUDE_NSS) | USERDB_SUPPRESS_SHADOW, &g);
|
r = groupdb_by_name(group_name, (nss_glue_userdb_flags() & ~USERDB_AVOID_NSS) | USERDB_AVOID_SHADOW, &g);
|
||||||
if (r == -ESRCH)
|
if (r == -ESRCH)
|
||||||
continue;
|
continue;
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
|
|||||||
@ -11,11 +11,11 @@
|
|||||||
#include "userdb.h"
|
#include "userdb.h"
|
||||||
|
|
||||||
UserDBFlags nss_glue_userdb_flags(void) {
|
UserDBFlags nss_glue_userdb_flags(void) {
|
||||||
UserDBFlags flags = USERDB_EXCLUDE_NSS;
|
UserDBFlags flags = USERDB_AVOID_NSS;
|
||||||
|
|
||||||
/* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
|
/* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
|
||||||
if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
|
if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
|
||||||
flags |= USERDB_EXCLUDE_DYNAMIC_USER;
|
flags |= USERDB_AVOID_DYNAMIC_USER;
|
||||||
|
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -426,7 +426,7 @@ static int portable_extract_by_path(
|
|||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
seq[0] = safe_close(seq[0]);
|
seq[0] = safe_close(seq[0]);
|
||||||
|
|
||||||
r = dissected_image_mount(m, tmpdir, UID_INVALID, UID_INVALID, DISSECT_IMAGE_READ_ONLY);
|
r = dissected_image_mount(m, tmpdir, UID_INVALID, DISSECT_IMAGE_READ_ONLY);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_debug_errno(r, "Failed to mount dissected image: %m");
|
log_debug_errno(r, "Failed to mount dissected image: %m");
|
||||||
goto child_finish;
|
goto child_finish;
|
||||||
|
|||||||
@ -1472,12 +1472,11 @@ static int mount_partition(
|
|||||||
const char *where,
|
const char *where,
|
||||||
const char *directory,
|
const char *directory,
|
||||||
uid_t uid_shift,
|
uid_t uid_shift,
|
||||||
uid_t uid_range,
|
|
||||||
DissectImageFlags flags) {
|
DissectImageFlags flags) {
|
||||||
|
|
||||||
_cleanup_free_ char *chased = NULL, *options = NULL;
|
_cleanup_free_ char *chased = NULL, *options = NULL;
|
||||||
const char *p, *node, *fstype;
|
const char *p, *node, *fstype;
|
||||||
bool rw, remap_uid_gid = false;
|
bool rw;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
@ -1537,9 +1536,7 @@ static int mount_partition(
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uid_is_valid(uid_shift) && uid_shift != 0) {
|
if (uid_is_valid(uid_shift) && uid_shift != 0 && fstype_can_uid_gid(fstype)) {
|
||||||
|
|
||||||
if (fstype_can_uid_gid(fstype)) {
|
|
||||||
_cleanup_free_ char *uid_option = NULL;
|
_cleanup_free_ char *uid_option = NULL;
|
||||||
|
|
||||||
if (asprintf(&uid_option, "uid=" UID_FMT ",gid=" GID_FMT, uid_shift, (gid_t) uid_shift) < 0)
|
if (asprintf(&uid_option, "uid=" UID_FMT ",gid=" GID_FMT, uid_shift, (gid_t) uid_shift) < 0)
|
||||||
@ -1547,8 +1544,6 @@ static int mount_partition(
|
|||||||
|
|
||||||
if (!strextend_with_separator(&options, ",", uid_option))
|
if (!strextend_with_separator(&options, ",", uid_option))
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
} else if (FLAGS_SET(flags, DISSECT_IMAGE_MOUNT_IDMAPPED))
|
|
||||||
remap_uid_gid = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isempty(m->mount_options))
|
if (!isempty(m->mount_options))
|
||||||
@ -1583,12 +1578,6 @@ static int mount_partition(
|
|||||||
if (rw && m->growfs && FLAGS_SET(flags, DISSECT_IMAGE_GROWFS))
|
if (rw && m->growfs && FLAGS_SET(flags, DISSECT_IMAGE_GROWFS))
|
||||||
(void) fs_grow(node, p);
|
(void) fs_grow(node, p);
|
||||||
|
|
||||||
if (remap_uid_gid) {
|
|
||||||
r = remount_idmap(p, uid_shift, uid_range);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1618,13 +1607,7 @@ static int mount_root_tmpfs(const char *where, uid_t uid_shift, DissectImageFlag
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int dissected_image_mount(
|
int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift, DissectImageFlags flags) {
|
||||||
DissectedImage *m,
|
|
||||||
const char *where,
|
|
||||||
uid_t uid_shift,
|
|
||||||
uid_t uid_range,
|
|
||||||
DissectImageFlags flags) {
|
|
||||||
|
|
||||||
int r, xbootldr_mounted;
|
int r, xbootldr_mounted;
|
||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
@ -1648,14 +1631,14 @@ int dissected_image_mount(
|
|||||||
|
|
||||||
/* First mount the root fs. If there's none we use a tmpfs. */
|
/* First mount the root fs. If there's none we use a tmpfs. */
|
||||||
if (m->partitions[PARTITION_ROOT].found)
|
if (m->partitions[PARTITION_ROOT].found)
|
||||||
r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, uid_shift, uid_range, flags);
|
r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, uid_shift, flags);
|
||||||
else
|
else
|
||||||
r = mount_root_tmpfs(where, uid_shift, flags);
|
r = mount_root_tmpfs(where, uid_shift, flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
/* For us mounting root always means mounting /usr as well */
|
/* For us mounting root always means mounting /usr as well */
|
||||||
r = mount_partition(m->partitions + PARTITION_USR, where, "/usr", uid_shift, uid_range, flags);
|
r = mount_partition(m->partitions + PARTITION_USR, where, "/usr", uid_shift, flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -1676,23 +1659,23 @@ int dissected_image_mount(
|
|||||||
if (flags & DISSECT_IMAGE_MOUNT_ROOT_ONLY)
|
if (flags & DISSECT_IMAGE_MOUNT_ROOT_ONLY)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", uid_shift, uid_range, flags);
|
r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", uid_shift, flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = mount_partition(m->partitions + PARTITION_SRV, where, "/srv", uid_shift, uid_range, flags);
|
r = mount_partition(m->partitions + PARTITION_SRV, where, "/srv", uid_shift, flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = mount_partition(m->partitions + PARTITION_VAR, where, "/var", uid_shift, uid_range, flags);
|
r = mount_partition(m->partitions + PARTITION_VAR, where, "/var", uid_shift, flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = mount_partition(m->partitions + PARTITION_TMP, where, "/var/tmp", uid_shift, uid_range, flags);
|
r = mount_partition(m->partitions + PARTITION_TMP, where, "/var/tmp", uid_shift, flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
xbootldr_mounted = mount_partition(m->partitions + PARTITION_XBOOTLDR, where, "/boot", uid_shift, uid_range, flags);
|
xbootldr_mounted = mount_partition(m->partitions + PARTITION_XBOOTLDR, where, "/boot", uid_shift, flags);
|
||||||
if (xbootldr_mounted < 0)
|
if (xbootldr_mounted < 0)
|
||||||
return xbootldr_mounted;
|
return xbootldr_mounted;
|
||||||
|
|
||||||
@ -1718,7 +1701,7 @@ int dissected_image_mount(
|
|||||||
return r;
|
return r;
|
||||||
} else if (dir_is_empty(p) > 0) {
|
} else if (dir_is_empty(p) > 0) {
|
||||||
/* It exists and is an empty directory. Let's mount the ESP there. */
|
/* It exists and is an empty directory. Let's mount the ESP there. */
|
||||||
r = mount_partition(m->partitions + PARTITION_ESP, where, "/boot", uid_shift, uid_range, flags);
|
r = mount_partition(m->partitions + PARTITION_ESP, where, "/boot", uid_shift, flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -1730,7 +1713,7 @@ int dissected_image_mount(
|
|||||||
if (!esp_done) {
|
if (!esp_done) {
|
||||||
/* OK, let's mount the ESP now to /efi (possibly creating the dir if missing) */
|
/* OK, let's mount the ESP now to /efi (possibly creating the dir if missing) */
|
||||||
|
|
||||||
r = mount_partition(m->partitions + PARTITION_ESP, where, "/efi", uid_shift, uid_range, flags);
|
r = mount_partition(m->partitions + PARTITION_ESP, where, "/efi", uid_shift, flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@ -1739,19 +1722,13 @@ int dissected_image_mount(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int dissected_image_mount_and_warn(
|
int dissected_image_mount_and_warn(DissectedImage *m, const char *where, uid_t uid_shift, DissectImageFlags flags) {
|
||||||
DissectedImage *m,
|
|
||||||
const char *where,
|
|
||||||
uid_t uid_shift,
|
|
||||||
uid_t uid_range,
|
|
||||||
DissectImageFlags flags) {
|
|
||||||
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
assert(where);
|
assert(where);
|
||||||
|
|
||||||
r = dissected_image_mount(m, where, uid_shift, uid_range, flags);
|
r = dissected_image_mount(m, where, uid_shift, flags);
|
||||||
if (r == -ENXIO)
|
if (r == -ENXIO)
|
||||||
return log_error_errno(r, "Not root file system found in image.");
|
return log_error_errno(r, "Not root file system found in image.");
|
||||||
if (r == -EMEDIUMTYPE)
|
if (r == -EMEDIUMTYPE)
|
||||||
@ -2546,7 +2523,6 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
|||||||
m,
|
m,
|
||||||
t,
|
t,
|
||||||
UID_INVALID,
|
UID_INVALID,
|
||||||
UID_INVALID,
|
|
||||||
DISSECT_IMAGE_READ_ONLY|
|
DISSECT_IMAGE_READ_ONLY|
|
||||||
DISSECT_IMAGE_MOUNT_ROOT_ONLY|
|
DISSECT_IMAGE_MOUNT_ROOT_ONLY|
|
||||||
DISSECT_IMAGE_VALIDATE_OS|
|
DISSECT_IMAGE_VALIDATE_OS|
|
||||||
@ -2835,7 +2811,7 @@ int mount_image_privately_interactively(
|
|||||||
|
|
||||||
created_dir = TAKE_PTR(temp);
|
created_dir = TAKE_PTR(temp);
|
||||||
|
|
||||||
r = dissected_image_mount_and_warn(dissected_image, created_dir, UID_INVALID, UID_INVALID, flags);
|
r = dissected_image_mount_and_warn(dissected_image, created_dir, UID_INVALID, flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -2941,7 +2917,7 @@ int verity_dissect_and_mount(
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_debug_errno(r, "Failed to umount under destination directory %s: %m", dest);
|
return log_debug_errno(r, "Failed to umount under destination directory %s: %m", dest);
|
||||||
|
|
||||||
r = dissected_image_mount(dissected_image, dest, UID_INVALID, UID_INVALID, dissect_image_flags);
|
r = dissected_image_mount(dissected_image, dest, UID_INVALID, dissect_image_flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_debug_errno(r, "Failed to mount image: %m");
|
return log_debug_errno(r, "Failed to mount image: %m");
|
||||||
|
|
||||||
|
|||||||
@ -112,7 +112,6 @@ typedef enum DissectImageFlags {
|
|||||||
DISSECT_IMAGE_READ_ONLY = DISSECT_IMAGE_DEVICE_READ_ONLY |
|
DISSECT_IMAGE_READ_ONLY = DISSECT_IMAGE_DEVICE_READ_ONLY |
|
||||||
DISSECT_IMAGE_MOUNT_READ_ONLY,
|
DISSECT_IMAGE_MOUNT_READ_ONLY,
|
||||||
DISSECT_IMAGE_GROWFS = 1 << 18, /* Grow file systems in partitions marked for that to the size of the partitions after mount */
|
DISSECT_IMAGE_GROWFS = 1 << 18, /* Grow file systems in partitions marked for that to the size of the partitions after mount */
|
||||||
DISSECT_IMAGE_MOUNT_IDMAPPED = 1 << 19, /* Mount mounts with kernel 5.12-style userns ID mapping, if file sytem type doesn't support uid=/gid= */
|
|
||||||
} DissectImageFlags;
|
} DissectImageFlags;
|
||||||
|
|
||||||
struct DissectedImage {
|
struct DissectedImage {
|
||||||
@ -170,8 +169,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
|
|||||||
|
|
||||||
int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const VeritySettings *verity, DissectImageFlags flags, DecryptedImage **ret);
|
int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const VeritySettings *verity, DissectImageFlags flags, DecryptedImage **ret);
|
||||||
int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const VeritySettings *verity, DissectImageFlags flags, DecryptedImage **ret);
|
int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const VeritySettings *verity, DissectImageFlags flags, DecryptedImage **ret);
|
||||||
int dissected_image_mount(DissectedImage *m, const char *dest, uid_t uid_shift, uid_t uid_range, DissectImageFlags flags);
|
int dissected_image_mount(DissectedImage *m, const char *dest, uid_t uid_shift, DissectImageFlags flags);
|
||||||
int dissected_image_mount_and_warn(DissectedImage *m, const char *where, uid_t uid_shift, uid_t uid_range, DissectImageFlags flags);
|
int dissected_image_mount_and_warn(DissectedImage *m, const char *where, uid_t uid_shift, DissectImageFlags flags);
|
||||||
|
|
||||||
int dissected_image_acquire_metadata(DissectedImage *m);
|
int dissected_image_acquire_metadata(DissectedImage *m);
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <linux/loop.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/statvfs.h>
|
#include <sys/statvfs.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <linux/loop.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "dissect-image.h"
|
#include "dissect-image.h"
|
||||||
@ -17,7 +16,6 @@
|
|||||||
#include "fs-util.h"
|
#include "fs-util.h"
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
#include "libmount-util.h"
|
#include "libmount-util.h"
|
||||||
#include "missing_syscall.h"
|
|
||||||
#include "mkdir.h"
|
#include "mkdir.h"
|
||||||
#include "mount-util.h"
|
#include "mount-util.h"
|
||||||
#include "mountpoint-util.h"
|
#include "mountpoint-util.h"
|
||||||
@ -988,104 +986,3 @@ int mount_image_in_namespace(
|
|||||||
|
|
||||||
return mount_in_namespace(target, propagate_path, incoming_path, src, dest, read_only, make_file_or_directory, options, true);
|
return mount_in_namespace(target, propagate_path, incoming_path, src, dest, read_only, make_file_or_directory, options, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
int make_mount_point(const char *path) {
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(path);
|
|
||||||
|
|
||||||
/* If 'path' is already a mount point, does nothing and returns 0. If it is not it makes it one, and returns 1. */
|
|
||||||
|
|
||||||
r = path_is_mount_point(path, NULL, 0);
|
|
||||||
if (r < 0)
|
|
||||||
return log_debug_errno(r, "Failed to determine whether '%s' is a mount point: %m", path);
|
|
||||||
if (r > 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
r = mount_nofollow_verbose(LOG_DEBUG, path, path, NULL, MS_BIND|MS_REC, NULL);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int make_userns(uid_t uid_shift, uid_t uid_range) {
|
|
||||||
char uid_map[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(uid_t) + 1], line[DECIMAL_STR_MAX(uid_t)*3+3+1];
|
|
||||||
_cleanup_(sigkill_waitp) pid_t pid = 0;
|
|
||||||
_cleanup_close_ int userns_fd = -1;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
/* Allocates a userns file descriptor with the mapping we need. For this we'll fork off a child
|
|
||||||
* process whose only purpose is to give us a new user namespace. It's killed when we got it. */
|
|
||||||
|
|
||||||
r = safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_NEW_USERNS, &pid);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
if (r == 0) {
|
|
||||||
/* Child. We do nothing here, just freeze until somebody kills us. */
|
|
||||||
freeze();
|
|
||||||
_exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
xsprintf(line, UID_FMT " " UID_FMT " " UID_FMT "\n", 0, uid_shift, uid_range);
|
|
||||||
|
|
||||||
xsprintf(uid_map, "/proc/" PID_FMT "/uid_map", pid);
|
|
||||||
r = write_string_file(uid_map, line, WRITE_STRING_FILE_DISABLE_BUFFER);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to write UID map: %m");
|
|
||||||
|
|
||||||
/* We always assign the same UID and GID ranges */
|
|
||||||
xsprintf(uid_map, "/proc/" PID_FMT "/gid_map", pid);
|
|
||||||
r = write_string_file(uid_map, line, WRITE_STRING_FILE_DISABLE_BUFFER);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to write GID map: %m");
|
|
||||||
|
|
||||||
r = namespace_open(pid, NULL, NULL, NULL, &userns_fd, NULL);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
return TAKE_FD(userns_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
int remount_idmap(
|
|
||||||
const char *p,
|
|
||||||
uid_t uid_shift,
|
|
||||||
uid_t uid_range) {
|
|
||||||
|
|
||||||
_cleanup_close_ int mount_fd = -1, userns_fd = -1;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(p);
|
|
||||||
|
|
||||||
if (!userns_shift_range_valid(uid_shift, uid_range))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
/* Clone the mount point */
|
|
||||||
mount_fd = open_tree(-1, p, OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
|
|
||||||
if (mount_fd < 0)
|
|
||||||
return log_debug_errno(errno, "Failed to open tree of mounted filesystem '%s': %m", p);
|
|
||||||
|
|
||||||
/* Create a user namespace mapping */
|
|
||||||
userns_fd = make_userns(uid_shift, uid_range);
|
|
||||||
if (userns_fd < 0)
|
|
||||||
return userns_fd;
|
|
||||||
|
|
||||||
/* Set the user namespace mapping attribute on the cloned mount point */
|
|
||||||
if (mount_setattr(mount_fd, "", AT_EMPTY_PATH | AT_RECURSIVE,
|
|
||||||
&(struct mount_attr) {
|
|
||||||
.attr_set = MOUNT_ATTR_IDMAP,
|
|
||||||
.userns_fd = userns_fd,
|
|
||||||
}, sizeof(struct mount_attr)) < 0)
|
|
||||||
return log_debug_errno(errno, "Failed to change bind mount attributes for '%s': %m", p);
|
|
||||||
|
|
||||||
/* Remove the old mount point */
|
|
||||||
r = umount_verbose(LOG_DEBUG, p, UMOUNT_NOFOLLOW);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
/* And place the cloned version in its place */
|
|
||||||
if (move_mount(mount_fd, "", -1, p, MOVE_MOUNT_F_EMPTY_PATH) < 0)
|
|
||||||
return log_debug_errno(errno, "Failed to attach UID mapped mount to '%s': %m", p);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -103,7 +103,3 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(char*, umount_and_rmdir_and_free);
|
|||||||
|
|
||||||
int bind_mount_in_namespace(pid_t target, const char *propagate_path, const char *incoming_path, const char *src, const char *dest, bool read_only, bool make_file_or_directory);
|
int bind_mount_in_namespace(pid_t target, const char *propagate_path, const char *incoming_path, const char *src, const char *dest, bool read_only, bool make_file_or_directory);
|
||||||
int mount_image_in_namespace(pid_t target, const char *propagate_path, const char *incoming_path, const char *src, const char *dest, bool read_only, bool make_file_or_directory, const MountOptions *options);
|
int mount_image_in_namespace(pid_t target, const char *propagate_path, const char *incoming_path, const char *src, const char *dest, bool read_only, bool make_file_or_directory, const MountOptions *options);
|
||||||
|
|
||||||
int make_mount_point(const char *path);
|
|
||||||
|
|
||||||
int remount_idmap(const char *p, uid_t uid_shift, uid_t uid_range);
|
|
||||||
|
|||||||
@ -402,9 +402,6 @@ static int userdb_start_query(
|
|||||||
assert(iterator);
|
assert(iterator);
|
||||||
assert(method);
|
assert(method);
|
||||||
|
|
||||||
if (FLAGS_SET(flags, USERDB_EXCLUDE_VARLINK))
|
|
||||||
return -ENOLINK;
|
|
||||||
|
|
||||||
e = getenv("SYSTEMD_BYPASS_USERDB");
|
e = getenv("SYSTEMD_BYPASS_USERDB");
|
||||||
if (e) {
|
if (e) {
|
||||||
r = parse_boolean(e);
|
r = parse_boolean(e);
|
||||||
@ -425,7 +422,7 @@ static int userdb_start_query(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* First, let's talk to the multiplexer, if we can */
|
/* First, let's talk to the multiplexer, if we can */
|
||||||
if ((flags & (USERDB_AVOID_MULTIPLEXER|USERDB_EXCLUDE_DYNAMIC_USER|USERDB_EXCLUDE_NSS|USERDB_DONT_SYNTHESIZE)) == 0 &&
|
if ((flags & (USERDB_AVOID_MULTIPLEXER|USERDB_AVOID_DYNAMIC_USER|USERDB_AVOID_NSS|USERDB_DONT_SYNTHESIZE)) == 0 &&
|
||||||
!strv_contains(except, "io.systemd.Multiplexer") &&
|
!strv_contains(except, "io.systemd.Multiplexer") &&
|
||||||
(!only || strv_contains(only, "io.systemd.Multiplexer"))) {
|
(!only || strv_contains(only, "io.systemd.Multiplexer"))) {
|
||||||
_cleanup_(json_variant_unrefp) JsonVariant *patched_query = json_variant_ref(query);
|
_cleanup_(json_variant_unrefp) JsonVariant *patched_query = json_variant_ref(query);
|
||||||
@ -457,7 +454,7 @@ static int userdb_start_query(
|
|||||||
if (streq(de->d_name, "io.systemd.Multiplexer")) /* We already tried this above, don't try this again */
|
if (streq(de->d_name, "io.systemd.Multiplexer")) /* We already tried this above, don't try this again */
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (FLAGS_SET(flags, USERDB_EXCLUDE_DYNAMIC_USER) &&
|
if (FLAGS_SET(flags, USERDB_AVOID_DYNAMIC_USER) &&
|
||||||
streq(de->d_name, "io.systemd.DynamicUser"))
|
streq(de->d_name, "io.systemd.DynamicUser"))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -466,7 +463,7 @@ static int userdb_start_query(
|
|||||||
* (and when we run as part of systemd-userdbd.service we don't want to talk to ourselves
|
* (and when we run as part of systemd-userdbd.service we don't want to talk to ourselves
|
||||||
* anyway). */
|
* anyway). */
|
||||||
is_nss = streq(de->d_name, "io.systemd.NameServiceSwitch");
|
is_nss = streq(de->d_name, "io.systemd.NameServiceSwitch");
|
||||||
if ((flags & (USERDB_EXCLUDE_NSS|USERDB_AVOID_MULTIPLEXER)) && is_nss)
|
if ((flags & (USERDB_AVOID_NSS|USERDB_AVOID_MULTIPLEXER)) && is_nss)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (strv_contains(except, de->d_name))
|
if (strv_contains(except, de->d_name))
|
||||||
@ -624,13 +621,13 @@ int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && !iterator->nss_covered) {
|
if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !iterator->nss_covered) {
|
||||||
/* Make sure the NSS lookup doesn't recurse back to us. */
|
/* Make sure the NSS lookup doesn't recurse back to us. */
|
||||||
|
|
||||||
r = userdb_iterator_block_nss_systemd(iterator);
|
r = userdb_iterator_block_nss_systemd(iterator);
|
||||||
if (r >= 0) {
|
if (r >= 0) {
|
||||||
/* Client-side NSS fallback */
|
/* Client-side NSS fallback */
|
||||||
r = nss_user_record_by_name(name, !FLAGS_SET(flags, USERDB_SUPPRESS_SHADOW), ret);
|
r = nss_user_record_by_name(name, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret);
|
||||||
if (r >= 0)
|
if (r >= 0)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@ -671,11 +668,11 @@ int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && !iterator->nss_covered) {
|
if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !iterator->nss_covered) {
|
||||||
r = userdb_iterator_block_nss_systemd(iterator);
|
r = userdb_iterator_block_nss_systemd(iterator);
|
||||||
if (r >= 0) {
|
if (r >= 0) {
|
||||||
/* Client-side NSS fallback */
|
/* Client-side NSS fallback */
|
||||||
r = nss_user_record_by_uid(uid, !FLAGS_SET(flags, USERDB_SUPPRESS_SHADOW), ret);
|
r = nss_user_record_by_uid(uid, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret);
|
||||||
if (r >= 0)
|
if (r >= 0)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@ -706,7 +703,7 @@ int userdb_all(UserDBFlags flags, UserDBIterator **ret) {
|
|||||||
|
|
||||||
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", true, NULL, flags);
|
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", true, NULL, flags);
|
||||||
|
|
||||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (r < 0 || !iterator->nss_covered)) {
|
if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && (r < 0 || !iterator->nss_covered)) {
|
||||||
r = userdb_iterator_block_nss_systemd(iterator);
|
r = userdb_iterator_block_nss_systemd(iterator);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
@ -743,7 +740,7 @@ int userdb_iterator_get(UserDBIterator *iterator, UserRecord **ret) {
|
|||||||
if (pw->pw_uid == UID_NOBODY)
|
if (pw->pw_uid == UID_NOBODY)
|
||||||
iterator->synthesize_nobody = false;
|
iterator->synthesize_nobody = false;
|
||||||
|
|
||||||
if (!FLAGS_SET(iterator->flags, USERDB_SUPPRESS_SHADOW)) {
|
if (!FLAGS_SET(iterator->flags, USERDB_AVOID_SHADOW)) {
|
||||||
r = nss_spwd_for_passwd(pw, &spwd, &buffer);
|
r = nss_spwd_for_passwd(pw, &spwd, &buffer);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_debug_errno(r, "Failed to acquire shadow entry for user %s, ignoring: %m", pw->pw_name);
|
log_debug_errno(r, "Failed to acquire shadow entry for user %s, ignoring: %m", pw->pw_name);
|
||||||
@ -835,10 +832,10 @@ int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && !(iterator && iterator->nss_covered)) {
|
if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !(iterator && iterator->nss_covered)) {
|
||||||
r = userdb_iterator_block_nss_systemd(iterator);
|
r = userdb_iterator_block_nss_systemd(iterator);
|
||||||
if (r >= 0) {
|
if (r >= 0) {
|
||||||
r = nss_group_record_by_name(name, !FLAGS_SET(flags, USERDB_SUPPRESS_SHADOW), ret);
|
r = nss_group_record_by_name(name, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret);
|
||||||
if (r >= 0)
|
if (r >= 0)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@ -879,10 +876,10 @@ int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && !(iterator && iterator->nss_covered)) {
|
if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !(iterator && iterator->nss_covered)) {
|
||||||
r = userdb_iterator_block_nss_systemd(iterator);
|
r = userdb_iterator_block_nss_systemd(iterator);
|
||||||
if (r >= 0) {
|
if (r >= 0) {
|
||||||
r = nss_group_record_by_gid(gid, !FLAGS_SET(flags, USERDB_SUPPRESS_SHADOW), ret);
|
r = nss_group_record_by_gid(gid, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret);
|
||||||
if (r >= 0)
|
if (r >= 0)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@ -913,7 +910,7 @@ int groupdb_all(UserDBFlags flags, UserDBIterator **ret) {
|
|||||||
|
|
||||||
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", true, NULL, flags);
|
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", true, NULL, flags);
|
||||||
|
|
||||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (r < 0 || !iterator->nss_covered)) {
|
if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && (r < 0 || !iterator->nss_covered)) {
|
||||||
r = userdb_iterator_block_nss_systemd(iterator);
|
r = userdb_iterator_block_nss_systemd(iterator);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
@ -948,7 +945,7 @@ int groupdb_iterator_get(UserDBIterator *iterator, GroupRecord **ret) {
|
|||||||
if (gr->gr_gid == GID_NOBODY)
|
if (gr->gr_gid == GID_NOBODY)
|
||||||
iterator->synthesize_nobody = false;
|
iterator->synthesize_nobody = false;
|
||||||
|
|
||||||
if (!FLAGS_SET(iterator->flags, USERDB_SUPPRESS_SHADOW)) {
|
if (!FLAGS_SET(iterator->flags, USERDB_AVOID_SHADOW)) {
|
||||||
r = nss_sgrp_for_group(gr, &sgrp, &buffer);
|
r = nss_sgrp_for_group(gr, &sgrp, &buffer);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_debug_errno(r, "Failed to acquire shadow entry for group %s, ignoring: %m", gr->gr_name);
|
log_debug_errno(r, "Failed to acquire shadow entry for group %s, ignoring: %m", gr->gr_name);
|
||||||
@ -1019,7 +1016,7 @@ int membershipdb_by_user(const char *name, UserDBFlags flags, UserDBIterator **r
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, query, flags);
|
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, query, flags);
|
||||||
if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_EXCLUDE_NSS))
|
if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_AVOID_NSS))
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|
||||||
r = userdb_iterator_block_nss_systemd(iterator);
|
r = userdb_iterator_block_nss_systemd(iterator);
|
||||||
@ -1062,7 +1059,7 @@ int membershipdb_by_group(const char *name, UserDBFlags flags, UserDBIterator **
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, query, flags);
|
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, query, flags);
|
||||||
if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_EXCLUDE_NSS))
|
if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_AVOID_NSS))
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|
||||||
r = userdb_iterator_block_nss_systemd(iterator);
|
r = userdb_iterator_block_nss_systemd(iterator);
|
||||||
@ -1103,7 +1100,7 @@ int membershipdb_all(UserDBFlags flags, UserDBIterator **ret) {
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, NULL, flags);
|
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, NULL, flags);
|
||||||
if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_EXCLUDE_NSS))
|
if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_AVOID_NSS))
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|
||||||
r = userdb_iterator_block_nss_systemd(iterator);
|
r = userdb_iterator_block_nss_systemd(iterator);
|
||||||
|
|||||||
@ -15,18 +15,11 @@ UserDBIterator *userdb_iterator_free(UserDBIterator *iterator);
|
|||||||
DEFINE_TRIVIAL_CLEANUP_FUNC(UserDBIterator*, userdb_iterator_free);
|
DEFINE_TRIVIAL_CLEANUP_FUNC(UserDBIterator*, userdb_iterator_free);
|
||||||
|
|
||||||
typedef enum UserDBFlags {
|
typedef enum UserDBFlags {
|
||||||
/* The main sources */
|
USERDB_AVOID_NSS = 1 << 0, /* don't do client-side nor server-side NSS */
|
||||||
USERDB_EXCLUDE_NSS = 1 << 0, /* don't do client-side nor server-side NSS */
|
USERDB_AVOID_SHADOW = 1 << 1, /* don't do client-side shadow calls (server side might happen though) */
|
||||||
USERDB_EXCLUDE_VARLINK = 1 << 1, /* don't talk to any varlink services */
|
USERDB_AVOID_DYNAMIC_USER = 1 << 2, /* exclude looking up in io.systemd.DynamicUser */
|
||||||
|
USERDB_AVOID_MULTIPLEXER = 1 << 3, /* exclude looking up via io.systemd.Multiplexer */
|
||||||
/* Modifications */
|
USERDB_DONT_SYNTHESIZE = 1 << 4, /* 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 = 1 << 6, /* don't synthesize root/nobody */
|
|
||||||
|
|
||||||
/* Combinations */
|
|
||||||
USERDB_NSS_ONLY = USERDB_EXCLUDE_VARLINK|USERDB_DONT_SYNTHESIZE,
|
|
||||||
} UserDBFlags;
|
} UserDBFlags;
|
||||||
|
|
||||||
int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret);
|
int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret);
|
||||||
|
|||||||
@ -551,7 +551,6 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
|
|||||||
m,
|
m,
|
||||||
p,
|
p,
|
||||||
UID_INVALID,
|
UID_INVALID,
|
||||||
UID_INVALID,
|
|
||||||
flags);
|
flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|||||||
@ -77,7 +77,7 @@ static void* thread_func(void *ptr) {
|
|||||||
assert_se(dissected->partitions[PARTITION_HOME].found);
|
assert_se(dissected->partitions[PARTITION_HOME].found);
|
||||||
assert_se(dissected->partitions[PARTITION_HOME].node);
|
assert_se(dissected->partitions[PARTITION_HOME].node);
|
||||||
|
|
||||||
r = dissected_image_mount(dissected, mounted, UID_INVALID, UID_INVALID, DISSECT_IMAGE_READ_ONLY);
|
r = dissected_image_mount(dissected, mounted, UID_INVALID, DISSECT_IMAGE_READ_ONLY);
|
||||||
log_notice_errno(r, "Mounted %s → %s: %m", loop->node, mounted);
|
log_notice_errno(r, "Mounted %s → %s: %m", loop->node, mounted);
|
||||||
assert_se(r >= 0);
|
assert_se(r >= 0);
|
||||||
|
|
||||||
@ -217,7 +217,7 @@ int main(int argc, char *argv[]) {
|
|||||||
assert_se(mkdtemp_malloc(NULL, &mounted) >= 0);
|
assert_se(mkdtemp_malloc(NULL, &mounted) >= 0);
|
||||||
|
|
||||||
/* This first (writable) mount will initialize the mount point dirs, so that the subsequent read-only ones can work */
|
/* This first (writable) mount will initialize the mount point dirs, so that the subsequent read-only ones can work */
|
||||||
assert_se(dissected_image_mount(dissected, mounted, UID_INVALID, UID_INVALID, 0) >= 0);
|
assert_se(dissected_image_mount(dissected, mounted, UID_INVALID, 0) >= 0);
|
||||||
|
|
||||||
assert_se(umount_recursive(mounted, 0) >= 0);
|
assert_se(umount_recursive(mounted, 0) >= 0);
|
||||||
loop = loop_device_unref(loop);
|
loop = loop_device_unref(loop);
|
||||||
|
|||||||
@ -717,7 +717,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'N':
|
case 'N':
|
||||||
arg_userdb_flags |= USERDB_EXCLUDE_NSS|USERDB_DONT_SYNTHESIZE;
|
arg_userdb_flags |= USERDB_AVOID_NSS|USERDB_DONT_SYNTHESIZE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ARG_WITH_NSS:
|
case ARG_WITH_NSS:
|
||||||
@ -725,7 +725,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
SET_FLAG(arg_userdb_flags, USERDB_EXCLUDE_NSS, !r);
|
SET_FLAG(arg_userdb_flags, USERDB_AVOID_NSS, !r);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ARG_SYNTHESIZE:
|
case ARG_SYNTHESIZE:
|
||||||
|
|||||||
@ -113,21 +113,6 @@ static int build_user_json(Varlink *link, UserRecord *ur, JsonVariant **ret) {
|
|||||||
JSON_BUILD_PAIR("incomplete", JSON_BUILD_BOOLEAN(stripped->incomplete))));
|
JSON_BUILD_PAIR("incomplete", JSON_BUILD_BOOLEAN(stripped->incomplete))));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int userdb_flags_from_service(Varlink *link, const char *service, UserDBFlags *ret) {
|
|
||||||
assert(link);
|
|
||||||
assert(service);
|
|
||||||
assert(ret);
|
|
||||||
|
|
||||||
if (streq_ptr(service, "io.systemd.NameServiceSwitch"))
|
|
||||||
*ret = USERDB_NSS_ONLY|USERDB_AVOID_MULTIPLEXER;
|
|
||||||
else if (streq_ptr(service, "io.systemd.Multiplexer"))
|
|
||||||
*ret = USERDB_AVOID_MULTIPLEXER;
|
|
||||||
else
|
|
||||||
return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
|
static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
|
||||||
|
|
||||||
static const JsonDispatch dispatch_table[] = {
|
static const JsonDispatch dispatch_table[] = {
|
||||||
@ -142,7 +127,6 @@ static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, Var
|
|||||||
LookupParameters p = {
|
LookupParameters p = {
|
||||||
.uid = UID_INVALID,
|
.uid = UID_INVALID,
|
||||||
};
|
};
|
||||||
UserDBFlags userdb_flags;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(parameters);
|
assert(parameters);
|
||||||
@ -151,19 +135,77 @@ static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, Var
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = userdb_flags_from_service(link, p.service, &userdb_flags);
|
if (streq_ptr(p.service, "io.systemd.NameServiceSwitch")) {
|
||||||
|
if (uid_is_valid(p.uid))
|
||||||
|
r = nss_user_record_by_uid(p.uid, true, &hr);
|
||||||
|
else if (p.user_name)
|
||||||
|
r = nss_user_record_by_name(p.user_name, true, &hr);
|
||||||
|
else {
|
||||||
|
_cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
|
||||||
|
|
||||||
|
setpwent();
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
_cleanup_(user_record_unrefp) UserRecord *z = NULL;
|
||||||
|
_cleanup_free_ char *sbuf = NULL;
|
||||||
|
struct passwd *pw;
|
||||||
|
struct spwd spwd;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
pw = getpwent();
|
||||||
|
if (!pw) {
|
||||||
|
if (errno != 0)
|
||||||
|
log_debug_errno(errno, "Failure while iterating through NSS user database, ignoring: %m");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = nss_spwd_for_passwd(pw, &spwd, &sbuf);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
log_debug_errno(r, "Failed to acquire shadow entry for user %s, ignoring: %m", pw->pw_name);
|
||||||
|
|
||||||
|
r = nss_passwd_to_user_record(pw, NULL, &z);
|
||||||
|
if (r < 0) {
|
||||||
|
endpwent();
|
||||||
return r;
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last) {
|
||||||
|
r = varlink_notify(link, last);
|
||||||
|
if (r < 0) {
|
||||||
|
endpwent();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = json_variant_unref(last);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = build_user_json(link, z, &last);
|
||||||
|
if (r < 0) {
|
||||||
|
endpwent();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
endpwent();
|
||||||
|
|
||||||
|
if (!last)
|
||||||
|
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
|
||||||
|
|
||||||
|
return varlink_reply(link, last);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (streq_ptr(p.service, "io.systemd.Multiplexer")) {
|
||||||
|
|
||||||
if (uid_is_valid(p.uid))
|
if (uid_is_valid(p.uid))
|
||||||
r = userdb_by_uid(p.uid, userdb_flags, &hr);
|
r = userdb_by_uid(p.uid, USERDB_AVOID_MULTIPLEXER, &hr);
|
||||||
else if (p.user_name)
|
else if (p.user_name)
|
||||||
r = userdb_by_name(p.user_name, userdb_flags, &hr);
|
r = userdb_by_name(p.user_name, USERDB_AVOID_MULTIPLEXER, &hr);
|
||||||
else {
|
else {
|
||||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||||
_cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
|
_cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
|
||||||
|
|
||||||
r = userdb_all(userdb_flags, &iterator);
|
r = userdb_all(USERDB_AVOID_MULTIPLEXER, &iterator);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -194,6 +236,8 @@ static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, Var
|
|||||||
|
|
||||||
return varlink_reply(link, last);
|
return varlink_reply(link, last);
|
||||||
}
|
}
|
||||||
|
} else
|
||||||
|
return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
|
||||||
if (r == -ESRCH)
|
if (r == -ESRCH)
|
||||||
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
|
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
@ -269,7 +313,6 @@ static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, Va
|
|||||||
LookupParameters p = {
|
LookupParameters p = {
|
||||||
.gid = GID_INVALID,
|
.gid = GID_INVALID,
|
||||||
};
|
};
|
||||||
UserDBFlags userdb_flags;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(parameters);
|
assert(parameters);
|
||||||
@ -278,19 +321,78 @@ static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, Va
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = userdb_flags_from_service(link, p.service, &userdb_flags);
|
if (streq_ptr(p.service, "io.systemd.NameServiceSwitch")) {
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if (gid_is_valid(p.gid))
|
if (gid_is_valid(p.gid))
|
||||||
r = groupdb_by_gid(p.gid, userdb_flags, &g);
|
r = nss_group_record_by_gid(p.gid, true, &g);
|
||||||
else if (p.group_name)
|
else if (p.group_name)
|
||||||
r = groupdb_by_name(p.group_name, userdb_flags, &g);
|
r = nss_group_record_by_name(p.group_name, true, &g);
|
||||||
|
else {
|
||||||
|
_cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
|
||||||
|
|
||||||
|
setgrent();
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
_cleanup_(group_record_unrefp) GroupRecord *z = NULL;
|
||||||
|
_cleanup_free_ char *sbuf = NULL;
|
||||||
|
struct group *grp;
|
||||||
|
struct sgrp sgrp;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
grp = getgrent();
|
||||||
|
if (!grp) {
|
||||||
|
if (errno != 0)
|
||||||
|
log_debug_errno(errno, "Failure while iterating through NSS group database, ignoring: %m");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = nss_sgrp_for_group(grp, &sgrp, &sbuf);
|
||||||
|
if (r < 0)
|
||||||
|
log_debug_errno(r, "Failed to acquire shadow entry for group %s, ignoring: %m", grp->gr_name);
|
||||||
|
|
||||||
|
r = nss_group_to_group_record(grp, r >= 0 ? &sgrp : NULL, &z);
|
||||||
|
if (r < 0) {
|
||||||
|
endgrent();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last) {
|
||||||
|
r = varlink_notify(link, last);
|
||||||
|
if (r < 0) {
|
||||||
|
endgrent();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = json_variant_unref(last);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = build_group_json(link, z, &last);
|
||||||
|
if (r < 0) {
|
||||||
|
endgrent();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
endgrent();
|
||||||
|
|
||||||
|
if (!last)
|
||||||
|
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
|
||||||
|
|
||||||
|
return varlink_reply(link, last);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (streq_ptr(p.service, "io.systemd.Multiplexer")) {
|
||||||
|
|
||||||
|
if (gid_is_valid(p.gid))
|
||||||
|
r = groupdb_by_gid(p.gid, USERDB_AVOID_MULTIPLEXER, &g);
|
||||||
|
else if (p.group_name)
|
||||||
|
r = groupdb_by_name(p.group_name, USERDB_AVOID_MULTIPLEXER, &g);
|
||||||
else {
|
else {
|
||||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||||
_cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
|
_cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
|
||||||
|
|
||||||
r = groupdb_all(userdb_flags, &iterator);
|
r = groupdb_all(USERDB_AVOID_MULTIPLEXER, &iterator);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -321,6 +423,8 @@ static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, Va
|
|||||||
|
|
||||||
return varlink_reply(link, last);
|
return varlink_reply(link, last);
|
||||||
}
|
}
|
||||||
|
} else
|
||||||
|
return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
|
||||||
if (r == -ESRCH)
|
if (r == -ESRCH)
|
||||||
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
|
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
@ -347,10 +451,7 @@ static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, Var
|
|||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
_cleanup_free_ char *last_user_name = NULL, *last_group_name = NULL;
|
|
||||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
|
||||||
LookupParameters p = {};
|
LookupParameters p = {};
|
||||||
UserDBFlags userdb_flags;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(parameters);
|
assert(parameters);
|
||||||
@ -359,16 +460,121 @@ static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, Var
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = userdb_flags_from_service(link, p.service, &userdb_flags);
|
if (streq_ptr(p.service, "io.systemd.NameServiceSwitch")) {
|
||||||
|
|
||||||
|
if (p.group_name) {
|
||||||
|
_cleanup_(group_record_unrefp) GroupRecord *g = NULL;
|
||||||
|
const char *last = NULL;
|
||||||
|
char **i;
|
||||||
|
|
||||||
|
r = nss_group_record_by_name(p.group_name, true, &g);
|
||||||
|
if (r == -ESRCH)
|
||||||
|
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
STRV_FOREACH(i, g->members) {
|
||||||
|
|
||||||
|
if (p.user_name && !streq_ptr(p.user_name, *i))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (last) {
|
||||||
|
r = varlink_notifyb(link, JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last)),
|
||||||
|
JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(g->group_name))));
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = *i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!last)
|
||||||
|
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
|
||||||
|
|
||||||
|
return varlink_replyb(link, JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last)),
|
||||||
|
JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(g->group_name))));
|
||||||
|
} else {
|
||||||
|
_cleanup_free_ char *last_user_name = NULL, *last_group_name = NULL;
|
||||||
|
|
||||||
|
setgrent();
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
struct group *grp;
|
||||||
|
const char* two[2], **users, **i;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
grp = getgrent();
|
||||||
|
if (!grp) {
|
||||||
|
if (errno != 0)
|
||||||
|
log_debug_errno(errno, "Failure while iterating through NSS group database, ignoring: %m");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.user_name) {
|
||||||
|
if (!strv_contains(grp->gr_mem, p.user_name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
two[0] = p.user_name;
|
||||||
|
two[1] = NULL;
|
||||||
|
|
||||||
|
users = two;
|
||||||
|
} else
|
||||||
|
users = (const char**) grp->gr_mem;
|
||||||
|
|
||||||
|
STRV_FOREACH(i, users) {
|
||||||
|
|
||||||
|
if (last_user_name) {
|
||||||
|
assert(last_group_name);
|
||||||
|
|
||||||
|
r = varlink_notifyb(link, JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
|
||||||
|
JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
|
||||||
|
if (r < 0) {
|
||||||
|
endgrent();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(last_user_name);
|
||||||
|
free(last_group_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
last_user_name = strdup(*i);
|
||||||
|
last_group_name = strdup(grp->gr_name);
|
||||||
|
if (!last_user_name || !last_group_name) {
|
||||||
|
endgrent();
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
endgrent();
|
||||||
|
|
||||||
|
if (!last_user_name) {
|
||||||
|
assert(!last_group_name);
|
||||||
|
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(last_group_name);
|
||||||
|
|
||||||
|
return varlink_replyb(link, JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
|
||||||
|
JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (streq_ptr(p.service, "io.systemd.Multiplexer")) {
|
||||||
|
|
||||||
|
_cleanup_free_ char *last_user_name = NULL, *last_group_name = NULL;
|
||||||
|
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||||
|
|
||||||
if (p.group_name)
|
if (p.group_name)
|
||||||
r = membershipdb_by_group(p.group_name, userdb_flags, &iterator);
|
r = membershipdb_by_group(p.group_name, USERDB_AVOID_MULTIPLEXER, &iterator);
|
||||||
else if (p.user_name)
|
else if (p.user_name)
|
||||||
r = membershipdb_by_user(p.user_name, userdb_flags, &iterator);
|
r = membershipdb_by_user(p.user_name, USERDB_AVOID_MULTIPLEXER, &iterator);
|
||||||
else
|
else
|
||||||
r = membershipdb_all(userdb_flags, &iterator);
|
r = membershipdb_all(USERDB_AVOID_MULTIPLEXER, &iterator);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -414,6 +620,9 @@ static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, Var
|
|||||||
JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
|
JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static int process_connection(VarlinkServer *server, int fd) {
|
static int process_connection(VarlinkServer *server, int fd) {
|
||||||
_cleanup_(varlink_close_unrefp) Varlink *vl = NULL;
|
_cleanup_(varlink_close_unrefp) Varlink *vl = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|||||||
@ -44,10 +44,10 @@ systemd-repart --definitions=/tmp/testsuite-58-defs/ \
|
|||||||
--seed=750b6cd5c4ae4012a15e7be3c29e6a47 \
|
--seed=750b6cd5c4ae4012a15e7be3c29e6a47 \
|
||||||
/var/tmp/testsuite-58.img
|
/var/tmp/testsuite-58.img
|
||||||
|
|
||||||
sfdisk --dump /var/tmp/testsuite-58.img | tee /tmp/testsuite-58.dump
|
sfdisk --dump /var/tmp/testsuite-58.img >/tmp/testsuite-58.dump
|
||||||
|
|
||||||
grep -qxF '/var/tmp/testsuite-58.img1 : start= 2048, size= 20480, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=39107B09-615D-48FB-BA37-C663885FCE67, name="esp"' /tmp/testsuite-58.dump
|
grep -qxF '/var/tmp/testsuite-58.img1 : start= 2048, size= 20480, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, uuid=39107B09-615D-48FB-BA37-C663885FCE67, name="esp"' /tmp/testsuite-58.dump
|
||||||
grep -qxF '/var/tmp/testsuite-58.img2 : start= 22528, size= 20480, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=60F33797-1D71-4DCB-AA6F-20564F036CD0, name="root-x86-64", attrs="GUID:59"' /tmp/testsuite-58.dump
|
grep -qxF '/var/tmp/testsuite-58.img2 : start= 22528, size= 20480, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=60F33797-1D71-4DCB-AA6F-20564F036CD0, name="root-x86-64"' /tmp/testsuite-58.dump
|
||||||
grep -qxF '/var/tmp/testsuite-58.img3 : start= 43008, size= 20480, type=8484680C-9521-48C6-9C11-B0720656F69E, uuid=7E3369DD-D653-4513-ADF5-B993A9F20C16, name="usr-x86-64", attrs="GUID:60"' /tmp/testsuite-58.dump
|
grep -qxF '/var/tmp/testsuite-58.img3 : start= 43008, size= 20480, type=8484680C-9521-48C6-9C11-B0720656F69E, uuid=7E3369DD-D653-4513-ADF5-B993A9F20C16, name="usr-x86-64", attrs="GUID:60"' /tmp/testsuite-58.dump
|
||||||
|
|
||||||
# Second part, duplicate it with CopyBlocks=auto
|
# Second part, duplicate it with CopyBlocks=auto
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user