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