1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "core-varlink.h"
8 typedef struct LookupParameters
{
10 const char *group_name
;
18 static int build_user_json(const char *user_name
, uid_t uid
, JsonVariant
**ret
) {
20 assert(uid_is_valid(uid
));
23 return json_build(ret
, JSON_BUILD_OBJECT(
24 JSON_BUILD_PAIR("record", JSON_BUILD_OBJECT(
25 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(user_name
)),
26 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid
)),
27 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(uid
)),
28 JSON_BUILD_PAIR("realName", JSON_BUILD_STRING("Dynamic User")),
29 JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING("/")),
30 JSON_BUILD_PAIR("shell", JSON_BUILD_STRING(NOLOGIN
)),
31 JSON_BUILD_PAIR("locked", JSON_BUILD_BOOLEAN(true)),
32 JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.DynamicUser")),
33 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("dynamic"))))));
36 static bool user_match_lookup_parameters(LookupParameters
*p
, const char *name
, uid_t uid
) {
39 if (p
->user_name
&& !streq(name
, p
->user_name
))
42 if (uid_is_valid(p
->uid
) && uid
!= p
->uid
)
48 static int vl_method_get_user_record(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
50 static const JsonDispatch dispatch_table
[] = {
51 { "uid", JSON_VARIANT_UNSIGNED
, json_dispatch_uid_gid
, offsetof(LookupParameters
, uid
), 0 },
52 { "userName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, user_name
), JSON_SAFE
},
53 { "service", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, service
), 0 },
57 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
58 LookupParameters p
= {
61 _cleanup_free_
char *found_name
= NULL
;
62 uid_t found_uid
= UID_INVALID
, uid
;
63 Manager
*m
= userdata
;
70 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
74 if (!streq_ptr(p
.service
, "io.systemd.DynamicUser"))
75 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
77 if (uid_is_valid(p
.uid
))
78 r
= dynamic_user_lookup_uid(m
, p
.uid
, &found_name
);
80 r
= dynamic_user_lookup_name(m
, p
.user_name
, &found_uid
);
85 HASHMAP_FOREACH(d
, m
->dynamic_users
, i
) {
86 r
= dynamic_user_current(d
, &uid
);
87 if (r
== -EAGAIN
) /* not realized yet? */
92 if (!user_match_lookup_parameters(&p
, d
->name
, uid
))
96 r
= varlink_notify(link
, v
);
100 v
= json_variant_unref(v
);
103 r
= build_user_json(d
->name
, uid
, &v
);
109 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
111 return varlink_reply(link
, v
);
114 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
118 uid
= uid_is_valid(found_uid
) ? found_uid
: p
.uid
;
119 un
= found_name
?: p
.user_name
;
121 if (!user_match_lookup_parameters(&p
, un
, uid
))
122 return varlink_error(link
, "io.systemd.UserDatabase.ConflictingRecordFound", NULL
);
124 r
= build_user_json(un
, uid
, &v
);
128 return varlink_reply(link
, v
);
131 static int build_group_json(const char *group_name
, gid_t gid
, JsonVariant
**ret
) {
133 assert(gid_is_valid(gid
));
136 return json_build(ret
, JSON_BUILD_OBJECT(
137 JSON_BUILD_PAIR("record", JSON_BUILD_OBJECT(
138 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(group_name
)),
139 JSON_BUILD_PAIR("description", JSON_BUILD_STRING("Dynamic Group")),
140 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid
)),
141 JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.DynamicUser")),
142 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("dynamic"))))));
145 static bool group_match_lookup_parameters(LookupParameters
*p
, const char *name
, gid_t gid
) {
148 if (p
->group_name
&& !streq(name
, p
->group_name
))
151 if (gid_is_valid(p
->gid
) && gid
!= p
->gid
)
157 static int vl_method_get_group_record(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
159 static const JsonDispatch dispatch_table
[] = {
160 { "gid", JSON_VARIANT_UNSIGNED
, json_dispatch_uid_gid
, offsetof(LookupParameters
, gid
), 0 },
161 { "groupName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, group_name
), JSON_SAFE
},
162 { "service", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, service
), 0 },
166 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
167 LookupParameters p
= {
170 _cleanup_free_
char *found_name
= NULL
;
171 uid_t found_gid
= GID_INVALID
, gid
;
172 Manager
*m
= userdata
;
179 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
183 if (!streq_ptr(p
.service
, "io.systemd.DynamicUser"))
184 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
186 if (gid_is_valid(p
.gid
))
187 r
= dynamic_user_lookup_uid(m
, (uid_t
) p
.gid
, &found_name
);
188 else if (p
.group_name
)
189 r
= dynamic_user_lookup_name(m
, p
.group_name
, (uid_t
*) &found_gid
);
194 HASHMAP_FOREACH(d
, m
->dynamic_users
, i
) {
197 r
= dynamic_user_current(d
, &uid
);
203 if (!group_match_lookup_parameters(&p
, d
->name
, (gid_t
) uid
))
207 r
= varlink_notify(link
, v
);
211 v
= json_variant_unref(v
);
214 r
= build_group_json(d
->name
, (gid_t
) uid
, &v
);
220 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
222 return varlink_reply(link
, v
);
225 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
229 gid
= gid_is_valid(found_gid
) ? found_gid
: p
.gid
;
230 gn
= found_name
?: p
.group_name
;
232 if (!group_match_lookup_parameters(&p
, gn
, gid
))
233 return varlink_error(link
, "io.systemd.UserDatabase.ConflictingRecordFound", NULL
);
235 r
= build_group_json(gn
, gid
, &v
);
239 return varlink_reply(link
, v
);
242 static int vl_method_get_memberships(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
244 static const JsonDispatch dispatch_table
[] = {
245 { "userName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, user_name
), JSON_SAFE
},
246 { "groupName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, group_name
), JSON_SAFE
},
247 { "service", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, service
), 0 },
251 LookupParameters p
= {};
256 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
260 if (!streq_ptr(p
.service
, "io.systemd.DynamicUser"))
261 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
263 /* We don't support auxiliary groups with dynamic users. */
264 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
267 int manager_varlink_init(Manager
*m
) {
268 _cleanup_(varlink_server_unrefp
) VarlinkServer
*s
= NULL
;
273 if (m
->varlink_server
)
276 if (!MANAGER_IS_SYSTEM(m
))
279 r
= varlink_server_new(&s
, VARLINK_SERVER_ACCOUNT_UID
);
281 return log_error_errno(r
, "Failed to allocate varlink server object: %m");
283 varlink_server_set_userdata(s
, m
);
285 r
= varlink_server_bind_method_many(
287 "io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record
,
288 "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record
,
289 "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships
);
291 return log_error_errno(r
, "Failed to register varlink methods: %m");
293 if (!MANAGER_IS_TEST_RUN(m
)) {
294 (void) mkdir_p("/run/systemd/userdb", 0755);
296 r
= varlink_server_listen_address(s
, "/run/systemd/userdb/io.systemd.DynamicUser", 0666);
298 return log_error_errno(r
, "Failed to bind to varlink socket: %m");
301 r
= varlink_server_attach_event(s
, m
->event
, SD_EVENT_PRIORITY_NORMAL
);
303 return log_error_errno(r
, "Failed to attach varlink connection to event loop: %m");
305 m
->varlink_server
= TAKE_PTR(s
);
309 void manager_varlink_done(Manager
*m
) {
312 m
->varlink_server
= varlink_server_unref(m
->varlink_server
);