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], *x;
62 _cleanup_close_ int ttyfd = -1, notify = -1;
63 struct pollfd pollfd[2];
64 bool reset_tty = false;
65 bool silent_mode = false;
76 notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
82 if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
88 ttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC);
91 if (tcgetattr(ttyfd, &old_termios) < 0) {
96 loop_write(ttyfd, ANSI_HIGHLIGHT_ON, sizeof(ANSI_HIGHLIGHT_ON)-1, false);
97 loop_write(ttyfd, message, strlen(message), false);
98 loop_write(ttyfd, " ", 1, false);
99 loop_write(ttyfd, ANSI_HIGHLIGHT_OFF, sizeof(ANSI_HIGHLIGHT_OFF)-1, false);
101 new_termios = old_termios;
102 new_termios.c_lflag &= ~(ICANON|ECHO);
103 new_termios.c_cc[VMIN] = 1;
104 new_termios.c_cc[VTIME] = 0;
106 if (tcsetattr(ttyfd, TCSADRAIN, &new_termios) < 0) {
115 pollfd[POLL_TTY].fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO;
116 pollfd[POLL_TTY].events = POLLIN;
117 pollfd[POLL_INOTIFY].fd = notify;
118 pollfd[POLL_INOTIFY].events = POLLIN;
122 int sleep_for = -1, k;
128 y = now(CLOCK_MONOTONIC);
135 sleep_for = (int) ((until - y) / USEC_PER_MSEC);
139 if (access(flag_file, F_OK) < 0) {
144 k = poll(pollfd, notify > 0 ? 2 : 1, sleep_for);
156 if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
159 if (pollfd[POLL_TTY].revents == 0)
162 n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1);
164 if (errno == EINTR || errno == EAGAIN)
175 else if (c == 21) { /* C-u */
178 backspace_chars(ttyfd, p);
181 } else if (c == '\b' || c == 127) {
186 backspace_chars(ttyfd, 1);
189 } else if (!dirty && !silent_mode) {
193 /* There are two ways to enter silent
194 * mode. Either by pressing backspace
195 * as first key (and only as first key),
198 loop_write(ttyfd, "(no echo) ", 10, false);
200 } else if (ttyfd >= 0)
201 loop_write(ttyfd, "\a", 1, false);
203 } else if (c == '\t' && !silent_mode) {
205 backspace_chars(ttyfd, p);
208 /* ... or by pressing TAB at any time. */
211 loop_write(ttyfd, "(no echo) ", 10, false);
213 if (p >= sizeof(passphrase)-1) {
214 loop_write(ttyfd, "\a", 1, false);
220 if (!silent_mode && ttyfd >= 0)
221 loop_write(ttyfd, "*", 1, false);
227 x = strndup(passphrase, p);
237 if (ttyfd >= 0 && reset_tty) {
238 loop_write(ttyfd, "\n", 1, false);
239 tcsetattr(ttyfd, TCSADRAIN, &old_termios);
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);
298 int ask_password_agent(
304 char ***_passphrases) {
312 char temp[] = "/run/systemd/ask-password/tmp.XXXXXX";
313 char final[sizeof(temp)] = "";
314 _cleanup_fclose_ FILE *f = NULL;
315 _cleanup_free_ char *socket_name = NULL;
316 _cleanup_close_ int socket_fd = -1, signal_fd = -1, fd = -1;
317 sigset_t mask, oldmask;
318 struct pollfd pollfd[_FD_MAX];
321 assert(_passphrases);
323 assert_se(sigemptyset(&mask) == 0);
324 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
325 assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0);
327 mkdir_p_label("/run/systemd/ask-password", 0755);
329 fd = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
331 log_error("Failed to create password file: %m");
340 log_error("Failed to allocate FILE: %m");
347 signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
349 log_error("signalfd(): %m");
354 socket_fd = create_socket(&socket_name);
365 "NotAfter="USEC_FMT"\n",
368 accept_cached ? 1 : 0,
372 fprintf(f, "Message=%s\n", message);
375 fprintf(f, "Icon=%s\n", icon);
378 fprintf(f, "Id=%s\n", id);
383 log_error("Failed to write query file: %m");
388 memcpy(final, temp, sizeof(temp));
390 final[sizeof(final)-11] = 'a';
391 final[sizeof(final)-10] = 's';
392 final[sizeof(final)-9] = 'k';
394 if (rename(temp, final) < 0) {
395 log_error("Failed to rename query file: %m");
401 pollfd[FD_SOCKET].fd = socket_fd;
402 pollfd[FD_SOCKET].events = POLLIN;
403 pollfd[FD_SIGNAL].fd = signal_fd;
404 pollfd[FD_SIGNAL].events = POLLIN;
407 char passphrase[LINE_MAX+1];
408 struct msghdr msghdr;
412 struct cmsghdr cmsghdr;
413 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
419 t = now(CLOCK_MONOTONIC);
421 if (until > 0 && until <= t) {
422 log_notice("Timed out");
427 k = poll(pollfd, _FD_MAX, until > 0 ? (int) ((until-t)/USEC_PER_MSEC) : -1);
432 log_error("poll() failed: %m");
438 log_notice("Timed out");
443 if (pollfd[FD_SIGNAL].revents & POLLIN) {
448 if (pollfd[FD_SOCKET].revents != POLLIN) {
449 log_error("Unexpected poll() event.");
455 iovec.iov_base = passphrase;
456 iovec.iov_len = sizeof(passphrase);
460 msghdr.msg_iov = &iovec;
461 msghdr.msg_iovlen = 1;
462 msghdr.msg_control = &control;
463 msghdr.msg_controllen = sizeof(control);
465 n = recvmsg(socket_fd, &msghdr, 0);
467 if (errno == EAGAIN ||
471 log_error("recvmsg() failed: %m");
477 log_error("Message too short");
481 if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
482 control.cmsghdr.cmsg_level != SOL_SOCKET ||
483 control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
484 control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
485 log_warning("Received message without credentials. Ignoring.");
489 ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
490 if (ucred->uid != 0) {
491 log_warning("Got request from unprivileged user. Ignoring.");
495 if (passphrase[0] == '+') {
499 l = strv_new("", NULL);
501 l = strv_parse_nulstr(passphrase+1, n-1);
502 /* An empty message refers to the empty password */
509 if (strv_length(l) <= 0) {
511 log_error("Invalid packet");
517 } else if (passphrase[0] == '-') {
521 log_error("Invalid packet");
539 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
544 int ask_password_auto(const char *message, const char *icon, const char *id,
545 usec_t until, bool accept_cached, char ***_passphrases) {
547 assert(_passphrases);
549 if (isatty(STDIN_FILENO)) {
551 char *s = NULL, **l = NULL;
553 r = ask_password_tty(message, until, NULL, &s);
557 r = strv_consume(&l, s);
564 return ask_password_agent(message, icon, id, until, accept_cached, _passphrases);