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>
38 #include "ask-password-api.h"
40 static void backspace_chars(int ttyfd, size_t p) {
48 loop_write(ttyfd, "\b \b", 3, false);
56 const char *flag_file,
59 struct termios old_termios, new_termios;
60 char passphrase[LINE_MAX], *x;
63 _cleanup_close_ int ttyfd = -1, notify = -1;
64 struct pollfd pollfd[2];
65 bool reset_tty = false;
66 bool silent_mode = false;
77 notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
83 if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
89 ttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC);
92 if (tcgetattr(ttyfd, &old_termios) < 0) {
97 loop_write(ttyfd, ANSI_HIGHLIGHT_ON, sizeof(ANSI_HIGHLIGHT_ON)-1, false);
98 loop_write(ttyfd, message, strlen(message), false);
99 loop_write(ttyfd, " ", 1, false);
100 loop_write(ttyfd, ANSI_HIGHLIGHT_OFF, sizeof(ANSI_HIGHLIGHT_OFF)-1, false);
102 new_termios = old_termios;
103 new_termios.c_lflag &= ~(ICANON|ECHO);
104 new_termios.c_cc[VMIN] = 1;
105 new_termios.c_cc[VTIME] = 0;
107 if (tcsetattr(ttyfd, TCSADRAIN, &new_termios) < 0) {
116 pollfd[POLL_TTY].fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO;
117 pollfd[POLL_TTY].events = POLLIN;
118 pollfd[POLL_INOTIFY].fd = notify;
119 pollfd[POLL_INOTIFY].events = POLLIN;
123 int sleep_for = -1, k;
129 y = now(CLOCK_MONOTONIC);
136 sleep_for = (int) ((until - y) / USEC_PER_MSEC);
140 if (access(flag_file, F_OK) < 0) {
145 k = poll(pollfd, notify > 0 ? 2 : 1, sleep_for);
157 if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
160 if (pollfd[POLL_TTY].revents == 0)
163 n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1);
165 if (errno == EINTR || errno == EAGAIN)
176 else if (c == 21) { /* C-u */
179 backspace_chars(ttyfd, p);
182 } else if (c == '\b' || c == 127) {
187 backspace_chars(ttyfd, 1);
190 } else if (!dirty && !silent_mode) {
194 /* There are two ways to enter silent
195 * mode. Either by pressing backspace
196 * as first key (and only as first key),
199 loop_write(ttyfd, "(no echo) ", 10, false);
201 } else if (ttyfd >= 0)
202 loop_write(ttyfd, "\a", 1, false);
204 } else if (c == '\t' && !silent_mode) {
206 backspace_chars(ttyfd, p);
209 /* ... or by pressing TAB at any time. */
212 loop_write(ttyfd, "(no echo) ", 10, false);
214 if (p >= sizeof(passphrase)-1) {
215 loop_write(ttyfd, "\a", 1, false);
221 if (!silent_mode && ttyfd >= 0)
222 loop_write(ttyfd, echo ? &c : "*", 1, false);
228 x = strndup(passphrase, p);
238 if (ttyfd >= 0 && reset_tty) {
239 loop_write(ttyfd, "\n", 1, false);
240 tcsetattr(ttyfd, TCSADRAIN, &old_termios);
246 static int create_socket(char **name) {
250 struct sockaddr_un un;
252 .un.sun_family = AF_UNIX,
260 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
262 log_error("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("bind(%s) failed: %m", sa.un.sun_path);
278 if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
280 log_error("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("Failed to create password file: %m");
342 log_error("Failed to allocate FILE: %m");
349 signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
351 log_error("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("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("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("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("recvmsg() failed: %m");
481 log_error("Message too short");
485 if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
486 control.cmsghdr.cmsg_level != SOL_SOCKET ||
487 control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
488 control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
489 log_warning("Received message without credentials. Ignoring.");
493 ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
494 if (ucred->uid != 0) {
495 log_warning("Got request from unprivileged user. Ignoring.");
499 if (passphrase[0] == '+') {
503 l = strv_new("", NULL);
505 l = strv_parse_nulstr(passphrase+1, n-1);
506 /* An empty message refers to the empty password */
513 if (strv_length(l) <= 0) {
515 log_error("Invalid packet");
521 } else if (passphrase[0] == '-') {
525 log_error("Invalid packet");
543 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
548 int ask_password_auto(const char *message, const char *icon, const char *id,
549 usec_t until, bool accept_cached, char ***_passphrases) {
551 assert(_passphrases);
553 if (isatty(STDIN_FILENO)) {
555 char *s = NULL, **l = NULL;
557 r = ask_password_tty(message, until, false, NULL, &s);
561 r = strv_consume(&l, s);
568 return ask_password_agent(message, icon, id, until, false, accept_cached, _passphrases);