]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/shared/ask-password-api.c
man/systemd-sysext: list ephemeral/ephemeral-import in the list of options
[thirdparty/systemd.git] / src / shared / ask-password-api.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <fcntl.h>
4#include <poll.h>
5#include <stdio.h>
6#include <sys/inotify.h>
7#include <sys/signalfd.h>
8#include <sys/stat.h>
9#include <termios.h>
10#include <unistd.h>
11
12#include "alloc-util.h"
13#include "ansi-color.h"
14#include "ask-password-api.h"
15#include "creds-util.h"
16#include "fd-util.h"
17#include "fileio.h"
18#include "format-util.h"
19#include "fs-util.h"
20#include "glyph-util.h"
21#include "inotify-util.h"
22#include "io-util.h"
23#include "iovec-util.h"
24#include "keyring-util.h"
25#include "log.h"
26#include "missing_syscall.h"
27#include "nulstr-util.h"
28#include "parse-util.h"
29#include "path-lookup.h"
30#include "plymouth-util.h"
31#include "process-util.h"
32#include "random-util.h"
33#include "signal-util.h"
34#include "socket-util.h"
35#include "string-table.h"
36#include "string-util.h"
37#include "strv.h"
38#include "terminal-util.h"
39#include "time-util.h"
40#include "tmpfile-util.h"
41#include "umask-util.h"
42#include "utf8.h"
43
44#define KEYRING_TIMEOUT_USEC ((5 * USEC_PER_MINUTE) / 2)
45
46static const char* keyring_table[] = {
47 [-KEY_SPEC_THREAD_KEYRING] = "thread",
48 [-KEY_SPEC_PROCESS_KEYRING] = "process",
49 [-KEY_SPEC_SESSION_KEYRING] = "session",
50 [-KEY_SPEC_USER_KEYRING] = "user",
51 [-KEY_SPEC_USER_SESSION_KEYRING] = "user-session",
52 [-KEY_SPEC_GROUP_KEYRING] = "group",
53};
54
55DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(keyring, int);
56
57static int lookup_key(const char *keyname, key_serial_t *ret) {
58 key_serial_t serial;
59
60 assert(keyname);
61 assert(ret);
62
63 serial = request_key("user", keyname, /* callout_info= */ NULL, /* destringid= */ 0);
64 if (serial == -1)
65 return negative_errno();
66
67 *ret = serial;
68 return 0;
69}
70
71static int retrieve_key(key_serial_t serial, char ***ret) {
72 _cleanup_(erase_and_freep) void *p = NULL;
73 char **l;
74 size_t n;
75 int r;
76
77 assert(ret);
78
79 r = keyring_read(serial, &p, &n);
80 if (r < 0)
81 return r;
82
83 l = strv_parse_nulstr(p, n);
84 if (!l)
85 return -ENOMEM;
86
87 *ret = l;
88 return 0;
89}
90
91static int get_ask_password_directory_for_flags(AskPasswordFlags flags, char **ret) {
92 if (FLAGS_SET(flags, ASK_PASSWORD_USER))
93 return acquire_user_ask_password_directory(ret);
94
95 return strdup_to_full(ret, "/run/systemd/ask-password/"); /* Returns 1, indicating there's a suitable directory */
96}
97
98static int touch_ask_password_directory(AskPasswordFlags flags) {
99 int r;
100
101 _cleanup_free_ char *p = NULL;
102 r = get_ask_password_directory_for_flags(flags, &p);
103 if (r <= 0)
104 return r;
105
106 _cleanup_close_ int fd = open_mkdir(p, O_CLOEXEC, 0755);
107 if (fd < 0)
108 return fd;
109
110 r = touch_fd(fd, USEC_INFINITY);
111 if (r < 0)
112 return r;
113
114 return 1; /* did something */
115}
116
117static usec_t keyring_cache_timeout(void) {
118 static usec_t saved_timeout = KEYRING_TIMEOUT_USEC;
119 static bool saved_timeout_set = false;
120 int r;
121
122 if (saved_timeout_set)
123 return saved_timeout;
124
125 const char *e = secure_getenv("SYSTEMD_ASK_PASSWORD_KEYRING_TIMEOUT_SEC");
126 if (e) {
127 r = parse_sec(e, &saved_timeout);
128 if (r < 0)
129 log_debug_errno(r, "Invalid value in $SYSTEMD_ASK_PASSWORD_KEYRING_TIMEOUT_SEC, ignoring: %s", e);
130 }
131
132 saved_timeout_set = true;
133
134 return saved_timeout;
135}
136
137static key_serial_t keyring_cache_type(void) {
138 static key_serial_t saved_keyring = KEY_SPEC_USER_KEYRING;
139 static bool saved_keyring_set = false;
140 int r;
141
142 if (saved_keyring_set)
143 return saved_keyring;
144
145 const char *e = secure_getenv("SYSTEMD_ASK_PASSWORD_KEYRING_TYPE");
146 if (e) {
147 key_serial_t keyring;
148
149 r = safe_atoi32(e, &keyring);
150 if (r >= 0)
151 if (keyring < 0)
152 log_debug_errno(keyring, "Invalid value in $SYSTEMD_ASK_PASSWORD_KEYRING_TYPE, ignoring: %s", e);
153 else
154 saved_keyring = keyring;
155 else {
156 keyring = keyring_from_string(e);
157 if (keyring < 0)
158 log_debug_errno(keyring, "Invalid value in $SYSTEMD_ASK_PASSWORD_KEYRING_TYPE, ignoring: %s", e);
159 else
160 saved_keyring = -keyring;
161 }
162 }
163
164 saved_keyring_set = true;
165
166 return saved_keyring;
167}
168
169static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **passwords) {
170 _cleanup_strv_free_erase_ char **l = NULL;
171 _cleanup_(erase_and_freep) char *p = NULL;
172 key_serial_t serial;
173 size_t n;
174 int r;
175
176 assert(keyname);
177
178 if (!FLAGS_SET(flags, ASK_PASSWORD_PUSH_CACHE) || keyring_cache_timeout() == 0)
179 return 0;
180 if (strv_isempty(passwords))
181 return 0;
182
183 r = lookup_key(keyname, &serial);
184 if (r >= 0) {
185 r = retrieve_key(serial, &l);
186 if (r < 0)
187 return r;
188 } else if (r != -ENOKEY)
189 return r;
190
191 r = strv_extend_strv(&l, passwords, /* filter_duplicates= */ true);
192 if (r <= 0)
193 return r;
194
195 r = strv_make_nulstr(l, &p, &n);
196 if (r < 0)
197 return r;
198
199 /* chop off the final NUL byte. We do this because we want to use the separator NUL bytes only if we
200 * have multiple passwords. */
201 n = LESS_BY(n, (size_t) 1);
202
203 serial = add_key("user", keyname, p, n, keyring_cache_type());
204 if (serial == -1)
205 return -errno;
206
207 if (keyring_cache_timeout() != USEC_INFINITY &&
208 keyctl(KEYCTL_SET_TIMEOUT,
209 (unsigned long) serial,
210 (unsigned long) DIV_ROUND_UP(keyring_cache_timeout(), USEC_PER_SEC), 0, 0) < 0)
211 log_debug_errno(errno, "Failed to adjust kernel keyring key timeout: %m");
212
213 /* Tell everyone to check the keyring */
214 (void) touch_ask_password_directory(flags);
215
216 log_debug("Added key to kernel keyring as %" PRIi32 ".", serial);
217
218 return 1;
219}
220
221static int add_to_keyring_and_log(const char *keyname, AskPasswordFlags flags, char **passwords) {
222 int r;
223
224 assert(keyname);
225
226 r = add_to_keyring(keyname, flags, passwords);
227 if (r < 0)
228 return log_debug_errno(r, "Failed to add password to kernel keyring: %m");
229
230 return 0;
231}
232
233static int ask_password_keyring(const AskPasswordRequest *req, AskPasswordFlags flags, char ***ret) {
234 key_serial_t serial;
235 int r;
236
237 assert(req);
238 assert(ret);
239
240 if (!FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED))
241 return -EUNATCH;
242
243 r = lookup_key(req->keyring, &serial);
244 if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || r == -EPERM)
245 /* When retrieving, the distinction between "kernel or container manager don't support or
246 * allow this" and "no matching key known" doesn't matter. Note that we propagate EACCESS
247 * here (even if EPERM not) since that is used if the keyring is available, but we lack
248 * access to the key. */
249 return -ENOKEY;
250 if (r < 0)
251 return r;
252
253 _cleanup_strv_free_erase_ char **l = NULL;
254 r = retrieve_key(serial, &l);
255 if (r < 0)
256 return r;
257
258 if (strv_isempty(l))
259 return log_debug_errno(SYNTHETIC_ERRNO(ENOKEY), "Found an empty password from keyring.");
260
261 *ret = TAKE_PTR(l);
262 return 0;
263}
264
265static int backspace_chars(int ttyfd, size_t p) {
266 if (ttyfd < 0)
267 return 0;
268
269 _cleanup_free_ char *buf = malloc_multiply(3, p);
270 if (!buf)
271 return log_oom();
272
273 for (size_t i = 0; i < p; i++)
274 memcpy(buf + 3 * i, "\b \b", 3);
275
276 return loop_write(ttyfd, buf, 3 * p);
277}
278
279static int backspace_string(int ttyfd, const char *str) {
280 assert(str);
281
282 /* Backspaces through enough characters to entirely undo printing of the specified string. */
283
284 if (ttyfd < 0)
285 return 0;
286
287 size_t m = utf8_n_codepoints(str);
288 if (m == SIZE_MAX)
289 m = strlen(str); /* Not a valid UTF-8 string? If so, let's backspace the number of bytes
290 * output. Most likely this happened because we are not in a UTF-8 locale,
291 * and in that case that is the correct thing to do. And even if it's not,
292 * terminals tend to stop backspacing at the leftmost column, hence
293 * backspacing too much should be mostly OK. */
294
295 return backspace_chars(ttyfd, m);
296}
297
298int ask_password_plymouth(
299 const AskPasswordRequest *req,
300 AskPasswordFlags flags,
301 char ***ret) {
302
303 _cleanup_close_ int fd = -EBADF, inotify_fd = -EBADF;
304 _cleanup_free_ char *packet = NULL;
305 ssize_t k;
306 int r, n;
307 char buffer[LINE_MAX];
308 size_t p = 0;
309
310 assert(req);
311 assert(ret);
312
313 if (FLAGS_SET(flags, ASK_PASSWORD_HEADLESS))
314 return -ENOEXEC;
315
316 const char *message = req->message ?: "Password:";
317
318 if (req->flag_file) {
319 inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
320 if (inotify_fd < 0)
321 return -errno;
322
323 if (inotify_add_watch(inotify_fd, req->flag_file, IN_ATTRIB) < 0) /* for the link count */
324 return -errno;
325 }
326
327 fd = plymouth_connect(SOCK_NONBLOCK);
328 if (fd < 0)
329 return fd;
330
331 if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED)) {
332 packet = strdup("c");
333 n = 1;
334 } else if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
335 packet = NULL;
336 if (!packet)
337 return -ENOMEM;
338
339 r = loop_write_full(fd, packet, n + 1, USEC_INFINITY);
340 if (r < 0)
341 return r;
342
343 CLEANUP_ERASE(buffer);
344
345 enum {
346 POLL_SOCKET,
347 POLL_TWO,
348 POLL_THREE,
349 _POLL_MAX,
350 };
351
352 struct pollfd pollfd[_POLL_MAX] = {
353 [POLL_SOCKET] = {
354 .fd = fd,
355 .events = POLLIN,
356 },
357 };
358 size_t n_pollfd = POLL_SOCKET + 1, inotify_idx = SIZE_MAX, hup_fd_idx = SIZE_MAX;
359 if (inotify_fd >= 0)
360 pollfd[inotify_idx = n_pollfd++] = (struct pollfd) {
361 .fd = inotify_fd,
362 .events = POLLIN,
363 };
364 if (req->hup_fd >= 0)
365 pollfd[hup_fd_idx = n_pollfd++] = (struct pollfd) {
366 .fd = req->hup_fd,
367 .events = POLLHUP,
368 };
369
370 assert(n_pollfd <= _POLL_MAX);
371
372 for (;;) {
373 usec_t timeout;
374
375 if (req->until > 0)
376 timeout = usec_sub_unsigned(req->until, now(CLOCK_MONOTONIC));
377 else
378 timeout = USEC_INFINITY;
379
380 if (req->flag_file && access(req->flag_file, F_OK) < 0)
381 return -errno;
382
383 r = ppoll_usec(pollfd, n_pollfd, timeout);
384 if (r == -EINTR)
385 continue;
386 if (r < 0)
387 return r;
388 if (r == 0)
389 return -ETIME;
390
391 if (req->hup_fd >= 0 && pollfd[hup_fd_idx].revents & POLLHUP)
392 return -ECONNRESET;
393
394 if (inotify_fd >= 0 && pollfd[inotify_idx].revents != 0)
395 (void) flush_fd(inotify_fd);
396
397 if (pollfd[POLL_SOCKET].revents == 0)
398 continue;
399
400 k = read(fd, buffer + p, sizeof(buffer) - p);
401 if (k < 0) {
402 if (ERRNO_IS_TRANSIENT(errno))
403 continue;
404
405 return -errno;
406 }
407 if (k == 0)
408 return -EIO;
409
410 p += k;
411
412 if (buffer[0] == 5) {
413
414 if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED)) {
415 /* Hmm, first try with cached
416 * passwords failed, so let's retry
417 * with a normal password request */
418 packet = mfree(packet);
419
420 if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
421 return -ENOMEM;
422
423 r = loop_write_full(fd, packet, n + 1, USEC_INFINITY);
424 if (r < 0)
425 return r;
426
427 flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
428 p = 0;
429 continue;
430 }
431
432 /* No password, because UI not shown */
433 return -ENOENT;
434
435 } else if (IN_SET(buffer[0], 2, 9)) {
436 _cleanup_strv_free_erase_ char **l = NULL;
437 uint32_t size;
438
439 /* One or more answers */
440 if (p < 5)
441 continue;
442
443 memcpy(&size, buffer+1, sizeof(size));
444 size = le32toh(size);
445 if (size + 5 > sizeof(buffer))
446 return -EIO;
447
448 if (p-5 < size)
449 continue;
450
451 l = strv_parse_nulstr(buffer + 5, size);
452 if (!l)
453 return -ENOMEM;
454
455 if (strv_isempty(l))
456 return log_debug_errno(SYNTHETIC_ERRNO(ECANCELED), "Received an empty password.");
457
458 *ret = TAKE_PTR(l);
459 return 0;
460
461 } else
462 /* Unknown packet */
463 return -EIO;
464 }
465}
466
467#define NO_ECHO "(no echo) "
468#define PRESS_TAB "(press TAB for no echo) "
469#define SKIPPED "(skipped)"
470
471int ask_password_tty(
472 const AskPasswordRequest *req,
473 AskPasswordFlags flags,
474 char ***ret) {
475
476 bool reset_tty = false, dirty = false, use_color = false, press_tab_visible = false;
477 _cleanup_close_ int cttyfd = -EBADF, inotify_fd = -EBADF;
478 struct termios old_termios, new_termios;
479 char passphrase[LINE_MAX + 1] = {}, *x;
480 _cleanup_strv_free_erase_ char **l = NULL;
481 size_t p = 0, codepoint = 0;
482 int r;
483
484 assert(req);
485 assert(ret);
486
487 if (FLAGS_SET(flags, ASK_PASSWORD_HEADLESS))
488 return -ENOEXEC;
489
490 if (FLAGS_SET(flags, ASK_PASSWORD_NO_TTY))
491 return -EUNATCH;
492
493 const char *message = req->message ?: "Password:";
494 const char *keyring = req->keyring;
495
496 if (!FLAGS_SET(flags, ASK_PASSWORD_HIDE_EMOJI) && emoji_enabled())
497 message = strjoina(glyph(GLYPH_LOCK_AND_KEY), " ", message);
498
499 if (req->flag_file || (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && keyring)) {
500 inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
501 if (inotify_fd < 0)
502 return -errno;
503 }
504 if (req->flag_file)
505 if (inotify_add_watch(inotify_fd, req->flag_file, IN_ATTRIB /* for the link count */) < 0)
506 return -errno;
507 if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && keyring) {
508 r = ask_password_keyring(req, flags, ret);
509 if (r >= 0)
510 return 0;
511 if (r != -ENOKEY)
512 return r;
513
514 /* Let's watch the askpw directory for mtime changes, which we issue above whenever the
515 * keyring changes */
516 _cleanup_free_ char *watch_path = NULL;
517 r = get_ask_password_directory_for_flags(flags, &watch_path);
518 if (r < 0)
519 return r;
520 if (r > 0) {
521 _cleanup_close_ int watch_fd = open_mkdir(watch_path, O_CLOEXEC|O_RDONLY, 0755);
522 if (watch_fd < 0)
523 return watch_fd;
524
525 r = inotify_add_watch_fd(inotify_fd, watch_fd, IN_ONLYDIR|IN_ATTRIB /* for mtime */);
526 if (r < 0)
527 return r;
528 }
529 }
530
531 CLEANUP_ERASE(passphrase);
532
533 /* If the caller didn't specify a TTY, then use the controlling tty, if we can. */
534 int ttyfd;
535 if (req->tty_fd < 0)
536 ttyfd = cttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC);
537 else
538 ttyfd = req->tty_fd;
539
540 if (ttyfd >= 0) {
541 if (tcgetattr(ttyfd, &old_termios) < 0)
542 return -errno;
543
544 if (FLAGS_SET(flags, ASK_PASSWORD_CONSOLE_COLOR))
545 use_color = dev_console_colors_enabled();
546 else
547 use_color = colors_enabled();
548
549 if (use_color)
550 (void) loop_write(ttyfd, ANSI_HIGHLIGHT, SIZE_MAX);
551
552 (void) loop_write(ttyfd, message, SIZE_MAX);
553 (void) loop_write(ttyfd, " ", 1);
554
555 if (!FLAGS_SET(flags, ASK_PASSWORD_SILENT) && !FLAGS_SET(flags, ASK_PASSWORD_ECHO)) {
556 if (use_color)
557 (void) loop_write(ttyfd, ansi_grey(), SIZE_MAX);
558
559 (void) loop_write(ttyfd, PRESS_TAB, SIZE_MAX);
560 press_tab_visible = true;
561 }
562
563 if (use_color)
564 (void) loop_write(ttyfd, ANSI_NORMAL, SIZE_MAX);
565
566 new_termios = old_termios;
567 termios_disable_echo(&new_termios);
568
569 r = RET_NERRNO(tcsetattr(ttyfd, TCSANOW, &new_termios));
570 if (r < 0)
571 goto finish;
572
573 reset_tty = true;
574 }
575
576 enum {
577 POLL_TTY,
578 POLL_TWO,
579 POLL_THREE,
580 _POLL_MAX,
581 };
582
583 struct pollfd pollfd[_POLL_MAX] = {
584 [POLL_TTY] = {
585 .fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO,
586 .events = POLLIN,
587 },
588 };
589 size_t n_pollfd = POLL_TTY + 1, inotify_idx = SIZE_MAX, hup_fd_idx = SIZE_MAX;
590
591 if (inotify_fd >= 0)
592 pollfd[inotify_idx = n_pollfd++] = (struct pollfd) {
593 .fd = inotify_fd,
594 .events = POLLIN,
595 };
596 if (req->hup_fd >= 0)
597 pollfd[hup_fd_idx = n_pollfd++] = (struct pollfd) {
598 .fd = req->hup_fd,
599 .events = POLLHUP,
600 };
601
602 assert(n_pollfd <= _POLL_MAX);
603
604 for (;;) {
605 _cleanup_(erase_char) char c;
606 usec_t timeout;
607 ssize_t n;
608
609 if (req->until > 0)
610 timeout = usec_sub_unsigned(req->until, now(CLOCK_MONOTONIC));
611 else
612 timeout = USEC_INFINITY;
613
614 if (req->flag_file) {
615 r = RET_NERRNO(access(req->flag_file, F_OK));
616 if (r < 0)
617 goto finish;
618 }
619
620 r = ppoll_usec(pollfd, n_pollfd, timeout);
621 if (r == -EINTR)
622 continue;
623 if (r < 0)
624 goto finish;
625 if (r == 0) {
626 r = -ETIME;
627 goto finish;
628 }
629
630 if (req->hup_fd >= 0 && pollfd[hup_fd_idx].revents & POLLHUP) {
631 r = -ECONNRESET;
632 goto finish;
633 }
634
635 if (inotify_fd >= 0 && pollfd[inotify_idx].revents != 0 && keyring) {
636 (void) flush_fd(inotify_fd);
637
638 r = ask_password_keyring(req, flags, ret);
639 if (r >= 0) {
640 r = 0;
641 goto finish;
642 } else if (r != -ENOKEY)
643 goto finish;
644 }
645
646 if (pollfd[POLL_TTY].revents == 0)
647 continue;
648
649 n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1);
650 if (n < 0) {
651 if (ERRNO_IS_TRANSIENT(errno))
652 continue;
653
654 r = -errno;
655 goto finish;
656
657 }
658
659 if (press_tab_visible) {
660 assert(ttyfd >= 0);
661 backspace_chars(ttyfd, strlen(PRESS_TAB));
662 press_tab_visible = false;
663 }
664
665 /* We treat EOF, newline and NUL byte all as valid end markers */
666 if (n == 0 || c == '\n' || c == 0)
667 break;
668
669 if (c == 4) { /* C-d also known as EOT */
670 if (ttyfd >= 0)
671 (void) loop_write(ttyfd, SKIPPED, SIZE_MAX);
672
673 goto skipped;
674 }
675
676 if (c == 21) { /* C-u */
677
678 if (!FLAGS_SET(flags, ASK_PASSWORD_SILENT))
679 (void) backspace_string(ttyfd, passphrase);
680
681 explicit_bzero_safe(passphrase, sizeof(passphrase));
682 p = codepoint = 0;
683
684 } else if (IN_SET(c, '\b', 127)) {
685
686 if (p > 0) {
687 size_t q;
688
689 if (!FLAGS_SET(flags, ASK_PASSWORD_SILENT))
690 (void) backspace_chars(ttyfd, 1);
691
692 /* Remove a full UTF-8 codepoint from the end. For that, figure out where the
693 * last one begins */
694 q = 0;
695 for (;;) {
696 int z;
697
698 z = utf8_encoded_valid_unichar(passphrase + q, SIZE_MAX);
699 if (z <= 0) {
700 q = SIZE_MAX; /* Invalid UTF8! */
701 break;
702 }
703
704 if (q + z >= p) /* This one brings us over the edge */
705 break;
706
707 q += z;
708 }
709
710 p = codepoint = q == SIZE_MAX ? p - 1 : q;
711 explicit_bzero_safe(passphrase + p, sizeof(passphrase) - p);
712
713 } else if (!dirty && !FLAGS_SET(flags, ASK_PASSWORD_SILENT)) {
714
715 flags |= ASK_PASSWORD_SILENT;
716
717 /* There are two ways to enter silent mode. Either by pressing backspace as
718 * first key (and only as first key), or ... */
719
720 if (ttyfd >= 0)
721 (void) loop_write(ttyfd, NO_ECHO, SIZE_MAX);
722
723 } else if (ttyfd >= 0)
724 (void) loop_write(ttyfd, "\a", 1);
725
726 } else if (c == '\t' && !FLAGS_SET(flags, ASK_PASSWORD_SILENT)) {
727
728 (void) backspace_string(ttyfd, passphrase);
729 flags |= ASK_PASSWORD_SILENT;
730
731 /* ... or by pressing TAB at any time. */
732
733 if (ttyfd >= 0)
734 (void) loop_write(ttyfd, NO_ECHO, SIZE_MAX);
735
736 } else if (char_is_cc(c) || p >= sizeof(passphrase)-1) {
737 /* Don't accept control chars or overly long passphrases */
738 if (ttyfd >= 0)
739 (void) loop_write(ttyfd, "\a", 1);
740
741 } else {
742 passphrase[p++] = c;
743
744 if (!FLAGS_SET(flags, ASK_PASSWORD_SILENT) && ttyfd >= 0) {
745 /* Check if we got a complete UTF-8 character now. If so, let's output one '*'. */
746 n = utf8_encoded_valid_unichar(passphrase + codepoint, SIZE_MAX);
747 if (n >= 0) {
748 if (FLAGS_SET(flags, ASK_PASSWORD_ECHO))
749 (void) loop_write(ttyfd, passphrase + codepoint, n);
750 else
751 (void) loop_write(ttyfd,
752 glyph(GLYPH_BULLET),
753 SIZE_MAX);
754 codepoint = p;
755 }
756 }
757
758 dirty = true;
759 }
760 }
761
762 x = strndup(passphrase, p);
763 if (!x) {
764 r = -ENOMEM;
765 goto finish;
766 }
767
768 r = strv_consume(&l, x);
769 if (r < 0)
770 goto finish;
771
772skipped:
773 if (strv_isempty(l))
774 r = log_debug_errno(SYNTHETIC_ERRNO(ECANCELED), "Password query was cancelled.");
775 else {
776 if (keyring)
777 (void) add_to_keyring_and_log(keyring, flags, l);
778
779 *ret = TAKE_PTR(l);
780 r = 0;
781 }
782
783finish:
784 if (ttyfd >= 0 && reset_tty) {
785 (void) loop_write(ttyfd, "\n", 1);
786 (void) tcsetattr(ttyfd, TCSANOW, &old_termios);
787 }
788
789 return r;
790}
791
792static int create_socket(const char *askpwdir, char **ret) {
793 _cleanup_free_ char *path = NULL;
794 union sockaddr_union sa;
795 socklen_t sa_len;
796 _cleanup_close_ int fd = -EBADF;
797 int r;
798
799 assert(askpwdir);
800 assert(ret);
801
802 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
803 if (fd < 0)
804 return -errno;
805
806 if (asprintf(&path, "%s/sck.%" PRIx64, askpwdir, random_u64()) < 0)
807 return -ENOMEM;
808
809 r = sockaddr_un_set_path(&sa.un, path);
810 if (r < 0)
811 return r;
812 sa_len = r;
813
814 WITH_UMASK(0177)
815 if (bind(fd, &sa.sa, sa_len) < 0)
816 return -errno;
817
818 r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true);
819 if (r < 0)
820 return r;
821
822 (void) setsockopt_int(fd, SOL_SOCKET, SO_PASSRIGHTS, false);
823
824 *ret = TAKE_PTR(path);
825 return TAKE_FD(fd);
826}
827
828int ask_password_agent(
829 const AskPasswordRequest *req,
830 AskPasswordFlags flags,
831 char ***ret) {
832
833 _cleanup_close_ int socket_fd = -EBADF, signal_fd = -EBADF, inotify_fd = -EBADF, dfd = -EBADF;
834 _cleanup_(unlink_and_freep) char *socket_name = NULL;
835 _cleanup_free_ char *temp = NULL, *final = NULL;
836 _cleanup_strv_free_erase_ char **l = NULL;
837 _cleanup_fclose_ FILE *f = NULL;
838 sigset_t mask, oldmask;
839 int r;
840
841 assert(req);
842 assert(ret);
843
844 if (FLAGS_SET(flags, ASK_PASSWORD_HEADLESS))
845 return -ENOEXEC;
846
847 if (FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT))
848 return -EUNATCH;
849
850 /* We don't support the flag file concept for now when querying via the agent logic */
851 if (req->flag_file)
852 return -EOPNOTSUPP;
853
854 assert_se(sigemptyset(&mask) >= 0);
855 assert_se(sigset_add_many(&mask, SIGINT, SIGTERM) >= 0);
856 assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) >= 0);
857
858 _cleanup_free_ char *askpwdir = NULL;
859 r = get_ask_password_directory_for_flags(flags, &askpwdir);
860 if (r < 0)
861 goto finish;
862 if (r == 0) {
863 r = -ENXIO;
864 goto finish;
865 }
866
867 dfd = open_mkdir(askpwdir, O_RDONLY|O_CLOEXEC, 0755);
868 if (dfd < 0) {
869 r = log_debug_errno(dfd, "Failed to open directory '%s': %m", askpwdir);
870 goto finish;
871 }
872
873 if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && req->keyring) {
874 r = ask_password_keyring(req, flags, ret);
875 if (r >= 0) {
876 r = 0;
877 goto finish;
878 } else if (r != -ENOKEY)
879 goto finish;
880
881 inotify_fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
882 if (inotify_fd < 0) {
883 r = -errno;
884 goto finish;
885 }
886
887 r = inotify_add_watch_fd(inotify_fd, dfd, IN_ONLYDIR|IN_ATTRIB /* for mtime */);
888 if (r < 0)
889 goto finish;
890 }
891
892 if (asprintf(&final, "ask.%" PRIu64, random_u64()) < 0) {
893 r = -ENOMEM;
894 goto finish;
895 }
896
897 r = fopen_temporary_at(dfd, final, &f, &temp);
898 if (r < 0)
899 goto finish;
900
901 signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
902 if (signal_fd < 0) {
903 r = -errno;
904 goto finish;
905 }
906
907 socket_fd = create_socket(askpwdir, &socket_name);
908 if (socket_fd < 0) {
909 r = socket_fd;
910 goto finish;
911 }
912
913 fprintf(f,
914 "[Ask]\n"
915 "PID="PID_FMT"\n"
916 "Socket=%s\n"
917 "AcceptCached=%i\n"
918 "Echo=%i\n"
919 "NotAfter="USEC_FMT"\n"
920 "Silent=%i\n",
921 getpid_cached(),
922 socket_name,
923 FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED),
924 FLAGS_SET(flags, ASK_PASSWORD_ECHO),
925 req->until,
926 FLAGS_SET(flags, ASK_PASSWORD_SILENT));
927
928 if (req->message)
929 fprintf(f, "Message=%s\n", req->message);
930
931 if (req->icon)
932 fprintf(f, "Icon=%s\n", req->icon);
933
934 if (req->id)
935 fprintf(f, "Id=%s\n", req->id);
936
937 if (fchmod(fileno(f), 0644) < 0) {
938 r = -errno;
939 goto finish;
940 }
941
942 r = fflush_and_check(f);
943 if (r < 0)
944 goto finish;
945
946 if (renameat(dfd, temp, dfd, final) < 0) {
947 r = -errno;
948 goto finish;
949 }
950
951 temp = mfree(temp);
952
953 enum {
954 POLL_SOCKET,
955 POLL_SIGNAL,
956 POLL_THREE,
957 POLL_FOUR,
958 _POLL_MAX
959 };
960
961 struct pollfd pollfd[_POLL_MAX] = {
962 [POLL_SOCKET] = { .fd = socket_fd, .events = POLLIN },
963 [POLL_SIGNAL] = { .fd = signal_fd, .events = POLLIN },
964 };
965 size_t n_pollfd = POLL_SIGNAL + 1, inotify_idx = SIZE_MAX, hup_fd_idx = SIZE_MAX;
966
967 if (inotify_fd >= 0)
968 pollfd[inotify_idx = n_pollfd++] = (struct pollfd) {
969 .fd = inotify_fd,
970 .events = POLLIN,
971 };
972 if (req->hup_fd >= 0)
973 pollfd[hup_fd_idx = n_pollfd ++] = (struct pollfd) {
974 .fd = req->hup_fd,
975 .events = POLLHUP,
976 };
977
978 assert(n_pollfd <= _POLL_MAX);
979
980 for (;;) {
981 CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
982 char passphrase[LINE_MAX+1];
983 struct iovec iovec;
984 struct ucred *ucred;
985 usec_t timeout;
986 ssize_t n;
987
988 if (req->until > 0)
989 timeout = usec_sub_unsigned(req->until, now(CLOCK_MONOTONIC));
990 else
991 timeout = USEC_INFINITY;
992
993 r = ppoll_usec(pollfd, n_pollfd, timeout);
994 if (r == -EINTR)
995 continue;
996 if (r < 0)
997 goto finish;
998 if (r == 0) {
999 r = -ETIME;
1000 goto finish;
1001 }
1002
1003 if (pollfd[POLL_SIGNAL].revents & POLLIN) {
1004 r = -EINTR;
1005 goto finish;
1006 }
1007
1008 if (req->hup_fd >= 0 && pollfd[hup_fd_idx].revents & POLLHUP)
1009 return -ECONNRESET;
1010
1011 if (inotify_fd >= 0 && pollfd[inotify_idx].revents != 0) {
1012 (void) flush_fd(inotify_fd);
1013
1014 if (req->keyring) {
1015 r = ask_password_keyring(req, flags, ret);
1016 if (r >= 0) {
1017 r = 0;
1018 goto finish;
1019 } else if (r != -ENOKEY)
1020 goto finish;
1021 }
1022 }
1023
1024 if (pollfd[POLL_SOCKET].revents == 0)
1025 continue;
1026
1027 if (pollfd[POLL_SOCKET].revents != POLLIN) {
1028 r = -EIO;
1029 goto finish;
1030 }
1031
1032 iovec = IOVEC_MAKE(passphrase, sizeof(passphrase));
1033
1034 struct msghdr msghdr = {
1035 .msg_iov = &iovec,
1036 .msg_iovlen = 1,
1037 .msg_control = &control,
1038 .msg_controllen = sizeof(control),
1039 };
1040
1041 n = recvmsg_safe(socket_fd, &msghdr, 0);
1042 if (ERRNO_IS_NEG_TRANSIENT(n))
1043 continue;
1044 if (n == -ECHRNG) {
1045 log_debug_errno(n, "Got message with truncated control data (unexpected fds sent?), ignoring.");
1046 continue;
1047 }
1048 if (n == -EXFULL) {
1049 log_debug_errno(n, "Got message with truncated payload data, ignoring.");
1050 continue;
1051 }
1052 if (n < 0) {
1053 r = (int) n;
1054 goto finish;
1055 }
1056
1057 CLEANUP_ERASE(passphrase);
1058
1059 cmsg_close_all(&msghdr);
1060
1061 if (n == 0) {
1062 log_debug("Message too short");
1063 continue;
1064 }
1065
1066 ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred);
1067 if (!ucred) {
1068 log_debug("Received message without credentials. Ignoring.");
1069 continue;
1070 }
1071
1072 if (ucred->uid != getuid() && ucred->uid != 0) {
1073 log_debug("Got response from bad user. Ignoring.");
1074 continue;
1075 }
1076
1077 if (passphrase[0] == '+') {
1078 /* An empty message refers to the empty password */
1079 if (n == 1)
1080 l = strv_new("");
1081 else
1082 l = strv_parse_nulstr(passphrase+1, n-1);
1083 if (!l) {
1084 r = -ENOMEM;
1085 goto finish;
1086 }
1087
1088 if (strv_isempty(l)) {
1089 l = strv_free(l);
1090 log_debug("Invalid packet");
1091 continue;
1092 }
1093
1094 break;
1095 }
1096
1097 if (passphrase[0] == '-') {
1098 r = -ECANCELED;
1099 goto finish;
1100 }
1101
1102 log_debug("Invalid packet");
1103 }
1104
1105 if (req->keyring)
1106 (void) add_to_keyring_and_log(req->keyring, flags, l);
1107
1108 *ret = TAKE_PTR(l);
1109 r = 0;
1110
1111finish:
1112 if (temp) {
1113 assert(dfd >= 0);
1114 (void) unlinkat(dfd, temp, 0);
1115 } else if (final) {
1116 assert(dfd >= 0);
1117 (void) unlinkat(dfd, final, 0);
1118 }
1119
1120 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
1121 return r;
1122}
1123
1124static int ask_password_credential(const AskPasswordRequest *req, AskPasswordFlags flags, char ***ret) {
1125 _cleanup_(erase_and_freep) char *buffer = NULL;
1126 _cleanup_strv_free_erase_ char **l = NULL;
1127 size_t size;
1128 int r;
1129
1130 assert(req);
1131 assert(req->credential);
1132 assert(ret);
1133
1134 r = read_credential(req->credential, (void**) &buffer, &size);
1135 if (IN_SET(r, -ENXIO, -ENOENT)) /* No credentials passed or this credential not defined? */
1136 return -ENOKEY;
1137
1138 l = strv_parse_nulstr(buffer, size);
1139 if (!l)
1140 return -ENOMEM;
1141
1142 if (strv_isempty(l))
1143 return log_debug_errno(SYNTHETIC_ERRNO(ENOKEY), "Found an empty password in credential.");
1144
1145 *ret = TAKE_PTR(l);
1146 return 0;
1147}
1148
1149int ask_password_auto(
1150 const AskPasswordRequest *req,
1151 AskPasswordFlags flags,
1152 char ***ret) {
1153
1154 int r;
1155
1156 assert(req);
1157 assert(ret);
1158
1159 /* Returns the following well-known errors:
1160 *
1161 * -ETIME → a timeout was specified and hit
1162 * -EUNATCH → couldn't ask interactively and no cached password available either
1163 * -ENOENT → the specified flag file disappeared
1164 * -ECANCELED → the user explicitly cancelled the request
1165 * -EINTR → SIGINT/SIGTERM where received during the query
1166 * -ENOEXEC → headless mode was requested but no password could be acquired non-interactively
1167 * -ECONNRESET → a POLLHUP has been seen on the specified hup_fd
1168 */
1169
1170 if (!FLAGS_SET(flags, ASK_PASSWORD_NO_CREDENTIAL) && req->credential) {
1171 r = ask_password_credential(req, flags, ret);
1172 if (r != -ENOKEY)
1173 return r;
1174 }
1175
1176 if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) &&
1177 req->keyring &&
1178 (FLAGS_SET(flags, ASK_PASSWORD_NO_TTY) || !isatty_safe(STDIN_FILENO)) &&
1179 FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT)) {
1180 r = ask_password_keyring(req, flags, ret);
1181 if (r != -ENOKEY)
1182 return r;
1183 }
1184
1185 if (!FLAGS_SET(flags, ASK_PASSWORD_NO_TTY) && isatty_safe(STDIN_FILENO))
1186 return ask_password_tty(req, flags, ret);
1187
1188 if (!FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT))
1189 return ask_password_agent(req, flags, ret);
1190
1191 return -EUNATCH;
1192}
1193
1194int acquire_user_ask_password_directory(char **ret) {
1195 int r;
1196
1197 r = xdg_user_runtime_dir("systemd/ask-password", ret);
1198 if (r == -ENXIO) {
1199 if (ret)
1200 *ret = NULL;
1201 return 0;
1202 }
1203 if (r < 0)
1204 return r;
1205
1206 return 1;
1207}