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