1
0
mirror of https://github.com/systemd/systemd synced 2026-03-14 17:14:49 +01:00

Compare commits

..

No commits in common. "a346a34f7ffe3101761717b672f71c3aca3e3e0a" and "ee3713b71ddf182852a399953968a2b39af22104" have entirely different histories.

24 changed files with 100 additions and 729 deletions

View File

@ -19,12 +19,6 @@ expose. Or in other words, it both allows applications to efficiently query
user/group records from local services, and allows local subsystems to provide
user/group records efficiently to local applications.
The concepts described here define an IPC interface. Alternatively, user/group
records may be dropped in number of drop-in directories as files where they are
picked up in addition to the users/groups defined by this IPC logic. See
[`nss-systemd(8)`](https://www.freedesktop.org/software/systemd/man/nss-systemd.html)
for details.
This simple API only exposes only three method calls, and requires only a small
subset of the Varlink functionality.

View File

@ -75,11 +75,7 @@ Records](https://systemd.io/GROUP_RECORD) that encapsulate UNIX groups.
JSON User Records may be transferred or written to disk in various protocols
and formats. To inquire about such records defined on the local system use the
[User/Group Lookup API via
Varlink](https://systemd.io/USER_GROUP_API). User/group records may also be
dropped in number of drop-in directories as files. See
[`nss-systemd(8)`](https://www.freedesktop.org/software/systemd/man/nss-systemd.html)
for details.
[User/Group Lookup API via Varlink](https://systemd.io/USER_GROUP_API).
## Why JSON?

View File

@ -56,49 +56,6 @@
<filename>/etc/gshadow</filename> based mappings take precedence.</para>
</refsect1>
<refsect1>
<title>Static Drop-In JSON User/Group Records</title>
<para>Besides user/group records acquired via the aforementioned Varlink IPC interfaces and the
synthesized root and nobody accounts, this module also makes user and group accounts available to the
system that are defined in static drop-in files in the <filename>/etc/userdb/</filename>,
<filename>/run/userdb/</filename>, <filename>/run/host/userdb/</filename> and
<filename>/usr/lib/userdb/</filename> directories.</para>
<para>This is a simple mechanism to provide static user and group records via JSON drop-in files. Such
user records should be defined in the format described by the <ulink
url="https://systemd.io/USER_RECORD">JSON User Record</ulink> specification and be placed in one of the
aforementioned directories under a file name composed of the user name suffixed with
<filename>.user</filename>, with a world-readable access mode. A symlink named after the user record's
UID formatted in decimal and suffixed with <filename>.user</filename> pointing to the primary record file
should be created as well, in order to allow both lookups by username and by UID. Privileged user record
data (e.g. hashed UNIX passwords) may optionally be provided as well, in a pair of separate companion
files with the <filename>.user-privileged</filename> suffix. The data should be stored in a regular file
named after the user name, suffixed with <filename>.user-privileged</filename>, and a symlink pointing to
it, named after the used numeric UID formatted in decimal with the same suffix. These companion files
should not be readable to anyone but root. Example:</para>
<programlisting>-rw-r--r--. 1 root root 723 May 10 foobar.user
-rw-------. 1 root root 123 May 10 foobar.user-privileged
lrwxrwxrwx. 1 root root 19 May 10 4711.user -> foobar.user
lrwxrwxrwx. 1 root root 19 May 10 4711.user-privileged -> foobar.user-privileged</programlisting>
<para>Similarly, group records following the format described in <ulink
url="https://systemd.io/GROUP_RECORD">JSON Group Record</ulink> may be defined, using the file suffixes
<filename>.group</filename> and <filename>.group-privileged</filename>.</para>
<para>The primary user/group record files (i.e. those with the <filename>.user</filename> and
<filename>.group</filename> suffixes) should not contain the <literal>privileged</literal> section as
described in the specifications. The privileged user/group record files (i.e. those with the
<filename>.user-privileged</filename> and <filename>.group-privileged</filename> suffixes) should
contain this section, exclusively.</para>
<para>Note that static user/group records generally do not override conflicting records in
<filename>/etc/passwd</filename> or <filename>/etc/group</filename> or other account databases. In fact,
before dropping in these files a reasonable level of care should be taken to avoid user/group name and
UID/GID conflicts.</para>
</refsect1>
<refsect1>
<title>Configuration in <filename>/etc/nsswitch.conf</filename></title>

View File

@ -32,9 +32,7 @@
<para><command>systemd-userdbd</command> is a system service that multiplexes user/group lookups to all
local services that provide JSON user/group record definitions to the system. In addition it synthesizes
JSON user/group records from classic UNIX/glibc NSS user/group records in order to provide full backwards
compatibility. It may also pick up statically defined JSON user/group records from drop-in files in
<filename>/etc/userdb/</filename>, <filename>/run/userdb/</filename>,
<filename>/run/host/userdb/</filename> and <filename>/use/lib/userdb/</filename>.</para>
compatibility.</para>
<para>Most of <command>systemd-userdbd</command>'s functionality is accessible through the
<citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
@ -47,18 +45,16 @@
multiplexes access other services implementing this API, too. It is thus both server and client of this
API.</para>
<para>This service provides three distinct <ulink url="https://varlink.org/">Varlink</ulink> services:
<para>This service provides two distinct <ulink url="https://varlink.org/">Varlink</ulink> services:
<constant>io.systemd.Multiplexer</constant> provides a single, unified API for querying JSON user and
group records. Internally it talks to all other user/group record services running on the system in
parallel and forwards any information discovered. This simplifies clients substantially since they need
to talk to a single service only instead of all of them in
parallel. <constant>io.systemd.NameServiceSwitch</constant> provides compatibility with classic
UNIX/glibc NSS user records, i.e. converts <type>struct passwd</type> and <type>struct group</type>
records as acquired with APIs such as <citerefentry
project='man-pages'><refentrytitle>getpwnam</refentrytitle><manvolnum>1</manvolnum></citerefentry> to
JSON user/group records, thus hiding the differences between the services as much as
possible. <constant>io.systemd.Dropin</constant> makes JSON user/group records from the aforementioned
drop-in directories available.</para>
parallel. <constant>io.systemd.NameServiceSwitch</constant> provides compatibility with classic UNIX/glibc
NSS user records, i.e. converts <type>struct passwd</type> and <type>struct group</type> records as
acquired with APIs such as <citerefentry
project='man-pages'><refentrytitle>getpwnam</refentrytitle><manvolnum>1</manvolnum></citerefentry> to JSON
user/group records, thus hiding the differences between the services as much as possible.</para>
</refsect1>
<refsect1>

View File

@ -133,10 +133,7 @@
<term><varname>Type=</varname></term>
<listitem>
<para>A whitespace-separated list of shell-style globs matching the device type, as exposed by
<command>networkctl list</command>. If the list is prefixed with a "!", the test is inverted.
Some valid values are <literal>ether</literal>, <literal>loopback</literal>, <literal>wlan</literal>, <literal>wwan</literal>.
Valid types are named either from the udev <literal>DEVTYPE</literal> attribute, or
<literal>ARPHRD_</literal> macros in <filename>linux/if_arp.h</filename>, so this is not comprehensive.
<command>networkctl status</command>. If the list is prefixed with a "!", the test is inverted.
</para>
</listitem>
</varlistentry>

View File

@ -1,5 +1,5 @@
[Match]
Type=wlan
Type=wifi
WLANInterfaceType=ad-hoc
[Network]

View File

@ -1,5 +1,5 @@
[Match]
Type=wlan
Type=wifi
WLANInterfaceType=ap
[Network]

View File

@ -1,5 +1,5 @@
[Match]
Type=wlan
Type=wifi
WLANInterfaceType=station
[Network]

View File

@ -81,9 +81,6 @@ static int dhcp_server_emit_changed(Link *link, const char *property, ...) {
assert(link);
if (sd_bus_is_ready(link->manager->bus) <= 0)
return 0;
path = link_bus_path(link);
if (!path)
return log_oom();

View File

@ -2272,9 +2272,6 @@ int manager_connect_bus(Manager *m) {
int _manager_send_changed(Manager *manager, const char *property, ...) {
assert(manager);
if (sd_bus_is_ready(manager->bus) <= 0)
return 0;
char **l = strv_from_stdarg_alloca(property);
int r = sd_bus_emit_properties_changed_strv(

View File

@ -500,7 +500,7 @@ void dns_zone_item_conflict(DnsZoneItem *i) {
/* Withdraw the conflict item */
i->state = DNS_ZONE_ITEM_WITHDRAWN;
(void) dnssd_signal_conflict(i->scope->manager, dns_resource_key_name(i->rr->key));
dnssd_signal_conflict(i->scope->manager, dns_resource_key_name(i->rr->key));
/* Maybe change the hostname */
if (manager_is_own_hostname(i->scope->manager, dns_resource_key_name(i->rr->key)) > 0)

View File

@ -329,13 +329,10 @@ int dnssd_txt_item_new_from_data(const char *key, const void *data, const size_t
return 0;
}
int dnssd_signal_conflict(Manager *manager, const char *name) {
void dnssd_signal_conflict(Manager *manager, const char *name) {
DnssdService *s;
int r;
if (sd_bus_is_ready(manager->bus) <= 0)
return 0;
HASHMAP_FOREACH(s, manager->dnssd_services) {
if (s->withdrawn)
continue;
@ -346,20 +343,22 @@ int dnssd_signal_conflict(Manager *manager, const char *name) {
s->withdrawn = true;
r = sd_bus_path_encode("/org/freedesktop/resolve1/dnssd", s->name, &path);
if (r < 0)
return log_error_errno(r, "Can't get D-BUS object path: %m");
if (r < 0) {
log_error_errno(r, "Can't get D-BUS object path: %m");
return;
}
r = sd_bus_emit_signal(manager->bus,
path,
"org.freedesktop.resolve1.DnssdService",
"Conflicted",
NULL);
if (r < 0)
return log_error_errno(r, "Cannot emit signal: %m");
if (r < 0) {
log_error_errno(r, "Cannot emit signal: %m");
return;
}
break;
}
}
return 0;
}

View File

@ -58,4 +58,4 @@ int dnssd_load(Manager *manager);
int dnssd_txt_item_new_from_string(const char *key, const char *value, DnsTxtItem **ret_item);
int dnssd_txt_item_new_from_data(const char *key, const void *value, const size_t size, DnsTxtItem **ret_item);
int dnssd_update_rrs(DnssdService *s);
int dnssd_signal_conflict(Manager *manager, const char *name);
void dnssd_signal_conflict(Manager *manager, const char *name);

View File

@ -395,8 +395,7 @@ int local_outbounds(
sa.in = (struct sockaddr_in) {
.sin_family = AF_INET,
.sin_addr = gateways[i].address.in,
.sin_port = htobe16(53), /* doesn't really matter which port we pick —
* we just care about the routing decision */
.sin_port = htobe16(53), /* doesn't really matter which port we pick — we just care about the routing decision */
};
break;

View File

@ -262,8 +262,6 @@ shared_sources = files('''
user-record.h
userdb.c
userdb.h
userdb-dropin.c
userdb-dropin.h
utmp-wtmp.h
varlink.c
varlink.h

View File

@ -1,302 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
#include "path-util.h"
#include "stdio-util.h"
#include "user-util.h"
#include "userdb-dropin.h"
static int load_user(
FILE *f,
const char *path,
const char *name,
uid_t uid,
UserDBFlags flags,
UserRecord **ret) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
_cleanup_(user_record_unrefp) UserRecord *u = NULL;
bool have_privileged;
int r;
assert(f);
r = json_parse_file(f, path, 0, &v, NULL, NULL);
if (r < 0)
return r;
if (FLAGS_SET(flags, USERDB_SUPPRESS_SHADOW) || !path || !(name || uid_is_valid(uid)))
have_privileged = false;
else {
_cleanup_(json_variant_unrefp) JsonVariant *privileged_v = NULL;
_cleanup_free_ char *d = NULL, *j = NULL;
/* Let's load the "privileged" section from a companion file. But only if USERDB_AVOID_SHADOW
* is not set. After all, the privileged section kinda takes the role of the data from the
* shadow file, hence it makes sense to use the same flag here.
*
* The general assumption is that whoever provides these records makes the .user file
* world-readable, but the .privilege file readable to root and the assigned UID only. But we
* won't verify that here, as it would be too late. */
r = path_extract_directory(path, &d);
if (r < 0)
return r;
if (name) {
j = strjoin(d, "/", name, ".user-privileged");
if (!j)
return -ENOMEM;
} else {
assert(uid_is_valid(uid));
if (asprintf(&j, "%s/" UID_FMT ".user-privileged", d, uid) < 0)
return -ENOMEM;
}
r = json_parse_file(NULL, j, JSON_PARSE_SENSITIVE, &privileged_v, NULL, NULL);
if (ERRNO_IS_PRIVILEGE(r))
have_privileged = false;
else if (r == -ENOENT)
have_privileged = true; /* if the privileged file doesn't exist, we are complete */
else if (r < 0)
return r;
else {
r = json_variant_merge(&v, privileged_v);
if (r < 0)
return r;
have_privileged = true;
}
}
u = user_record_new();
if (!u)
return -ENOMEM;
r = user_record_load(
u, v,
USER_RECORD_REQUIRE_REGULAR|
USER_RECORD_ALLOW_PER_MACHINE|
USER_RECORD_ALLOW_BINDING|
USER_RECORD_ALLOW_SIGNATURE|
(have_privileged ? USER_RECORD_ALLOW_PRIVILEGED : 0));
if (r < 0)
return r;
if (name && !streq_ptr(name, u->user_name))
return -EINVAL;
if (uid_is_valid(uid) && uid != u->uid)
return -EINVAL;
u->incomplete = !have_privileged;
if (ret)
*ret = TAKE_PTR(u);
return 0;
}
int dropin_user_record_by_name(const char *name, const char *path, UserDBFlags flags, UserRecord **ret) {
_cleanup_free_ char *found_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
assert(name);
if (path) {
f = fopen(path, "re");
if (!f)
return errno == ENOENT ? -ESRCH : -errno; /* We generally want ESRCH to indicate no such user */
} else {
const char *j;
j = strjoina(name, ".user");
if (!filename_is_valid(j)) /* Doesn't qualify as valid filename? Then it's definitely not provided as a drop-in */
return -ESRCH;
r = search_and_fopen_nulstr(j, "re", NULL, USERDB_DROPIN_DIR_NULSTR("userdb"), &f, &found_path);
if (r == -ENOENT)
return -ESRCH;
if (r < 0)
return r;
path = found_path;
}
return load_user(f, path, name, UID_INVALID, flags, ret);
}
int dropin_user_record_by_uid(uid_t uid, const char *path, UserDBFlags flags, UserRecord **ret) {
_cleanup_free_ char *found_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
assert(uid_is_valid(uid));
if (path) {
f = fopen(path, "re");
if (!f)
return errno == ENOENT ? -ESRCH : -errno;
} else {
char buf[DECIMAL_STR_MAX(uid_t) + STRLEN(".user") + 1];
xsprintf(buf, UID_FMT ".user", uid);
/* Note that we don't bother to validate this as a filename, as this is generated from a decimal
* integer, i.e. is definitely OK as a filename */
r = search_and_fopen_nulstr(buf, "re", NULL, USERDB_DROPIN_DIR_NULSTR("userdb"), &f, &found_path);
if (r == -ENOENT)
return -ESRCH;
if (r < 0)
return r;
path = found_path;
}
return load_user(f, path, NULL, uid, flags, ret);
}
static int load_group(
FILE *f,
const char *path,
const char *name,
gid_t gid,
UserDBFlags flags,
GroupRecord **ret) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
_cleanup_(group_record_unrefp) GroupRecord *g = NULL;
bool have_privileged;
int r;
assert(f);
r = json_parse_file(f, path, 0, &v, NULL, NULL);
if (r < 0)
return r;
if (FLAGS_SET(flags, USERDB_SUPPRESS_SHADOW) || !path || !(name || gid_is_valid(gid)))
have_privileged = false;
else {
_cleanup_(json_variant_unrefp) JsonVariant *privileged_v = NULL;
_cleanup_free_ char *d = NULL, *j = NULL;
r = path_extract_directory(path, &d);
if (r < 0)
return r;
if (name) {
j = strjoin(d, "/", name, ".group-privileged");
if (!j)
return -ENOMEM;
} else {
assert(gid_is_valid(gid));
if (asprintf(&j, "%s/" GID_FMT ".group-privileged", d, gid) < 0)
return -ENOMEM;
}
r = json_parse_file(NULL, j, JSON_PARSE_SENSITIVE, &privileged_v, NULL, NULL);
if (ERRNO_IS_PRIVILEGE(r))
have_privileged = false;
else if (r == -ENOENT)
have_privileged = true; /* if the privileged file doesn't exist, we are complete */
else if (r < 0)
return r;
else {
r = json_variant_merge(&v, privileged_v);
if (r < 0)
return r;
have_privileged = true;
}
}
g = group_record_new();
if (!g)
return -ENOMEM;
r = group_record_load(
g, v,
USER_RECORD_REQUIRE_REGULAR|
USER_RECORD_ALLOW_PER_MACHINE|
USER_RECORD_ALLOW_BINDING|
USER_RECORD_ALLOW_SIGNATURE|
(have_privileged ? USER_RECORD_ALLOW_PRIVILEGED : 0));
if (r < 0)
return r;
if (name && !streq_ptr(name, g->group_name))
return -EINVAL;
if (gid_is_valid(gid) && gid != g->gid)
return -EINVAL;
g->incomplete = !have_privileged;
if (ret)
*ret = TAKE_PTR(g);
return 0;
}
int dropin_group_record_by_name(const char *name, const char *path, UserDBFlags flags, GroupRecord **ret) {
_cleanup_free_ char *found_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
assert(name);
if (path) {
f = fopen(path, "re");
if (!f)
return errno == ENOENT ? -ESRCH : -errno;
} else {
const char *j;
j = strjoina(name, ".group");
if (!filename_is_valid(j)) /* Doesn't qualify as valid filename? Then it's definitely not provided as a drop-in */
return -ESRCH;
r = search_and_fopen_nulstr(j, "re", NULL, USERDB_DROPIN_DIR_NULSTR("userdb"), &f, &found_path);
if (r == -ENOENT)
return -ESRCH;
if (r < 0)
return r;
path = found_path;
}
return load_group(f, path, name, GID_INVALID, flags, ret);
}
int dropin_group_record_by_gid(gid_t gid, const char *path, UserDBFlags flags, GroupRecord **ret) {
_cleanup_free_ char *found_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
assert(gid_is_valid(gid));
if (path) {
f = fopen(path, "re");
if (!f)
return errno == ENOENT ? -ESRCH : -errno;
} else {
char buf[DECIMAL_STR_MAX(gid_t) + STRLEN(".group") + 1];
xsprintf(buf, GID_FMT ".group", gid);
r = search_and_fopen_nulstr(buf, "re", NULL, USERDB_DROPIN_DIR_NULSTR("userdb"), &f, &found_path);
if (r == -ENOENT)
return -ESRCH;
if (r < 0)
return r;
path = found_path;
}
return load_group(f, path, NULL, gid, flags, ret);
}

View File

@ -1,23 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "def.h"
#include "group-record.h"
#include "user-record.h"
#include "userdb.h"
/* This could be put together with CONF_PATHS_NULSTR, with the exception of the /run/host/ part in the
* middle, which we use here, but not otherwise. */
#define USERDB_DROPIN_DIR_NULSTR(n) \
"/etc/" n "\0" \
"/run/" n "\0" \
"/run/host/" n "\0" \
"/usr/local/lib/" n "\0" \
"/usr/lib/" n "\0" \
_CONF_PATHS_SPLIT_USR_NULSTR(n)
int dropin_user_record_by_name(const char *name, const char *path, UserDBFlags flags, UserRecord **ret);
int dropin_user_record_by_uid(uid_t uid, const char *path, UserDBFlags flags, UserRecord **ret);
int dropin_group_record_by_name(const char *name, const char *path, UserDBFlags flags, GroupRecord **ret);
int dropin_group_record_by_gid(gid_t gid, const char *path, UserDBFlags flags, GroupRecord **ret);

View File

@ -2,12 +2,10 @@
#include <sys/auxv.h>
#include "conf-files.h"
#include "dirent-util.h"
#include "dlfcn-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "format-util.h"
#include "missing_syscall.h"
#include "parse-util.h"
#include "set.h"
@ -15,7 +13,6 @@
#include "strv.h"
#include "user-record-nss.h"
#include "user-util.h"
#include "userdb-dropin.h"
#include "userdb.h"
#include "varlink.h"
@ -34,12 +31,9 @@ struct UserDBIterator {
Set *links;
bool nss_covered:1;
bool nss_iterating:1;
bool dropin_covered:1;
bool synthesize_root:1;
bool synthesize_nobody:1;
bool nss_systemd_blocked:1;
char **dropins;
size_t current_dropin;
int error;
unsigned n_found;
sd_event *event;
@ -49,7 +43,7 @@ struct UserDBIterator {
char *found_user_name, *found_group_name; /* when .what == LOOKUP_MEMBERSHIP */
char **members_of_group;
size_t index_members_of_group;
char *filter_user_name, *filter_group_name;
char *filter_user_name;
};
UserDBIterator* userdb_iterator_free(UserDBIterator *iterator) {
@ -57,7 +51,6 @@ UserDBIterator* userdb_iterator_free(UserDBIterator *iterator) {
return NULL;
set_free(iterator->links);
strv_free(iterator->dropins);
switch (iterator->what) {
@ -82,7 +75,6 @@ UserDBIterator* userdb_iterator_free(UserDBIterator *iterator) {
free(iterator->found_group_name);
strv_free(iterator->members_of_group);
free(iterator->filter_user_name);
free(iterator->filter_group_name);
if (iterator->nss_iterating)
endgrent();
@ -433,7 +425,7 @@ static int userdb_start_query(
}
/* First, let's talk to the multiplexer, if we can */
if ((flags & (USERDB_AVOID_MULTIPLEXER|USERDB_EXCLUDE_DYNAMIC_USER|USERDB_EXCLUDE_NSS|USERDB_EXCLUDE_DROPIN|USERDB_DONT_SYNTHESIZE)) == 0 &&
if ((flags & (USERDB_AVOID_MULTIPLEXER|USERDB_EXCLUDE_DYNAMIC_USER|USERDB_EXCLUDE_NSS|USERDB_DONT_SYNTHESIZE)) == 0 &&
!strv_contains(except, "io.systemd.Multiplexer") &&
(!only || strv_contains(only, "io.systemd.Multiplexer"))) {
_cleanup_(json_variant_unrefp) JsonVariant *patched_query = json_variant_ref(query);
@ -445,7 +437,6 @@ static int userdb_start_query(
r = userdb_connect(iterator, "/run/systemd/userdb/io.systemd.Multiplexer", method, more, patched_query);
if (r >= 0) {
iterator->nss_covered = true; /* The multiplexer does NSS */
iterator->dropin_covered = true; /* It also handles drop-in stuff */
return 0;
}
}
@ -461,7 +452,7 @@ static int userdb_start_query(
FOREACH_DIRENT(de, d, return -errno) {
_cleanup_(json_variant_unrefp) JsonVariant *patched_query = NULL;
_cleanup_free_ char *p = NULL;
bool is_nss, is_dropin;
bool is_nss;
if (streq(de->d_name, "io.systemd.Multiplexer")) /* We already tried this above, don't try this again */
continue;
@ -478,11 +469,6 @@ static int userdb_start_query(
if ((flags & (USERDB_EXCLUDE_NSS|USERDB_AVOID_MULTIPLEXER)) && is_nss)
continue;
/* Similar for the drop-in service */
is_dropin = streq(de->d_name, "io.systemd.DropIn");
if ((flags & (USERDB_EXCLUDE_DROPIN|USERDB_AVOID_MULTIPLEXER)) && is_dropin)
continue;
if (strv_contains(except, de->d_name))
continue;
@ -499,11 +485,9 @@ static int userdb_start_query(
return log_debug_errno(r, "Unable to set service JSON field: %m");
r = userdb_connect(iterator, p, method, more, patched_query);
if (is_nss && r >= 0) /* Turn off fallback NSS + dropin if we found the NSS/dropin service
* and could connect to it */
if (is_nss && r >= 0) /* Turn off fallback NSS if we found the NSS service and could connect
* to it */
iterator->nss_covered = true;
if (is_dropin && r >= 0)
iterator->dropin_covered = true;
if (ret == 0 && r < 0)
ret = r;
@ -640,12 +624,6 @@ int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret) {
return r;
}
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && !iterator->dropin_covered) {
r = dropin_user_record_by_name(name, NULL, flags, ret);
if (r >= 0)
return r;
}
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && !iterator->nss_covered) {
/* Make sure the NSS lookup doesn't recurse back to us. */
@ -693,12 +671,6 @@ int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret) {
return r;
}
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && !iterator->dropin_covered) {
r = dropin_user_record_by_uid(uid, NULL, flags, ret);
if (r >= 0)
return r;
}
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && !iterator->nss_covered) {
r = userdb_iterator_block_nss_systemd(iterator);
if (r >= 0) {
@ -722,7 +694,7 @@ int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret) {
int userdb_all(UserDBFlags flags, UserDBIterator **ret) {
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
int r, qr;
int r;
assert(ret);
@ -732,33 +704,17 @@ int userdb_all(UserDBFlags flags, UserDBIterator **ret) {
iterator->synthesize_root = iterator->synthesize_nobody = !FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE);
qr = 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) && (qr < 0 || !iterator->nss_covered)) {
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (r < 0 || !iterator->nss_covered)) {
r = userdb_iterator_block_nss_systemd(iterator);
if (r < 0)
return r;
setpwent();
iterator->nss_iterating = true;
}
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && (qr < 0 || !iterator->dropin_covered)) {
r = conf_files_list_nulstr(
&iterator->dropins,
".user",
NULL,
CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED,
USERDB_DROPIN_DIR_NULSTR("userdb"));
if (r < 0)
log_debug_errno(r, "Failed to find user drop-ins, ignoring: %m");
}
/* propagate IPC error, but only if there are no drop-ins */
if (qr < 0 &&
!iterator->nss_iterating &&
strv_isempty(iterator->dropins))
return qr;
} else if (r < 0)
return r;
*ret = TAKE_PTR(iterator);
return 0;
@ -816,45 +772,9 @@ int userdb_iterator_get(UserDBIterator *iterator, UserRecord **ret) {
endpwent();
}
for (; iterator->dropins && iterator->dropins[iterator->current_dropin]; iterator->current_dropin++) {
const char *i = iterator->dropins[iterator->current_dropin];
_cleanup_free_ char *fn = NULL;
uid_t uid;
char *e;
/* Next, let's add in the static drop-ins, which are quick to retrieve */
r = path_extract_filename(i, &fn);
if (r < 0)
return r;
e = endswith(fn, ".user"); /* not actually a .user file? Then skip to next */
if (!e)
continue;
*e = 0; /* Chop off suffix */
if (parse_uid(fn, &uid) < 0) /* not a UID .user file? Then skip to next */
continue;
r = dropin_user_record_by_uid(uid, i, iterator->flags, ret);
if (r < 0) {
log_debug_errno(r, "Failed to parse user record for UID " UID_FMT ", ignoring: %m", uid);
continue; /* If we failed to parse this record, let's suppress it from enumeration,
* and continue with the next record. Maybe someone is dropping it files
* and only partially wrote this one. */
}
iterator->current_dropin++; /* make sure on the next call of userdb_iterator_get() we continue with the next dropin */
iterator->n_found++;
return 0;
}
/* Then, let's return the users provided by varlink IPC */
r = userdb_process(iterator, ret, NULL, NULL, NULL);
if (r < 0) {
/* Finally, synthesize root + nobody if not done yet */
if (r < 0) {
if (iterator->synthesize_root) {
iterator->synthesize_root = false;
iterator->n_found++;
@ -915,13 +835,6 @@ int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret) {
return r;
}
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && !(iterator && iterator->dropin_covered)) {
r = dropin_group_record_by_name(name, NULL, flags, ret);
if (r >= 0)
return r;
}
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && !(iterator && iterator->nss_covered)) {
r = userdb_iterator_block_nss_systemd(iterator);
if (r >= 0) {
@ -966,12 +879,6 @@ int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret) {
return r;
}
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && !(iterator && iterator->dropin_covered)) {
r = dropin_group_record_by_gid(gid, NULL, flags, ret);
if (r >= 0)
return r;
}
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && !(iterator && iterator->nss_covered)) {
r = userdb_iterator_block_nss_systemd(iterator);
if (r >= 0) {
@ -994,7 +901,7 @@ int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret) {
int groupdb_all(UserDBFlags flags, UserDBIterator **ret) {
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
int r, qr;
int r;
assert(ret);
@ -1004,32 +911,17 @@ int groupdb_all(UserDBFlags flags, UserDBIterator **ret) {
iterator->synthesize_root = iterator->synthesize_nobody = !FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE);
qr = 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) && (qr < 0 || !iterator->nss_covered)) {
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (r < 0 || !iterator->nss_covered)) {
r = userdb_iterator_block_nss_systemd(iterator);
if (r < 0)
return r;
setgrent();
iterator->nss_iterating = true;
}
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && (qr < 0 || !iterator->dropin_covered)) {
r = conf_files_list_nulstr(
&iterator->dropins,
".group",
NULL,
CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED,
USERDB_DROPIN_DIR_NULSTR("userdb"));
if (r < 0)
log_debug_errno(r, "Failed to find group drop-ins, ignoring: %m");
}
if (qr < 0 &&
!iterator->nss_iterating &&
strv_isempty(iterator->dropins))
return qr;
} else if (r < 0)
return r;
*ret = TAKE_PTR(iterator);
return 0;
@ -1085,36 +977,6 @@ int groupdb_iterator_get(UserDBIterator *iterator, GroupRecord **ret) {
endgrent();
}
for (; iterator->dropins && iterator->dropins[iterator->current_dropin]; iterator->current_dropin++) {
const char *i = iterator->dropins[iterator->current_dropin];
_cleanup_free_ char *fn = NULL;
gid_t gid;
char *e;
r = path_extract_filename(i, &fn);
if (r < 0)
return r;
e = endswith(fn, ".group");
if (!e)
continue;
*e = 0; /* Chop off suffix */
if (parse_gid(fn, &gid) < 0)
continue;
r = dropin_group_record_by_gid(gid, i, iterator->flags, ret);
if (r < 0) {
log_debug_errno(r, "Failed to parse group record for GID " GID_FMT ", ignoring: %m", gid);
continue;
}
iterator->current_dropin++;
iterator->n_found++;
return 0;
}
r = userdb_process(iterator, NULL, ret, NULL, NULL);
if (r < 0) {
if (iterator->synthesize_root) {
@ -1137,23 +999,10 @@ int groupdb_iterator_get(UserDBIterator *iterator, GroupRecord **ret) {
return r;
}
static void discover_membership_dropins(UserDBIterator *i, UserDBFlags flags) {
int r;
r = conf_files_list_nulstr(
&i->dropins,
".membership",
NULL,
CONF_FILES_REGULAR|CONF_FILES_BASENAME|CONF_FILES_FILTER_MASKED,
USERDB_DROPIN_DIR_NULSTR("userdb"));
if (r < 0)
log_debug_errno(r, "Failed to find membership drop-ins, ignoring: %m");
}
int membershipdb_by_user(const char *name, UserDBFlags flags, UserDBIterator **ret) {
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
int r, qr;
int r;
assert(ret);
@ -1169,37 +1018,34 @@ int membershipdb_by_user(const char *name, UserDBFlags flags, UserDBIterator **r
if (!iterator)
return -ENOMEM;
iterator->filter_user_name = strdup(name);
if (!iterator->filter_user_name)
return -ENOMEM;
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, query, flags);
if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_EXCLUDE_NSS))
goto finish;
qr = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, query, flags);
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (qr < 0 || !iterator->nss_covered)) {
r = userdb_iterator_block_nss_systemd(iterator);
if (r < 0)
return r;
iterator->filter_user_name = strdup(name);
if (!iterator->filter_user_name)
return -ENOMEM;
setgrent();
iterator->nss_iterating = true;
}
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && (qr < 0 || !iterator->dropin_covered))
discover_membership_dropins(iterator, flags);
if (qr < 0 &&
!iterator->nss_iterating &&
strv_isempty(iterator->dropins))
return qr;
r = 0;
finish:
if (r >= 0)
*ret = TAKE_PTR(iterator);
return 0;
return r;
}
int membershipdb_by_group(const char *name, UserDBFlags flags, UserDBIterator **ret) {
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
int r, qr;
_cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
int r;
assert(ret);
@ -1215,14 +1061,9 @@ int membershipdb_by_group(const char *name, UserDBFlags flags, UserDBIterator **
if (!iterator)
return -ENOMEM;
iterator->filter_group_name = strdup(name);
if (!iterator->filter_group_name)
return -ENOMEM;
qr = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, query, flags);
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (qr < 0 || !iterator->nss_covered)) {
_cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, query, flags);
if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_EXCLUDE_NSS))
goto finish;
r = userdb_iterator_block_nss_systemd(iterator);
if (r < 0)
@ -1241,23 +1082,19 @@ int membershipdb_by_group(const char *name, UserDBFlags flags, UserDBIterator **
if (!iterator->found_group_name)
return -ENOMEM;
}
}
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && (qr < 0 || !iterator->dropin_covered))
discover_membership_dropins(iterator, flags);
if (qr < 0 &&
strv_isempty(iterator->members_of_group) &&
strv_isempty(iterator->dropins))
return qr;
r = 0;
finish:
if (r >= 0)
*ret = TAKE_PTR(iterator);
return 0;
return r;
}
int membershipdb_all(UserDBFlags flags, UserDBIterator **ret) {
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
int r, qr;
int r;
assert(ret);
@ -1265,27 +1102,24 @@ int membershipdb_all(UserDBFlags flags, UserDBIterator **ret) {
if (!iterator)
return -ENOMEM;
qr = 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))
goto finish;
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (qr < 0 || !iterator->nss_covered)) {
r = userdb_iterator_block_nss_systemd(iterator);
if (r < 0)
return r;
setgrent();
iterator->nss_iterating = true;
}
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && (qr < 0 || !iterator->dropin_covered))
discover_membership_dropins(iterator, flags);
if (qr < 0 &&
!iterator->nss_iterating &&
strv_isempty(iterator->dropins))
return qr;
r = 0;
finish:
if (r >= 0)
*ret = TAKE_PTR(iterator);
return 0;
return r;
}
int membershipdb_iterator_get(
@ -1371,48 +1205,6 @@ int membershipdb_iterator_get(
iterator->found_group_name = mfree(iterator->found_group_name);
}
for (; iterator->dropins && iterator->dropins[iterator->current_dropin]; iterator->current_dropin++) {
const char *i = iterator->dropins[iterator->current_dropin], *e, *c;
_cleanup_free_ char *un = NULL, *gn = NULL;
e = endswith(i, ".membership");
if (!e)
continue;
c = memchr(i, ':', e - i);
if (!c)
continue;
un = strndup(i, c - i);
if (!un)
return -ENOMEM;
if (iterator->filter_user_name) {
if (!streq(un, iterator->filter_user_name))
continue;
} else if (!valid_user_group_name(un, VALID_USER_RELAX))
continue;
c++; /* skip over ':' */
gn = strndup(c, e - c);
if (!gn)
return -ENOMEM;
if (iterator->filter_group_name) {
if (!streq(gn, iterator->filter_group_name))
continue;
} else if (!valid_user_group_name(gn, VALID_USER_RELAX))
continue;
iterator->current_dropin++;
iterator->n_found++;
if (ret_user)
*ret_user = TAKE_PTR(un);
if (ret_group)
*ret_group = TAKE_PTR(gn);
return 0;
}
r = userdb_process(iterator, NULL, NULL, ret_user, ret_group);
if (r < 0 && iterator->n_found > 0)
return -ESRCH;

View File

@ -18,7 +18,6 @@ typedef enum UserDBFlags {
/* The main sources */
USERDB_EXCLUDE_NSS = 1 << 0, /* don't do client-side nor server-side NSS */
USERDB_EXCLUDE_VARLINK = 1 << 1, /* don't talk to any varlink services */
USERDB_EXCLUDE_DROPIN = 1 << 2, /* don't load drop-in user/group definitions */
/* Modifications */
USERDB_SUPPRESS_SHADOW = 1 << 3, /* don't do client-side shadow calls (server side might happen though) */
@ -27,18 +26,9 @@ typedef enum UserDBFlags {
USERDB_DONT_SYNTHESIZE = 1 << 6, /* don't synthesize root/nobody */
/* Combinations */
USERDB_NSS_ONLY = USERDB_EXCLUDE_VARLINK|USERDB_EXCLUDE_DROPIN|USERDB_DONT_SYNTHESIZE,
USERDB_DROPIN_ONLY = USERDB_EXCLUDE_NSS|USERDB_EXCLUDE_VARLINK|USERDB_DONT_SYNTHESIZE,
USERDB_NSS_ONLY = USERDB_EXCLUDE_VARLINK|USERDB_DONT_SYNTHESIZE,
} UserDBFlags;
/* Well-known errors we'll return here:
*
* -ESRCH: No such user/group
* -ELINK: Varlink logic turned off (and no other source available)
* -EOPNOTSUPP: Enumeration not supported
* -ETIMEDOUT: Time-out
*/
int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret);
int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret);
int userdb_all(UserDBFlags flags, UserDBIterator **ret);

View File

@ -607,13 +607,7 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
m->poll_interval_usec / USEC_PER_SEC, offset, delay, m->samples_jitter, m->drift_freq / 65536,
spike ? " (ignored)" : "");
if (sd_bus_is_ready(m->bus) > 0)
(void) sd_bus_emit_properties_changed(
m->bus,
"/org/freedesktop/timesync1",
"org.freedesktop.timesync1.Manager",
"NTPMessage",
NULL);
(void) sd_bus_emit_properties_changed(m->bus, "/org/freedesktop/timesync1", "org.freedesktop.timesync1.Manager", "NTPMessage", NULL);
if (!m->good) {
_cleanup_free_ char *pretty = NULL;

View File

@ -289,11 +289,6 @@ int manager_startup(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to bind io.systemd.Multiplexer: %m");
r = symlink_idempotent("io.systemd.Multiplexer",
"/run/systemd/userdb/io.systemd.DropIn", false);
if (r < 0)
return log_error_errno(r, "Failed to bind io.systemd.Multiplexer: %m");
if (listen(m->listen_fd, SOMAXCONN) < 0)
return log_error_errno(errno, "Failed to listen on socket: %m");
}

View File

@ -17,9 +17,6 @@
* io.systemd.Multiplexer: this multiplexes lookup requests to all Varlink services that have a
* socket in /run/systemd/userdb/. It's supposed to simplify clients that don't want to implement
* the full iterative logic on their own.
*
* io.systemd.DropIn: this makes JSON user/group records dropped into /run/userdb/ available as
* regular users.
*/
static int run(int argc, char *argv[]) {
@ -34,8 +31,8 @@ static int run(int argc, char *argv[]) {
if (argc != 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
if (setenv("SYSTEMD_BYPASS_USERDB", "io.systemd.NameServiceSwitch:io.systemd.Multiplexer:io.systemd.DropIn", 1) < 0)
return log_error_errno(errno, "Failed to set $SYSTEMD_BYPASS_USERDB: %m");
if (setenv("SYSTEMD_BYPASS_USERDB", "io.systemd.NameServiceSwitch:io.systemd.Multiplexer", 1) < 0)
return log_error_errno(errno, "Failed to se $SYSTEMD_BYPASS_USERDB: %m");
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, SIGUSR2, -1) >= 0);

View File

@ -120,8 +120,6 @@ static int userdb_flags_from_service(Varlink *link, const char *service, UserDBF
if (streq_ptr(service, "io.systemd.NameServiceSwitch"))
*ret = USERDB_NSS_ONLY|USERDB_AVOID_MULTIPLEXER;
if (streq_ptr(service, "io.systemd.DropIn"))
*ret = USERDB_DROPIN_ONLY|USERDB_AVOID_MULTIPLEXER;
else if (streq_ptr(service, "io.systemd.Multiplexer"))
*ret = USERDB_AVOID_MULTIPLEXER;
else

View File

@ -15,7 +15,7 @@ Before=sockets.target
[Socket]
ListenStream=/run/systemd/userdb/io.systemd.Multiplexer
Symlinks=/run/systemd/userdb/io.systemd.NameServiceSwitch /run/systemd/userdb/io.systemd.DropIn
Symlinks=/run/systemd/userdb/io.systemd.NameServiceSwitch
SocketMode=0666
[Install]