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