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.
a346a34f7f
...
ee3713b71d
@ -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.
|
||||
|
||||
|
||||
@ -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?
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
[Match]
|
||||
Type=wlan
|
||||
Type=wifi
|
||||
WLANInterfaceType=ad-hoc
|
||||
|
||||
[Network]
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
[Match]
|
||||
Type=wlan
|
||||
Type=wifi
|
||||
WLANInterfaceType=ap
|
||||
|
||||
[Network]
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
[Match]
|
||||
Type=wlan
|
||||
Type=wifi
|
||||
WLANInterfaceType=station
|
||||
|
||||
[Network]
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
@ -428,7 +427,7 @@ int local_outbounds(
|
||||
* good chance this fails. Since 5.7 this restriction was dropped and the first
|
||||
* SO_BINDTOINDEX on a socket may be done without privileges. This one has the benefit of
|
||||
* really influencing the routing decision, i.e. this one definitely works for us — as long
|
||||
* as we have the privileges for it. */
|
||||
* as we have the privileges for it.*/
|
||||
r = socket_bind_to_ifindex(fd, gateways[i].ifindex);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to bind socket to interface %i, ignoring: %m", gateways[i].ifindex);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user