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);
56 const char *flag_file,
59 struct termios old_termios, new_termios;
60 char passphrase[LINE_MAX], *x;
63 _cleanup_close_ int ttyfd = -1, notify = -1;
64 struct pollfd pollfd[2];
65 bool reset_tty = false;
66 bool silent_mode = false;
77 notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
83 if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
89 ttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC);
92 if (tcgetattr(ttyfd, &old_termios) < 0) {
97 loop_write(ttyfd, ANSI_HIGHLIGHT_ON, sizeof(ANSI_HIGHLIGHT_ON)-1, false);
98 loop_write(ttyfd, message, strlen(message), false);
99 loop_write(ttyfd, " ", 1, false);
100 loop_write(ttyfd, ANSI_HIGHLIGHT_OFF, sizeof(ANSI_HIGHLIGHT_OFF)-1, false);
102 new_termios = old_termios;
103 new_termios.c_lflag &= ~(ICANON|ECHO);
104 new_termios.c_cc[VMIN] = 1;
105 new_termios.c_cc[VTIME] = 0;
107 if (tcsetattr(ttyfd, TCSADRAIN, &new_termios) < 0) {
116 pollfd[POLL_TTY].fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO;
117 pollfd[POLL_TTY].events = POLLIN;
118 pollfd[POLL_INOTIFY].fd = notify;
119 pollfd[POLL_INOTIFY].events = POLLIN;
123 int sleep_for = -1, k;
129 y = now(CLOCK_MONOTONIC);
136 sleep_for = (int) ((until - y) / USEC_PER_MSEC);
140 if (access(flag_file, F_OK) < 0) {
145 k = poll(pollfd, notify > 0 ? 2 : 1, sleep_for);
157 if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
160 if (pollfd[POLL_TTY].revents == 0)
163 n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1);
165 if (errno == EINTR || errno == EAGAIN)
176 else if (c == 21) { /* C-u */
179 backspace_chars(ttyfd, p);
182 } else if (c == '\b' || c == 127) {
187 backspace_chars(ttyfd, 1);
190 } else if (!dirty && !silent_mode) {
194 /* There are two ways to enter silent
195 * mode. Either by pressing backspace
196 * as first key (and only as first key),
199 loop_write(ttyfd, "(no echo) ", 10, false);
201 } else if (ttyfd >= 0)
202 loop_write(ttyfd, "\a", 1, false);
204 } else if (c == '\t' && !silent_mode) {
206 backspace_chars(ttyfd, p);
209 /* ... or by pressing TAB at any time. */
212 loop_write(ttyfd, "(no echo) ", 10, false);
214 if (p >= sizeof(passphrase)-1) {
215 loop_write(ttyfd, "\a", 1, false);
221 if (!silent_mode && ttyfd >= 0)
222 loop_write(ttyfd, echo ? &c : "*", 1, false);
228 x = strndup(passphrase, p);
238 if (ttyfd >= 0 && reset_tty) {
239 loop_write(ttyfd, "\n", 1, false);
240 tcsetattr(ttyfd, TCSADRAIN, &old_termios);
246 static int create_socket(char **name) {
250 struct sockaddr_un un;
252 .un.sun_family = AF_UNIX,
260 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
262 return log_error_errno(errno, "socket() failed: %m");
264 snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%" PRIx64, random_u64());
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_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
276 if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
278 log_error_errno(errno, "SO_PASSCRED failed: %m");
282 c = strdup(sa.un.sun_path);
297 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_errno(errno, "Failed to create password file: %m");
340 log_error_errno(errno, "Failed to allocate FILE: %m");
347 signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
349 log_error_errno(errno, "signalfd(): %m");
354 socket_fd = create_socket(&socket_name);
366 "NotAfter="USEC_FMT"\n",
369 accept_cached ? 1 : 0,
374 fprintf(f, "Message=%s\n", message);
377 fprintf(f, "Icon=%s\n", icon);
380 fprintf(f, "Id=%s\n", id);
385 log_error_errno(errno, "Failed to write query file: %m");
390 memcpy(final, temp, sizeof(temp));
392 final[sizeof(final)-11] = 'a';
393 final[sizeof(final)-10] = 's';
394 final[sizeof(final)-9] = 'k';
396 if (rename(temp, final) < 0) {
397 log_error_errno(errno, "Failed to rename query file: %m");
403 pollfd[FD_SOCKET].fd = socket_fd;
404 pollfd[FD_SOCKET].events = POLLIN;
405 pollfd[FD_SIGNAL].fd = signal_fd;
406 pollfd[FD_SIGNAL].events = POLLIN;
409 char passphrase[LINE_MAX+1];
410 struct msghdr msghdr;
414 struct cmsghdr cmsghdr;
415 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
421 t = now(CLOCK_MONOTONIC);
423 if (until > 0 && until <= t) {
424 log_notice("Timed out");
429 k = poll(pollfd, _FD_MAX, until > 0 ? (int) ((until-t)/USEC_PER_MSEC) : -1);
434 log_error_errno(errno, "poll() failed: %m");
440 log_notice("Timed out");
445 if (pollfd[FD_SIGNAL].revents & POLLIN) {
450 if (pollfd[FD_SOCKET].revents != POLLIN) {
451 log_error("Unexpected poll() event.");
457 iovec.iov_base = passphrase;
458 iovec.iov_len = sizeof(passphrase);
462 msghdr.msg_iov = &iovec;
463 msghdr.msg_iovlen = 1;
464 msghdr.msg_control = &control;
465 msghdr.msg_controllen = sizeof(control);
467 n = recvmsg(socket_fd, &msghdr, 0);
469 if (errno == EAGAIN ||
473 log_error_errno(errno, "recvmsg() failed: %m");
479 log_error("Message too short");
483 if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
484 control.cmsghdr.cmsg_level != SOL_SOCKET ||
485 control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
486 control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
487 log_warning("Received message without credentials. Ignoring.");
491 ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
492 if (ucred->uid != 0) {
493 log_warning("Got request from unprivileged user. Ignoring.");
497 if (passphrase[0] == '+') {
501 l = strv_new("", NULL);
503 l = strv_parse_nulstr(passphrase+1, n-1);
504 /* An empty message refers to the empty password */
511 if (strv_length(l) <= 0) {
513 log_error("Invalid packet");
519 } else if (passphrase[0] == '-') {
523 log_error("Invalid packet");
541 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
546 int ask_password_auto(const char *message, const char *icon, const char *id,
547 usec_t until, bool accept_cached, char ***_passphrases) {
549 assert(_passphrases);
551 if (isatty(STDIN_FILENO)) {
553 char *s = NULL, **l = NULL;
555 r = ask_password_tty(message, until, false, NULL, &s);
559 r = strv_consume(&l, s);
566 return ask_password_agent(message, icon, id, until, false, accept_cached, _passphrases);