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 if (!valid_user_group_name(name
, 0))
100 e
= startswith(name
, "vu-");
108 if (parse_uid(d
+ 1, &uid
) < 0)
111 mn
= strndup(e
, d
- e
);
115 machine
= hashmap_get(m
->machines
, mn
);
119 if (machine
->class != MACHINE_CONTAINER
)
122 r
= machine_translate_uid(machine
, uid
, &converted_uid
);
126 if (asprintf(&rn
, "UID " UID_FMT
" of Container %s", uid
, machine
->name
) < 0)
128 if (!valid_gecos(rn
))
131 *ret_uid
= converted_uid
;
132 *ret_real_name
= TAKE_PTR(rn
);
136 static int vl_method_get_user_record(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
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 },
145 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
146 LookupParameters p
= {
149 _cleanup_free_
char *found_name
= NULL
, *found_real_name
= NULL
;
150 uid_t found_uid
= UID_INVALID
, uid
;
151 Manager
*m
= userdata
;
158 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
162 if (!streq_ptr(p
.service
, "io.systemd.Machine"))
163 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
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
);
170 return varlink_error(link
, "io.systemd.UserDatabase.EnumerationNotSupported", NULL
);
172 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
176 uid
= uid_is_valid(found_uid
) ? found_uid
: p
.uid
;
177 un
= found_name
?: p
.user_name
;
179 if (!user_match_lookup_parameters(&p
, un
, uid
))
180 return varlink_error(link
, "io.systemd.UserDatabase.ConflictingRecordFound", NULL
);
182 r
= build_user_json(un
, uid
, found_real_name
, &v
);
186 return varlink_reply(link
, v
);
189 static int build_group_json(const char *group_name
, gid_t gid
, JsonVariant
**ret
) {
191 assert(gid_is_valid(gid
));
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"))))));
202 static bool group_match_lookup_parameters(LookupParameters
*p
, const char *name
, gid_t gid
) {
205 if (p
->group_name
&& !streq(name
, p
->group_name
))
208 if (gid_is_valid(p
->gid
) && gid
!= p
->gid
)
214 static int group_lookup_gid(Manager
*m
, gid_t gid
, char **ret_name
) {
215 _cleanup_free_
char *n
= NULL
;
221 assert(gid_is_valid(gid
));
224 if (gid
< 0x10000) /* Host GID range */
227 r
= manager_find_machine_for_gid(m
, gid
, &machine
, &converted_gid
);
233 if (asprintf(&n
, "vg-%s-" GID_FMT
, machine
->name
, converted_gid
) < 0)
236 if (!valid_user_group_name(n
, 0))
239 *ret_name
= TAKE_PTR(n
);
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
;
252 if (!valid_user_group_name(name
, 0))
255 e
= startswith(name
, "vg-");
263 if (parse_gid(d
+ 1, &gid
) < 0)
266 mn
= strndup(e
, d
- e
);
270 machine
= hashmap_get(m
->machines
, mn
);
274 if (machine
->class != MACHINE_CONTAINER
)
277 r
= machine_translate_gid(machine
, gid
, &converted_gid
);
281 *ret_gid
= converted_gid
;
285 static int vl_method_get_group_record(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
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 },
294 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
295 LookupParameters p
= {
298 _cleanup_free_
char *found_name
= NULL
;
299 uid_t found_gid
= GID_INVALID
, gid
;
300 Manager
*m
= userdata
;
307 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
311 if (!streq_ptr(p
.service
, "io.systemd.Machine"))
312 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
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
);
319 return varlink_error(link
, "io.systemd.UserDatabase.EnumerationNotSupported", NULL
);
321 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
325 gid
= gid_is_valid(found_gid
) ? found_gid
: p
.gid
;
326 gn
= found_name
?: p
.group_name
;
328 if (!group_match_lookup_parameters(&p
, gn
, gid
))
329 return varlink_error(link
, "io.systemd.UserDatabase.ConflictingRecordFound", NULL
);
331 r
= build_group_json(gn
, gid
, &v
);
335 return varlink_reply(link
, v
);
338 static int vl_method_get_memberships(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
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 },
347 LookupParameters p
= {};
352 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
356 if (!streq_ptr(p
.service
, "io.systemd.Machine"))
357 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
359 /* We don't support auxiliary groups for machines. */
360 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
363 int manager_varlink_init(Manager
*m
) {
364 _cleanup_(varlink_server_unrefp
) VarlinkServer
*s
= NULL
;
369 if (m
->varlink_server
)
372 r
= varlink_server_new(&s
, VARLINK_SERVER_ACCOUNT_UID
);
374 return log_error_errno(r
, "Failed to allocate varlink server object: %m");
376 varlink_server_set_userdata(s
, m
);
378 r
= varlink_server_bind_method_many(
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
);
384 return log_error_errno(r
, "Failed to register varlink methods: %m");
386 (void) mkdir_p("/run/systemd/userdb", 0755);
388 r
= varlink_server_listen_address(s
, "/run/systemd/userdb/io.systemd.Machine", 0666);
390 return log_error_errno(r
, "Failed to bind to varlink socket: %m");
392 r
= varlink_server_attach_event(s
, m
->event
, SD_EVENT_PRIORITY_NORMAL
);
394 return log_error_errno(r
, "Failed to attach varlink connection to event loop: %m");
396 m
->varlink_server
= TAKE_PTR(s
);
400 void manager_varlink_done(Manager
*m
) {
403 m
->varlink_server
= varlink_server_unref(m
->varlink_server
);