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,
258 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
260 log_error("socket() failed: %m");
264 snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%llu", random_ull());
266 RUN_WITH_UMASK(0177) {
267 r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
272 log_error("bind() failed: %m");
276 if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
278 log_error("SO_PASSCRED failed: %m");
282 c = strdup(sa.un.sun_path);
292 close_nointr_nofail(fd);
297 int ask_password_agent(
302 char ***_passphrases) {
310 char temp[] = "/run/systemd/ask-password/tmp.XXXXXX";
311 char final[sizeof(temp)] = "";
314 char *socket_name = NULL;
315 int socket_fd = -1, signal_fd = -1;
316 sigset_t mask, oldmask;
317 struct pollfd pollfd[_FD_MAX];
319 assert(_passphrases);
321 assert_se(sigemptyset(&mask) == 0);
322 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
323 assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0);
325 mkdir_p_label("/run/systemd/ask-password", 0755);
327 RUN_WITH_UMASK(0022) {
328 fd = mkostemp(temp, O_CLOEXEC|O_CREAT|O_WRONLY);
332 log_error("Failed to create password file: %m");
339 if (!(f = fdopen(fd, "w"))) {
340 log_error("Failed to allocate FILE: %m");
347 if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
348 log_error("signalfd(): %m");
353 if ((socket_fd = create_socket(&socket_name)) < 0) {
364 (unsigned long) getpid(),
366 accept_cached ? 1 : 0,
367 (unsigned long long) until);
370 fprintf(f, "Message=%s\n", message);
373 fprintf(f, "Icon=%s\n", icon);
378 log_error("Failed to write query file: %m");
383 memcpy(final, temp, sizeof(temp));
385 final[sizeof(final)-11] = 'a';
386 final[sizeof(final)-10] = 's';
387 final[sizeof(final)-9] = 'k';
389 if (rename(temp, final) < 0) {
390 log_error("Failed to rename query file: %m");
396 pollfd[FD_SOCKET].fd = socket_fd;
397 pollfd[FD_SOCKET].events = POLLIN;
398 pollfd[FD_SIGNAL].fd = signal_fd;
399 pollfd[FD_SIGNAL].events = POLLIN;
402 char passphrase[LINE_MAX+1];
403 struct msghdr msghdr;
407 struct cmsghdr cmsghdr;
408 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
414 t = now(CLOCK_MONOTONIC);
416 if (until > 0 && until <= t) {
417 log_notice("Timed out");
422 if ((k = poll(pollfd, _FD_MAX, until > 0 ? (int) ((until-t)/USEC_PER_MSEC) : -1)) < 0) {
427 log_error("poll() failed: %m");
433 log_notice("Timed out");
438 if (pollfd[FD_SIGNAL].revents & POLLIN) {
443 if (pollfd[FD_SOCKET].revents != POLLIN) {
444 log_error("Unexpected poll() event.");
450 iovec.iov_base = passphrase;
451 iovec.iov_len = sizeof(passphrase);
455 msghdr.msg_iov = &iovec;
456 msghdr.msg_iovlen = 1;
457 msghdr.msg_control = &control;
458 msghdr.msg_controllen = sizeof(control);
460 if ((n = recvmsg(socket_fd, &msghdr, 0)) < 0) {
462 if (errno == EAGAIN ||
466 log_error("recvmsg() failed: %m");
472 log_error("Message too short");
476 if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
477 control.cmsghdr.cmsg_level != SOL_SOCKET ||
478 control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
479 control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
480 log_warning("Received message without credentials. Ignoring.");
484 ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
485 if (ucred->uid != 0) {
486 log_warning("Got request from unprivileged user. Ignoring.");
490 if (passphrase[0] == '+') {
494 l = strv_new("", NULL);
496 l = strv_parse_nulstr(passphrase+1, n-1);
497 /* An empty message refers to the empty password */
504 if (strv_length(l) <= 0) {
506 log_error("Invalid packet");
512 } else if (passphrase[0] == '-') {
516 log_error("Invalid packet");
527 close_nointr_nofail(fd);
535 close_nointr_nofail(socket_fd);
538 close_nointr_nofail(signal_fd);
548 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
553 int ask_password_auto(const char *message, const char *icon, usec_t until, bool accept_cached, char ***_passphrases) {
555 assert(_passphrases);
557 if (isatty(STDIN_FILENO)) {
559 char *s = NULL, **l = NULL;
561 if ((r = ask_password_tty(message, until, NULL, &s)) < 0)
564 l = strv_new(s, NULL);
574 return ask_password_agent(message, icon, until, accept_cached, _passphrases);