]> git.ipfire.org Git - thirdparty/systemd.git/blame - 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
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
00843602 2
7f4e0805 3#include <fcntl.h>
69a283c5 4#include <poll.h>
a8fbdf54 5#include <stdio.h>
00843602 6#include <sys/inotify.h>
7f4e0805 7#include <sys/signalfd.h>
a8fbdf54 8#include <sys/stat.h>
00843602
LP
9#include <termios.h>
10#include <unistd.h>
7f4e0805 11
b5efdb8a 12#include "alloc-util.h"
b7120388 13#include "ansi-color.h"
3ffd4af2 14#include "ask-password-api.h"
8806bb4b 15#include "creds-util.h"
3ffd4af2 16#include "fd-util.h"
0d39fa9c 17#include "fileio.h"
f97b34a6 18#include "format-util.h"
c7b7d74e 19#include "fs-util.h"
d8e32c47 20#include "glyph-util.h"
4dd2748b 21#include "inotify-util.h"
c004493c 22#include "io-util.h"
bd1ae178 23#include "iovec-util.h"
cbae575e 24#include "keyring-util.h"
a8fbdf54 25#include "log.h"
f5947a5e 26#include "missing_syscall.h"
08af3cc5 27#include "nulstr-util.h"
c94f6ab1 28#include "parse-util.h"
ec572753 29#include "path-lookup.h"
aa25e19b 30#include "plymouth-util.h"
df0ff127 31#include "process-util.h"
3df3e884 32#include "random-util.h"
24882e06 33#include "signal-util.h"
00843602 34#include "socket-util.h"
36c6c696 35#include "string-table.h"
07630cea 36#include "string-util.h"
00843602
LP
37#include "strv.h"
38#include "terminal-util.h"
a8fbdf54 39#include "time-util.h"
e4de7287 40#include "tmpfile-util.h"
affb60b1 41#include "umask-util.h"
f3149d57 42#include "utf8.h"
7f4e0805 43
e287086b
LP
44#define KEYRING_TIMEOUT_USEC ((5 * USEC_PER_MINUTE) / 2)
45
36c6c696
DDM
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
e287086b
LP
57static int lookup_key(const char *keyname, key_serial_t *ret) {
58 key_serial_t serial;
59
60 assert(keyname);
61 assert(ret);
62
d7e986b7 63 serial = request_key("user", keyname, /* callout_info= */ NULL, /* destringid= */ 0);
e287086b 64 if (serial == -1)
9c4615fb 65 return negative_errno();
e287086b
LP
66
67 *ret = serial;
68 return 0;
69}
70
71static int retrieve_key(key_serial_t serial, char ***ret) {
cbae575e 72 _cleanup_(erase_and_freep) void *p = NULL;
e287086b 73 char **l;
cbae575e
LP
74 size_t n;
75 int r;
e287086b
LP
76
77 assert(ret);
78
cbae575e
LP
79 r = keyring_read(serial, &p, &n);
80 if (r < 0)
81 return r;
82
83 l = strv_parse_nulstr(p, n);
e287086b
LP
84 if (!l)
85 return -ENOMEM;
86
87 *ret = l;
88 return 0;
89}
90
4dd2748b
LP
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
2ee6fa55
LP
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);
4dd2748b
LP
111 if (r < 0)
112 return r;
113
114 return 1; /* did something */
115}
116
d9f4dad9 117static usec_t keyring_cache_timeout(void) {
b3bca11c 118 static usec_t saved_timeout = KEYRING_TIMEOUT_USEC;
d9f4dad9
DDM
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");
01d138b9 126 if (e) {
d9f4dad9
DDM
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
36c6c696
DDM
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
e287086b 169static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **passwords) {
ab84f5b9 170 _cleanup_strv_free_erase_ char **l = NULL;
e693a932 171 _cleanup_(erase_and_freep) char *p = NULL;
e287086b
LP
172 key_serial_t serial;
173 size_t n;
174 int r;
175
176 assert(keyname);
e287086b 177
d9f4dad9 178 if (!FLAGS_SET(flags, ASK_PASSWORD_PUSH_CACHE) || keyring_cache_timeout() == 0)
e287086b 179 return 0;
e013e10d
LP
180 if (strv_isempty(passwords))
181 return 0;
e287086b
LP
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
dbdec4b1 191 r = strv_extend_strv(&l, passwords, /* filter_duplicates= */ true);
e287086b
LP
192 if (r <= 0)
193 return r;
194
195 r = strv_make_nulstr(l, &p, &n);
196 if (r < 0)
197 return r;
198
76078ad8
LP
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
36c6c696 203 serial = add_key("user", keyname, p, n, keyring_cache_type());
e287086b
LP
204 if (serial == -1)
205 return -errno;
206
d9f4dad9
DDM
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)
e3ac53a2 211 log_debug_errno(errno, "Failed to adjust kernel keyring key timeout: %m");
e287086b 212
c7b7d74e 213 /* Tell everyone to check the keyring */
4dd2748b 214 (void) touch_ask_password_directory(flags);
c7b7d74e 215
e3ac53a2 216 log_debug("Added key to kernel keyring as %" PRIi32 ".", serial);
e287086b
LP
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);
e287086b
LP
225
226 r = add_to_keyring(keyname, flags, passwords);
227 if (r < 0)
e3ac53a2 228 return log_debug_errno(r, "Failed to add password to kernel keyring: %m");
e287086b
LP
229
230 return 0;
231}
232
d08fd4c3 233static int ask_password_keyring(const AskPasswordRequest *req, AskPasswordFlags flags, char ***ret) {
e287086b
LP
234 key_serial_t serial;
235 int r;
236
d08fd4c3 237 assert(req);
e287086b
LP
238 assert(ret);
239
9cb5bf91 240 if (!FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED))
e287086b
LP
241 return -EUNATCH;
242
d08fd4c3 243 r = lookup_key(req->keyring, &serial);
bb44fd07
ZJS
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)
e287086b
LP
251 return r;
252
623a8b19
YW
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;
e287086b
LP
263}
264
8e9d1eec 265static int backspace_chars(int ttyfd, size_t p) {
58fc840b 266 if (ttyfd < 0)
8e9d1eec 267 return 0;
58fc840b 268
8e9d1eec
ZJS
269 _cleanup_free_ char *buf = malloc_multiply(3, p);
270 if (!buf)
271 return log_oom();
58fc840b 272
8e9d1eec
ZJS
273 for (size_t i = 0; i < p; i++)
274 memcpy(buf + 3 * i, "\b \b", 3);
58fc840b 275
e22c60a9 276 return loop_write(ttyfd, buf, 3 * p);
8e9d1eec 277}
fd6ac62c 278
8e9d1eec 279static int backspace_string(int ttyfd, const char *str) {
fd6ac62c
LP
280 assert(str);
281
f95dbcc2 282 /* Backspaces through enough characters to entirely undo printing of the specified string. */
fd6ac62c 283
8e9d1eec
ZJS
284 if (ttyfd < 0)
285 return 0;
286
287 size_t m = utf8_n_codepoints(str);
f5fbe71d 288 if (m == SIZE_MAX)
8e9d1eec 289 m = strlen(str); /* Not a valid UTF-8 string? If so, let's backspace the number of bytes
5bc9ea07 290 * output. Most likely this happened because we are not in a UTF-8 locale,
8e9d1eec
ZJS
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. */
fd6ac62c 294
8e9d1eec 295 return backspace_chars(ttyfd, m);
fd6ac62c
LP
296}
297
6b2faba7 298int ask_password_plymouth(
d08fd4c3 299 const AskPasswordRequest *req,
6b2faba7 300 AskPasswordFlags flags,
6b2faba7
FB
301 char ***ret) {
302
dbdec4b1 303 _cleanup_close_ int fd = -EBADF, inotify_fd = -EBADF;
6b2faba7
FB
304 _cleanup_free_ char *packet = NULL;
305 ssize_t k;
306 int r, n;
6b2faba7
FB
307 char buffer[LINE_MAX];
308 size_t p = 0;
6b2faba7 309
05db3fe7 310 assert(req);
6b2faba7
FB
311 assert(ret);
312
b2ac9280
LP
313 if (FLAGS_SET(flags, ASK_PASSWORD_HEADLESS))
314 return -ENOEXEC;
315
05db3fe7 316 const char *message = req->message ?: "Password:";
66bff73b 317
4ff3689a 318 if (req->flag_file) {
dbdec4b1
LP
319 inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
320 if (inotify_fd < 0)
6b2faba7
FB
321 return -errno;
322
4ff3689a 323 if (inotify_add_watch(inotify_fd, req->flag_file, IN_ATTRIB) < 0) /* for the link count */
6b2faba7
FB
324 return -errno;
325 }
326
aa25e19b 327 fd = plymouth_connect(SOCK_NONBLOCK);
6b2faba7 328 if (fd < 0)
aa25e19b 329 return fd;
6b2faba7 330
9cb5bf91 331 if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED)) {
6b2faba7
FB
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
e22c60a9 339 r = loop_write_full(fd, packet, n + 1, USEC_INFINITY);
6b2faba7
FB
340 if (r < 0)
341 return r;
342
692597c8
LP
343 CLEANUP_ERASE(buffer);
344
dbdec4b1
LP
345 enum {
346 POLL_SOCKET,
d66894a7
LP
347 POLL_TWO,
348 POLL_THREE,
dbdec4b1
LP
349 _POLL_MAX,
350 };
351
352 struct pollfd pollfd[_POLL_MAX] = {
d66894a7
LP
353 [POLL_SOCKET] = {
354 .fd = fd,
355 .events = POLLIN,
356 },
dbdec4b1 357 };
d66894a7
LP
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);
6b2faba7
FB
371
372 for (;;) {
d9e2af0a 373 usec_t timeout;
6b2faba7 374
c4a02a52
LP
375 if (req->until > 0)
376 timeout = usec_sub_unsigned(req->until, now(CLOCK_MONOTONIC));
d9e2af0a
YW
377 else
378 timeout = USEC_INFINITY;
6b2faba7 379
4ff3689a 380 if (req->flag_file && access(req->flag_file, F_OK) < 0)
692597c8 381 return -errno;
6b2faba7 382
dbdec4b1 383 r = ppoll_usec(pollfd, n_pollfd, timeout);
d9e2af0a
YW
384 if (r == -EINTR)
385 continue;
386 if (r < 0)
692597c8
LP
387 return r;
388 if (r == 0)
389 return -ETIME;
6b2faba7 390
d66894a7
LP
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)
dbdec4b1 395 (void) flush_fd(inotify_fd);
6b2faba7
FB
396
397 if (pollfd[POLL_SOCKET].revents == 0)
398 continue;
399
400 k = read(fd, buffer + p, sizeof(buffer) - p);
401 if (k < 0) {
8add30a0 402 if (ERRNO_IS_TRANSIENT(errno))
6b2faba7
FB
403 continue;
404
692597c8 405 return -errno;
6b2faba7 406 }
692597c8
LP
407 if (k == 0)
408 return -EIO;
6b2faba7
FB
409
410 p += k;
411
6b2faba7
FB
412 if (buffer[0] == 5) {
413
9cb5bf91 414 if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED)) {
6b2faba7
FB
415 /* Hmm, first try with cached
416 * passwords failed, so let's retry
417 * with a normal password request */
418 packet = mfree(packet);
419
692597c8
LP
420 if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
421 return -ENOMEM;
6b2faba7 422
e22c60a9 423 r = loop_write_full(fd, packet, n + 1, USEC_INFINITY);
6b2faba7 424 if (r < 0)
692597c8 425 return r;
6b2faba7
FB
426
427 flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
428 p = 0;
429 continue;
430 }
431
432 /* No password, because UI not shown */
692597c8 433 return -ENOENT;
6b2faba7
FB
434
435 } else if (IN_SET(buffer[0], 2, 9)) {
623a8b19 436 _cleanup_strv_free_erase_ char **l = NULL;
6b2faba7 437 uint32_t size;
6b2faba7
FB
438
439 /* One or more answers */
440 if (p < 5)
441 continue;
442
443 memcpy(&size, buffer+1, sizeof(size));
444 size = le32toh(size);
692597c8
LP
445 if (size + 5 > sizeof(buffer))
446 return -EIO;
6b2faba7
FB
447
448 if (p-5 < size)
449 continue;
450
451 l = strv_parse_nulstr(buffer + 5, size);
692597c8
LP
452 if (!l)
453 return -ENOMEM;
6b2faba7 454
623a8b19
YW
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;
6b2faba7 460
692597c8 461 } else
6b2faba7 462 /* Unknown packet */
692597c8 463 return -EIO;
6b2faba7 464 }
6b2faba7
FB
465}
466
8aaf18e0
ZJS
467#define NO_ECHO "(no echo) "
468#define PRESS_TAB "(press TAB for no echo) "
469#define SKIPPED "(skipped)"
470
7f4e0805 471int ask_password_tty(
d08fd4c3 472 const AskPasswordRequest *req,
e287086b 473 AskPasswordFlags flags,
c7b7d74e 474 char ***ret) {
7f4e0805 475
8aaf18e0 476 bool reset_tty = false, dirty = false, use_color = false, press_tab_visible = false;
dbdec4b1 477 _cleanup_close_ int cttyfd = -EBADF, inotify_fd = -EBADF;
7f4e0805 478 struct termios old_termios, new_termios;
f3149d57 479 char passphrase[LINE_MAX + 1] = {}, *x;
c7b7d74e 480 _cleanup_strv_free_erase_ char **l = NULL;
f3149d57 481 size_t p = 0, codepoint = 0;
088dcd8e 482 int r;
7f4e0805 483
05db3fe7 484 assert(req);
e287086b
LP
485 assert(ret);
486
b2ac9280
LP
487 if (FLAGS_SET(flags, ASK_PASSWORD_HEADLESS))
488 return -ENOEXEC;
489
9cb5bf91 490 if (FLAGS_SET(flags, ASK_PASSWORD_NO_TTY))
e287086b
LP
491 return -EUNATCH;
492
05db3fe7
YW
493 const char *message = req->message ?: "Password:";
494 const char *keyring = req->keyring;
7f4e0805 495
e390c34d 496 if (!FLAGS_SET(flags, ASK_PASSWORD_HIDE_EMOJI) && emoji_enabled())
1ae9b0cf 497 message = strjoina(glyph(GLYPH_LOCK_AND_KEY), " ", message);
52d199e3 498
4ff3689a 499 if (req->flag_file || (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && keyring)) {
dbdec4b1
LP
500 inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
501 if (inotify_fd < 0)
a497a296 502 return -errno;
c7b7d74e 503 }
4ff3689a
LP
504 if (req->flag_file)
505 if (inotify_add_watch(inotify_fd, req->flag_file, IN_ATTRIB /* for the link count */) < 0)
a497a296 506 return -errno;
05db3fe7 507 if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && keyring) {
d08fd4c3 508 r = ask_password_keyring(req, flags, ret);
c7b7d74e
XF
509 if (r >= 0)
510 return 0;
dbdec4b1 511 if (r != -ENOKEY)
c7b7d74e
XF
512 return r;
513
4dd2748b
LP
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 }
c7b7d74e 529 }
7f4e0805 530
692597c8
LP
531 CLEANUP_ERASE(passphrase);
532
daa55720 533 /* If the caller didn't specify a TTY, then use the controlling tty, if we can. */
72068d9d
LP
534 int ttyfd;
535 if (req->tty_fd < 0)
daa55720 536 ttyfd = cttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC);
72068d9d
LP
537 else
538 ttyfd = req->tty_fd;
7f4e0805 539
daa55720 540 if (ttyfd >= 0) {
a497a296
LP
541 if (tcgetattr(ttyfd, &old_termios) < 0)
542 return -errno;
7f4e0805 543
9cb5bf91 544 if (FLAGS_SET(flags, ASK_PASSWORD_CONSOLE_COLOR))
c2b32159
LP
545 use_color = dev_console_colors_enabled();
546 else
547 use_color = colors_enabled();
548
549 if (use_color)
e22c60a9 550 (void) loop_write(ttyfd, ANSI_HIGHLIGHT, SIZE_MAX);
ac7a9674 551
e22c60a9
MY
552 (void) loop_write(ttyfd, message, SIZE_MAX);
553 (void) loop_write(ttyfd, " ", 1);
ac7a9674 554
9cb5bf91 555 if (!FLAGS_SET(flags, ASK_PASSWORD_SILENT) && !FLAGS_SET(flags, ASK_PASSWORD_ECHO)) {
8aaf18e0 556 if (use_color)
e22c60a9
MY
557 (void) loop_write(ttyfd, ansi_grey(), SIZE_MAX);
558
559 (void) loop_write(ttyfd, PRESS_TAB, SIZE_MAX);
8aaf18e0
ZJS
560 press_tab_visible = true;
561 }
562
c2b32159 563 if (use_color)
e22c60a9 564 (void) loop_write(ttyfd, ANSI_NORMAL, SIZE_MAX);
7f4e0805
LP
565
566 new_termios = old_termios;
d02d4f83 567 termios_disable_echo(&new_termios);
7f4e0805 568
f789b17e 569 r = RET_NERRNO(tcsetattr(ttyfd, TCSANOW, &new_termios));
108dfff2 570 if (r < 0)
7f4e0805 571 goto finish;
7f4e0805
LP
572
573 reset_tty = true;
574 }
575
dbdec4b1
LP
576 enum {
577 POLL_TTY,
d66894a7
LP
578 POLL_TWO,
579 POLL_THREE,
dbdec4b1 580 _POLL_MAX,
70dee475 581 };
dbdec4b1
LP
582
583 struct pollfd pollfd[_POLL_MAX] = {
d66894a7
LP
584 [POLL_TTY] = {
585 .fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO,
586 .events = POLLIN,
587 },
70dee475 588 };
d66894a7
LP
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);
7f4e0805
LP
603
604 for (;;) {
e1ed99c8 605 _cleanup_(erase_char) char c;
d9e2af0a 606 usec_t timeout;
7f4e0805
LP
607 ssize_t n;
608
c4a02a52
LP
609 if (req->until > 0)
610 timeout = usec_sub_unsigned(req->until, now(CLOCK_MONOTONIC));
d9e2af0a
YW
611 else
612 timeout = USEC_INFINITY;
7f4e0805 613
4ff3689a
LP
614 if (req->flag_file) {
615 r = RET_NERRNO(access(req->flag_file, F_OK));
108dfff2 616 if (r < 0)
7f4e0805 617 goto finish;
108dfff2 618 }
7f4e0805 619
dbdec4b1 620 r = ppoll_usec(pollfd, n_pollfd, timeout);
d9e2af0a
YW
621 if (r == -EINTR)
622 continue;
623 if (r < 0)
7f4e0805 624 goto finish;
d9e2af0a 625 if (r == 0) {
ccc80078 626 r = -ETIME;
7f4e0805
LP
627 goto finish;
628 }
629
d66894a7
LP
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) {
dbdec4b1 636 (void) flush_fd(inotify_fd);
7f4e0805 637
d08fd4c3 638 r = ask_password_keyring(req, flags, ret);
c7b7d74e
XF
639 if (r >= 0) {
640 r = 0;
641 goto finish;
642 } else if (r != -ENOKEY)
643 goto finish;
644 }
645
7f4e0805
LP
646 if (pollfd[POLL_TTY].revents == 0)
647 continue;
648
981e4cd3
LP
649 n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1);
650 if (n < 0) {
8add30a0 651 if (ERRNO_IS_TRANSIENT(errno))
7f4e0805
LP
652 continue;
653
654 r = -errno;
655 goto finish;
656
fd6ac62c 657 }
7f4e0805 658
8aaf18e0
ZJS
659 if (press_tab_visible) {
660 assert(ttyfd >= 0);
661 backspace_chars(ttyfd, strlen(PRESS_TAB));
662 press_tab_visible = false;
663 }
664
fd6ac62c
LP
665 /* We treat EOF, newline and NUL byte all as valid end markers */
666 if (n == 0 || c == '\n' || c == 0)
7f4e0805 667 break;
fd6ac62c 668
d325f244
FB
669 if (c == 4) { /* C-d also known as EOT */
670 if (ttyfd >= 0)
e22c60a9 671 (void) loop_write(ttyfd, SKIPPED, SIZE_MAX);
d325f244
FB
672
673 goto skipped;
674 }
675
fd6ac62c 676 if (c == 21) { /* C-u */
7f4e0805 677
9cb5bf91 678 if (!FLAGS_SET(flags, ASK_PASSWORD_SILENT))
8e9d1eec 679 (void) backspace_string(ttyfd, passphrase);
fd6ac62c 680
87f54463 681 explicit_bzero_safe(passphrase, sizeof(passphrase));
fd6ac62c 682 p = codepoint = 0;
7f4e0805 683
4c701096 684 } else if (IN_SET(c, '\b', 127)) {
58fc840b
LP
685
686 if (p > 0) {
fd6ac62c 687 size_t q;
58fc840b 688
9cb5bf91 689 if (!FLAGS_SET(flags, ASK_PASSWORD_SILENT))
8e9d1eec 690 (void) backspace_chars(ttyfd, 1);
58fc840b 691
92e068b4
ZJS
692 /* Remove a full UTF-8 codepoint from the end. For that, figure out where the
693 * last one begins */
fd6ac62c
LP
694 q = 0;
695 for (;;) {
37ca78a3 696 int z;
fd6ac62c 697
f5fbe71d 698 z = utf8_encoded_valid_unichar(passphrase + q, SIZE_MAX);
37ca78a3 699 if (z <= 0) {
f5fbe71d 700 q = SIZE_MAX; /* Invalid UTF8! */
fd6ac62c
LP
701 break;
702 }
703
704 if (q + z >= p) /* This one brings us over the edge */
705 break;
706
707 q += z;
708 }
709
f5fbe71d 710 p = codepoint = q == SIZE_MAX ? p - 1 : q;
87f54463 711 explicit_bzero_safe(passphrase + p, sizeof(passphrase) - p);
fd6ac62c 712
9cb5bf91 713 } else if (!dirty && !FLAGS_SET(flags, ASK_PASSWORD_SILENT)) {
441dfe09 714
e287086b 715 flags |= ASK_PASSWORD_SILENT;
441dfe09 716
92e068b4
ZJS
717 /* There are two ways to enter silent mode. Either by pressing backspace as
718 * first key (and only as first key), or ... */
fd6ac62c 719
441dfe09 720 if (ttyfd >= 0)
e22c60a9 721 (void) loop_write(ttyfd, NO_ECHO, SIZE_MAX);
441dfe09 722
58fc840b 723 } else if (ttyfd >= 0)
e22c60a9 724 (void) loop_write(ttyfd, "\a", 1);
7f4e0805 725
9cb5bf91 726 } else if (c == '\t' && !FLAGS_SET(flags, ASK_PASSWORD_SILENT)) {
58fc840b 727
8e9d1eec 728 (void) backspace_string(ttyfd, passphrase);
e287086b 729 flags |= ASK_PASSWORD_SILENT;
58fc840b 730
441dfe09
LP
731 /* ... or by pressing TAB at any time. */
732
58fc840b 733 if (ttyfd >= 0)
e22c60a9 734 (void) loop_write(ttyfd, NO_ECHO, SIZE_MAX);
036eeac5 735
4ba044eb
LP
736 } else if (char_is_cc(c) || p >= sizeof(passphrase)-1) {
737 /* Don't accept control chars or overly long passphrases */
fd6ac62c 738 if (ttyfd >= 0)
e22c60a9 739 (void) loop_write(ttyfd, "\a", 1);
fd6ac62c
LP
740
741 } else {
7f4e0805
LP
742 passphrase[p++] = c;
743
9cb5bf91 744 if (!FLAGS_SET(flags, ASK_PASSWORD_SILENT) && ttyfd >= 0) {
fd6ac62c 745 /* Check if we got a complete UTF-8 character now. If so, let's output one '*'. */
f5fbe71d 746 n = utf8_encoded_valid_unichar(passphrase + codepoint, SIZE_MAX);
f3149d57 747 if (n >= 0) {
9cb5bf91 748 if (FLAGS_SET(flags, ASK_PASSWORD_ECHO))
e22c60a9 749 (void) loop_write(ttyfd, passphrase + codepoint, n);
d26eef92 750 else
e22c60a9 751 (void) loop_write(ttyfd,
1ae9b0cf 752 glyph(GLYPH_BULLET),
e22c60a9 753 SIZE_MAX);
f3149d57 754 codepoint = p;
f3149d57
ZJS
755 }
756 }
441dfe09
LP
757
758 dirty = true;
7f4e0805
LP
759 }
760 }
761
981e4cd3
LP
762 x = strndup(passphrase, p);
763 if (!x) {
7f4e0805
LP
764 r = -ENOMEM;
765 goto finish;
766 }
767
0e28c86f
LP
768 r = strv_consume(&l, x);
769 if (r < 0)
c7b7d74e 770 goto finish;
c7b7d74e 771
d325f244 772skipped:
72c08a47
ZJS
773 if (strv_isempty(l))
774 r = log_debug_errno(SYNTHETIC_ERRNO(ECANCELED), "Password query was cancelled.");
775 else {
d08fd4c3
LP
776 if (keyring)
777 (void) add_to_keyring_and_log(keyring, flags, l);
72c08a47
ZJS
778
779 *ret = TAKE_PTR(l);
780 r = 0;
781 }
7f4e0805
LP
782
783finish:
981e4cd3 784 if (ttyfd >= 0 && reset_tty) {
e22c60a9 785 (void) loop_write(ttyfd, "\n", 1);
f789b17e 786 (void) tcsetattr(ttyfd, TCSANOW, &old_termios);
7f4e0805
LP
787 }
788
789 return r;
790}
791
4dd2748b 792static int create_socket(const char *askpwdir, char **ret) {
15a3e96f 793 _cleanup_free_ char *path = NULL;
f36a9d59
ZJS
794 union sockaddr_union sa;
795 socklen_t sa_len;
254d1313 796 _cleanup_close_ int fd = -EBADF;
f36a9d59 797 int r;
7f4e0805 798
4dd2748b 799 assert(askpwdir);
15a3e96f 800 assert(ret);
7f4e0805 801
e62d8c39 802 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
4a62c710 803 if (fd < 0)
00843602 804 return -errno;
7f4e0805 805
4dd2748b 806 if (asprintf(&path, "%s/sck.%" PRIx64, askpwdir, random_u64()) < 0)
15a3e96f
LP
807 return -ENOMEM;
808
f36a9d59
ZJS
809 r = sockaddr_un_set_path(&sa.un, path);
810 if (r < 0)
811 return r;
812 sa_len = r;
7f4e0805 813
2053593f 814 WITH_UMASK(0177)
f36a9d59 815 if (bind(fd, &sa.sa, sa_len) < 0)
00843602 816 return -errno;
1d6702e8 817
2ff48e98
LP
818 r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true);
819 if (r < 0)
820 return r;
7f4e0805 821
85352c09
MY
822 (void) setsockopt_int(fd, SOL_SOCKET, SO_PASSRIGHTS, false);
823
15a3e96f
LP
824 *ret = TAKE_PTR(path);
825 return TAKE_FD(fd);
7f4e0805
LP
826}
827
7f4e0805 828int ask_password_agent(
d08fd4c3 829 const AskPasswordRequest *req,
e287086b
LP
830 AskPasswordFlags flags,
831 char ***ret) {
7f4e0805 832
4dd2748b 833 _cleanup_close_ int socket_fd = -EBADF, signal_fd = -EBADF, inotify_fd = -EBADF, dfd = -EBADF;
dbdec4b1 834 _cleanup_(unlink_and_freep) char *socket_name = NULL;
4dd2748b 835 _cleanup_free_ char *temp = NULL, *final = NULL;
c7b7d74e 836 _cleanup_strv_free_erase_ char **l = NULL;
e287086b 837 _cleanup_fclose_ FILE *f = NULL;
e287086b 838 sigset_t mask, oldmask;
981e4cd3 839 int r;
7f4e0805 840
05db3fe7 841 assert(req);
e287086b 842 assert(ret);
21bc923a 843
b2ac9280
LP
844 if (FLAGS_SET(flags, ASK_PASSWORD_HEADLESS))
845 return -ENOEXEC;
846
9cb5bf91 847 if (FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT))
e287086b 848 return -EUNATCH;
00843602 849
4ff3689a
LP
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
72c0a2c2 854 assert_se(sigemptyset(&mask) >= 0);
db7136ec 855 assert_se(sigset_add_many(&mask, SIGINT, SIGTERM) >= 0);
72c0a2c2 856 assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) >= 0);
f9b72cd8 857
4dd2748b
LP
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 }
7f4e0805 872
05db3fe7 873 if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && req->keyring) {
d08fd4c3 874 r = ask_password_keyring(req, flags, ret);
c7b7d74e
XF
875 if (r >= 0) {
876 r = 0;
877 goto finish;
878 } else if (r != -ENOKEY)
879 goto finish;
880
dbdec4b1
LP
881 inotify_fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
882 if (inotify_fd < 0) {
c7b7d74e
XF
883 r = -errno;
884 goto finish;
885 }
108dfff2 886
4dd2748b 887 r = inotify_add_watch_fd(inotify_fd, dfd, IN_ONLYDIR|IN_ATTRIB /* for mtime */);
108dfff2 888 if (r < 0)
c7b7d74e 889 goto finish;
c7b7d74e
XF
890 }
891
4dd2748b
LP
892 if (asprintf(&final, "ask.%" PRIu64, random_u64()) < 0) {
893 r = -ENOMEM;
7f4e0805
LP
894 goto finish;
895 }
896
4dd2748b
LP
897 r = fopen_temporary_at(dfd, final, &f, &temp);
898 if (r < 0)
7f4e0805 899 goto finish;
7f4e0805 900
981e4cd3
LP
901 signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
902 if (signal_fd < 0) {
00843602 903 r = -errno;
7f4e0805
LP
904 goto finish;
905 }
906
4dd2748b 907 socket_fd = create_socket(askpwdir, &socket_name);
981e4cd3 908 if (socket_fd < 0) {
7f4e0805
LP
909 r = socket_fd;
910 goto finish;
911 }
912
913 fprintf(f,
914 "[Ask]\n"
de0671ee 915 "PID="PID_FMT"\n"
7f4e0805 916 "Socket=%s\n"
21bc923a 917 "AcceptCached=%i\n"
64845bdc 918 "Echo=%i\n"
1fa94a31
SB
919 "NotAfter="USEC_FMT"\n"
920 "Silent=%i\n",
df0ff127 921 getpid_cached(),
7f4e0805 922 socket_name,
9cb5bf91
CH
923 FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED),
924 FLAGS_SET(flags, ASK_PASSWORD_ECHO),
c4a02a52 925 req->until,
9cb5bf91 926 FLAGS_SET(flags, ASK_PASSWORD_SILENT));
7f4e0805 927
05db3fe7
YW
928 if (req->message)
929 fprintf(f, "Message=%s\n", req->message);
7f4e0805 930
05db3fe7
YW
931 if (req->icon)
932 fprintf(f, "Icon=%s\n", req->icon);
7f4e0805 933
05db3fe7
YW
934 if (req->id)
935 fprintf(f, "Id=%s\n", req->id);
9fa1de96 936
4dd2748b
LP
937 if (fchmod(fileno(f), 0644) < 0) {
938 r = -errno;
939 goto finish;
940 }
941
dacd6cee 942 r = fflush_and_check(f);
00843602 943 if (r < 0)
7f4e0805 944 goto finish;
7f4e0805 945
4dd2748b
LP
946 if (renameat(dfd, temp, dfd, final) < 0) {
947 r = -errno;
7f4e0805 948 goto finish;
4dd2748b
LP
949 }
950
951 temp = mfree(temp);
7f4e0805 952
dbdec4b1
LP
953 enum {
954 POLL_SOCKET,
955 POLL_SIGNAL,
d66894a7
LP
956 POLL_THREE,
957 POLL_FOUR,
dbdec4b1
LP
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 },
dbdec4b1 964 };
d66894a7
LP
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);
7f4e0805
LP
979
980 for (;;) {
fb29cdbe 981 CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
7f4e0805 982 char passphrase[LINE_MAX+1];
7f4e0805
LP
983 struct iovec iovec;
984 struct ucred *ucred;
d9e2af0a 985 usec_t timeout;
7f4e0805 986 ssize_t n;
7f4e0805 987
c4a02a52
LP
988 if (req->until > 0)
989 timeout = usec_sub_unsigned(req->until, now(CLOCK_MONOTONIC));
d9e2af0a
YW
990 else
991 timeout = USEC_INFINITY;
7f4e0805 992
dbdec4b1 993 r = ppoll_usec(pollfd, n_pollfd, timeout);
d9e2af0a
YW
994 if (r == -EINTR)
995 continue;
996 if (r < 0)
7f4e0805 997 goto finish;
d9e2af0a 998 if (r == 0) {
7f4e0805
LP
999 r = -ETIME;
1000 goto finish;
1001 }
1002
dbdec4b1 1003 if (pollfd[POLL_SIGNAL].revents & POLLIN) {
21bc923a
LP
1004 r = -EINTR;
1005 goto finish;
1006 }
7f4e0805 1007
d66894a7
LP
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) {
dbdec4b1 1012 (void) flush_fd(inotify_fd);
c7b7d74e 1013
05db3fe7 1014 if (req->keyring) {
d08fd4c3
LP
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 }
c7b7d74e
XF
1022 }
1023
dbdec4b1 1024 if (pollfd[POLL_SOCKET].revents == 0)
c7b7d74e
XF
1025 continue;
1026
dbdec4b1 1027 if (pollfd[POLL_SOCKET].revents != POLLIN) {
7f4e0805
LP
1028 r = -EIO;
1029 goto finish;
1030 }
1031
5cfa2c3d 1032 iovec = IOVEC_MAKE(passphrase, sizeof(passphrase));
7f4e0805 1033
41ab8c67
LP
1034 struct msghdr msghdr = {
1035 .msg_iov = &iovec,
1036 .msg_iovlen = 1,
1037 .msg_control = &control,
1038 .msg_controllen = sizeof(control),
1039 };
7f4e0805 1040
3691bcf3 1041 n = recvmsg_safe(socket_fd, &msghdr, 0);
bb44fd07
ZJS
1042 if (ERRNO_IS_NEG_TRANSIENT(n))
1043 continue;
ad501930
MY
1044 if (n == -ECHRNG) {
1045 log_debug_errno(n, "Got message with truncated control data (unexpected fds sent?), ignoring.");
bb44fd07 1046 continue;
ad501930
MY
1047 }
1048 if (n == -EXFULL) {
1049 log_debug_errno(n, "Got message with truncated payload data, ignoring.");
1050 continue;
1051 }
1052 if (n < 0) {
3691bcf3 1053 r = (int) n;
7f4e0805
LP
1054 goto finish;
1055 }
1056
692597c8
LP
1057 CLEANUP_ERASE(passphrase);
1058
1c8da044
LP
1059 cmsg_close_all(&msghdr);
1060
8add30a0 1061 if (n == 0) {
00843602 1062 log_debug("Message too short");
7f4e0805
LP
1063 continue;
1064 }
1065
371d72e0
LP
1066 ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred);
1067 if (!ucred) {
00843602 1068 log_debug("Received message without credentials. Ignoring.");
7f4e0805
LP
1069 continue;
1070 }
1071
4dd2748b
LP
1072 if (ucred->uid != getuid() && ucred->uid != 0) {
1073 log_debug("Got response from bad user. Ignoring.");
7f4e0805
LP
1074 continue;
1075 }
1076
1077 if (passphrase[0] == '+') {
e287086b 1078 /* An empty message refers to the empty password */
8254a475 1079 if (n == 1)
bea1a013 1080 l = strv_new("");
8254a475
LP
1081 else
1082 l = strv_parse_nulstr(passphrase+1, n-1);
8254a475 1083 if (!l) {
7f4e0805
LP
1084 r = -ENOMEM;
1085 goto finish;
1086 }
1087
58629001 1088 if (strv_isempty(l)) {
e287086b 1089 l = strv_free(l);
00843602 1090 log_debug("Invalid packet");
21bc923a
LP
1091 continue;
1092 }
1093
e287086b
LP
1094 break;
1095 }
21bc923a 1096
e287086b 1097 if (passphrase[0] == '-') {
7f4e0805
LP
1098 r = -ECANCELED;
1099 goto finish;
7f4e0805
LP
1100 }
1101
e287086b 1102 log_debug("Invalid packet");
7f4e0805
LP
1103 }
1104
05db3fe7 1105 if (req->keyring)
d08fd4c3 1106 (void) add_to_keyring_and_log(req->keyring, flags, l);
e287086b 1107
ae2a15bc 1108 *ret = TAKE_PTR(l);
7f4e0805
LP
1109 r = 0;
1110
1111finish:
4dd2748b
LP
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 }
7f4e0805 1119
f9b72cd8 1120 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
7f4e0805
LP
1121 return r;
1122}
260ab287 1123
d08fd4c3 1124static int ask_password_credential(const AskPasswordRequest *req, AskPasswordFlags flags, char ***ret) {
8806bb4b 1125 _cleanup_(erase_and_freep) char *buffer = NULL;
623a8b19 1126 _cleanup_strv_free_erase_ char **l = NULL;
8806bb4b 1127 size_t size;
8806bb4b
LP
1128 int r;
1129
d08fd4c3
LP
1130 assert(req);
1131 assert(req->credential);
8806bb4b
LP
1132 assert(ret);
1133
d08fd4c3 1134 r = read_credential(req->credential, (void**) &buffer, &size);
8806bb4b
LP
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
623a8b19
YW
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);
8806bb4b
LP
1146 return 0;
1147}
1148
00843602 1149int ask_password_auto(
d08fd4c3 1150 const AskPasswordRequest *req,
e287086b
LP
1151 AskPasswordFlags flags,
1152 char ***ret) {
00843602
LP
1153
1154 int r;
1155
05db3fe7 1156 assert(req);
e287086b 1157 assert(ret);
21bc923a 1158
7fd0ea6c
LP
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
05db3fe7 1170 if (!FLAGS_SET(flags, ASK_PASSWORD_NO_CREDENTIAL) && req->credential) {
d08fd4c3 1171 r = ask_password_credential(req, flags, ret);
8806bb4b
LP
1172 if (r != -ENOKEY)
1173 return r;
1174 }
1175
9cb5bf91 1176 if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) &&
05db3fe7 1177 req->keyring &&
300b7e76 1178 (FLAGS_SET(flags, ASK_PASSWORD_NO_TTY) || !isatty_safe(STDIN_FILENO)) &&
9cb5bf91 1179 FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT)) {
d08fd4c3 1180 r = ask_password_keyring(req, flags, ret);
e287086b
LP
1181 if (r != -ENOKEY)
1182 return r;
1183 }
1184
300b7e76 1185 if (!FLAGS_SET(flags, ASK_PASSWORD_NO_TTY) && isatty_safe(STDIN_FILENO))
c4a02a52 1186 return ask_password_tty(req, flags, ret);
00843602 1187
9cb5bf91 1188 if (!FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT))
c4a02a52 1189 return ask_password_agent(req, flags, ret);
e287086b
LP
1190
1191 return -EUNATCH;
260ab287 1192}
ec572753
LP
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}