1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
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
|USER_RECORD_PERMISSIVE
;
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
= ASSERT_PTR(userdata
);
93 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
97 if (!streq_ptr(p
.service
, m
->userdb_service
))
98 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
100 if (uid_is_valid(p
.uid
))
101 h
= hashmap_get(m
->homes_by_uid
, UID_TO_PTR(p
.uid
));
102 else if (p
.user_name
)
103 h
= hashmap_get(m
->homes_by_name
, p
.user_name
);
106 /* If neither UID nor name was specified, then dump all homes. Do so with varlink_notify()
107 * for all entries but the last, so that clients can stream the results, and easily process
110 HASHMAP_FOREACH(h
, m
->homes_by_name
) {
112 if (!home_user_match_lookup_parameters(&p
, h
))
116 /* An entry set from the previous iteration? Then send it now */
117 r
= varlink_notify(link
, v
);
121 v
= json_variant_unref(v
);
124 trusted
= client_is_trusted(link
, h
);
126 r
= build_user_json(h
, trusted
, &v
);
132 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
134 return varlink_reply(link
, v
);
138 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
140 if (!home_user_match_lookup_parameters(&p
, h
))
141 return varlink_error(link
, "io.systemd.UserDatabase.ConflictingRecordFound", NULL
);
143 trusted
= client_is_trusted(link
, h
);
145 r
= build_user_json(h
, trusted
, &v
);
149 return varlink_reply(link
, v
);
152 static int build_group_json(Home
*h
, JsonVariant
**ret
) {
153 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
159 g
= group_record_new();
163 r
= group_record_synthesize(g
, h
->record
);
167 assert(!FLAGS_SET(g
->mask
, USER_RECORD_SECRET
));
168 assert(!FLAGS_SET(g
->mask
, USER_RECORD_PRIVILEGED
));
170 return json_build(ret
,
172 JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(g
->json
))));
175 static bool home_group_match_lookup_parameters(LookupParameters
*p
, Home
*h
) {
179 if (p
->group_name
&& !streq(h
->user_name
, p
->group_name
))
182 if (gid_is_valid(p
->gid
) && h
->uid
!= (uid_t
) p
->gid
)
188 int vl_method_get_group_record(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
190 static const JsonDispatch dispatch_table
[] = {
191 { "gid", JSON_VARIANT_UNSIGNED
, json_dispatch_uid_gid
, offsetof(LookupParameters
, gid
), 0 },
192 { "groupName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, group_name
), JSON_SAFE
},
193 { "service", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, service
), 0 },
197 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
198 LookupParameters p
= {
201 Manager
*m
= ASSERT_PTR(userdata
);
207 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
211 if (!streq_ptr(p
.service
, m
->userdb_service
))
212 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
214 if (gid_is_valid(p
.gid
))
215 h
= hashmap_get(m
->homes_by_uid
, UID_TO_PTR((uid_t
) p
.gid
));
216 else if (p
.group_name
)
217 h
= hashmap_get(m
->homes_by_name
, p
.group_name
);
220 HASHMAP_FOREACH(h
, m
->homes_by_name
) {
222 if (!home_group_match_lookup_parameters(&p
, h
))
226 r
= varlink_notify(link
, v
);
230 v
= json_variant_unref(v
);
233 r
= build_group_json(h
, &v
);
239 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
241 return varlink_reply(link
, v
);
245 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
247 if (!home_group_match_lookup_parameters(&p
, h
))
248 return varlink_error(link
, "io.systemd.UserDatabase.ConflictingRecordFound", NULL
);
250 r
= build_group_json(h
, &v
);
254 return varlink_reply(link
, v
);
257 int vl_method_get_memberships(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
259 static const JsonDispatch dispatch_table
[] = {
260 { "userName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, user_name
), JSON_SAFE
},
261 { "groupName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, group_name
), JSON_SAFE
},
262 { "service", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, service
), 0 },
266 Manager
*m
= ASSERT_PTR(userdata
);
267 LookupParameters p
= {};
273 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
277 if (!streq_ptr(p
.service
, m
->userdb_service
))
278 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
281 const char *last
= NULL
;
283 h
= hashmap_get(m
->homes_by_name
, p
.user_name
);
285 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
288 if (!strv_contains(h
->record
->member_of
, p
.group_name
))
289 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
291 return varlink_replyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h
->user_name
)),
292 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p
.group_name
))));
295 STRV_FOREACH(i
, h
->record
->member_of
) {
297 r
= varlink_notifyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h
->user_name
)),
298 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last
))));
307 return varlink_replyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h
->user_name
)),
308 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last
))));
310 } else if (p
.group_name
) {
311 const char *last
= NULL
;
313 HASHMAP_FOREACH(h
, m
->homes_by_name
) {
315 if (!strv_contains(h
->record
->member_of
, p
.group_name
))
319 r
= varlink_notifyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last
)),
320 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p
.group_name
))));
329 return varlink_replyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last
)),
330 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p
.group_name
))));
332 const char *last_user_name
= NULL
, *last_group_name
= NULL
;
334 HASHMAP_FOREACH(h
, m
->homes_by_name
)
335 STRV_FOREACH(j
, h
->record
->member_of
) {
337 if (last_user_name
) {
338 assert(last_group_name
);
340 r
= varlink_notifyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name
)),
341 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name
))));
347 last_user_name
= h
->user_name
;
348 last_group_name
= *j
;
351 if (last_user_name
) {
352 assert(last_group_name
);
353 return varlink_replyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name
)),
354 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name
))));
358 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);