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