]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/ask-password-api.c
use "Out of memory." consistantly (or with "\n")
[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);
112
113 pollfd[POLL_TTY].fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO;
114 pollfd[POLL_TTY].events = POLLIN;
115 pollfd[POLL_INOTIFY].fd = notify;
116 pollfd[POLL_INOTIFY].events = POLLIN;
117
118 for (;;) {
119 char c;
120 int sleep_for = -1, k;
121 ssize_t n;
122
123 if (until > 0) {
124 usec_t y;
125
126 y = now(CLOCK_MONOTONIC);
127
128 if (y > until) {
7ded2e28 129 r = -ETIME;
7f4e0805
LP
130 goto finish;
131 }
132
133 sleep_for = (int) ((until - y) / USEC_PER_MSEC);
134 }
135
136 if (flag_file)
137 if (access(flag_file, F_OK) < 0) {
138 r = -errno;
139 goto finish;
140 }
141
142 if ((k = poll(pollfd, notify > 0 ? 2 : 1, sleep_for)) < 0) {
143
144 if (errno == EINTR)
145 continue;
146
147 r = -errno;
148 goto finish;
149 } else if (k == 0) {
ccc80078 150 r = -ETIME;
7f4e0805
LP
151 goto finish;
152 }
153
154 if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
155 flush_fd(notify);
156
157 if (pollfd[POLL_TTY].revents == 0)
158 continue;
159
160 if ((n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1)) < 0) {
161
162 if (errno == EINTR || errno == EAGAIN)
163 continue;
164
165 r = -errno;
166 goto finish;
167
168 } else if (n == 0)
169 break;
170
171 if (c == '\n')
172 break;
58fc840b 173 else if (c == 21) { /* C-u */
7f4e0805 174
58fc840b
LP
175 if (!silent_mode)
176 backspace_chars(ttyfd, p);
177 p = 0;
7f4e0805
LP
178
179 } else if (c == '\b' || c == 127) {
58fc840b
LP
180
181 if (p > 0) {
182
183 if (!silent_mode)
184 backspace_chars(ttyfd, 1);
185
7f4e0805 186 p--;
441dfe09
LP
187 } else if (!dirty && !silent_mode) {
188
189 silent_mode = true;
190
191 /* There are two ways to enter silent
192 * mode. Either by pressing backspace
193 * as first key (and only as first key),
194 * or ... */
195 if (ttyfd >= 0)
196 loop_write(ttyfd, "(no echo) ", 10, false);
197
58fc840b
LP
198 } else if (ttyfd >= 0)
199 loop_write(ttyfd, "\a", 1, false);
7f4e0805 200
58fc840b
LP
201 } else if (c == '\t' && !silent_mode) {
202
203 backspace_chars(ttyfd, p);
204 silent_mode = true;
205
441dfe09
LP
206 /* ... or by pressing TAB at any time. */
207
58fc840b
LP
208 if (ttyfd >= 0)
209 loop_write(ttyfd, "(no echo) ", 10, false);
7f4e0805
LP
210 } else {
211 passphrase[p++] = c;
212
d1676230 213 if (!silent_mode && ttyfd >= 0)
7f4e0805 214 loop_write(ttyfd, "*", 1, false);
441dfe09
LP
215
216 dirty = true;
7f4e0805
LP
217 }
218 }
219
7f4e0805
LP
220 passphrase[p] = 0;
221
222 if (!(*_passphrase = strdup(passphrase))) {
223 r = -ENOMEM;
224 goto finish;
225 }
226
227 r = 0;
228
229finish:
230 if (notify >= 0)
231 close_nointr_nofail(notify);
232
233 if (ttyfd >= 0) {
c0f9c7da
LP
234
235 if (reset_tty) {
236 loop_write(ttyfd, "\n", 1, false);
7f4e0805 237 tcsetattr(ttyfd, TCSADRAIN, &old_termios);
c0f9c7da 238 }
7f4e0805
LP
239
240 close_nointr_nofail(ttyfd);
241 }
242
243 return r;
244}
245
246static int create_socket(char **name) {
247 int fd;
248 union {
249 struct sockaddr sa;
250 struct sockaddr_un un;
251 } sa;
252 int one = 1, r;
253 char *c;
1d6702e8 254 mode_t u;
7f4e0805
LP
255
256 assert(name);
257
258 if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
259 log_error("socket() failed: %m");
260 return -errno;
261 }
262
263 zero(sa);
264 sa.un.sun_family = AF_UNIX;
2b583ce6 265 snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%llu", random_ull());
7f4e0805 266
1d6702e8
LP
267 u = umask(0177);
268 r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
269 umask(u);
270
271 if (r < 0) {
7f4e0805
LP
272 r = -errno;
273 log_error("bind() failed: %m");
274 goto fail;
275 }
276
277 if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
278 r = -errno;
279 log_error("SO_PASSCRED failed: %m");
280 goto fail;
281 }
282
283 if (!(c = strdup(sa.un.sun_path))) {
284 r = -ENOMEM;
669241a0 285 log_error("Out of memory.");
7f4e0805
LP
286 goto fail;
287 }
288
289 *name = c;
290 return fd;
291
292fail:
293 close_nointr_nofail(fd);
294
295 return r;
296}
297
7f4e0805
LP
298int ask_password_agent(
299 const char *message,
300 const char *icon,
301 usec_t until,
21bc923a
LP
302 bool accept_cached,
303 char ***_passphrases) {
7f4e0805
LP
304
305 enum {
306 FD_SOCKET,
307 FD_SIGNAL,
308 _FD_MAX
309 };
310
2b583ce6 311 char temp[] = "/run/systemd/ask-password/tmp.XXXXXX";
7f4e0805
LP
312 char final[sizeof(temp)] = "";
313 int fd = -1, r;
314 FILE *f = NULL;
315 char *socket_name = NULL;
316 int socket_fd = -1, signal_fd = -1;
f9b72cd8 317 sigset_t mask, oldmask;
7f4e0805 318 struct pollfd pollfd[_FD_MAX];
1d6702e8 319 mode_t u;
7f4e0805 320
21bc923a
LP
321 assert(_passphrases);
322
f9b72cd8
LP
323 assert_se(sigemptyset(&mask) == 0);
324 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
325 assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0);
326
d2e54fae 327 mkdir_p_label("/run/systemd/ask-password", 0755);
7f4e0805 328
1d6702e8
LP
329 u = umask(0022);
330 fd = mkostemp(temp, O_CLOEXEC|O_CREAT|O_WRONLY);
331 umask(u);
332
333 if (fd < 0) {
7f4e0805
LP
334 log_error("Failed to create password file: %m");
335 r = -errno;
336 goto finish;
337 }
338
339 fchmod(fd, 0644);
340
341 if (!(f = fdopen(fd, "w"))) {
342 log_error("Failed to allocate FILE: %m");
343 r = -errno;
344 goto finish;
345 }
346
347 fd = -1;
348
7f4e0805
LP
349 if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
350 log_error("signalfd(): %m");
351 r = -errno;
352 goto finish;
353 }
354
355 if ((socket_fd = create_socket(&socket_name)) < 0) {
356 r = socket_fd;
357 goto finish;
358 }
359
360 fprintf(f,
361 "[Ask]\n"
362 "PID=%lu\n"
363 "Socket=%s\n"
21bc923a 364 "AcceptCached=%i\n"
7f4e0805
LP
365 "NotAfter=%llu\n",
366 (unsigned long) getpid(),
367 socket_name,
21bc923a 368 accept_cached ? 1 : 0,
7f4e0805
LP
369 (unsigned long long) until);
370
371 if (message)
372 fprintf(f, "Message=%s\n", message);
373
374 if (icon)
375 fprintf(f, "Icon=%s\n", icon);
376
377 fflush(f);
378
379 if (ferror(f)) {
380 log_error("Failed to write query file: %m");
381 r = -errno;
382 goto finish;
383 }
384
385 memcpy(final, temp, sizeof(temp));
386
387 final[sizeof(final)-11] = 'a';
388 final[sizeof(final)-10] = 's';
389 final[sizeof(final)-9] = 'k';
390
391 if (rename(temp, final) < 0) {
392 log_error("Failed to rename query file: %m");
393 r = -errno;
394 goto finish;
395 }
396
397 zero(pollfd);
398 pollfd[FD_SOCKET].fd = socket_fd;
399 pollfd[FD_SOCKET].events = POLLIN;
400 pollfd[FD_SIGNAL].fd = signal_fd;
401 pollfd[FD_SIGNAL].events = POLLIN;
402
403 for (;;) {
404 char passphrase[LINE_MAX+1];
405 struct msghdr msghdr;
406 struct iovec iovec;
407 struct ucred *ucred;
408 union {
409 struct cmsghdr cmsghdr;
410 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
411 } control;
412 ssize_t n;
413 int k;
414 usec_t t;
415
416 t = now(CLOCK_MONOTONIC);
417
7dcda352 418 if (until > 0 && until <= t) {
7f4e0805
LP
419 log_notice("Timed out");
420 r = -ETIME;
421 goto finish;
422 }
423
7dcda352 424 if ((k = poll(pollfd, _FD_MAX, until > 0 ? (int) ((until-t)/USEC_PER_MSEC) : -1)) < 0) {
7f4e0805
LP
425
426 if (errno == EINTR)
427 continue;
428
429 log_error("poll() failed: %m");
430 r = -errno;
431 goto finish;
432 }
433
434 if (k <= 0) {
435 log_notice("Timed out");
436 r = -ETIME;
437 goto finish;
438 }
439
21bc923a
LP
440 if (pollfd[FD_SIGNAL].revents & POLLIN) {
441 r = -EINTR;
442 goto finish;
443 }
7f4e0805
LP
444
445 if (pollfd[FD_SOCKET].revents != POLLIN) {
446 log_error("Unexpected poll() event.");
447 r = -EIO;
448 goto finish;
449 }
450
451 zero(iovec);
452 iovec.iov_base = passphrase;
21bc923a 453 iovec.iov_len = sizeof(passphrase);
7f4e0805
LP
454
455 zero(control);
456 zero(msghdr);
457 msghdr.msg_iov = &iovec;
458 msghdr.msg_iovlen = 1;
459 msghdr.msg_control = &control;
460 msghdr.msg_controllen = sizeof(control);
461
462 if ((n = recvmsg(socket_fd, &msghdr, 0)) < 0) {
463
464 if (errno == EAGAIN ||
465 errno == EINTR)
466 continue;
467
468 log_error("recvmsg() failed: %m");
469 r = -errno;
470 goto finish;
471 }
472
473 if (n <= 0) {
474 log_error("Message too short");
475 continue;
476 }
477
478 if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
479 control.cmsghdr.cmsg_level != SOL_SOCKET ||
480 control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
481 control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
482 log_warning("Received message without credentials. Ignoring.");
483 continue;
484 }
485
486 ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
487 if (ucred->uid != 0) {
488 log_warning("Got request from unprivileged user. Ignoring.");
489 continue;
490 }
491
492 if (passphrase[0] == '+') {
21bc923a 493 char **l;
7f4e0805 494
8254a475
LP
495 if (n == 1)
496 l = strv_new("", NULL);
497 else
498 l = strv_parse_nulstr(passphrase+1, n-1);
499 /* An empty message refers to the empty password */
500
501 if (!l) {
7f4e0805
LP
502 r = -ENOMEM;
503 goto finish;
504 }
505
21bc923a
LP
506 if (strv_length(l) <= 0) {
507 strv_free(l);
508 log_error("Invalid packet");
509 continue;
510 }
511
512 *_passphrases = l;
513
7f4e0805
LP
514 } else if (passphrase[0] == '-') {
515 r = -ECANCELED;
516 goto finish;
517 } else {
518 log_error("Invalid packet");
519 continue;
520 }
521
522 break;
523 }
524
525 r = 0;
526
527finish:
528 if (fd >= 0)
529 close_nointr_nofail(fd);
530
531 if (socket_name) {
532 unlink(socket_name);
533 free(socket_name);
534 }
535
536 if (socket_fd >= 0)
537 close_nointr_nofail(socket_fd);
538
539 if (signal_fd >= 0)
540 close_nointr_nofail(signal_fd);
541
542 if (f)
543 fclose(f);
544
545 unlink(temp);
546
547 if (final[0])
548 unlink(final);
549
f9b72cd8
LP
550 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
551
7f4e0805
LP
552 return r;
553}
260ab287 554
21bc923a 555int ask_password_auto(const char *message, const char *icon, usec_t until, bool accept_cached, char ***_passphrases) {
260ab287 556 assert(message);
21bc923a
LP
557 assert(_passphrases);
558
559 if (isatty(STDIN_FILENO)) {
560 int r;
561 char *s = NULL, **l = NULL;
562
563 if ((r = ask_password_tty(message, until, NULL, &s)) < 0)
564 return r;
565
566 l = strv_new(s, NULL);
567 free(s);
568
569 if (!l)
570 return -ENOMEM;
571
572 *_passphrases = l;
573 return r;
260ab287 574
21bc923a
LP
575 } else
576 return ask_password_agent(message, icon, until, accept_cached, _passphrases);
260ab287 577}