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