1 /* SPDX-License-Identifier: LGPL-2.1+ */
10 #include "group-record-nss.h"
11 #include "group-record.h"
13 #include "main-func.h"
14 #include "process-util.h"
16 #include "time-util.h"
17 #include "user-record-nss.h"
18 #include "user-record.h"
19 #include "user-util.h"
23 #define ITERATIONS_MAX 64U
24 #define RUNTIME_MAX_USEC (5 * USEC_PER_MINUTE)
25 #define PRESSURE_SLEEP_TIME_USEC (50 * USEC_PER_MSEC)
26 #define CONNECTION_IDLE_USEC (15 * USEC_PER_SEC)
27 #define LISTEN_IDLE_USEC (90 * USEC_PER_SEC)
29 typedef struct LookupParameters
{
30 const char *user_name
;
31 const char *group_name
;
39 static int add_nss_service(JsonVariant
**v
) {
40 _cleanup_(json_variant_unrefp
) JsonVariant
*status
= NULL
, *z
= NULL
;
41 char buf
[SD_ID128_STRING_MAX
];
47 /* Patch in service field if it's missing. The assumption here is that this field is unset only for
50 if (json_variant_by_key(*v
, "service"))
53 r
= sd_id128_get_machine(&mid
);
57 status
= json_variant_ref(json_variant_by_key(*v
, "status"));
58 z
= json_variant_ref(json_variant_by_key(status
, sd_id128_to_string(mid
, buf
)));
60 if (json_variant_by_key(z
, "service"))
63 r
= json_variant_set_field_string(&z
, "service", "io.systemd.NameServiceSwitch");
67 r
= json_variant_set_field(&status
, buf
, z
);
71 return json_variant_set_field(v
, "status", status
);
74 static int build_user_json(Varlink
*link
, UserRecord
*ur
, JsonVariant
**ret
) {
75 _cleanup_(user_record_unrefp
) UserRecord
*stripped
= NULL
;
76 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
77 UserRecordLoadFlags flags
;
85 r
= varlink_get_peer_uid(link
, &peer_uid
);
87 log_debug_errno(r
, "Unable to query peer UID, ignoring: %m");
90 trusted
= peer_uid
== 0 || peer_uid
== ur
->uid
;
92 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
;
94 flags
|= USER_RECORD_ALLOW_PRIVILEGED
;
96 flags
|= USER_RECORD_STRIP_PRIVILEGED
;
98 r
= user_record_clone(ur
, flags
, &stripped
);
102 stripped
->incomplete
=
104 (FLAGS_SET(ur
->mask
, USER_RECORD_PRIVILEGED
) &&
105 !FLAGS_SET(stripped
->mask
, USER_RECORD_PRIVILEGED
));
107 v
= json_variant_ref(stripped
->json
);
108 r
= add_nss_service(&v
);
112 return json_build(ret
, JSON_BUILD_OBJECT(
113 JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(v
)),
114 JSON_BUILD_PAIR("incomplete", JSON_BUILD_BOOLEAN(stripped
->incomplete
))));
117 static int vl_method_get_user_record(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
119 static const JsonDispatch dispatch_table
[] = {
120 { "uid", JSON_VARIANT_UNSIGNED
, json_dispatch_uid_gid
, offsetof(LookupParameters
, uid
), 0 },
121 { "userName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, user_name
), 0 },
122 { "service", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, service
), 0 },
126 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
127 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
128 LookupParameters p
= {
135 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
139 if (streq_ptr(p
.service
, "io.systemd.NameServiceSwitch")) {
140 if (uid_is_valid(p
.uid
))
141 r
= nss_user_record_by_uid(p
.uid
, true, &hr
);
142 else if (p
.user_name
)
143 r
= nss_user_record_by_name(p
.user_name
, true, &hr
);
145 _cleanup_(json_variant_unrefp
) JsonVariant
*last
= NULL
;
150 _cleanup_(user_record_unrefp
) UserRecord
*z
= NULL
;
151 _cleanup_free_
char *sbuf
= NULL
;
159 log_debug_errno(errno
, "Failure while iterating through NSS user database, ignoring: %m");
164 r
= nss_spwd_for_passwd(pw
, &spwd
, &sbuf
);
166 log_debug_errno(r
, "Failed to acquire shadow entry for user %s, ignoring: %m", pw
->pw_name
);
168 r
= nss_passwd_to_user_record(pw
, NULL
, &z
);
175 r
= varlink_notify(link
, last
);
181 last
= json_variant_unref(last
);
184 r
= build_user_json(link
, z
, &last
);
194 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
196 return varlink_reply(link
, last
);
199 } else if (streq_ptr(p
.service
, "io.systemd.Multiplexer")) {
201 if (uid_is_valid(p
.uid
))
202 r
= userdb_by_uid(p
.uid
, USERDB_AVOID_MULTIPLEXER
, &hr
);
203 else if (p
.user_name
)
204 r
= userdb_by_name(p
.user_name
, USERDB_AVOID_MULTIPLEXER
, &hr
);
206 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
207 _cleanup_(json_variant_unrefp
) JsonVariant
*last
= NULL
;
209 r
= userdb_all(USERDB_AVOID_MULTIPLEXER
, &iterator
);
214 _cleanup_(user_record_unrefp
) UserRecord
*z
= NULL
;
216 r
= userdb_iterator_get(iterator
, &z
);
223 r
= varlink_notify(link
, last
);
227 last
= json_variant_unref(last
);
230 r
= build_user_json(link
, z
, &last
);
236 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
238 return varlink_reply(link
, last
);
241 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
243 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
245 log_debug_errno(r
, "User lookup failed abnormally: %m");
246 return varlink_error(link
, "io.systemd.UserDatabase.ServiceNotAvailable", NULL
);
249 if ((uid_is_valid(p
.uid
) && hr
->uid
!= p
.uid
) ||
250 (p
.user_name
&& !streq(hr
->user_name
, p
.user_name
)))
251 return varlink_error(link
, "io.systemd.UserDatabase.ConflictingRecordFound", NULL
);
253 r
= build_user_json(link
, hr
, &v
);
257 return varlink_reply(link
, v
);
260 static int build_group_json(Varlink
*link
, GroupRecord
*gr
, JsonVariant
**ret
) {
261 _cleanup_(group_record_unrefp
) GroupRecord
*stripped
= NULL
;
262 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
263 UserRecordLoadFlags flags
;
271 r
= varlink_get_peer_uid(link
, &peer_uid
);
273 log_debug_errno(r
, "Unable to query peer UID, ignoring: %m");
276 trusted
= peer_uid
== 0;
278 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
;
280 flags
|= USER_RECORD_ALLOW_PRIVILEGED
;
282 flags
|= USER_RECORD_STRIP_PRIVILEGED
;
284 r
= group_record_clone(gr
, flags
, &stripped
);
288 stripped
->incomplete
=
290 (FLAGS_SET(gr
->mask
, USER_RECORD_PRIVILEGED
) &&
291 !FLAGS_SET(stripped
->mask
, USER_RECORD_PRIVILEGED
));
293 v
= json_variant_ref(gr
->json
);
294 r
= add_nss_service(&v
);
298 return json_build(ret
, JSON_BUILD_OBJECT(
299 JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(v
)),
300 JSON_BUILD_PAIR("incomplete", JSON_BUILD_BOOLEAN(stripped
->incomplete
))));
303 static int vl_method_get_group_record(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
305 static const JsonDispatch dispatch_table
[] = {
306 { "gid", JSON_VARIANT_UNSIGNED
, json_dispatch_uid_gid
, offsetof(LookupParameters
, gid
), 0 },
307 { "groupName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, group_name
), 0 },
308 { "service", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, service
), 0 },
312 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
313 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
314 LookupParameters p
= {
321 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
325 if (streq_ptr(p
.service
, "io.systemd.NameServiceSwitch")) {
327 if (gid_is_valid(p
.gid
))
328 r
= nss_group_record_by_gid(p
.gid
, true, &g
);
329 else if (p
.group_name
)
330 r
= nss_group_record_by_name(p
.group_name
, true, &g
);
332 _cleanup_(json_variant_unrefp
) JsonVariant
*last
= NULL
;
337 _cleanup_(group_record_unrefp
) GroupRecord
*z
= NULL
;
338 _cleanup_free_
char *sbuf
= NULL
;
346 log_debug_errno(errno
, "Failure while iterating through NSS group database, ignoring: %m");
351 r
= nss_sgrp_for_group(grp
, &sgrp
, &sbuf
);
353 log_debug_errno(r
, "Failed to acquire shadow entry for group %s, ignoring: %m", grp
->gr_name
);
355 r
= nss_group_to_group_record(grp
, r
>= 0 ? &sgrp
: NULL
, &z
);
362 r
= varlink_notify(link
, last
);
368 last
= json_variant_unref(last
);
371 r
= build_group_json(link
, z
, &last
);
381 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
383 return varlink_reply(link
, last
);
386 } else if (streq_ptr(p
.service
, "io.systemd.Multiplexer")) {
388 if (gid_is_valid(p
.gid
))
389 r
= groupdb_by_gid(p
.gid
, USERDB_AVOID_MULTIPLEXER
, &g
);
390 else if (p
.group_name
)
391 r
= groupdb_by_name(p
.group_name
, USERDB_AVOID_MULTIPLEXER
, &g
);
393 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
394 _cleanup_(json_variant_unrefp
) JsonVariant
*last
= NULL
;
396 r
= groupdb_all(USERDB_AVOID_MULTIPLEXER
, &iterator
);
401 _cleanup_(group_record_unrefp
) GroupRecord
*z
= NULL
;
403 r
= groupdb_iterator_get(iterator
, &z
);
410 r
= varlink_notify(link
, last
);
414 last
= json_variant_unref(last
);
417 r
= build_group_json(link
, z
, &last
);
423 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
425 return varlink_reply(link
, last
);
428 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
430 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
432 log_debug_errno(r
, "Group lookup failed abnormally: %m");
433 return varlink_error(link
, "io.systemd.UserDatabase.ServiceNotAvailable", NULL
);
436 if ((uid_is_valid(p
.gid
) && g
->gid
!= p
.gid
) ||
437 (p
.group_name
&& !streq(g
->group_name
, p
.group_name
)))
438 return varlink_error(link
, "io.systemd.UserDatabase.ConflictingRecordFound", NULL
);
440 r
= build_group_json(link
, g
, &v
);
444 return varlink_reply(link
, v
);
447 static int vl_method_get_memberships(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
448 static const JsonDispatch dispatch_table
[] = {
449 { "userName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, user_name
), 0 },
450 { "groupName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, group_name
), 0 },
451 { "service", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(LookupParameters
, service
), 0 },
455 LookupParameters p
= {};
460 r
= json_dispatch(parameters
, dispatch_table
, NULL
, 0, &p
);
464 if (streq_ptr(p
.service
, "io.systemd.NameServiceSwitch")) {
467 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
468 const char *last
= NULL
;
471 r
= nss_group_record_by_name(p
.group_name
, true, &g
);
473 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
477 STRV_FOREACH(i
, g
->members
) {
479 if (p
.user_name
&& !streq_ptr(p
.user_name
, *i
))
483 r
= varlink_notifyb(link
, JSON_BUILD_OBJECT(
484 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last
)),
485 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(g
->group_name
))));
494 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
496 return varlink_replyb(link
, JSON_BUILD_OBJECT(
497 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last
)),
498 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(g
->group_name
))));
500 _cleanup_free_
char *last_user_name
= NULL
, *last_group_name
= NULL
;
506 const char* two
[2], **users
, **i
;
512 log_debug_errno(errno
, "Failure while iterating through NSS group database, ignoring: %m");
518 if (!strv_contains(grp
->gr_mem
, p
.user_name
))
521 two
[0] = p
.user_name
;
526 users
= (const char**) grp
->gr_mem
;
528 STRV_FOREACH(i
, users
) {
530 if (last_user_name
) {
531 assert(last_group_name
);
533 r
= varlink_notifyb(link
, JSON_BUILD_OBJECT(
534 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name
)),
535 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name
))));
541 free(last_user_name
);
542 free(last_group_name
);
545 last_user_name
= strdup(*i
);
546 last_group_name
= strdup(grp
->gr_name
);
547 if (!last_user_name
|| !last_group_name
) {
556 if (!last_user_name
) {
557 assert(!last_group_name
);
558 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
561 assert(last_group_name
);
563 return varlink_replyb(link
, JSON_BUILD_OBJECT(
564 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name
)),
565 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name
))));
568 } else if (streq_ptr(p
.service
, "io.systemd.Multiplexer")) {
570 _cleanup_free_
char *last_user_name
= NULL
, *last_group_name
= NULL
;
571 _cleanup_(userdb_iterator_freep
) UserDBIterator
*iterator
= NULL
;
574 r
= membershipdb_by_group(p
.group_name
, USERDB_AVOID_MULTIPLEXER
, &iterator
);
575 else if (p
.user_name
)
576 r
= membershipdb_by_user(p
.user_name
, USERDB_AVOID_MULTIPLEXER
, &iterator
);
578 r
= membershipdb_all(USERDB_AVOID_MULTIPLEXER
, &iterator
);
583 _cleanup_free_
char *user_name
= NULL
, *group_name
= NULL
;
585 r
= membershipdb_iterator_get(iterator
, &user_name
, &group_name
);
591 /* If both group + user are specified do a-posteriori filtering */
592 if (p
.group_name
&& p
.user_name
&& !streq(group_name
, p
.group_name
))
595 if (last_user_name
) {
596 assert(last_group_name
);
598 r
= varlink_notifyb(link
, JSON_BUILD_OBJECT(
599 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name
)),
600 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name
))));
604 free(last_user_name
);
605 free(last_group_name
);
608 last_user_name
= TAKE_PTR(user_name
);
609 last_group_name
= TAKE_PTR(group_name
);
612 if (!last_user_name
) {
613 assert(!last_group_name
);
614 return varlink_error(link
, "io.systemd.UserDatabase.NoRecordFound", NULL
);
617 assert(last_group_name
);
619 return varlink_replyb(link
, JSON_BUILD_OBJECT(
620 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name
)),
621 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name
))));
624 return varlink_error(link
, "io.systemd.UserDatabase.BadService", NULL
);
627 static int process_connection(VarlinkServer
*server
, int fd
) {
628 _cleanup_(varlink_close_unrefp
) Varlink
*vl
= NULL
;
631 r
= varlink_server_add_connection(server
, fd
, &vl
);
634 return log_error_errno(r
, "Failed to add connection: %m");
637 vl
= varlink_ref(vl
);
640 r
= varlink_process(vl
);
641 if (r
== -ENOTCONN
) {
642 log_debug("Connection terminated.");
646 return log_error_errno(r
, "Failed to process connection: %m");
650 r
= varlink_wait(vl
, CONNECTION_IDLE_USEC
);
652 return log_error_errno(r
, "Failed to wait for connection events: %m");
660 static int run(int argc
, char *argv
[]) {
661 usec_t start_time
, listen_idle_usec
, last_busy_usec
= USEC_INFINITY
;
662 _cleanup_(varlink_server_unrefp
) VarlinkServer
*server
= NULL
;
663 unsigned n_iterations
= 0;
668 m
= sd_listen_fds(false);
670 return log_error_errno(m
, "Failed to determine number of listening fds: %m");
672 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No socket to listen on received.");
674 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Worker can only listen on a single socket at a time.");
676 listen_fd
= SD_LISTEN_FDS_START
;
678 r
= fd_nonblock(listen_fd
, false);
680 return log_error_errno(r
, "Failed to turn off non-blocking mode for listening socket: %m");
682 r
= varlink_server_new(&server
, 0);
684 return log_error_errno(r
, "Failed to allocate server: %m");
686 r
= varlink_server_bind_method_many(
688 "io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record
,
689 "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record
,
690 "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships
);
692 return log_error_errno(r
, "Failed to bind methods: %m");
694 r
= getenv_bool("USERDB_FIXED_WORKER");
696 return log_error_errno(r
, "Failed to parse USERDB_FIXED_WORKER: %m");
697 listen_idle_usec
= r
? USEC_INFINITY
: LISTEN_IDLE_USEC
;
699 r
= userdb_block_nss_systemd(true);
701 return log_error_errno(r
, "Failed to disable userdb NSS compatibility: %m");
703 start_time
= now(CLOCK_MONOTONIC
);
706 _cleanup_close_
int fd
= -1;
709 /* Exit the worker in regular intervals, to flush out all memory use */
710 if (n_iterations
++ > ITERATIONS_MAX
) {
711 log_debug("Exiting worker, processed %u iterations, that's enough.", n_iterations
);
715 n
= now(CLOCK_MONOTONIC
);
716 if (n
>= usec_add(start_time
, RUNTIME_MAX_USEC
)) {
717 char buf
[FORMAT_TIMESPAN_MAX
];
718 log_debug("Exiting worker, ran for %s, that's enough.",
719 format_timespan(buf
, sizeof(buf
), usec_sub_unsigned(n
, start_time
), 0));
723 if (last_busy_usec
== USEC_INFINITY
)
725 else if (listen_idle_usec
!= USEC_INFINITY
&& n
>= usec_add(last_busy_usec
, listen_idle_usec
)) {
726 char buf
[FORMAT_TIMESPAN_MAX
];
727 log_debug("Exiting worker, been idle for %s.",
728 format_timespan(buf
, sizeof(buf
), usec_sub_unsigned(n
, last_busy_usec
), 0));
732 (void) rename_process("systemd-userwork: waiting...");
734 fd
= accept4(listen_fd
, NULL
, NULL
, SOCK_NONBLOCK
|SOCK_CLOEXEC
);
738 (void) rename_process("systemd-userwork: processing...");
741 continue; /* The listening socket has SO_RECVTIMEO set, hence a timeout is expected
742 * after a while, let's check if it's time to exit though. */
744 continue; /* Might be that somebody attached via strace, let's just continue in that
747 return log_error_errno(fd
, "Failed to accept() from listening socket: %m");
749 if (now(CLOCK_MONOTONIC
) <= usec_add(n
, PRESSURE_SLEEP_TIME_USEC
)) {
750 /* We only slept a very short time? If so, let's see if there are more sockets
751 * pending, and if so, let's ask our parent for more workers */
753 r
= fd_wait_for_event(listen_fd
, POLLIN
, 0);
755 return log_error_errno(r
, "Failed to test for POLLIN on listening socket: %m");
757 if (FLAGS_SET(r
, POLLIN
)) {
762 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Parent already died?");
764 if (kill(parent
, SIGUSR2
) < 0)
765 return log_error_errno(errno
, "Failed to kill our own parent.");
769 (void) process_connection(server
, TAKE_FD(fd
));
770 last_busy_usec
= USEC_INFINITY
;
776 DEFINE_MAIN_FUNCTION(run
);