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/>.
28 #include <sys/inotify.h>
29 #include <sys/signalfd.h>
30 #include <sys/socket.h>
35 #include "ask-password-api.h"
37 #include "formats-util.h"
41 #include "random-util.h"
42 #include "signal-util.h"
43 #include "socket-util.h"
44 #include "string-util.h"
46 #include "terminal-util.h"
49 #define KEYRING_TIMEOUT_USEC ((5 * USEC_PER_MINUTE) / 2)
51 static int lookup_key(const char *keyname
, key_serial_t
*ret
) {
57 serial
= request_key("user", keyname
, NULL
, 0);
65 static int retrieve_key(key_serial_t serial
, char ***ret
) {
66 _cleanup_free_
char *p
= NULL
;
77 n
= keyctl(KEYCTL_READ
, (unsigned long) serial
, (unsigned long) p
, (unsigned long) m
, 0);
89 l
= strv_parse_nulstr(p
, n
);
99 static int add_to_keyring(const char *keyname
, AskPasswordFlags flags
, char **passwords
) {
100 _cleanup_strv_free_erase_
char **l
= NULL
;
101 _cleanup_free_
char *p
= NULL
;
109 if (!(flags
& ASK_PASSWORD_PUSH_CACHE
))
112 r
= lookup_key(keyname
, &serial
);
114 r
= retrieve_key(serial
, &l
);
117 } else if (r
!= -ENOKEY
)
120 r
= strv_extend_strv(&l
, passwords
, true);
124 r
= strv_make_nulstr(l
, &p
, &n
);
128 /* Truncate trailing NUL */
132 serial
= add_key("user", keyname
, p
, n
-1, KEY_SPEC_USER_KEYRING
);
137 if (keyctl(KEYCTL_SET_TIMEOUT
,
138 (unsigned long) serial
,
139 (unsigned long) DIV_ROUND_UP(KEYRING_TIMEOUT_USEC
, USEC_PER_SEC
), 0, 0) < 0)
140 log_debug_errno(errno
, "Failed to adjust timeout: %m");
142 log_debug("Added key to keyring as %" PRIi32
".", serial
);
147 static int add_to_keyring_and_log(const char *keyname
, AskPasswordFlags flags
, char **passwords
) {
153 r
= add_to_keyring(keyname
, flags
, passwords
);
155 return log_debug_errno(r
, "Failed to add password to keyring: %m");
160 int ask_password_keyring(const char *keyname
, AskPasswordFlags flags
, char ***ret
) {
168 if (!(flags
& ASK_PASSWORD_ACCEPT_CACHED
))
171 r
= lookup_key(keyname
, &serial
);
172 if (r
== -ENOSYS
) /* when retrieving the distinction doesn't matter */
177 return retrieve_key(serial
, ret
);
180 static void backspace_chars(int ttyfd
, size_t p
) {
188 loop_write(ttyfd
, "\b \b", 3, false);
192 int ask_password_tty(
196 AskPasswordFlags flags
,
197 const char *flag_file
,
200 struct termios old_termios
, new_termios
;
201 char passphrase
[LINE_MAX
], *x
;
204 _cleanup_close_
int ttyfd
= -1, notify
= -1;
205 struct pollfd pollfd
[2];
206 bool reset_tty
= false;
215 if (flags
& ASK_PASSWORD_NO_TTY
)
219 message
= "Password:";
222 notify
= inotify_init1(IN_CLOEXEC
|IN_NONBLOCK
);
228 if (inotify_add_watch(notify
, flag_file
, IN_ATTRIB
/* for the link count */) < 0) {
234 ttyfd
= open("/dev/tty", O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
237 if (tcgetattr(ttyfd
, &old_termios
) < 0) {
242 loop_write(ttyfd
, ANSI_HIGHLIGHT
, strlen(ANSI_HIGHLIGHT
), false);
243 loop_write(ttyfd
, message
, strlen(message
), false);
244 loop_write(ttyfd
, " ", 1, false);
245 loop_write(ttyfd
, ANSI_NORMAL
, strlen(ANSI_NORMAL
), false);
247 new_termios
= old_termios
;
248 new_termios
.c_lflag
&= ~(ICANON
|ECHO
);
249 new_termios
.c_cc
[VMIN
] = 1;
250 new_termios
.c_cc
[VTIME
] = 0;
252 if (tcsetattr(ttyfd
, TCSADRAIN
, &new_termios
) < 0) {
261 pollfd
[POLL_TTY
].fd
= ttyfd
>= 0 ? ttyfd
: STDIN_FILENO
;
262 pollfd
[POLL_TTY
].events
= POLLIN
;
263 pollfd
[POLL_INOTIFY
].fd
= notify
;
264 pollfd
[POLL_INOTIFY
].events
= POLLIN
;
268 int sleep_for
= -1, k
;
274 y
= now(CLOCK_MONOTONIC
);
281 sleep_for
= (int) ((until
- y
) / USEC_PER_MSEC
);
285 if (access(flag_file
, F_OK
) < 0) {
290 k
= poll(pollfd
, notify
>= 0 ? 2 : 1, sleep_for
);
302 if (notify
>= 0 && pollfd
[POLL_INOTIFY
].revents
!= 0)
305 if (pollfd
[POLL_TTY
].revents
== 0)
308 n
= read(ttyfd
>= 0 ? ttyfd
: STDIN_FILENO
, &c
, 1);
310 if (errno
== EINTR
|| errno
== EAGAIN
)
321 else if (c
== 21) { /* C-u */
323 if (!(flags
& ASK_PASSWORD_SILENT
))
324 backspace_chars(ttyfd
, p
);
327 } else if (c
== '\b' || c
== 127) {
331 if (!(flags
& ASK_PASSWORD_SILENT
))
332 backspace_chars(ttyfd
, 1);
335 } else if (!dirty
&& !(flags
& ASK_PASSWORD_SILENT
)) {
337 flags
|= ASK_PASSWORD_SILENT
;
339 /* There are two ways to enter silent
340 * mode. Either by pressing backspace
341 * as first key (and only as first
344 loop_write(ttyfd
, "(no echo) ", 10, false);
346 } else if (ttyfd
>= 0)
347 loop_write(ttyfd
, "\a", 1, false);
349 } else if (c
== '\t' && !(flags
& ASK_PASSWORD_SILENT
)) {
351 backspace_chars(ttyfd
, p
);
352 flags
|= ASK_PASSWORD_SILENT
;
354 /* ... or by pressing TAB at any time. */
357 loop_write(ttyfd
, "(no echo) ", 10, false);
359 if (p
>= sizeof(passphrase
)-1) {
360 loop_write(ttyfd
, "\a", 1, false);
366 if (!(flags
& ASK_PASSWORD_SILENT
) && ttyfd
>= 0)
367 loop_write(ttyfd
, (flags
& ASK_PASSWORD_ECHO
) ? &c
: "*", 1, false);
375 x
= strndup(passphrase
, p
);
376 memory_erase(passphrase
, p
);
383 (void) add_to_keyring_and_log(keyname
, flags
, STRV_MAKE(x
));
389 if (ttyfd
>= 0 && reset_tty
) {
390 loop_write(ttyfd
, "\n", 1, false);
391 tcsetattr(ttyfd
, TCSADRAIN
, &old_termios
);
397 static int create_socket(char **name
) {
398 union sockaddr_union sa
= {
399 .un
.sun_family
= AF_UNIX
,
401 _cleanup_close_
int fd
= -1;
402 static const int one
= 1;
408 fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
412 snprintf(sa
.un
.sun_path
, sizeof(sa
.un
.sun_path
)-1, "/run/systemd/ask-password/sck.%" PRIx64
, random_u64());
414 RUN_WITH_UMASK(0177) {
415 if (bind(fd
, &sa
.sa
, offsetof(struct sockaddr_un
, sun_path
) + strlen(sa
.un
.sun_path
)) < 0)
419 if (setsockopt(fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
)) < 0)
422 c
= strdup(sa
.un
.sun_path
);
434 int ask_password_agent(
440 AskPasswordFlags flags
,
449 _cleanup_close_
int socket_fd
= -1, signal_fd
= -1, fd
= -1;
450 char temp
[] = "/run/systemd/ask-password/tmp.XXXXXX";
451 char final
[sizeof(temp
)] = "";
452 _cleanup_free_
char *socket_name
= NULL
;
453 _cleanup_strv_free_
char **l
= NULL
;
454 _cleanup_fclose_
FILE *f
= NULL
;
455 struct pollfd pollfd
[_FD_MAX
];
456 sigset_t mask
, oldmask
;
461 if (flags
& ASK_PASSWORD_NO_AGENT
)
464 assert_se(sigemptyset(&mask
) >= 0);
465 assert_se(sigset_add_many(&mask
, SIGINT
, SIGTERM
, -1) >= 0);
466 assert_se(sigprocmask(SIG_BLOCK
, &mask
, &oldmask
) >= 0);
468 (void) mkdir_p_label("/run/systemd/ask-password", 0755);
470 fd
= mkostemp_safe(temp
, O_WRONLY
|O_CLOEXEC
);
476 (void) fchmod(fd
, 0644);
486 signal_fd
= signalfd(-1, &mask
, SFD_NONBLOCK
|SFD_CLOEXEC
);
492 socket_fd
= create_socket(&socket_name
);
504 "NotAfter="USEC_FMT
"\n",
507 (flags
& ASK_PASSWORD_ACCEPT_CACHED
) ? 1 : 0,
508 (flags
& ASK_PASSWORD_ECHO
) ? 1 : 0,
512 fprintf(f
, "Message=%s\n", message
);
515 fprintf(f
, "Icon=%s\n", icon
);
518 fprintf(f
, "Id=%s\n", id
);
520 r
= fflush_and_check(f
);
524 memcpy(final
, temp
, sizeof(temp
));
526 final
[sizeof(final
)-11] = 'a';
527 final
[sizeof(final
)-10] = 's';
528 final
[sizeof(final
)-9] = 'k';
530 if (rename(temp
, final
) < 0) {
536 pollfd
[FD_SOCKET
].fd
= socket_fd
;
537 pollfd
[FD_SOCKET
].events
= POLLIN
;
538 pollfd
[FD_SIGNAL
].fd
= signal_fd
;
539 pollfd
[FD_SIGNAL
].events
= POLLIN
;
542 char passphrase
[LINE_MAX
+1];
543 struct msghdr msghdr
;
547 struct cmsghdr cmsghdr
;
548 uint8_t buf
[CMSG_SPACE(sizeof(struct ucred
))];
554 t
= now(CLOCK_MONOTONIC
);
556 if (until
> 0 && until
<= t
) {
561 k
= poll(pollfd
, _FD_MAX
, until
> 0 ? (int) ((until
-t
)/USEC_PER_MSEC
) : -1);
575 if (pollfd
[FD_SIGNAL
].revents
& POLLIN
) {
580 if (pollfd
[FD_SOCKET
].revents
!= POLLIN
) {
586 iovec
.iov_base
= passphrase
;
587 iovec
.iov_len
= sizeof(passphrase
);
591 msghdr
.msg_iov
= &iovec
;
592 msghdr
.msg_iovlen
= 1;
593 msghdr
.msg_control
= &control
;
594 msghdr
.msg_controllen
= sizeof(control
);
596 n
= recvmsg(socket_fd
, &msghdr
, 0);
598 if (errno
== EAGAIN
||
606 cmsg_close_all(&msghdr
);
609 log_debug("Message too short");
613 if (msghdr
.msg_controllen
< CMSG_LEN(sizeof(struct ucred
)) ||
614 control
.cmsghdr
.cmsg_level
!= SOL_SOCKET
||
615 control
.cmsghdr
.cmsg_type
!= SCM_CREDENTIALS
||
616 control
.cmsghdr
.cmsg_len
!= CMSG_LEN(sizeof(struct ucred
))) {
617 log_debug("Received message without credentials. Ignoring.");
621 ucred
= (struct ucred
*) CMSG_DATA(&control
.cmsghdr
);
622 if (ucred
->uid
!= 0) {
623 log_debug("Got request from unprivileged user. Ignoring.");
627 if (passphrase
[0] == '+') {
628 /* An empty message refers to the empty password */
630 l
= strv_new("", NULL
);
632 l
= strv_parse_nulstr(passphrase
+1, n
-1);
633 memory_erase(passphrase
, n
);
639 if (strv_length(l
) <= 0) {
641 log_debug("Invalid packet");
648 if (passphrase
[0] == '-') {
653 log_debug("Invalid packet");
657 (void) add_to_keyring_and_log(keyname
, flags
, l
);
665 (void) unlink(socket_name
);
670 (void) unlink(final
);
672 assert_se(sigprocmask(SIG_SETMASK
, &oldmask
, NULL
) == 0);
676 int ask_password_auto(
682 AskPasswordFlags flags
,
689 if ((flags
& ASK_PASSWORD_ACCEPT_CACHED
) && keyname
) {
690 r
= ask_password_keyring(keyname
, flags
, ret
);
695 if (!(flags
& ASK_PASSWORD_NO_TTY
) && isatty(STDIN_FILENO
)) {
696 char *s
= NULL
, **l
= NULL
;
698 r
= ask_password_tty(message
, keyname
, until
, flags
, NULL
, &s
);
702 r
= strv_push(&l
, s
);
713 if (!(flags
& ASK_PASSWORD_NO_AGENT
))
714 return ask_password_agent(message
, icon
, id
, keyname
, until
, flags
, ret
);