1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "group-record.h"
4 #include "homed-varlink.h"
6 #include "user-record-util.h"
7 #include "user-record.h"
9 #include "format-util.h"
11 typedef struct LookupParameters
{
12 const char *user_name
;
13 const char *group_name
;
21 static bool client_is_trusted(Varlink
*link
, Home
*h
) {
28 r
= varlink_get_peer_uid(link
, &peer_uid
);
30 log_debug_errno(r
, "Unable to query peer UID, ignoring: %m");
34 return peer_uid
== 0 || peer_uid
== h
->uid
;
37 static int build_user_json(Home
*h
, bool trusted
, JsonVariant
**ret
) {
38 _cleanup_(user_record_unrefp
) UserRecord
*augmented
= NULL
;
39 UserRecordLoadFlags flags
;
45 flags
= USER_RECORD_REQUIRE_REGULAR
|USER_RECORD_ALLOW_PER_MACHINE
|USER_RECORD_ALLOW_BINDING
|USER_RECORD_STRIP_SECRET
|USER_RECORD_ALLOW_STATUS
|USER_RECORD_ALLOW_SIGNATURE
;
47 flags
|= USER_RECORD_ALLOW_PRIVILEGED
;
49 flags
|= USER_RECORD_STRIP_PRIVILEGED
;
51 r
= home_augment_status(h
, flags
, &augmented
);
55 return json_build(ret
, JSON_BUILD_OBJECT(
56 JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(augmented
->json
)),
57 JSON_BUILD_PAIR("incomplete", JSON_BUILD_BOOLEAN(augmented
->incomplete
))));
60 static bool home_user_match_lookup_parameters(LookupParameters
*p
, Home
*h
) {
64 if (p
->user_name
&& !streq(p
->user_name
, h
->user_name
))
67 if (uid_is_valid(p
->uid
) && h
->uid
!= p
->uid
)
73 int vl_method_get_user_record(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
75 static const JsonDispatch dispatch_table
[] = {
76 { "uid", JSON_VARIANT_UNSIGNED
, json_dispatch_uid_gid
, offsetof(LookupParameters
, uid
), 0 },
77 { "userName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, user_name
), JSON_SAFE
},
78 { "service", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, service
), 0 },
82 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
83 LookupParameters p
= {
86 Manager
*m
= userdata
;
94 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
98 if (!streq_ptr(p
.service
, "io.systemd.Home"))
99 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
101 if (uid_is_valid(p
.uid
))
102 h
= hashmap_get(m
->homes_by_uid
, UID_TO_PTR(p
.uid
));
103 else if (p
.user_name
)
104 h
= hashmap_get(m
->homes_by_name
, p
.user_name
);
107 /* If neither UID nor name was specified, then dump all homes. Do so with varlink_notify()
108 * for all entries but the last, so that clients can stream the results, and easily process
111 HASHMAP_FOREACH(h
, m
->homes_by_name
) {
113 if (!home_user_match_lookup_parameters(&p
, h
))
117 /* An entry set from the previous iteration? Then send it now */
118 r
= varlink_notify(link
, v
);
122 v
= json_variant_unref(v
);
125 trusted
= client_is_trusted(link
, h
);
127 r
= build_user_json(h
, trusted
, &v
);
133 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
135 return varlink_reply(link
, v
);
139 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
141 if (!home_user_match_lookup_parameters(&p
, h
))
142 return varlink_error(link
, "io.systemd.UserDatabase.ConflictingRecordFound", NULL
);
144 trusted
= client_is_trusted(link
, h
);
146 r
= build_user_json(h
, trusted
, &v
);
150 return varlink_reply(link
, v
);
153 static int build_group_json(Home
*h
, JsonVariant
**ret
) {
154 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
160 g
= group_record_new();
164 r
= group_record_synthesize(g
, h
->record
);
168 assert(!FLAGS_SET(g
->mask
, USER_RECORD_SECRET
));
169 assert(!FLAGS_SET(g
->mask
, USER_RECORD_PRIVILEGED
));
171 return json_build(ret
,
173 JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(g
->json
))));
176 static bool home_group_match_lookup_parameters(LookupParameters
*p
, Home
*h
) {
180 if (p
->group_name
&& !streq(h
->user_name
, p
->group_name
))
183 if (gid_is_valid(p
->gid
) && h
->uid
!= (uid_t
) p
->gid
)
189 int vl_method_get_group_record(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
191 static const JsonDispatch dispatch_table
[] = {
192 { "gid", JSON_VARIANT_UNSIGNED
, json_dispatch_uid_gid
, offsetof(LookupParameters
, gid
), 0 },
193 { "groupName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, group_name
), JSON_SAFE
},
194 { "service", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, service
), 0 },
198 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
199 LookupParameters p
= {
202 Manager
*m
= userdata
;
209 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
213 if (!streq_ptr(p
.service
, "io.systemd.Home"))
214 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
216 if (gid_is_valid(p
.gid
))
217 h
= hashmap_get(m
->homes_by_uid
, UID_TO_PTR((uid_t
) p
.gid
));
218 else if (p
.group_name
)
219 h
= hashmap_get(m
->homes_by_name
, p
.group_name
);
222 HASHMAP_FOREACH(h
, m
->homes_by_name
) {
224 if (!home_group_match_lookup_parameters(&p
, h
))
228 r
= varlink_notify(link
, v
);
232 v
= json_variant_unref(v
);
235 r
= build_group_json(h
, &v
);
241 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
243 return varlink_reply(link
, v
);
247 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
249 if (!home_group_match_lookup_parameters(&p
, h
))
250 return varlink_error(link
, "io.systemd.UserDatabase.ConflictingRecordFound", NULL
);
252 r
= build_group_json(h
, &v
);
256 return varlink_reply(link
, v
);
259 int vl_method_get_memberships(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
261 static const JsonDispatch dispatch_table
[] = {
262 { "userName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, user_name
), JSON_SAFE
},
263 { "groupName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, group_name
), JSON_SAFE
},
264 { "service", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, service
), 0 },
268 Manager
*m
= userdata
;
269 LookupParameters p
= {};
276 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
280 if (!streq_ptr(p
.service
, "io.systemd.Home"))
281 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
284 const char *last
= NULL
;
287 h
= hashmap_get(m
->homes_by_name
, p
.user_name
);
289 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
292 if (!strv_contains(h
->record
->member_of
, p
.group_name
))
293 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
295 return varlink_replyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h
->user_name
)),
296 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p
.group_name
))));
299 STRV_FOREACH(i
, h
->record
->member_of
) {
301 r
= varlink_notifyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h
->user_name
)),
302 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last
))));
311 return varlink_replyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h
->user_name
)),
312 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last
))));
314 } else if (p
.group_name
) {
315 const char *last
= NULL
;
317 HASHMAP_FOREACH(h
, m
->homes_by_name
) {
319 if (!strv_contains(h
->record
->member_of
, p
.group_name
))
323 r
= varlink_notifyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last
)),
324 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p
.group_name
))));
333 return varlink_replyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last
)),
334 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p
.group_name
))));
336 const char *last_user_name
= NULL
, *last_group_name
= NULL
;
338 HASHMAP_FOREACH(h
, m
->homes_by_name
) {
341 STRV_FOREACH(j
, h
->record
->member_of
) {
343 if (last_user_name
) {
344 assert(last_group_name
);
346 r
= varlink_notifyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name
)),
347 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name
))));
353 last_user_name
= h
->user_name
;
354 last_group_name
= *j
;
358 if (last_user_name
) {
359 assert(last_group_name
);
360 return varlink_replyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name
)),
361 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name
))));
365 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);