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 if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
259 log_error("socket() failed: %m");
264 sa.un.sun_family = AF_UNIX;
265 snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%llu", random_ull());
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 if (!(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];
320 assert(_passphrases);
322 assert_se(sigemptyset(&mask) == 0);
323 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
324 assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0);
326 mkdir_p_label("/run/systemd/ask-password", 0755);
329 fd = mkostemp(temp, O_CLOEXEC|O_CREAT|O_WRONLY);
333 log_error("Failed to create password file: %m");
340 if (!(f = fdopen(fd, "w"))) {
341 log_error("Failed to allocate FILE: %m");
348 if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
349 log_error("signalfd(): %m");
354 if ((socket_fd = create_socket(&socket_name)) < 0) {
365 (unsigned long) getpid(),
367 accept_cached ? 1 : 0,
368 (unsigned long long) until);
371 fprintf(f, "Message=%s\n", message);
374 fprintf(f, "Icon=%s\n", icon);
379 log_error("Failed to write query file: %m");
384 memcpy(final, temp, sizeof(temp));
386 final[sizeof(final)-11] = 'a';
387 final[sizeof(final)-10] = 's';
388 final[sizeof(final)-9] = 'k';
390 if (rename(temp, final) < 0) {
391 log_error("Failed to rename query file: %m");
397 pollfd[FD_SOCKET].fd = socket_fd;
398 pollfd[FD_SOCKET].events = POLLIN;
399 pollfd[FD_SIGNAL].fd = signal_fd;
400 pollfd[FD_SIGNAL].events = POLLIN;
403 char passphrase[LINE_MAX+1];
404 struct msghdr msghdr;
408 struct cmsghdr cmsghdr;
409 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
415 t = now(CLOCK_MONOTONIC);
417 if (until > 0 && until <= t) {
418 log_notice("Timed out");
423 if ((k = poll(pollfd, _FD_MAX, until > 0 ? (int) ((until-t)/USEC_PER_MSEC) : -1)) < 0) {
428 log_error("poll() failed: %m");
434 log_notice("Timed out");
439 if (pollfd[FD_SIGNAL].revents & POLLIN) {
444 if (pollfd[FD_SOCKET].revents != POLLIN) {
445 log_error("Unexpected poll() event.");
451 iovec.iov_base = passphrase;
452 iovec.iov_len = sizeof(passphrase);
456 msghdr.msg_iov = &iovec;
457 msghdr.msg_iovlen = 1;
458 msghdr.msg_control = &control;
459 msghdr.msg_controllen = sizeof(control);
461 if ((n = recvmsg(socket_fd, &msghdr, 0)) < 0) {
463 if (errno == EAGAIN ||
467 log_error("recvmsg() failed: %m");
473 log_error("Message too short");
477 if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
478 control.cmsghdr.cmsg_level != SOL_SOCKET ||
479 control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
480 control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
481 log_warning("Received message without credentials. Ignoring.");
485 ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
486 if (ucred->uid != 0) {
487 log_warning("Got request from unprivileged user. Ignoring.");
491 if (passphrase[0] == '+') {
495 l = strv_new("", NULL);
497 l = strv_parse_nulstr(passphrase+1, n-1);
498 /* An empty message refers to the empty password */
505 if (strv_length(l) <= 0) {
507 log_error("Invalid packet");
513 } else if (passphrase[0] == '-') {
517 log_error("Invalid packet");
528 close_nointr_nofail(fd);
536 close_nointr_nofail(socket_fd);
539 close_nointr_nofail(signal_fd);
549 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
554 int ask_password_auto(const char *message, const char *icon, usec_t until, bool accept_cached, char ***_passphrases) {
556 assert(_passphrases);
558 if (isatty(STDIN_FILENO)) {
560 char *s = NULL, **l = NULL;
562 if ((r = ask_password_tty(message, until, NULL, &s)) < 0)
565 l = strv_new(s, NULL);
575 return ask_password_agent(message, icon, until, accept_cached, _passphrases);