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