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 "formats-util.h"
38 #include "random-util.h"
39 #include "signal-util.h"
40 #include "socket-util.h"
41 #include "string-util.h"
43 #include "terminal-util.h"
45 #include "ask-password-api.h"
47 #define KEYRING_TIMEOUT_USEC ((5 * USEC_PER_MINUTE) / 2)
49 static int lookup_key(const char *keyname
, key_serial_t
*ret
) {
55 serial
= request_key("user", keyname
, NULL
, 0);
63 static int retrieve_key(key_serial_t serial
, char ***ret
) {
64 _cleanup_free_
char *p
= NULL
;
75 n
= keyctl(KEYCTL_READ
, (unsigned long) serial
, (unsigned long) p
, (unsigned long) m
, 0);
87 l
= strv_parse_nulstr(p
, n
);
97 static int add_to_keyring(const char *keyname
, AskPasswordFlags flags
, char **passwords
) {
98 _cleanup_strv_free_erase_
char **l
= NULL
;
99 _cleanup_free_
char *p
= NULL
;
107 if (!(flags
& ASK_PASSWORD_PUSH_CACHE
))
110 r
= lookup_key(keyname
, &serial
);
112 r
= retrieve_key(serial
, &l
);
115 } else if (r
!= -ENOKEY
)
118 r
= strv_extend_strv(&l
, passwords
, true);
122 r
= strv_make_nulstr(l
, &p
, &n
);
126 /* Truncate trailing NUL */
130 serial
= add_key("user", keyname
, p
, n
-1, KEY_SPEC_USER_KEYRING
);
135 if (keyctl(KEYCTL_SET_TIMEOUT
,
136 (unsigned long) serial
,
137 (unsigned long) DIV_ROUND_UP(KEYRING_TIMEOUT_USEC
, USEC_PER_SEC
), 0, 0) < 0)
138 log_debug_errno(errno
, "Failed to adjust timeout: %m");
140 log_debug("Added key to keyring as %" PRIi32
".", serial
);
145 static int add_to_keyring_and_log(const char *keyname
, AskPasswordFlags flags
, char **passwords
) {
151 r
= add_to_keyring(keyname
, flags
, passwords
);
153 return log_debug_errno(r
, "Failed to add password to keyring: %m");
158 int ask_password_keyring(const char *keyname
, AskPasswordFlags flags
, char ***ret
) {
166 if (!(flags
& ASK_PASSWORD_ACCEPT_CACHED
))
169 r
= lookup_key(keyname
, &serial
);
170 if (r
== -ENOSYS
) /* when retrieving the distinction doesn't matter */
175 return retrieve_key(serial
, ret
);
178 static void backspace_chars(int ttyfd
, size_t p
) {
186 loop_write(ttyfd
, "\b \b", 3, false);
190 int ask_password_tty(
194 AskPasswordFlags flags
,
195 const char *flag_file
,
198 struct termios old_termios
, new_termios
;
199 char passphrase
[LINE_MAX
], *x
;
202 _cleanup_close_
int ttyfd
= -1, notify
= -1;
203 struct pollfd pollfd
[2];
204 bool reset_tty
= false;
213 if (flags
& ASK_PASSWORD_NO_TTY
)
217 message
= "Password:";
220 notify
= inotify_init1(IN_CLOEXEC
|IN_NONBLOCK
);
226 if (inotify_add_watch(notify
, flag_file
, IN_ATTRIB
/* for the link count */) < 0) {
232 ttyfd
= open("/dev/tty", O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
235 if (tcgetattr(ttyfd
, &old_termios
) < 0) {
240 loop_write(ttyfd
, ANSI_HIGHLIGHT
, strlen(ANSI_HIGHLIGHT
), false);
241 loop_write(ttyfd
, message
, strlen(message
), false);
242 loop_write(ttyfd
, " ", 1, false);
243 loop_write(ttyfd
, ANSI_NORMAL
, strlen(ANSI_NORMAL
), false);
245 new_termios
= old_termios
;
246 new_termios
.c_lflag
&= ~(ICANON
|ECHO
);
247 new_termios
.c_cc
[VMIN
] = 1;
248 new_termios
.c_cc
[VTIME
] = 0;
250 if (tcsetattr(ttyfd
, TCSADRAIN
, &new_termios
) < 0) {
259 pollfd
[POLL_TTY
].fd
= ttyfd
>= 0 ? ttyfd
: STDIN_FILENO
;
260 pollfd
[POLL_TTY
].events
= POLLIN
;
261 pollfd
[POLL_INOTIFY
].fd
= notify
;
262 pollfd
[POLL_INOTIFY
].events
= POLLIN
;
266 int sleep_for
= -1, k
;
272 y
= now(CLOCK_MONOTONIC
);
279 sleep_for
= (int) ((until
- y
) / USEC_PER_MSEC
);
283 if (access(flag_file
, F_OK
) < 0) {
288 k
= poll(pollfd
, notify
>= 0 ? 2 : 1, sleep_for
);
300 if (notify
>= 0 && pollfd
[POLL_INOTIFY
].revents
!= 0)
303 if (pollfd
[POLL_TTY
].revents
== 0)
306 n
= read(ttyfd
>= 0 ? ttyfd
: STDIN_FILENO
, &c
, 1);
308 if (errno
== EINTR
|| errno
== EAGAIN
)
319 else if (c
== 21) { /* C-u */
321 if (!(flags
& ASK_PASSWORD_SILENT
))
322 backspace_chars(ttyfd
, p
);
325 } else if (c
== '\b' || c
== 127) {
329 if (!(flags
& ASK_PASSWORD_SILENT
))
330 backspace_chars(ttyfd
, 1);
333 } else if (!dirty
&& !(flags
& ASK_PASSWORD_SILENT
)) {
335 flags
|= ASK_PASSWORD_SILENT
;
337 /* There are two ways to enter silent
338 * mode. Either by pressing backspace
339 * as first key (and only as first
342 loop_write(ttyfd
, "(no echo) ", 10, false);
344 } else if (ttyfd
>= 0)
345 loop_write(ttyfd
, "\a", 1, false);
347 } else if (c
== '\t' && !(flags
& ASK_PASSWORD_SILENT
)) {
349 backspace_chars(ttyfd
, p
);
350 flags
|= ASK_PASSWORD_SILENT
;
352 /* ... or by pressing TAB at any time. */
355 loop_write(ttyfd
, "(no echo) ", 10, false);
357 if (p
>= sizeof(passphrase
)-1) {
358 loop_write(ttyfd
, "\a", 1, false);
364 if (!(flags
& ASK_PASSWORD_SILENT
) && ttyfd
>= 0)
365 loop_write(ttyfd
, (flags
& ASK_PASSWORD_ECHO
) ? &c
: "*", 1, false);
373 x
= strndup(passphrase
, p
);
374 memory_erase(passphrase
, p
);
381 (void) add_to_keyring_and_log(keyname
, flags
, STRV_MAKE(x
));
387 if (ttyfd
>= 0 && reset_tty
) {
388 loop_write(ttyfd
, "\n", 1, false);
389 tcsetattr(ttyfd
, TCSADRAIN
, &old_termios
);
395 static int create_socket(char **name
) {
396 union sockaddr_union sa
= {
397 .un
.sun_family
= AF_UNIX
,
399 _cleanup_close_
int fd
= -1;
400 static const int one
= 1;
406 fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
410 snprintf(sa
.un
.sun_path
, sizeof(sa
.un
.sun_path
)-1, "/run/systemd/ask-password/sck.%" PRIx64
, random_u64());
412 RUN_WITH_UMASK(0177) {
413 if (bind(fd
, &sa
.sa
, offsetof(struct sockaddr_un
, sun_path
) + strlen(sa
.un
.sun_path
)) < 0)
417 if (setsockopt(fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
)) < 0)
420 c
= strdup(sa
.un
.sun_path
);
432 int ask_password_agent(
438 AskPasswordFlags flags
,
447 _cleanup_close_
int socket_fd
= -1, signal_fd
= -1, fd
= -1;
448 char temp
[] = "/run/systemd/ask-password/tmp.XXXXXX";
449 char final
[sizeof(temp
)] = "";
450 _cleanup_free_
char *socket_name
= NULL
;
451 _cleanup_strv_free_
char **l
= NULL
;
452 _cleanup_fclose_
FILE *f
= NULL
;
453 struct pollfd pollfd
[_FD_MAX
];
454 sigset_t mask
, oldmask
;
459 if (flags
& ASK_PASSWORD_NO_AGENT
)
462 assert_se(sigemptyset(&mask
) >= 0);
463 assert_se(sigset_add_many(&mask
, SIGINT
, SIGTERM
, -1) >= 0);
464 assert_se(sigprocmask(SIG_BLOCK
, &mask
, &oldmask
) >= 0);
466 (void) mkdir_p_label("/run/systemd/ask-password", 0755);
468 fd
= mkostemp_safe(temp
, O_WRONLY
|O_CLOEXEC
);
474 (void) fchmod(fd
, 0644);
484 signal_fd
= signalfd(-1, &mask
, SFD_NONBLOCK
|SFD_CLOEXEC
);
490 socket_fd
= create_socket(&socket_name
);
502 "NotAfter="USEC_FMT
"\n",
505 (flags
& ASK_PASSWORD_ACCEPT_CACHED
) ? 1 : 0,
506 (flags
& ASK_PASSWORD_ECHO
) ? 1 : 0,
510 fprintf(f
, "Message=%s\n", message
);
513 fprintf(f
, "Icon=%s\n", icon
);
516 fprintf(f
, "Id=%s\n", id
);
518 r
= fflush_and_check(f
);
522 memcpy(final
, temp
, sizeof(temp
));
524 final
[sizeof(final
)-11] = 'a';
525 final
[sizeof(final
)-10] = 's';
526 final
[sizeof(final
)-9] = 'k';
528 if (rename(temp
, final
) < 0) {
534 pollfd
[FD_SOCKET
].fd
= socket_fd
;
535 pollfd
[FD_SOCKET
].events
= POLLIN
;
536 pollfd
[FD_SIGNAL
].fd
= signal_fd
;
537 pollfd
[FD_SIGNAL
].events
= POLLIN
;
540 char passphrase
[LINE_MAX
+1];
541 struct msghdr msghdr
;
545 struct cmsghdr cmsghdr
;
546 uint8_t buf
[CMSG_SPACE(sizeof(struct ucred
))];
552 t
= now(CLOCK_MONOTONIC
);
554 if (until
> 0 && until
<= t
) {
559 k
= poll(pollfd
, _FD_MAX
, until
> 0 ? (int) ((until
-t
)/USEC_PER_MSEC
) : -1);
573 if (pollfd
[FD_SIGNAL
].revents
& POLLIN
) {
578 if (pollfd
[FD_SOCKET
].revents
!= POLLIN
) {
584 iovec
.iov_base
= passphrase
;
585 iovec
.iov_len
= sizeof(passphrase
);
589 msghdr
.msg_iov
= &iovec
;
590 msghdr
.msg_iovlen
= 1;
591 msghdr
.msg_control
= &control
;
592 msghdr
.msg_controllen
= sizeof(control
);
594 n
= recvmsg(socket_fd
, &msghdr
, 0);
596 if (errno
== EAGAIN
||
604 cmsg_close_all(&msghdr
);
607 log_debug("Message too short");
611 if (msghdr
.msg_controllen
< CMSG_LEN(sizeof(struct ucred
)) ||
612 control
.cmsghdr
.cmsg_level
!= SOL_SOCKET
||
613 control
.cmsghdr
.cmsg_type
!= SCM_CREDENTIALS
||
614 control
.cmsghdr
.cmsg_len
!= CMSG_LEN(sizeof(struct ucred
))) {
615 log_debug("Received message without credentials. Ignoring.");
619 ucred
= (struct ucred
*) CMSG_DATA(&control
.cmsghdr
);
620 if (ucred
->uid
!= 0) {
621 log_debug("Got request from unprivileged user. Ignoring.");
625 if (passphrase
[0] == '+') {
626 /* An empty message refers to the empty password */
628 l
= strv_new("", NULL
);
630 l
= strv_parse_nulstr(passphrase
+1, n
-1);
631 memory_erase(passphrase
, n
);
637 if (strv_length(l
) <= 0) {
639 log_debug("Invalid packet");
646 if (passphrase
[0] == '-') {
651 log_debug("Invalid packet");
655 (void) add_to_keyring_and_log(keyname
, flags
, l
);
663 (void) unlink(socket_name
);
668 (void) unlink(final
);
670 assert_se(sigprocmask(SIG_SETMASK
, &oldmask
, NULL
) == 0);
674 int ask_password_auto(
680 AskPasswordFlags flags
,
687 if ((flags
& ASK_PASSWORD_ACCEPT_CACHED
) && keyname
) {
688 r
= ask_password_keyring(keyname
, flags
, ret
);
693 if (!(flags
& ASK_PASSWORD_NO_TTY
) && isatty(STDIN_FILENO
)) {
694 char *s
= NULL
, **l
= NULL
;
696 r
= ask_password_tty(message
, keyname
, until
, flags
, NULL
, &s
);
700 r
= strv_push(&l
, s
);
711 if (!(flags
& ASK_PASSWORD_NO_AGENT
))
712 return ask_password_agent(message
, icon
, id
, keyname
, until
, flags
, ret
);