]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/machine/machined-varlink.c
test: drop --tpm2-public-key= from TEST-70
[thirdparty/systemd.git] / src / machine / machined-varlink.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "format-util.h"
4 #include "machined-varlink.h"
5 #include "mkdir.h"
6 #include "user-util.h"
7 #include "varlink.h"
8 #include "varlink-io.systemd.UserDatabase.h"
9
10 typedef struct LookupParameters {
11 const char *user_name;
12 const char *group_name;
13 union {
14 uid_t uid;
15 gid_t gid;
16 };
17 const char *service;
18 } LookupParameters;
19
20 static int build_user_json(const char *user_name, uid_t uid, const char *real_name, JsonVariant **ret) {
21 assert(user_name);
22 assert(uid_is_valid(uid));
23 assert(ret);
24
25 return json_build(ret, JSON_BUILD_OBJECT(
26 JSON_BUILD_PAIR("record", JSON_BUILD_OBJECT(
27 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(user_name)),
28 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid)),
29 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY)),
30 JSON_BUILD_PAIR_CONDITION(!isempty(real_name), "realName", JSON_BUILD_STRING(real_name)),
31 JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_CONST_STRING("/")),
32 JSON_BUILD_PAIR("shell", JSON_BUILD_STRING(NOLOGIN)),
33 JSON_BUILD_PAIR("locked", JSON_BUILD_BOOLEAN(true)),
34 JSON_BUILD_PAIR("service", JSON_BUILD_CONST_STRING("io.systemd.Machine")),
35 JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("container"))))));
36 }
37
38 static bool user_match_lookup_parameters(LookupParameters *p, const char *name, uid_t uid) {
39 assert(p);
40
41 if (p->user_name && !streq(name, p->user_name))
42 return false;
43
44 if (uid_is_valid(p->uid) && uid != p->uid)
45 return false;
46
47 return true;
48 }
49
50 static int user_lookup_uid(Manager *m, uid_t uid, char **ret_name, char **ret_real_name) {
51 _cleanup_free_ char *n = NULL, *rn = NULL;
52 uid_t converted_uid;
53 Machine *machine;
54 int r;
55
56 assert(m);
57 assert(uid_is_valid(uid));
58 assert(ret_name);
59 assert(ret_real_name);
60
61 if (uid < 0x10000) /* Host UID range */
62 return -ESRCH;
63
64 r = manager_find_machine_for_uid(m, uid, &machine, &converted_uid);
65 if (r < 0)
66 return r;
67 if (!r)
68 return -ESRCH;
69
70 if (asprintf(&n, "vu-%s-" UID_FMT, machine->name, converted_uid) < 0)
71 return -ENOMEM;
72
73 /* Don't synthesize invalid user/group names (too long...) */
74 if (!valid_user_group_name(n, 0))
75 return -ESRCH;
76
77 if (asprintf(&rn, "UID " UID_FMT " of Container %s", converted_uid, machine->name) < 0)
78 return -ENOMEM;
79
80 /* Don't synthesize invalid real names either, but since this field doesn't matter much, simply invalidate things */
81 if (!valid_gecos(rn))
82 rn = mfree(rn);
83
84 *ret_name = TAKE_PTR(n);
85 *ret_real_name = TAKE_PTR(rn);
86 return 0;
87 }
88
89 static int user_lookup_name(Manager *m, const char *name, uid_t *ret_uid, char **ret_real_name) {
90 _cleanup_free_ char *mn = NULL, *rn = NULL;
91 uid_t uid, converted_uid;
92 Machine *machine;
93 const char *e, *d;
94 int r;
95
96 assert(m);
97 assert(ret_uid);
98 assert(ret_real_name);
99
100 if (!valid_user_group_name(name, 0))
101 return -ESRCH;
102
103 e = startswith(name, "vu-");
104 if (!e)
105 return -ESRCH;
106
107 d = strrchr(e, '-');
108 if (!d)
109 return -ESRCH;
110
111 if (parse_uid(d + 1, &uid) < 0)
112 return -ESRCH;
113
114 mn = strndup(e, d - e);
115 if (!mn)
116 return -ENOMEM;
117
118 machine = hashmap_get(m->machines, mn);
119 if (!machine)
120 return -ESRCH;
121
122 if (machine->class != MACHINE_CONTAINER)
123 return -ESRCH;
124
125 r = machine_translate_uid(machine, uid, &converted_uid);
126 if (r < 0)
127 return r;
128
129 if (asprintf(&rn, "UID " UID_FMT " of Container %s", uid, machine->name) < 0)
130 return -ENOMEM;
131 if (!valid_gecos(rn))
132 rn = mfree(rn);
133
134 *ret_uid = converted_uid;
135 *ret_real_name = TAKE_PTR(rn);
136 return 0;
137 }
138
139 static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
140
141 static const JsonDispatch dispatch_table[] = {
142 { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 },
143 { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE },
144 { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
145 {}
146 };
147
148 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
149 LookupParameters p = {
150 .uid = UID_INVALID,
151 };
152 _cleanup_free_ char *found_name = NULL, *found_real_name = NULL;
153 uid_t found_uid = UID_INVALID, uid;
154 Manager *m = ASSERT_PTR(userdata);
155 const char *un;
156 int r;
157
158 assert(parameters);
159
160 r = varlink_dispatch(link, parameters, dispatch_table, &p);
161 if (r != 0)
162 return r;
163
164 if (!streq_ptr(p.service, "io.systemd.Machine"))
165 return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
166
167 if (uid_is_valid(p.uid))
168 r = user_lookup_uid(m, p.uid, &found_name, &found_real_name);
169 else if (p.user_name)
170 r = user_lookup_name(m, p.user_name, &found_uid, &found_real_name);
171 else
172 return varlink_error(link, "io.systemd.UserDatabase.EnumerationNotSupported", NULL);
173 if (r == -ESRCH)
174 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
175 if (r < 0)
176 return r;
177
178 uid = uid_is_valid(found_uid) ? found_uid : p.uid;
179 un = found_name ?: p.user_name;
180
181 if (!user_match_lookup_parameters(&p, un, uid))
182 return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
183
184 r = build_user_json(un, uid, found_real_name, &v);
185 if (r < 0)
186 return r;
187
188 return varlink_reply(link, v);
189 }
190
191 static int build_group_json(const char *group_name, gid_t gid, const char *description, JsonVariant **ret) {
192 assert(group_name);
193 assert(gid_is_valid(gid));
194 assert(ret);
195
196 return json_build(ret, JSON_BUILD_OBJECT(
197 JSON_BUILD_PAIR("record", JSON_BUILD_OBJECT(
198 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(group_name)),
199 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid)),
200 JSON_BUILD_PAIR_CONDITION(!isempty(description), "description", JSON_BUILD_STRING(description)),
201 JSON_BUILD_PAIR("service", JSON_BUILD_CONST_STRING("io.systemd.Machine")),
202 JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("container"))))));
203 }
204
205 static bool group_match_lookup_parameters(LookupParameters *p, const char *name, gid_t gid) {
206 assert(p);
207
208 if (p->group_name && !streq(name, p->group_name))
209 return false;
210
211 if (gid_is_valid(p->gid) && gid != p->gid)
212 return false;
213
214 return true;
215 }
216
217 static int group_lookup_gid(Manager *m, gid_t gid, char **ret_name, char **ret_description) {
218 _cleanup_free_ char *n = NULL, *d = NULL;
219 gid_t converted_gid;
220 Machine *machine;
221 int r;
222
223 assert(m);
224 assert(gid_is_valid(gid));
225 assert(ret_name);
226 assert(ret_description);
227
228 if (gid < 0x10000) /* Host GID range */
229 return -ESRCH;
230
231 r = manager_find_machine_for_gid(m, gid, &machine, &converted_gid);
232 if (r < 0)
233 return r;
234 if (!r)
235 return -ESRCH;
236
237 if (asprintf(&n, "vg-%s-" GID_FMT, machine->name, converted_gid) < 0)
238 return -ENOMEM;
239
240 if (!valid_user_group_name(n, 0))
241 return -ESRCH;
242
243 if (asprintf(&d, "GID " GID_FMT " of Container %s", converted_gid, machine->name) < 0)
244 return -ENOMEM;
245 if (!valid_gecos(d))
246 d = mfree(d);
247
248 *ret_name = TAKE_PTR(n);
249 *ret_description = TAKE_PTR(d);
250
251 return 0;
252 }
253
254 static int group_lookup_name(Manager *m, const char *name, gid_t *ret_gid, char **ret_description) {
255 _cleanup_free_ char *mn = NULL, *desc = NULL;
256 gid_t gid, converted_gid;
257 Machine *machine;
258 const char *e, *d;
259 int r;
260
261 assert(m);
262 assert(ret_gid);
263 assert(ret_description);
264
265 if (!valid_user_group_name(name, 0))
266 return -ESRCH;
267
268 e = startswith(name, "vg-");
269 if (!e)
270 return -ESRCH;
271
272 d = strrchr(e, '-');
273 if (!d)
274 return -ESRCH;
275
276 if (parse_gid(d + 1, &gid) < 0)
277 return -ESRCH;
278
279 mn = strndup(e, d - e);
280 if (!mn)
281 return -ENOMEM;
282
283 machine = hashmap_get(m->machines, mn);
284 if (!machine)
285 return -ESRCH;
286
287 if (machine->class != MACHINE_CONTAINER)
288 return -ESRCH;
289
290 r = machine_translate_gid(machine, gid, &converted_gid);
291 if (r < 0)
292 return r;
293
294 if (asprintf(&desc, "GID " GID_FMT " of Container %s", gid, machine->name) < 0)
295 return -ENOMEM;
296 if (!valid_gecos(desc))
297 desc = mfree(desc);
298
299 *ret_gid = converted_gid;
300 *ret_description = TAKE_PTR(desc);
301 return 0;
302 }
303
304 static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
305
306 static const JsonDispatch dispatch_table[] = {
307 { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 },
308 { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE },
309 { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
310 {}
311 };
312
313 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
314 LookupParameters p = {
315 .gid = GID_INVALID,
316 };
317 _cleanup_free_ char *found_name = NULL, *found_description = NULL;
318 uid_t found_gid = GID_INVALID, gid;
319 Manager *m = ASSERT_PTR(userdata);
320 const char *gn;
321 int r;
322
323 assert(parameters);
324
325 r = varlink_dispatch(link, parameters, dispatch_table, &p);
326 if (r != 0)
327 return r;
328
329 if (!streq_ptr(p.service, "io.systemd.Machine"))
330 return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
331
332 if (gid_is_valid(p.gid))
333 r = group_lookup_gid(m, p.gid, &found_name, &found_description);
334 else if (p.group_name)
335 r = group_lookup_name(m, p.group_name, (uid_t*) &found_gid, &found_description);
336 else
337 return varlink_error(link, "io.systemd.UserDatabase.EnumerationNotSupported", NULL);
338 if (r == -ESRCH)
339 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
340 if (r < 0)
341 return r;
342
343 gid = gid_is_valid(found_gid) ? found_gid : p.gid;
344 gn = found_name ?: p.group_name;
345
346 if (!group_match_lookup_parameters(&p, gn, gid))
347 return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
348
349 r = build_group_json(gn, gid, found_description, &v);
350 if (r < 0)
351 return r;
352
353 return varlink_reply(link, v);
354 }
355
356 static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
357
358 static const JsonDispatch dispatch_table[] = {
359 { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE },
360 { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE },
361 { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
362 {}
363 };
364
365 LookupParameters p = {};
366 int r;
367
368 assert(parameters);
369
370 r = varlink_dispatch(link, parameters, dispatch_table, &p);
371 if (r != 0)
372 return r;
373
374 if (!streq_ptr(p.service, "io.systemd.Machine"))
375 return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
376
377 /* We don't support auxiliary groups for machines. */
378 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
379 }
380
381 int manager_varlink_init(Manager *m) {
382 _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
383 int r;
384
385 assert(m);
386
387 if (m->varlink_server)
388 return 0;
389
390 r = varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID|VARLINK_SERVER_INHERIT_USERDATA);
391 if (r < 0)
392 return log_error_errno(r, "Failed to allocate varlink server object: %m");
393
394 varlink_server_set_userdata(s, m);
395
396 r = varlink_server_add_interface(s, &vl_interface_io_systemd_UserDatabase);
397 if (r < 0)
398 return log_error_errno(r, "Failed to add UserDatabase interface to varlink server: %m");
399
400 r = varlink_server_bind_method_many(
401 s,
402 "io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record,
403 "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record,
404 "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships);
405 if (r < 0)
406 return log_error_errno(r, "Failed to register varlink methods: %m");
407
408 (void) mkdir_p("/run/systemd/userdb", 0755);
409
410 r = varlink_server_listen_address(s, "/run/systemd/userdb/io.systemd.Machine", 0666);
411 if (r < 0)
412 return log_error_errno(r, "Failed to bind to varlink socket: %m");
413
414 r = varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL);
415 if (r < 0)
416 return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
417
418 m->varlink_server = TAKE_PTR(s);
419 return 0;
420 }
421
422 void manager_varlink_done(Manager *m) {
423 assert(m);
424
425 m->varlink_server = varlink_server_unref(m->varlink_server);
426 }