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);
55 const char *flag_file,
58 struct termios old_termios, new_termios;
59 char passphrase[LINE_MAX];
61 int r, ttyfd = -1, notify = -1;
62 struct pollfd pollfd[2];
63 bool reset_tty = false;
64 bool silent_mode = false;
75 if ((notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
80 if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
86 if ((ttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC)) >= 0) {
88 if (tcgetattr(ttyfd, &old_termios) < 0) {
93 loop_write(ttyfd, ANSI_HIGHLIGHT_ON, sizeof(ANSI_HIGHLIGHT_ON)-1, false);
94 loop_write(ttyfd, message, strlen(message), false);
95 loop_write(ttyfd, " ", 1, false);
96 loop_write(ttyfd, ANSI_HIGHLIGHT_OFF, sizeof(ANSI_HIGHLIGHT_OFF)-1, false);
98 new_termios = old_termios;
99 new_termios.c_lflag &= ~(ICANON|ECHO);
100 new_termios.c_cc[VMIN] = 1;
101 new_termios.c_cc[VTIME] = 0;
103 if (tcsetattr(ttyfd, TCSADRAIN, &new_termios) < 0) {
112 pollfd[POLL_TTY].fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO;
113 pollfd[POLL_TTY].events = POLLIN;
114 pollfd[POLL_INOTIFY].fd = notify;
115 pollfd[POLL_INOTIFY].events = POLLIN;
119 int sleep_for = -1, k;
125 y = now(CLOCK_MONOTONIC);
132 sleep_for = (int) ((until - y) / USEC_PER_MSEC);
136 if (access(flag_file, F_OK) < 0) {
141 if ((k = poll(pollfd, notify > 0 ? 2 : 1, sleep_for)) < 0) {
153 if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
156 if (pollfd[POLL_TTY].revents == 0)
159 if ((n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1)) < 0) {
161 if (errno == EINTR || errno == EAGAIN)
172 else if (c == 21) { /* C-u */
175 backspace_chars(ttyfd, p);
178 } else if (c == '\b' || c == 127) {
183 backspace_chars(ttyfd, 1);
186 } else if (!dirty && !silent_mode) {
190 /* There are two ways to enter silent
191 * mode. Either by pressing backspace
192 * as first key (and only as first key),
195 loop_write(ttyfd, "(no echo) ", 10, false);
197 } else if (ttyfd >= 0)
198 loop_write(ttyfd, "\a", 1, false);
200 } else if (c == '\t' && !silent_mode) {
202 backspace_chars(ttyfd, p);
205 /* ... or by pressing TAB at any time. */
208 loop_write(ttyfd, "(no echo) ", 10, false);
212 if (!silent_mode && ttyfd >= 0)
213 loop_write(ttyfd, "*", 1, false);
221 if (!(*_passphrase = strdup(passphrase))) {
230 close_nointr_nofail(notify);
235 loop_write(ttyfd, "\n", 1, false);
236 tcsetattr(ttyfd, TCSADRAIN, &old_termios);
239 close_nointr_nofail(ttyfd);
245 static int create_socket(char **name) {
249 struct sockaddr_un un;
251 .un.sun_family = AF_UNIX,
259 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
261 log_error("socket() failed: %m");
265 snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%" PRIx64, random_u64());
267 RUN_WITH_UMASK(0177) {
268 r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
273 log_error("bind() failed: %m");
277 if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
279 log_error("SO_PASSCRED failed: %m");
283 c = strdup(sa.un.sun_path);
293 close_nointr_nofail(fd);
298 int ask_password_agent(
303 char ***_passphrases) {
311 char temp[] = "/run/systemd/ask-password/tmp.XXXXXX";
312 char final[sizeof(temp)] = "";
315 char *socket_name = NULL;
316 int socket_fd = -1, signal_fd = -1;
317 sigset_t mask, oldmask;
318 struct pollfd pollfd[_FD_MAX];
320 assert(_passphrases);
322 assert_se(sigemptyset(&mask) == 0);
323 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
324 assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0);
326 mkdir_p_label("/run/systemd/ask-password", 0755);
328 fd = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
330 log_error("Failed to create password file: %m");
337 if (!(f = fdopen(fd, "w"))) {
338 log_error("Failed to allocate FILE: %m");
345 if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
346 log_error("signalfd(): %m");
351 if ((socket_fd = create_socket(&socket_name)) < 0) {
362 (unsigned long) getpid(),
364 accept_cached ? 1 : 0,
365 (unsigned long long) until);
368 fprintf(f, "Message=%s\n", message);
371 fprintf(f, "Icon=%s\n", icon);
376 log_error("Failed to write query file: %m");
381 memcpy(final, temp, sizeof(temp));
383 final[sizeof(final)-11] = 'a';
384 final[sizeof(final)-10] = 's';
385 final[sizeof(final)-9] = 'k';
387 if (rename(temp, final) < 0) {
388 log_error("Failed to rename query file: %m");
394 pollfd[FD_SOCKET].fd = socket_fd;
395 pollfd[FD_SOCKET].events = POLLIN;
396 pollfd[FD_SIGNAL].fd = signal_fd;
397 pollfd[FD_SIGNAL].events = POLLIN;
400 char passphrase[LINE_MAX+1];
401 struct msghdr msghdr;
405 struct cmsghdr cmsghdr;
406 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
412 t = now(CLOCK_MONOTONIC);
414 if (until > 0 && until <= t) {
415 log_notice("Timed out");
420 if ((k = poll(pollfd, _FD_MAX, until > 0 ? (int) ((until-t)/USEC_PER_MSEC) : -1)) < 0) {
425 log_error("poll() failed: %m");
431 log_notice("Timed out");
436 if (pollfd[FD_SIGNAL].revents & POLLIN) {
441 if (pollfd[FD_SOCKET].revents != POLLIN) {
442 log_error("Unexpected poll() event.");
448 iovec.iov_base = passphrase;
449 iovec.iov_len = sizeof(passphrase);
453 msghdr.msg_iov = &iovec;
454 msghdr.msg_iovlen = 1;
455 msghdr.msg_control = &control;
456 msghdr.msg_controllen = sizeof(control);
458 if ((n = recvmsg(socket_fd, &msghdr, 0)) < 0) {
460 if (errno == EAGAIN ||
464 log_error("recvmsg() failed: %m");
470 log_error("Message too short");
474 if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
475 control.cmsghdr.cmsg_level != SOL_SOCKET ||
476 control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
477 control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
478 log_warning("Received message without credentials. Ignoring.");
482 ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
483 if (ucred->uid != 0) {
484 log_warning("Got request from unprivileged user. Ignoring.");
488 if (passphrase[0] == '+') {
492 l = strv_new("", NULL);
494 l = strv_parse_nulstr(passphrase+1, n-1);
495 /* An empty message refers to the empty password */
502 if (strv_length(l) <= 0) {
504 log_error("Invalid packet");
510 } else if (passphrase[0] == '-') {
514 log_error("Invalid packet");
525 close_nointr_nofail(fd);
533 close_nointr_nofail(socket_fd);
536 close_nointr_nofail(signal_fd);
546 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
551 int ask_password_auto(const char *message, const char *icon, usec_t until, bool accept_cached, char ***_passphrases) {
553 assert(_passphrases);
555 if (isatty(STDIN_FILENO)) {
557 char *s = NULL, **l = NULL;
559 if ((r = ask_password_tty(message, until, NULL, &s)) < 0)
562 l = strv_new(s, NULL);
572 return ask_password_agent(message, icon, until, accept_cached, _passphrases);