chiark / gitweb /
core: make systemd.confirm_spawn=1 actually work
[elogind.git] / src / tty-ask-password-agent / tty-ask-password-agent.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdbool.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <sys/socket.h>
26 #include <sys/un.h>
27 #include <stddef.h>
28 #include <sys/poll.h>
29 #include <sys/inotify.h>
30 #include <unistd.h>
31 #include <getopt.h>
32 #include <sys/signalfd.h>
33 #include <fcntl.h>
34
35 #include "util.h"
36 #include "mkdir.h"
37 #include "path-util.h"
38 #include "conf-parser.h"
39 #include "utmp-wtmp.h"
40 #include "socket-util.h"
41 #include "ask-password-api.h"
42 #include "strv.h"
43
44 static enum {
45         ACTION_LIST,
46         ACTION_QUERY,
47         ACTION_WATCH,
48         ACTION_WALL
49 } arg_action = ACTION_QUERY;
50
51 static bool arg_plymouth = false;
52 static bool arg_console = false;
53
54 static int ask_password_plymouth(
55                 const char *message,
56                 usec_t until,
57                 const char *flag_file,
58                 bool accept_cached,
59                 char ***_passphrases) {
60
61         int fd = -1, notify = -1;
62         union sockaddr_union sa;
63         char *packet = NULL;
64         ssize_t k;
65         int r, n;
66         struct pollfd pollfd[2];
67         char buffer[LINE_MAX];
68         size_t p = 0;
69         enum {
70                 POLL_SOCKET,
71                 POLL_INOTIFY
72         };
73
74         assert(_passphrases);
75
76         if (flag_file) {
77                 if ((notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
78                         r = -errno;
79                         goto finish;
80                 }
81
82                 if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
83                         r = -errno;
84                         goto finish;
85                 }
86         }
87
88         if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
89                 r = -errno;
90                 goto finish;
91         }
92
93         zero(sa);
94         sa.sa.sa_family = AF_UNIX;
95         strncpy(sa.un.sun_path+1, "/org/freedesktop/plymouthd", sizeof(sa.un.sun_path)-1);
96         if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
97                 log_error("Failed to connect to Plymouth: %m");
98                 r = -errno;
99                 goto finish;
100         }
101
102         if (accept_cached) {
103                 packet = strdup("c");
104                 n = 1;
105         } else
106                 asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n);
107
108         if (!packet) {
109                 r = -ENOMEM;
110                 goto finish;
111         }
112
113         if ((k = loop_write(fd, packet, n+1, true)) != n+1) {
114                 r = k < 0 ? (int) k : -EIO;
115                 goto finish;
116         }
117
118         zero(pollfd);
119         pollfd[POLL_SOCKET].fd = fd;
120         pollfd[POLL_SOCKET].events = POLLIN;
121         pollfd[POLL_INOTIFY].fd = notify;
122         pollfd[POLL_INOTIFY].events = POLLIN;
123
124         for (;;) {
125                 int sleep_for = -1, j;
126
127                 if (until > 0) {
128                         usec_t y;
129
130                         y = now(CLOCK_MONOTONIC);
131
132                         if (y > until) {
133                                 r = -ETIME;
134                                 goto finish;
135                         }
136
137                         sleep_for = (int) ((until - y) / USEC_PER_MSEC);
138                 }
139
140                 if (flag_file)
141                         if (access(flag_file, F_OK) < 0) {
142                                 r = -errno;
143                                 goto finish;
144                         }
145
146                 if ((j = poll(pollfd, notify > 0 ? 2 : 1, sleep_for)) < 0) {
147
148                         if (errno == EINTR)
149                                 continue;
150
151                         r = -errno;
152                         goto finish;
153                 } else if (j == 0) {
154                         r = -ETIME;
155                         goto finish;
156                 }
157
158                 if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
159                         flush_fd(notify);
160
161                 if (pollfd[POLL_SOCKET].revents == 0)
162                         continue;
163
164                 if ((k = read(fd, buffer + p, sizeof(buffer) - p)) <= 0) {
165                         r = k < 0 ? -errno : -EIO;
166                         goto finish;
167                 }
168
169                 p += k;
170
171                 if (p < 1)
172                         continue;
173
174                 if (buffer[0] == 5) {
175
176                         if (accept_cached) {
177                                 /* Hmm, first try with cached
178                                  * passwords failed, so let's retry
179                                  * with a normal password request */
180                                 free(packet);
181                                 packet = NULL;
182
183                                 if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) {
184                                         r = -ENOMEM;
185                                         goto finish;
186                                 }
187
188                                 if ((k = loop_write(fd, packet, n+1, true)) != n+1) {
189                                         r = k < 0 ? (int) k : -EIO;
190                                         goto finish;
191                                 }
192
193                                 accept_cached = false;
194                                 p = 0;
195                                 continue;
196                         }
197
198                         /* No password, because UI not shown */
199                         r = -ENOENT;
200                         goto finish;
201
202                 } else if (buffer[0] == 2 || buffer[0] == 9) {
203                         uint32_t size;
204                         char **l;
205
206                         /* One ore more answers */
207                         if (p < 5)
208                                 continue;
209
210                         memcpy(&size, buffer+1, sizeof(size));
211                         size = le32toh(size);
212                         if (size+5 > sizeof(buffer)) {
213                                 r = -EIO;
214                                 goto finish;
215                         }
216
217                         if (p-5 < size)
218                                 continue;
219
220                         if (!(l = strv_parse_nulstr(buffer + 5, size))) {
221                                 r = -ENOMEM;
222                                 goto finish;
223                         }
224
225                         *_passphrases = l;
226                         break;
227
228                 } else {
229                         /* Unknown packet */
230                         r = -EIO;
231                         goto finish;
232                 }
233         }
234
235         r = 0;
236
237 finish:
238         if (notify >= 0)
239                 close_nointr_nofail(notify);
240
241         if (fd >= 0)
242                 close_nointr_nofail(fd);
243
244         free(packet);
245
246         return r;
247 }
248
249 static int parse_password(const char *filename, char **wall) {
250         char *socket_name = NULL, *message = NULL, *packet = NULL;
251         uint64_t not_after = 0;
252         unsigned pid = 0;
253         int socket_fd = -1;
254         bool accept_cached = false;
255
256         const ConfigTableItem items[] = {
257                 { "Ask", "Socket",       config_parse_string,   0, &socket_name   },
258                 { "Ask", "NotAfter",     config_parse_uint64,   0, &not_after     },
259                 { "Ask", "Message",      config_parse_string,   0, &message       },
260                 { "Ask", "PID",          config_parse_unsigned, 0, &pid           },
261                 { "Ask", "AcceptCached", config_parse_bool,     0, &accept_cached },
262                 { NULL, NULL, NULL, 0, NULL }
263         };
264
265         FILE *f;
266         int r;
267
268         assert(filename);
269
270         f = fopen(filename, "re");
271         if (!f) {
272                 if (errno == ENOENT)
273                         return 0;
274
275                 log_error("open(%s): %m", filename);
276                 return -errno;
277         }
278
279         r = config_parse(filename, f, NULL, config_item_table_lookup, (void*) items, true, NULL);
280         if (r < 0) {
281                 log_error("Failed to parse password file %s: %s", filename, strerror(-r));
282                 goto finish;
283         }
284
285         if (!socket_name) {
286                 log_error("Invalid password file %s", filename);
287                 r = -EBADMSG;
288                 goto finish;
289         }
290
291         if (not_after > 0) {
292                 if (now(CLOCK_MONOTONIC) > not_after) {
293                         r = 0;
294                         goto finish;
295                 }
296         }
297
298         if (pid > 0 &&
299             kill(pid, 0) < 0 &&
300             errno == ESRCH) {
301                 r = 0;
302                 goto finish;
303         }
304
305         if (arg_action == ACTION_LIST)
306                 printf("'%s' (PID %u)\n", message, pid);
307         else if (arg_action == ACTION_WALL) {
308                 char *_wall;
309
310                 if (asprintf(&_wall,
311                              "%s%sPassword entry required for \'%s\' (PID %u).\r\n"
312                              "Please enter password with the systemd-tty-ask-password-agent tool!",
313                              *wall ? *wall : "",
314                              *wall ? "\r\n\r\n" : "",
315                              message,
316                              pid) < 0) {
317                         log_error("Out of memory");
318                         r = -ENOMEM;
319                         goto finish;
320                 }
321
322                 free(*wall);
323                 *wall = _wall;
324         } else {
325                 union {
326                         struct sockaddr sa;
327                         struct sockaddr_un un;
328                 } sa;
329                 size_t packet_length = 0;
330
331                 assert(arg_action == ACTION_QUERY ||
332                        arg_action == ACTION_WATCH);
333
334                 if (access(socket_name, W_OK) < 0) {
335
336                         if (arg_action == ACTION_QUERY)
337                                 log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid);
338
339                         r = 0;
340                         goto finish;
341                 }
342
343                 if (arg_plymouth) {
344                         char **passwords = NULL;
345
346                         if ((r = ask_password_plymouth(message, not_after, filename, accept_cached, &passwords)) >= 0) {
347                                 char **p;
348
349                                 packet_length = 1;
350                                 STRV_FOREACH(p, passwords)
351                                         packet_length += strlen(*p) + 1;
352
353                                 if (!(packet = new(char, packet_length)))
354                                         r = -ENOMEM;
355                                 else {
356                                         char *d;
357
358                                         packet[0] = '+';
359                                         d = packet+1;
360
361                                         STRV_FOREACH(p, passwords)
362                                                 d = stpcpy(d, *p) + 1;
363                                 }
364                         }
365
366                 } else {
367                         int tty_fd = -1;
368                         char *password;
369
370                         if (arg_console)
371                                 if ((tty_fd = acquire_terminal("/dev/console", false, false, false, (usec_t) -1)) < 0) {
372                                         r = tty_fd;
373                                         goto finish;
374                                 }
375
376                         r = ask_password_tty(message, not_after, filename, &password);
377
378                         if (arg_console) {
379                                 close_nointr_nofail(tty_fd);
380                                 release_terminal();
381                         }
382
383                         if (r >= 0) {
384                                 packet_length = 1+strlen(password)+1;
385                                 if (!(packet = new(char, packet_length)))
386                                         r = -ENOMEM;
387                                 else {
388                                         packet[0] = '+';
389                                         strcpy(packet+1, password);
390                                 }
391
392                                 free(password);
393                         }
394                 }
395
396                 if (r == -ETIME || r == -ENOENT) {
397                         /* If the query went away, that's OK */
398                         r = 0;
399                         goto finish;
400                 }
401
402                 if (r < 0) {
403                         log_error("Failed to query password: %s", strerror(-r));
404                         goto finish;
405                 }
406
407                 if ((socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
408                         log_error("socket(): %m");
409                         r = -errno;
410                         goto finish;
411                 }
412
413                 zero(sa);
414                 sa.un.sun_family = AF_UNIX;
415                 strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
416
417                 if (sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) {
418                         log_error("Failed to send: %m");
419                         r = -errno;
420                         goto finish;
421                 }
422         }
423
424 finish:
425         fclose(f);
426
427         if (socket_fd >= 0)
428                 close_nointr_nofail(socket_fd);
429
430         free(packet);
431         free(socket_name);
432         free(message);
433
434         return r;
435 }
436
437 static int wall_tty_block(void) {
438         char *p;
439         int fd, r;
440         dev_t devnr;
441
442         r = get_ctty_devnr(0, &devnr);
443         if (r < 0)
444                 return -r;
445
446         if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
447                 return -ENOMEM;
448
449         mkdir_parents_label(p, 0700);
450         mkfifo(p, 0600);
451
452         fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
453         free(p);
454
455         if (fd < 0)
456                 return -errno;
457
458         return fd;
459 }
460
461 static bool wall_tty_match(const char *path) {
462         int fd, k;
463         char *p;
464         struct stat st;
465
466         if (path_is_absolute(path))
467                 k = lstat(path, &st);
468         else {
469                 if (asprintf(&p, "/dev/%s", path) < 0)
470                         return true;
471
472                 k = lstat(p, &st);
473                 free(p);
474         }
475
476         if (k < 0)
477                 return true;
478
479         if (!S_ISCHR(st.st_mode))
480                 return true;
481
482         /* We use named pipes to ensure that wall messages suggesting
483          * password entry are not printed over password prompts
484          * already shown. We use the fact here that opening a pipe in
485          * non-blocking mode for write-only will succeed only if
486          * there's some writer behind it. Using pipes has the
487          * advantage that the block will automatically go away if the
488          * process dies. */
489
490         if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
491                 return true;
492
493         fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
494         free(p);
495
496         if (fd < 0)
497                 return true;
498
499         /* What, we managed to open the pipe? Then this tty is filtered. */
500         close_nointr_nofail(fd);
501         return false;
502 }
503
504 static int show_passwords(void) {
505         DIR *d;
506         struct dirent *de;
507         int r = 0;
508
509         if (!(d = opendir("/run/systemd/ask-password"))) {
510                 if (errno == ENOENT)
511                         return 0;
512
513                 log_error("opendir(): %m");
514                 return -errno;
515         }
516
517         while ((de = readdir(d))) {
518                 char *p;
519                 int q;
520                 char *wall;
521
522                 /* We only support /dev on tmpfs, hence we can rely on
523                  * d_type to be reliable */
524
525                 if (de->d_type != DT_REG)
526                         continue;
527
528                 if (ignore_file(de->d_name))
529                         continue;
530
531                 if (!startswith(de->d_name, "ask."))
532                         continue;
533
534                 if (!(p = strappend("/run/systemd/ask-password/", de->d_name))) {
535                         log_error("Out of memory");
536                         r = -ENOMEM;
537                         goto finish;
538                 }
539
540                 wall = NULL;
541                 if ((q = parse_password(p, &wall)) < 0)
542                         r = q;
543
544                 free(p);
545
546                 if (wall) {
547                         utmp_wall(wall, wall_tty_match);
548                         free(wall);
549                 }
550         }
551
552 finish:
553         if (d)
554                 closedir(d);
555
556         return r;
557 }
558
559 static int watch_passwords(void) {
560         enum {
561                 FD_INOTIFY,
562                 FD_SIGNAL,
563                 _FD_MAX
564         };
565
566         int notify = -1, signal_fd = -1, tty_block_fd = -1;
567         struct pollfd pollfd[_FD_MAX];
568         sigset_t mask;
569         int r;
570
571         tty_block_fd = wall_tty_block();
572
573         mkdir_p_label("/run/systemd/ask-password", 0755);
574
575         if ((notify = inotify_init1(IN_CLOEXEC)) < 0) {
576                 r = -errno;
577                 goto finish;
578         }
579
580         if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0) {
581                 r = -errno;
582                 goto finish;
583         }
584
585         assert_se(sigemptyset(&mask) == 0);
586         sigset_add_many(&mask, SIGINT, SIGTERM, -1);
587         assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
588
589         if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
590                 log_error("signalfd(): %m");
591                 r = -errno;
592                 goto finish;
593         }
594
595         zero(pollfd);
596         pollfd[FD_INOTIFY].fd = notify;
597         pollfd[FD_INOTIFY].events = POLLIN;
598         pollfd[FD_SIGNAL].fd = signal_fd;
599         pollfd[FD_SIGNAL].events = POLLIN;
600
601         for (;;) {
602                 if ((r = show_passwords()) < 0)
603                         log_error("Failed to show password: %s", strerror(-r));
604
605                 if (poll(pollfd, _FD_MAX, -1) < 0) {
606
607                         if (errno == EINTR)
608                                 continue;
609
610                         r = -errno;
611                         goto finish;
612                 }
613
614                 if (pollfd[FD_INOTIFY].revents != 0)
615                         flush_fd(notify);
616
617                 if (pollfd[FD_SIGNAL].revents != 0)
618                         break;
619         }
620
621         r = 0;
622
623 finish:
624         if (notify >= 0)
625                 close_nointr_nofail(notify);
626
627         if (signal_fd >= 0)
628                 close_nointr_nofail(signal_fd);
629
630         if (tty_block_fd >= 0)
631                 close_nointr_nofail(tty_block_fd);
632
633         return r;
634 }
635
636 static int help(void) {
637
638         printf("%s [OPTIONS...]\n\n"
639                "Process system password requests.\n\n"
640                "  -h --help     Show this help\n"
641                "     --list     Show pending password requests\n"
642                "     --query    Process pending password requests\n"
643                "     --watch    Continuously process password requests\n"
644                "     --wall     Continuously forward password requests to wall\n"
645                "     --plymouth Ask question with Plymouth instead of on TTY\n"
646                "     --console  Ask question on /dev/console instead of current TTY\n",
647                program_invocation_short_name);
648
649         return 0;
650 }
651
652 static int parse_argv(int argc, char *argv[]) {
653
654         enum {
655                 ARG_LIST = 0x100,
656                 ARG_QUERY,
657                 ARG_WATCH,
658                 ARG_WALL,
659                 ARG_PLYMOUTH,
660                 ARG_CONSOLE
661         };
662
663         static const struct option options[] = {
664                 { "help",     no_argument, NULL, 'h'          },
665                 { "list",     no_argument, NULL, ARG_LIST     },
666                 { "query",    no_argument, NULL, ARG_QUERY    },
667                 { "watch",    no_argument, NULL, ARG_WATCH    },
668                 { "wall",     no_argument, NULL, ARG_WALL     },
669                 { "plymouth", no_argument, NULL, ARG_PLYMOUTH },
670                 { "console",  no_argument, NULL, ARG_CONSOLE  },
671                 { NULL,    0,           NULL, 0               }
672         };
673
674         int c;
675
676         assert(argc >= 0);
677         assert(argv);
678
679         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
680
681                 switch (c) {
682
683                 case 'h':
684                         help();
685                         return 0;
686
687                 case ARG_LIST:
688                         arg_action = ACTION_LIST;
689                         break;
690
691                 case ARG_QUERY:
692                         arg_action = ACTION_QUERY;
693                         break;
694
695                 case ARG_WATCH:
696                         arg_action = ACTION_WATCH;
697                         break;
698
699                 case ARG_WALL:
700                         arg_action = ACTION_WALL;
701                         break;
702
703                 case ARG_PLYMOUTH:
704                         arg_plymouth = true;
705                         break;
706
707                 case ARG_CONSOLE:
708                         arg_console = true;
709                         break;
710
711                 case '?':
712                         return -EINVAL;
713
714                 default:
715                         log_error("Unknown option code %c", c);
716                         return -EINVAL;
717                 }
718         }
719
720         if (optind != argc) {
721                 help();
722                 return -EINVAL;
723         }
724
725         return 1;
726 }
727
728 int main(int argc, char *argv[]) {
729         int r;
730
731         log_parse_environment();
732         log_open();
733
734         umask(0022);
735
736         if ((r = parse_argv(argc, argv)) <= 0)
737                 goto finish;
738
739         if (arg_console) {
740                 setsid();
741                 release_terminal();
742         }
743
744         if (arg_action == ACTION_WATCH ||
745             arg_action == ACTION_WALL)
746                 r = watch_passwords();
747         else
748                 r = show_passwords();
749
750         if (r < 0)
751                 log_error("Error: %s", strerror(-r));
752
753 finish:
754         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
755 }