1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
33 #include <sys/inotify.h>
34 #include <sys/signalfd.h>
35 #include <sys/socket.h>
43 #include "alloc-util.h"
44 #include "ask-password-api.h"
47 #include "format-util.h"
53 #include "process-util.h"
54 #include "random-util.h"
55 #include "signal-util.h"
56 #include "socket-util.h"
57 #include "string-util.h"
59 #include "terminal-util.h"
60 #include "time-util.h"
61 #include "umask-util.h"
65 #define KEYRING_TIMEOUT_USEC ((5 * USEC_PER_MINUTE) / 2)
67 static int lookup_key(const char *keyname
, key_serial_t
*ret
) {
73 serial
= request_key("user", keyname
, NULL
, 0);
75 return negative_errno();
81 static int retrieve_key(key_serial_t serial
, char ***ret
) {
82 _cleanup_free_
char *p
= NULL
;
93 n
= keyctl(KEYCTL_READ
, (unsigned long) serial
, (unsigned long) p
, (unsigned long) m
, 0);
100 explicit_bzero(p
, n
);
105 l
= strv_parse_nulstr(p
, n
);
109 explicit_bzero(p
, n
);
115 static int add_to_keyring(const char *keyname
, AskPasswordFlags flags
, char **passwords
) {
116 _cleanup_strv_free_erase_
char **l
= NULL
;
117 _cleanup_free_
char *p
= NULL
;
125 if (!(flags
& ASK_PASSWORD_PUSH_CACHE
))
128 r
= lookup_key(keyname
, &serial
);
130 r
= retrieve_key(serial
, &l
);
133 } else if (r
!= -ENOKEY
)
136 r
= strv_extend_strv(&l
, passwords
, true);
140 r
= strv_make_nulstr(l
, &p
, &n
);
144 serial
= add_key("user", keyname
, p
, n
, KEY_SPEC_USER_KEYRING
);
145 explicit_bzero(p
, n
);
149 if (keyctl(KEYCTL_SET_TIMEOUT
,
150 (unsigned long) serial
,
151 (unsigned long) DIV_ROUND_UP(KEYRING_TIMEOUT_USEC
, USEC_PER_SEC
), 0, 0) < 0)
152 log_debug_errno(errno
, "Failed to adjust timeout: %m");
154 log_debug("Added key to keyring as %" PRIi32
".", serial
);
159 static int add_to_keyring_and_log(const char *keyname
, AskPasswordFlags flags
, char **passwords
) {
165 r
= add_to_keyring(keyname
, flags
, passwords
);
167 return log_debug_errno(r
, "Failed to add password to keyring: %m");
172 int ask_password_keyring(const char *keyname
, AskPasswordFlags flags
, char ***ret
) {
180 if (!(flags
& ASK_PASSWORD_ACCEPT_CACHED
))
183 r
= lookup_key(keyname
, &serial
);
184 if (r
== -ENOSYS
) /* when retrieving the distinction doesn't matter */
189 return retrieve_key(serial
, ret
);
192 static void backspace_chars(int ttyfd
, size_t p
) {
200 loop_write(ttyfd
, "\b \b", 3, false);
204 int ask_password_tty(
208 AskPasswordFlags flags
,
209 const char *flag_file
,
212 struct termios old_termios
, new_termios
;
213 char passphrase
[LINE_MAX
+ 1] = {}, *x
;
214 size_t p
= 0, codepoint
= 0;
216 _cleanup_close_
int ttyfd
= -1, notify
= -1;
217 struct pollfd pollfd
[2];
218 bool reset_tty
= false;
227 if (flags
& ASK_PASSWORD_NO_TTY
)
231 message
= "Password:";
234 notify
= inotify_init1(IN_CLOEXEC
|IN_NONBLOCK
);
240 if (inotify_add_watch(notify
, flag_file
, IN_ATTRIB
/* for the link count */) < 0) {
246 ttyfd
= open("/dev/tty", O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
249 if (tcgetattr(ttyfd
, &old_termios
) < 0) {
254 if (colors_enabled())
255 loop_write(ttyfd
, ANSI_HIGHLIGHT
, strlen(ANSI_HIGHLIGHT
), false);
256 loop_write(ttyfd
, message
, strlen(message
), false);
257 loop_write(ttyfd
, " ", 1, false);
258 if (colors_enabled())
259 loop_write(ttyfd
, ANSI_NORMAL
, strlen(ANSI_NORMAL
), false);
261 new_termios
= old_termios
;
262 new_termios
.c_lflag
&= ~(ICANON
|ECHO
);
263 new_termios
.c_cc
[VMIN
] = 1;
264 new_termios
.c_cc
[VTIME
] = 0;
266 if (tcsetattr(ttyfd
, TCSADRAIN
, &new_termios
) < 0) {
275 pollfd
[POLL_TTY
].fd
= ttyfd
>= 0 ? ttyfd
: STDIN_FILENO
;
276 pollfd
[POLL_TTY
].events
= POLLIN
;
277 pollfd
[POLL_INOTIFY
].fd
= notify
;
278 pollfd
[POLL_INOTIFY
].events
= POLLIN
;
282 int sleep_for
= -1, k
;
288 y
= now(CLOCK_MONOTONIC
);
295 sleep_for
= (int) ((until
- y
) / USEC_PER_MSEC
);
299 if (access(flag_file
, F_OK
) < 0) {
304 k
= poll(pollfd
, notify
>= 0 ? 2 : 1, sleep_for
);
316 if (notify
>= 0 && pollfd
[POLL_INOTIFY
].revents
!= 0)
319 if (pollfd
[POLL_TTY
].revents
== 0)
322 n
= read(ttyfd
>= 0 ? ttyfd
: STDIN_FILENO
, &c
, 1);
324 if (IN_SET(errno
, EINTR
, EAGAIN
))
335 else if (c
== 21) { /* C-u */
337 if (!(flags
& ASK_PASSWORD_SILENT
))
338 backspace_chars(ttyfd
, p
);
341 } else if (IN_SET(c
, '\b', 127)) {
345 if (!(flags
& ASK_PASSWORD_SILENT
))
346 backspace_chars(ttyfd
, 1);
349 } else if (!dirty
&& !(flags
& ASK_PASSWORD_SILENT
)) {
351 flags
|= ASK_PASSWORD_SILENT
;
353 /* There are two ways to enter silent
354 * mode. Either by pressing backspace
355 * as first key (and only as first
358 loop_write(ttyfd
, "(no echo) ", 10, false);
360 } else if (ttyfd
>= 0)
361 loop_write(ttyfd
, "\a", 1, false);
363 } else if (c
== '\t' && !(flags
& ASK_PASSWORD_SILENT
)) {
365 backspace_chars(ttyfd
, p
);
366 flags
|= ASK_PASSWORD_SILENT
;
368 /* ... or by pressing TAB at any time. */
371 loop_write(ttyfd
, "(no echo) ", 10, false);
373 if (p
>= sizeof(passphrase
)-1) {
374 loop_write(ttyfd
, "\a", 1, false);
380 if (!(flags
& ASK_PASSWORD_SILENT
) && ttyfd
>= 0) {
381 n
= utf8_encoded_valid_unichar(passphrase
+ codepoint
);
384 loop_write(ttyfd
, (flags
& ASK_PASSWORD_ECHO
) ? &c
: "*", 1, false);
394 x
= strndup(passphrase
, p
);
395 explicit_bzero(passphrase
, p
);
402 (void) add_to_keyring_and_log(keyname
, flags
, STRV_MAKE(x
));
408 if (ttyfd
>= 0 && reset_tty
) {
409 loop_write(ttyfd
, "\n", 1, false);
410 tcsetattr(ttyfd
, TCSADRAIN
, &old_termios
);
416 static int create_socket(char **name
) {
417 union sockaddr_union sa
= {
418 .un
.sun_family
= AF_UNIX
,
420 _cleanup_close_
int fd
= -1;
421 static const int one
= 1;
427 fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
431 snprintf(sa
.un
.sun_path
, sizeof(sa
.un
.sun_path
)-1, "/run/systemd/ask-password/sck.%" PRIx64
, random_u64());
433 RUN_WITH_UMASK(0177) {
434 if (bind(fd
, &sa
.sa
, SOCKADDR_UN_LEN(sa
.un
)) < 0)
438 if (setsockopt(fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
)) < 0)
441 c
= strdup(sa
.un
.sun_path
);
453 int ask_password_agent(
459 AskPasswordFlags flags
,
468 _cleanup_close_
int socket_fd
= -1, signal_fd
= -1, fd
= -1;
469 char temp
[] = "/run/systemd/ask-password/tmp.XXXXXX";
470 char final
[sizeof(temp
)] = "";
471 _cleanup_free_
char *socket_name
= NULL
;
472 _cleanup_strv_free_
char **l
= NULL
;
473 _cleanup_fclose_
FILE *f
= NULL
;
474 struct pollfd pollfd
[_FD_MAX
];
475 sigset_t mask
, oldmask
;
480 if (flags
& ASK_PASSWORD_NO_AGENT
)
483 assert_se(sigemptyset(&mask
) >= 0);
484 assert_se(sigset_add_many(&mask
, SIGINT
, SIGTERM
, -1) >= 0);
485 assert_se(sigprocmask(SIG_BLOCK
, &mask
, &oldmask
) >= 0);
487 (void) mkdir_p_label("/run/systemd/ask-password", 0755);
489 fd
= mkostemp_safe(temp
);
495 (void) fchmod(fd
, 0644);
505 signal_fd
= signalfd(-1, &mask
, SFD_NONBLOCK
|SFD_CLOEXEC
);
511 socket_fd
= create_socket(&socket_name
);
523 "NotAfter="USEC_FMT
"\n",
526 (flags
& ASK_PASSWORD_ACCEPT_CACHED
) ? 1 : 0,
527 (flags
& ASK_PASSWORD_ECHO
) ? 1 : 0,
531 fprintf(f
, "Message=%s\n", message
);
534 fprintf(f
, "Icon=%s\n", icon
);
537 fprintf(f
, "Id=%s\n", id
);
539 r
= fflush_and_check(f
);
543 memcpy(final
, temp
, sizeof(temp
));
545 final
[sizeof(final
)-11] = 'a';
546 final
[sizeof(final
)-10] = 's';
547 final
[sizeof(final
)-9] = 'k';
549 if (rename(temp
, final
) < 0) {
555 pollfd
[FD_SOCKET
].fd
= socket_fd
;
556 pollfd
[FD_SOCKET
].events
= POLLIN
;
557 pollfd
[FD_SIGNAL
].fd
= signal_fd
;
558 pollfd
[FD_SIGNAL
].events
= POLLIN
;
561 char passphrase
[LINE_MAX
+1];
562 struct msghdr msghdr
;
566 struct cmsghdr cmsghdr
;
567 uint8_t buf
[CMSG_SPACE(sizeof(struct ucred
))];
573 t
= now(CLOCK_MONOTONIC
);
575 if (until
> 0 && until
<= t
) {
580 k
= poll(pollfd
, _FD_MAX
, until
> 0 ? (int) ((until
-t
)/USEC_PER_MSEC
) : -1);
594 if (pollfd
[FD_SIGNAL
].revents
& POLLIN
) {
599 if (pollfd
[FD_SOCKET
].revents
!= POLLIN
) {
605 iovec
.iov_base
= passphrase
;
606 iovec
.iov_len
= sizeof(passphrase
);
610 msghdr
.msg_iov
= &iovec
;
611 msghdr
.msg_iovlen
= 1;
612 msghdr
.msg_control
= &control
;
613 msghdr
.msg_controllen
= sizeof(control
);
615 n
= recvmsg(socket_fd
, &msghdr
, 0);
617 if (IN_SET(errno
, EAGAIN
, EINTR
))
624 cmsg_close_all(&msghdr
);
627 log_debug("Message too short");
631 if (msghdr
.msg_controllen
< CMSG_LEN(sizeof(struct ucred
)) ||
632 control
.cmsghdr
.cmsg_level
!= SOL_SOCKET
||
633 control
.cmsghdr
.cmsg_type
!= SCM_CREDENTIALS
||
634 control
.cmsghdr
.cmsg_len
!= CMSG_LEN(sizeof(struct ucred
))) {
635 log_debug("Received message without credentials. Ignoring.");
639 ucred
= (struct ucred
*) CMSG_DATA(&control
.cmsghdr
);
640 if (ucred
->uid
!= 0) {
641 log_debug("Got request from unprivileged user. Ignoring.");
645 if (passphrase
[0] == '+') {
646 /* An empty message refers to the empty password */
648 l
= strv_new("", NULL
);
650 l
= strv_parse_nulstr(passphrase
+1, n
-1);
651 explicit_bzero(passphrase
, n
);
657 if (strv_length(l
) <= 0) {
659 log_debug("Invalid packet");
666 if (passphrase
[0] == '-') {
671 log_debug("Invalid packet");
675 (void) add_to_keyring_and_log(keyname
, flags
, l
);
683 (void) unlink(socket_name
);
688 (void) unlink(final
);
690 assert_se(sigprocmask(SIG_SETMASK
, &oldmask
, NULL
) == 0);
694 int ask_password_auto(
700 AskPasswordFlags flags
,
707 if ((flags
& ASK_PASSWORD_ACCEPT_CACHED
) && keyname
) {
708 r
= ask_password_keyring(keyname
, flags
, ret
);
713 if (!(flags
& ASK_PASSWORD_NO_TTY
) && isatty(STDIN_FILENO
)) {
714 char *s
= NULL
, **l
= NULL
;
716 r
= ask_password_tty(message
, keyname
, until
, flags
, NULL
, &s
);
720 r
= strv_push(&l
, s
);
731 if (!(flags
& ASK_PASSWORD_NO_AGENT
))
732 return ask_password_agent(message
, icon
, id
, keyname
, until
, flags
, ret
);