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>
35 #include "formats-util.h"
39 #include "ask-password-api.h"
41 static void backspace_chars(int ttyfd, size_t p) {
49 loop_write(ttyfd, "\b \b", 3, false);
57 const char *flag_file,
60 struct termios old_termios, new_termios;
61 char passphrase[LINE_MAX], *x;
64 _cleanup_close_ int ttyfd = -1, notify = -1;
65 struct pollfd pollfd[2];
66 bool reset_tty = false;
67 bool silent_mode = false;
78 notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
84 if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
90 ttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC);
93 if (tcgetattr(ttyfd, &old_termios) < 0) {
98 loop_write(ttyfd, ANSI_HIGHLIGHT_ON, sizeof(ANSI_HIGHLIGHT_ON)-1, false);
99 loop_write(ttyfd, message, strlen(message), false);
100 loop_write(ttyfd, " ", 1, false);
101 loop_write(ttyfd, ANSI_HIGHLIGHT_OFF, sizeof(ANSI_HIGHLIGHT_OFF)-1, false);
103 new_termios = old_termios;
104 new_termios.c_lflag &= ~(ICANON|ECHO);
105 new_termios.c_cc[VMIN] = 1;
106 new_termios.c_cc[VTIME] = 0;
108 if (tcsetattr(ttyfd, TCSADRAIN, &new_termios) < 0) {
117 pollfd[POLL_TTY].fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO;
118 pollfd[POLL_TTY].events = POLLIN;
119 pollfd[POLL_INOTIFY].fd = notify;
120 pollfd[POLL_INOTIFY].events = POLLIN;
124 int sleep_for = -1, k;
130 y = now(CLOCK_MONOTONIC);
137 sleep_for = (int) ((until - y) / USEC_PER_MSEC);
141 if (access(flag_file, F_OK) < 0) {
146 k = poll(pollfd, notify > 0 ? 2 : 1, sleep_for);
158 if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
161 if (pollfd[POLL_TTY].revents == 0)
164 n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1);
166 if (errno == EINTR || errno == EAGAIN)
177 else if (c == 21) { /* C-u */
180 backspace_chars(ttyfd, p);
183 } else if (c == '\b' || c == 127) {
188 backspace_chars(ttyfd, 1);
191 } else if (!dirty && !silent_mode) {
195 /* There are two ways to enter silent
196 * mode. Either by pressing backspace
197 * as first key (and only as first key),
200 loop_write(ttyfd, "(no echo) ", 10, false);
202 } else if (ttyfd >= 0)
203 loop_write(ttyfd, "\a", 1, false);
205 } else if (c == '\t' && !silent_mode) {
207 backspace_chars(ttyfd, p);
210 /* ... or by pressing TAB at any time. */
213 loop_write(ttyfd, "(no echo) ", 10, false);
215 if (p >= sizeof(passphrase)-1) {
216 loop_write(ttyfd, "\a", 1, false);
222 if (!silent_mode && ttyfd >= 0)
223 loop_write(ttyfd, echo ? &c : "*", 1, false);
229 x = strndup(passphrase, p);
239 if (ttyfd >= 0 && reset_tty) {
240 loop_write(ttyfd, "\n", 1, false);
241 tcsetattr(ttyfd, TCSADRAIN, &old_termios);
247 static int create_socket(char **name) {
251 struct sockaddr_un un;
253 .un.sun_family = AF_UNIX,
261 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
263 return log_error_errno(errno, "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_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
277 if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
279 log_error_errno(errno, "SO_PASSCRED failed: %m");
283 c = strdup(sa.un.sun_path);
298 int ask_password_agent(
305 char ***_passphrases) {
313 char temp[] = "/run/systemd/ask-password/tmp.XXXXXX";
314 char final[sizeof(temp)] = "";
315 _cleanup_fclose_ FILE *f = NULL;
316 _cleanup_free_ char *socket_name = NULL;
317 _cleanup_close_ int socket_fd = -1, signal_fd = -1, fd = -1;
318 sigset_t mask, oldmask;
319 struct pollfd pollfd[_FD_MAX];
322 assert(_passphrases);
324 assert_se(sigemptyset(&mask) == 0);
325 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
326 assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0);
328 mkdir_p_label("/run/systemd/ask-password", 0755);
330 fd = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
332 log_error_errno(errno, "Failed to create password file: %m");
341 log_error_errno(errno, "Failed to allocate FILE: %m");
348 signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
350 log_error_errno(errno, "signalfd(): %m");
355 socket_fd = create_socket(&socket_name);
367 "NotAfter="USEC_FMT"\n",
370 accept_cached ? 1 : 0,
375 fprintf(f, "Message=%s\n", message);
378 fprintf(f, "Icon=%s\n", icon);
381 fprintf(f, "Id=%s\n", id);
386 log_error_errno(errno, "Failed to write query file: %m");
391 memcpy(final, temp, sizeof(temp));
393 final[sizeof(final)-11] = 'a';
394 final[sizeof(final)-10] = 's';
395 final[sizeof(final)-9] = 'k';
397 if (rename(temp, final) < 0) {
398 log_error_errno(errno, "Failed to rename query file: %m");
404 pollfd[FD_SOCKET].fd = socket_fd;
405 pollfd[FD_SOCKET].events = POLLIN;
406 pollfd[FD_SIGNAL].fd = signal_fd;
407 pollfd[FD_SIGNAL].events = POLLIN;
410 char passphrase[LINE_MAX+1];
411 struct msghdr msghdr;
415 struct cmsghdr cmsghdr;
416 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
422 t = now(CLOCK_MONOTONIC);
424 if (until > 0 && until <= t) {
425 log_notice("Timed out");
430 k = poll(pollfd, _FD_MAX, until > 0 ? (int) ((until-t)/USEC_PER_MSEC) : -1);
435 log_error_errno(errno, "poll() failed: %m");
441 log_notice("Timed out");
446 if (pollfd[FD_SIGNAL].revents & POLLIN) {
451 if (pollfd[FD_SOCKET].revents != POLLIN) {
452 log_error("Unexpected poll() event.");
458 iovec.iov_base = passphrase;
459 iovec.iov_len = sizeof(passphrase);
463 msghdr.msg_iov = &iovec;
464 msghdr.msg_iovlen = 1;
465 msghdr.msg_control = &control;
466 msghdr.msg_controllen = sizeof(control);
468 n = recvmsg(socket_fd, &msghdr, 0);
470 if (errno == EAGAIN ||
474 log_error_errno(errno, "recvmsg() failed: %m");
479 cmsg_close_all(&msghdr);
482 log_error("Message too short");
486 if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
487 control.cmsghdr.cmsg_level != SOL_SOCKET ||
488 control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
489 control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
490 log_warning("Received message without credentials. Ignoring.");
494 ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
495 if (ucred->uid != 0) {
496 log_warning("Got request from unprivileged user. Ignoring.");
500 if (passphrase[0] == '+') {
504 l = strv_new("", NULL);
506 l = strv_parse_nulstr(passphrase+1, n-1);
507 /* An empty message refers to the empty password */
514 if (strv_length(l) <= 0) {
516 log_error("Invalid packet");
522 } else if (passphrase[0] == '-') {
526 log_error("Invalid packet");
544 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
549 int ask_password_auto(const char *message, const char *icon, const char *id,
550 usec_t until, bool accept_cached, char ***_passphrases) {
552 assert(_passphrases);
554 if (isatty(STDIN_FILENO)) {
556 char *s = NULL, **l = NULL;
558 r = ask_password_tty(message, until, false, NULL, &s);
562 r = strv_consume(&l, s);
569 return ask_password_agent(message, icon, id, until, false, accept_cached, _passphrases);