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);
210 if (p >= sizeof(passphrase)-1) {
211 loop_write(ttyfd, "\a", 1, false);
217 if (!silent_mode && ttyfd >= 0)
218 loop_write(ttyfd, "*", 1, false);
226 if (!(*_passphrase = strdup(passphrase))) {
239 loop_write(ttyfd, "\n", 1, false);
240 tcsetattr(ttyfd, TCSADRAIN, &old_termios);
249 static int create_socket(char **name) {
253 struct sockaddr_un un;
255 .un.sun_family = AF_UNIX,
263 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
265 log_error("socket() failed: %m");
269 snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%" PRIx64, random_u64());
271 RUN_WITH_UMASK(0177) {
272 r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
277 log_error("bind() failed: %m");
281 if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
283 log_error("SO_PASSCRED failed: %m");
287 c = strdup(sa.un.sun_path);
302 int ask_password_agent(
307 char ***_passphrases) {
315 char temp[] = "/run/systemd/ask-password/tmp.XXXXXX";
316 char final[sizeof(temp)] = "";
319 char *socket_name = NULL;
320 int socket_fd = -1, signal_fd = -1;
321 sigset_t mask, oldmask;
322 struct pollfd pollfd[_FD_MAX];
324 assert(_passphrases);
326 assert_se(sigemptyset(&mask) == 0);
327 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
328 assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0);
330 mkdir_p_label("/run/systemd/ask-password", 0755);
332 fd = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
334 log_error("Failed to create password file: %m");
341 if (!(f = fdopen(fd, "w"))) {
342 log_error("Failed to allocate FILE: %m");
349 if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
350 log_error("signalfd(): %m");
355 if ((socket_fd = create_socket(&socket_name)) < 0) {
366 (unsigned long) getpid(),
368 accept_cached ? 1 : 0,
369 (unsigned long long) until);
372 fprintf(f, "Message=%s\n", message);
375 fprintf(f, "Icon=%s\n", icon);
380 log_error("Failed to write query file: %m");
385 memcpy(final, temp, sizeof(temp));
387 final[sizeof(final)-11] = 'a';
388 final[sizeof(final)-10] = 's';
389 final[sizeof(final)-9] = 'k';
391 if (rename(temp, final) < 0) {
392 log_error("Failed to rename query file: %m");
398 pollfd[FD_SOCKET].fd = socket_fd;
399 pollfd[FD_SOCKET].events = POLLIN;
400 pollfd[FD_SIGNAL].fd = signal_fd;
401 pollfd[FD_SIGNAL].events = POLLIN;
404 char passphrase[LINE_MAX+1];
405 struct msghdr msghdr;
409 struct cmsghdr cmsghdr;
410 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
416 t = now(CLOCK_MONOTONIC);
418 if (until > 0 && until <= t) {
419 log_notice("Timed out");
424 if ((k = poll(pollfd, _FD_MAX, until > 0 ? (int) ((until-t)/USEC_PER_MSEC) : -1)) < 0) {
429 log_error("poll() failed: %m");
435 log_notice("Timed out");
440 if (pollfd[FD_SIGNAL].revents & POLLIN) {
445 if (pollfd[FD_SOCKET].revents != POLLIN) {
446 log_error("Unexpected poll() event.");
452 iovec.iov_base = passphrase;
453 iovec.iov_len = sizeof(passphrase);
457 msghdr.msg_iov = &iovec;
458 msghdr.msg_iovlen = 1;
459 msghdr.msg_control = &control;
460 msghdr.msg_controllen = sizeof(control);
462 if ((n = recvmsg(socket_fd, &msghdr, 0)) < 0) {
464 if (errno == EAGAIN ||
468 log_error("recvmsg() failed: %m");
474 log_error("Message too short");
478 if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
479 control.cmsghdr.cmsg_level != SOL_SOCKET ||
480 control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
481 control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
482 log_warning("Received message without credentials. Ignoring.");
486 ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
487 if (ucred->uid != 0) {
488 log_warning("Got request from unprivileged user. Ignoring.");
492 if (passphrase[0] == '+') {
496 l = strv_new("", NULL);
498 l = strv_parse_nulstr(passphrase+1, n-1);
499 /* An empty message refers to the empty password */
506 if (strv_length(l) <= 0) {
508 log_error("Invalid packet");
514 } else if (passphrase[0] == '-') {
518 log_error("Invalid packet");
535 safe_close(socket_fd);
536 safe_close(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);