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 <filename>nss-mymachines</filename> + Example: Mappings provided by <filename>nss-mymachines</filename> 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 <filename>/etc/nsswitch.conf</filename> 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 <filename>systemd-machined.service</filename> + + 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