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