]>
Commit | Line | Data |
---|---|---|
c903ee89 LP |
1 | --- |
2 | title: User/Group Record Lookup API via Varlink | |
5fe63895 | 3 | category: Users, Groups and Home Directories |
c903ee89 LP |
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 | |
86b52a39 | 98 | one service instead of all of them in parallel. Note that it is not available |
c903ee89 LP |
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 | ||
4c2cf157 | 111 | The `systemd` project provides three other services implementing this |
c903ee89 LP |
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 | ||
4c2cf157 LP |
122 | 3. `io.systemd.Machine` → This service is implemented by |
123 | `systemd-machined.service` and provides records for the users and groups used | |
124 | by local containers that use user namespacing. | |
125 | ||
c903ee89 LP |
126 | Other projects are invited to implement these services too. For example it |
127 | would make sense for LDAP/ActiveDirectory projects to implement these | |
128 | interfaces, which would provide them a way to do per-user resource management | |
129 | enforced by systemd and defined directly in LDAP directories. | |
130 | ||
131 | ## Compatibility with NSS | |
132 | ||
133 | Two-way compatibility with classic UNIX/glibc NSS user/group records is | |
134 | provided. When using the Varlink API, lookups into databases provided only via | |
135 | NSS (and not natively via Varlink) are handled by the | |
136 | `io.systemd.NameServiceSwitch` service (see above). When using the NSS API | |
137 | (i.e. `getpwnam()` and friends) the `nss-systemd` module will automatically | |
138 | synthesize NSS records for users/groups natively defined via a Varlink | |
139 | API. Special care is taken to avoid recursion between these two compatibility | |
140 | mechanisms. | |
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 | |
144 | all records are accessible via both APIs, due to the bidirectional | |
145 | forwarding. 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. There | |
147 | are mechanisms in place for this, please contact the systemd project for | |
148 | details, as these are currently not documented. | |
149 | ||
150 | ## Caching of User Records | |
151 | ||
152 | This API defines no concepts for caching records. If caching is desired it | |
153 | should be implemented in the subsystems that provide the user records, not in | |
154 | the clients consuming them. | |
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 | ||
195 | The `GetUserRecord` method looks up or enumerates a user record. If the `uid` | |
196 | parameter is set it specifies the numeric UNIX UID to search for. If the | |
197 | `userName` parameter is set it specifies the name of the user to search | |
198 | for. Typically, only one of the two parameters are set, depending whether a | |
199 | look-up by UID or by name is desired. However, clients may also specify both | |
200 | 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 | |
202 | `ConflictingRecordFound` is returned. If neither of the two parameters are set | |
203 | the whole user database is enumerated. In this case the method call needs to be | |
204 | made with `more` set, so that multiple method call replies may be generated as | |
205 | effect, each carrying one user record. | |
206 | ||
207 | The `service` parameter is mandatory and should be set to the service name | |
208 | being talked to (i.e. to the same name as the `AF_UNIX` socket path, with the | |
209 | `/run/systemd/userdb/` prefix removed). This is useful to allow implementation | |
210 | of multiple services on the same socket (which is used by | |
211 | `systemd-userdbd.service`). | |
212 | ||
213 | The method call returns one or more user records, depending which type of query is | |
214 | used (see above). The record is returned in the `record` field. The | |
215 | `incomplete` field indicates whether the record is complete. Services providing | |
216 | user record lookup should only pass the `privileged` section of user records to | |
217 | clients that either match the user the record is about or to sufficiently | |
218 | privileged clients, for all others the section must be removed so that no | |
219 | sensitive data is leaked this way. The `incomplete` parameter should indicate | |
220 | whether the record has been modified like this or not (i.e. it is `true` if a | |
221 | `privileged` section existed in the user record and was removed, and `false` if | |
222 | no `privileged` section existed or one existed but hasn't been removed). | |
223 | ||
224 | If no user record matching the specified UID or name is known the error | |
225 | `NoRecordFound` is returned (this is also returned if neither UID nor name are | |
226 | specified, and hence enumeration requested but the subsystem currently has no | |
227 | users defined). | |
228 | ||
229 | If a method call with an incorrectly set `service` field is received | |
230 | (i.e. either not set at all, or not to the service's own name) a `BadService` | |
231 | error is generated. Finally, `ServiceNotAvailable` should be returned when the | |
232 | backing subsystem is not operational for some reason and hence no information | |
233 | about existence or non-existence of a record can be returned nor any user | |
234 | record at all. (The `service` field is defined in order to allow implementation | |
235 | of daemons that provide multiple distinct user/group services over the same | |
236 | `AF_UNIX` socket: in order to correctly determine which service a client wants | |
237 | to talk to the client needs to provide the name in each request.) | |
238 | ||
239 | The `GetGroupRecord` method call works analogously but for groups. | |
240 | ||
241 | The `GetMemberships` method call may be used to inquire about group | |
242 | memberships. The `userName` and `groupName` arguments take what the name | |
243 | suggests. If one of the two is specified all matching memberships are returned, | |
244 | if neither is specified all known memberships of any user and any group are | |
245 | returned. The return value is a pair of user name and group name, where the | |
246 | user is a member of the group. If both arguments are specified the specified | |
247 | membership will be tested for, but no others, and the pair is returned if it is | |
248 | defined. Unless both arguments are specified the method call needs to be made | |
249 | with `more` set, so that multiple replies can be returned (since typically | |
250 | there are are multiple members per group and also multiple groups a user is | |
251 | member of). As with `GetUserRecord` and `GetGroupRecord` the `service` | |
252 | parameter needs to contain the name of the service being talked to, in order to | |
253 | allow implementation of multiple service within the same IPC socket. In case no | |
254 | matching membership is known `NoRecordFound` is returned. The other two errors | |
255 | are also generated in the same cases as for `GetUserRecord` and | |
256 | `GetGroupRecord`. | |
257 | ||
258 | Unlike with `GetUserRecord` and `GetGroupRecord` the lists of memberships | |
259 | returned by services are always combined. Thus unlike the other two calls a | |
260 | membership lookup query has to wait for the last simultaneous query to complete | |
261 | before the complete list is acquired. | |
262 | ||
263 | Note that only the `GetMemberships` call is authoritative about memberships of | |
264 | users in groups. i.e. it should not be considered sufficient to check the | |
265 | `memberOf` field of user records and the `members` field of group records to | |
266 | acquire the full list of memberships. The full list can only bet determined by | |
267 | `GetMemberships`, and as mentioned requires merging of these lists of all local | |
268 | services. Result of this is that it can be one service that defines a user A, | |
269 | and another service that defines a group B, and a third service that declares | |
270 | that A is a member of B. | |
271 | ||
56870d32 LP |
272 | Looking up explicit users/groups by their name or UID/GID, or querying |
273 | user/group memberships must be supported by all services implementing these | |
274 | interfaces. However, supporting enumeration (i.e. user/group lookups that may | |
275 | result in more than one reply, because neither UID/GID nor name is specified) | |
276 | is optional. Services which are asked for enumeration may return the | |
277 | `EnumerationNotSupported` error in this case. | |
278 | ||
c903ee89 | 279 | And that's really all there is to it. |