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"
40 #include "ask-password-api.h"
42 static void backspace_chars(int ttyfd, size_t p) {
50 loop_write(ttyfd, "\b \b", 3, false);
58 const char *flag_file,
61 struct termios old_termios, new_termios;
62 char passphrase[LINE_MAX], *x;
65 _cleanup_close_ int ttyfd = -1, notify = -1;
66 struct pollfd pollfd[2];
67 bool reset_tty = false;
68 bool silent_mode = false;
79 notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
85 if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
91 ttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC);
94 if (tcgetattr(ttyfd, &old_termios) < 0) {
99 loop_write(ttyfd, ANSI_HIGHLIGHT_ON, sizeof(ANSI_HIGHLIGHT_ON)-1, false);
100 loop_write(ttyfd, message, strlen(message), false);
101 loop_write(ttyfd, " ", 1, false);
102 loop_write(ttyfd, ANSI_HIGHLIGHT_OFF, sizeof(ANSI_HIGHLIGHT_OFF)-1, false);
104 new_termios = old_termios;
105 new_termios.c_lflag &= ~(ICANON|ECHO);
106 new_termios.c_cc[VMIN] = 1;
107 new_termios.c_cc[VTIME] = 0;
109 if (tcsetattr(ttyfd, TCSADRAIN, &new_termios) < 0) {
118 pollfd[POLL_TTY].fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO;
119 pollfd[POLL_TTY].events = POLLIN;
120 pollfd[POLL_INOTIFY].fd = notify;
121 pollfd[POLL_INOTIFY].events = POLLIN;
125 int sleep_for = -1, k;
131 y = now(CLOCK_MONOTONIC);
138 sleep_for = (int) ((until - y) / USEC_PER_MSEC);
142 if (access(flag_file, F_OK) < 0) {
147 k = poll(pollfd, notify > 0 ? 2 : 1, sleep_for);
159 if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
162 if (pollfd[POLL_TTY].revents == 0)
165 n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1);
167 if (errno == EINTR || errno == EAGAIN)
178 else if (c == 21) { /* C-u */
181 backspace_chars(ttyfd, p);
184 } else if (c == '\b' || c == 127) {
189 backspace_chars(ttyfd, 1);
192 } else if (!dirty && !silent_mode) {
196 /* There are two ways to enter silent
197 * mode. Either by pressing backspace
198 * as first key (and only as first key),
201 loop_write(ttyfd, "(no echo) ", 10, false);
203 } else if (ttyfd >= 0)
204 loop_write(ttyfd, "\a", 1, false);
206 } else if (c == '\t' && !silent_mode) {
208 backspace_chars(ttyfd, p);
211 /* ... or by pressing TAB at any time. */
214 loop_write(ttyfd, "(no echo) ", 10, false);
216 if (p >= sizeof(passphrase)-1) {
217 loop_write(ttyfd, "\a", 1, false);
223 if (!silent_mode && ttyfd >= 0)
224 loop_write(ttyfd, echo ? &c : "*", 1, false);
230 x = strndup(passphrase, p);
240 if (ttyfd >= 0 && reset_tty) {
241 loop_write(ttyfd, "\n", 1, false);
242 tcsetattr(ttyfd, TCSADRAIN, &old_termios);
248 static int create_socket(char **name) {
252 struct sockaddr_un un;
254 .un.sun_family = AF_UNIX,
262 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
264 return log_error_errno(errno, "socket() failed: %m");
266 snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%" PRIx64, random_u64());
268 RUN_WITH_UMASK(0177) {
269 r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
274 log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
278 if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
280 log_error_errno(errno, "SO_PASSCRED failed: %m");
284 c = strdup(sa.un.sun_path);
299 int ask_password_agent(
306 char ***_passphrases) {
314 char temp[] = "/run/systemd/ask-password/tmp.XXXXXX";
315 char final[sizeof(temp)] = "";
316 _cleanup_fclose_ FILE *f = NULL;
317 _cleanup_free_ char *socket_name = NULL;
318 _cleanup_close_ int socket_fd = -1, signal_fd = -1, fd = -1;
319 sigset_t mask, oldmask;
320 struct pollfd pollfd[_FD_MAX];
323 assert(_passphrases);
325 assert_se(sigemptyset(&mask) == 0);
326 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
327 assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0);
329 mkdir_p_label("/run/systemd/ask-password", 0755);
331 fd = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
333 log_error_errno(errno, "Failed to create password file: %m");
342 log_error_errno(errno, "Failed to allocate FILE: %m");
349 signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
351 log_error_errno(errno, "signalfd(): %m");
356 socket_fd = create_socket(&socket_name);
368 "NotAfter="USEC_FMT"\n",
371 accept_cached ? 1 : 0,
376 fprintf(f, "Message=%s\n", message);
379 fprintf(f, "Icon=%s\n", icon);
382 fprintf(f, "Id=%s\n", id);
387 log_error_errno(errno, "Failed to write query file: %m");
392 memcpy(final, temp, sizeof(temp));
394 final[sizeof(final)-11] = 'a';
395 final[sizeof(final)-10] = 's';
396 final[sizeof(final)-9] = 'k';
398 if (rename(temp, final) < 0) {
399 log_error_errno(errno, "Failed to rename query file: %m");
405 pollfd[FD_SOCKET].fd = socket_fd;
406 pollfd[FD_SOCKET].events = POLLIN;
407 pollfd[FD_SIGNAL].fd = signal_fd;
408 pollfd[FD_SIGNAL].events = POLLIN;
411 char passphrase[LINE_MAX+1];
412 struct msghdr msghdr;
416 struct cmsghdr cmsghdr;
417 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
423 t = now(CLOCK_MONOTONIC);
425 if (until > 0 && until <= t) {
426 log_notice("Timed out");
431 k = poll(pollfd, _FD_MAX, until > 0 ? (int) ((until-t)/USEC_PER_MSEC) : -1);
436 log_error_errno(errno, "poll() failed: %m");
442 log_notice("Timed out");
447 if (pollfd[FD_SIGNAL].revents & POLLIN) {
452 if (pollfd[FD_SOCKET].revents != POLLIN) {
453 log_error("Unexpected poll() event.");
459 iovec.iov_base = passphrase;
460 iovec.iov_len = sizeof(passphrase);
464 msghdr.msg_iov = &iovec;
465 msghdr.msg_iovlen = 1;
466 msghdr.msg_control = &control;
467 msghdr.msg_controllen = sizeof(control);
469 n = recvmsg(socket_fd, &msghdr, 0);
471 if (errno == EAGAIN ||
475 log_error_errno(errno, "recvmsg() failed: %m");
480 cmsg_close_all(&msghdr);
483 log_error("Message too short");
487 if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
488 control.cmsghdr.cmsg_level != SOL_SOCKET ||
489 control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
490 control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
491 log_warning("Received message without credentials. Ignoring.");
495 ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
496 if (ucred->uid != 0) {
497 log_warning("Got request from unprivileged user. Ignoring.");
501 if (passphrase[0] == '+') {
505 l = strv_new("", NULL);
507 l = strv_parse_nulstr(passphrase+1, n-1);
508 /* An empty message refers to the empty password */
515 if (strv_length(l) <= 0) {
517 log_error("Invalid packet");
523 } else if (passphrase[0] == '-') {
527 log_error("Invalid packet");
545 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
550 int ask_password_auto(const char *message, const char *icon, const char *id,
551 usec_t until, bool accept_cached, char ***_passphrases) {
553 assert(_passphrases);
555 if (isatty(STDIN_FILENO)) {
557 char *s = NULL, **l = NULL;
559 r = ask_password_tty(message, until, false, NULL, &s);
563 r = strv_consume(&l, s);
570 return ask_password_agent(message, icon, id, until, false, accept_cached, _passphrases);