LCOV - code coverage report
Current view: top level - tty-ask-password-agent - tty-ask-password-agent.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 22 418 5.3 %
Date: 2019-08-23 13:36:53 Functions: 4 14 28.6 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 7 406 1.7 %

           Branch data     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                 :         12 : static int help(void) {
     562                 :         12 :         _cleanup_free_ char *link = NULL;
     563                 :            :         int r;
     564                 :            : 
     565                 :         12 :         r = terminal_urlify_man("systemd-tty-ask-password-agent", "1", &link);
     566         [ -  + ]:         12 :         if (r < 0)
     567                 :          0 :                 return log_oom();
     568                 :            : 
     569                 :         12 :         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                 :         12 :         return 0;
     585                 :            : }
     586                 :            : 
     587                 :         16 : 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         [ -  + ]:         16 :         assert(argc >= 0);
     614         [ -  + ]:         16 :         assert(argv);
     615                 :            : 
     616         [ +  - ]:         16 :         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
     617                 :            : 
     618   [ +  -  -  -  :         16 :                 switch (c) {
          -  -  -  -  +  
                      - ]
     619                 :            : 
     620                 :         12 :                 case 'h':
     621                 :         12 :                         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                 :          4 :                 case '?':
     659                 :          4 :                         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                 :         16 : static int run(int argc, char *argv[]) {
     839                 :            :         int r;
     840                 :            : 
     841                 :         16 :         log_setup_service();
     842                 :            : 
     843                 :         16 :         umask(0022);
     844                 :            : 
     845                 :         16 :         r = parse_argv(argc, argv);
     846         [ +  - ]:         16 :         if (r <= 0)
     847                 :         16 :                 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                 :         16 : DEFINE_MAIN_FUNCTION(run);

Generated by: LCOV version 1.14