]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/home/homed-varlink.c
Merge pull request #17100 from poettering/homed-fixes
[thirdparty/systemd.git] / src / home / homed-varlink.c
CommitLineData
70a5db58
LP
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
3#include "group-record.h"
4#include "homed-varlink.h"
5#include "strv.h"
6#include "user-record-util.h"
7#include "user-record.h"
8#include "user-util.h"
9#include "format-util.h"
10
11typedef struct LookupParameters {
12 const char *user_name;
13 const char *group_name;
14 union {
15 uid_t uid;
16 gid_t gid;
17 };
18 const char *service;
19} LookupParameters;
20
21static bool client_is_trusted(Varlink *link, Home *h) {
22 uid_t peer_uid;
23 int r;
24
25 assert(link);
26 assert(h);
27
28 r = varlink_get_peer_uid(link, &peer_uid);
29 if (r < 0) {
30 log_debug_errno(r, "Unable to query peer UID, ignoring: %m");
31 return false;
32 }
33
34 return peer_uid == 0 || peer_uid == h->uid;
35}
36
37static int build_user_json(Home *h, bool trusted, JsonVariant **ret) {
38 _cleanup_(user_record_unrefp) UserRecord *augmented = NULL;
39 UserRecordLoadFlags flags;
40 int r;
41
42 assert(h);
43 assert(ret);
44
45 flags = USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_BINDING|USER_RECORD_STRIP_SECRET|USER_RECORD_ALLOW_STATUS|USER_RECORD_ALLOW_SIGNATURE;
46 if (trusted)
47 flags |= USER_RECORD_ALLOW_PRIVILEGED;
48 else
49 flags |= USER_RECORD_STRIP_PRIVILEGED;
50
51 r = home_augment_status(h, flags, &augmented);
52 if (r < 0)
53 return r;
54
55 return json_build(ret, JSON_BUILD_OBJECT(
56 JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(augmented->json)),
57 JSON_BUILD_PAIR("incomplete", JSON_BUILD_BOOLEAN(augmented->incomplete))));
58}
59
60static bool home_user_match_lookup_parameters(LookupParameters *p, Home *h) {
61 assert(p);
62 assert(h);
63
64 if (p->user_name && !streq(p->user_name, h->user_name))
65 return false;
66
67 if (uid_is_valid(p->uid) && h->uid != p->uid)
68 return false;
69
70 return true;
71}
72
73int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
74
75 static const JsonDispatch dispatch_table[] = {
76 { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 },
77 { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE },
78 { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
79 {}
80 };
81
82 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
83 LookupParameters p = {
84 .uid = UID_INVALID,
85 };
86 Manager *m = userdata;
87 bool trusted;
88 Home *h;
89 int r;
90
91 assert(parameters);
92 assert(m);
93
94 r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
95 if (r < 0)
96 return r;
97
cc9886bc 98 if (!streq_ptr(p.service, m->userdb_service))
70a5db58
LP
99 return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
100
101 if (uid_is_valid(p.uid))
102 h = hashmap_get(m->homes_by_uid, UID_TO_PTR(p.uid));
103 else if (p.user_name)
104 h = hashmap_get(m->homes_by_name, p.user_name);
105 else {
70a5db58
LP
106
107 /* If neither UID nor name was specified, then dump all homes. Do so with varlink_notify()
108 * for all entries but the last, so that clients can stream the results, and easily process
109 * them piecemeal. */
110
90e74a66 111 HASHMAP_FOREACH(h, m->homes_by_name) {
70a5db58
LP
112
113 if (!home_user_match_lookup_parameters(&p, h))
114 continue;
115
116 if (v) {
117 /* An entry set from the previous iteration? Then send it now */
118 r = varlink_notify(link, v);
119 if (r < 0)
120 return r;
121
122 v = json_variant_unref(v);
123 }
124
125 trusted = client_is_trusted(link, h);
126
127 r = build_user_json(h, trusted, &v);
128 if (r < 0)
129 return r;
130 }
131
132 if (!v)
133 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
134
135 return varlink_reply(link, v);
136 }
137
138 if (!h)
139 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
140
141 if (!home_user_match_lookup_parameters(&p, h))
142 return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
143
144 trusted = client_is_trusted(link, h);
145
146 r = build_user_json(h, trusted, &v);
147 if (r < 0)
148 return r;
149
150 return varlink_reply(link, v);
151}
152
153static int build_group_json(Home *h, JsonVariant **ret) {
154 _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
155 int r;
156
157 assert(h);
158 assert(ret);
159
160 g = group_record_new();
161 if (!g)
162 return -ENOMEM;
163
164 r = group_record_synthesize(g, h->record);
165 if (r < 0)
166 return r;
167
168 assert(!FLAGS_SET(g->mask, USER_RECORD_SECRET));
169 assert(!FLAGS_SET(g->mask, USER_RECORD_PRIVILEGED));
170
171 return json_build(ret,
172 JSON_BUILD_OBJECT(
173 JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(g->json))));
174}
175
176static bool home_group_match_lookup_parameters(LookupParameters *p, Home *h) {
177 assert(p);
178 assert(h);
179
180 if (p->group_name && !streq(h->user_name, p->group_name))
181 return false;
182
183 if (gid_is_valid(p->gid) && h->uid != (uid_t) p->gid)
184 return false;
185
186 return true;
187}
188
189int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
190
191 static const JsonDispatch dispatch_table[] = {
192 { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 },
193 { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE },
194 { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
195 {}
196 };
197
198 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
199 LookupParameters p = {
200 .gid = GID_INVALID,
201 };
202 Manager *m = userdata;
203 Home *h;
204 int r;
205
206 assert(parameters);
207 assert(m);
208
209 r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
210 if (r < 0)
211 return r;
212
cc9886bc 213 if (!streq_ptr(p.service, m->userdb_service))
70a5db58
LP
214 return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
215
216 if (gid_is_valid(p.gid))
217 h = hashmap_get(m->homes_by_uid, UID_TO_PTR((uid_t) p.gid));
218 else if (p.group_name)
219 h = hashmap_get(m->homes_by_name, p.group_name);
220 else {
70a5db58 221
90e74a66 222 HASHMAP_FOREACH(h, m->homes_by_name) {
70a5db58
LP
223
224 if (!home_group_match_lookup_parameters(&p, h))
225 continue;
226
227 if (v) {
228 r = varlink_notify(link, v);
229 if (r < 0)
230 return r;
231
232 v = json_variant_unref(v);
233 }
234
235 r = build_group_json(h, &v);
236 if (r < 0)
237 return r;
238 }
239
240 if (!v)
241 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
242
243 return varlink_reply(link, v);
244 }
245
246 if (!h)
247 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
248
249 if (!home_group_match_lookup_parameters(&p, h))
250 return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
251
252 r = build_group_json(h, &v);
253 if (r < 0)
254 return r;
255
256 return varlink_reply(link, v);
257}
258
259int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
260
261 static const JsonDispatch dispatch_table[] = {
262 { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE },
263 { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE },
264 { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
265 {}
266 };
267
268 Manager *m = userdata;
269 LookupParameters p = {};
270 Home *h;
271 int r;
272
273 assert(parameters);
274 assert(m);
275
276 r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
277 if (r < 0)
278 return r;
279
cc9886bc 280 if (!streq_ptr(p.service, m->userdb_service))
70a5db58
LP
281 return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
282
283 if (p.user_name) {
284 const char *last = NULL;
285 char **i;
286
287 h = hashmap_get(m->homes_by_name, p.user_name);
288 if (!h)
289 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
290
291 if (p.group_name) {
292 if (!strv_contains(h->record->member_of, p.group_name))
293 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
294
295 return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h->user_name)),
296 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p.group_name))));
297 }
298
299 STRV_FOREACH(i, h->record->member_of) {
300 if (last) {
301 r = varlink_notifyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h->user_name)),
302 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last))));
303 if (r < 0)
304 return r;
305 }
306
307 last = *i;
308 }
309
310 if (last)
311 return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h->user_name)),
312 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last))));
313
314 } else if (p.group_name) {
315 const char *last = NULL;
70a5db58 316
90e74a66 317 HASHMAP_FOREACH(h, m->homes_by_name) {
70a5db58
LP
318
319 if (!strv_contains(h->record->member_of, p.group_name))
320 continue;
321
322 if (last) {
323 r = varlink_notifyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last)),
324 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p.group_name))));
325 if (r < 0)
326 return r;
327 }
328
329 last = h->user_name;
330 }
331
332 if (last)
333 return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last)),
334 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p.group_name))));
335 } else {
336 const char *last_user_name = NULL, *last_group_name = NULL;
70a5db58 337
90e74a66 338 HASHMAP_FOREACH(h, m->homes_by_name) {
70a5db58
LP
339 char **j;
340
341 STRV_FOREACH(j, h->record->member_of) {
342
343 if (last_user_name) {
344 assert(last_group_name);
345
346 r = varlink_notifyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
347 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
348
349 if (r < 0)
350 return r;
351 }
352
353 last_user_name = h->user_name;
354 last_group_name = *j;
355 }
356 }
357
358 if (last_user_name) {
359 assert(last_group_name);
360 return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
361 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
362 }
363 }
364
365 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
366}