mirror of
https://github.com/systemd/systemd
synced 2026-03-14 17:14:49 +01:00
Compare commits
14 Commits
ee3713b71d
...
a346a34f7f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a346a34f7f | ||
|
|
f2147ed5ea | ||
|
|
62a90b48d0 | ||
|
|
71b5738030 | ||
|
|
8fbb1941f1 | ||
|
|
85f088abe8 | ||
|
|
2d0b71b6f6 | ||
|
|
8808d3289e | ||
|
|
2480ca95ba | ||
|
|
b419e8776b | ||
|
|
b8d6689a7f | ||
|
|
933e95d716 | ||
|
|
706875f165 | ||
|
|
098d42b67e |
@ -19,6 +19,12 @@ 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 from local services, and allows local subsystems to provide
|
||||||
user/group records efficiently to local applications.
|
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
|
This simple API only exposes only three method calls, and requires only a small
|
||||||
subset of the Varlink functionality.
|
subset of the Varlink functionality.
|
||||||
|
|
||||||
|
|||||||
@ -75,7 +75,11 @@ Records](https://systemd.io/GROUP_RECORD) that encapsulate UNIX groups.
|
|||||||
|
|
||||||
JSON User Records may be transferred or written to disk in various protocols
|
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
|
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 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.
|
||||||
|
|
||||||
## Why JSON?
|
## Why JSON?
|
||||||
|
|
||||||
|
|||||||
@ -56,6 +56,49 @@
|
|||||||
<filename>/etc/gshadow</filename> based mappings take precedence.</para>
|
<filename>/etc/gshadow</filename> based mappings take precedence.</para>
|
||||||
</refsect1>
|
</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>
|
<refsect1>
|
||||||
<title>Configuration in <filename>/etc/nsswitch.conf</filename></title>
|
<title>Configuration in <filename>/etc/nsswitch.conf</filename></title>
|
||||||
|
|
||||||
|
|||||||
@ -32,7 +32,9 @@
|
|||||||
<para><command>systemd-userdbd</command> is a system service that multiplexes user/group lookups to all
|
<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
|
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
|
JSON user/group records from classic UNIX/glibc NSS user/group records in order to provide full backwards
|
||||||
compatibility.</para>
|
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>
|
||||||
|
|
||||||
<para>Most of <command>systemd-userdbd</command>'s functionality is accessible through the
|
<para>Most of <command>systemd-userdbd</command>'s functionality is accessible through the
|
||||||
<citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
<citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||||
@ -45,16 +47,18 @@
|
|||||||
multiplexes access other services implementing this API, too. It is thus both server and client of this
|
multiplexes access other services implementing this API, too. It is thus both server and client of this
|
||||||
API.</para>
|
API.</para>
|
||||||
|
|
||||||
<para>This service provides two distinct <ulink url="https://varlink.org/">Varlink</ulink> services:
|
<para>This service provides three distinct <ulink url="https://varlink.org/">Varlink</ulink> services:
|
||||||
<constant>io.systemd.Multiplexer</constant> provides a single, unified API for querying JSON user and
|
<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
|
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
|
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
|
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
|
parallel. <constant>io.systemd.NameServiceSwitch</constant> provides compatibility with classic
|
||||||
NSS user records, i.e. converts <type>struct passwd</type> and <type>struct group</type> records as
|
UNIX/glibc NSS user records, i.e. converts <type>struct passwd</type> and <type>struct group</type>
|
||||||
acquired with APIs such as <citerefentry
|
records as acquired with APIs such as <citerefentry
|
||||||
project='man-pages'><refentrytitle>getpwnam</refentrytitle><manvolnum>1</manvolnum></citerefentry> to JSON
|
project='man-pages'><refentrytitle>getpwnam</refentrytitle><manvolnum>1</manvolnum></citerefentry> to
|
||||||
user/group records, thus hiding the differences between the services as much as possible.</para>
|
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>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
|
|||||||
@ -133,7 +133,10 @@
|
|||||||
<term><varname>Type=</varname></term>
|
<term><varname>Type=</varname></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>A whitespace-separated list of shell-style globs matching the device type, as exposed by
|
<para>A whitespace-separated list of shell-style globs matching the device type, as exposed by
|
||||||
<command>networkctl status</command>. If the list is prefixed with a "!", the test is inverted.
|
<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.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
[Match]
|
[Match]
|
||||||
Type=wifi
|
Type=wlan
|
||||||
WLANInterfaceType=ad-hoc
|
WLANInterfaceType=ad-hoc
|
||||||
|
|
||||||
[Network]
|
[Network]
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
[Match]
|
[Match]
|
||||||
Type=wifi
|
Type=wlan
|
||||||
WLANInterfaceType=ap
|
WLANInterfaceType=ap
|
||||||
|
|
||||||
[Network]
|
[Network]
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
[Match]
|
[Match]
|
||||||
Type=wifi
|
Type=wlan
|
||||||
WLANInterfaceType=station
|
WLANInterfaceType=station
|
||||||
|
|
||||||
[Network]
|
[Network]
|
||||||
|
|||||||
@ -81,6 +81,9 @@ static int dhcp_server_emit_changed(Link *link, const char *property, ...) {
|
|||||||
|
|
||||||
assert(link);
|
assert(link);
|
||||||
|
|
||||||
|
if (sd_bus_is_ready(link->manager->bus) <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
path = link_bus_path(link);
|
path = link_bus_path(link);
|
||||||
if (!path)
|
if (!path)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|||||||
@ -2272,6 +2272,9 @@ int manager_connect_bus(Manager *m) {
|
|||||||
int _manager_send_changed(Manager *manager, const char *property, ...) {
|
int _manager_send_changed(Manager *manager, const char *property, ...) {
|
||||||
assert(manager);
|
assert(manager);
|
||||||
|
|
||||||
|
if (sd_bus_is_ready(manager->bus) <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
char **l = strv_from_stdarg_alloca(property);
|
char **l = strv_from_stdarg_alloca(property);
|
||||||
|
|
||||||
int r = sd_bus_emit_properties_changed_strv(
|
int r = sd_bus_emit_properties_changed_strv(
|
||||||
|
|||||||
@ -500,7 +500,7 @@ void dns_zone_item_conflict(DnsZoneItem *i) {
|
|||||||
/* Withdraw the conflict item */
|
/* Withdraw the conflict item */
|
||||||
i->state = DNS_ZONE_ITEM_WITHDRAWN;
|
i->state = DNS_ZONE_ITEM_WITHDRAWN;
|
||||||
|
|
||||||
dnssd_signal_conflict(i->scope->manager, dns_resource_key_name(i->rr->key));
|
(void) dnssd_signal_conflict(i->scope->manager, dns_resource_key_name(i->rr->key));
|
||||||
|
|
||||||
/* Maybe change the hostname */
|
/* Maybe change the hostname */
|
||||||
if (manager_is_own_hostname(i->scope->manager, dns_resource_key_name(i->rr->key)) > 0)
|
if (manager_is_own_hostname(i->scope->manager, dns_resource_key_name(i->rr->key)) > 0)
|
||||||
|
|||||||
@ -329,10 +329,13 @@ int dnssd_txt_item_new_from_data(const char *key, const void *data, const size_t
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dnssd_signal_conflict(Manager *manager, const char *name) {
|
int dnssd_signal_conflict(Manager *manager, const char *name) {
|
||||||
DnssdService *s;
|
DnssdService *s;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
if (sd_bus_is_ready(manager->bus) <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
HASHMAP_FOREACH(s, manager->dnssd_services) {
|
HASHMAP_FOREACH(s, manager->dnssd_services) {
|
||||||
if (s->withdrawn)
|
if (s->withdrawn)
|
||||||
continue;
|
continue;
|
||||||
@ -343,22 +346,20 @@ void dnssd_signal_conflict(Manager *manager, const char *name) {
|
|||||||
s->withdrawn = true;
|
s->withdrawn = true;
|
||||||
|
|
||||||
r = sd_bus_path_encode("/org/freedesktop/resolve1/dnssd", s->name, &path);
|
r = sd_bus_path_encode("/org/freedesktop/resolve1/dnssd", s->name, &path);
|
||||||
if (r < 0) {
|
if (r < 0)
|
||||||
log_error_errno(r, "Can't get D-BUS object path: %m");
|
return log_error_errno(r, "Can't get D-BUS object path: %m");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = sd_bus_emit_signal(manager->bus,
|
r = sd_bus_emit_signal(manager->bus,
|
||||||
path,
|
path,
|
||||||
"org.freedesktop.resolve1.DnssdService",
|
"org.freedesktop.resolve1.DnssdService",
|
||||||
"Conflicted",
|
"Conflicted",
|
||||||
NULL);
|
NULL);
|
||||||
if (r < 0) {
|
if (r < 0)
|
||||||
log_error_errno(r, "Cannot emit signal: %m");
|
return log_error_errno(r, "Cannot emit signal: %m");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
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_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_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_update_rrs(DnssdService *s);
|
||||||
void dnssd_signal_conflict(Manager *manager, const char *name);
|
int dnssd_signal_conflict(Manager *manager, const char *name);
|
||||||
|
|||||||
@ -395,7 +395,8 @@ int local_outbounds(
|
|||||||
sa.in = (struct sockaddr_in) {
|
sa.in = (struct sockaddr_in) {
|
||||||
.sin_family = AF_INET,
|
.sin_family = AF_INET,
|
||||||
.sin_addr = gateways[i].address.in,
|
.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;
|
break;
|
||||||
@ -427,7 +428,7 @@ int local_outbounds(
|
|||||||
* good chance this fails. Since 5.7 this restriction was dropped and the first
|
* 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
|
* 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
|
* 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);
|
r = socket_bind_to_ifindex(fd, gateways[i].ifindex);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
log_debug_errno(r, "Failed to bind socket to interface %i, ignoring: %m", gateways[i].ifindex);
|
log_debug_errno(r, "Failed to bind socket to interface %i, ignoring: %m", gateways[i].ifindex);
|
||||||
|
|||||||
@ -262,6 +262,8 @@ shared_sources = files('''
|
|||||||
user-record.h
|
user-record.h
|
||||||
userdb.c
|
userdb.c
|
||||||
userdb.h
|
userdb.h
|
||||||
|
userdb-dropin.c
|
||||||
|
userdb-dropin.h
|
||||||
utmp-wtmp.h
|
utmp-wtmp.h
|
||||||
varlink.c
|
varlink.c
|
||||||
varlink.h
|
varlink.h
|
||||||
|
|||||||
302
src/shared/userdb-dropin.c
Normal file
302
src/shared/userdb-dropin.c
Normal file
@ -0,0 +1,302 @@
|
|||||||
|
/* 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);
|
||||||
|
}
|
||||||
23
src/shared/userdb-dropin.h
Normal file
23
src/shared/userdb-dropin.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/* 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,10 +2,12 @@
|
|||||||
|
|
||||||
#include <sys/auxv.h>
|
#include <sys/auxv.h>
|
||||||
|
|
||||||
|
#include "conf-files.h"
|
||||||
#include "dirent-util.h"
|
#include "dirent-util.h"
|
||||||
#include "dlfcn-util.h"
|
#include "dlfcn-util.h"
|
||||||
#include "errno-util.h"
|
#include "errno-util.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
|
#include "format-util.h"
|
||||||
#include "missing_syscall.h"
|
#include "missing_syscall.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
#include "set.h"
|
#include "set.h"
|
||||||
@ -13,6 +15,7 @@
|
|||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
#include "user-record-nss.h"
|
#include "user-record-nss.h"
|
||||||
#include "user-util.h"
|
#include "user-util.h"
|
||||||
|
#include "userdb-dropin.h"
|
||||||
#include "userdb.h"
|
#include "userdb.h"
|
||||||
#include "varlink.h"
|
#include "varlink.h"
|
||||||
|
|
||||||
@ -31,9 +34,12 @@ struct UserDBIterator {
|
|||||||
Set *links;
|
Set *links;
|
||||||
bool nss_covered:1;
|
bool nss_covered:1;
|
||||||
bool nss_iterating:1;
|
bool nss_iterating:1;
|
||||||
|
bool dropin_covered:1;
|
||||||
bool synthesize_root:1;
|
bool synthesize_root:1;
|
||||||
bool synthesize_nobody:1;
|
bool synthesize_nobody:1;
|
||||||
bool nss_systemd_blocked:1;
|
bool nss_systemd_blocked:1;
|
||||||
|
char **dropins;
|
||||||
|
size_t current_dropin;
|
||||||
int error;
|
int error;
|
||||||
unsigned n_found;
|
unsigned n_found;
|
||||||
sd_event *event;
|
sd_event *event;
|
||||||
@ -43,7 +49,7 @@ struct UserDBIterator {
|
|||||||
char *found_user_name, *found_group_name; /* when .what == LOOKUP_MEMBERSHIP */
|
char *found_user_name, *found_group_name; /* when .what == LOOKUP_MEMBERSHIP */
|
||||||
char **members_of_group;
|
char **members_of_group;
|
||||||
size_t index_members_of_group;
|
size_t index_members_of_group;
|
||||||
char *filter_user_name;
|
char *filter_user_name, *filter_group_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
UserDBIterator* userdb_iterator_free(UserDBIterator *iterator) {
|
UserDBIterator* userdb_iterator_free(UserDBIterator *iterator) {
|
||||||
@ -51,6 +57,7 @@ UserDBIterator* userdb_iterator_free(UserDBIterator *iterator) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
set_free(iterator->links);
|
set_free(iterator->links);
|
||||||
|
strv_free(iterator->dropins);
|
||||||
|
|
||||||
switch (iterator->what) {
|
switch (iterator->what) {
|
||||||
|
|
||||||
@ -75,6 +82,7 @@ UserDBIterator* userdb_iterator_free(UserDBIterator *iterator) {
|
|||||||
free(iterator->found_group_name);
|
free(iterator->found_group_name);
|
||||||
strv_free(iterator->members_of_group);
|
strv_free(iterator->members_of_group);
|
||||||
free(iterator->filter_user_name);
|
free(iterator->filter_user_name);
|
||||||
|
free(iterator->filter_group_name);
|
||||||
|
|
||||||
if (iterator->nss_iterating)
|
if (iterator->nss_iterating)
|
||||||
endgrent();
|
endgrent();
|
||||||
@ -425,7 +433,7 @@ static int userdb_start_query(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* First, let's talk to the multiplexer, if we can */
|
/* First, let's talk to the multiplexer, if we can */
|
||||||
if ((flags & (USERDB_AVOID_MULTIPLEXER|USERDB_EXCLUDE_DYNAMIC_USER|USERDB_EXCLUDE_NSS|USERDB_DONT_SYNTHESIZE)) == 0 &&
|
if ((flags & (USERDB_AVOID_MULTIPLEXER|USERDB_EXCLUDE_DYNAMIC_USER|USERDB_EXCLUDE_NSS|USERDB_EXCLUDE_DROPIN|USERDB_DONT_SYNTHESIZE)) == 0 &&
|
||||||
!strv_contains(except, "io.systemd.Multiplexer") &&
|
!strv_contains(except, "io.systemd.Multiplexer") &&
|
||||||
(!only || strv_contains(only, "io.systemd.Multiplexer"))) {
|
(!only || strv_contains(only, "io.systemd.Multiplexer"))) {
|
||||||
_cleanup_(json_variant_unrefp) JsonVariant *patched_query = json_variant_ref(query);
|
_cleanup_(json_variant_unrefp) JsonVariant *patched_query = json_variant_ref(query);
|
||||||
@ -437,6 +445,7 @@ static int userdb_start_query(
|
|||||||
r = userdb_connect(iterator, "/run/systemd/userdb/io.systemd.Multiplexer", method, more, patched_query);
|
r = userdb_connect(iterator, "/run/systemd/userdb/io.systemd.Multiplexer", method, more, patched_query);
|
||||||
if (r >= 0) {
|
if (r >= 0) {
|
||||||
iterator->nss_covered = true; /* The multiplexer does NSS */
|
iterator->nss_covered = true; /* The multiplexer does NSS */
|
||||||
|
iterator->dropin_covered = true; /* It also handles drop-in stuff */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -452,7 +461,7 @@ static int userdb_start_query(
|
|||||||
FOREACH_DIRENT(de, d, return -errno) {
|
FOREACH_DIRENT(de, d, return -errno) {
|
||||||
_cleanup_(json_variant_unrefp) JsonVariant *patched_query = NULL;
|
_cleanup_(json_variant_unrefp) JsonVariant *patched_query = NULL;
|
||||||
_cleanup_free_ char *p = NULL;
|
_cleanup_free_ char *p = NULL;
|
||||||
bool is_nss;
|
bool is_nss, is_dropin;
|
||||||
|
|
||||||
if (streq(de->d_name, "io.systemd.Multiplexer")) /* We already tried this above, don't try this again */
|
if (streq(de->d_name, "io.systemd.Multiplexer")) /* We already tried this above, don't try this again */
|
||||||
continue;
|
continue;
|
||||||
@ -469,6 +478,11 @@ static int userdb_start_query(
|
|||||||
if ((flags & (USERDB_EXCLUDE_NSS|USERDB_AVOID_MULTIPLEXER)) && is_nss)
|
if ((flags & (USERDB_EXCLUDE_NSS|USERDB_AVOID_MULTIPLEXER)) && is_nss)
|
||||||
continue;
|
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))
|
if (strv_contains(except, de->d_name))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -485,9 +499,11 @@ static int userdb_start_query(
|
|||||||
return log_debug_errno(r, "Unable to set service JSON field: %m");
|
return log_debug_errno(r, "Unable to set service JSON field: %m");
|
||||||
|
|
||||||
r = userdb_connect(iterator, p, method, more, patched_query);
|
r = userdb_connect(iterator, p, method, more, patched_query);
|
||||||
if (is_nss && r >= 0) /* Turn off fallback NSS if we found the NSS service and could connect
|
if (is_nss && r >= 0) /* Turn off fallback NSS + dropin if we found the NSS/dropin service
|
||||||
* to it */
|
* and could connect to it */
|
||||||
iterator->nss_covered = true;
|
iterator->nss_covered = true;
|
||||||
|
if (is_dropin && r >= 0)
|
||||||
|
iterator->dropin_covered = true;
|
||||||
|
|
||||||
if (ret == 0 && r < 0)
|
if (ret == 0 && r < 0)
|
||||||
ret = r;
|
ret = r;
|
||||||
@ -624,6 +640,12 @@ int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret) {
|
|||||||
return r;
|
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) {
|
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && !iterator->nss_covered) {
|
||||||
/* Make sure the NSS lookup doesn't recurse back to us. */
|
/* Make sure the NSS lookup doesn't recurse back to us. */
|
||||||
|
|
||||||
@ -671,6 +693,12 @@ int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret) {
|
|||||||
return r;
|
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) {
|
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && !iterator->nss_covered) {
|
||||||
r = userdb_iterator_block_nss_systemd(iterator);
|
r = userdb_iterator_block_nss_systemd(iterator);
|
||||||
if (r >= 0) {
|
if (r >= 0) {
|
||||||
@ -694,7 +722,7 @@ int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret) {
|
|||||||
|
|
||||||
int userdb_all(UserDBFlags flags, UserDBIterator **ret) {
|
int userdb_all(UserDBFlags flags, UserDBIterator **ret) {
|
||||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||||
int r;
|
int r, qr;
|
||||||
|
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
|
||||||
@ -704,17 +732,33 @@ int userdb_all(UserDBFlags flags, UserDBIterator **ret) {
|
|||||||
|
|
||||||
iterator->synthesize_root = iterator->synthesize_nobody = !FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE);
|
iterator->synthesize_root = iterator->synthesize_nobody = !FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE);
|
||||||
|
|
||||||
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", true, NULL, flags);
|
qr = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", true, NULL, flags);
|
||||||
|
|
||||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (r < 0 || !iterator->nss_covered)) {
|
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (qr < 0 || !iterator->nss_covered)) {
|
||||||
r = userdb_iterator_block_nss_systemd(iterator);
|
r = userdb_iterator_block_nss_systemd(iterator);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
setpwent();
|
setpwent();
|
||||||
iterator->nss_iterating = true;
|
iterator->nss_iterating = true;
|
||||||
} else if (r < 0)
|
}
|
||||||
return r;
|
|
||||||
|
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;
|
||||||
|
|
||||||
*ret = TAKE_PTR(iterator);
|
*ret = TAKE_PTR(iterator);
|
||||||
return 0;
|
return 0;
|
||||||
@ -772,9 +816,45 @@ int userdb_iterator_get(UserDBIterator *iterator, UserRecord **ret) {
|
|||||||
endpwent();
|
endpwent();
|
||||||
}
|
}
|
||||||
|
|
||||||
r = userdb_process(iterator, ret, NULL, NULL, NULL);
|
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) {
|
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 (iterator->synthesize_root) {
|
if (iterator->synthesize_root) {
|
||||||
iterator->synthesize_root = false;
|
iterator->synthesize_root = false;
|
||||||
iterator->n_found++;
|
iterator->n_found++;
|
||||||
@ -835,6 +915,13 @@ int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret) {
|
|||||||
return r;
|
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)) {
|
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && !(iterator && iterator->nss_covered)) {
|
||||||
r = userdb_iterator_block_nss_systemd(iterator);
|
r = userdb_iterator_block_nss_systemd(iterator);
|
||||||
if (r >= 0) {
|
if (r >= 0) {
|
||||||
@ -879,6 +966,12 @@ int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret) {
|
|||||||
return r;
|
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)) {
|
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && !(iterator && iterator->nss_covered)) {
|
||||||
r = userdb_iterator_block_nss_systemd(iterator);
|
r = userdb_iterator_block_nss_systemd(iterator);
|
||||||
if (r >= 0) {
|
if (r >= 0) {
|
||||||
@ -901,7 +994,7 @@ int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret) {
|
|||||||
|
|
||||||
int groupdb_all(UserDBFlags flags, UserDBIterator **ret) {
|
int groupdb_all(UserDBFlags flags, UserDBIterator **ret) {
|
||||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||||
int r;
|
int r, qr;
|
||||||
|
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
|
||||||
@ -911,17 +1004,32 @@ int groupdb_all(UserDBFlags flags, UserDBIterator **ret) {
|
|||||||
|
|
||||||
iterator->synthesize_root = iterator->synthesize_nobody = !FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE);
|
iterator->synthesize_root = iterator->synthesize_nobody = !FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE);
|
||||||
|
|
||||||
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", true, NULL, flags);
|
qr = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", true, NULL, flags);
|
||||||
|
|
||||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (r < 0 || !iterator->nss_covered)) {
|
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (qr < 0 || !iterator->nss_covered)) {
|
||||||
r = userdb_iterator_block_nss_systemd(iterator);
|
r = userdb_iterator_block_nss_systemd(iterator);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
setgrent();
|
setgrent();
|
||||||
iterator->nss_iterating = true;
|
iterator->nss_iterating = true;
|
||||||
} else if (r < 0)
|
}
|
||||||
return r;
|
|
||||||
|
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;
|
||||||
|
|
||||||
*ret = TAKE_PTR(iterator);
|
*ret = TAKE_PTR(iterator);
|
||||||
return 0;
|
return 0;
|
||||||
@ -977,6 +1085,36 @@ int groupdb_iterator_get(UserDBIterator *iterator, GroupRecord **ret) {
|
|||||||
endgrent();
|
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);
|
r = userdb_process(iterator, NULL, ret, NULL, NULL);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
if (iterator->synthesize_root) {
|
if (iterator->synthesize_root) {
|
||||||
@ -999,10 +1137,23 @@ int groupdb_iterator_get(UserDBIterator *iterator, GroupRecord **ret) {
|
|||||||
return r;
|
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) {
|
int membershipdb_by_user(const char *name, UserDBFlags flags, UserDBIterator **ret) {
|
||||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||||
_cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
|
_cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
|
||||||
int r;
|
int r, qr;
|
||||||
|
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
|
||||||
@ -1018,34 +1169,37 @@ int membershipdb_by_user(const char *name, UserDBFlags flags, UserDBIterator **r
|
|||||||
if (!iterator)
|
if (!iterator)
|
||||||
return -ENOMEM;
|
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;
|
|
||||||
|
|
||||||
r = userdb_iterator_block_nss_systemd(iterator);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
iterator->filter_user_name = strdup(name);
|
iterator->filter_user_name = strdup(name);
|
||||||
if (!iterator->filter_user_name)
|
if (!iterator->filter_user_name)
|
||||||
return -ENOMEM;
|
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)) {
|
||||||
|
r = userdb_iterator_block_nss_systemd(iterator);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
setgrent();
|
setgrent();
|
||||||
iterator->nss_iterating = true;
|
iterator->nss_iterating = true;
|
||||||
|
}
|
||||||
|
|
||||||
r = 0;
|
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;
|
||||||
|
|
||||||
finish:
|
|
||||||
if (r >= 0)
|
|
||||||
*ret = TAKE_PTR(iterator);
|
*ret = TAKE_PTR(iterator);
|
||||||
return r;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int membershipdb_by_group(const char *name, UserDBFlags flags, UserDBIterator **ret) {
|
int membershipdb_by_group(const char *name, UserDBFlags flags, UserDBIterator **ret) {
|
||||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||||
_cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
|
_cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
|
||||||
_cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
|
int r, qr;
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
|
||||||
@ -1061,9 +1215,14 @@ int membershipdb_by_group(const char *name, UserDBFlags flags, UserDBIterator **
|
|||||||
if (!iterator)
|
if (!iterator)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, query, flags);
|
iterator->filter_group_name = strdup(name);
|
||||||
if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_EXCLUDE_NSS))
|
if (!iterator->filter_group_name)
|
||||||
goto finish;
|
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_iterator_block_nss_systemd(iterator);
|
r = userdb_iterator_block_nss_systemd(iterator);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -1082,19 +1241,23 @@ int membershipdb_by_group(const char *name, UserDBFlags flags, UserDBIterator **
|
|||||||
if (!iterator->found_group_name)
|
if (!iterator->found_group_name)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
r = 0;
|
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;
|
||||||
|
|
||||||
finish:
|
|
||||||
if (r >= 0)
|
|
||||||
*ret = TAKE_PTR(iterator);
|
*ret = TAKE_PTR(iterator);
|
||||||
|
return 0;
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int membershipdb_all(UserDBFlags flags, UserDBIterator **ret) {
|
int membershipdb_all(UserDBFlags flags, UserDBIterator **ret) {
|
||||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||||
int r;
|
int r, qr;
|
||||||
|
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
|
||||||
@ -1102,24 +1265,27 @@ int membershipdb_all(UserDBFlags flags, UserDBIterator **ret) {
|
|||||||
if (!iterator)
|
if (!iterator)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, NULL, flags);
|
qr = 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);
|
r = userdb_iterator_block_nss_systemd(iterator);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
setgrent();
|
setgrent();
|
||||||
iterator->nss_iterating = true;
|
iterator->nss_iterating = true;
|
||||||
|
}
|
||||||
|
|
||||||
r = 0;
|
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;
|
||||||
|
|
||||||
finish:
|
|
||||||
if (r >= 0)
|
|
||||||
*ret = TAKE_PTR(iterator);
|
*ret = TAKE_PTR(iterator);
|
||||||
|
return 0;
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int membershipdb_iterator_get(
|
int membershipdb_iterator_get(
|
||||||
@ -1205,6 +1371,48 @@ int membershipdb_iterator_get(
|
|||||||
iterator->found_group_name = mfree(iterator->found_group_name);
|
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);
|
r = userdb_process(iterator, NULL, NULL, ret_user, ret_group);
|
||||||
if (r < 0 && iterator->n_found > 0)
|
if (r < 0 && iterator->n_found > 0)
|
||||||
return -ESRCH;
|
return -ESRCH;
|
||||||
|
|||||||
@ -18,6 +18,7 @@ typedef enum UserDBFlags {
|
|||||||
/* The main sources */
|
/* The main sources */
|
||||||
USERDB_EXCLUDE_NSS = 1 << 0, /* don't do client-side nor server-side NSS */
|
USERDB_EXCLUDE_NSS = 1 << 0, /* don't do client-side nor server-side NSS */
|
||||||
USERDB_EXCLUDE_VARLINK = 1 << 1, /* don't talk to any varlink services */
|
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 */
|
/* Modifications */
|
||||||
USERDB_SUPPRESS_SHADOW = 1 << 3, /* don't do client-side shadow calls (server side might happen though) */
|
USERDB_SUPPRESS_SHADOW = 1 << 3, /* don't do client-side shadow calls (server side might happen though) */
|
||||||
@ -26,9 +27,18 @@ typedef enum UserDBFlags {
|
|||||||
USERDB_DONT_SYNTHESIZE = 1 << 6, /* don't synthesize root/nobody */
|
USERDB_DONT_SYNTHESIZE = 1 << 6, /* don't synthesize root/nobody */
|
||||||
|
|
||||||
/* Combinations */
|
/* Combinations */
|
||||||
USERDB_NSS_ONLY = USERDB_EXCLUDE_VARLINK|USERDB_DONT_SYNTHESIZE,
|
USERDB_NSS_ONLY = USERDB_EXCLUDE_VARLINK|USERDB_EXCLUDE_DROPIN|USERDB_DONT_SYNTHESIZE,
|
||||||
|
USERDB_DROPIN_ONLY = USERDB_EXCLUDE_NSS|USERDB_EXCLUDE_VARLINK|USERDB_DONT_SYNTHESIZE,
|
||||||
} UserDBFlags;
|
} 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_name(const char *name, UserDBFlags flags, UserRecord **ret);
|
||||||
int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret);
|
int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret);
|
||||||
int userdb_all(UserDBFlags flags, UserDBIterator **ret);
|
int userdb_all(UserDBFlags flags, UserDBIterator **ret);
|
||||||
|
|||||||
@ -607,7 +607,13 @@ 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,
|
m->poll_interval_usec / USEC_PER_SEC, offset, delay, m->samples_jitter, m->drift_freq / 65536,
|
||||||
spike ? " (ignored)" : "");
|
spike ? " (ignored)" : "");
|
||||||
|
|
||||||
(void) sd_bus_emit_properties_changed(m->bus, "/org/freedesktop/timesync1", "org.freedesktop.timesync1.Manager", "NTPMessage", NULL);
|
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);
|
||||||
|
|
||||||
if (!m->good) {
|
if (!m->good) {
|
||||||
_cleanup_free_ char *pretty = NULL;
|
_cleanup_free_ char *pretty = NULL;
|
||||||
|
|||||||
@ -289,6 +289,11 @@ int manager_startup(Manager *m) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to bind io.systemd.Multiplexer: %m");
|
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)
|
if (listen(m->listen_fd, SOMAXCONN) < 0)
|
||||||
return log_error_errno(errno, "Failed to listen on socket: %m");
|
return log_error_errno(errno, "Failed to listen on socket: %m");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,9 @@
|
|||||||
* → io.systemd.Multiplexer: this multiplexes lookup requests to all Varlink services that have a
|
* → 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
|
* socket in /run/systemd/userdb/. It's supposed to simplify clients that don't want to implement
|
||||||
* the full iterative logic on their own.
|
* 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[]) {
|
static int run(int argc, char *argv[]) {
|
||||||
@ -31,8 +34,8 @@ static int run(int argc, char *argv[]) {
|
|||||||
if (argc != 1)
|
if (argc != 1)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
|
||||||
|
|
||||||
if (setenv("SYSTEMD_BYPASS_USERDB", "io.systemd.NameServiceSwitch:io.systemd.Multiplexer", 1) < 0)
|
if (setenv("SYSTEMD_BYPASS_USERDB", "io.systemd.NameServiceSwitch:io.systemd.Multiplexer:io.systemd.DropIn", 1) < 0)
|
||||||
return log_error_errno(errno, "Failed to se $SYSTEMD_BYPASS_USERDB: %m");
|
return log_error_errno(errno, "Failed to set $SYSTEMD_BYPASS_USERDB: %m");
|
||||||
|
|
||||||
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, SIGUSR2, -1) >= 0);
|
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, SIGUSR2, -1) >= 0);
|
||||||
|
|
||||||
|
|||||||
@ -120,6 +120,8 @@ static int userdb_flags_from_service(Varlink *link, const char *service, UserDBF
|
|||||||
|
|
||||||
if (streq_ptr(service, "io.systemd.NameServiceSwitch"))
|
if (streq_ptr(service, "io.systemd.NameServiceSwitch"))
|
||||||
*ret = USERDB_NSS_ONLY|USERDB_AVOID_MULTIPLEXER;
|
*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"))
|
else if (streq_ptr(service, "io.systemd.Multiplexer"))
|
||||||
*ret = USERDB_AVOID_MULTIPLEXER;
|
*ret = USERDB_AVOID_MULTIPLEXER;
|
||||||
else
|
else
|
||||||
|
|||||||
@ -15,7 +15,7 @@ Before=sockets.target
|
|||||||
|
|
||||||
[Socket]
|
[Socket]
|
||||||
ListenStream=/run/systemd/userdb/io.systemd.Multiplexer
|
ListenStream=/run/systemd/userdb/io.systemd.Multiplexer
|
||||||
Symlinks=/run/systemd/userdb/io.systemd.NameServiceSwitch
|
Symlinks=/run/systemd/userdb/io.systemd.NameServiceSwitch /run/systemd/userdb/io.systemd.DropIn
|
||||||
SocketMode=0666
|
SocketMode=0666
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user