]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/ask-password-api.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / shared / ask-password-api.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <inttypes.h>
24 #include <limits.h>
25 #include <poll.h>
26 #include <signal.h>
27 #include <stdbool.h>
28 #include <stddef.h>
29 #include <stdint.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/inotify.h>
34 #include <sys/signalfd.h>
35 #include <sys/socket.h>
36 #include <sys/stat.h>
37 #include <sys/time.h>
38 #include <sys/uio.h>
39 #include <sys/un.h>
40 #include <termios.h>
41 #include <unistd.h>
42
43 #include "alloc-util.h"
44 #include "ask-password-api.h"
45 #include "fd-util.h"
46 #include "fileio.h"
47 #include "format-util.h"
48 #include "io-util.h"
49 #include "log.h"
50 #include "macro.h"
51 #include "missing.h"
52 #include "mkdir.h"
53 #include "process-util.h"
54 #include "random-util.h"
55 #include "signal-util.h"
56 #include "socket-util.h"
57 #include "string-util.h"
58 #include "strv.h"
59 #include "terminal-util.h"
60 #include "time-util.h"
61 #include "umask-util.h"
62 #include "utf8.h"
63 #include "util.h"
64
65 #define KEYRING_TIMEOUT_USEC ((5 * USEC_PER_MINUTE) / 2)
66
67 static int lookup_key(const char *keyname, key_serial_t *ret) {
68 key_serial_t serial;
69
70 assert(keyname);
71 assert(ret);
72
73 serial = request_key("user", keyname, NULL, 0);
74 if (serial == -1)
75 return negative_errno();
76
77 *ret = serial;
78 return 0;
79 }
80
81 static int retrieve_key(key_serial_t serial, char ***ret) {
82 _cleanup_free_ char *p = NULL;
83 long m = 100, n;
84 char **l;
85
86 assert(ret);
87
88 for (;;) {
89 p = new(char, m);
90 if (!p)
91 return -ENOMEM;
92
93 n = keyctl(KEYCTL_READ, (unsigned long) serial, (unsigned long) p, (unsigned long) m, 0);
94 if (n < 0)
95 return -errno;
96
97 if (n < m)
98 break;
99
100 explicit_bzero(p, n);
101 free(p);
102 m *= 2;
103 }
104
105 l = strv_parse_nulstr(p, n);
106 if (!l)
107 return -ENOMEM;
108
109 explicit_bzero(p, n);
110
111 *ret = l;
112 return 0;
113 }
114
115 static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **passwords) {
116 _cleanup_strv_free_erase_ char **l = NULL;
117 _cleanup_free_ char *p = NULL;
118 key_serial_t serial;
119 size_t n;
120 int r;
121
122 assert(keyname);
123 assert(passwords);
124
125 if (!(flags & ASK_PASSWORD_PUSH_CACHE))
126 return 0;
127
128 r = lookup_key(keyname, &serial);
129 if (r >= 0) {
130 r = retrieve_key(serial, &l);
131 if (r < 0)
132 return r;
133 } else if (r != -ENOKEY)
134 return r;
135
136 r = strv_extend_strv(&l, passwords, true);
137 if (r <= 0)
138 return r;
139
140 r = strv_make_nulstr(l, &p, &n);
141 if (r < 0)
142 return r;
143
144 serial = add_key("user", keyname, p, n, KEY_SPEC_USER_KEYRING);
145 explicit_bzero(p, n);
146 if (serial == -1)
147 return -errno;
148
149 if (keyctl(KEYCTL_SET_TIMEOUT,
150 (unsigned long) serial,
151 (unsigned long) DIV_ROUND_UP(KEYRING_TIMEOUT_USEC, USEC_PER_SEC), 0, 0) < 0)
152 log_debug_errno(errno, "Failed to adjust timeout: %m");
153
154 log_debug("Added key to keyring as %" PRIi32 ".", serial);
155
156 return 1;
157 }
158
159 static int add_to_keyring_and_log(const char *keyname, AskPasswordFlags flags, char **passwords) {
160 int r;
161
162 assert(keyname);
163 assert(passwords);
164
165 r = add_to_keyring(keyname, flags, passwords);
166 if (r < 0)
167 return log_debug_errno(r, "Failed to add password to keyring: %m");
168
169 return 0;
170 }
171
172 int ask_password_keyring(const char *keyname, AskPasswordFlags flags, char ***ret) {
173
174 key_serial_t serial;
175 int r;
176
177 assert(keyname);
178 assert(ret);
179
180 if (!(flags & ASK_PASSWORD_ACCEPT_CACHED))
181 return -EUNATCH;
182
183 r = lookup_key(keyname, &serial);
184 if (r == -ENOSYS) /* when retrieving the distinction doesn't matter */
185 return -ENOKEY;
186 if (r < 0)
187 return r;
188
189 return retrieve_key(serial, ret);
190 }
191
192 static void backspace_chars(int ttyfd, size_t p) {
193
194 if (ttyfd < 0)
195 return;
196
197 while (p > 0) {
198 p--;
199
200 loop_write(ttyfd, "\b \b", 3, false);
201 }
202 }
203
204 int ask_password_tty(
205 const char *message,
206 const char *keyname,
207 usec_t until,
208 AskPasswordFlags flags,
209 const char *flag_file,
210 char **ret) {
211
212 struct termios old_termios, new_termios;
213 char passphrase[LINE_MAX + 1] = {}, *x;
214 size_t p = 0, codepoint = 0;
215 int r;
216 _cleanup_close_ int ttyfd = -1, notify = -1;
217 struct pollfd pollfd[2];
218 bool reset_tty = false;
219 bool dirty = false;
220 enum {
221 POLL_TTY,
222 POLL_INOTIFY
223 };
224
225 assert(ret);
226
227 if (flags & ASK_PASSWORD_NO_TTY)
228 return -EUNATCH;
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 r = -errno;
237 goto finish;
238 }
239
240 if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
241 r = -errno;
242 goto finish;
243 }
244 }
245
246 ttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC);
247 if (ttyfd >= 0) {
248
249 if (tcgetattr(ttyfd, &old_termios) < 0) {
250 r = -errno;
251 goto finish;
252 }
253
254 if (colors_enabled())
255 loop_write(ttyfd, ANSI_HIGHLIGHT, strlen(ANSI_HIGHLIGHT), false);
256 loop_write(ttyfd, message, strlen(message), false);
257 loop_write(ttyfd, " ", 1, false);
258 if (colors_enabled())
259 loop_write(ttyfd, ANSI_NORMAL, strlen(ANSI_NORMAL), false);
260
261 new_termios = old_termios;
262 new_termios.c_lflag &= ~(ICANON|ECHO);
263 new_termios.c_cc[VMIN] = 1;
264 new_termios.c_cc[VTIME] = 0;
265
266 if (tcsetattr(ttyfd, TCSADRAIN, &new_termios) < 0) {
267 r = -errno;
268 goto finish;
269 }
270
271 reset_tty = true;
272 }
273
274 zero(pollfd);
275 pollfd[POLL_TTY].fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO;
276 pollfd[POLL_TTY].events = POLLIN;
277 pollfd[POLL_INOTIFY].fd = notify;
278 pollfd[POLL_INOTIFY].events = POLLIN;
279
280 for (;;) {
281 char c;
282 int sleep_for = -1, k;
283 ssize_t n;
284
285 if (until > 0) {
286 usec_t y;
287
288 y = now(CLOCK_MONOTONIC);
289
290 if (y > until) {
291 r = -ETIME;
292 goto finish;
293 }
294
295 sleep_for = (int) ((until - y) / USEC_PER_MSEC);
296 }
297
298 if (flag_file)
299 if (access(flag_file, F_OK) < 0) {
300 r = -errno;
301 goto finish;
302 }
303
304 k = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for);
305 if (k < 0) {
306 if (errno == EINTR)
307 continue;
308
309 r = -errno;
310 goto finish;
311 } else if (k == 0) {
312 r = -ETIME;
313 goto finish;
314 }
315
316 if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0)
317 flush_fd(notify);
318
319 if (pollfd[POLL_TTY].revents == 0)
320 continue;
321
322 n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1);
323 if (n < 0) {
324 if (IN_SET(errno, EINTR, EAGAIN))
325 continue;
326
327 r = -errno;
328 goto finish;
329
330 } else if (n == 0)
331 break;
332
333 if (c == '\n')
334 break;
335 else if (c == 21) { /* C-u */
336
337 if (!(flags & ASK_PASSWORD_SILENT))
338 backspace_chars(ttyfd, p);
339 p = 0;
340
341 } else if (IN_SET(c, '\b', 127)) {
342
343 if (p > 0) {
344
345 if (!(flags & ASK_PASSWORD_SILENT))
346 backspace_chars(ttyfd, 1);
347
348 p--;
349 } else if (!dirty && !(flags & ASK_PASSWORD_SILENT)) {
350
351 flags |= ASK_PASSWORD_SILENT;
352
353 /* There are two ways to enter silent
354 * mode. Either by pressing backspace
355 * as first key (and only as first
356 * key), or ... */
357 if (ttyfd >= 0)
358 loop_write(ttyfd, "(no echo) ", 10, false);
359
360 } else if (ttyfd >= 0)
361 loop_write(ttyfd, "\a", 1, false);
362
363 } else if (c == '\t' && !(flags & ASK_PASSWORD_SILENT)) {
364
365 backspace_chars(ttyfd, p);
366 flags |= ASK_PASSWORD_SILENT;
367
368 /* ... or by pressing TAB at any time. */
369
370 if (ttyfd >= 0)
371 loop_write(ttyfd, "(no echo) ", 10, false);
372 } else {
373 if (p >= sizeof(passphrase)-1) {
374 loop_write(ttyfd, "\a", 1, false);
375 continue;
376 }
377
378 passphrase[p++] = c;
379
380 if (!(flags & ASK_PASSWORD_SILENT) && ttyfd >= 0) {
381 n = utf8_encoded_valid_unichar(passphrase + codepoint);
382 if (n >= 0) {
383 codepoint = p;
384 loop_write(ttyfd, (flags & ASK_PASSWORD_ECHO) ? &c : "*", 1, false);
385 }
386 }
387
388 dirty = true;
389 }
390
391 c = 'x';
392 }
393
394 x = strndup(passphrase, p);
395 explicit_bzero(passphrase, p);
396 if (!x) {
397 r = -ENOMEM;
398 goto finish;
399 }
400
401 if (keyname)
402 (void) add_to_keyring_and_log(keyname, flags, STRV_MAKE(x));
403
404 *ret = x;
405 r = 0;
406
407 finish:
408 if (ttyfd >= 0 && reset_tty) {
409 loop_write(ttyfd, "\n", 1, false);
410 tcsetattr(ttyfd, TCSADRAIN, &old_termios);
411 }
412
413 return r;
414 }
415
416 static int create_socket(char **name) {
417 union sockaddr_union sa = {
418 .un.sun_family = AF_UNIX,
419 };
420 _cleanup_close_ int fd = -1;
421 static const int one = 1;
422 char *c;
423 int r;
424
425 assert(name);
426
427 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
428 if (fd < 0)
429 return -errno;
430
431 snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%" PRIx64, random_u64());
432
433 RUN_WITH_UMASK(0177) {
434 if (bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
435 return -errno;
436 }
437
438 if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
439 return -errno;
440
441 c = strdup(sa.un.sun_path);
442 if (!c)
443 return -ENOMEM;
444
445 *name = c;
446
447 r = fd;
448 fd = -1;
449
450 return r;
451 }
452
453 int ask_password_agent(
454 const char *message,
455 const char *icon,
456 const char *id,
457 const char *keyname,
458 usec_t until,
459 AskPasswordFlags flags,
460 char ***ret) {
461
462 enum {
463 FD_SOCKET,
464 FD_SIGNAL,
465 _FD_MAX
466 };
467
468 _cleanup_close_ int socket_fd = -1, signal_fd = -1, fd = -1;
469 char temp[] = "/run/systemd/ask-password/tmp.XXXXXX";
470 char final[sizeof(temp)] = "";
471 _cleanup_free_ char *socket_name = NULL;
472 _cleanup_strv_free_ char **l = NULL;
473 _cleanup_fclose_ FILE *f = NULL;
474 struct pollfd pollfd[_FD_MAX];
475 sigset_t mask, oldmask;
476 int r;
477
478 assert(ret);
479
480 if (flags & ASK_PASSWORD_NO_AGENT)
481 return -EUNATCH;
482
483 assert_se(sigemptyset(&mask) >= 0);
484 assert_se(sigset_add_many(&mask, SIGINT, SIGTERM, -1) >= 0);
485 assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) >= 0);
486
487 (void) mkdir_p_label("/run/systemd/ask-password", 0755);
488
489 fd = mkostemp_safe(temp);
490 if (fd < 0) {
491 r = fd;
492 goto finish;
493 }
494
495 (void) fchmod(fd, 0644);
496
497 f = fdopen(fd, "w");
498 if (!f) {
499 r = -errno;
500 goto finish;
501 }
502
503 fd = -1;
504
505 signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
506 if (signal_fd < 0) {
507 r = -errno;
508 goto finish;
509 }
510
511 socket_fd = create_socket(&socket_name);
512 if (socket_fd < 0) {
513 r = socket_fd;
514 goto finish;
515 }
516
517 fprintf(f,
518 "[Ask]\n"
519 "PID="PID_FMT"\n"
520 "Socket=%s\n"
521 "AcceptCached=%i\n"
522 "Echo=%i\n"
523 "NotAfter="USEC_FMT"\n",
524 getpid_cached(),
525 socket_name,
526 (flags & ASK_PASSWORD_ACCEPT_CACHED) ? 1 : 0,
527 (flags & ASK_PASSWORD_ECHO) ? 1 : 0,
528 until);
529
530 if (message)
531 fprintf(f, "Message=%s\n", message);
532
533 if (icon)
534 fprintf(f, "Icon=%s\n", icon);
535
536 if (id)
537 fprintf(f, "Id=%s\n", id);
538
539 r = fflush_and_check(f);
540 if (r < 0)
541 goto finish;
542
543 memcpy(final, temp, sizeof(temp));
544
545 final[sizeof(final)-11] = 'a';
546 final[sizeof(final)-10] = 's';
547 final[sizeof(final)-9] = 'k';
548
549 if (rename(temp, final) < 0) {
550 r = -errno;
551 goto finish;
552 }
553
554 zero(pollfd);
555 pollfd[FD_SOCKET].fd = socket_fd;
556 pollfd[FD_SOCKET].events = POLLIN;
557 pollfd[FD_SIGNAL].fd = signal_fd;
558 pollfd[FD_SIGNAL].events = POLLIN;
559
560 for (;;) {
561 char passphrase[LINE_MAX+1];
562 struct msghdr msghdr;
563 struct iovec iovec;
564 struct ucred *ucred;
565 union {
566 struct cmsghdr cmsghdr;
567 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
568 } control;
569 ssize_t n;
570 int k;
571 usec_t t;
572
573 t = now(CLOCK_MONOTONIC);
574
575 if (until > 0 && until <= t) {
576 r = -ETIME;
577 goto finish;
578 }
579
580 k = poll(pollfd, _FD_MAX, until > 0 ? (int) ((until-t)/USEC_PER_MSEC) : -1);
581 if (k < 0) {
582 if (errno == EINTR)
583 continue;
584
585 r = -errno;
586 goto finish;
587 }
588
589 if (k <= 0) {
590 r = -ETIME;
591 goto finish;
592 }
593
594 if (pollfd[FD_SIGNAL].revents & POLLIN) {
595 r = -EINTR;
596 goto finish;
597 }
598
599 if (pollfd[FD_SOCKET].revents != POLLIN) {
600 r = -EIO;
601 goto finish;
602 }
603
604 zero(iovec);
605 iovec.iov_base = passphrase;
606 iovec.iov_len = sizeof(passphrase);
607
608 zero(control);
609 zero(msghdr);
610 msghdr.msg_iov = &iovec;
611 msghdr.msg_iovlen = 1;
612 msghdr.msg_control = &control;
613 msghdr.msg_controllen = sizeof(control);
614
615 n = recvmsg(socket_fd, &msghdr, 0);
616 if (n < 0) {
617 if (IN_SET(errno, EAGAIN, EINTR))
618 continue;
619
620 r = -errno;
621 goto finish;
622 }
623
624 cmsg_close_all(&msghdr);
625
626 if (n <= 0) {
627 log_debug("Message too short");
628 continue;
629 }
630
631 if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
632 control.cmsghdr.cmsg_level != SOL_SOCKET ||
633 control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
634 control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
635 log_debug("Received message without credentials. Ignoring.");
636 continue;
637 }
638
639 ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
640 if (ucred->uid != 0) {
641 log_debug("Got request from unprivileged user. Ignoring.");
642 continue;
643 }
644
645 if (passphrase[0] == '+') {
646 /* An empty message refers to the empty password */
647 if (n == 1)
648 l = strv_new("", NULL);
649 else
650 l = strv_parse_nulstr(passphrase+1, n-1);
651 explicit_bzero(passphrase, n);
652 if (!l) {
653 r = -ENOMEM;
654 goto finish;
655 }
656
657 if (strv_length(l) <= 0) {
658 l = strv_free(l);
659 log_debug("Invalid packet");
660 continue;
661 }
662
663 break;
664 }
665
666 if (passphrase[0] == '-') {
667 r = -ECANCELED;
668 goto finish;
669 }
670
671 log_debug("Invalid packet");
672 }
673
674 if (keyname)
675 (void) add_to_keyring_and_log(keyname, flags, l);
676
677 *ret = l;
678 l = NULL;
679 r = 0;
680
681 finish:
682 if (socket_name)
683 (void) unlink(socket_name);
684
685 (void) unlink(temp);
686
687 if (final[0])
688 (void) unlink(final);
689
690 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
691 return r;
692 }
693
694 int ask_password_auto(
695 const char *message,
696 const char *icon,
697 const char *id,
698 const char *keyname,
699 usec_t until,
700 AskPasswordFlags flags,
701 char ***ret) {
702
703 int r;
704
705 assert(ret);
706
707 if ((flags & ASK_PASSWORD_ACCEPT_CACHED) && keyname) {
708 r = ask_password_keyring(keyname, flags, ret);
709 if (r != -ENOKEY)
710 return r;
711 }
712
713 if (!(flags & ASK_PASSWORD_NO_TTY) && isatty(STDIN_FILENO)) {
714 char *s = NULL, **l = NULL;
715
716 r = ask_password_tty(message, keyname, until, flags, NULL, &s);
717 if (r < 0)
718 return r;
719
720 r = strv_push(&l, s);
721 if (r < 0) {
722 string_erase(s);
723 free(s);
724 return -ENOMEM;
725 }
726
727 *ret = l;
728 return 0;
729 }
730
731 if (!(flags & ASK_PASSWORD_NO_AGENT))
732 return ask_password_agent(message, icon, id, keyname, until, flags, ret);
733
734 return -EUNATCH;
735 }