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
= userdata
;
94 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
98 if (!streq_ptr(p
.service
, m
->userdb_service
))
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
, m
->userdb_service
))
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
, m
->userdb_service
))
281 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
284 const char *last
= NULL
;
286 h
= hashmap_get(m
->homes_by_name
, p
.user_name
);
288 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
291 if (!strv_contains(h
->record
->member_of
, p
.group_name
))
292 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
294 return varlink_replyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h
->user_name
)),
295 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p
.group_name
))));
298 STRV_FOREACH(i
, h
->record
->member_of
) {
300 r
= varlink_notifyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h
->user_name
)),
301 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last
))));
310 return varlink_replyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h
->user_name
)),
311 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last
))));
313 } else if (p
.group_name
) {
314 const char *last
= NULL
;
316 HASHMAP_FOREACH(h
, m
->homes_by_name
) {
318 if (!strv_contains(h
->record
->member_of
, p
.group_name
))
322 r
= varlink_notifyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last
)),
323 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p
.group_name
))));
332 return varlink_replyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last
)),
333 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p
.group_name
))));
335 const char *last_user_name
= NULL
, *last_group_name
= NULL
;
337 HASHMAP_FOREACH(h
, m
->homes_by_name
)
338 STRV_FOREACH(j
, h
->record
->member_of
) {
340 if (last_user_name
) {
341 assert(last_group_name
);
343 r
= varlink_notifyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name
)),
344 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name
))));
350 last_user_name
= h
->user_name
;
351 last_group_name
= *j
;
354 if (last_user_name
) {
355 assert(last_group_name
);
356 return varlink_replyb(link
, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name
)),
357 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name
))));
361 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);