]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/ask-password-api.c
tty-ask-pwd-agent: minor simplification by using FOREACH_DIRENT instead of FOREACH_DI...
[thirdparty/systemd.git] / src / shared / ask-password-api.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
00843602 2
7f4e0805
LP
3#include <errno.h>
4#include <fcntl.h>
a8fbdf54
TA
5#include <inttypes.h>
6#include <limits.h>
00843602 7#include <poll.h>
a8fbdf54 8#include <signal.h>
00843602 9#include <stdbool.h>
7f4e0805 10#include <stddef.h>
a8fbdf54
TA
11#include <stdint.h>
12#include <stdio.h>
13#include <stdlib.h>
00843602
LP
14#include <string.h>
15#include <sys/inotify.h>
7f4e0805 16#include <sys/signalfd.h>
00843602 17#include <sys/socket.h>
a8fbdf54
TA
18#include <sys/stat.h>
19#include <sys/time.h>
20#include <sys/uio.h>
00843602
LP
21#include <sys/un.h>
22#include <termios.h>
23#include <unistd.h>
7f4e0805 24
b5efdb8a 25#include "alloc-util.h"
3ffd4af2
LP
26#include "ask-password-api.h"
27#include "fd-util.h"
0d39fa9c 28#include "fileio.h"
f97b34a6 29#include "format-util.h"
c7b7d74e 30#include "fs-util.h"
c004493c 31#include "io-util.h"
a8fbdf54
TA
32#include "log.h"
33#include "macro.h"
0a970718 34#include "memory-util.h"
e287086b 35#include "missing.h"
49e942b2 36#include "mkdir.h"
df0ff127 37#include "process-util.h"
3df3e884 38#include "random-util.h"
24882e06 39#include "signal-util.h"
00843602 40#include "socket-util.h"
07630cea 41#include "string-util.h"
00843602
LP
42#include "strv.h"
43#include "terminal-util.h"
a8fbdf54 44#include "time-util.h"
e4de7287 45#include "tmpfile-util.h"
affb60b1 46#include "umask-util.h"
f3149d57 47#include "utf8.h"
7f4e0805 48
e287086b
LP
49#define KEYRING_TIMEOUT_USEC ((5 * USEC_PER_MINUTE) / 2)
50
51static int lookup_key(const char *keyname, key_serial_t *ret) {
52 key_serial_t serial;
53
54 assert(keyname);
55 assert(ret);
56
57 serial = request_key("user", keyname, NULL, 0);
58 if (serial == -1)
9c4615fb 59 return negative_errno();
e287086b
LP
60
61 *ret = serial;
62 return 0;
63}
64
65static int retrieve_key(key_serial_t serial, char ***ret) {
e693a932 66 size_t nfinal, m = 100;
e287086b 67 char **l;
e693a932 68 _cleanup_(erase_and_freep) char *pfinal = NULL;
e287086b
LP
69
70 assert(ret);
71
72 for (;;) {
e693a932
ZJS
73 _cleanup_(erase_and_freep) char *p = NULL;
74 long n;
75
e287086b
LP
76 p = new(char, m);
77 if (!p)
78 return -ENOMEM;
79
80 n = keyctl(KEYCTL_READ, (unsigned long) serial, (unsigned long) p, (unsigned long) m, 0);
81 if (n < 0)
82 return -errno;
e693a932
ZJS
83 if ((size_t) n < m) {
84 nfinal = (size_t) n;
85 pfinal = TAKE_PTR(p);
e287086b 86 break;
e693a932 87 }
054b6be0
LP
88
89 if (m > LONG_MAX / 2) /* overflow check */
90 return -ENOMEM;
e287086b
LP
91 m *= 2;
92 }
93
e693a932 94 l = strv_parse_nulstr(pfinal, nfinal);
e287086b
LP
95 if (!l)
96 return -ENOMEM;
97
98 *ret = l;
99 return 0;
100}
101
102static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **passwords) {
ab84f5b9 103 _cleanup_strv_free_erase_ char **l = NULL;
e693a932 104 _cleanup_(erase_and_freep) char *p = NULL;
e287086b
LP
105 key_serial_t serial;
106 size_t n;
107 int r;
108
109 assert(keyname);
110 assert(passwords);
111
112 if (!(flags & ASK_PASSWORD_PUSH_CACHE))
113 return 0;
114
115 r = lookup_key(keyname, &serial);
116 if (r >= 0) {
117 r = retrieve_key(serial, &l);
118 if (r < 0)
119 return r;
120 } else if (r != -ENOKEY)
121 return r;
122
123 r = strv_extend_strv(&l, passwords, true);
124 if (r <= 0)
125 return r;
126
127 r = strv_make_nulstr(l, &p, &n);
128 if (r < 0)
129 return r;
130
b60df13b 131 serial = add_key("user", keyname, p, n, KEY_SPEC_USER_KEYRING);
e287086b
LP
132 if (serial == -1)
133 return -errno;
134
135 if (keyctl(KEYCTL_SET_TIMEOUT,
136 (unsigned long) serial,
137 (unsigned long) DIV_ROUND_UP(KEYRING_TIMEOUT_USEC, USEC_PER_SEC), 0, 0) < 0)
138 log_debug_errno(errno, "Failed to adjust timeout: %m");
139
c7b7d74e
XF
140 /* Tell everyone to check the keyring */
141 (void) touch("/run/systemd/ask-password");
142
e287086b
LP
143 log_debug("Added key to keyring as %" PRIi32 ".", serial);
144
145 return 1;
146}
147
148static int add_to_keyring_and_log(const char *keyname, AskPasswordFlags flags, char **passwords) {
149 int r;
150
151 assert(keyname);
152 assert(passwords);
153
154 r = add_to_keyring(keyname, flags, passwords);
155 if (r < 0)
156 return log_debug_errno(r, "Failed to add password to keyring: %m");
157
158 return 0;
159}
160
8a111277 161static int ask_password_keyring(const char *keyname, AskPasswordFlags flags, char ***ret) {
e287086b
LP
162
163 key_serial_t serial;
164 int r;
165
166 assert(keyname);
167 assert(ret);
168
169 if (!(flags & ASK_PASSWORD_ACCEPT_CACHED))
170 return -EUNATCH;
171
172 r = lookup_key(keyname, &serial);
173 if (r == -ENOSYS) /* when retrieving the distinction doesn't matter */
174 return -ENOKEY;
175 if (r < 0)
176 return r;
177
178 return retrieve_key(serial, ret);
179}
180
8e9d1eec 181static int backspace_chars(int ttyfd, size_t p) {
58fc840b 182 if (ttyfd < 0)
8e9d1eec 183 return 0;
58fc840b 184
8e9d1eec
ZJS
185 _cleanup_free_ char *buf = malloc_multiply(3, p);
186 if (!buf)
187 return log_oom();
58fc840b 188
8e9d1eec
ZJS
189 for (size_t i = 0; i < p; i++)
190 memcpy(buf + 3 * i, "\b \b", 3);
58fc840b 191
8e9d1eec
ZJS
192 return loop_write(ttyfd, buf, 3*p, false);
193}
fd6ac62c 194
8e9d1eec 195static int backspace_string(int ttyfd, const char *str) {
fd6ac62c
LP
196 assert(str);
197
f95dbcc2 198 /* Backspaces through enough characters to entirely undo printing of the specified string. */
fd6ac62c 199
8e9d1eec
ZJS
200 if (ttyfd < 0)
201 return 0;
202
203 size_t m = utf8_n_codepoints(str);
fd6ac62c 204 if (m == (size_t) -1)
8e9d1eec
ZJS
205 m = strlen(str); /* Not a valid UTF-8 string? If so, let's backspace the number of bytes
206 * output. Most likely this happened because we are not in an UTF-8 locale,
207 * and in that case that is the correct thing to do. And even if it's not,
208 * terminals tend to stop backspacing at the leftmost column, hence
209 * backspacing too much should be mostly OK. */
fd6ac62c 210
8e9d1eec 211 return backspace_chars(ttyfd, m);
fd6ac62c
LP
212}
213
7f4e0805 214int ask_password_tty(
daa55720 215 int ttyfd,
7f4e0805 216 const char *message,
e287086b 217 const char *keyname,
7f4e0805 218 usec_t until,
e287086b 219 AskPasswordFlags flags,
7f4e0805 220 const char *flag_file,
c7b7d74e 221 char ***ret) {
7f4e0805 222
088dcd8e
LP
223 enum {
224 POLL_TTY,
225 POLL_INOTIFY,
226 _POLL_MAX,
227 };
228
c2b32159 229 bool reset_tty = false, dirty = false, use_color = false;
daa55720 230 _cleanup_close_ int cttyfd = -1, notify = -1;
7f4e0805 231 struct termios old_termios, new_termios;
f3149d57 232 char passphrase[LINE_MAX + 1] = {}, *x;
c7b7d74e 233 _cleanup_strv_free_erase_ char **l = NULL;
088dcd8e 234 struct pollfd pollfd[_POLL_MAX];
f3149d57 235 size_t p = 0, codepoint = 0;
088dcd8e 236 int r;
7f4e0805 237
e287086b
LP
238 assert(ret);
239
240 if (flags & ASK_PASSWORD_NO_TTY)
241 return -EUNATCH;
242
243 if (!message)
244 message = "Password:";
7f4e0805 245
c7b7d74e 246 if (flag_file || ((flags & ASK_PASSWORD_ACCEPT_CACHED) && keyname)) {
981e4cd3 247 notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
a497a296
LP
248 if (notify < 0)
249 return -errno;
c7b7d74e
XF
250 }
251 if (flag_file) {
a497a296
LP
252 if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0)
253 return -errno;
7f4e0805 254 }
c7b7d74e
XF
255 if ((flags & ASK_PASSWORD_ACCEPT_CACHED) && keyname) {
256 r = ask_password_keyring(keyname, flags, ret);
257 if (r >= 0)
258 return 0;
259 else if (r != -ENOKEY)
260 return r;
261
262 if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_ATTRIB /* for mtime */) < 0)
263 return -errno;
264 }
7f4e0805 265
daa55720
LP
266 /* If the caller didn't specify a TTY, then use the controlling tty, if we can. */
267 if (ttyfd < 0)
268 ttyfd = cttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC);
7f4e0805 269
daa55720 270 if (ttyfd >= 0) {
a497a296
LP
271 if (tcgetattr(ttyfd, &old_termios) < 0)
272 return -errno;
7f4e0805 273
c2b32159
LP
274 if (flags & ASK_PASSWORD_CONSOLE_COLOR)
275 use_color = dev_console_colors_enabled();
276 else
277 use_color = colors_enabled();
278
279 if (use_color)
ac7a9674
LP
280 (void) loop_write(ttyfd, ANSI_HIGHLIGHT, STRLEN(ANSI_HIGHLIGHT), false);
281
282 (void) loop_write(ttyfd, message, strlen(message), false);
283 (void) loop_write(ttyfd, " ", 1, false);
284
c2b32159 285 if (use_color)
ac7a9674 286 (void) loop_write(ttyfd, ANSI_NORMAL, STRLEN(ANSI_NORMAL), false);
7f4e0805
LP
287
288 new_termios = old_termios;
289 new_termios.c_lflag &= ~(ICANON|ECHO);
290 new_termios.c_cc[VMIN] = 1;
291 new_termios.c_cc[VTIME] = 0;
292
293 if (tcsetattr(ttyfd, TCSADRAIN, &new_termios) < 0) {
294 r = -errno;
295 goto finish;
296 }
297
298 reset_tty = true;
299 }
300
70dee475
LP
301 pollfd[POLL_TTY] = (struct pollfd) {
302 .fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO,
303 .events = POLLIN,
304 };
305 pollfd[POLL_INOTIFY] = (struct pollfd) {
306 .fd = notify,
307 .events = POLLIN,
308 };
7f4e0805
LP
309
310 for (;;) {
e1ed99c8 311 _cleanup_(erase_char) char c;
7f4e0805
LP
312 int sleep_for = -1, k;
313 ssize_t n;
314
315 if (until > 0) {
316 usec_t y;
317
318 y = now(CLOCK_MONOTONIC);
319
320 if (y > until) {
7ded2e28 321 r = -ETIME;
7f4e0805
LP
322 goto finish;
323 }
324
c9eb4a00 325 sleep_for = (int) DIV_ROUND_UP(until - y, USEC_PER_MSEC);
7f4e0805
LP
326 }
327
328 if (flag_file)
329 if (access(flag_file, F_OK) < 0) {
330 r = -errno;
331 goto finish;
332 }
333
e287086b 334 k = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for);
981e4cd3 335 if (k < 0) {
7f4e0805
LP
336 if (errno == EINTR)
337 continue;
338
339 r = -errno;
340 goto finish;
341 } else if (k == 0) {
ccc80078 342 r = -ETIME;
7f4e0805
LP
343 goto finish;
344 }
345
1f00998c 346 if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0 && keyname) {
665dfe93 347 (void) flush_fd(notify);
7f4e0805 348
c7b7d74e
XF
349 r = ask_password_keyring(keyname, flags, ret);
350 if (r >= 0) {
351 r = 0;
352 goto finish;
353 } else if (r != -ENOKEY)
354 goto finish;
355 }
356
7f4e0805
LP
357 if (pollfd[POLL_TTY].revents == 0)
358 continue;
359
981e4cd3
LP
360 n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1);
361 if (n < 0) {
3742095b 362 if (IN_SET(errno, EINTR, EAGAIN))
7f4e0805
LP
363 continue;
364
365 r = -errno;
366 goto finish;
367
fd6ac62c 368 }
7f4e0805 369
fd6ac62c
LP
370 /* We treat EOF, newline and NUL byte all as valid end markers */
371 if (n == 0 || c == '\n' || c == 0)
7f4e0805 372 break;
fd6ac62c
LP
373
374 if (c == 21) { /* C-u */
7f4e0805 375
e287086b 376 if (!(flags & ASK_PASSWORD_SILENT))
8e9d1eec 377 (void) backspace_string(ttyfd, passphrase);
fd6ac62c 378
87f54463 379 explicit_bzero_safe(passphrase, sizeof(passphrase));
fd6ac62c 380 p = codepoint = 0;
7f4e0805 381
4c701096 382 } else if (IN_SET(c, '\b', 127)) {
58fc840b
LP
383
384 if (p > 0) {
fd6ac62c 385 size_t q;
58fc840b 386
e287086b 387 if (!(flags & ASK_PASSWORD_SILENT))
8e9d1eec 388 (void) backspace_chars(ttyfd, 1);
58fc840b 389
92e068b4
ZJS
390 /* Remove a full UTF-8 codepoint from the end. For that, figure out where the
391 * last one begins */
fd6ac62c
LP
392 q = 0;
393 for (;;) {
394 size_t z;
395
92e068b4 396 z = utf8_encoded_valid_unichar(passphrase + q, (size_t) -1);
fd6ac62c
LP
397 if (z == 0) {
398 q = (size_t) -1; /* Invalid UTF8! */
399 break;
400 }
401
402 if (q + z >= p) /* This one brings us over the edge */
403 break;
404
405 q += z;
406 }
407
408 p = codepoint = q == (size_t) -1 ? p - 1 : q;
87f54463 409 explicit_bzero_safe(passphrase + p, sizeof(passphrase) - p);
fd6ac62c 410
e287086b 411 } else if (!dirty && !(flags & ASK_PASSWORD_SILENT)) {
441dfe09 412
e287086b 413 flags |= ASK_PASSWORD_SILENT;
441dfe09 414
92e068b4
ZJS
415 /* There are two ways to enter silent mode. Either by pressing backspace as
416 * first key (and only as first key), or ... */
fd6ac62c 417
441dfe09 418 if (ttyfd >= 0)
ac7a9674 419 (void) loop_write(ttyfd, "(no echo) ", 10, false);
441dfe09 420
58fc840b 421 } else if (ttyfd >= 0)
ac7a9674 422 (void) loop_write(ttyfd, "\a", 1, false);
7f4e0805 423
e287086b 424 } else if (c == '\t' && !(flags & ASK_PASSWORD_SILENT)) {
58fc840b 425
8e9d1eec 426 (void) backspace_string(ttyfd, passphrase);
e287086b 427 flags |= ASK_PASSWORD_SILENT;
58fc840b 428
441dfe09
LP
429 /* ... or by pressing TAB at any time. */
430
58fc840b 431 if (ttyfd >= 0)
ac7a9674 432 (void) loop_write(ttyfd, "(no echo) ", 10, false);
036eeac5 433
fd6ac62c
LP
434 } else if (p >= sizeof(passphrase)-1) {
435
436 /* Reached the size limit */
437 if (ttyfd >= 0)
438 (void) loop_write(ttyfd, "\a", 1, false);
439
440 } else {
7f4e0805
LP
441 passphrase[p++] = c;
442
f3149d57 443 if (!(flags & ASK_PASSWORD_SILENT) && ttyfd >= 0) {
fd6ac62c 444 /* Check if we got a complete UTF-8 character now. If so, let's output one '*'. */
92e068b4 445 n = utf8_encoded_valid_unichar(passphrase + codepoint, (size_t) -1);
f3149d57 446 if (n >= 0) {
d26eef92
ZJS
447 if (flags & ASK_PASSWORD_ECHO)
448 (void) loop_write(ttyfd, passphrase + codepoint, n, false);
449 else
450 (void) loop_write(ttyfd, "*", 1, false);
f3149d57 451 codepoint = p;
f3149d57
ZJS
452 }
453 }
441dfe09
LP
454
455 dirty = true;
7f4e0805
LP
456 }
457 }
458
981e4cd3 459 x = strndup(passphrase, p);
87f54463 460 explicit_bzero_safe(passphrase, sizeof(passphrase));
981e4cd3 461 if (!x) {
7f4e0805
LP
462 r = -ENOMEM;
463 goto finish;
464 }
465
0e28c86f
LP
466 r = strv_consume(&l, x);
467 if (r < 0)
c7b7d74e 468 goto finish;
c7b7d74e 469
e287086b 470 if (keyname)
c7b7d74e 471 (void) add_to_keyring_and_log(keyname, flags, l);
e287086b 472
c7b7d74e 473 *ret = TAKE_PTR(l);
7f4e0805
LP
474 r = 0;
475
476finish:
981e4cd3 477 if (ttyfd >= 0 && reset_tty) {
ac7a9674
LP
478 (void) loop_write(ttyfd, "\n", 1, false);
479 (void) tcsetattr(ttyfd, TCSADRAIN, &old_termios);
7f4e0805
LP
480 }
481
482 return r;
483}
484
15a3e96f
LP
485static int create_socket(char **ret) {
486 _cleanup_free_ char *path = NULL;
487 union sockaddr_union sa = {};
00843602 488 _cleanup_close_ int fd = -1;
2ff48e98 489 int salen, r;
7f4e0805 490
15a3e96f 491 assert(ret);
7f4e0805 492
e62d8c39 493 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
4a62c710 494 if (fd < 0)
00843602 495 return -errno;
7f4e0805 496
15a3e96f
LP
497 if (asprintf(&path, "/run/systemd/ask-password/sck.%" PRIx64, random_u64()) < 0)
498 return -ENOMEM;
499
500 salen = sockaddr_un_set_path(&sa.un, path);
501 if (salen < 0)
502 return salen;
7f4e0805 503
5c0d398d 504 RUN_WITH_UMASK(0177) {
15a3e96f 505 if (bind(fd, &sa.sa, salen) < 0)
00843602 506 return -errno;
5c0d398d 507 }
1d6702e8 508
2ff48e98
LP
509 r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true);
510 if (r < 0)
511 return r;
7f4e0805 512
15a3e96f
LP
513 *ret = TAKE_PTR(path);
514 return TAKE_FD(fd);
7f4e0805
LP
515}
516
7f4e0805
LP
517int ask_password_agent(
518 const char *message,
519 const char *icon,
9fa1de96 520 const char *id,
e287086b 521 const char *keyname,
7f4e0805 522 usec_t until,
e287086b
LP
523 AskPasswordFlags flags,
524 char ***ret) {
7f4e0805
LP
525
526 enum {
527 FD_SOCKET,
528 FD_SIGNAL,
c7b7d74e 529 FD_INOTIFY,
7f4e0805
LP
530 _FD_MAX
531 };
532
c7b7d74e 533 _cleanup_close_ int socket_fd = -1, signal_fd = -1, notify = -1, fd = -1;
2b583ce6 534 char temp[] = "/run/systemd/ask-password/tmp.XXXXXX";
7f4e0805 535 char final[sizeof(temp)] = "";
981e4cd3 536 _cleanup_free_ char *socket_name = NULL;
c7b7d74e 537 _cleanup_strv_free_erase_ char **l = NULL;
e287086b 538 _cleanup_fclose_ FILE *f = NULL;
7f4e0805 539 struct pollfd pollfd[_FD_MAX];
e287086b 540 sigset_t mask, oldmask;
981e4cd3 541 int r;
7f4e0805 542
e287086b 543 assert(ret);
21bc923a 544
e287086b
LP
545 if (flags & ASK_PASSWORD_NO_AGENT)
546 return -EUNATCH;
00843602 547
72c0a2c2
LP
548 assert_se(sigemptyset(&mask) >= 0);
549 assert_se(sigset_add_many(&mask, SIGINT, SIGTERM, -1) >= 0);
550 assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) >= 0);
f9b72cd8 551
00843602 552 (void) mkdir_p_label("/run/systemd/ask-password", 0755);
7f4e0805 553
c7b7d74e
XF
554 if ((flags & ASK_PASSWORD_ACCEPT_CACHED) && keyname) {
555 r = ask_password_keyring(keyname, flags, ret);
556 if (r >= 0) {
557 r = 0;
558 goto finish;
559 } else if (r != -ENOKEY)
560 goto finish;
561
562 notify = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
563 if (notify < 0) {
564 r = -errno;
565 goto finish;
566 }
567 if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_ATTRIB /* for mtime */) < 0) {
568 r = -errno;
569 goto finish;
570 }
571 }
572
646853bd 573 fd = mkostemp_safe(temp);
1d6702e8 574 if (fd < 0) {
709f6e46 575 r = fd;
7f4e0805
LP
576 goto finish;
577 }
578
00843602 579 (void) fchmod(fd, 0644);
7f4e0805 580
981e4cd3
LP
581 f = fdopen(fd, "w");
582 if (!f) {
00843602 583 r = -errno;
7f4e0805
LP
584 goto finish;
585 }
586
587 fd = -1;
588
981e4cd3
LP
589 signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
590 if (signal_fd < 0) {
00843602 591 r = -errno;
7f4e0805
LP
592 goto finish;
593 }
594
981e4cd3
LP
595 socket_fd = create_socket(&socket_name);
596 if (socket_fd < 0) {
7f4e0805
LP
597 r = socket_fd;
598 goto finish;
599 }
600
601 fprintf(f,
602 "[Ask]\n"
de0671ee 603 "PID="PID_FMT"\n"
7f4e0805 604 "Socket=%s\n"
21bc923a 605 "AcceptCached=%i\n"
64845bdc 606 "Echo=%i\n"
de0671ee 607 "NotAfter="USEC_FMT"\n",
df0ff127 608 getpid_cached(),
7f4e0805 609 socket_name,
e287086b
LP
610 (flags & ASK_PASSWORD_ACCEPT_CACHED) ? 1 : 0,
611 (flags & ASK_PASSWORD_ECHO) ? 1 : 0,
de0671ee 612 until);
7f4e0805
LP
613
614 if (message)
615 fprintf(f, "Message=%s\n", message);
616
617 if (icon)
618 fprintf(f, "Icon=%s\n", icon);
619
9fa1de96
DH
620 if (id)
621 fprintf(f, "Id=%s\n", id);
622
dacd6cee 623 r = fflush_and_check(f);
00843602 624 if (r < 0)
7f4e0805 625 goto finish;
7f4e0805
LP
626
627 memcpy(final, temp, sizeof(temp));
628
629 final[sizeof(final)-11] = 'a';
630 final[sizeof(final)-10] = 's';
631 final[sizeof(final)-9] = 'k';
632
633 if (rename(temp, final) < 0) {
00843602 634 r = -errno;
7f4e0805
LP
635 goto finish;
636 }
637
638 zero(pollfd);
639 pollfd[FD_SOCKET].fd = socket_fd;
640 pollfd[FD_SOCKET].events = POLLIN;
641 pollfd[FD_SIGNAL].fd = signal_fd;
642 pollfd[FD_SIGNAL].events = POLLIN;
c7b7d74e
XF
643 pollfd[FD_INOTIFY].fd = notify;
644 pollfd[FD_INOTIFY].events = POLLIN;
7f4e0805
LP
645
646 for (;;) {
647 char passphrase[LINE_MAX+1];
648 struct msghdr msghdr;
649 struct iovec iovec;
650 struct ucred *ucred;
651 union {
652 struct cmsghdr cmsghdr;
653 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
654 } control;
655 ssize_t n;
656 int k;
657 usec_t t;
658
659 t = now(CLOCK_MONOTONIC);
660
7dcda352 661 if (until > 0 && until <= t) {
7f4e0805
LP
662 r = -ETIME;
663 goto finish;
664 }
665
c7b7d74e 666 k = poll(pollfd, notify >= 0 ? _FD_MAX : _FD_MAX - 1, until > 0 ? (int) ((until-t)/USEC_PER_MSEC) : -1);
981e4cd3 667 if (k < 0) {
7f4e0805
LP
668 if (errno == EINTR)
669 continue;
670
00843602 671 r = -errno;
7f4e0805
LP
672 goto finish;
673 }
674
675 if (k <= 0) {
7f4e0805
LP
676 r = -ETIME;
677 goto finish;
678 }
679
21bc923a
LP
680 if (pollfd[FD_SIGNAL].revents & POLLIN) {
681 r = -EINTR;
682 goto finish;
683 }
7f4e0805 684
c7b7d74e
XF
685 if (notify >= 0 && pollfd[FD_INOTIFY].revents != 0) {
686 (void) flush_fd(notify);
687
688 r = ask_password_keyring(keyname, flags, ret);
689 if (r >= 0) {
690 r = 0;
691 goto finish;
692 } else if (r != -ENOKEY)
693 goto finish;
694 }
695
696 if (pollfd[FD_SOCKET].revents == 0)
697 continue;
698
7f4e0805 699 if (pollfd[FD_SOCKET].revents != POLLIN) {
7f4e0805
LP
700 r = -EIO;
701 goto finish;
702 }
703
5cfa2c3d 704 iovec = IOVEC_MAKE(passphrase, sizeof(passphrase));
7f4e0805
LP
705
706 zero(control);
707 zero(msghdr);
708 msghdr.msg_iov = &iovec;
709 msghdr.msg_iovlen = 1;
710 msghdr.msg_control = &control;
711 msghdr.msg_controllen = sizeof(control);
712
981e4cd3
LP
713 n = recvmsg(socket_fd, &msghdr, 0);
714 if (n < 0) {
3742095b 715 if (IN_SET(errno, EAGAIN, EINTR))
7f4e0805
LP
716 continue;
717
00843602 718 r = -errno;
7f4e0805
LP
719 goto finish;
720 }
721
1c8da044
LP
722 cmsg_close_all(&msghdr);
723
7f4e0805 724 if (n <= 0) {
00843602 725 log_debug("Message too short");
7f4e0805
LP
726 continue;
727 }
728
729 if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
730 control.cmsghdr.cmsg_level != SOL_SOCKET ||
731 control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
732 control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
00843602 733 log_debug("Received message without credentials. Ignoring.");
7f4e0805
LP
734 continue;
735 }
736
737 ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
738 if (ucred->uid != 0) {
00843602 739 log_debug("Got request from unprivileged user. Ignoring.");
7f4e0805
LP
740 continue;
741 }
742
743 if (passphrase[0] == '+') {
e287086b 744 /* An empty message refers to the empty password */
8254a475 745 if (n == 1)
bea1a013 746 l = strv_new("");
8254a475
LP
747 else
748 l = strv_parse_nulstr(passphrase+1, n-1);
87f54463 749 explicit_bzero_safe(passphrase, n);
8254a475 750 if (!l) {
7f4e0805
LP
751 r = -ENOMEM;
752 goto finish;
753 }
754
58629001 755 if (strv_isempty(l)) {
e287086b 756 l = strv_free(l);
00843602 757 log_debug("Invalid packet");
21bc923a
LP
758 continue;
759 }
760
e287086b
LP
761 break;
762 }
21bc923a 763
e287086b 764 if (passphrase[0] == '-') {
7f4e0805
LP
765 r = -ECANCELED;
766 goto finish;
7f4e0805
LP
767 }
768
e287086b 769 log_debug("Invalid packet");
7f4e0805
LP
770 }
771
e287086b
LP
772 if (keyname)
773 (void) add_to_keyring_and_log(keyname, flags, l);
774
ae2a15bc 775 *ret = TAKE_PTR(l);
7f4e0805
LP
776 r = 0;
777
778finish:
981e4cd3 779 if (socket_name)
00843602 780 (void) unlink(socket_name);
7f4e0805 781
00843602 782 (void) unlink(temp);
7f4e0805
LP
783
784 if (final[0])
00843602 785 (void) unlink(final);
7f4e0805 786
f9b72cd8 787 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
7f4e0805
LP
788 return r;
789}
260ab287 790
00843602
LP
791int ask_password_auto(
792 const char *message,
793 const char *icon,
794 const char *id,
e287086b 795 const char *keyname,
00843602 796 usec_t until,
e287086b
LP
797 AskPasswordFlags flags,
798 char ***ret) {
00843602
LP
799
800 int r;
801
e287086b 802 assert(ret);
21bc923a 803
c7b7d74e
XF
804 if ((flags & ASK_PASSWORD_ACCEPT_CACHED) &&
805 keyname &&
806 ((flags & ASK_PASSWORD_NO_TTY) || !isatty(STDIN_FILENO)) &&
807 (flags & ASK_PASSWORD_NO_AGENT)) {
e287086b
LP
808 r = ask_password_keyring(keyname, flags, ret);
809 if (r != -ENOKEY)
810 return r;
811 }
812
c7b7d74e
XF
813 if (!(flags & ASK_PASSWORD_NO_TTY) && isatty(STDIN_FILENO))
814 return ask_password_tty(-1, message, keyname, until, flags, NULL, ret);
00843602 815
e287086b
LP
816 if (!(flags & ASK_PASSWORD_NO_AGENT))
817 return ask_password_agent(message, icon, id, keyname, until, flags, ret);
818
819 return -EUNATCH;
260ab287 820}