1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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/>.
25 #include <sys/inotify.h>
28 #include <sys/socket.h>
32 #include <sys/signalfd.h>
35 #include "formats-util.h"
38 #include "random-util.h"
39 #include "terminal-util.h"
40 #include "signal-util.h"
41 #include "ask-password-api.h"
43 static void backspace_chars(int ttyfd, size_t p) {
51 loop_write(ttyfd, "\b \b", 3, false);
59 const char *flag_file,
62 struct termios old_termios, new_termios;
63 char passphrase[LINE_MAX], *x;
66 _cleanup_close_ int ttyfd = -1, notify = -1;
67 struct pollfd pollfd[2];
68 bool reset_tty = false;
69 bool silent_mode = false;
80 notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
86 if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
92 ttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC);
95 if (tcgetattr(ttyfd, &old_termios) < 0) {
100 loop_write(ttyfd, ANSI_HIGHLIGHT_ON, sizeof(ANSI_HIGHLIGHT_ON)-1, false);
101 loop_write(ttyfd, message, strlen(message), false);
102 loop_write(ttyfd, " ", 1, false);
103 loop_write(ttyfd, ANSI_HIGHLIGHT_OFF, sizeof(ANSI_HIGHLIGHT_OFF)-1, false);
105 new_termios = old_termios;
106 new_termios.c_lflag &= ~(ICANON|ECHO);
107 new_termios.c_cc[VMIN] = 1;
108 new_termios.c_cc[VTIME] = 0;
110 if (tcsetattr(ttyfd, TCSADRAIN, &new_termios) < 0) {
119 pollfd[POLL_TTY].fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO;
120 pollfd[POLL_TTY].events = POLLIN;
121 pollfd[POLL_INOTIFY].fd = notify;
122 pollfd[POLL_INOTIFY].events = POLLIN;
126 int sleep_for = -1, k;
132 y = now(CLOCK_MONOTONIC);
139 sleep_for = (int) ((until - y) / USEC_PER_MSEC);
143 if (access(flag_file, F_OK) < 0) {
148 k = poll(pollfd, notify > 0 ? 2 : 1, sleep_for);
160 if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
163 if (pollfd[POLL_TTY].revents == 0)
166 n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1);
168 if (errno == EINTR || errno == EAGAIN)
179 else if (c == 21) { /* C-u */
182 backspace_chars(ttyfd, p);
185 } else if (c == '\b' || c == 127) {
190 backspace_chars(ttyfd, 1);
193 } else if (!dirty && !silent_mode) {
197 /* There are two ways to enter silent
198 * mode. Either by pressing backspace
199 * as first key (and only as first key),
202 loop_write(ttyfd, "(no echo) ", 10, false);
204 } else if (ttyfd >= 0)
205 loop_write(ttyfd, "\a", 1, false);
207 } else if (c == '\t' && !silent_mode) {
209 backspace_chars(ttyfd, p);
212 /* ... or by pressing TAB at any time. */
215 loop_write(ttyfd, "(no echo) ", 10, false);
217 if (p >= sizeof(passphrase)-1) {
218 loop_write(ttyfd, "\a", 1, false);
224 if (!silent_mode && ttyfd >= 0)
225 loop_write(ttyfd, echo ? &c : "*", 1, false);
231 x = strndup(passphrase, p);
241 if (ttyfd >= 0 && reset_tty) {
242 loop_write(ttyfd, "\n", 1, false);
243 tcsetattr(ttyfd, TCSADRAIN, &old_termios);
249 static int create_socket(char **name) {
253 struct sockaddr_un un;
255 .un.sun_family = AF_UNIX,
263 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
265 return log_error_errno(errno, "socket() failed: %m");
267 snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%" PRIx64, random_u64());
269 RUN_WITH_UMASK(0177) {
270 r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
275 log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
279 if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
281 log_error_errno(errno, "SO_PASSCRED failed: %m");
285 c = strdup(sa.un.sun_path);
300 int ask_password_agent(
307 char ***_passphrases) {
315 char temp[] = "/run/systemd/ask-password/tmp.XXXXXX";
316 char final[sizeof(temp)] = "";
317 _cleanup_fclose_ FILE *f = NULL;
318 _cleanup_free_ char *socket_name = NULL;
319 _cleanup_close_ int socket_fd = -1, signal_fd = -1, fd = -1;
320 sigset_t mask, oldmask;
321 struct pollfd pollfd[_FD_MAX];
324 assert(_passphrases);
326 assert_se(sigemptyset(&mask) >= 0);
327 assert_se(sigset_add_many(&mask, SIGINT, SIGTERM, -1) >= 0);
328 assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) >= 0);
330 mkdir_p_label("/run/systemd/ask-password", 0755);
332 fd = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
334 log_error_errno(errno, "Failed to create password file: %m");
343 log_error_errno(errno, "Failed to allocate FILE: %m");
350 signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
352 log_error_errno(errno, "signalfd(): %m");
357 socket_fd = create_socket(&socket_name);
369 "NotAfter="USEC_FMT"\n",
372 accept_cached ? 1 : 0,
377 fprintf(f, "Message=%s\n", message);
380 fprintf(f, "Icon=%s\n", icon);
383 fprintf(f, "Id=%s\n", id);
388 log_error_errno(errno, "Failed to write query file: %m");
393 memcpy(final, temp, sizeof(temp));
395 final[sizeof(final)-11] = 'a';
396 final[sizeof(final)-10] = 's';
397 final[sizeof(final)-9] = 'k';
399 if (rename(temp, final) < 0) {
400 log_error_errno(errno, "Failed to rename query file: %m");
406 pollfd[FD_SOCKET].fd = socket_fd;
407 pollfd[FD_SOCKET].events = POLLIN;
408 pollfd[FD_SIGNAL].fd = signal_fd;
409 pollfd[FD_SIGNAL].events = POLLIN;
412 char passphrase[LINE_MAX+1];
413 struct msghdr msghdr;
417 struct cmsghdr cmsghdr;
418 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
424 t = now(CLOCK_MONOTONIC);
426 if (until > 0 && until <= t) {
427 log_notice("Timed out");
432 k = poll(pollfd, _FD_MAX, until > 0 ? (int) ((until-t)/USEC_PER_MSEC) : -1);
437 log_error_errno(errno, "poll() failed: %m");
443 log_notice("Timed out");
448 if (pollfd[FD_SIGNAL].revents & POLLIN) {
453 if (pollfd[FD_SOCKET].revents != POLLIN) {
454 log_error("Unexpected poll() event.");
460 iovec.iov_base = passphrase;
461 iovec.iov_len = sizeof(passphrase);
465 msghdr.msg_iov = &iovec;
466 msghdr.msg_iovlen = 1;
467 msghdr.msg_control = &control;
468 msghdr.msg_controllen = sizeof(control);
470 n = recvmsg(socket_fd, &msghdr, 0);
472 if (errno == EAGAIN ||
476 log_error_errno(errno, "recvmsg() failed: %m");
481 cmsg_close_all(&msghdr);
484 log_error("Message too short");
488 if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
489 control.cmsghdr.cmsg_level != SOL_SOCKET ||
490 control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
491 control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
492 log_warning("Received message without credentials. Ignoring.");
496 ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
497 if (ucred->uid != 0) {
498 log_warning("Got request from unprivileged user. Ignoring.");
502 if (passphrase[0] == '+') {
506 l = strv_new("", NULL);
508 l = strv_parse_nulstr(passphrase+1, n-1);
509 /* An empty message refers to the empty password */
516 if (strv_length(l) <= 0) {
518 log_error("Invalid packet");
524 } else if (passphrase[0] == '-') {
528 log_error("Invalid packet");
546 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
551 int ask_password_auto(const char *message, const char *icon, const char *id,
552 usec_t until, bool accept_cached, char ***_passphrases) {
554 assert(_passphrases);
556 if (isatty(STDIN_FILENO)) {
558 char *s = NULL, **l = NULL;
560 r = ask_password_tty(message, until, false, NULL, &s);
564 r = strv_consume(&l, s);
571 return ask_password_agent(message, icon, id, until, false, accept_cached, _passphrases);