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