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