1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "format-util.h"
4 #include "machined-varlink.h"
9 typedef struct LookupParameters
{
10 const char *user_name
;
11 const char *group_name
;
19 static int build_user_json(const char *user_name
, uid_t uid
, const char *real_name
, JsonVariant
**ret
) {
21 assert(uid_is_valid(uid
));
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"))))));
37 static bool user_match_lookup_parameters(LookupParameters
*p
, const char *name
, uid_t uid
) {
40 if (p
->user_name
&& !streq(name
, p
->user_name
))
43 if (uid_is_valid(p
->uid
) && uid
!= p
->uid
)
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
;
56 assert(uid_is_valid(uid
));
58 assert(ret_real_name
);
60 if (uid
< 0x10000) /* Host UID range */
63 r
= manager_find_machine_for_uid(m
, uid
, &machine
, &converted_uid
);
69 if (asprintf(&n
, "vu-%s-" UID_FMT
, machine
->name
, converted_uid
) < 0)
72 /* Don't synthesize invalid user/group names (too long...) */
73 if (!valid_user_group_name(n
, 0))
76 if (asprintf(&rn
, "UID " UID_FMT
" of Container %s", converted_uid
, machine
->name
) < 0)
79 /* Don't synthesize invalid real names either, but since this field doesn't matter much, simply invalidate things */
83 *ret_name
= TAKE_PTR(n
);
84 *ret_real_name
= TAKE_PTR(rn
);
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
;
97 assert(ret_real_name
);
99 if (!valid_user_group_name(name
, 0))
102 e
= startswith(name
, "vu-");
110 if (parse_uid(d
+ 1, &uid
) < 0)
113 mn
= strndup(e
, d
- e
);
117 machine
= hashmap_get(m
->machines
, mn
);
121 if (machine
->class != MACHINE_CONTAINER
)
124 r
= machine_translate_uid(machine
, uid
, &converted_uid
);
128 if (asprintf(&rn
, "UID " UID_FMT
" of Container %s", uid
, machine
->name
) < 0)
130 if (!valid_gecos(rn
))
133 *ret_uid
= converted_uid
;
134 *ret_real_name
= TAKE_PTR(rn
);
138 static int vl_method_get_user_record(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
140 static const JsonDispatch dispatch_table
[] = {
141 { "uid", JSON_VARIANT_UNSIGNED
, json_dispatch_uid_gid
, offsetof(LookupParameters
, uid
), 0 },
142 { "userName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, user_name
), JSON_SAFE
},
143 { "service", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, service
), 0 },
147 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
148 LookupParameters p
= {
151 _cleanup_free_
char *found_name
= NULL
, *found_real_name
= NULL
;
152 uid_t found_uid
= UID_INVALID
, uid
;
153 Manager
*m
= userdata
;
160 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
164 if (!streq_ptr(p
.service
, "io.systemd.Machine"))
165 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
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
);
172 return varlink_error(link
, "io.systemd.UserDatabase.EnumerationNotSupported", NULL
);
174 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
178 uid
= uid_is_valid(found_uid
) ? found_uid
: p
.uid
;
179 un
= found_name
?: p
.user_name
;
181 if (!user_match_lookup_parameters(&p
, un
, uid
))
182 return varlink_error(link
, "io.systemd.UserDatabase.ConflictingRecordFound", NULL
);
184 r
= build_user_json(un
, uid
, found_real_name
, &v
);
188 return varlink_reply(link
, v
);
191 static int build_group_json(const char *group_name
, gid_t gid
, const char *description
, JsonVariant
**ret
) {
193 assert(gid_is_valid(gid
));
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_STRING("io.systemd.Machine")),
202 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("container"))))));
205 static bool group_match_lookup_parameters(LookupParameters
*p
, const char *name
, gid_t gid
) {
208 if (p
->group_name
&& !streq(name
, p
->group_name
))
211 if (gid_is_valid(p
->gid
) && gid
!= p
->gid
)
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
;
224 assert(gid_is_valid(gid
));
226 assert(ret_description
);
228 if (gid
< 0x10000) /* Host GID range */
231 r
= manager_find_machine_for_gid(m
, gid
, &machine
, &converted_gid
);
237 if (asprintf(&n
, "vg-%s-" GID_FMT
, machine
->name
, converted_gid
) < 0)
240 if (!valid_user_group_name(n
, 0))
243 if (asprintf(&d
, "GID " GID_FMT
" of Container %s", converted_gid
, machine
->name
) < 0)
248 *ret_name
= TAKE_PTR(n
);
249 *ret_description
= TAKE_PTR(d
);
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
;
263 assert(ret_description
);
265 if (!valid_user_group_name(name
, 0))
268 e
= startswith(name
, "vg-");
276 if (parse_gid(d
+ 1, &gid
) < 0)
279 mn
= strndup(e
, d
- e
);
283 machine
= hashmap_get(m
->machines
, mn
);
287 if (machine
->class != MACHINE_CONTAINER
)
290 r
= machine_translate_gid(machine
, gid
, &converted_gid
);
294 if (asprintf(&desc
, "GID " GID_FMT
" of Container %s", gid
, machine
->name
) < 0)
296 if (!valid_gecos(desc
))
299 *ret_gid
= converted_gid
;
300 *ret_description
= desc
;
304 static int vl_method_get_group_record(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
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 },
313 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
314 LookupParameters p
= {
317 _cleanup_free_
char *found_name
= NULL
, *found_description
= NULL
;
318 uid_t found_gid
= GID_INVALID
, gid
;
319 Manager
*m
= userdata
;
326 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
330 if (!streq_ptr(p
.service
, "io.systemd.Machine"))
331 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
333 if (gid_is_valid(p
.gid
))
334 r
= group_lookup_gid(m
, p
.gid
, &found_name
, &found_description
);
335 else if (p
.group_name
)
336 r
= group_lookup_name(m
, p
.group_name
, (uid_t
*) &found_gid
, &found_description
);
338 return varlink_error(link
, "io.systemd.UserDatabase.EnumerationNotSupported", NULL
);
340 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
344 gid
= gid_is_valid(found_gid
) ? found_gid
: p
.gid
;
345 gn
= found_name
?: p
.group_name
;
347 if (!group_match_lookup_parameters(&p
, gn
, gid
))
348 return varlink_error(link
, "io.systemd.UserDatabase.ConflictingRecordFound", NULL
);
350 r
= build_group_json(gn
, gid
, found_description
, &v
);
354 return varlink_reply(link
, v
);
357 static int vl_method_get_memberships(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
359 static const JsonDispatch dispatch_table
[] = {
360 { "userName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, user_name
), JSON_SAFE
},
361 { "groupName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, group_name
), JSON_SAFE
},
362 { "service", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, service
), 0 },
366 LookupParameters p
= {};
371 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
375 if (!streq_ptr(p
.service
, "io.systemd.Machine"))
376 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
378 /* We don't support auxiliary groups for machines. */
379 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
382 int manager_varlink_init(Manager
*m
) {
383 _cleanup_(varlink_server_unrefp
) VarlinkServer
*s
= NULL
;
388 if (m
->varlink_server
)
391 r
= varlink_server_new(&s
, VARLINK_SERVER_ACCOUNT_UID
);
393 return log_error_errno(r
, "Failed to allocate varlink server object: %m");
395 varlink_server_set_userdata(s
, m
);
397 r
= varlink_server_bind_method_many(
399 "io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record
,
400 "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record
,
401 "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships
);
403 return log_error_errno(r
, "Failed to register varlink methods: %m");
405 (void) mkdir_p("/run/systemd/userdb", 0755);
407 r
= varlink_server_listen_address(s
, "/run/systemd/userdb/io.systemd.Machine", 0666);
409 return log_error_errno(r
, "Failed to bind to varlink socket: %m");
411 r
= varlink_server_attach_event(s
, m
->event
, SD_EVENT_PRIORITY_NORMAL
);
413 return log_error_errno(r
, "Failed to attach varlink connection to event loop: %m");
415 m
->varlink_server
= TAKE_PTR(s
);
419 void manager_varlink_done(Manager
*m
) {
422 m
->varlink_server
= varlink_server_unref(m
->varlink_server
);