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