diff --git a/README b/README
index 4269f0c73d..558b8d9195 100644
--- a/README
+++ b/README
@@ -262,19 +262,19 @@ USERS AND GROUPS:
NSS:
systemd ships with four glibc NSS modules:
- nss-myhostname resolves the local hostname to locally
- configured IP addresses, as well as "localhost" to
- 127.0.0.1/::1.
+ nss-myhostname resolves the local hostname to locally configured IP
+ addresses, as well as "localhost" to 127.0.0.1/::1.
- nss-resolve enables DNS resolution via the systemd-resolved
- DNS/LLMNR caching stub resolver "systemd-resolved".
+ nss-resolve enables DNS resolution via the systemd-resolved DNS/LLMNR
+ caching stub resolver "systemd-resolved".
nss-mymachines enables resolution of all local containers registered
- with machined to their respective IP addresses. It also maps UID/GIDs
- ranges used by containers to useful names.
+ with machined to their respective IP addresses.
- nss-systemd enables resolution of all dynamically allocated service
- users. (See the DynamicUser= setting in unit files.)
+ nss-systemd enables resolution of users/group registered via the
+ User/Group Record Lookup API (https://systemd.io/USER_GROUP_API/),
+ including all dynamically allocated service users. (See the
+ DynamicUser= setting in unit files.)
To make use of these NSS modules, please add them to the "hosts:",
"passwd:" and "group:" lines in /etc/nsswitch.conf. The "resolve"
@@ -283,8 +283,8 @@ NSS:
The four modules should be used in the following order:
- passwd: compat mymachines systemd
- group: compat mymachines systemd
+ passwd: compat systemd
+ group: compat systemd
hosts: files mymachines resolve [!UNAVAIL=return] dns myhostname
SYSV INIT.D SCRIPTS:
diff --git a/TODO b/TODO
index 4e4ec5a1c8..31eff4f633 100644
--- a/TODO
+++ b/TODO
@@ -336,7 +336,6 @@ Features:
* homed:
- when user tries to log into record signed by unrecognized key, automatically add key to our chain after polkit auth
- - hook up machined/nspawn users with a varlink user query interface
- rollback when resize fails mid-operation
- GNOME's side for forget key on suspend (requires rework so that lock screen runs outside of uid)
- resize on login?
diff --git a/docs/UIDS-GIDS.md b/docs/UIDS-GIDS.md
index c41c4d7eec..67e6d083ff 100644
--- a/docs/UIDS-GIDS.md
+++ b/docs/UIDS-GIDS.md
@@ -132,7 +132,7 @@ but downstreams are strongly advised against doing that.)
range is above the 16bit boundary. Moreover it's below the 31bit boundary,
as some broken code (specifically: the kernel's `devpts` file system)
erroneously considers UIDs signed integers, and hence can't deal with values
- above 2^31. The `nss-mymachines` glibc NSS module will synthesize user
+ above 2^31. The `systemd-machined.service` service will synthesize user
database records for all UIDs assigned to a running container from this
range.
@@ -240,14 +240,14 @@ the artifacts the container manager persistently leaves in the system.
| 5 | `tty` group | `systemd` | `/etc/passwd` |
| 6…999 | System users | Distributions | `/etc/passwd` |
| 1000…60000 | Regular users | Distributions | `/etc/passwd` + LDAP/NIS/… |
-| 60001…60513 | Human Users (homed) | `systemd` | `nss-systemd`
+| 60001…60513 | Human Users (homed) | `systemd` | `nss-systemd` |
| 60514…61183 | Unused | | |
| 61184…65519 | Dynamic service users | `systemd` | `nss-systemd` |
| 65520…65533 | Unused | | |
| 65534 | `nobody` user | Linux | `/etc/passwd` + `nss-systemd` |
| 65535 | 16bit `(uid_t) -1` | Linux | |
| 65536…524287 | Unused | | |
-| 524288…1879048191 | Container UID ranges | `systemd` | `nss-mymachines` |
+| 524288…1879048191 | Container UID ranges | `systemd` | `nss-systemd` |
| 1879048192…2147483647 | Unused | | |
| 2147483648…4294967294 | HIC SVNT LEONES | | |
| 4294967295 | 32bit `(uid_t) -1` | Linux | |
diff --git a/docs/USER_GROUP_API.md b/docs/USER_GROUP_API.md
index ebff6e6903..ca88b3a16d 100644
--- a/docs/USER_GROUP_API.md
+++ b/docs/USER_GROUP_API.md
@@ -108,7 +108,7 @@ example, introspection is not available, and the resolver logic is not used.
## Other Services
-The `systemd` project provides two other services implementing this
+The `systemd` project provides three other services implementing this
interface. Specifically:
1. `io.systemd.DynamicUser` → This service is implemented by the service
@@ -119,6 +119,10 @@ interface. Specifically:
and provides records for the users and groups defined by the home
directories it manages.
+3. `io.systemd.Machine` → This service is implemented by
+ `systemd-machined.service` and provides records for the users and groups used
+ by local containers that use user namespacing.
+
Other projects are invited to implement these services too. For example it
would make sense for LDAP/ActiveDirectory projects to implement these
interfaces, which would provide them a way to do per-user resource management
@@ -185,6 +189,7 @@ error NoRecordFound()
error BadService()
error ServiceNotAvailable()
error ConflictingRecordFound()
+error EnumerationNotSupported()
```
The `GetUserRecord` method looks up or enumerates a user record. If the `uid`
@@ -264,4 +269,11 @@ services. Result of this is that it can be one service that defines a user A,
and another service that defines a group B, and a third service that declares
that A is a member of B.
+Looking up explicit users/groups by their name or UID/GID, or querying
+user/group memberships must be supported by all services implementing these
+interfaces. However, supporting enumeration (i.e. user/group lookups that may
+result in more than one reply, because neither UID/GID nor name is specified)
+is optional. Services which are asked for enumeration may return the
+`EnumerationNotSupported` error in this case.
+
And that's really all there is to it.
diff --git a/factory/etc/nsswitch.conf b/factory/etc/nsswitch.conf
index e7365cd142..da74b19d90 100644
--- a/factory/etc/nsswitch.conf
+++ b/factory/etc/nsswitch.conf
@@ -1,7 +1,7 @@
# This file is part of systemd.
-passwd: compat mymachines systemd
-group: compat [SUCCESS=merge] mymachines [SUCCESS=merge] systemd
+passwd: compat systemd
+group: compat [SUCCESS=merge] systemd
shadow: compat
hosts: files mymachines resolve [!UNAVAIL=return] dns myhostname
diff --git a/man/nss-myhostname.xml b/man/nss-myhostname.xml
index 8bb21f2621..a41c383bb3 100644
--- a/man/nss-myhostname.xml
+++ b/man/nss-myhostname.xml
@@ -82,8 +82,8 @@
nss-myhostname correctly:
-passwd: compat mymachines systemd
-group: compat mymachines systemd
+passwd: compat systemd
+group: compat systemd
shadow: compat
# Either (untrusted network):
diff --git a/man/nss-mymachines.xml b/man/nss-mymachines.xml
index 6e48835550..e0e6989c40 100644
--- a/man/nss-mymachines.xml
+++ b/man/nss-mymachines.xml
@@ -39,22 +39,13 @@
Note that the name that is resolved is the one registered with systemd-machined, which
may be different than the hostname configured inside of the container.
- The module also provides name resolution for user and group identifiers mapped to containers. All names from
- the range allocated to a given container container are exposed on the host as
- vu-container-uid and
- vg-container-gid (see example below). This
- functionality only applies to containers using user namespacing (see the description of
- in
- systemd-nspawn1).
-
- To activate the NSS module, add mymachines to the lines starting with
- hosts:, passwd: and group: in
- /etc/nsswitch.conf.
+ To activate the NSS module, add mymachines to the line starting with
+ hosts: in /etc/nsswitch.conf.
It is recommended to place mymachines after the files or
- compat entry of the /etc/nsswitch.conf lines to make sure that its mappings
- are preferred over other resolvers such as DNS, but so that /etc/hosts,
- /etc/passwd and /etc/group based mappings take precedence.
+ compat entry of the /etc/nsswitch.conf line to make sure that its
+ mappings are preferred over other resolvers such as DNS, but so that /etc/hosts
+ based mappings take precedence.
@@ -64,8 +55,8 @@
nss-mymachines correctly:
- passwd: compat mymachines systemd
-group: compat mymachines systemd
+ passwd: compat systemd
+group: compat systemd
shadow: compat
hosts: mymachines resolve [!UNAVAIL=return] myhostname files dns
@@ -81,7 +72,7 @@ netgroup: nis
- Mappings provided by nss-mymachines
+ Example: Mappings provided by nss-mymachines
The container rawhide is spawned using
systemd-nspawn1:
@@ -96,29 +87,6 @@ $ machinectl --max-addresses=3
MACHINE CLASS SERVICE OS VERSION ADDRESSES
rawhide container systemd-nspawn fedora 30 169.254.40.164 fe80::94aa:3aff:fe7b:d4b9
-$ getent passwd vu-rawhide-0 vu-rawhide-81
-vu-rawhide-0:*:20119552:65534:vu-rawhide-0:/:/usr/sbin/nologin
-vu-rawhide-81:*:20119633:65534:vu-rawhide-81:/:/usr/sbin/nologin
-
-$ getent group vg-rawhide-0 vg-rawhide-81
-vg-rawhide-0:*:20119552:
-vg-rawhide-81:*:20119633:
-
-$ ps -o user:15,pid,tty,command -e|grep '^vu-rawhide'
-vu-rawhide-0 692 ? /usr/lib/systemd/systemd
-vu-rawhide-0 731 ? /usr/lib/systemd/systemd-journald
-vu-rawhide-192 734 ? /usr/lib/systemd/systemd-networkd
-vu-rawhide-193 738 ? /usr/lib/systemd/systemd-resolved
-vu-rawhide-0 742 ? /usr/lib/systemd/systemd-logind
-vu-rawhide-81 744 ? /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
-vu-rawhide-0 746 ? /usr/sbin/sshd -D ...
-vu-rawhide-0 752 ? /usr/lib/systemd/systemd --user
-vu-rawhide-0 753 ? (sd-pam)
-vu-rawhide-0 1628 ? login -- zbyszek
-vu-rawhide-1000 1630 ? /usr/lib/systemd/systemd --user
-vu-rawhide-1000 1631 ? (sd-pam)
-vu-rawhide-1000 1637 pts/8 -zsh
-
$ ping -c1 rawhide
PING rawhide(fe80::94aa:3aff:fe7b:d4b9%ve-rawhide (fe80::94aa:3aff:fe7b:d4b9%ve-rawhide)) 56 data bytes
64 bytes from fe80::94aa:3aff:fe7b:d4b9%ve-rawhide (fe80::94aa:3aff:fe7b:d4b9%ve-rawhide): icmp_seq=1 ttl=64 time=0.045 ms
diff --git a/man/nss-resolve.xml b/man/nss-resolve.xml
index 332cf0b06d..c377468953 100644
--- a/man/nss-resolve.xml
+++ b/man/nss-resolve.xml
@@ -63,8 +63,8 @@
correctly:
-passwd: compat mymachines systemd
-group: compat mymachines systemd
+passwd: compat systemd
+group: compat systemd
shadow: compat
hosts: mymachines resolve [!UNAVAIL=return] myhostname files dns
diff --git a/man/nss-systemd.xml b/man/nss-systemd.xml
index 26730f0fe9..34aee0e880 100644
--- a/man/nss-systemd.xml
+++ b/man/nss-systemd.xml
@@ -35,8 +35,8 @@
systemd1 (for its
DynamicUser= feature, see
systemd.exec5 for
- details) or
- systemd-homed.service8.
+ details),
+ systemd-homed.service8, or systemd-machined.service8.
This module also ensures that the root and nobody users and groups (i.e. the users/groups with the UIDs/GIDs
0 and 65534) remain resolvable at all times, even if they aren't listed in /etc/passwd or
@@ -55,14 +55,14 @@
- Example
+ Configuration in /etc/nsswitch.conf
Here is an example /etc/nsswitch.conf file that enables
nss-systemd correctly:
- passwd: compat mymachines systemd
-group: compat [SUCCESS=merge] mymachines [SUCCESS=merge] systemd
+ passwd: compat systemd
+group: compat [SUCCESS=merge] systemd
shadow: compat
hosts: mymachines resolve [!UNAVAIL=return] myhostname files dns
@@ -77,6 +77,47 @@ netgroup: nis
+
+ Example: Mappings provided by systemd-machined.service
+
+ The container rawhide is spawned using
+ systemd-nspawn1:
+
+
+ # systemd-nspawn -M rawhide --boot --network-veth --private-users=pick
+Spawning container rawhide on /var/lib/machines/rawhide.
+Selected user namespace base 20119552 and range 65536.
+...
+
+$ machinectl --max-addresses=3
+MACHINE CLASS SERVICE OS VERSION ADDRESSES
+rawhide container systemd-nspawn fedora 30 169.254.40.164 fe80::94aa:3aff:fe7b:d4b9
+
+$ getent passwd vu-rawhide-0 vu-rawhide-81
+vu-rawhide-0:*:20119552:65534:vu-rawhide-0:/:/usr/sbin/nologin
+vu-rawhide-81:*:20119633:65534:vu-rawhide-81:/:/usr/sbin/nologin
+
+$ getent group vg-rawhide-0 vg-rawhide-81
+vg-rawhide-0:*:20119552:
+vg-rawhide-81:*:20119633:
+
+$ ps -o user:15,pid,tty,command -e|grep '^vu-rawhide'
+vu-rawhide-0 692 ? /usr/lib/systemd/systemd
+vu-rawhide-0 731 ? /usr/lib/systemd/systemd-journald
+vu-rawhide-192 734 ? /usr/lib/systemd/systemd-networkd
+vu-rawhide-193 738 ? /usr/lib/systemd/systemd-resolved
+vu-rawhide-0 742 ? /usr/lib/systemd/systemd-logind
+vu-rawhide-81 744 ? /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
+vu-rawhide-0 746 ? /usr/sbin/sshd -D ...
+vu-rawhide-0 752 ? /usr/lib/systemd/systemd --user
+vu-rawhide-0 753 ? (sd-pam)
+vu-rawhide-0 1628 ? login -- zbyszek
+vu-rawhide-1000 1630 ? /usr/lib/systemd/systemd --user
+vu-rawhide-1000 1631 ? (sd-pam)
+vu-rawhide-1000 1637 pts/8 -zsh
+
+
+
See Also
@@ -85,6 +126,9 @@ netgroup: nis
nss-resolve8,
nss-myhostname8,
nss-mymachines8,
+ systemd-userdbd.service8,
+ systemd-homed.service8,
+ systemd-machined.service8,
nsswitch.conf5,
getent1
diff --git a/man/systemd-machined.service.xml b/man/systemd-machined.service.xml
index 5172f6511a..a976c606bd 100644
--- a/man/systemd-machined.service.xml
+++ b/man/systemd-machined.service.xml
@@ -116,6 +116,13 @@
systemd-importd.service8
is also available, which implements importing, exporting, and downloading of container and VM images.
+
+ For each container registered with systemd-machined.service that employs user
+ namespacing, users/groups are synthesized for the used UIDs/GIDs. These are made available to the system
+ using the User/Group Record Lookup API via
+ Varlink, and thus may be resolved with
+ userdbctl1 or the
+ usual glibc NSS calls.
diff --git a/man/userdbctl.xml b/man/userdbctl.xml
index 777393ab75..9a69f33edb 100644
--- a/man/userdbctl.xml
+++ b/man/userdbctl.xml
@@ -186,6 +186,15 @@
available to the system.
+
+ io.systemd.Machine
+
+ This service is provided by
+ systemd-machined.service8
+ and synthesizes records for all users/groups used by a container that employs user
+ namespacing.
+
+
io.systemd.Multiplexer
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index e0b52246ba..49bb74ac0f 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -20,14 +20,16 @@
#define procfs_file_alloca(pid, field) \
({ \
pid_t _pid_ = (pid); \
- const char *_r_; \
+ const char *_field_ = (field); \
+ char *_r_; \
if (_pid_ == 0) { \
- _r_ = ("/proc/self/" field); \
+ _r_ = newa(char, STRLEN("/proc/self/") + strlen(_field_) + 1); \
+ strcpy(stpcpy(_r_, "/proc/self/"), _field_); \
} else { \
- _r_ = newa(char, STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \
- sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \
+ _r_ = newa(char, STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + strlen(_field_) + 1); \
+ sprintf(_r_, "/proc/" PID_FMT "/%s", _pid_, _field_); \
} \
- _r_; \
+ (const char*) _r_; \
})
typedef enum ProcessCmdlineFlags {
diff --git a/src/machine/machine.c b/src/machine/machine.c
index 8154c42ed7..ace84edbb4 100644
--- a/src/machine/machine.c
+++ b/src/machine/machine.c
@@ -746,6 +746,143 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) {
return 0;
}
+static int machine_owns_uid_internal(
+ Machine *machine,
+ const char *map_file, /* "uid_map" or "gid_map" */
+ uid_t uid,
+ uid_t *ret_internal_uid) {
+
+ _cleanup_fclose_ FILE *f = NULL;
+ const char *p;
+
+ /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
+ assert_cc(sizeof(uid_t) == sizeof(gid_t));
+
+ assert(machine);
+
+ /* Checks if the specified host UID is owned by the machine, and returns the UID it maps to
+ * internally in the machine */
+
+ if (machine->class != MACHINE_CONTAINER)
+ goto negative;
+
+ p = procfs_file_alloca(machine->leader, map_file);
+ f = fopen(p, "re");
+ if (!f) {
+ log_debug_errno(errno, "Failed to open %s, ignoring.", p);
+ goto negative;
+ }
+
+ for (;;) {
+ uid_t uid_base, uid_shift, uid_range, converted;
+ int k;
+
+ errno = 0;
+ k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
+ if (k < 0 && feof(f))
+ break;
+ if (k != 3) {
+ if (ferror(f))
+ return errno_or_else(EIO);
+
+ return -EIO;
+ }
+
+ /* The private user namespace is disabled, ignoring. */
+ if (uid_shift == 0)
+ continue;
+
+ if (uid < uid_shift || uid >= uid_shift + uid_range)
+ continue;
+
+ converted = (uid - uid_shift + uid_base);
+ if (!uid_is_valid(converted))
+ return -EINVAL;
+
+ if (ret_internal_uid)
+ *ret_internal_uid = converted;
+
+ return true;
+ }
+
+negative:
+ if (ret_internal_uid)
+ *ret_internal_uid = UID_INVALID;
+
+ return false;
+}
+
+int machine_owns_uid(Machine *machine, uid_t uid, uid_t *ret_internal_uid) {
+ return machine_owns_uid_internal(machine, "uid_map", uid, ret_internal_uid);
+}
+
+int machine_owns_gid(Machine *machine, gid_t gid, gid_t *ret_internal_gid) {
+ return machine_owns_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_internal_gid);
+}
+
+static int machine_translate_uid_internal(
+ Machine *machine,
+ const char *map_file, /* "uid_map" or "gid_map" */
+ uid_t uid,
+ uid_t *ret_host_uid) {
+
+ _cleanup_fclose_ FILE *f = NULL;
+ const char *p;
+
+ /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
+ assert_cc(sizeof(uid_t) == sizeof(gid_t));
+
+ assert(machine);
+ assert(uid_is_valid(uid));
+
+ if (machine->class != MACHINE_CONTAINER)
+ return -ESRCH;
+
+ /* Translates a machine UID into a host UID */
+
+ p = procfs_file_alloca(machine->leader, map_file);
+ f = fopen(p, "re");
+ if (!f)
+ return -errno;
+
+ for (;;) {
+ uid_t uid_base, uid_shift, uid_range, converted;
+ int k;
+
+ errno = 0;
+ k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
+ if (k < 0 && feof(f))
+ break;
+ if (k != 3) {
+ if (ferror(f))
+ return errno_or_else(EIO);
+
+ return -EIO;
+ }
+
+ if (uid < uid_base || uid >= uid_base + uid_range)
+ continue;
+
+ converted = uid - uid_base + uid_shift;
+ if (!uid_is_valid(converted))
+ return -EINVAL;
+
+ if (ret_host_uid)
+ *ret_host_uid = converted;
+ return 0;
+ }
+
+ return -ESRCH;
+}
+
+int machine_translate_uid(Machine *machine, gid_t uid, gid_t *ret_host_uid) {
+ return machine_translate_uid_internal(machine, "uid_map", uid, ret_host_uid);
+}
+
+int machine_translate_gid(Machine *machine, gid_t gid, gid_t *ret_host_gid) {
+ return machine_translate_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_host_gid);
+}
+
static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
[MACHINE_CONTAINER] = "container",
[MACHINE_VM] = "vm",
diff --git a/src/machine/machine.h b/src/machine/machine.h
index 0a39e61052..634c5fc648 100644
--- a/src/machine/machine.h
+++ b/src/machine/machine.h
@@ -94,3 +94,9 @@ int machine_openpt(Machine *m, int flags, char **ret_slave);
int machine_open_terminal(Machine *m, const char *path, int mode);
int machine_get_uid_shift(Machine *m, uid_t *ret);
+
+int machine_owns_uid(Machine *m, uid_t host_uid, uid_t *ret_internal_uid);
+int machine_owns_gid(Machine *m, gid_t host_gid, gid_t *ret_internal_gid);
+
+int machine_translate_uid(Machine *m, uid_t internal_uid, uid_t *ret_host_uid);
+int machine_translate_gid(Machine *m, gid_t internal_gid, gid_t *ret_host_gid);
diff --git a/src/machine/machined-core.c b/src/machine/machined-core.c
index 6a404805ea..c44bb94d8a 100644
--- a/src/machine/machined-core.c
+++ b/src/machine/machined-core.c
@@ -3,6 +3,7 @@
#include "machined.h"
#include "nscd-flush.h"
#include "strv.h"
+#include "user-util.h"
static int on_nscd_cache_flush_event(sd_event_source *s, void *userdata) {
/* Let's ask glibc's nscd daemon to flush its caches. We request this for the three database machines may show
@@ -34,3 +35,72 @@ int manager_enqueue_nscd_cache_flush(Manager *m) {
return 0;
}
+
+int manager_find_machine_for_uid(Manager *m, uid_t uid, Machine **ret_machine, uid_t *ret_internal_uid) {
+ Machine *machine;
+ Iterator i;
+ int r;
+
+ assert(m);
+ assert(uid_is_valid(uid));
+
+ /* Finds the machine for the specified host UID and returns it along with the UID translated into the
+ * internal UID inside the machine */
+
+ HASHMAP_FOREACH(machine, m->machines, i) {
+ uid_t converted;
+
+ r = machine_owns_uid(machine, uid, &converted);
+ if (r < 0)
+ return r;
+ if (r) {
+ if (ret_machine)
+ *ret_machine = machine;
+
+ if (ret_internal_uid)
+ *ret_internal_uid = converted;
+
+ return true;
+ }
+ }
+
+ if (ret_machine)
+ *ret_machine = NULL;
+ if (ret_internal_uid)
+ *ret_internal_uid = UID_INVALID;
+
+ return false;
+}
+
+int manager_find_machine_for_gid(Manager *m, gid_t gid, Machine **ret_machine, gid_t *ret_internal_gid) {
+ Machine *machine;
+ Iterator i;
+ int r;
+
+ assert(m);
+ assert(gid_is_valid(gid));
+
+ HASHMAP_FOREACH(machine, m->machines, i) {
+ gid_t converted;
+
+ r = machine_owns_gid(machine, gid, &converted);
+ if (r < 0)
+ return r;
+ if (r) {
+ if (ret_machine)
+ *ret_machine = machine;
+
+ if (ret_internal_gid)
+ *ret_internal_gid = converted;
+
+ return true;
+ }
+ }
+
+ if (ret_machine)
+ *ret_machine = NULL;
+ if (ret_internal_gid)
+ *ret_internal_gid = GID_INVALID;
+
+ return false;
+}
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 2d9c180aa4..467f16b72a 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -883,11 +883,11 @@ static int method_set_image_limit(sd_bus_message *message, void *userdata, sd_bu
}
static int method_map_from_machine_user(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_fclose_ FILE *f = NULL;
Manager *m = userdata;
- const char *name, *p;
+ const char *name;
Machine *machine;
uint32_t uid;
+ uid_t converted;
int r;
r = sd_bus_message_read(message, "su", &name, &uid);
@@ -904,44 +904,20 @@ static int method_map_from_machine_user(sd_bus_message *message, void *userdata,
if (machine->class != MACHINE_CONTAINER)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines.");
- p = procfs_file_alloca(machine->leader, "uid_map");
- f = fopen(p, "re");
- if (!f)
- return -errno;
+ r = machine_translate_uid(machine, uid, &converted);
+ if (r == -ESRCH)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "Machine '%s' has no matching user mappings.", name);
+ if (r < 0)
+ return r;
- for (;;) {
- uid_t uid_base, uid_shift, uid_range, converted;
- int k;
-
- errno = 0;
- k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
- if (k < 0 && feof(f))
- break;
- if (k != 3) {
- if (ferror(f))
- return errno_or_else(EIO);
-
- return -EIO;
- }
-
- if (uid < uid_base || uid >= uid_base + uid_range)
- continue;
-
- converted = uid - uid_base + uid_shift;
- if (!uid_is_valid(converted))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid);
-
- return sd_bus_reply_method_return(message, "u", (uint32_t) converted);
- }
-
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "Machine '%s' has no matching user mappings.", name);
+ return sd_bus_reply_method_return(message, "u", (uint32_t) converted);
}
static int method_map_to_machine_user(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_free_ char *o = NULL;
Manager *m = userdata;
Machine *machine;
- uid_t uid;
- Iterator i;
+ uid_t uid, converted;
int r;
r = sd_bus_message_read(message, "u", &uid);
@@ -952,63 +928,24 @@ static int method_map_to_machine_user(sd_bus_message *message, void *userdata, s
if (uid < 0x10000)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "User " UID_FMT " belongs to host UID range", uid);
- HASHMAP_FOREACH(machine, m->machines, i) {
- _cleanup_fclose_ FILE *f = NULL;
- char p[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
+ r = manager_find_machine_for_uid(m, uid, &machine, &converted);
+ if (r < 0)
+ return r;
+ if (!r)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "No matching user mapping for " UID_FMT ".", uid);
- if (machine->class != MACHINE_CONTAINER)
- continue;
+ o = machine_bus_path(machine);
+ if (!o)
+ return -ENOMEM;
- xsprintf(p, "/proc/" UID_FMT "/uid_map", machine->leader);
- f = fopen(p, "re");
- if (!f) {
- log_warning_errno(errno, "Failed to open %s, ignoring,", p);
- continue;
- }
-
- for (;;) {
- _cleanup_free_ char *o = NULL;
- uid_t uid_base, uid_shift, uid_range, converted;
- int k;
-
- errno = 0;
- k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
- if (k < 0 && feof(f))
- break;
- if (k != 3) {
- if (ferror(f))
- return errno_or_else(EIO);
-
- return -EIO;
- }
-
- /* The private user namespace is disabled, ignoring. */
- if (uid_shift == 0)
- continue;
-
- if (uid < uid_shift || uid >= uid_shift + uid_range)
- continue;
-
- converted = (uid - uid_shift + uid_base);
- if (!uid_is_valid(converted))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid);
-
- o = machine_bus_path(machine);
- if (!o)
- return -ENOMEM;
-
- return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted);
- }
- }
-
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "No matching user mapping for " UID_FMT ".", uid);
+ return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted);
}
-static int method_map_from_machine_group(sd_bus_message *message, void *groupdata, sd_bus_error *error) {
- _cleanup_fclose_ FILE *f = NULL;
- Manager *m = groupdata;
- const char *name, *p;
+static int method_map_from_machine_group(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ const char *name;
Machine *machine;
+ gid_t converted;
uint32_t gid;
int r;
@@ -1026,44 +963,20 @@ static int method_map_from_machine_group(sd_bus_message *message, void *groupdat
if (machine->class != MACHINE_CONTAINER)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines.");
- p = procfs_file_alloca(machine->leader, "gid_map");
- f = fopen(p, "re");
- if (!f)
- return -errno;
+ r = machine_translate_gid(machine, gid, &converted);
+ if (r == -ESRCH)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "Machine '%s' has no matching group mappings.", name);
+ if (r < 0)
+ return r;
- for (;;) {
- gid_t gid_base, gid_shift, gid_range, converted;
- int k;
-
- errno = 0;
- k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT, &gid_base, &gid_shift, &gid_range);
- if (k < 0 && feof(f))
- break;
- if (k != 3) {
- if (ferror(f))
- return errno_or_else(EIO);
-
- return -EIO;
- }
-
- if (gid < gid_base || gid >= gid_base + gid_range)
- continue;
-
- converted = gid - gid_base + gid_shift;
- if (!gid_is_valid(converted))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid);
-
- return sd_bus_reply_method_return(message, "u", (uint32_t) converted);
- }
-
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "Machine '%s' has no matching group mappings.", name);
+ return sd_bus_reply_method_return(message, "u", (uint32_t) converted);
}
-static int method_map_to_machine_group(sd_bus_message *message, void *groupdata, sd_bus_error *error) {
- Manager *m = groupdata;
+static int method_map_to_machine_group(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_free_ char *o = NULL;
+ Manager *m = userdata;
Machine *machine;
- gid_t gid;
- Iterator i;
+ gid_t gid, converted;
int r;
r = sd_bus_message_read(message, "u", &gid);
@@ -1074,56 +987,17 @@ static int method_map_to_machine_group(sd_bus_message *message, void *groupdata,
if (gid < 0x10000)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "Group " GID_FMT " belongs to host GID range", gid);
- HASHMAP_FOREACH(machine, m->machines, i) {
- _cleanup_fclose_ FILE *f = NULL;
- char p[STRLEN("/proc//gid_map") + DECIMAL_STR_MAX(pid_t) + 1];
+ r = manager_find_machine_for_gid(m, gid, &machine, &converted);
+ if (r < 0)
+ return r;
+ if (!r)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "No matching group mapping for " GID_FMT ".", gid);
- if (machine->class != MACHINE_CONTAINER)
- continue;
+ o = machine_bus_path(machine);
+ if (!o)
+ return -ENOMEM;
- xsprintf(p, "/proc/" GID_FMT "/gid_map", machine->leader);
- f = fopen(p, "re");
- if (!f) {
- log_warning_errno(errno, "Failed to open %s, ignoring,", p);
- continue;
- }
-
- for (;;) {
- _cleanup_free_ char *o = NULL;
- gid_t gid_base, gid_shift, gid_range, converted;
- int k;
-
- errno = 0;
- k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT, &gid_base, &gid_shift, &gid_range);
- if (k < 0 && feof(f))
- break;
- if (k != 3) {
- if (ferror(f))
- return errno_or_else(EIO);
-
- return -EIO;
- }
-
- /* The private user namespace is disabled, ignoring. */
- if (gid_shift == 0)
- continue;
-
- if (gid < gid_shift || gid >= gid_shift + gid_range)
- continue;
-
- converted = (gid - gid_shift + gid_base);
- if (!gid_is_valid(converted))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid);
-
- o = machine_bus_path(machine);
- if (!o)
- return -ENOMEM;
-
- return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted);
- }
- }
-
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "No matching group mapping for " GID_FMT ".", gid);
+ return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted);
}
const sd_bus_vtable manager_vtable[] = {
diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c
new file mode 100644
index 0000000000..892de7071a
--- /dev/null
+++ b/src/machine/machined-varlink.c
@@ -0,0 +1,404 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "format-util.h"
+#include "machined-varlink.h"
+#include "mkdir.h"
+#include "user-util.h"
+#include "varlink.h"
+
+typedef struct LookupParameters {
+ const char *user_name;
+ const char *group_name;
+ union {
+ uid_t uid;
+ gid_t gid;
+ };
+ const char *service;
+} LookupParameters;
+
+static int build_user_json(const char *user_name, uid_t uid, const char *real_name, JsonVariant **ret) {
+ assert(user_name);
+ assert(uid_is_valid(uid));
+ assert(ret);
+
+ return json_build(ret, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("record", JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(user_name)),
+ JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid)),
+ JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY)),
+ JSON_BUILD_PAIR_CONDITION(!isempty(real_name), "realName", JSON_BUILD_STRING(real_name)),
+ JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING("/")),
+ JSON_BUILD_PAIR("shell", JSON_BUILD_STRING(NOLOGIN)),
+ JSON_BUILD_PAIR("locked", JSON_BUILD_BOOLEAN(true)),
+ JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.Machine")),
+ JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("container"))))));
+}
+
+static bool user_match_lookup_parameters(LookupParameters *p, const char *name, uid_t uid) {
+ assert(p);
+
+ if (p->user_name && !streq(name, p->user_name))
+ return false;
+
+ if (uid_is_valid(p->uid) && uid != p->uid)
+ return false;
+
+ return true;
+}
+
+static int user_lookup_uid(Manager *m, uid_t uid, char **ret_name, char **ret_real_name) {
+ _cleanup_free_ char *n = NULL, *rn = NULL;
+ uid_t converted_uid;
+ Machine *machine;
+ int r;
+
+ assert(m);
+ assert(uid_is_valid(uid));
+ assert(ret_name);
+ assert(ret_real_name);
+
+ if (uid < 0x10000) /* Host UID range */
+ return -ESRCH;
+
+ r = manager_find_machine_for_uid(m, uid, &machine, &converted_uid);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -ESRCH;
+
+ if (asprintf(&n, "vu-%s-" UID_FMT, machine->name, converted_uid) < 0)
+ return -ENOMEM;
+
+ /* Don't synthesize invalid user/group names (too long...) */
+ if (!valid_user_group_name(n, 0))
+ return -ESRCH;
+
+ if (asprintf(&rn, "UID " UID_FMT " of Container %s", converted_uid, machine->name) < 0)
+ return -ENOMEM;
+
+ /* Don't synthesize invalid real names either, but since this field doesn't matter much, simply invalidate things */
+ if (!valid_gecos(rn))
+ rn = mfree(rn);
+
+ *ret_name = TAKE_PTR(n);
+ *ret_real_name = TAKE_PTR(rn);
+ return 0;
+}
+
+static int user_lookup_name(Manager *m, const char *name, uid_t *ret_uid, char **ret_real_name) {
+ _cleanup_free_ char *mn = NULL, *rn = NULL;
+ uid_t uid, converted_uid;
+ Machine *machine;
+ const char *e, *d;
+ int r;
+
+ assert(m);
+
+ if (!valid_user_group_name(name, 0))
+ return -ESRCH;
+
+ e = startswith(name, "vu-");
+ if (!e)
+ return -ESRCH;
+
+ d = strrchr(e, '-');
+ if (!d)
+ return -ESRCH;
+
+ if (parse_uid(d + 1, &uid) < 0)
+ return -ESRCH;
+
+ mn = strndup(e, d - e);
+ if (!mn)
+ return -ENOMEM;
+
+ machine = hashmap_get(m->machines, mn);
+ if (!machine)
+ return -ESRCH;
+
+ if (machine->class != MACHINE_CONTAINER)
+ return -ESRCH;
+
+ r = machine_translate_uid(machine, uid, &converted_uid);
+ if (r < 0)
+ return r;
+
+ if (asprintf(&rn, "UID " UID_FMT " of Container %s", uid, machine->name) < 0)
+ return -ENOMEM;
+ if (!valid_gecos(rn))
+ rn = mfree(rn);
+
+ *ret_uid = converted_uid;
+ *ret_real_name = TAKE_PTR(rn);
+ return 0;
+}
+
+static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 },
+ { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE },
+ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
+ {}
+ };
+
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ LookupParameters p = {
+ .uid = UID_INVALID,
+ };
+ _cleanup_free_ char *found_name = NULL, *found_real_name = NULL;
+ uid_t found_uid = UID_INVALID, uid;
+ Manager *m = userdata;
+ const char *un;
+ int r;
+
+ assert(parameters);
+ assert(m);
+
+ r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
+ if (r < 0)
+ return r;
+
+ if (!streq_ptr(p.service, "io.systemd.Machine"))
+ return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+ if (uid_is_valid(p.uid))
+ r = user_lookup_uid(m, p.uid, &found_name, &found_real_name);
+ else if (p.user_name)
+ r = user_lookup_name(m, p.user_name, &found_uid, &found_real_name);
+ else
+ return varlink_error(link, "io.systemd.UserDatabase.EnumerationNotSupported", NULL);
+ if (r == -ESRCH)
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+ if (r < 0)
+ return r;
+
+ uid = uid_is_valid(found_uid) ? found_uid : p.uid;
+ un = found_name ?: p.user_name;
+
+ if (!user_match_lookup_parameters(&p, un, uid))
+ return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
+
+ r = build_user_json(un, uid, found_real_name, &v);
+ if (r < 0)
+ return r;
+
+ return varlink_reply(link, v);
+}
+
+static int build_group_json(const char *group_name, gid_t gid, JsonVariant **ret) {
+ assert(group_name);
+ assert(gid_is_valid(gid));
+ assert(ret);
+
+ return json_build(ret, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("record", JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(group_name)),
+ JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid)),
+ JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.Machine")),
+ JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("container"))))));
+ }
+
+static bool group_match_lookup_parameters(LookupParameters *p, const char *name, gid_t gid) {
+ assert(p);
+
+ if (p->group_name && !streq(name, p->group_name))
+ return false;
+
+ if (gid_is_valid(p->gid) && gid != p->gid)
+ return false;
+
+ return true;
+}
+
+static int group_lookup_gid(Manager *m, gid_t gid, char **ret_name) {
+ _cleanup_free_ char *n = NULL;
+ gid_t converted_gid;
+ Machine *machine;
+ int r;
+
+ assert(m);
+ assert(gid_is_valid(gid));
+ assert(ret_name);
+
+ if (gid < 0x10000) /* Host GID range */
+ return -ESRCH;
+
+ r = manager_find_machine_for_gid(m, gid, &machine, &converted_gid);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -ESRCH;
+
+ if (asprintf(&n, "vg-%s-" GID_FMT, machine->name, converted_gid) < 0)
+ return -ENOMEM;
+
+ if (!valid_user_group_name(n, 0))
+ return -ESRCH;
+
+ *ret_name = TAKE_PTR(n);
+ return 0;
+}
+
+static int group_lookup_name(Manager *m, const char *name, gid_t *ret_gid) {
+ _cleanup_free_ char *mn = NULL;
+ gid_t gid, converted_gid;
+ Machine *machine;
+ const char *e, *d;
+ int r;
+
+ assert(m);
+
+ if (!valid_user_group_name(name, 0))
+ return -ESRCH;
+
+ e = startswith(name, "vg-");
+ if (!e)
+ return -ESRCH;
+
+ d = strrchr(e, '-');
+ if (!d)
+ return -ESRCH;
+
+ if (parse_gid(d + 1, &gid) < 0)
+ return -ESRCH;
+
+ mn = strndup(e, d - e);
+ if (!mn)
+ return -ENOMEM;
+
+ machine = hashmap_get(m->machines, mn);
+ if (!mn)
+ return -ESRCH;
+
+ if (machine->class != MACHINE_CONTAINER)
+ return -ESRCH;
+
+ r = machine_translate_gid(machine, gid, &converted_gid);
+ if (r < 0)
+ return r;
+
+ *ret_gid = converted_gid;
+ return 0;
+}
+
+static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 },
+ { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE },
+ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
+ {}
+ };
+
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ LookupParameters p = {
+ .gid = GID_INVALID,
+ };
+ _cleanup_free_ char *found_name = NULL;
+ uid_t found_gid = GID_INVALID, gid;
+ Manager *m = userdata;
+ const char *gn;
+ int r;
+
+ assert(parameters);
+ assert(m);
+
+ r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
+ if (r < 0)
+ return r;
+
+ if (!streq_ptr(p.service, "io.systemd.Machine"))
+ return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+ if (gid_is_valid(p.gid))
+ r = group_lookup_gid(m, p.gid, &found_name);
+ else if (p.group_name)
+ r = group_lookup_name(m, p.group_name, (uid_t*) &found_gid);
+ else
+ return varlink_error(link, "io.systemd.UserDatabase.EnumerationNotSupported", NULL);
+ if (r == -ESRCH)
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+ if (r < 0)
+ return r;
+
+ gid = gid_is_valid(found_gid) ? found_gid : p.gid;
+ gn = found_name ?: p.group_name;
+
+ if (!group_match_lookup_parameters(&p, gn, gid))
+ return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
+
+ r = build_group_json(gn, gid, &v);
+ if (r < 0)
+ return r;
+
+ return varlink_reply(link, v);
+}
+
+static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE },
+ { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE },
+ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
+ {}
+ };
+
+ LookupParameters p = {};
+ int r;
+
+ assert(parameters);
+
+ r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
+ if (r < 0)
+ return r;
+
+ if (!streq_ptr(p.service, "io.systemd.Machine"))
+ return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+ /* We don't support auxiliary groups for machines. */
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+}
+
+int manager_varlink_init(Manager *m) {
+ _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
+ int r;
+
+ assert(m);
+
+ if (m->varlink_server)
+ return 0;
+
+ r = varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate varlink server object: %m");
+
+ varlink_server_set_userdata(s, m);
+
+ r = varlink_server_bind_method_many(
+ s,
+ "io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record,
+ "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record,
+ "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships);
+ if (r < 0)
+ return log_error_errno(r, "Failed to register varlink methods: %m");
+
+ (void) mkdir_p("/run/systemd/userdb", 0755);
+
+ r = varlink_server_listen_address(s, "/run/systemd/userdb/io.systemd.Machine", 0666);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind to varlink socket: %m");
+
+ r = varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
+
+ m->varlink_server = TAKE_PTR(s);
+ return 0;
+}
+
+void manager_varlink_done(Manager *m) {
+ assert(m);
+
+ m->varlink_server = varlink_server_unref(m->varlink_server);
+}
diff --git a/src/machine/machined-varlink.h b/src/machine/machined-varlink.h
new file mode 100644
index 0000000000..42e1c3f4a5
--- /dev/null
+++ b/src/machine/machined-varlink.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "machined.h"
+
+int manager_varlink_init(Manager *m);
+void manager_varlink_done(Manager *m);
diff --git a/src/machine/machined.c b/src/machine/machined.c
index 4645e4ce41..a884f3de9b 100644
--- a/src/machine/machined.c
+++ b/src/machine/machined.c
@@ -20,6 +20,7 @@
#include "hostname-util.h"
#include "label.h"
#include "machine-image.h"
+#include "machined-varlink.h"
#include "machined.h"
#include "main-func.h"
#include "process-util.h"
@@ -86,6 +87,8 @@ static Manager* manager_unref(Manager *m) {
bus_verify_polkit_async_registry_free(m->polkit_registry);
+ manager_varlink_done(m);
+
sd_bus_flush_close_unref(m->bus);
sd_event_unref(m->event);
@@ -272,6 +275,11 @@ static int manager_startup(Manager *m) {
if (r < 0)
return r;
+ /* Set up Varlink service */
+ r = manager_varlink_init(m);
+ if (r < 0)
+ return r;
+
/* Deserialize state */
manager_enumerate_machines(m);
@@ -291,6 +299,9 @@ static bool check_idle(void *userdata) {
if (m->operations)
return false;
+ if (varlink_server_current_connections(m->varlink_server) > 0)
+ return false;
+
manager_gc(m, true);
return hashmap_isempty(m->machines);
diff --git a/src/machine/machined.h b/src/machine/machined.h
index 0ec3fb9326..c83017acb8 100644
--- a/src/machine/machined.h
+++ b/src/machine/machined.h
@@ -6,15 +6,15 @@
#include "sd-bus.h"
#include "sd-event.h"
-#include "hashmap.h"
-#include "list.h"
-
typedef struct Manager Manager;
+#include "hashmap.h"
#include "image-dbus.h"
+#include "list.h"
#include "machine-dbus.h"
#include "machine.h"
#include "operation.h"
+#include "varlink.h"
struct Manager {
sd_event *event;
@@ -37,6 +37,8 @@ struct Manager {
unsigned n_operations;
sd_event_source *nscd_cache_flush_event;
+
+ VarlinkServer *varlink_server;
};
int manager_add_machine(Manager *m, const char *name, Machine **_machine);
@@ -56,3 +58,6 @@ int manager_unit_is_active(Manager *manager, const char *unit);
int manager_job_is_active(Manager *manager, const char *path);
int manager_enqueue_nscd_cache_flush(Manager *m);
+
+int manager_find_machine_for_uid(Manager *m, uid_t host_uid, Machine **ret_machine, uid_t *ret_internal_uid);
+int manager_find_machine_for_gid(Manager *m, gid_t host_gid, Machine **ret_machine, gid_t *ret_internal_gid);
diff --git a/src/machine/meson.build b/src/machine/meson.build
index bc670714b4..291630ece9 100644
--- a/src/machine/meson.build
+++ b/src/machine/meson.build
@@ -6,14 +6,16 @@ systemd_machined_sources = files('''
'''.split())
libmachine_core_sources = files('''
- machine.c
- machine.h
- machined-dbus.c
- machined-core.c
- machine-dbus.c
- machine-dbus.h
image-dbus.c
image-dbus.h
+ machine-dbus.c
+ machine-dbus.h
+ machine.c
+ machine.h
+ machined-core.c
+ machined-dbus.c
+ machined-varlink.c
+ machined-varlink.h
operation.c
operation.h
'''.split())
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 1f321f9ea3..330bd7e7d2 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -4636,7 +4636,7 @@ static int run_container(
if (!barrier_place_and_sync(&barrier)) /* #5 */
return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Child died too early.");
- /* At this point we have made use of the UID we picked, and thus nss-mymachines
+ /* At this point we have made use of the UID we picked, and thus nss-systemd/systemd-machined.service
* will make them appear in getpwuid(), thus we can release the /etc/passwd lock. */
etc_passwd_lock = safe_close(etc_passwd_lock);
diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c
index 9269e7fd7b..5db0dcef76 100644
--- a/src/nss-mymachines/nss-mymachines.c
+++ b/src/nss-mymachines/nss-mymachines.c
@@ -19,15 +19,11 @@
#include "nss-util.h"
#include "signal-util.h"
#include "string-util.h"
-#include "user-util.h"
NSS_GETHOSTBYNAME_PROTOTYPES(mymachines);
NSS_GETPW_PROTOTYPES(mymachines);
NSS_GETGR_PROTOTYPES(mymachines);
-#define HOST_UID_LIMIT ((uid_t) UINT32_C(0x10000))
-#define HOST_GID_LIMIT ((gid_t) UINT32_C(0x10000))
-
static int count_addresses(sd_bus_message *m, int af, unsigned *ret) {
unsigned c = 0;
int r;
@@ -402,94 +398,7 @@ enum nss_status _nss_mymachines_getpwnam_r(
char *buffer, size_t buflen,
int *errnop) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- const char *p, *e, *machine;
- uint32_t mapped;
- uid_t uid;
- size_t l;
- int r;
-
- PROTECT_ERRNO;
- BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
-
- assert(name);
- assert(pwd);
-
- p = startswith(name, "vu-");
- if (!p)
- return NSS_STATUS_NOTFOUND;
-
- e = strrchr(p, '-');
- if (!e || e == p)
- return NSS_STATUS_NOTFOUND;
-
- if (e - p > HOST_NAME_MAX - 1) /* -1 for the last dash */
- return NSS_STATUS_NOTFOUND;
-
- r = parse_uid(e + 1, &uid);
- if (r < 0)
- return NSS_STATUS_NOTFOUND;
-
- machine = strndupa(p, e - p);
- if (!machine_name_is_valid(machine))
- return NSS_STATUS_NOTFOUND;
-
- if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
- /* Make sure we can't deadlock if we are invoked by dbus-daemon. This way, it won't be able to resolve
- * these UIDs, but that should be unproblematic as containers should never be able to connect to a bus
- * running on the host. */
- return NSS_STATUS_NOTFOUND;
-
- if (avoid_deadlock()) {
- r = -EDEADLK;
- goto fail;
- }
-
- r = sd_bus_open_system(&bus);
- if (r < 0)
- goto fail;
-
- r = bus_call_method(bus, bus_machine_mgr, "MapFromMachineUser", &error, &reply, "su", machine, (uint32_t) uid);
- if (r < 0) {
- if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING))
- return NSS_STATUS_NOTFOUND;
-
- goto fail;
- }
-
- r = sd_bus_message_read(reply, "u", &mapped);
- if (r < 0)
- goto fail;
-
- /* Refuse to work if the mapped address is in the host UID range, or if there was no mapping at all. */
- if (mapped < HOST_UID_LIMIT || mapped == uid)
- return NSS_STATUS_NOTFOUND;
-
- l = strlen(name);
- if (buflen < l+1) {
- UNPROTECT_ERRNO;
- *errnop = ERANGE;
- return NSS_STATUS_TRYAGAIN;
- }
-
- memcpy(buffer, name, l+1);
-
- pwd->pw_name = buffer;
- pwd->pw_uid = mapped;
- pwd->pw_gid = GID_NOBODY;
- pwd->pw_gecos = buffer;
- pwd->pw_passwd = (char*) "*"; /* locked */
- pwd->pw_dir = (char*) "/";
- pwd->pw_shell = (char*) NOLOGIN;
-
- return NSS_STATUS_SUCCESS;
-
-fail:
- UNPROTECT_ERRNO;
- *errnop = -r;
- return NSS_STATUS_UNAVAIL;
+ return NSS_STATUS_NOTFOUND;
}
enum nss_status _nss_mymachines_getpwuid_r(
@@ -498,162 +407,16 @@ enum nss_status _nss_mymachines_getpwuid_r(
char *buffer, size_t buflen,
int *errnop) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- const char *machine;
- uint32_t mapped;
- int r;
-
- PROTECT_ERRNO;
- BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
-
- if (!uid_is_valid(uid))
- return NSS_STATUS_NOTFOUND;
-
- /* We consider all uids < 65536 host uids */
- if (uid < HOST_UID_LIMIT)
- return NSS_STATUS_NOTFOUND;
-
- if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
- return NSS_STATUS_NOTFOUND;
-
- if (avoid_deadlock()) {
- r = -EDEADLK;
- goto fail;
- }
-
- r = sd_bus_open_system(&bus);
- if (r < 0)
- goto fail;
-
- r = bus_call_method(bus, bus_machine_mgr, "MapToMachineUser", &error, &reply, "u", (uint32_t) uid);
- if (r < 0) {
- if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING))
- return NSS_STATUS_NOTFOUND;
-
- goto fail;
- }
-
- r = sd_bus_message_read(reply, "sou", &machine, NULL, &mapped);
- if (r < 0)
- goto fail;
-
- if (mapped == uid)
- return NSS_STATUS_NOTFOUND;
-
- if (snprintf(buffer, buflen, "vu-%s-" UID_FMT, machine, (uid_t) mapped) >= (int) buflen) {
- UNPROTECT_ERRNO;
- *errnop = ERANGE;
- return NSS_STATUS_TRYAGAIN;
- }
-
- pwd->pw_name = buffer;
- pwd->pw_uid = uid;
- pwd->pw_gid = GID_NOBODY;
- pwd->pw_gecos = buffer;
- pwd->pw_passwd = (char*) "*"; /* locked */
- pwd->pw_dir = (char*) "/";
- pwd->pw_shell = (char*) NOLOGIN;
-
- return NSS_STATUS_SUCCESS;
-
-fail:
- UNPROTECT_ERRNO;
- *errnop = -r;
- return NSS_STATUS_UNAVAIL;
+ return NSS_STATUS_NOTFOUND;
}
-#pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
-
enum nss_status _nss_mymachines_getgrnam_r(
const char *name,
struct group *gr,
char *buffer, size_t buflen,
int *errnop) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- const char *p, *e, *machine;
- uint32_t mapped;
- uid_t gid;
- size_t l;
- int r;
-
- PROTECT_ERRNO;
- BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
-
- assert(name);
- assert(gr);
-
- p = startswith(name, "vg-");
- if (!p)
- return NSS_STATUS_NOTFOUND;
-
- e = strrchr(p, '-');
- if (!e || e == p)
- return NSS_STATUS_NOTFOUND;
-
- if (e - p > HOST_NAME_MAX - 1) /* -1 for the last dash */
- return NSS_STATUS_NOTFOUND;
-
- r = parse_gid(e + 1, &gid);
- if (r < 0)
- return NSS_STATUS_NOTFOUND;
-
- machine = strndupa(p, e - p);
- if (!machine_name_is_valid(machine))
- return NSS_STATUS_NOTFOUND;
-
- if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
- return NSS_STATUS_NOTFOUND;
-
- if (avoid_deadlock()) {
- r = -EDEADLK;
- goto fail;
- }
-
- r = sd_bus_open_system(&bus);
- if (r < 0)
- goto fail;
-
- r = bus_call_method(bus, bus_machine_mgr, "MapFromMachineGroup", &error, &reply, "su", machine, (uint32_t) gid);
- if (r < 0) {
- if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING))
- return NSS_STATUS_NOTFOUND;
-
- goto fail;
- }
-
- r = sd_bus_message_read(reply, "u", &mapped);
- if (r < 0)
- goto fail;
-
- if (mapped < HOST_GID_LIMIT || mapped == gid)
- return NSS_STATUS_NOTFOUND;
-
- l = sizeof(char*) + strlen(name) + 1;
- if (buflen < l) {
- UNPROTECT_ERRNO;
- *errnop = ERANGE;
- return NSS_STATUS_TRYAGAIN;
- }
-
- memzero(buffer, sizeof(char*));
- strcpy(buffer + sizeof(char*), name);
-
- gr->gr_name = buffer + sizeof(char*);
- gr->gr_gid = mapped;
- gr->gr_passwd = (char*) "*"; /* locked */
- gr->gr_mem = (char**) buffer;
-
- return NSS_STATUS_SUCCESS;
-
-fail:
- UNPROTECT_ERRNO;
- *errnop = -r;
- return NSS_STATUS_UNAVAIL;
+ return NSS_STATUS_NOTFOUND;
}
enum nss_status _nss_mymachines_getgrgid_r(
@@ -662,72 +425,5 @@ enum nss_status _nss_mymachines_getgrgid_r(
char *buffer, size_t buflen,
int *errnop) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- const char *machine;
- uint32_t mapped;
- int r;
-
- PROTECT_ERRNO;
- BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
-
- if (!gid_is_valid(gid))
- return NSS_STATUS_NOTFOUND;
-
- /* We consider all gids < 65536 host gids */
- if (gid < HOST_GID_LIMIT)
- return NSS_STATUS_NOTFOUND;
-
- if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
- return NSS_STATUS_NOTFOUND;
-
- if (avoid_deadlock()) {
- r = -EDEADLK;
- goto fail;
- }
-
- r = sd_bus_open_system(&bus);
- if (r < 0)
- goto fail;
-
- r = bus_call_method(bus, bus_machine_mgr, "MapToMachineGroup", &error, &reply, "u", (uint32_t) gid);
- if (r < 0) {
- if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING))
- return NSS_STATUS_NOTFOUND;
-
- goto fail;
- }
-
- r = sd_bus_message_read(reply, "sou", &machine, NULL, &mapped);
- if (r < 0)
- goto fail;
-
- if (mapped == gid)
- return NSS_STATUS_NOTFOUND;
-
- if (buflen < sizeof(char*) + 1) {
- UNPROTECT_ERRNO;
- *errnop = ERANGE;
- return NSS_STATUS_TRYAGAIN;
- }
-
- memzero(buffer, sizeof(char*));
- if (snprintf(buffer + sizeof(char*), buflen - sizeof(char*), "vg-%s-" GID_FMT, machine, (gid_t) mapped) >= (int) buflen) {
- UNPROTECT_ERRNO;
- *errnop = ERANGE;
- return NSS_STATUS_TRYAGAIN;
- }
-
- gr->gr_name = buffer + sizeof(char*);
- gr->gr_gid = gid;
- gr->gr_passwd = (char*) "*"; /* locked */
- gr->gr_mem = (char**) buffer;
-
- return NSS_STATUS_SUCCESS;
-
-fail:
- UNPROTECT_ERRNO;
- *errnop = -r;
- return NSS_STATUS_UNAVAIL;
+ return NSS_STATUS_NOTFOUND;
}
diff --git a/src/shared/userdb.c b/src/shared/userdb.c
index 3fc333fd35..94120862df 100644
--- a/src/shared/userdb.c
+++ b/src/shared/userdb.c
@@ -156,6 +156,8 @@ static int userdb_on_query_reply(
r = -ESRCH;
else if (streq(error_id, "io.systemd.UserDatabase.ServiceNotAvailable"))
r = -EHOSTDOWN;
+ else if (streq(error_id, "io.systemd.UserDatabase.EnumerationNotSupported"))
+ r = -EOPNOTSUPP;
else if (streq(error_id, VARLINK_ERROR_TIMEOUT))
r = -ETIMEDOUT;
else