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("gid", JSON_BUILD_UNSIGNED(gid
)),
140 JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.DynamicUser")),
141 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("dynamic"))))));
144 static bool group_match_lookup_parameters(LookupParameters
*p
, const char *name
, gid_t gid
) {
147 if (p
->group_name
&& !streq(name
, p
->group_name
))
150 if (gid_is_valid(p
->gid
) && gid
!= p
->gid
)
156 static int vl_method_get_group_record(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
158 static const JsonDispatch dispatch_table
[] = {
159 { "gid", JSON_VARIANT_UNSIGNED
, json_dispatch_uid_gid
, offsetof(LookupParameters
, gid
), 0 },
160 { "groupName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, group_name
), JSON_SAFE
},
161 { "service", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, service
), 0 },
165 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
166 LookupParameters p
= {
169 _cleanup_free_
char *found_name
= NULL
;
170 uid_t found_gid
= GID_INVALID
, gid
;
171 Manager
*m
= userdata
;
178 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
182 if (!streq_ptr(p
.service
, "io.systemd.DynamicUser"))
183 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
185 if (gid_is_valid(p
.gid
))
186 r
= dynamic_user_lookup_uid(m
, (uid_t
) p
.gid
, &found_name
);
187 else if (p
.group_name
)
188 r
= dynamic_user_lookup_name(m
, p
.group_name
, (uid_t
*) &found_gid
);
193 HASHMAP_FOREACH(d
, m
->dynamic_users
, i
) {
196 r
= dynamic_user_current(d
, &uid
);
202 if (!group_match_lookup_parameters(&p
, d
->name
, (gid_t
) uid
))
206 r
= varlink_notify(link
, v
);
210 v
= json_variant_unref(v
);
213 r
= build_group_json(d
->name
, (gid_t
) uid
, &v
);
219 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
221 return varlink_reply(link
, v
);
224 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
228 gid
= gid_is_valid(found_gid
) ? found_gid
: p
.gid
;
229 gn
= found_name
?: p
.group_name
;
231 if (!group_match_lookup_parameters(&p
, gn
, gid
))
232 return varlink_error(link
, "io.systemd.UserDatabase.ConflictingRecordFound", NULL
);
234 r
= build_group_json(gn
, gid
, &v
);
238 return varlink_reply(link
, v
);
241 static int vl_method_get_memberships(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
243 static const JsonDispatch dispatch_table
[] = {
244 { "userName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, user_name
), JSON_SAFE
},
245 { "groupName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, group_name
), JSON_SAFE
},
246 { "service", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, service
), 0 },
250 LookupParameters p
= {};
255 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
259 if (!streq_ptr(p
.service
, "io.systemd.DynamicUser"))
260 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
262 /* We don't support auxiliary groups with dynamic users. */
263 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
266 int manager_varlink_init(Manager
*m
) {
267 _cleanup_(varlink_server_unrefp
) VarlinkServer
*s
= NULL
;
272 if (m
->varlink_server
)
275 if (!MANAGER_IS_SYSTEM(m
))
278 r
= varlink_server_new(&s
, VARLINK_SERVER_ACCOUNT_UID
);
280 return log_error_errno(r
, "Failed to allocate varlink server object: %m");
282 varlink_server_set_userdata(s
, m
);
284 r
= varlink_server_bind_method_many(
286 "io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record
,
287 "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record
,
288 "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships
);
290 return log_error_errno(r
, "Failed to register varlink methods: %m");
292 (void) mkdir_p("/run/systemd/userdb", 0755);
294 r
= varlink_server_listen_address(s
, "/run/systemd/userdb/io.systemd.DynamicUser", 0666);
296 return log_error_errno(r
, "Failed to bind to varlink socket: %m");
298 r
= varlink_server_attach_event(s
, m
->event
, SD_EVENT_PRIORITY_NORMAL
);
300 return log_error_errno(r
, "Failed to attach varlink connection to event loop: %m");
302 m
->varlink_server
= TAKE_PTR(s
);
306 void manager_varlink_done(Manager
*m
) {
309 m
->varlink_server
= varlink_server_unref(m
->varlink_server
);