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