Compare commits
9 Commits
f5ba69327f
...
09fcfa9ad1
Author | SHA1 | Date |
---|---|---|
anonymix007 | 09fcfa9ad1 | |
Yu Watanabe | 52b0351a15 | |
Luca Boccassi | fe077a1a58 | |
Mike Yuan | b718b86e1b | |
Mike Yuan | d911778877 | |
Mike Yuan | eea9d3eb10 | |
Mike Yuan | 579ce77ead | |
anonymix007 | 18639361da | |
anonymix007 | 07879e662b |
|
@ -71,6 +71,8 @@
|
|||
<varname>Cmdline=</varname>/<option>--cmdline=</option>,
|
||||
<varname>OSRelease=</varname>/<option>--os-release=</option>,
|
||||
<varname>DeviceTree=</varname>/<option>--devicetree=</option>,
|
||||
<varname>DeviceTreeAuto=</varname>/<option>--devicetree-auto=</option>,
|
||||
<varname>HWIDs=</varname>/<option>--hwids=</option>,
|
||||
<varname>Splash=</varname>/<option>--splash=</option>,
|
||||
<varname>PCRPKey=</varname>/<option>--pcrpkey=</option>,
|
||||
<varname>Uname=</varname>/<option>--uname=</option>,
|
||||
|
@ -373,6 +375,30 @@
|
|||
<xi:include href="version-info.xml" xpointer="v253"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>DeviceTreeAuto=<replaceable>PATH</replaceable>...</varname></term>
|
||||
<term><option>--devicetree-auto=<replaceable>PATH</replaceable></option></term>
|
||||
|
||||
<listitem><para>Zero or more automatically selectable DeviceTree files. In the configuration file, items are separated by
|
||||
whitespace. Each DeviceTree will be in a separate <literal>.dtbauto</literal> section.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>HWIDs=<replaceable>PATH</replaceable></varname></term>
|
||||
<term><option>--hwids=<replaceable>PATH</replaceable></option></term>
|
||||
|
||||
<listitem><para>The hardware ID device table (the <literal>.hwids</literal> section). The argument is a
|
||||
path to a directory with JSON HWID device description files. Each JSON file needs to contain <literal>name</literal>
|
||||
and <literal>compatible</literal> strings and also array of CHIDs/HWIDs as <literal>hwids</literal>.
|
||||
If not specified, the section will not be present. It is recommended to specify this parameter if automatically
|
||||
selectable DeviceTrees are to be used.
|
||||
</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Uname=<replaceable>VERSION</replaceable></varname></term>
|
||||
<term><option>--uname=<replaceable>VERSION</replaceable></option></term>
|
||||
|
|
|
@ -220,9 +220,9 @@ static int synthesize_user_creds(
|
|||
if (ret_gid)
|
||||
*ret_gid = GID_NOBODY;
|
||||
if (ret_home)
|
||||
*ret_home = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/";
|
||||
*ret_home = FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : "/";
|
||||
if (ret_shell)
|
||||
*ret_shell = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : NOLOGIN;
|
||||
*ret_shell = FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : NOLOGIN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -244,6 +244,7 @@ int get_user_creds(
|
|||
|
||||
assert(username);
|
||||
assert(*username);
|
||||
assert((ret_home || ret_shell) || !(flags & (USER_CREDS_SUPPRESS_PLACEHOLDER|USER_CREDS_CLEAN)));
|
||||
|
||||
if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
|
||||
(!ret_home && !ret_shell)) {
|
||||
|
@ -315,17 +316,14 @@ int get_user_creds(
|
|||
|
||||
if (ret_home)
|
||||
/* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
|
||||
*ret_home = (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
|
||||
(empty_or_root(p->pw_dir) ||
|
||||
!path_is_valid(p->pw_dir) ||
|
||||
!path_is_absolute(p->pw_dir))) ? NULL : p->pw_dir;
|
||||
*ret_home = (FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) && empty_or_root(p->pw_dir)) ||
|
||||
(FLAGS_SET(flags, USER_CREDS_CLEAN) && (!path_is_valid(p->pw_dir) || !path_is_absolute(p->pw_dir)))
|
||||
? NULL : p->pw_dir;
|
||||
|
||||
if (ret_shell)
|
||||
*ret_shell = (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
|
||||
(isempty(p->pw_shell) ||
|
||||
!path_is_valid(p->pw_shell) ||
|
||||
!path_is_absolute(p->pw_shell) ||
|
||||
is_nologin_shell(p->pw_shell))) ? NULL : p->pw_shell;
|
||||
*ret_shell = (FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) && shell_is_placeholder(p->pw_shell)) ||
|
||||
(FLAGS_SET(flags, USER_CREDS_CLEAN) && (!path_is_valid(p->pw_shell) || !path_is_absolute(p->pw_shell)))
|
||||
? NULL : p->pw_shell;
|
||||
|
||||
if (patch_username)
|
||||
*username = p->pw_name;
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "string-util.h"
|
||||
|
||||
/* Users managed by systemd-homed. See https://systemd.io/UIDS-GIDS for details how this range fits into the rest of the world */
|
||||
#define HOME_UID_MIN ((uid_t) 60001)
|
||||
#define HOME_UID_MAX ((uid_t) 60513)
|
||||
|
@ -36,10 +38,20 @@ static inline int parse_gid(const char *s, gid_t *ret_gid) {
|
|||
char* getlogname_malloc(void);
|
||||
char* getusername_malloc(void);
|
||||
|
||||
const char* default_root_shell_at(int rfd);
|
||||
const char* default_root_shell(const char *root);
|
||||
|
||||
bool is_nologin_shell(const char *shell);
|
||||
|
||||
static inline bool shell_is_placeholder(const char *shell) {
|
||||
return isempty(shell) || is_nologin_shell(shell);
|
||||
}
|
||||
|
||||
typedef enum UserCredsFlags {
|
||||
USER_CREDS_PREFER_NSS = 1 << 0, /* if set, only synthesize user records if database lacks them. Normally we bypass the userdb entirely for the records we can synthesize */
|
||||
USER_CREDS_ALLOW_MISSING = 1 << 1, /* if a numeric UID string is resolved, be OK if there's no record for it */
|
||||
USER_CREDS_CLEAN = 1 << 2, /* try to clean up shell and home fields with invalid data */
|
||||
USER_CREDS_PREFER_NSS = 1 << 0, /* if set, only synthesize user records if database lacks them. Normally we bypass the userdb entirely for the records we can synthesize */
|
||||
USER_CREDS_ALLOW_MISSING = 1 << 1, /* if a numeric UID string is resolved, be OK if there's no record for it */
|
||||
USER_CREDS_CLEAN = 1 << 2, /* try to clean up shell and home fields with invalid data */
|
||||
USER_CREDS_SUPPRESS_PLACEHOLDER = 1 << 3, /* suppress home and/or shell fields if value is placeholder (root/empty/nologin) */
|
||||
} UserCredsFlags;
|
||||
|
||||
int get_user_creds(const char **username, uid_t *ret_uid, gid_t *ret_gid, const char **ret_home, const char **ret_shell, UserCredsFlags flags);
|
||||
|
@ -125,10 +137,6 @@ int fgetsgent_sane(FILE *stream, struct sgrp **sg);
|
|||
int putsgent_sane(const struct sgrp *sg, FILE *stream);
|
||||
#endif
|
||||
|
||||
bool is_nologin_shell(const char *shell);
|
||||
const char* default_root_shell_at(int rfd);
|
||||
const char* default_root_shell(const char *root);
|
||||
|
||||
int is_this_me(const char *username);
|
||||
|
||||
const char* get_home_root(void);
|
||||
|
|
|
@ -855,9 +855,6 @@ static int get_fixed_user(
|
|||
assert(user_or_uid);
|
||||
assert(ret_username);
|
||||
|
||||
/* Note that we don't set $HOME or $SHELL if they are not particularly enlightening anyway
|
||||
* (i.e. are "/" or "/bin/nologin"). */
|
||||
|
||||
r = get_user_creds(&user_or_uid, ret_uid, ret_gid, ret_home, ret_shell, USER_CREDS_CLEAN);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -1883,7 +1880,10 @@ static int build_environment(
|
|||
}
|
||||
}
|
||||
|
||||
if (home && set_user_login_env) {
|
||||
/* Note that we don't set $HOME or $SHELL if they are not particularly enlightening anyway
|
||||
* (i.e. are "/" or "/bin/nologin"). */
|
||||
|
||||
if (home && set_user_login_env && !empty_or_root(home)) {
|
||||
x = strjoin("HOME=", home);
|
||||
if (!x)
|
||||
return -ENOMEM;
|
||||
|
@ -1892,7 +1892,7 @@ static int build_environment(
|
|||
our_env[n_env++] = x;
|
||||
}
|
||||
|
||||
if (shell && set_user_login_env) {
|
||||
if (shell && set_user_login_env && !shell_is_placeholder(shell)) {
|
||||
x = strjoin("SHELL=", shell);
|
||||
if (!x)
|
||||
return -ENOMEM;
|
||||
|
@ -3471,20 +3471,16 @@ static int apply_working_directory(
|
|||
const ExecContext *context,
|
||||
const ExecParameters *params,
|
||||
ExecRuntime *runtime,
|
||||
const char *home,
|
||||
int *exit_status) {
|
||||
const char *home) {
|
||||
|
||||
const char *wd;
|
||||
int r;
|
||||
|
||||
assert(context);
|
||||
assert(exit_status);
|
||||
|
||||
if (context->working_directory_home) {
|
||||
if (!home) {
|
||||
*exit_status = EXIT_CHDIR;
|
||||
if (!home)
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
wd = home;
|
||||
} else
|
||||
|
@ -3503,13 +3499,7 @@ static int apply_working_directory(
|
|||
if (r >= 0)
|
||||
r = RET_NERRNO(fchdir(dfd));
|
||||
}
|
||||
|
||||
if (r < 0 && !context->working_directory_missing_ok) {
|
||||
*exit_status = EXIT_CHDIR;
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return context->working_directory_missing_ok ? 0 : r;
|
||||
}
|
||||
|
||||
static int apply_root_directory(
|
||||
|
@ -3785,7 +3775,7 @@ static int acquire_home(const ExecContext *c, const char **home, char **ret_buf)
|
|||
if (!c->working_directory_home)
|
||||
return 0;
|
||||
|
||||
if (c->dynamic_user)
|
||||
if (c->dynamic_user || (c->user && is_this_me(c->user) <= 0))
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
r = get_home_dir(ret_buf);
|
||||
|
@ -4543,7 +4533,7 @@ int exec_invoke(
|
|||
r = acquire_home(context, &home, &home_buffer);
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_CHDIR;
|
||||
return log_exec_error_errno(context, params, r, "Failed to determine $HOME for user: %m");
|
||||
return log_exec_error_errno(context, params, r, "Failed to determine $HOME for the invoking user: %m");
|
||||
}
|
||||
|
||||
/* If a socket is connected to STDIN/STDOUT/STDERR, we must drop O_NONBLOCK */
|
||||
|
@ -5382,9 +5372,11 @@ int exec_invoke(
|
|||
* running this service might have the correct privilege to change to the working directory. Also, it
|
||||
* is absolutely 💣 crucial 💣 we applied all mount namespacing rearrangements before this, so that
|
||||
* the cwd cannot be used to pin directories outside of the sandbox. */
|
||||
r = apply_working_directory(context, params, runtime, home, exit_status);
|
||||
if (r < 0)
|
||||
r = apply_working_directory(context, params, runtime, home);
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_CHDIR;
|
||||
return log_exec_error_errno(context, params, r, "Changing to the requested working directory failed: %m");
|
||||
}
|
||||
|
||||
if (needs_sandboxing) {
|
||||
/* Apply other MAC contexts late, but before seccomp syscall filtering, as those should really be last to
|
||||
|
|
|
@ -2297,7 +2297,8 @@ static int start_transient_scope(sd_bus *bus) {
|
|||
uid_t uid;
|
||||
gid_t gid;
|
||||
|
||||
r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell, USER_CREDS_CLEAN|USER_CREDS_PREFER_NSS);
|
||||
r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell,
|
||||
USER_CREDS_CLEAN|USER_CREDS_SUPPRESS_PLACEHOLDER|USER_CREDS_PREFER_NSS);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user);
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ import subprocess
|
|||
import sys
|
||||
import tempfile
|
||||
import textwrap
|
||||
import uuid
|
||||
from collections.abc import Iterable, Iterator, Sequence
|
||||
from hashlib import sha256
|
||||
from pathlib import Path
|
||||
|
@ -1013,14 +1014,9 @@ def merge_sbat(input_pe: list[Path], input_text: list[str]) -> str:
|
|||
)
|
||||
|
||||
|
||||
# Keep in sync with EFI_GUID (src/boot/efi.h)
|
||||
# uint32_t Data1, uint16_t Data2, uint16_t Data3, uint8_t Data4[8]
|
||||
EFI_GUID = tuple[int, int, int, tuple[int, int, int, int, int, int, int, int]]
|
||||
EFI_GUID_STRUCT_SIZE = 4 + 2 + 2 + 1 * 8
|
||||
|
||||
# Keep in sync with Device (DEVICE_TYPE_DEVICETREE) from src/boot/chid.h
|
||||
# uint32_t descriptor, EFI_GUID chid, uint32_t name_offset, uint32_t compatible_offset
|
||||
DEVICE_STRUCT_SIZE = 4 + EFI_GUID_STRUCT_SIZE + 4 + 4
|
||||
DEVICE_STRUCT_SIZE = 4 + 16 + 4 + 4
|
||||
NULL_DEVICE = b'\0' * DEVICE_STRUCT_SIZE
|
||||
DEVICE_TYPE_DEVICETREE = 1
|
||||
|
||||
|
@ -1029,29 +1025,21 @@ def device_make_descriptor(device_type: int, size: int) -> int:
|
|||
return (size) | (device_type << 28)
|
||||
|
||||
|
||||
def pack_device(offsets: dict[str, int], name: str, compatible: str, chids: list[EFI_GUID]) -> bytes:
|
||||
DEVICETREE_DESCRIPTOR = device_make_descriptor(DEVICE_TYPE_DEVICETREE, DEVICE_STRUCT_SIZE)
|
||||
|
||||
|
||||
def pack_device(offsets: dict[str, int], name: str, compatible: str, chids: list[uuid.UUID]) -> bytes:
|
||||
data = b''
|
||||
|
||||
for data1, data2, data3, data4 in chids:
|
||||
data += struct.pack(
|
||||
'<IIHH8BII',
|
||||
device_make_descriptor(DEVICE_TYPE_DEVICETREE, DEVICE_STRUCT_SIZE),
|
||||
data1,
|
||||
data2,
|
||||
data3,
|
||||
*data4,
|
||||
offsets[name],
|
||||
offsets[compatible],
|
||||
)
|
||||
for chid in chids:
|
||||
data += struct.pack('<I', DEVICETREE_DESCRIPTOR)
|
||||
data += chid.bytes_le
|
||||
data += struct.pack('<II', offsets[name], offsets[compatible])
|
||||
|
||||
assert len(data) == DEVICE_STRUCT_SIZE * len(chids)
|
||||
return data
|
||||
|
||||
|
||||
def hex_pairs_list(string: str) -> list[int]:
|
||||
return [int(string[i : i + 2], 16) for i in range(0, len(string), 2)]
|
||||
|
||||
|
||||
def pack_strings(strings: set[str], base: int) -> tuple[bytes, dict[str, int]]:
|
||||
blob = b''
|
||||
offsets = {}
|
||||
|
@ -1064,56 +1052,22 @@ def pack_strings(strings: set[str], base: int) -> tuple[bytes, dict[str, int]]:
|
|||
|
||||
|
||||
def parse_hwid_dir(path: Path) -> bytes:
|
||||
hwid_files = path.rglob('*.txt')
|
||||
hwid_files = path.rglob('*.json')
|
||||
|
||||
strings: set[str] = set()
|
||||
devices: collections.defaultdict[tuple[str, str], list[EFI_GUID]] = collections.defaultdict(list)
|
||||
|
||||
uuid_regexp = re.compile(
|
||||
r'\{[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}\}', re.I
|
||||
)
|
||||
devices: collections.defaultdict[tuple[str, str], list[uuid.UUID]] = collections.defaultdict(list)
|
||||
|
||||
for hwid_file in hwid_files:
|
||||
content = hwid_file.open().readlines()
|
||||
data = json.loads(hwid_file.read_text(encoding='UTF-8'))
|
||||
|
||||
data: dict[str, str] = {
|
||||
'Manufacturer': '',
|
||||
'Family': '',
|
||||
'Compatible': '',
|
||||
}
|
||||
uuids: list[EFI_GUID] = []
|
||||
|
||||
for line in content:
|
||||
for k in data:
|
||||
if line.startswith(k):
|
||||
data[k] = line.split(':')[1].strip()
|
||||
break
|
||||
else:
|
||||
uuid = uuid_regexp.match(line)
|
||||
if uuid is not None:
|
||||
d1, d2, d3, d4, d5 = uuid.group(0)[1:-1].split('-')
|
||||
|
||||
data1 = int(d1, 16)
|
||||
data2 = int(d2, 16)
|
||||
data3 = int(d3, 16)
|
||||
data4 = cast(
|
||||
tuple[int, int, int, int, int, int, int, int],
|
||||
tuple(hex_pairs_list(d4) + hex_pairs_list(d5)),
|
||||
)
|
||||
|
||||
uuids.append((data1, data2, data3, data4))
|
||||
|
||||
for k, v in data.items():
|
||||
if not v:
|
||||
for k in ['name', 'compatible', 'hwids']:
|
||||
if k not in data:
|
||||
raise ValueError(f'hwid description file "{hwid_file}" does not contain "{k}"')
|
||||
|
||||
name = data['Manufacturer'] + ' ' + data['Family']
|
||||
compatible = data['Compatible']
|
||||
strings |= set([data['name'], data['compatible']])
|
||||
|
||||
strings |= set([name, compatible])
|
||||
|
||||
# (compatible, name) pair uniquely identifies the device
|
||||
devices[(compatible, name)] += uuids
|
||||
# (name, compatible) pair uniquely identifies the device
|
||||
devices[(data['name'], data['compatible'])] += [uuid.UUID(u) for u in data['hwids']]
|
||||
|
||||
total_device_structs = 1
|
||||
for dev, uuids in devices.items():
|
||||
|
@ -1122,7 +1076,7 @@ def parse_hwid_dir(path: Path) -> bytes:
|
|||
strings_blob, offsets = pack_strings(strings, total_device_structs * DEVICE_STRUCT_SIZE)
|
||||
|
||||
devices_blob = b''
|
||||
for (compatible, name), uuids in devices.items():
|
||||
for (name, compatible), uuids in devices.items():
|
||||
devices_blob += pack_device(offsets, name, compatible, uuids)
|
||||
|
||||
devices_blob += NULL_DEVICE
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
# shellcheck source=test/units/util.sh
|
||||
. "$(dirname "$0")"/util.sh
|
||||
|
||||
(! systemd-run --wait -p DynamicUser=yes \
|
||||
-p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
|
||||
-p WorkingDirectory='~' true)
|
||||
|
||||
assert_eq "$(systemd-run --pipe --uid=root -p WorkingDirectory='~' pwd)" "/root"
|
||||
assert_eq "$(systemd-run --pipe --uid=nobody -p WorkingDirectory='~' pwd)" "/"
|
||||
assert_eq "$(systemd-run --pipe --uid=testuser -p WorkingDirectory='~' pwd)" "/home/testuser"
|
||||
|
||||
(! systemd-run --wait -p DynamicUser=yes -p User=testuser \
|
||||
-p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
|
||||
-p WorkingDirectory='~' true)
|
|
@ -16,6 +16,7 @@ ConditionDirectoryNotEmpty=|/run/confexts
|
|||
ConditionDirectoryNotEmpty=|/var/lib/confexts
|
||||
ConditionDirectoryNotEmpty=|/usr/local/lib/confexts
|
||||
ConditionDirectoryNotEmpty=|/usr/lib/confexts
|
||||
ConditionDirectoryNotEmpty=|/.extra/confext
|
||||
|
||||
DefaultDependencies=no
|
||||
After=local-fs.target
|
||||
|
|
Loading…
Reference in New Issue