]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/ask-password-api.c
Merge pull request #25389 from fbuihuu/update-test-for-opensuse
[thirdparty/systemd.git] / src / shared / ask-password-api.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <inttypes.h>
6 #include <limits.h>
7 #include <stdbool.h>
8 #include <stddef.h>
9 #include <stdint.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <sys/inotify.h>
13 #include <sys/signalfd.h>
14 #include <sys/stat.h>
15 #include <sys/time.h>
16 #include <sys/uio.h>
17 #include <sys/un.h>
18 #include <termios.h>
19 #include <unistd.h>
20
21 #include "alloc-util.h"
22 #include "ask-password-api.h"
23 #include "constants.h"
24 #include "creds-util.h"
25 #include "fd-util.h"
26 #include "fileio.h"
27 #include "format-util.h"
28 #include "fs-util.h"
29 #include "glyph-util.h"
30 #include "io-util.h"
31 #include "keyring-util.h"
32 #include "log.h"
33 #include "macro.h"
34 #include "memory-util.h"
35 #include "missing_syscall.h"
36 #include "mkdir-label.h"
37 #include "nulstr-util.h"
38 #include "process-util.h"
39 #include "random-util.h"
40 #include "signal-util.h"
41 #include "socket-util.h"
42 #include "string-util.h"
43 #include "strv.h"
44 #include "terminal-util.h"
45 #include "time-util.h"
46 #include "tmpfile-util.h"
47 #include "umask-util.h"
48 #include "utf8.h"
49
50 #define KEYRING_TIMEOUT_USEC ((5 * USEC_PER_MINUTE) / 2)
51
52 static int lookup_key(const char *keyname, key_serial_t *ret) {
53 key_serial_t serial;
54
55 assert(keyname);
56 assert(ret);
57
58 serial = request_key("user", keyname, NULL, 0);
59 if (serial == -1)
60 return negative_errno();
61
62 *ret = serial;
63 return 0;
64 }
65
66 static int retrieve_key(key_serial_t serial, char ***ret) {
67 _cleanup_(erase_and_freep) void *p = NULL;
68 char **l;
69 size_t n;
70 int r;
71
72 assert(ret);
73
74 r = keyring_read(serial, &p, &n);
75 if (r < 0)
76 return r;
77
78 l = strv_parse_nulstr(p, n);
79 if (!l)
80 return -ENOMEM;
81
82 *ret = l;
83 return 0;
84 }
85
86 static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **passwords) {
87 _cleanup_strv_free_erase_ char **l = NULL;
88 _cleanup_(erase_and_freep) char *p = NULL;
89 key_serial_t serial;
90 size_t n;
91 int r;
92
93 assert(keyname);
94
95 if (!FLAGS_SET(flags, ASK_PASSWORD_PUSH_CACHE))
96 return 0;
97 if (strv_isempty(passwords))
98 return 0;
99
100 r = lookup_key(keyname, &serial);
101 if (r >= 0) {
102 r = retrieve_key(serial, &l);
103 if (r < 0)
104 return r;
105 } else if (r != -ENOKEY)
106 return r;
107
108 r = strv_extend_strv(&l, passwords, true);
109 if (r <= 0)
110 return r;
111
112 r = strv_make_nulstr(l, &p, &n);
113 if (r < 0)
114 return r;
115
116 /* chop off the final NUL byte. We do this because we want to use the separator NUL bytes only if we
117 * have multiple passwords. */
118 n = LESS_BY(n, (size_t) 1);
119
120 serial = add_key("user", keyname, p, n, KEY_SPEC_USER_KEYRING);
121 if (serial == -1)
122 return -errno;
123
124 if (keyctl(KEYCTL_SET_TIMEOUT,
125 (unsigned long) serial,
126 (unsigned long) DIV_ROUND_UP(KEYRING_TIMEOUT_USEC, USEC_PER_SEC), 0, 0) < 0)
127 log_debug_errno(errno, "Failed to adjust kernel keyring key timeout: %m");
128
129 /* Tell everyone to check the keyring */
130 (void) touch("/run/systemd/ask-password");
131
132 log_debug("Added key to kernel keyring as %" PRIi32 ".", serial);
133
134 return 1;
135 }
136
137 static int add_to_keyring_and_log(const char *keyname, AskPasswordFlags flags, char **passwords) {
138 int r;
139
140 assert(keyname);
141
142 r = add_to_keyring(keyname, flags, passwords);
143 if (r < 0)
144 return log_debug_errno(r, "Failed to add password to kernel keyring: %m");
145
146 return 0;
147 }
148
149 static int ask_password_keyring(const char *keyname, AskPasswordFlags flags, char ***ret) {
150
151 key_serial_t serial;
152 int r;
153
154 assert(keyname);
155 assert(ret);
156
157 if (!FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED))
158 return -EUNATCH;
159
160 r = lookup_key(keyname, &serial);
161 if (r < 0) {
162 /* when retrieving the distinction between "kernel or container manager don't support
163 * or allow this" and "no matching key known" doesn't matter. Note that we propagate
164 * EACCESS here (even if EPERM not) since that is used if the keyring is available but
165 * we lack access to the key. */
166 if (ERRNO_IS_NOT_SUPPORTED(r) || r == -EPERM)
167 return -ENOKEY;
168
169 return r;
170 }
171
172 return retrieve_key(serial, ret);
173 }
174
175 static int backspace_chars(int ttyfd, size_t p) {
176 if (ttyfd < 0)
177 return 0;
178
179 _cleanup_free_ char *buf = malloc_multiply(3, p);
180 if (!buf)
181 return log_oom();
182
183 for (size_t i = 0; i < p; i++)
184 memcpy(buf + 3 * i, "\b \b", 3);
185
186 return loop_write(ttyfd, buf, 3*p, false);
187 }
188
189 static int backspace_string(int ttyfd, const char *str) {
190 assert(str);
191
192 /* Backspaces through enough characters to entirely undo printing of the specified string. */
193
194 if (ttyfd < 0)
195 return 0;
196
197 size_t m = utf8_n_codepoints(str);
198 if (m == SIZE_MAX)
199 m = strlen(str); /* Not a valid UTF-8 string? If so, let's backspace the number of bytes
200 * output. Most likely this happened because we are not in an UTF-8 locale,
201 * and in that case that is the correct thing to do. And even if it's not,
202 * terminals tend to stop backspacing at the leftmost column, hence
203 * backspacing too much should be mostly OK. */
204
205 return backspace_chars(ttyfd, m);
206 }
207
208 int ask_password_plymouth(
209 const char *message,
210 usec_t until,
211 AskPasswordFlags flags,
212 const char *flag_file,
213 char ***ret) {
214
215 static const union sockaddr_union sa = PLYMOUTH_SOCKET;
216 _cleanup_close_ int fd = -1, notify = -1;
217 _cleanup_free_ char *packet = NULL;
218 ssize_t k;
219 int r, n;
220 struct pollfd pollfd[2] = {};
221 char buffer[LINE_MAX];
222 size_t p = 0;
223 enum {
224 POLL_SOCKET,
225 POLL_INOTIFY
226 };
227
228 assert(ret);
229
230 if (!message)
231 message = "Password:";
232
233 if (flag_file) {
234 notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
235 if (notify < 0)
236 return -errno;
237
238 r = inotify_add_watch(notify, flag_file, IN_ATTRIB); /* for the link count */
239 if (r < 0)
240 return -errno;
241 }
242
243 fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
244 if (fd < 0)
245 return -errno;
246
247 r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
248 if (r < 0)
249 return -errno;
250
251 if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED)) {
252 packet = strdup("c");
253 n = 1;
254 } else if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
255 packet = NULL;
256 if (!packet)
257 return -ENOMEM;
258
259 r = loop_write(fd, packet, n + 1, true);
260 if (r < 0)
261 return r;
262
263 pollfd[POLL_SOCKET].fd = fd;
264 pollfd[POLL_SOCKET].events = POLLIN;
265 pollfd[POLL_INOTIFY].fd = notify;
266 pollfd[POLL_INOTIFY].events = POLLIN;
267
268 for (;;) {
269 usec_t timeout;
270
271 if (until > 0)
272 timeout = usec_sub_unsigned(until, now(CLOCK_MONOTONIC));
273 else
274 timeout = USEC_INFINITY;
275
276 if (flag_file && access(flag_file, F_OK) < 0) {
277 r = -errno;
278 goto finish;
279 }
280
281 r = ppoll_usec(pollfd, notify >= 0 ? 2 : 1, timeout);
282 if (r == -EINTR)
283 continue;
284 if (r < 0)
285 goto finish;
286 if (r == 0) {
287 r = -ETIME;
288 goto finish;
289 }
290
291 if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0)
292 (void) flush_fd(notify);
293
294 if (pollfd[POLL_SOCKET].revents == 0)
295 continue;
296
297 k = read(fd, buffer + p, sizeof(buffer) - p);
298 if (k < 0) {
299 if (ERRNO_IS_TRANSIENT(errno))
300 continue;
301
302 r = -errno;
303 goto finish;
304 }
305 if (k == 0) {
306 r = -EIO;
307 goto finish;
308 }
309
310 p += k;
311
312 if (buffer[0] == 5) {
313
314 if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED)) {
315 /* Hmm, first try with cached
316 * passwords failed, so let's retry
317 * with a normal password request */
318 packet = mfree(packet);
319
320 if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) {
321 r = -ENOMEM;
322 goto finish;
323 }
324
325 r = loop_write(fd, packet, n+1, true);
326 if (r < 0)
327 goto finish;
328
329 flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
330 p = 0;
331 continue;
332 }
333
334 /* No password, because UI not shown */
335 r = -ENOENT;
336 goto finish;
337
338 } else if (IN_SET(buffer[0], 2, 9)) {
339 uint32_t size;
340 char **l;
341
342 /* One or more answers */
343 if (p < 5)
344 continue;
345
346 memcpy(&size, buffer+1, sizeof(size));
347 size = le32toh(size);
348 if (size + 5 > sizeof(buffer)) {
349 r = -EIO;
350 goto finish;
351 }
352
353 if (p-5 < size)
354 continue;
355
356 l = strv_parse_nulstr(buffer + 5, size);
357 if (!l) {
358 r = -ENOMEM;
359 goto finish;
360 }
361
362 *ret = l;
363 break;
364
365 } else {
366 /* Unknown packet */
367 r = -EIO;
368 goto finish;
369 }
370 }
371
372 r = 0;
373
374 finish:
375 explicit_bzero_safe(buffer, sizeof(buffer));
376 return r;
377 }
378
379 #define NO_ECHO "(no echo) "
380 #define PRESS_TAB "(press TAB for no echo) "
381 #define SKIPPED "(skipped)"
382
383 int ask_password_tty(
384 int ttyfd,
385 const char *message,
386 const char *keyname,
387 usec_t until,
388 AskPasswordFlags flags,
389 const char *flag_file,
390 char ***ret) {
391
392 enum {
393 POLL_TTY,
394 POLL_INOTIFY,
395 _POLL_MAX,
396 };
397
398 bool reset_tty = false, dirty = false, use_color = false, press_tab_visible = false;
399 _cleanup_close_ int cttyfd = -1, notify = -1;
400 struct termios old_termios, new_termios;
401 char passphrase[LINE_MAX + 1] = {}, *x;
402 _cleanup_strv_free_erase_ char **l = NULL;
403 struct pollfd pollfd[_POLL_MAX];
404 size_t p = 0, codepoint = 0;
405 int r;
406
407 assert(ret);
408
409 if (FLAGS_SET(flags, ASK_PASSWORD_NO_TTY))
410 return -EUNATCH;
411
412 if (!message)
413 message = "Password:";
414
415 if (!FLAGS_SET(flags, ASK_PASSWORD_HIDE_EMOJI) && emoji_enabled())
416 message = strjoina(special_glyph(SPECIAL_GLYPH_LOCK_AND_KEY), " ", message);
417
418 if (flag_file || (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && keyname)) {
419 notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
420 if (notify < 0)
421 return -errno;
422 }
423 if (flag_file) {
424 if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0)
425 return -errno;
426 }
427 if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && keyname) {
428 r = ask_password_keyring(keyname, flags, ret);
429 if (r >= 0)
430 return 0;
431 else if (r != -ENOKEY)
432 return r;
433
434 if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_ATTRIB /* for mtime */) < 0)
435 return -errno;
436 }
437
438 /* If the caller didn't specify a TTY, then use the controlling tty, if we can. */
439 if (ttyfd < 0)
440 ttyfd = cttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC);
441
442 if (ttyfd >= 0) {
443 if (tcgetattr(ttyfd, &old_termios) < 0)
444 return -errno;
445
446 if (FLAGS_SET(flags, ASK_PASSWORD_CONSOLE_COLOR))
447 use_color = dev_console_colors_enabled();
448 else
449 use_color = colors_enabled();
450
451 if (use_color)
452 (void) loop_write(ttyfd, ANSI_HIGHLIGHT, STRLEN(ANSI_HIGHLIGHT), false);
453
454 (void) loop_write(ttyfd, message, strlen(message), false);
455 (void) loop_write(ttyfd, " ", 1, false);
456
457 if (!FLAGS_SET(flags, ASK_PASSWORD_SILENT) && !FLAGS_SET(flags, ASK_PASSWORD_ECHO)) {
458 if (use_color)
459 (void) loop_write(ttyfd, ansi_grey(), strlen(ansi_grey()), false);
460 (void) loop_write(ttyfd, PRESS_TAB, strlen(PRESS_TAB), false);
461 press_tab_visible = true;
462 }
463
464 if (use_color)
465 (void) loop_write(ttyfd, ANSI_NORMAL, STRLEN(ANSI_NORMAL), false);
466
467 new_termios = old_termios;
468 new_termios.c_lflag &= ~(ICANON|ECHO);
469 new_termios.c_cc[VMIN] = 1;
470 new_termios.c_cc[VTIME] = 0;
471
472 if (tcsetattr(ttyfd, TCSADRAIN, &new_termios) < 0) {
473 r = -errno;
474 goto finish;
475 }
476
477 reset_tty = true;
478 }
479
480 pollfd[POLL_TTY] = (struct pollfd) {
481 .fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO,
482 .events = POLLIN,
483 };
484 pollfd[POLL_INOTIFY] = (struct pollfd) {
485 .fd = notify,
486 .events = POLLIN,
487 };
488
489 for (;;) {
490 _cleanup_(erase_char) char c;
491 usec_t timeout;
492 ssize_t n;
493
494 if (until > 0)
495 timeout = usec_sub_unsigned(until, now(CLOCK_MONOTONIC));
496 else
497 timeout = USEC_INFINITY;
498
499 if (flag_file)
500 if (access(flag_file, F_OK) < 0) {
501 r = -errno;
502 goto finish;
503 }
504
505 r = ppoll_usec(pollfd, notify >= 0 ? 2 : 1, timeout);
506 if (r == -EINTR)
507 continue;
508 if (r < 0)
509 goto finish;
510 if (r == 0) {
511 r = -ETIME;
512 goto finish;
513 }
514
515 if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0 && keyname) {
516 (void) flush_fd(notify);
517
518 r = ask_password_keyring(keyname, flags, ret);
519 if (r >= 0) {
520 r = 0;
521 goto finish;
522 } else if (r != -ENOKEY)
523 goto finish;
524 }
525
526 if (pollfd[POLL_TTY].revents == 0)
527 continue;
528
529 n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1);
530 if (n < 0) {
531 if (ERRNO_IS_TRANSIENT(errno))
532 continue;
533
534 r = -errno;
535 goto finish;
536
537 }
538
539 if (press_tab_visible) {
540 assert(ttyfd >= 0);
541 backspace_chars(ttyfd, strlen(PRESS_TAB));
542 press_tab_visible = false;
543 }
544
545 /* We treat EOF, newline and NUL byte all as valid end markers */
546 if (n == 0 || c == '\n' || c == 0)
547 break;
548
549 if (c == 4) { /* C-d also known as EOT */
550 if (ttyfd >= 0)
551 (void) loop_write(ttyfd, SKIPPED, strlen(SKIPPED), false);
552
553 goto skipped;
554 }
555
556 if (c == 21) { /* C-u */
557
558 if (!FLAGS_SET(flags, ASK_PASSWORD_SILENT))
559 (void) backspace_string(ttyfd, passphrase);
560
561 explicit_bzero_safe(passphrase, sizeof(passphrase));
562 p = codepoint = 0;
563
564 } else if (IN_SET(c, '\b', 127)) {
565
566 if (p > 0) {
567 size_t q;
568
569 if (!FLAGS_SET(flags, ASK_PASSWORD_SILENT))
570 (void) backspace_chars(ttyfd, 1);
571
572 /* Remove a full UTF-8 codepoint from the end. For that, figure out where the
573 * last one begins */
574 q = 0;
575 for (;;) {
576 int z;
577
578 z = utf8_encoded_valid_unichar(passphrase + q, SIZE_MAX);
579 if (z <= 0) {
580 q = SIZE_MAX; /* Invalid UTF8! */
581 break;
582 }
583
584 if (q + z >= p) /* This one brings us over the edge */
585 break;
586
587 q += z;
588 }
589
590 p = codepoint = q == SIZE_MAX ? p - 1 : q;
591 explicit_bzero_safe(passphrase + p, sizeof(passphrase) - p);
592
593 } else if (!dirty && !FLAGS_SET(flags, ASK_PASSWORD_SILENT)) {
594
595 flags |= ASK_PASSWORD_SILENT;
596
597 /* There are two ways to enter silent mode. Either by pressing backspace as
598 * first key (and only as first key), or ... */
599
600 if (ttyfd >= 0)
601 (void) loop_write(ttyfd, NO_ECHO, strlen(NO_ECHO), false);
602
603 } else if (ttyfd >= 0)
604 (void) loop_write(ttyfd, "\a", 1, false);
605
606 } else if (c == '\t' && !FLAGS_SET(flags, ASK_PASSWORD_SILENT)) {
607
608 (void) backspace_string(ttyfd, passphrase);
609 flags |= ASK_PASSWORD_SILENT;
610
611 /* ... or by pressing TAB at any time. */
612
613 if (ttyfd >= 0)
614 (void) loop_write(ttyfd, NO_ECHO, strlen(NO_ECHO), false);
615
616 } else if (p >= sizeof(passphrase)-1) {
617
618 /* Reached the size limit */
619 if (ttyfd >= 0)
620 (void) loop_write(ttyfd, "\a", 1, false);
621
622 } else {
623 passphrase[p++] = c;
624
625 if (!FLAGS_SET(flags, ASK_PASSWORD_SILENT) && ttyfd >= 0) {
626 /* Check if we got a complete UTF-8 character now. If so, let's output one '*'. */
627 n = utf8_encoded_valid_unichar(passphrase + codepoint, SIZE_MAX);
628 if (n >= 0) {
629 if (FLAGS_SET(flags, ASK_PASSWORD_ECHO))
630 (void) loop_write(ttyfd, passphrase + codepoint, n, false);
631 else
632 (void) loop_write(ttyfd, "*", 1, false);
633 codepoint = p;
634 }
635 }
636
637 dirty = true;
638 }
639 }
640
641 x = strndup(passphrase, p);
642 explicit_bzero_safe(passphrase, sizeof(passphrase));
643 if (!x) {
644 r = -ENOMEM;
645 goto finish;
646 }
647
648 r = strv_consume(&l, x);
649 if (r < 0)
650 goto finish;
651
652 skipped:
653 if (strv_isempty(l))
654 r = log_debug_errno(SYNTHETIC_ERRNO(ECANCELED), "Password query was cancelled.");
655 else {
656 if (keyname)
657 (void) add_to_keyring_and_log(keyname, flags, l);
658
659 *ret = TAKE_PTR(l);
660 r = 0;
661 }
662
663 finish:
664 if (ttyfd >= 0 && reset_tty) {
665 (void) loop_write(ttyfd, "\n", 1, false);
666 (void) tcsetattr(ttyfd, TCSADRAIN, &old_termios);
667 }
668
669 return r;
670 }
671
672 static int create_socket(char **ret) {
673 _cleanup_free_ char *path = NULL;
674 union sockaddr_union sa;
675 socklen_t sa_len;
676 _cleanup_close_ int fd = -1;
677 int r;
678
679 assert(ret);
680
681 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
682 if (fd < 0)
683 return -errno;
684
685 if (asprintf(&path, "/run/systemd/ask-password/sck.%" PRIx64, random_u64()) < 0)
686 return -ENOMEM;
687
688 r = sockaddr_un_set_path(&sa.un, path);
689 if (r < 0)
690 return r;
691 sa_len = r;
692
693 RUN_WITH_UMASK(0177)
694 if (bind(fd, &sa.sa, sa_len) < 0)
695 return -errno;
696
697 r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true);
698 if (r < 0)
699 return r;
700
701 *ret = TAKE_PTR(path);
702 return TAKE_FD(fd);
703 }
704
705 int ask_password_agent(
706 const char *message,
707 const char *icon,
708 const char *id,
709 const char *keyname,
710 usec_t until,
711 AskPasswordFlags flags,
712 char ***ret) {
713
714 enum {
715 FD_SOCKET,
716 FD_SIGNAL,
717 FD_INOTIFY,
718 _FD_MAX
719 };
720
721 _cleanup_close_ int socket_fd = -1, signal_fd = -1, notify = -1, fd = -1;
722 char temp[] = "/run/systemd/ask-password/tmp.XXXXXX";
723 char final[sizeof(temp)] = "";
724 _cleanup_free_ char *socket_name = NULL;
725 _cleanup_strv_free_erase_ char **l = NULL;
726 _cleanup_fclose_ FILE *f = NULL;
727 struct pollfd pollfd[_FD_MAX];
728 sigset_t mask, oldmask;
729 int r;
730
731 assert(ret);
732
733 if (FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT))
734 return -EUNATCH;
735
736 assert_se(sigemptyset(&mask) >= 0);
737 assert_se(sigset_add_many(&mask, SIGINT, SIGTERM, -1) >= 0);
738 assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) >= 0);
739
740 (void) mkdir_p_label("/run/systemd/ask-password", 0755);
741
742 if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && keyname) {
743 r = ask_password_keyring(keyname, flags, ret);
744 if (r >= 0) {
745 r = 0;
746 goto finish;
747 } else if (r != -ENOKEY)
748 goto finish;
749
750 notify = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
751 if (notify < 0) {
752 r = -errno;
753 goto finish;
754 }
755 if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_ATTRIB /* for mtime */) < 0) {
756 r = -errno;
757 goto finish;
758 }
759 }
760
761 fd = mkostemp_safe(temp);
762 if (fd < 0) {
763 r = fd;
764 goto finish;
765 }
766
767 (void) fchmod(fd, 0644);
768
769 f = take_fdopen(&fd, "w");
770 if (!f) {
771 r = -errno;
772 goto finish;
773 }
774
775 signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
776 if (signal_fd < 0) {
777 r = -errno;
778 goto finish;
779 }
780
781 socket_fd = create_socket(&socket_name);
782 if (socket_fd < 0) {
783 r = socket_fd;
784 goto finish;
785 }
786
787 fprintf(f,
788 "[Ask]\n"
789 "PID="PID_FMT"\n"
790 "Socket=%s\n"
791 "AcceptCached=%i\n"
792 "Echo=%i\n"
793 "NotAfter="USEC_FMT"\n"
794 "Silent=%i\n",
795 getpid_cached(),
796 socket_name,
797 FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED),
798 FLAGS_SET(flags, ASK_PASSWORD_ECHO),
799 until,
800 FLAGS_SET(flags, ASK_PASSWORD_SILENT));
801
802 if (message)
803 fprintf(f, "Message=%s\n", message);
804
805 if (icon)
806 fprintf(f, "Icon=%s\n", icon);
807
808 if (id)
809 fprintf(f, "Id=%s\n", id);
810
811 r = fflush_and_check(f);
812 if (r < 0)
813 goto finish;
814
815 memcpy(final, temp, sizeof(temp));
816
817 final[sizeof(final)-11] = 'a';
818 final[sizeof(final)-10] = 's';
819 final[sizeof(final)-9] = 'k';
820
821 if (rename(temp, final) < 0) {
822 r = -errno;
823 goto finish;
824 }
825
826 zero(pollfd);
827 pollfd[FD_SOCKET].fd = socket_fd;
828 pollfd[FD_SOCKET].events = POLLIN;
829 pollfd[FD_SIGNAL].fd = signal_fd;
830 pollfd[FD_SIGNAL].events = POLLIN;
831 pollfd[FD_INOTIFY].fd = notify;
832 pollfd[FD_INOTIFY].events = POLLIN;
833
834 for (;;) {
835 CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
836 char passphrase[LINE_MAX+1];
837 struct iovec iovec;
838 struct ucred *ucred;
839 usec_t timeout;
840 ssize_t n;
841
842 if (until > 0)
843 timeout = usec_sub_unsigned(until, now(CLOCK_MONOTONIC));
844 else
845 timeout = USEC_INFINITY;
846
847 r = ppoll_usec(pollfd, notify >= 0 ? _FD_MAX : _FD_MAX - 1, timeout);
848 if (r == -EINTR)
849 continue;
850 if (r < 0)
851 goto finish;
852 if (r == 0) {
853 r = -ETIME;
854 goto finish;
855 }
856
857 if (pollfd[FD_SIGNAL].revents & POLLIN) {
858 r = -EINTR;
859 goto finish;
860 }
861
862 if (notify >= 0 && pollfd[FD_INOTIFY].revents != 0) {
863 (void) flush_fd(notify);
864
865 r = ask_password_keyring(keyname, flags, ret);
866 if (r >= 0) {
867 r = 0;
868 goto finish;
869 } else if (r != -ENOKEY)
870 goto finish;
871 }
872
873 if (pollfd[FD_SOCKET].revents == 0)
874 continue;
875
876 if (pollfd[FD_SOCKET].revents != POLLIN) {
877 r = -EIO;
878 goto finish;
879 }
880
881 iovec = IOVEC_MAKE(passphrase, sizeof(passphrase));
882
883 struct msghdr msghdr = {
884 .msg_iov = &iovec,
885 .msg_iovlen = 1,
886 .msg_control = &control,
887 .msg_controllen = sizeof(control),
888 };
889
890 n = recvmsg_safe(socket_fd, &msghdr, 0);
891 if (n < 0) {
892 if (ERRNO_IS_TRANSIENT(n))
893 continue;
894 if (n == -EXFULL) {
895 log_debug("Got message with truncated control data, ignoring.");
896 continue;
897 }
898
899 r = (int) n;
900 goto finish;
901 }
902
903 cmsg_close_all(&msghdr);
904
905 if (n == 0) {
906 log_debug("Message too short");
907 continue;
908 }
909
910 ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred);
911 if (!ucred) {
912 log_debug("Received message without credentials. Ignoring.");
913 continue;
914 }
915
916 if (ucred->uid != 0) {
917 log_debug("Got request from unprivileged user. Ignoring.");
918 continue;
919 }
920
921 if (passphrase[0] == '+') {
922 /* An empty message refers to the empty password */
923 if (n == 1)
924 l = strv_new("");
925 else
926 l = strv_parse_nulstr(passphrase+1, n-1);
927 explicit_bzero_safe(passphrase, n);
928 if (!l) {
929 r = -ENOMEM;
930 goto finish;
931 }
932
933 if (strv_isempty(l)) {
934 l = strv_free(l);
935 log_debug("Invalid packet");
936 continue;
937 }
938
939 break;
940 }
941
942 if (passphrase[0] == '-') {
943 r = -ECANCELED;
944 goto finish;
945 }
946
947 log_debug("Invalid packet");
948 }
949
950 if (keyname)
951 (void) add_to_keyring_and_log(keyname, flags, l);
952
953 *ret = TAKE_PTR(l);
954 r = 0;
955
956 finish:
957 if (socket_name)
958 (void) unlink(socket_name);
959
960 (void) unlink(temp);
961
962 if (final[0])
963 (void) unlink(final);
964
965 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
966 return r;
967 }
968
969 static int ask_password_credential(const char *credential_name, AskPasswordFlags flags, char ***ret) {
970 _cleanup_(erase_and_freep) char *buffer = NULL;
971 size_t size;
972 char **l;
973 int r;
974
975 assert(credential_name);
976 assert(ret);
977
978 r = read_credential(credential_name, (void**) &buffer, &size);
979 if (IN_SET(r, -ENXIO, -ENOENT)) /* No credentials passed or this credential not defined? */
980 return -ENOKEY;
981
982 l = strv_parse_nulstr(buffer, size);
983 if (!l)
984 return -ENOMEM;
985
986 *ret = l;
987 return 0;
988 }
989
990 int ask_password_auto(
991 const char *message,
992 const char *icon,
993 const char *id, /* id in "ask-password" protocol */
994 const char *key_name, /* name in kernel keyring */
995 const char *credential_name, /* name in $CREDENTIALS_DIRECTORY directory */
996 usec_t until,
997 AskPasswordFlags flags,
998 char ***ret) {
999
1000 int r;
1001
1002 assert(ret);
1003
1004 if (!FLAGS_SET(flags, ASK_PASSWORD_NO_CREDENTIAL) && credential_name) {
1005 r = ask_password_credential(credential_name, flags, ret);
1006 if (r != -ENOKEY)
1007 return r;
1008 }
1009
1010 if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) &&
1011 key_name &&
1012 (FLAGS_SET(flags, ASK_PASSWORD_NO_TTY) || !isatty(STDIN_FILENO)) &&
1013 FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT)) {
1014 r = ask_password_keyring(key_name, flags, ret);
1015 if (r != -ENOKEY)
1016 return r;
1017 }
1018
1019 if (!FLAGS_SET(flags, ASK_PASSWORD_NO_TTY) && isatty(STDIN_FILENO))
1020 return ask_password_tty(-1, message, key_name, until, flags, NULL, ret);
1021
1022 if (!FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT))
1023 return ask_password_agent(message, icon, id, key_name, until, flags, ret);
1024
1025 return -EUNATCH;
1026 }