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