]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/ask-password-api.c
man: document ARM root partition types
[thirdparty/systemd.git] / src / shared / ask-password-api.c
CommitLineData
7f4e0805
LP
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
5430f7f2
LP
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
7f4e0805
LP
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
5430f7f2 16 Lesser General Public License for more details.
7f4e0805 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
7f4e0805
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
d1676230 21#include <stdbool.h>
7f4e0805
LP
22#include <termios.h>
23#include <unistd.h>
24#include <sys/poll.h>
25#include <sys/inotify.h>
26#include <errno.h>
27#include <fcntl.h>
28#include <sys/socket.h>
29#include <string.h>
30#include <sys/un.h>
31#include <stddef.h>
32#include <sys/signalfd.h>
33
34#include "util.h"
49e942b2 35#include "mkdir.h"
21bc923a 36#include "strv.h"
7f4e0805
LP
37
38#include "ask-password-api.h"
39
58fc840b
LP
40static void backspace_chars(int ttyfd, size_t p) {
41
42 if (ttyfd < 0)
43 return;
44
45 while (p > 0) {
46 p--;
47
48 loop_write(ttyfd, "\b \b", 3, false);
49 }
50}
51
7f4e0805
LP
52int ask_password_tty(
53 const char *message,
54 usec_t until,
55 const char *flag_file,
56 char **_passphrase) {
57
58 struct termios old_termios, new_termios;
59 char passphrase[LINE_MAX];
60 size_t p = 0;
61 int r, ttyfd = -1, notify = -1;
62 struct pollfd pollfd[2];
63 bool reset_tty = false;
d1676230 64 bool silent_mode = false;
441dfe09 65 bool dirty = false;
7f4e0805
LP
66 enum {
67 POLL_TTY,
68 POLL_INOTIFY
69 };
70
71 assert(message);
72 assert(_passphrase);
73
74 if (flag_file) {
75 if ((notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
76 r = -errno;
77 goto finish;
78 }
79
80 if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
81 r = -errno;
82 goto finish;
83 }
84 }
85
86 if ((ttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC)) >= 0) {
87
88 if (tcgetattr(ttyfd, &old_termios) < 0) {
89 r = -errno;
90 goto finish;
91 }
92
c1072ea0 93 loop_write(ttyfd, ANSI_HIGHLIGHT_ON, sizeof(ANSI_HIGHLIGHT_ON)-1, false);
7f4e0805 94 loop_write(ttyfd, message, strlen(message), false);
bd1db33f 95 loop_write(ttyfd, " ", 1, false);
c1072ea0 96 loop_write(ttyfd, ANSI_HIGHLIGHT_OFF, sizeof(ANSI_HIGHLIGHT_OFF)-1, false);
7f4e0805
LP
97
98 new_termios = old_termios;
99 new_termios.c_lflag &= ~(ICANON|ECHO);
100 new_termios.c_cc[VMIN] = 1;
101 new_termios.c_cc[VTIME] = 0;
102
103 if (tcsetattr(ttyfd, TCSADRAIN, &new_termios) < 0) {
104 r = -errno;
105 goto finish;
106 }
107
108 reset_tty = true;
109 }
110
111 zero(pollfd);
7f4e0805
LP
112 pollfd[POLL_TTY].fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO;
113 pollfd[POLL_TTY].events = POLLIN;
114 pollfd[POLL_INOTIFY].fd = notify;
115 pollfd[POLL_INOTIFY].events = POLLIN;
116
117 for (;;) {
118 char c;
119 int sleep_for = -1, k;
120 ssize_t n;
121
122 if (until > 0) {
123 usec_t y;
124
125 y = now(CLOCK_MONOTONIC);
126
127 if (y > until) {
7ded2e28 128 r = -ETIME;
7f4e0805
LP
129 goto finish;
130 }
131
132 sleep_for = (int) ((until - y) / USEC_PER_MSEC);
133 }
134
135 if (flag_file)
136 if (access(flag_file, F_OK) < 0) {
137 r = -errno;
138 goto finish;
139 }
140
141 if ((k = poll(pollfd, notify > 0 ? 2 : 1, sleep_for)) < 0) {
142
143 if (errno == EINTR)
144 continue;
145
146 r = -errno;
147 goto finish;
148 } else if (k == 0) {
ccc80078 149 r = -ETIME;
7f4e0805
LP
150 goto finish;
151 }
152
153 if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
154 flush_fd(notify);
155
156 if (pollfd[POLL_TTY].revents == 0)
157 continue;
158
159 if ((n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1)) < 0) {
160
161 if (errno == EINTR || errno == EAGAIN)
162 continue;
163
164 r = -errno;
165 goto finish;
166
167 } else if (n == 0)
168 break;
169
170 if (c == '\n')
171 break;
58fc840b 172 else if (c == 21) { /* C-u */
7f4e0805 173
58fc840b
LP
174 if (!silent_mode)
175 backspace_chars(ttyfd, p);
176 p = 0;
7f4e0805
LP
177
178 } else if (c == '\b' || c == 127) {
58fc840b
LP
179
180 if (p > 0) {
181
182 if (!silent_mode)
183 backspace_chars(ttyfd, 1);
184
7f4e0805 185 p--;
441dfe09
LP
186 } else if (!dirty && !silent_mode) {
187
188 silent_mode = true;
189
190 /* There are two ways to enter silent
191 * mode. Either by pressing backspace
192 * as first key (and only as first key),
193 * or ... */
194 if (ttyfd >= 0)
195 loop_write(ttyfd, "(no echo) ", 10, false);
196
58fc840b
LP
197 } else if (ttyfd >= 0)
198 loop_write(ttyfd, "\a", 1, false);
7f4e0805 199
58fc840b
LP
200 } else if (c == '\t' && !silent_mode) {
201
202 backspace_chars(ttyfd, p);
203 silent_mode = true;
204
441dfe09
LP
205 /* ... or by pressing TAB at any time. */
206
58fc840b
LP
207 if (ttyfd >= 0)
208 loop_write(ttyfd, "(no echo) ", 10, false);
7f4e0805
LP
209 } else {
210 passphrase[p++] = c;
211
d1676230 212 if (!silent_mode && ttyfd >= 0)
7f4e0805 213 loop_write(ttyfd, "*", 1, false);
441dfe09
LP
214
215 dirty = true;
7f4e0805
LP
216 }
217 }
218
7f4e0805
LP
219 passphrase[p] = 0;
220
221 if (!(*_passphrase = strdup(passphrase))) {
222 r = -ENOMEM;
223 goto finish;
224 }
225
226 r = 0;
227
228finish:
03e334a1 229 safe_close(notify);
7f4e0805
LP
230
231 if (ttyfd >= 0) {
c0f9c7da
LP
232
233 if (reset_tty) {
234 loop_write(ttyfd, "\n", 1, false);
7f4e0805 235 tcsetattr(ttyfd, TCSADRAIN, &old_termios);
c0f9c7da 236 }
7f4e0805 237
03e334a1 238 safe_close(ttyfd);
7f4e0805
LP
239 }
240
241 return r;
242}
243
244static int create_socket(char **name) {
245 int fd;
246 union {
247 struct sockaddr sa;
248 struct sockaddr_un un;
b92bea5d
ZJS
249 } sa = {
250 .un.sun_family = AF_UNIX,
251 };
df28bc08
KS
252 int one = 1;
253 int r = 0;
7f4e0805
LP
254 char *c;
255
256 assert(name);
257
e62d8c39
ZJS
258 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
259 if (fd < 0) {
7f4e0805
LP
260 log_error("socket() failed: %m");
261 return -errno;
262 }
263
9bf3b535 264 snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%" PRIx64, random_u64());
7f4e0805 265
5c0d398d
LP
266 RUN_WITH_UMASK(0177) {
267 r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
268 }
1d6702e8
LP
269
270 if (r < 0) {
7f4e0805
LP
271 r = -errno;
272 log_error("bind() failed: %m");
273 goto fail;
274 }
275
276 if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
277 r = -errno;
278 log_error("SO_PASSCRED failed: %m");
279 goto fail;
280 }
281
e62d8c39
ZJS
282 c = strdup(sa.un.sun_path);
283 if (!c) {
0d0f0c50 284 r = log_oom();
7f4e0805
LP
285 goto fail;
286 }
287
288 *name = c;
289 return fd;
290
291fail:
03e334a1 292 safe_close(fd);
7f4e0805
LP
293
294 return r;
295}
296
7f4e0805
LP
297int ask_password_agent(
298 const char *message,
299 const char *icon,
300 usec_t until,
21bc923a
LP
301 bool accept_cached,
302 char ***_passphrases) {
7f4e0805
LP
303
304 enum {
305 FD_SOCKET,
306 FD_SIGNAL,
307 _FD_MAX
308 };
309
2b583ce6 310 char temp[] = "/run/systemd/ask-password/tmp.XXXXXX";
7f4e0805
LP
311 char final[sizeof(temp)] = "";
312 int fd = -1, r;
313 FILE *f = NULL;
314 char *socket_name = NULL;
315 int socket_fd = -1, signal_fd = -1;
f9b72cd8 316 sigset_t mask, oldmask;
7f4e0805
LP
317 struct pollfd pollfd[_FD_MAX];
318
21bc923a
LP
319 assert(_passphrases);
320
f9b72cd8
LP
321 assert_se(sigemptyset(&mask) == 0);
322 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
323 assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0);
324
d2e54fae 325 mkdir_p_label("/run/systemd/ask-password", 0755);
7f4e0805 326
2d5bdf5b 327 fd = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC);
1d6702e8 328 if (fd < 0) {
7f4e0805
LP
329 log_error("Failed to create password file: %m");
330 r = -errno;
331 goto finish;
332 }
333
334 fchmod(fd, 0644);
335
336 if (!(f = fdopen(fd, "w"))) {
337 log_error("Failed to allocate FILE: %m");
338 r = -errno;
339 goto finish;
340 }
341
342 fd = -1;
343
7f4e0805
LP
344 if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
345 log_error("signalfd(): %m");
346 r = -errno;
347 goto finish;
348 }
349
350 if ((socket_fd = create_socket(&socket_name)) < 0) {
351 r = socket_fd;
352 goto finish;
353 }
354
355 fprintf(f,
356 "[Ask]\n"
357 "PID=%lu\n"
358 "Socket=%s\n"
21bc923a 359 "AcceptCached=%i\n"
7f4e0805
LP
360 "NotAfter=%llu\n",
361 (unsigned long) getpid(),
362 socket_name,
21bc923a 363 accept_cached ? 1 : 0,
7f4e0805
LP
364 (unsigned long long) until);
365
366 if (message)
367 fprintf(f, "Message=%s\n", message);
368
369 if (icon)
370 fprintf(f, "Icon=%s\n", icon);
371
372 fflush(f);
373
374 if (ferror(f)) {
375 log_error("Failed to write query file: %m");
376 r = -errno;
377 goto finish;
378 }
379
380 memcpy(final, temp, sizeof(temp));
381
382 final[sizeof(final)-11] = 'a';
383 final[sizeof(final)-10] = 's';
384 final[sizeof(final)-9] = 'k';
385
386 if (rename(temp, final) < 0) {
387 log_error("Failed to rename query file: %m");
388 r = -errno;
389 goto finish;
390 }
391
392 zero(pollfd);
393 pollfd[FD_SOCKET].fd = socket_fd;
394 pollfd[FD_SOCKET].events = POLLIN;
395 pollfd[FD_SIGNAL].fd = signal_fd;
396 pollfd[FD_SIGNAL].events = POLLIN;
397
398 for (;;) {
399 char passphrase[LINE_MAX+1];
400 struct msghdr msghdr;
401 struct iovec iovec;
402 struct ucred *ucred;
403 union {
404 struct cmsghdr cmsghdr;
405 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
406 } control;
407 ssize_t n;
408 int k;
409 usec_t t;
410
411 t = now(CLOCK_MONOTONIC);
412
7dcda352 413 if (until > 0 && until <= t) {
7f4e0805
LP
414 log_notice("Timed out");
415 r = -ETIME;
416 goto finish;
417 }
418
7dcda352 419 if ((k = poll(pollfd, _FD_MAX, until > 0 ? (int) ((until-t)/USEC_PER_MSEC) : -1)) < 0) {
7f4e0805
LP
420
421 if (errno == EINTR)
422 continue;
423
424 log_error("poll() failed: %m");
425 r = -errno;
426 goto finish;
427 }
428
429 if (k <= 0) {
430 log_notice("Timed out");
431 r = -ETIME;
432 goto finish;
433 }
434
21bc923a
LP
435 if (pollfd[FD_SIGNAL].revents & POLLIN) {
436 r = -EINTR;
437 goto finish;
438 }
7f4e0805
LP
439
440 if (pollfd[FD_SOCKET].revents != POLLIN) {
441 log_error("Unexpected poll() event.");
442 r = -EIO;
443 goto finish;
444 }
445
446 zero(iovec);
447 iovec.iov_base = passphrase;
21bc923a 448 iovec.iov_len = sizeof(passphrase);
7f4e0805
LP
449
450 zero(control);
451 zero(msghdr);
452 msghdr.msg_iov = &iovec;
453 msghdr.msg_iovlen = 1;
454 msghdr.msg_control = &control;
455 msghdr.msg_controllen = sizeof(control);
456
457 if ((n = recvmsg(socket_fd, &msghdr, 0)) < 0) {
458
459 if (errno == EAGAIN ||
460 errno == EINTR)
461 continue;
462
463 log_error("recvmsg() failed: %m");
464 r = -errno;
465 goto finish;
466 }
467
468 if (n <= 0) {
469 log_error("Message too short");
470 continue;
471 }
472
473 if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
474 control.cmsghdr.cmsg_level != SOL_SOCKET ||
475 control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
476 control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
477 log_warning("Received message without credentials. Ignoring.");
478 continue;
479 }
480
481 ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
482 if (ucred->uid != 0) {
483 log_warning("Got request from unprivileged user. Ignoring.");
484 continue;
485 }
486
487 if (passphrase[0] == '+') {
21bc923a 488 char **l;
7f4e0805 489
8254a475
LP
490 if (n == 1)
491 l = strv_new("", NULL);
492 else
493 l = strv_parse_nulstr(passphrase+1, n-1);
494 /* An empty message refers to the empty password */
495
496 if (!l) {
7f4e0805
LP
497 r = -ENOMEM;
498 goto finish;
499 }
500
21bc923a
LP
501 if (strv_length(l) <= 0) {
502 strv_free(l);
503 log_error("Invalid packet");
504 continue;
505 }
506
507 *_passphrases = l;
508
7f4e0805
LP
509 } else if (passphrase[0] == '-') {
510 r = -ECANCELED;
511 goto finish;
512 } else {
513 log_error("Invalid packet");
514 continue;
515 }
516
517 break;
518 }
519
520 r = 0;
521
522finish:
03e334a1 523 safe_close(fd);
7f4e0805
LP
524
525 if (socket_name) {
526 unlink(socket_name);
527 free(socket_name);
528 }
529
03e334a1
LP
530 safe_close(socket_fd);
531 safe_close(signal_fd);
7f4e0805
LP
532
533 if (f)
534 fclose(f);
535
536 unlink(temp);
537
538 if (final[0])
539 unlink(final);
540
f9b72cd8
LP
541 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
542
7f4e0805
LP
543 return r;
544}
260ab287 545
21bc923a 546int ask_password_auto(const char *message, const char *icon, usec_t until, bool accept_cached, char ***_passphrases) {
260ab287 547 assert(message);
21bc923a
LP
548 assert(_passphrases);
549
550 if (isatty(STDIN_FILENO)) {
551 int r;
552 char *s = NULL, **l = NULL;
553
554 if ((r = ask_password_tty(message, until, NULL, &s)) < 0)
555 return r;
556
557 l = strv_new(s, NULL);
558 free(s);
559
560 if (!l)
561 return -ENOMEM;
562
563 *_passphrases = l;
564 return r;
260ab287 565
21bc923a
LP
566 } else
567 return ask_password_agent(message, icon, until, accept_cached, _passphrases);
260ab287 568}