LCOV - code coverage report
Current view: top level - tty-ask-password-agent - tty-ask-password-agent.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 22 418 5.3 %
Date: 2019-08-22 15:41:25 Functions: 4 14 28.6 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : /***
       3             :   Copyright © 2015 Werner Fink
       4             : ***/
       5             : 
       6             : #include <errno.h>
       7             : #include <fcntl.h>
       8             : #include <getopt.h>
       9             : #include <poll.h>
      10             : #include <signal.h>
      11             : #include <stdbool.h>
      12             : #include <stddef.h>
      13             : #include <string.h>
      14             : #include <sys/inotify.h>
      15             : #include <sys/prctl.h>
      16             : #include <sys/signalfd.h>
      17             : #include <sys/socket.h>
      18             : #include <sys/stat.h>
      19             : #include <sys/types.h>
      20             : #include <sys/un.h>
      21             : #include <sys/wait.h>
      22             : #include <unistd.h>
      23             : 
      24             : #include "alloc-util.h"
      25             : #include "ask-password-api.h"
      26             : #include "conf-parser.h"
      27             : #include "def.h"
      28             : #include "dirent-util.h"
      29             : #include "exit-status.h"
      30             : #include "fd-util.h"
      31             : #include "fileio.h"
      32             : #include "hashmap.h"
      33             : #include "io-util.h"
      34             : #include "macro.h"
      35             : #include "main-func.h"
      36             : #include "memory-util.h"
      37             : #include "mkdir.h"
      38             : #include "path-util.h"
      39             : #include "plymouth-util.h"
      40             : #include "pretty-print.h"
      41             : #include "process-util.h"
      42             : #include "set.h"
      43             : #include "signal-util.h"
      44             : #include "socket-util.h"
      45             : #include "string-util.h"
      46             : #include "strv.h"
      47             : #include "terminal-util.h"
      48             : #include "utmp-wtmp.h"
      49             : 
      50             : static enum {
      51             :         ACTION_LIST,
      52             :         ACTION_QUERY,
      53             :         ACTION_WATCH,
      54             :         ACTION_WALL
      55             : } arg_action = ACTION_QUERY;
      56             : 
      57             : static bool arg_plymouth = false;
      58             : static bool arg_console = false;
      59             : static const char *arg_device = NULL;
      60             : 
      61           0 : static int ask_password_plymouth(
      62             :                 const char *message,
      63             :                 usec_t until,
      64             :                 AskPasswordFlags flags,
      65             :                 const char *flag_file,
      66             :                 char ***ret) {
      67             : 
      68             :         static const union sockaddr_union sa = PLYMOUTH_SOCKET;
      69           0 :         _cleanup_close_ int fd = -1, notify = -1;
      70           0 :         _cleanup_free_ char *packet = NULL;
      71             :         ssize_t k;
      72             :         int r, n;
      73           0 :         struct pollfd pollfd[2] = {};
      74             :         char buffer[LINE_MAX];
      75           0 :         size_t p = 0;
      76             :         enum {
      77             :                 POLL_SOCKET,
      78             :                 POLL_INOTIFY
      79             :         };
      80             : 
      81           0 :         assert(ret);
      82             : 
      83           0 :         if (flag_file) {
      84           0 :                 notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
      85           0 :                 if (notify < 0)
      86           0 :                         return -errno;
      87             : 
      88           0 :                 r = inotify_add_watch(notify, flag_file, IN_ATTRIB); /* for the link count */
      89           0 :                 if (r < 0)
      90           0 :                         return -errno;
      91             :         }
      92             : 
      93           0 :         fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
      94           0 :         if (fd < 0)
      95           0 :                 return -errno;
      96             : 
      97           0 :         r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
      98           0 :         if (r < 0)
      99           0 :                 return -errno;
     100             : 
     101           0 :         if (flags & ASK_PASSWORD_ACCEPT_CACHED) {
     102           0 :                 packet = strdup("c");
     103           0 :                 n = 1;
     104           0 :         } else if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
     105           0 :                 packet = NULL;
     106           0 :         if (!packet)
     107           0 :                 return -ENOMEM;
     108             : 
     109           0 :         r = loop_write(fd, packet, n + 1, true);
     110           0 :         if (r < 0)
     111           0 :                 return r;
     112             : 
     113           0 :         pollfd[POLL_SOCKET].fd = fd;
     114           0 :         pollfd[POLL_SOCKET].events = POLLIN;
     115           0 :         pollfd[POLL_INOTIFY].fd = notify;
     116           0 :         pollfd[POLL_INOTIFY].events = POLLIN;
     117             : 
     118           0 :         for (;;) {
     119           0 :                 int sleep_for = -1, j;
     120             : 
     121           0 :                 if (until > 0) {
     122             :                         usec_t y;
     123             : 
     124           0 :                         y = now(CLOCK_MONOTONIC);
     125             : 
     126           0 :                         if (y > until) {
     127           0 :                                 r = -ETIME;
     128           0 :                                 goto finish;
     129             :                         }
     130             : 
     131           0 :                         sleep_for = (int) ((until - y) / USEC_PER_MSEC);
     132             :                 }
     133             : 
     134           0 :                 if (flag_file && access(flag_file, F_OK) < 0) {
     135           0 :                         r = -errno;
     136           0 :                         goto finish;
     137             :                 }
     138             : 
     139           0 :                 j = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for);
     140           0 :                 if (j < 0) {
     141           0 :                         if (errno == EINTR)
     142           0 :                                 continue;
     143             : 
     144           0 :                         r = -errno;
     145           0 :                         goto finish;
     146           0 :                 } else if (j == 0) {
     147           0 :                         r = -ETIME;
     148           0 :                         goto finish;
     149             :                 }
     150             : 
     151           0 :                 if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0)
     152           0 :                         (void) flush_fd(notify);
     153             : 
     154           0 :                 if (pollfd[POLL_SOCKET].revents == 0)
     155           0 :                         continue;
     156             : 
     157           0 :                 k = read(fd, buffer + p, sizeof(buffer) - p);
     158           0 :                 if (k < 0) {
     159           0 :                         if (IN_SET(errno, EINTR, EAGAIN))
     160           0 :                                 continue;
     161             : 
     162           0 :                         r = -errno;
     163           0 :                         goto finish;
     164           0 :                 } else if (k == 0) {
     165           0 :                         r = -EIO;
     166           0 :                         goto finish;
     167             :                 }
     168             : 
     169           0 :                 p += k;
     170             : 
     171           0 :                 if (p < 1)
     172           0 :                         continue;
     173             : 
     174           0 :                 if (buffer[0] == 5) {
     175             : 
     176           0 :                         if (flags & ASK_PASSWORD_ACCEPT_CACHED) {
     177             :                                 /* Hmm, first try with cached
     178             :                                  * passwords failed, so let's retry
     179             :                                  * with a normal password request */
     180           0 :                                 packet = mfree(packet);
     181             : 
     182           0 :                                 if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) {
     183           0 :                                         r = -ENOMEM;
     184           0 :                                         goto finish;
     185             :                                 }
     186             : 
     187           0 :                                 r = loop_write(fd, packet, n+1, true);
     188           0 :                                 if (r < 0)
     189           0 :                                         goto finish;
     190             : 
     191           0 :                                 flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
     192           0 :                                 p = 0;
     193           0 :                                 continue;
     194             :                         }
     195             : 
     196             :                         /* No password, because UI not shown */
     197           0 :                         r = -ENOENT;
     198           0 :                         goto finish;
     199             : 
     200           0 :                 } else if (IN_SET(buffer[0], 2, 9)) {
     201             :                         uint32_t size;
     202             :                         char **l;
     203             : 
     204             :                         /* One or more answers */
     205           0 :                         if (p < 5)
     206           0 :                                 continue;
     207             : 
     208           0 :                         memcpy(&size, buffer+1, sizeof(size));
     209           0 :                         size = le32toh(size);
     210           0 :                         if (size + 5 > sizeof(buffer)) {
     211           0 :                                 r = -EIO;
     212           0 :                                 goto finish;
     213             :                         }
     214             : 
     215           0 :                         if (p-5 < size)
     216           0 :                                 continue;
     217             : 
     218           0 :                         l = strv_parse_nulstr(buffer + 5, size);
     219           0 :                         if (!l) {
     220           0 :                                 r = -ENOMEM;
     221           0 :                                 goto finish;
     222             :                         }
     223             : 
     224           0 :                         *ret = l;
     225           0 :                         break;
     226             : 
     227             :                 } else {
     228             :                         /* Unknown packet */
     229           0 :                         r = -EIO;
     230           0 :                         goto finish;
     231             :                 }
     232             :         }
     233             : 
     234           0 :         r = 0;
     235             : 
     236           0 : finish:
     237           0 :         explicit_bzero_safe(buffer, sizeof(buffer));
     238           0 :         return r;
     239             : }
     240             : 
     241           0 : static int send_passwords(const char *socket_name, char **passwords) {
     242           0 :         _cleanup_(erase_and_freep) char *packet = NULL;
     243           0 :         _cleanup_close_ int socket_fd = -1;
     244           0 :         union sockaddr_union sa = {};
     245           0 :         size_t packet_length = 1;
     246             :         char **p, *d;
     247             :         ssize_t n;
     248             :         int salen;
     249             : 
     250           0 :         assert(socket_name);
     251             : 
     252           0 :         salen = sockaddr_un_set_path(&sa.un, socket_name);
     253           0 :         if (salen < 0)
     254           0 :                 return salen;
     255             : 
     256           0 :         STRV_FOREACH(p, passwords)
     257           0 :                 packet_length += strlen(*p) + 1;
     258             : 
     259           0 :         packet = new(char, packet_length);
     260           0 :         if (!packet)
     261           0 :                 return -ENOMEM;
     262             : 
     263           0 :         packet[0] = '+';
     264             : 
     265           0 :         d = packet + 1;
     266           0 :         STRV_FOREACH(p, passwords)
     267           0 :                 d = stpcpy(d, *p) + 1;
     268             : 
     269           0 :         socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
     270           0 :         if (socket_fd < 0)
     271           0 :                 return log_debug_errno(errno, "socket(): %m");
     272             : 
     273           0 :         n = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, salen);
     274           0 :         if (n < 0)
     275           0 :                 return log_debug_errno(errno, "sendto(): %m");
     276             : 
     277           0 :         return (int) n;
     278             : }
     279             : 
     280           0 : static int parse_password(const char *filename, char **wall) {
     281           0 :         _cleanup_free_ char *socket_name = NULL, *message = NULL;
     282           0 :         bool accept_cached = false, echo = false;
     283           0 :         uint64_t not_after = 0;
     284           0 :         unsigned pid = 0;
     285             : 
     286           0 :         const ConfigTableItem items[] = {
     287             :                 { "Ask", "Socket",       config_parse_string,   0, &socket_name   },
     288             :                 { "Ask", "NotAfter",     config_parse_uint64,   0, &not_after     },
     289             :                 { "Ask", "Message",      config_parse_string,   0, &message       },
     290             :                 { "Ask", "PID",          config_parse_unsigned, 0, &pid           },
     291             :                 { "Ask", "AcceptCached", config_parse_bool,     0, &accept_cached },
     292             :                 { "Ask", "Echo",         config_parse_bool,     0, &echo          },
     293             :                 {}
     294             :         };
     295             : 
     296             :         int r;
     297             : 
     298           0 :         assert(filename);
     299             : 
     300           0 :         r = config_parse(NULL, filename, NULL,
     301             :                          NULL,
     302             :                          config_item_table_lookup, items,
     303             :                          CONFIG_PARSE_RELAXED|CONFIG_PARSE_WARN, NULL);
     304           0 :         if (r < 0)
     305           0 :                 return r;
     306             : 
     307           0 :         if (!socket_name)
     308           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
     309             :                                        "Invalid password file %s", filename);
     310             : 
     311           0 :         if (not_after > 0 && now(CLOCK_MONOTONIC) > not_after)
     312           0 :                 return 0;
     313             : 
     314           0 :         if (pid > 0 && !pid_is_alive(pid))
     315           0 :                 return 0;
     316             : 
     317           0 :         if (arg_action == ACTION_LIST)
     318           0 :                 printf("'%s' (PID %u)\n", message, pid);
     319             : 
     320           0 :         else if (arg_action == ACTION_WALL) {
     321             :                 char *_wall;
     322             : 
     323           0 :                 if (asprintf(&_wall,
     324             :                              "%s%sPassword entry required for \'%s\' (PID %u).\r\n"
     325             :                              "Please enter password with the systemd-tty-ask-password-agent tool:",
     326             :                              strempty(*wall),
     327           0 :                              *wall ? "\r\n\r\n" : "",
     328             :                              message,
     329             :                              pid) < 0)
     330           0 :                         return log_oom();
     331             : 
     332           0 :                 free(*wall);
     333           0 :                 *wall = _wall;
     334             : 
     335             :         } else {
     336           0 :                 _cleanup_strv_free_erase_ char **passwords = NULL;
     337             : 
     338           0 :                 assert(IN_SET(arg_action, ACTION_QUERY, ACTION_WATCH));
     339             : 
     340           0 :                 if (access(socket_name, W_OK) < 0) {
     341           0 :                         if (arg_action == ACTION_QUERY)
     342           0 :                                 log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid);
     343             : 
     344           0 :                         return 0;
     345             :                 }
     346             : 
     347           0 :                 if (arg_plymouth)
     348           0 :                         r = ask_password_plymouth(message, not_after, accept_cached ? ASK_PASSWORD_ACCEPT_CACHED : 0, filename, &passwords);
     349             :                 else {
     350           0 :                         int tty_fd = -1;
     351             : 
     352           0 :                         if (arg_console) {
     353           0 :                                 const char *con = arg_device ?: "/dev/console";
     354             : 
     355           0 :                                 tty_fd = acquire_terminal(con, ACQUIRE_TERMINAL_WAIT, USEC_INFINITY);
     356           0 :                                 if (tty_fd < 0)
     357           0 :                                         return log_error_errno(tty_fd, "Failed to acquire %s: %m", con);
     358             : 
     359           0 :                                 r = reset_terminal_fd(tty_fd, true);
     360           0 :                                 if (r < 0)
     361           0 :                                         log_warning_errno(r, "Failed to reset terminal, ignoring: %m");
     362             :                         }
     363             : 
     364           0 :                         r = ask_password_tty(tty_fd, message, NULL, not_after,
     365           0 :                                              (echo ? ASK_PASSWORD_ECHO : 0) |
     366           0 :                                              (arg_console ? ASK_PASSWORD_CONSOLE_COLOR : 0),
     367             :                                              filename, &passwords);
     368             : 
     369           0 :                         if (arg_console) {
     370           0 :                                 tty_fd = safe_close(tty_fd);
     371           0 :                                 release_terminal();
     372             :                         }
     373             :                 }
     374             : 
     375             :                 /* If the query went away, that's OK */
     376           0 :                 if (IN_SET(r, -ETIME, -ENOENT))
     377           0 :                         return 0;
     378             : 
     379           0 :                 if (r < 0)
     380           0 :                         return log_error_errno(r, "Failed to query password: %m");
     381             : 
     382           0 :                 r = send_passwords(socket_name, passwords);
     383           0 :                 if (r < 0)
     384           0 :                         return log_error_errno(r, "Failed to send: %m");
     385             :         }
     386             : 
     387           0 :         return 0;
     388             : }
     389             : 
     390           0 : static int wall_tty_block(void) {
     391           0 :         _cleanup_free_ char *p = NULL;
     392             :         dev_t devnr;
     393             :         int fd, r;
     394             : 
     395           0 :         r = get_ctty_devnr(0, &devnr);
     396           0 :         if (r == -ENXIO) /* We have no controlling tty */
     397           0 :                 return -ENOTTY;
     398           0 :         if (r < 0)
     399           0 :                 return log_error_errno(r, "Failed to get controlling TTY: %m");
     400             : 
     401           0 :         if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
     402           0 :                 return log_oom();
     403             : 
     404           0 :         (void) mkdir_parents_label(p, 0700);
     405           0 :         (void) mkfifo(p, 0600);
     406             : 
     407           0 :         fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
     408           0 :         if (fd < 0)
     409           0 :                 return log_debug_errno(errno, "Failed to open %s: %m", p);
     410             : 
     411           0 :         return fd;
     412             : }
     413             : 
     414           0 : static bool wall_tty_match(const char *path, void *userdata) {
     415           0 :         _cleanup_free_ char *p = NULL;
     416           0 :         _cleanup_close_ int fd = -1;
     417             :         struct stat st;
     418             : 
     419           0 :         if (!path_is_absolute(path))
     420           0 :                 path = strjoina("/dev/", path);
     421             : 
     422           0 :         if (lstat(path, &st) < 0) {
     423           0 :                 log_debug_errno(errno, "Failed to stat %s: %m", path);
     424           0 :                 return true;
     425             :         }
     426             : 
     427           0 :         if (!S_ISCHR(st.st_mode)) {
     428           0 :                 log_debug("%s is not a character device.", path);
     429           0 :                 return true;
     430             :         }
     431             : 
     432             :         /* We use named pipes to ensure that wall messages suggesting
     433             :          * password entry are not printed over password prompts
     434             :          * already shown. We use the fact here that opening a pipe in
     435             :          * non-blocking mode for write-only will succeed only if
     436             :          * there's some writer behind it. Using pipes has the
     437             :          * advantage that the block will automatically go away if the
     438             :          * process dies. */
     439             : 
     440           0 :         if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0) {
     441           0 :                 log_oom();
     442           0 :                 return true;
     443             :         }
     444             : 
     445           0 :         fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
     446           0 :         if (fd < 0) {
     447           0 :                 log_debug_errno(errno, "Failed to open the wall pipe: %m");
     448           0 :                 return 1;
     449             :         }
     450             : 
     451             :         /* What, we managed to open the pipe? Then this tty is filtered. */
     452           0 :         return 0;
     453             : }
     454             : 
     455           0 : static int show_passwords(void) {
     456           0 :         _cleanup_closedir_ DIR *d;
     457             :         struct dirent *de;
     458           0 :         int r = 0;
     459             : 
     460           0 :         d = opendir("/run/systemd/ask-password");
     461           0 :         if (!d) {
     462           0 :                 if (errno == ENOENT)
     463           0 :                         return 0;
     464             : 
     465           0 :                 return log_error_errno(errno, "Failed to open /run/systemd/ask-password: %m");
     466             :         }
     467             : 
     468           0 :         FOREACH_DIRENT_ALL(de, d, return log_error_errno(errno, "Failed to read directory: %m")) {
     469           0 :                 _cleanup_free_ char *p = NULL, *wall = NULL;
     470             :                 int q;
     471             : 
     472             :                 /* We only support /dev on tmpfs, hence we can rely on
     473             :                  * d_type to be reliable */
     474             : 
     475           0 :                 if (de->d_type != DT_REG)
     476           0 :                         continue;
     477             : 
     478           0 :                 if (hidden_or_backup_file(de->d_name))
     479           0 :                         continue;
     480             : 
     481           0 :                 if (!startswith(de->d_name, "ask."))
     482           0 :                         continue;
     483             : 
     484           0 :                 p = path_join("/run/systemd/ask-password", de->d_name);
     485           0 :                 if (!p)
     486           0 :                         return log_oom();
     487             : 
     488           0 :                 q = parse_password(p, &wall);
     489           0 :                 if (q < 0 && r == 0)
     490           0 :                         r = q;
     491             : 
     492           0 :                 if (wall)
     493           0 :                         (void) utmp_wall(wall, NULL, NULL, wall_tty_match, NULL);
     494             :         }
     495             : 
     496           0 :         return r;
     497             : }
     498             : 
     499           0 : static int watch_passwords(void) {
     500             :         enum {
     501             :                 FD_INOTIFY,
     502             :                 FD_SIGNAL,
     503             :                 _FD_MAX
     504             :         };
     505             : 
     506           0 :         _cleanup_close_ int notify = -1, signal_fd = -1, tty_block_fd = -1;
     507           0 :         struct pollfd pollfd[_FD_MAX] = {};
     508             :         sigset_t mask;
     509             :         int r;
     510             : 
     511           0 :         tty_block_fd = wall_tty_block();
     512             : 
     513           0 :         (void) mkdir_p_label("/run/systemd/ask-password", 0755);
     514             : 
     515           0 :         notify = inotify_init1(IN_CLOEXEC);
     516           0 :         if (notify < 0)
     517           0 :                 return log_error_errno(errno, "Failed to allocate directory watch: %m");
     518             : 
     519           0 :         if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0) {
     520           0 :                 if (errno == ENOSPC)
     521           0 :                         return log_error_errno(errno, "Failed to add /run/systemd/ask-password to directory watch: inotify watch limit reached");
     522             :                 else
     523           0 :                         return log_error_errno(errno, "Failed to add /run/systemd/ask-password to directory watch: %m");
     524             :         }
     525             : 
     526           0 :         assert_se(sigemptyset(&mask) >= 0);
     527           0 :         assert_se(sigset_add_many(&mask, SIGINT, SIGTERM, -1) >= 0);
     528           0 :         assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) >= 0);
     529             : 
     530           0 :         signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
     531           0 :         if (signal_fd < 0)
     532           0 :                 return log_error_errno(errno, "Failed to allocate signal file descriptor: %m");
     533             : 
     534           0 :         pollfd[FD_INOTIFY].fd = notify;
     535           0 :         pollfd[FD_INOTIFY].events = POLLIN;
     536           0 :         pollfd[FD_SIGNAL].fd = signal_fd;
     537           0 :         pollfd[FD_SIGNAL].events = POLLIN;
     538             : 
     539             :         for (;;) {
     540           0 :                 r = show_passwords();
     541           0 :                 if (r < 0)
     542           0 :                         log_error_errno(r, "Failed to show password: %m");
     543             : 
     544           0 :                 if (poll(pollfd, _FD_MAX, -1) < 0) {
     545           0 :                         if (errno == EINTR)
     546           0 :                                 continue;
     547             : 
     548           0 :                         return -errno;
     549             :                 }
     550             : 
     551           0 :                 if (pollfd[FD_INOTIFY].revents != 0)
     552           0 :                         (void) flush_fd(notify);
     553             : 
     554           0 :                 if (pollfd[FD_SIGNAL].revents != 0)
     555           0 :                         break;
     556             :         }
     557             : 
     558           0 :         return 0;
     559             : }
     560             : 
     561           3 : static int help(void) {
     562           3 :         _cleanup_free_ char *link = NULL;
     563             :         int r;
     564             : 
     565           3 :         r = terminal_urlify_man("systemd-tty-ask-password-agent", "1", &link);
     566           3 :         if (r < 0)
     567           0 :                 return log_oom();
     568             : 
     569           3 :         printf("%s [OPTIONS...]\n\n"
     570             :                "Process system password requests.\n\n"
     571             :                "  -h --help     Show this help\n"
     572             :                "     --version  Show package version\n"
     573             :                "     --list     Show pending password requests\n"
     574             :                "     --query    Process pending password requests\n"
     575             :                "     --watch    Continuously process password requests\n"
     576             :                "     --wall     Continuously forward password requests to wall\n"
     577             :                "     --plymouth Ask question with Plymouth instead of on TTY\n"
     578             :                "     --console  Ask question on /dev/console instead of current TTY\n"
     579             :                "\nSee the %s for details.\n"
     580             :                , program_invocation_short_name
     581             :                , link
     582             :         );
     583             : 
     584           3 :         return 0;
     585             : }
     586             : 
     587           4 : static int parse_argv(int argc, char *argv[]) {
     588             : 
     589             :         enum {
     590             :                 ARG_LIST = 0x100,
     591             :                 ARG_QUERY,
     592             :                 ARG_WATCH,
     593             :                 ARG_WALL,
     594             :                 ARG_PLYMOUTH,
     595             :                 ARG_CONSOLE,
     596             :                 ARG_VERSION
     597             :         };
     598             : 
     599             :         static const struct option options[] = {
     600             :                 { "help",     no_argument,       NULL, 'h'          },
     601             :                 { "version",  no_argument,       NULL, ARG_VERSION  },
     602             :                 { "list",     no_argument,       NULL, ARG_LIST     },
     603             :                 { "query",    no_argument,       NULL, ARG_QUERY    },
     604             :                 { "watch",    no_argument,       NULL, ARG_WATCH    },
     605             :                 { "wall",     no_argument,       NULL, ARG_WALL     },
     606             :                 { "plymouth", no_argument,       NULL, ARG_PLYMOUTH },
     607             :                 { "console",  optional_argument, NULL, ARG_CONSOLE  },
     608             :                 {}
     609             :         };
     610             : 
     611             :         int c;
     612             : 
     613           4 :         assert(argc >= 0);
     614           4 :         assert(argv);
     615             : 
     616           4 :         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
     617             : 
     618           4 :                 switch (c) {
     619             : 
     620           3 :                 case 'h':
     621           3 :                         return help();
     622             : 
     623           0 :                 case ARG_VERSION:
     624           0 :                         return version();
     625             : 
     626           0 :                 case ARG_LIST:
     627           0 :                         arg_action = ACTION_LIST;
     628           0 :                         break;
     629             : 
     630           0 :                 case ARG_QUERY:
     631           0 :                         arg_action = ACTION_QUERY;
     632           0 :                         break;
     633             : 
     634           0 :                 case ARG_WATCH:
     635           0 :                         arg_action = ACTION_WATCH;
     636           0 :                         break;
     637             : 
     638           0 :                 case ARG_WALL:
     639           0 :                         arg_action = ACTION_WALL;
     640           0 :                         break;
     641             : 
     642           0 :                 case ARG_PLYMOUTH:
     643           0 :                         arg_plymouth = true;
     644           0 :                         break;
     645             : 
     646           0 :                 case ARG_CONSOLE:
     647           0 :                         arg_console = true;
     648           0 :                         if (optarg) {
     649             : 
     650           0 :                                 if (isempty(optarg))
     651           0 :                                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     652             :                                                                "Empty console device path is not allowed.");
     653             : 
     654           0 :                                 arg_device = optarg;
     655             :                         }
     656           0 :                         break;
     657             : 
     658           1 :                 case '?':
     659           1 :                         return -EINVAL;
     660             : 
     661           0 :                 default:
     662           0 :                         assert_not_reached("Unhandled option");
     663             :                 }
     664             : 
     665           0 :         if (optind != argc)
     666           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     667             :                                        "%s takes no arguments.", program_invocation_short_name);
     668             : 
     669           0 :         if (arg_plymouth || arg_console) {
     670             : 
     671           0 :                 if (!IN_SET(arg_action, ACTION_QUERY, ACTION_WATCH))
     672           0 :                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     673             :                                                "Options --query and --watch conflict.");
     674             : 
     675           0 :                 if (arg_plymouth && arg_console)
     676           0 :                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     677             :                                                "Options --plymouth and --console conflict.");
     678             :         }
     679             : 
     680           0 :         return 1;
     681             : }
     682             : 
     683             : /*
     684             :  * To be able to ask on all terminal devices of /dev/console the devices are collected. If more than one
     685             :  * device is found, then on each of the terminals a inquiring task is forked.  Every task has its own session
     686             :  * and its own controlling terminal.  If one of the tasks does handle a password, the remaining tasks will be
     687             :  * terminated.
     688             :  */
     689           0 : static int ask_on_this_console(const char *tty, pid_t *ret_pid, char **arguments) {
     690             :         static const struct sigaction sigchld = {
     691             :                 .sa_handler = nop_signal_handler,
     692             :                 .sa_flags = SA_NOCLDSTOP | SA_RESTART,
     693             :         };
     694             :         static const struct sigaction sighup = {
     695             :                 .sa_handler = SIG_DFL,
     696             :                 .sa_flags = SA_RESTART,
     697             :         };
     698             :         int r;
     699             : 
     700           0 :         assert_se(sigaction(SIGCHLD, &sigchld, NULL) >= 0);
     701           0 :         assert_se(sigaction(SIGHUP, &sighup, NULL) >= 0);
     702           0 :         assert_se(sigprocmask_many(SIG_UNBLOCK, NULL, SIGHUP, SIGCHLD, -1) >= 0);
     703             : 
     704           0 :         r = safe_fork("(sd-passwd)", FORK_RESET_SIGNALS|FORK_LOG, ret_pid);
     705           0 :         if (r < 0)
     706           0 :                 return r;
     707           0 :         if (r == 0) {
     708             :                 char **i;
     709             : 
     710           0 :                 assert_se(prctl(PR_SET_PDEATHSIG, SIGHUP) >= 0);
     711             : 
     712           0 :                 STRV_FOREACH(i, arguments) {
     713             :                         char *k;
     714             : 
     715           0 :                         if (!streq(*i, "--console"))
     716           0 :                                 continue;
     717             : 
     718           0 :                         k = strjoin("--console=", tty);
     719           0 :                         if (!k) {
     720           0 :                                 log_oom();
     721           0 :                                 _exit(EXIT_FAILURE);
     722             :                         }
     723             : 
     724           0 :                         free_and_replace(*i, k);
     725             :                 }
     726             : 
     727           0 :                 execv(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, arguments);
     728           0 :                 _exit(EXIT_FAILURE);
     729             :         }
     730             : 
     731           0 :         return 0;
     732             : }
     733             : 
     734           0 : static void terminate_agents(Set *pids) {
     735             :         struct timespec ts;
     736           0 :         siginfo_t status = {};
     737             :         sigset_t set;
     738             :         Iterator i;
     739             :         void *p;
     740             :         int r, signum;
     741             : 
     742             :         /*
     743             :          * Request termination of the remaining processes as those
     744             :          * are not required anymore.
     745             :          */
     746           0 :         SET_FOREACH(p, pids, i)
     747           0 :                 (void) kill(PTR_TO_PID(p), SIGTERM);
     748             : 
     749             :         /*
     750             :          * Collect the processes which have go away.
     751             :          */
     752           0 :         assert_se(sigemptyset(&set) >= 0);
     753           0 :         assert_se(sigaddset(&set, SIGCHLD) >= 0);
     754           0 :         timespec_store(&ts, 50 * USEC_PER_MSEC);
     755             : 
     756           0 :         while (!set_isempty(pids)) {
     757             : 
     758           0 :                 zero(status);
     759           0 :                 r = waitid(P_ALL, 0, &status, WEXITED|WNOHANG);
     760           0 :                 if (r < 0 && errno == EINTR)
     761           0 :                         continue;
     762             : 
     763           0 :                 if (r == 0 && status.si_pid > 0) {
     764           0 :                         set_remove(pids, PID_TO_PTR(status.si_pid));
     765           0 :                         continue;
     766             :                 }
     767             : 
     768           0 :                 signum = sigtimedwait(&set, NULL, &ts);
     769           0 :                 if (signum < 0) {
     770           0 :                         if (errno != EAGAIN)
     771           0 :                                 log_error_errno(errno, "sigtimedwait() failed: %m");
     772           0 :                         break;
     773             :                 }
     774           0 :                 assert(signum == SIGCHLD);
     775             :         }
     776             : 
     777             :         /*
     778             :          * Kill hanging processes.
     779             :          */
     780           0 :         SET_FOREACH(p, pids, i) {
     781           0 :                 log_warning("Failed to terminate child %d, killing it", PTR_TO_PID(p));
     782           0 :                 (void) kill(PTR_TO_PID(p), SIGKILL);
     783             :         }
     784           0 : }
     785             : 
     786           0 : static int ask_on_consoles(char *argv[]) {
     787           0 :         _cleanup_set_free_ Set *pids = NULL;
     788           0 :         _cleanup_strv_free_ char **consoles = NULL, **arguments = NULL;
     789           0 :         siginfo_t status = {};
     790             :         char **tty;
     791             :         pid_t pid;
     792             :         int r;
     793             : 
     794           0 :         r = get_kernel_consoles(&consoles);
     795           0 :         if (r < 0)
     796           0 :                 return log_error_errno(r, "Failed to determine devices of /dev/console: %m");
     797             : 
     798           0 :         pids = set_new(NULL);
     799           0 :         if (!pids)
     800           0 :                 return log_oom();
     801             : 
     802           0 :         arguments = strv_copy(argv);
     803           0 :         if (!arguments)
     804           0 :                 return log_oom();
     805             : 
     806             :         /* Start an agent on each console. */
     807           0 :         STRV_FOREACH(tty, consoles) {
     808           0 :                 r = ask_on_this_console(*tty, &pid, arguments);
     809           0 :                 if (r < 0)
     810           0 :                         return r;
     811             : 
     812           0 :                 if (set_put(pids, PID_TO_PTR(pid)) < 0)
     813           0 :                         return log_oom();
     814             :         }
     815             : 
     816             :         /* Wait for an agent to exit. */
     817             :         for (;;) {
     818           0 :                 zero(status);
     819             : 
     820           0 :                 if (waitid(P_ALL, 0, &status, WEXITED) < 0) {
     821           0 :                         if (errno == EINTR)
     822           0 :                                 continue;
     823             : 
     824           0 :                         return log_error_errno(errno, "waitid() failed: %m");
     825             :                 }
     826             : 
     827           0 :                 set_remove(pids, PID_TO_PTR(status.si_pid));
     828           0 :                 break;
     829             :         }
     830             : 
     831           0 :         if (!is_clean_exit(status.si_code, status.si_status, EXIT_CLEAN_DAEMON, NULL))
     832           0 :                 log_error("Password agent failed with: %d", status.si_status);
     833             : 
     834           0 :         terminate_agents(pids);
     835           0 :         return 0;
     836             : }
     837             : 
     838           4 : static int run(int argc, char *argv[]) {
     839             :         int r;
     840             : 
     841           4 :         log_setup_service();
     842             : 
     843           4 :         umask(0022);
     844             : 
     845           4 :         r = parse_argv(argc, argv);
     846           4 :         if (r <= 0)
     847           4 :                 return r;
     848             : 
     849           0 :         if (arg_console && !arg_device)
     850             :                 /*
     851             :                  * Spawn a separate process for each console device.
     852             :                  */
     853           0 :                 return ask_on_consoles(argv);
     854             : 
     855           0 :         if (arg_device) {
     856             :                 /*
     857             :                  * Later on, a controlling terminal will be acquired,
     858             :                  * therefore the current process has to become a session
     859             :                  * leader and should not have a controlling terminal already.
     860             :                  */
     861           0 :                 (void) setsid();
     862           0 :                 (void) release_terminal();
     863             :         }
     864             : 
     865           0 :         if (IN_SET(arg_action, ACTION_WATCH, ACTION_WALL))
     866           0 :                 return watch_passwords();
     867             :         else
     868           0 :                 return show_passwords();
     869             : }
     870             : 
     871           4 : DEFINE_MAIN_FUNCTION(run);

Generated by: LCOV version 1.14