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) {
113 pollfd[POLL_TTY].fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO;
114 pollfd[POLL_TTY].events = POLLIN;
115 pollfd[POLL_INOTIFY].fd = notify;
116 pollfd[POLL_INOTIFY].events = POLLIN;
120 int sleep_for = -1, k;
126 y = now(CLOCK_MONOTONIC);
133 sleep_for = (int) ((until - y) / USEC_PER_MSEC);
137 if (access(flag_file, F_OK) < 0) {
142 if ((k = poll(pollfd, notify > 0 ? 2 : 1, sleep_for)) < 0) {
154 if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
157 if (pollfd[POLL_TTY].revents == 0)
160 if ((n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1)) < 0) {
162 if (errno == EINTR || errno == EAGAIN)
173 else if (c == 21) { /* C-u */
176 backspace_chars(ttyfd, p);
179 } else if (c == '\b' || c == 127) {
184 backspace_chars(ttyfd, 1);
187 } else if (!dirty && !silent_mode) {
191 /* There are two ways to enter silent
192 * mode. Either by pressing backspace
193 * as first key (and only as first key),
196 loop_write(ttyfd, "(no echo) ", 10, false);
198 } else if (ttyfd >= 0)
199 loop_write(ttyfd, "\a", 1, false);
201 } else if (c == '\t' && !silent_mode) {
203 backspace_chars(ttyfd, p);
206 /* ... or by pressing TAB at any time. */
209 loop_write(ttyfd, "(no echo) ", 10, false);
213 if (!silent_mode && ttyfd >= 0)
214 loop_write(ttyfd, "*", 1, false);
222 if (!(*_passphrase = strdup(passphrase))) {
231 close_nointr_nofail(notify);
236 loop_write(ttyfd, "\n", 1, false);
237 tcsetattr(ttyfd, TCSADRAIN, &old_termios);
240 close_nointr_nofail(ttyfd);
246 static int create_socket(char **name) {
250 struct sockaddr_un un;
258 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
260 log_error("socket() failed: %m");
265 sa.un.sun_family = AF_UNIX;
266 snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1,
267 "/run/systemd/ask-password/sck.%llu", random_ull());
270 r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
275 log_error("bind() failed: %m");
279 if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
281 log_error("SO_PASSCRED failed: %m");
285 c = strdup(sa.un.sun_path);
295 close_nointr_nofail(fd);
300 int ask_password_agent(
305 char ***_passphrases) {
313 char temp[] = "/run/systemd/ask-password/tmp.XXXXXX";
314 char final[sizeof(temp)] = "";
317 char *socket_name = NULL;
318 int socket_fd = -1, signal_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);
332 fd = mkostemp(temp, O_CLOEXEC|O_CREAT|O_WRONLY);
336 log_error("Failed to create password file: %m");
343 if (!(f = fdopen(fd, "w"))) {
344 log_error("Failed to allocate FILE: %m");
351 if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
352 log_error("signalfd(): %m");
357 if ((socket_fd = create_socket(&socket_name)) < 0) {
368 (unsigned long) getpid(),
370 accept_cached ? 1 : 0,
371 (unsigned long long) until);
374 fprintf(f, "Message=%s\n", message);
377 fprintf(f, "Icon=%s\n", icon);
382 log_error("Failed to write query file: %m");
387 memcpy(final, temp, sizeof(temp));
389 final[sizeof(final)-11] = 'a';
390 final[sizeof(final)-10] = 's';
391 final[sizeof(final)-9] = 'k';
393 if (rename(temp, final) < 0) {
394 log_error("Failed to rename query file: %m");
400 pollfd[FD_SOCKET].fd = socket_fd;
401 pollfd[FD_SOCKET].events = POLLIN;
402 pollfd[FD_SIGNAL].fd = signal_fd;
403 pollfd[FD_SIGNAL].events = POLLIN;
406 char passphrase[LINE_MAX+1];
407 struct msghdr msghdr;
411 struct cmsghdr cmsghdr;
412 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
418 t = now(CLOCK_MONOTONIC);
420 if (until > 0 && until <= t) {
421 log_notice("Timed out");
426 if ((k = poll(pollfd, _FD_MAX, until > 0 ? (int) ((until-t)/USEC_PER_MSEC) : -1)) < 0) {
431 log_error("poll() failed: %m");
437 log_notice("Timed out");
442 if (pollfd[FD_SIGNAL].revents & POLLIN) {
447 if (pollfd[FD_SOCKET].revents != POLLIN) {
448 log_error("Unexpected poll() event.");
454 iovec.iov_base = passphrase;
455 iovec.iov_len = sizeof(passphrase);
459 msghdr.msg_iov = &iovec;
460 msghdr.msg_iovlen = 1;
461 msghdr.msg_control = &control;
462 msghdr.msg_controllen = sizeof(control);
464 if ((n = recvmsg(socket_fd, &msghdr, 0)) < 0) {
466 if (errno == EAGAIN ||
470 log_error("recvmsg() failed: %m");
476 log_error("Message too short");
480 if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
481 control.cmsghdr.cmsg_level != SOL_SOCKET ||
482 control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
483 control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
484 log_warning("Received message without credentials. Ignoring.");
488 ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
489 if (ucred->uid != 0) {
490 log_warning("Got request from unprivileged user. Ignoring.");
494 if (passphrase[0] == '+') {
498 l = strv_new("", NULL);
500 l = strv_parse_nulstr(passphrase+1, n-1);
501 /* An empty message refers to the empty password */
508 if (strv_length(l) <= 0) {
510 log_error("Invalid packet");
516 } else if (passphrase[0] == '-') {
520 log_error("Invalid packet");
531 close_nointr_nofail(fd);
539 close_nointr_nofail(socket_fd);
542 close_nointr_nofail(signal_fd);
552 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
557 int ask_password_auto(const char *message, const char *icon, usec_t until, bool accept_cached, char ***_passphrases) {
559 assert(_passphrases);
561 if (isatty(STDIN_FILENO)) {
563 char *s = NULL, **l = NULL;
565 if ((r = ask_password_tty(message, until, NULL, &s)) < 0)
568 l = strv_new(s, NULL);
578 return ask_password_agent(message, icon, until, accept_cached, _passphrases);