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