X Tutup
Skip to content

Commit c903ee8

Browse files
committed
docs: add documentation for the varlink user/group APIs
1 parent 32eb3c4 commit c903ee8

File tree

1 file changed

+267
-0
lines changed

1 file changed

+267
-0
lines changed

docs/USER_GROUP_API.md

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
---
2+
title: User/Group Record Lookup API via Varlink
3+
category: Interfaces
4+
layout: default
5+
---
6+
7+
# User/Group Record Lookup API via Varlink
8+
9+
JSON User/Group Records (as described in the [JSON User
10+
Records](https://systemd.io/USER_RECORD) and [JSON Group
11+
Records](https://systemd.io/GROUP_RECORD) documents) that are defined on the
12+
local system may be queried with a [Varlink](https://varlink.org/) API. This
13+
API takes both the role of what
14+
[`getpwnam(3)`](http://man7.org/linux/man-pages/man3/getpwnam.3.html) and
15+
related calls are for `struct passwd`, as well as the interfaces modules
16+
implementing the [glibc Name Service Switch
17+
(NSS)](https://www.gnu.org/software/libc/manual/html_node/Name-Service-Switch.html)
18+
expose. Or in other words, it both allows applications to efficiently query
19+
user/group records from local services, and allows local subsystems to provide
20+
user/group records efficiently to local applications.
21+
22+
This simple API only exposes only three method calls, and requires only a small
23+
subset of the Varlink functionality.
24+
25+
## Why Varlink?
26+
27+
The API described in this document is based on a simple subset of the
28+
mechanisms described by [Varlink](https://varlink.org/). The choice of
29+
preferring Varlink over D-Bus and other IPCs in this context was made for three
30+
reasons:
31+
32+
1. User/Group record resolution should work during early boot and late shutdown
33+
without special handling. This is very hard to do with D-Bus, as the broker
34+
service for D-Bus generally runs as regular system daemon and is hence only
35+
available at the latest boot stage.
36+
37+
2. The JSON user/group records are native JSON data, hence picking an IPC
38+
system that natively operates with JSON data is natural and clean.
39+
40+
3. IPC systems such as D-Bus do not provide flow control and are thus unusable
41+
for streaming data. They are useful to pass around short control messages,
42+
but as soon as potentially many and large objects shall be transferred,
43+
D-Bus is not suitable, as any such streaming of messages would be considered
44+
flooding in D-Bus' logic, and thus possibly result in termination of
45+
communication. Since the APIs defined in this document need to support
46+
enumerating potentially large numbers of users and groups, D-Bus is simply
47+
not an appropriate option.
48+
49+
## Concepts
50+
51+
Each subsystem that needs to define users and groups on the local system is
52+
supposed to implement this API, and offer its interfaces on a Varlink
53+
`AF_UNIX`/`SOCK_STREAM` file system socket bound into the
54+
`/run/systemd/userdb/` directory. When a client wants to look up a user or
55+
group record, it contacts all sockets bound in this directory in parallel, and
56+
enqueues the same query to each. The first positive reply is then returned to
57+
the application, or if all fail the last seen error is returned
58+
instead. (Alternatively a special Varlink service is available,
59+
`io.systemd.Multiplexer` which acts as frontend and will do the parallel
60+
queries on behalf of the client, drastically simplifying client
61+
development. This service is not available during earliest boot and final
62+
shutdown phases.)
63+
64+
Unlike with glibc NSS there's no order or programmatic expression language
65+
defined in which queries are issued to the various services. Instead, all
66+
queries are always enqueued in parallel to all defined services, in order to
67+
make look-ups efficient, and the simple rule of "first successful lookup wins"
68+
is unconditionally followed for user and group look-ups (though not for
69+
membership lookups, see below).
70+
71+
This simple scheme only works safely as long as every service providing
72+
user/group records carefully makes sure not to answer with conflicting
73+
records. This API does not define any mechanisms for dealing with user/group
74+
name/ID collisions during look-up nor during record registration. It assumes
75+
the various subsystems that want to offer user and group records to the rest of
76+
the system have made sufficiently sure in advance that their definitions do not
77+
collide with those of other services. Clients are not expected to merge
78+
multiple definitions for the same user or group, and will also not be able to
79+
detect conflicts and suppress such conflicting records.
80+
81+
It is recommended to name the sockets in the directory in reverse domain name
82+
notation, but this is neither required nor enforced.
83+
84+
## Well-Known Services
85+
86+
Any subsystem that wants to provide user/group records can do so, simply by
87+
binding a socket in the aforementioned directory. By default two
88+
services are listening there, that have special relevance:
89+
90+
1. `io.systemd.NameServiceSwitch` → This service makes the classic UNIX/glibc
91+
NSS user/group records available as JSON User/Group records. Any such
92+
records are automatically converted as needed, and possibly augmented with
93+
information from the shadow databases.
94+
95+
2. `io.systemd.Multiplexer` → This service multiplexes client queries to all
96+
other running services. It's supposed to simplify client development: in
97+
order to look up or enumerate user/group records it's sufficient to talk to
98+
one service instead of all of them in parallel. Note that it is not availabe
99+
during earliest boot and final shutdown phases, hence for programs running
100+
in that context it is preferable to implement the parallel lookup
101+
themselves.
102+
103+
Both these services are implemented by the same daemon
104+
`systemd-userdbd.service`.
105+
106+
Note that these services currently implement a subset of Varlink only. For
107+
example, introspection is not available, and the resolver logic is not used.
108+
109+
## Other Services
110+
111+
The `systemd` project provides two other services implementing this
112+
interface. Specifically:
113+
114+
1. `io.systemd.DynamicUser` → This service is implemented by the service
115+
manager itself, and provides records for the users and groups synthesized
116+
via `DynamicUser=` in unit files.
117+
118+
2. `io.systemd.Home` → This service is implemented by `systemd-homed.service`
119+
and provides records for the users and groups defined by the home
120+
directories it manages.
121+
122+
Other projects are invited to implement these services too. For example it
123+
would make sense for LDAP/ActiveDirectory projects to implement these
124+
interfaces, which would provide them a way to do per-user resource management
125+
enforced by systemd and defined directly in LDAP directories.
126+
127+
## Compatibility with NSS
128+
129+
Two-way compatibility with classic UNIX/glibc NSS user/group records is
130+
provided. When using the Varlink API, lookups into databases provided only via
131+
NSS (and not natively via Varlink) are handled by the
132+
`io.systemd.NameServiceSwitch` service (see above). When using the NSS API
133+
(i.e. `getpwnam()` and friends) the `nss-systemd` module will automatically
134+
synthesize NSS records for users/groups natively defined via a Varlink
135+
API. Special care is taken to avoid recursion between these two compatibility
136+
mechanisms.
137+
138+
Subsystems that shall provide user/group records to the system may choose
139+
between offering them via an NSS module or via a this Varlink API, either way
140+
all records are accessible via both APIs, due to the bidirectional
141+
forwarding. It is also possible to provide the same records via both APIs
142+
directly, but in that case the compatibility logic must be turned off. There
143+
are mechanisms in place for this, please contact the systemd project for
144+
details, as these are currently not documented.
145+
146+
## Caching of User Records
147+
148+
This API defines no concepts for caching records. If caching is desired it
149+
should be implemented in the subsystems that provide the user records, not in
150+
the clients consuming them.
151+
152+
## Method Calls
153+
154+
```
155+
interface io.systemd.UserDatabase
156+
157+
method GetUserRecord(
158+
uid : ?int,
159+
userName : ?string,
160+
service : string
161+
) -> (
162+
record : object,
163+
incomplete : boolean
164+
)
165+
166+
method GetGroupRecord(
167+
gid : ?int,
168+
groupName : ?string,
169+
service : string
170+
) -> (
171+
record : object,
172+
incomplete : boolean
173+
)
174+
175+
method GetMemberships(
176+
userName : ?string,
177+
groupName : ?string,
178+
service : string
179+
) -> (
180+
userName : string,
181+
groupName : string
182+
)
183+
184+
error NoRecordFound()
185+
error BadService()
186+
error ServiceNotAvailable()
187+
error ConflictingRecordFound()
188+
```
189+
190+
The `GetUserRecord` method looks up or enumerates a user record. If the `uid`
191+
parameter is set it specifies the numeric UNIX UID to search for. If the
192+
`userName` parameter is set it specifies the name of the user to search
193+
for. Typically, only one of the two parameters are set, depending whether a
194+
look-up by UID or by name is desired. However, clients may also specify both
195+
parameters, in which case a record matching both will be returned, and if only
196+
one exists that matches one of the two parameters but not the other an error of
197+
`ConflictingRecordFound` is returned. If neither of the two parameters are set
198+
the whole user database is enumerated. In this case the method call needs to be
199+
made with `more` set, so that multiple method call replies may be generated as
200+
effect, each carrying one user record.
201+
202+
The `service` parameter is mandatory and should be set to the service name
203+
being talked to (i.e. to the same name as the `AF_UNIX` socket path, with the
204+
`/run/systemd/userdb/` prefix removed). This is useful to allow implementation
205+
of multiple services on the same socket (which is used by
206+
`systemd-userdbd.service`).
207+
208+
The method call returns one or more user records, depending which type of query is
209+
used (see above). The record is returned in the `record` field. The
210+
`incomplete` field indicates whether the record is complete. Services providing
211+
user record lookup should only pass the `privileged` section of user records to
212+
clients that either match the user the record is about or to sufficiently
213+
privileged clients, for all others the section must be removed so that no
214+
sensitive data is leaked this way. The `incomplete` parameter should indicate
215+
whether the record has been modified like this or not (i.e. it is `true` if a
216+
`privileged` section existed in the user record and was removed, and `false` if
217+
no `privileged` section existed or one existed but hasn't been removed).
218+
219+
If no user record matching the specified UID or name is known the error
220+
`NoRecordFound` is returned (this is also returned if neither UID nor name are
221+
specified, and hence enumeration requested but the subsystem currently has no
222+
users defined).
223+
224+
If a method call with an incorrectly set `service` field is received
225+
(i.e. either not set at all, or not to the service's own name) a `BadService`
226+
error is generated. Finally, `ServiceNotAvailable` should be returned when the
227+
backing subsystem is not operational for some reason and hence no information
228+
about existence or non-existence of a record can be returned nor any user
229+
record at all. (The `service` field is defined in order to allow implementation
230+
of daemons that provide multiple distinct user/group services over the same
231+
`AF_UNIX` socket: in order to correctly determine which service a client wants
232+
to talk to the client needs to provide the name in each request.)
233+
234+
The `GetGroupRecord` method call works analogously but for groups.
235+
236+
The `GetMemberships` method call may be used to inquire about group
237+
memberships. The `userName` and `groupName` arguments take what the name
238+
suggests. If one of the two is specified all matching memberships are returned,
239+
if neither is specified all known memberships of any user and any group are
240+
returned. The return value is a pair of user name and group name, where the
241+
user is a member of the group. If both arguments are specified the specified
242+
membership will be tested for, but no others, and the pair is returned if it is
243+
defined. Unless both arguments are specified the method call needs to be made
244+
with `more` set, so that multiple replies can be returned (since typically
245+
there are are multiple members per group and also multiple groups a user is
246+
member of). As with `GetUserRecord` and `GetGroupRecord` the `service`
247+
parameter needs to contain the name of the service being talked to, in order to
248+
allow implementation of multiple service within the same IPC socket. In case no
249+
matching membership is known `NoRecordFound` is returned. The other two errors
250+
are also generated in the same cases as for `GetUserRecord` and
251+
`GetGroupRecord`.
252+
253+
Unlike with `GetUserRecord` and `GetGroupRecord` the lists of memberships
254+
returned by services are always combined. Thus unlike the other two calls a
255+
membership lookup query has to wait for the last simultaneous query to complete
256+
before the complete list is acquired.
257+
258+
Note that only the `GetMemberships` call is authoritative about memberships of
259+
users in groups. i.e. it should not be considered sufficient to check the
260+
`memberOf` field of user records and the `members` field of group records to
261+
acquire the full list of memberships. The full list can only bet determined by
262+
`GetMemberships`, and as mentioned requires merging of these lists of all local
263+
services. Result of this is that it can be one service that defines a user A,
264+
and another service that defines a group B, and a third service that declares
265+
that A is a member of B.
266+
267+
And that's really all there is to it.

0 commit comments

Comments
 (0)
X Tutup