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