]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/home/homed-varlink.c
sd-bus: Add sd_bus_message_peek_type docs
[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
98 if (!streq_ptr(p.service, "io.systemd.Home"))
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 {
106 Iterator i;
107
108 /* If neither UID nor name was specified, then dump all homes. Do so with varlink_notify()
109 * for all entries but the last, so that clients can stream the results, and easily process
110 * them piecemeal. */
111
112 HASHMAP_FOREACH(h, m->homes_by_name, i) {
113
114 if (!home_user_match_lookup_parameters(&p, h))
115 continue;
116
117 if (v) {
118 /* An entry set from the previous iteration? Then send it now */
119 r = varlink_notify(link, v);
120 if (r < 0)
121 return r;
122
123 v = json_variant_unref(v);
124 }
125
126 trusted = client_is_trusted(link, h);
127
128 r = build_user_json(h, trusted, &v);
129 if (r < 0)
130 return r;
131 }
132
133 if (!v)
134 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
135
136 return varlink_reply(link, v);
137 }
138
139 if (!h)
140 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
141
142 if (!home_user_match_lookup_parameters(&p, h))
143 return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
144
145 trusted = client_is_trusted(link, h);
146
147 r = build_user_json(h, trusted, &v);
148 if (r < 0)
149 return r;
150
151 return varlink_reply(link, v);
152}
153
154static int build_group_json(Home *h, JsonVariant **ret) {
155 _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
156 int r;
157
158 assert(h);
159 assert(ret);
160
161 g = group_record_new();
162 if (!g)
163 return -ENOMEM;
164
165 r = group_record_synthesize(g, h->record);
166 if (r < 0)
167 return r;
168
169 assert(!FLAGS_SET(g->mask, USER_RECORD_SECRET));
170 assert(!FLAGS_SET(g->mask, USER_RECORD_PRIVILEGED));
171
172 return json_build(ret,
173 JSON_BUILD_OBJECT(
174 JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(g->json))));
175}
176
177static bool home_group_match_lookup_parameters(LookupParameters *p, Home *h) {
178 assert(p);
179 assert(h);
180
181 if (p->group_name && !streq(h->user_name, p->group_name))
182 return false;
183
184 if (gid_is_valid(p->gid) && h->uid != (uid_t) p->gid)
185 return false;
186
187 return true;
188}
189
190int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
191
192 static const JsonDispatch dispatch_table[] = {
193 { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 },
194 { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE },
195 { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
196 {}
197 };
198
199 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
200 LookupParameters p = {
201 .gid = GID_INVALID,
202 };
203 Manager *m = userdata;
204 Home *h;
205 int r;
206
207 assert(parameters);
208 assert(m);
209
210 r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
211 if (r < 0)
212 return r;
213
214 if (!streq_ptr(p.service, "io.systemd.Home"))
215 return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
216
217 if (gid_is_valid(p.gid))
218 h = hashmap_get(m->homes_by_uid, UID_TO_PTR((uid_t) p.gid));
219 else if (p.group_name)
220 h = hashmap_get(m->homes_by_name, p.group_name);
221 else {
222 Iterator i;
223
224 HASHMAP_FOREACH(h, m->homes_by_name, i) {
225
226 if (!home_group_match_lookup_parameters(&p, h))
227 continue;
228
229 if (v) {
230 r = varlink_notify(link, v);
231 if (r < 0)
232 return r;
233
234 v = json_variant_unref(v);
235 }
236
237 r = build_group_json(h, &v);
238 if (r < 0)
239 return r;
240 }
241
242 if (!v)
243 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
244
245 return varlink_reply(link, v);
246 }
247
248 if (!h)
249 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
250
251 if (!home_group_match_lookup_parameters(&p, h))
252 return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
253
254 r = build_group_json(h, &v);
255 if (r < 0)
256 return r;
257
258 return varlink_reply(link, v);
259}
260
261int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
262
263 static const JsonDispatch dispatch_table[] = {
264 { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE },
265 { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE },
266 { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
267 {}
268 };
269
270 Manager *m = userdata;
271 LookupParameters p = {};
272 Home *h;
273 int r;
274
275 assert(parameters);
276 assert(m);
277
278 r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
279 if (r < 0)
280 return r;
281
282 if (!streq_ptr(p.service, "io.systemd.Home"))
283 return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
284
285 if (p.user_name) {
286 const char *last = NULL;
287 char **i;
288
289 h = hashmap_get(m->homes_by_name, p.user_name);
290 if (!h)
291 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
292
293 if (p.group_name) {
294 if (!strv_contains(h->record->member_of, p.group_name))
295 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
296
297 return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h->user_name)),
298 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p.group_name))));
299 }
300
301 STRV_FOREACH(i, h->record->member_of) {
302 if (last) {
303 r = varlink_notifyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h->user_name)),
304 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last))));
305 if (r < 0)
306 return r;
307 }
308
309 last = *i;
310 }
311
312 if (last)
313 return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h->user_name)),
314 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last))));
315
316 } else if (p.group_name) {
317 const char *last = NULL;
318 Iterator i;
319
320 HASHMAP_FOREACH(h, m->homes_by_name, i) {
321
322 if (!strv_contains(h->record->member_of, p.group_name))
323 continue;
324
325 if (last) {
326 r = varlink_notifyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last)),
327 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p.group_name))));
328 if (r < 0)
329 return r;
330 }
331
332 last = h->user_name;
333 }
334
335 if (last)
336 return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last)),
337 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p.group_name))));
338 } else {
339 const char *last_user_name = NULL, *last_group_name = NULL;
340 Iterator i;
341
342 HASHMAP_FOREACH(h, m->homes_by_name, i) {
343 char **j;
344
345 STRV_FOREACH(j, h->record->member_of) {
346
347 if (last_user_name) {
348 assert(last_group_name);
349
350 r = varlink_notifyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
351 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
352
353 if (r < 0)
354 return r;
355 }
356
357 last_user_name = h->user_name;
358 last_group_name = *j;
359 }
360 }
361
362 if (last_user_name) {
363 assert(last_group_name);
364 return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
365 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
366 }
367 }
368
369 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
370}