• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 13867348974

14 Mar 2025 10:27PM UTC coverage: 71.757% (-0.1%) from 71.9%
13867348974

push

github

yuwata
journal-remote: added custom headers support

54 of 67 new or added lines in 3 files covered. (80.6%)

1306 existing lines in 39 files now uncovered.

295405 of 411675 relevant lines covered (71.76%)

715677.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

36.03
/src/shared/ask-password-api.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <errno.h>
4
#include <fcntl.h>
5
#include <inttypes.h>
6
#include <limits.h>
7
#include <stdbool.h>
8
#include <stddef.h>
9
#include <stdint.h>
10
#include <stdio.h>
11
#include <stdlib.h>
12
#include <sys/inotify.h>
13
#include <sys/signalfd.h>
14
#include <sys/stat.h>
15
#include <sys/time.h>
16
#include <sys/uio.h>
17
#include <sys/un.h>
18
#include <termios.h>
19
#include <unistd.h>
20

21
#include "alloc-util.h"
22
#include "ansi-color.h"
23
#include "ask-password-api.h"
24
#include "creds-util.h"
25
#include "env-util.h"
26
#include "fd-util.h"
27
#include "fileio.h"
28
#include "format-util.h"
29
#include "fs-util.h"
30
#include "glyph-util.h"
31
#include "inotify-util.h"
32
#include "io-util.h"
33
#include "iovec-util.h"
34
#include "keyring-util.h"
35
#include "log.h"
36
#include "macro.h"
37
#include "memory-util.h"
38
#include "missing_syscall.h"
39
#include "mkdir-label.h"
40
#include "nulstr-util.h"
41
#include "path-lookup.h"
42
#include "plymouth-util.h"
43
#include "process-util.h"
44
#include "random-util.h"
45
#include "signal-util.h"
46
#include "socket-util.h"
47
#include "string-table.h"
48
#include "string-util.h"
49
#include "strv.h"
50
#include "terminal-util.h"
51
#include "time-util.h"
52
#include "tmpfile-util.h"
53
#include "umask-util.h"
54
#include "utf8.h"
55

56
#define KEYRING_TIMEOUT_USEC ((5 * USEC_PER_MINUTE) / 2)
57

58
static const char* keyring_table[] = {
59
        [-KEY_SPEC_THREAD_KEYRING]       = "thread",
60
        [-KEY_SPEC_PROCESS_KEYRING]      = "process",
61
        [-KEY_SPEC_SESSION_KEYRING]      = "session",
62
        [-KEY_SPEC_USER_KEYRING]         = "user",
63
        [-KEY_SPEC_USER_SESSION_KEYRING] = "user-session",
64
        [-KEY_SPEC_GROUP_KEYRING]        = "group",
65
};
66

67
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(keyring, int);
×
68

69
static int lookup_key(const char *keyname, key_serial_t *ret) {
4✔
70
        key_serial_t serial;
4✔
71

72
        assert(keyname);
4✔
73
        assert(ret);
4✔
74

75
        serial = request_key("user", keyname, /* callout_info= */ NULL, /* dest_keyring= */ 0);
4✔
76
        if (serial == -1)
4✔
77
                return negative_errno();
4✔
78

79
        *ret = serial;
×
80
        return 0;
×
81
}
82

83
static int retrieve_key(key_serial_t serial, char ***ret) {
×
84
        _cleanup_(erase_and_freep) void *p = NULL;
×
85
        char **l;
×
86
        size_t n;
×
87
        int r;
×
88

89
        assert(ret);
×
90

91
        r = keyring_read(serial, &p, &n);
×
92
        if (r < 0)
×
93
                return r;
94

95
        l = strv_parse_nulstr(p, n);
×
96
        if (!l)
×
97
                return -ENOMEM;
98

99
        *ret = l;
×
100
        return 0;
×
101
}
102

103
static int get_ask_password_directory_for_flags(AskPasswordFlags flags, char **ret) {
1✔
104
        if (FLAGS_SET(flags, ASK_PASSWORD_USER))
1✔
105
                return acquire_user_ask_password_directory(ret);
×
106

107
        return strdup_to_full(ret, "/run/systemd/ask-password/"); /* Returns 1, indicating there's a suitable directory */
1✔
108
}
109

110
static int touch_ask_password_directory(AskPasswordFlags flags) {
×
111
        int r;
×
112

113
        _cleanup_free_ char *p = NULL;
×
114
        r = get_ask_password_directory_for_flags(flags, &p);
×
115
        if (r <= 0)
×
116
                return r;
117

118
        _cleanup_close_ int fd = open_mkdir(p, O_CLOEXEC, 0755);
×
119
        if (fd < 0)
×
120
                return fd;
121

122
        r = touch_fd(fd, USEC_INFINITY);
×
123
        if (r < 0)
×
124
                return r;
×
125

126
        return 1; /* did something */
127
}
128

129
static usec_t keyring_cache_timeout(void) {
×
130
        static usec_t saved_timeout = KEYRING_TIMEOUT_USEC;
×
131
        static bool saved_timeout_set = false;
×
132
        int r;
×
133

134
        if (saved_timeout_set)
×
135
                return saved_timeout;
×
136

137
        const char *e = secure_getenv("SYSTEMD_ASK_PASSWORD_KEYRING_TIMEOUT_SEC");
×
138
        if (e) {
×
139
                r = parse_sec(e, &saved_timeout);
×
140
                if (r < 0)
×
141
                        log_debug_errno(r, "Invalid value in $SYSTEMD_ASK_PASSWORD_KEYRING_TIMEOUT_SEC, ignoring: %s", e);
×
142
        }
143

144
        saved_timeout_set = true;
×
145

146
        return saved_timeout;
×
147
}
148

149
static key_serial_t keyring_cache_type(void) {
×
150
        static key_serial_t saved_keyring = KEY_SPEC_USER_KEYRING;
×
151
        static bool saved_keyring_set = false;
×
152
        int r;
×
153

154
        if (saved_keyring_set)
×
155
                return saved_keyring;
×
156

157
        const char *e = secure_getenv("SYSTEMD_ASK_PASSWORD_KEYRING_TYPE");
×
158
        if (e) {
×
159
                key_serial_t keyring;
×
160

161
                r = safe_atoi32(e, &keyring);
×
162
                if (r >= 0)
×
163
                        if (keyring < 0)
×
164
                                log_debug_errno(keyring, "Invalid value in $SYSTEMD_ASK_PASSWORD_KEYRING_TYPE, ignoring: %s", e);
×
165
                        else
166
                                saved_keyring = keyring;
×
167
                else {
168
                        keyring = keyring_from_string(e);
×
169
                        if (keyring < 0)
×
170
                                log_debug_errno(keyring, "Invalid value in $SYSTEMD_ASK_PASSWORD_KEYRING_TYPE, ignoring: %s", e);
×
171
                        else
172
                                saved_keyring = -keyring;
×
173
                }
174
        }
175

176
        saved_keyring_set = true;
×
177

178
        return saved_keyring;
×
179
}
180

181
static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **passwords) {
×
182
        _cleanup_strv_free_erase_ char **l = NULL;
×
183
        _cleanup_(erase_and_freep) char *p = NULL;
×
184
        key_serial_t serial;
×
185
        size_t n;
×
186
        int r;
×
187

188
        assert(keyname);
×
189

190
        if (!FLAGS_SET(flags, ASK_PASSWORD_PUSH_CACHE) || keyring_cache_timeout() == 0)
×
191
                return 0;
×
192
        if (strv_isempty(passwords))
×
193
                return 0;
194

195
        r = lookup_key(keyname, &serial);
×
196
        if (r >= 0) {
×
197
                r = retrieve_key(serial, &l);
×
198
                if (r < 0)
×
199
                        return r;
200
        } else if (r != -ENOKEY)
×
201
                return r;
202

203
        r = strv_extend_strv(&l, passwords, /* filter_duplicates= */ true);
×
204
        if (r <= 0)
×
205
                return r;
206

207
        r = strv_make_nulstr(l, &p, &n);
×
208
        if (r < 0)
×
209
                return r;
210

211
        /* chop off the final NUL byte. We do this because we want to use the separator NUL bytes only if we
212
         * have multiple passwords. */
213
        n = LESS_BY(n, (size_t) 1);
×
214

215
        serial = add_key("user", keyname, p, n, keyring_cache_type());
×
216
        if (serial == -1)
×
217
                return -errno;
×
218

219
        if (keyring_cache_timeout() != USEC_INFINITY &&
×
220
                keyctl(KEYCTL_SET_TIMEOUT,
×
221
                       (unsigned long) serial,
222
                       (unsigned long) DIV_ROUND_UP(keyring_cache_timeout(), USEC_PER_SEC), 0, 0) < 0)
×
223
                log_debug_errno(errno, "Failed to adjust kernel keyring key timeout: %m");
×
224

225
        /* Tell everyone to check the keyring */
226
        (void) touch_ask_password_directory(flags);
×
227

228
        log_debug("Added key to kernel keyring as %" PRIi32 ".", serial);
×
229

230
        return 1;
231
}
232

233
static int add_to_keyring_and_log(const char *keyname, AskPasswordFlags flags, char **passwords) {
×
234
        int r;
×
235

236
        assert(keyname);
×
237

238
        r = add_to_keyring(keyname, flags, passwords);
×
239
        if (r < 0)
×
240
                return log_debug_errno(r, "Failed to add password to kernel keyring: %m");
×
241

242
        return 0;
243
}
244

245
static int ask_password_keyring(const AskPasswordRequest *req, AskPasswordFlags flags, char ***ret) {
4✔
246
        key_serial_t serial;
4✔
247
        int r;
4✔
248

249
        assert(req);
4✔
250
        assert(ret);
4✔
251

252
        if (!FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED))
4✔
253
                return -EUNATCH;
4✔
254

255
        r = lookup_key(req->keyring, &serial);
4✔
256
        if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || r == -EPERM)
8✔
257
                /* When retrieving, the distinction between "kernel or container manager don't support or
258
                 * allow this" and "no matching key known" doesn't matter. Note that we propagate EACCESS
259
                 * here (even if EPERM not) since that is used if the keyring is available, but we lack
260
                 * access to the key. */
261
                return -ENOKEY;
262
        if (r < 0)
4✔
263
                return r;
264

265
        _cleanup_strv_free_erase_ char **l = NULL;
×
266
        r = retrieve_key(serial, &l);
×
267
        if (r < 0)
×
268
                return r;
269

270
        if (strv_isempty(l))
×
271
                return log_debug_errno(SYNTHETIC_ERRNO(ENOKEY), "Found an empty password from keyring.");
×
272

273
        *ret = TAKE_PTR(l);
×
274
        return 0;
×
275
}
276

277
static int backspace_chars(int ttyfd, size_t p) {
×
278
        if (ttyfd < 0)
×
279
                return 0;
×
280

281
        _cleanup_free_ char *buf = malloc_multiply(3, p);
×
282
        if (!buf)
×
283
                return log_oom();
×
284

285
        for (size_t i = 0; i < p; i++)
×
286
                memcpy(buf + 3 * i, "\b \b", 3);
×
287

288
        return loop_write(ttyfd, buf, 3 * p);
×
289
}
290

291
static int backspace_string(int ttyfd, const char *str) {
×
292
        assert(str);
×
293

294
        /* Backspaces through enough characters to entirely undo printing of the specified string. */
295

296
        if (ttyfd < 0)
×
297
                return 0;
298

299
        size_t m = utf8_n_codepoints(str);
×
300
        if (m == SIZE_MAX)
×
301
                m = strlen(str); /* Not a valid UTF-8 string? If so, let's backspace the number of bytes
×
302
                                  * output. Most likely this happened because we are not in a UTF-8 locale,
303
                                  * and in that case that is the correct thing to do. And even if it's not,
304
                                  * terminals tend to stop backspacing at the leftmost column, hence
305
                                  * backspacing too much should be mostly OK. */
306

307
        return backspace_chars(ttyfd, m);
×
308
}
309

310
int ask_password_plymouth(
×
311
                const AskPasswordRequest *req,
312
                AskPasswordFlags flags,
313
                char ***ret) {
314

315
        _cleanup_close_ int fd = -EBADF, inotify_fd = -EBADF;
×
316
        _cleanup_free_ char *packet = NULL;
×
317
        ssize_t k;
×
318
        int r, n;
×
319
        char buffer[LINE_MAX];
×
320
        size_t p = 0;
×
321

322
        assert(req);
×
323
        assert(ret);
×
324

325
        if (FLAGS_SET(flags, ASK_PASSWORD_HEADLESS))
×
326
                return -ENOEXEC;
327

328
        const char *message = req->message ?: "Password:";
×
329

330
        if (req->flag_file) {
×
331
                inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
×
332
                if (inotify_fd < 0)
×
333
                        return -errno;
×
334

335
                if (inotify_add_watch(inotify_fd, req->flag_file, IN_ATTRIB) < 0) /* for the link count */
×
336
                        return -errno;
×
337
        }
338

339
        fd = plymouth_connect(SOCK_NONBLOCK);
×
340
        if (fd < 0)
×
341
                return fd;
342

343
        if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED)) {
×
344
                packet = strdup("c");
×
345
                n = 1;
×
346
        } else if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
×
347
                packet = NULL;
×
348
        if (!packet)
×
349
                return -ENOMEM;
350

351
        r = loop_write_full(fd, packet, n + 1, USEC_INFINITY);
×
352
        if (r < 0)
×
353
                return r;
354

355
        CLEANUP_ERASE(buffer);
×
356

357
        enum {
×
358
                POLL_SOCKET,
359
                POLL_TWO,
360
                POLL_THREE,
361
                _POLL_MAX,
362
        };
363

364
        struct pollfd pollfd[_POLL_MAX] = {
×
365
                [POLL_SOCKET] = {
366
                        .fd = fd,
367
                        .events = POLLIN,
368
                },
369
        };
370
        size_t n_pollfd = POLL_SOCKET + 1, inotify_idx = SIZE_MAX, hup_fd_idx = SIZE_MAX;
×
371
        if (inotify_fd >= 0)
×
372
                pollfd[inotify_idx = n_pollfd++] = (struct pollfd) {
×
373
                        .fd = inotify_fd,
374
                        .events = POLLIN,
375
                };
376
        if (req->hup_fd >= 0)
×
377
                pollfd[hup_fd_idx = n_pollfd++] = (struct pollfd) {
×
378
                        .fd = req->hup_fd,
379
                        .events = POLLHUP,
380
                };
381

382
        assert(n_pollfd <= _POLL_MAX);
×
383

384
        for (;;) {
×
385
                usec_t timeout;
×
386

387
                if (req->until > 0)
×
388
                        timeout = usec_sub_unsigned(req->until, now(CLOCK_MONOTONIC));
×
389
                else
390
                        timeout = USEC_INFINITY;
391

392
                if (req->flag_file && access(req->flag_file, F_OK) < 0)
×
393
                        return -errno;
×
394

395
                r = ppoll_usec(pollfd, n_pollfd, timeout);
×
396
                if (r == -EINTR)
×
397
                        continue;
×
398
                if (r < 0)
×
399
                        return r;
400
                if (r == 0)
×
401
                        return -ETIME;
402

403
                if (req->hup_fd >= 0 && pollfd[hup_fd_idx].revents & POLLHUP)
×
404
                        return -ECONNRESET;
405

406
                if (inotify_fd >= 0 && pollfd[inotify_idx].revents != 0)
×
407
                        (void) flush_fd(inotify_fd);
×
408

409
                if (pollfd[POLL_SOCKET].revents == 0)
×
410
                        continue;
×
411

412
                k = read(fd, buffer + p, sizeof(buffer) - p);
×
413
                if (k < 0) {
×
414
                        if (ERRNO_IS_TRANSIENT(errno))
×
415
                                continue;
×
416

417
                        return -errno;
×
418
                }
419
                if (k == 0)
×
420
                        return -EIO;
421

422
                p += k;
×
423

424
                if (buffer[0] == 5) {
×
425

426
                        if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED)) {
×
427
                                /* Hmm, first try with cached
428
                                 * passwords failed, so let's retry
429
                                 * with a normal password request */
430
                                packet = mfree(packet);
×
431

432
                                if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
×
433
                                        return -ENOMEM;
434

435
                                r = loop_write_full(fd, packet, n + 1, USEC_INFINITY);
×
436
                                if (r < 0)
×
437
                                        return r;
438

439
                                flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
×
440
                                p = 0;
×
441
                                continue;
×
442
                        }
443

444
                        /* No password, because UI not shown */
445
                        return -ENOENT;
446

447
                } else if (IN_SET(buffer[0], 2, 9)) {
×
448
                        _cleanup_strv_free_erase_ char **l = NULL;
×
449
                        uint32_t size;
×
450

451
                        /* One or more answers */
452
                        if (p < 5)
×
453
                                continue;
×
454

455
                        memcpy(&size, buffer+1, sizeof(size));
×
456
                        size = le32toh(size);
×
457
                        if (size + 5 > sizeof(buffer))
×
458
                                return -EIO;
459

460
                        if (p-5 < size)
×
461
                                continue;
×
462

463
                        l = strv_parse_nulstr(buffer + 5, size);
×
464
                        if (!l)
×
465
                                return -ENOMEM;
466

467
                        if (strv_isempty(l))
×
468
                                return log_debug_errno(SYNTHETIC_ERRNO(ECANCELED), "Received an empty password.");
×
469

470
                        *ret = TAKE_PTR(l);
×
471
                        return 0;
×
472

473
                } else
474
                        /* Unknown packet */
475
                        return -EIO;
476
        }
477
}
478

479
#define NO_ECHO "(no echo) "
480
#define PRESS_TAB "(press TAB for no echo) "
481
#define SKIPPED "(skipped)"
482

483
int ask_password_tty(
1✔
484
                const AskPasswordRequest *req,
485
                AskPasswordFlags flags,
486
                char ***ret) {
487

488
        bool reset_tty = false, dirty = false, use_color = false, press_tab_visible = false;
1✔
489
        _cleanup_close_ int cttyfd = -EBADF, inotify_fd = -EBADF;
1✔
490
        struct termios old_termios, new_termios;
1✔
491
        char passphrase[LINE_MAX + 1] = {}, *x;
1✔
492
        _cleanup_strv_free_erase_ char **l = NULL;
1✔
493
        size_t p = 0, codepoint = 0;
1✔
494
        int r;
1✔
495

496
        assert(req);
1✔
497
        assert(ret);
1✔
498

499
        if (FLAGS_SET(flags, ASK_PASSWORD_HEADLESS))
1✔
500
                return -ENOEXEC;
501

502
        if (FLAGS_SET(flags, ASK_PASSWORD_NO_TTY))
1✔
503
                return -EUNATCH;
504

505
        const char *message = req->message ?: "Password:";
1✔
506
        const char *keyring = req->keyring;
1✔
507

508
        if (!FLAGS_SET(flags, ASK_PASSWORD_HIDE_EMOJI) && emoji_enabled())
1✔
509
                message = strjoina(special_glyph(SPECIAL_GLYPH_LOCK_AND_KEY), " ", message);
×
510

511
        if (req->flag_file || (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && keyring)) {
1✔
UNCOV
512
                inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
×
UNCOV
513
                if (inotify_fd < 0)
×
514
                        return -errno;
×
515
        }
516
        if (req->flag_file)
1✔
UNCOV
517
                if (inotify_add_watch(inotify_fd, req->flag_file, IN_ATTRIB /* for the link count */) < 0)
×
UNCOV
518
                        return -errno;
×
519
        if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && keyring) {
1✔
520
                r = ask_password_keyring(req, flags, ret);
×
521
                if (r >= 0)
×
522
                        return 0;
×
523
                if (r != -ENOKEY)
×
524
                        return r;
525

526
                /* Let's watch the askpw directory for mtime changes, which we issue above whenever the
527
                 * keyring changes */
528
                _cleanup_free_ char *watch_path = NULL;
×
529
                r = get_ask_password_directory_for_flags(flags, &watch_path);
×
530
                if (r < 0)
×
531
                        return r;
532
                if (r > 0) {
×
533
                        _cleanup_close_ int watch_fd = open_mkdir(watch_path, O_CLOEXEC|O_RDONLY, 0755);
×
534
                        if (watch_fd < 0)
×
535
                                return watch_fd;
536

537
                        r = inotify_add_watch_fd(inotify_fd, watch_fd, IN_ONLYDIR|IN_ATTRIB /* for mtime */);
×
538
                        if (r < 0)
×
539
                                return r;
540
                }
541
        }
542

543
        CLEANUP_ERASE(passphrase);
1✔
544

545
        /* If the caller didn't specify a TTY, then use the controlling tty, if we can. */
546
        int ttyfd;
1✔
547
        if (req->tty_fd < 0)
1✔
548
                ttyfd = cttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC);
1✔
549
        else
550
                ttyfd = req->tty_fd;
551

552
        if (ttyfd >= 0) {
1✔
553
                if (tcgetattr(ttyfd, &old_termios) < 0)
×
554
                        return -errno;
×
555

556
                if (FLAGS_SET(flags, ASK_PASSWORD_CONSOLE_COLOR))
×
557
                        use_color = dev_console_colors_enabled();
×
558
                else
559
                        use_color = colors_enabled();
×
560

561
                if (use_color)
×
562
                        (void) loop_write(ttyfd, ANSI_HIGHLIGHT, SIZE_MAX);
×
563

564
                (void) loop_write(ttyfd, message, SIZE_MAX);
×
565
                (void) loop_write(ttyfd, " ", 1);
×
566

567
                if (!FLAGS_SET(flags, ASK_PASSWORD_SILENT) && !FLAGS_SET(flags, ASK_PASSWORD_ECHO)) {
×
568
                        if (use_color)
×
569
                                (void) loop_write(ttyfd, ansi_grey(), SIZE_MAX);
×
570

571
                        (void) loop_write(ttyfd, PRESS_TAB, SIZE_MAX);
×
572
                        press_tab_visible = true;
573
                }
574

575
                if (use_color)
×
576
                        (void) loop_write(ttyfd, ANSI_NORMAL, SIZE_MAX);
×
577

578
                new_termios = old_termios;
×
579
                termios_disable_echo(&new_termios);
×
580

581
                r = RET_NERRNO(tcsetattr(ttyfd, TCSADRAIN, &new_termios));
×
582
                if (r < 0)
×
583
                        goto finish;
×
584

585
                reset_tty = true;
586
        }
587

588
        enum {
1✔
589
                POLL_TTY,
590
                POLL_TWO,
591
                POLL_THREE,
592
                _POLL_MAX,
593
        };
594

595
        struct pollfd pollfd[_POLL_MAX] = {
1✔
596
                [POLL_TTY]     = {
597
                        .fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO,
1✔
598
                        .events = POLLIN,
599
                },
600
        };
601
        size_t n_pollfd = POLL_TTY + 1, inotify_idx = SIZE_MAX, hup_fd_idx = SIZE_MAX;
1✔
602

603
        if (inotify_fd >= 0)
1✔
604
                pollfd[inotify_idx = n_pollfd++] = (struct pollfd) {
×
605
                        .fd = inotify_fd,
606
                        .events = POLLIN,
607
                };
608
        if (req->hup_fd >= 0)
1✔
609
                pollfd[hup_fd_idx = n_pollfd++] = (struct pollfd) {
×
610
                        .fd = req->hup_fd,
611
                        .events = POLLHUP,
612
                };
613

614
        assert(n_pollfd <= _POLL_MAX);
1✔
615

616
        for (;;) {
1✔
617
                _cleanup_(erase_char) char c;
×
618
                usec_t timeout;
1✔
619
                ssize_t n;
1✔
620

621
                if (req->until > 0)
1✔
622
                        timeout = usec_sub_unsigned(req->until, now(CLOCK_MONOTONIC));
1✔
623
                else
624
                        timeout = USEC_INFINITY;
625

626
                if (req->flag_file) {
1✔
627
                        r = RET_NERRNO(access(req->flag_file, F_OK));
×
628
                        if (r < 0)
×
629
                                goto finish;
×
630
                }
631

632
                r = ppoll_usec(pollfd, n_pollfd, timeout);
1✔
633
                if (r == -EINTR)
1✔
634
                        continue;
×
635
                if (r < 0)
1✔
636
                        goto finish;
×
637
                if (r == 0) {
1✔
638
                        r = -ETIME;
×
639
                        goto finish;
×
640
                }
641

642
                if (req->hup_fd >= 0 && pollfd[hup_fd_idx].revents & POLLHUP) {
1✔
643
                        r = -ECONNRESET;
×
644
                        goto finish;
×
645
                }
646

647
                if (inotify_fd >= 0 && pollfd[inotify_idx].revents != 0 && keyring) {
1✔
648
                        (void) flush_fd(inotify_fd);
×
649

650
                        r = ask_password_keyring(req, flags, ret);
×
651
                        if (r >= 0) {
×
652
                                r = 0;
×
653
                                goto finish;
×
654
                        } else if (r != -ENOKEY)
×
655
                                goto finish;
×
656
                }
657

658
                if (pollfd[POLL_TTY].revents == 0)
1✔
659
                        continue;
×
660

661
                n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1);
1✔
662
                if (n < 0) {
1✔
663
                        if (ERRNO_IS_TRANSIENT(errno))
×
664
                                continue;
×
665

666
                        r = -errno;
×
667
                        goto finish;
×
668

669
                }
670

671
                if (press_tab_visible) {
1✔
672
                        assert(ttyfd >= 0);
×
673
                        backspace_chars(ttyfd, strlen(PRESS_TAB));
×
674
                        press_tab_visible = false;
675
                }
676

677
                /* We treat EOF, newline and NUL byte all as valid end markers */
678
                if (n == 0 || c == '\n' || c == 0)
1✔
679
                        break;
680

681
                if (c == 4) { /* C-d also known as EOT */
×
682
                        if (ttyfd >= 0)
×
683
                                (void) loop_write(ttyfd, SKIPPED, SIZE_MAX);
×
684

685
                        goto skipped;
×
686
                }
687

688
                if (c == 21) { /* C-u */
×
689

690
                        if (!FLAGS_SET(flags, ASK_PASSWORD_SILENT))
×
691
                                (void) backspace_string(ttyfd, passphrase);
×
692

693
                        explicit_bzero_safe(passphrase, sizeof(passphrase));
×
694
                        p = codepoint = 0;
×
695

696
                } else if (IN_SET(c, '\b', 127)) {
×
697

698
                        if (p > 0) {
×
699
                                size_t q;
×
700

701
                                if (!FLAGS_SET(flags, ASK_PASSWORD_SILENT))
×
702
                                        (void) backspace_chars(ttyfd, 1);
×
703

704
                                /* Remove a full UTF-8 codepoint from the end. For that, figure out where the
705
                                 * last one begins */
706
                                q = 0;
707
                                for (;;) {
×
708
                                        int z;
×
709

710
                                        z = utf8_encoded_valid_unichar(passphrase + q, SIZE_MAX);
×
711
                                        if (z <= 0) {
×
712
                                                q = SIZE_MAX; /* Invalid UTF8! */
713
                                                break;
714
                                        }
715

716
                                        if (q + z >= p) /* This one brings us over the edge */
×
717
                                                break;
718

719
                                        q += z;
720
                                }
721

722
                                p = codepoint = q == SIZE_MAX ? p - 1 : q;
×
723
                                explicit_bzero_safe(passphrase + p, sizeof(passphrase) - p);
×
724

725
                        } else if (!dirty && !FLAGS_SET(flags, ASK_PASSWORD_SILENT)) {
×
726

727
                                flags |= ASK_PASSWORD_SILENT;
×
728

729
                                /* There are two ways to enter silent mode. Either by pressing backspace as
730
                                 * first key (and only as first key), or ... */
731

732
                                if (ttyfd >= 0)
×
733
                                        (void) loop_write(ttyfd, NO_ECHO, SIZE_MAX);
×
734

735
                        } else if (ttyfd >= 0)
×
736
                                (void) loop_write(ttyfd, "\a", 1);
×
737

738
                } else if (c == '\t' && !FLAGS_SET(flags, ASK_PASSWORD_SILENT)) {
×
739

740
                        (void) backspace_string(ttyfd, passphrase);
×
741
                        flags |= ASK_PASSWORD_SILENT;
×
742

743
                        /* ... or by pressing TAB at any time. */
744

745
                        if (ttyfd >= 0)
×
746
                                (void) loop_write(ttyfd, NO_ECHO, SIZE_MAX);
×
747

748
                } else if (char_is_cc(c) || p >= sizeof(passphrase)-1) {
×
749
                        /* Don't accept control chars or overly long passphrases */
750
                        if (ttyfd >= 0)
×
751
                                (void) loop_write(ttyfd, "\a", 1);
×
752

753
                } else {
754
                        passphrase[p++] = c;
×
755

756
                        if (!FLAGS_SET(flags, ASK_PASSWORD_SILENT) && ttyfd >= 0) {
×
757
                                /* Check if we got a complete UTF-8 character now. If so, let's output one '*'. */
758
                                n = utf8_encoded_valid_unichar(passphrase + codepoint, SIZE_MAX);
×
759
                                if (n >= 0) {
×
760
                                        if (FLAGS_SET(flags, ASK_PASSWORD_ECHO))
×
761
                                                (void) loop_write(ttyfd, passphrase + codepoint, n);
×
762
                                        else
763
                                                (void) loop_write(ttyfd,
×
764
                                                                  special_glyph(SPECIAL_GLYPH_BULLET),
×
765
                                                                  SIZE_MAX);
766
                                        codepoint = p;
767
                                }
768
                        }
769

770
                        dirty = true;
771
                }
772
        }
773

774
        x = strndup(passphrase, p);
1✔
775
        if (!x) {
1✔
776
                r = -ENOMEM;
×
777
                goto finish;
×
778
        }
779

780
        r = strv_consume(&l, x);
1✔
781
        if (r < 0)
1✔
782
                goto finish;
×
783

784
skipped:
1✔
785
        if (strv_isempty(l))
1✔
786
                r = log_debug_errno(SYNTHETIC_ERRNO(ECANCELED), "Password query was cancelled.");
×
787
        else {
788
                if (keyring)
1✔
789
                        (void) add_to_keyring_and_log(keyring, flags, l);
×
790

791
                *ret = TAKE_PTR(l);
1✔
792
                r = 0;
1✔
793
        }
794

795
finish:
1✔
796
        if (ttyfd >= 0 && reset_tty) {
1✔
797
                (void) loop_write(ttyfd, "\n", 1);
×
798
                (void) tcsetattr(ttyfd, TCSADRAIN, &old_termios);
×
799
        }
800

801
        return r;
802
}
803

804
static int create_socket(const char *askpwdir, char **ret) {
1✔
805
        _cleanup_free_ char *path = NULL;
1✔
806
        union sockaddr_union sa;
1✔
807
        socklen_t sa_len;
1✔
808
        _cleanup_close_ int fd = -EBADF;
1✔
809
        int r;
1✔
810

811
        assert(askpwdir);
1✔
812
        assert(ret);
1✔
813

814
        fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
1✔
815
        if (fd < 0)
1✔
816
                return -errno;
×
817

818
        if (asprintf(&path, "%s/sck.%" PRIx64, askpwdir, random_u64()) < 0)
1✔
819
                return -ENOMEM;
820

821
        r = sockaddr_un_set_path(&sa.un, path);
1✔
822
        if (r < 0)
1✔
823
                return r;
824
        sa_len = r;
1✔
825

826
        WITH_UMASK(0177)
2✔
827
                if (bind(fd, &sa.sa, sa_len) < 0)
1✔
828
                        return -errno;
×
829

830
        r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true);
1✔
831
        if (r < 0)
1✔
832
                return r;
833

834
        *ret = TAKE_PTR(path);
1✔
835
        return TAKE_FD(fd);
1✔
836
}
837

838
int ask_password_agent(
1✔
839
                const AskPasswordRequest *req,
840
                AskPasswordFlags flags,
841
                char ***ret) {
842

843
        _cleanup_close_ int socket_fd = -EBADF, signal_fd = -EBADF, inotify_fd = -EBADF, dfd = -EBADF;
3✔
844
        _cleanup_(unlink_and_freep) char *socket_name = NULL;
1✔
845
        _cleanup_free_ char *temp = NULL, *final = NULL;
1✔
846
        _cleanup_strv_free_erase_ char **l = NULL;
×
847
        _cleanup_fclose_ FILE *f = NULL;
1✔
848
        sigset_t mask, oldmask;
1✔
849
        int r;
1✔
850

851
        assert(req);
1✔
852
        assert(ret);
1✔
853

854
        if (FLAGS_SET(flags, ASK_PASSWORD_HEADLESS))
1✔
855
                return -ENOEXEC;
856

857
        if (FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT))
1✔
858
                return -EUNATCH;
859

860
        /* We don't support the flag file concept for now when querying via the agent logic */
861
        if (req->flag_file)
1✔
862
                return -EOPNOTSUPP;
863

864
        assert_se(sigemptyset(&mask) >= 0);
1✔
865
        assert_se(sigset_add_many(&mask, SIGINT, SIGTERM) >= 0);
1✔
866
        assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) >= 0);
1✔
867

868
        _cleanup_free_ char *askpwdir = NULL;
1✔
869
        r = get_ask_password_directory_for_flags(flags, &askpwdir);
1✔
870
        if (r < 0)
1✔
871
                goto finish;
×
872
        if (r == 0) {
1✔
873
                r = -ENXIO;
×
874
                goto finish;
×
875
        }
876

877
        dfd = open_mkdir(askpwdir, O_RDONLY|O_CLOEXEC, 0755);
1✔
878
        if (dfd < 0) {
1✔
879
                r = log_debug_errno(dfd, "Failed to open directory '%s': %m", askpwdir);
×
880
                goto finish;
×
881
        }
882

883
        if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && req->keyring) {
1✔
884
                r = ask_password_keyring(req, flags, ret);
×
885
                if (r >= 0) {
×
886
                        r = 0;
×
887
                        goto finish;
×
888
                } else if (r != -ENOKEY)
×
889
                        goto finish;
×
890

891
                inotify_fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
×
892
                if (inotify_fd < 0) {
×
893
                        r = -errno;
×
894
                        goto finish;
×
895
                }
896

897
                r = inotify_add_watch_fd(inotify_fd, dfd, IN_ONLYDIR|IN_ATTRIB /* for mtime */);
×
898
                if (r < 0)
×
899
                        goto finish;
×
900
        }
901

902
        if (asprintf(&final, "ask.%" PRIu64, random_u64()) < 0) {
1✔
903
                r = -ENOMEM;
×
904
                goto finish;
×
905
        }
906

907
        r = fopen_temporary_at(dfd, final, &f, &temp);
1✔
908
        if (r < 0)
1✔
909
                goto finish;
×
910

911
        signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
1✔
912
        if (signal_fd < 0) {
1✔
913
                r = -errno;
×
914
                goto finish;
×
915
        }
916

917
        socket_fd = create_socket(askpwdir, &socket_name);
1✔
918
        if (socket_fd < 0) {
1✔
919
                r = socket_fd;
×
920
                goto finish;
×
921
        }
922

923
        fprintf(f,
1✔
924
                "[Ask]\n"
925
                "PID="PID_FMT"\n"
926
                "Socket=%s\n"
927
                "AcceptCached=%i\n"
928
                "Echo=%i\n"
929
                "NotAfter="USEC_FMT"\n"
930
                "Silent=%i\n",
931
                getpid_cached(),
932
                socket_name,
933
                FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED),
934
                FLAGS_SET(flags, ASK_PASSWORD_ECHO),
1✔
935
                req->until,
1✔
936
                FLAGS_SET(flags, ASK_PASSWORD_SILENT));
1✔
937

938
        if (req->message)
1✔
939
                fprintf(f, "Message=%s\n", req->message);
×
940

941
        if (req->icon)
1✔
942
                fprintf(f, "Icon=%s\n", req->icon);
×
943

944
        if (req->id)
1✔
945
                fprintf(f, "Id=%s\n", req->id);
×
946

947
        if (fchmod(fileno(f), 0644) < 0) {
1✔
948
                r = -errno;
×
949
                goto finish;
×
950
        }
951

952
        r = fflush_and_check(f);
1✔
953
        if (r < 0)
1✔
954
                goto finish;
×
955

956
        if (renameat(dfd, temp, dfd, final) < 0) {
1✔
957
                r = -errno;
×
958
                goto finish;
×
959
        }
960

961
        temp = mfree(temp);
1✔
962

963
        enum {
1✔
964
                POLL_SOCKET,
965
                POLL_SIGNAL,
966
                POLL_THREE,
967
                POLL_FOUR,
968
                _POLL_MAX
969
        };
970

971
        struct pollfd pollfd[_POLL_MAX] = {
1✔
972
                [POLL_SOCKET]  = { .fd = socket_fd,  .events = POLLIN },
973
                [POLL_SIGNAL]  = { .fd = signal_fd,  .events = POLLIN },
974
        };
975
        size_t n_pollfd = POLL_SIGNAL + 1, inotify_idx = SIZE_MAX, hup_fd_idx = SIZE_MAX;
1✔
976

977
        if (inotify_fd >= 0)
1✔
978
                pollfd[inotify_idx = n_pollfd++] = (struct pollfd) {
×
979
                        .fd = inotify_fd,
980
                        .events = POLLIN,
981
                };
982
        if (req->hup_fd >= 0)
1✔
983
                pollfd[hup_fd_idx = n_pollfd ++] = (struct pollfd) {
×
984
                        .fd = req->hup_fd,
985
                        .events = POLLHUP,
986
                };
987

988
        assert(n_pollfd <= _POLL_MAX);
1✔
989

990
        for (;;) {
1✔
991
                CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
1✔
992
                char passphrase[LINE_MAX+1];
1✔
993
                struct iovec iovec;
1✔
994
                struct ucred *ucred;
1✔
995
                usec_t timeout;
1✔
996
                ssize_t n;
1✔
997

998
                if (req->until > 0)
1✔
999
                        timeout = usec_sub_unsigned(req->until, now(CLOCK_MONOTONIC));
1✔
1000
                else
1001
                        timeout = USEC_INFINITY;
1002

1003
                r = ppoll_usec(pollfd, n_pollfd, timeout);
1✔
1004
                if (r == -EINTR)
1✔
1005
                        continue;
×
1006
                if (r < 0)
1✔
1007
                        goto finish;
×
1008
                if (r == 0) {
1✔
1009
                        r = -ETIME;
×
1010
                        goto finish;
×
1011
                }
1012

1013
                if (pollfd[POLL_SIGNAL].revents & POLLIN) {
1✔
1014
                        r = -EINTR;
×
1015
                        goto finish;
×
1016
                }
1017

1018
                if (req->hup_fd >= 0 && pollfd[hup_fd_idx].revents & POLLHUP)
1✔
1019
                        return -ECONNRESET;
×
1020

1021
                if (inotify_fd >= 0 && pollfd[inotify_idx].revents != 0) {
1✔
1022
                        (void) flush_fd(inotify_fd);
×
1023

1024
                        if (req->keyring) {
×
1025
                                r = ask_password_keyring(req, flags, ret);
×
1026
                                if (r >= 0) {
×
1027
                                        r = 0;
×
1028
                                        goto finish;
×
1029
                                } else if (r != -ENOKEY)
×
1030
                                        goto finish;
×
1031
                        }
1032
                }
1033

1034
                if (pollfd[POLL_SOCKET].revents == 0)
1✔
1035
                        continue;
×
1036

1037
                if (pollfd[POLL_SOCKET].revents != POLLIN) {
1✔
1038
                        r = -EIO;
×
1039
                        goto finish;
×
1040
                }
1041

1042
                iovec = IOVEC_MAKE(passphrase, sizeof(passphrase));
1✔
1043

1044
                struct msghdr msghdr = {
1✔
1045
                        .msg_iov = &iovec,
1046
                        .msg_iovlen = 1,
1047
                        .msg_control = &control,
1048
                        .msg_controllen = sizeof(control),
1049
                };
1050

1051
                n = recvmsg_safe(socket_fd, &msghdr, 0);
1✔
1052
                if (ERRNO_IS_NEG_TRANSIENT(n))
1✔
1053
                        continue;
×
1054
                if (n == -ECHRNG) {
1✔
1055
                        log_debug_errno(n, "Got message with truncated control data (unexpected fds sent?), ignoring.");
×
1056
                        continue;
×
1057
                }
1058
                if (n == -EXFULL) {
1✔
1059
                        log_debug_errno(n, "Got message with truncated payload data, ignoring.");
×
1060
                        continue;
×
1061
                }
1062
                if (n < 0) {
1✔
1063
                        r = (int) n;
×
1064
                        goto finish;
×
1065
                }
1066

1067
                CLEANUP_ERASE(passphrase);
×
1068

1069
                cmsg_close_all(&msghdr);
1✔
1070

1071
                if (n == 0) {
1✔
1072
                        log_debug("Message too short");
×
1073
                        continue;
×
1074
                }
1075

1076
                ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred);
1✔
1077
                if (!ucred) {
1✔
1078
                        log_debug("Received message without credentials. Ignoring.");
×
1079
                        continue;
×
1080
                }
1081

1082
                if (ucred->uid != getuid() && ucred->uid != 0) {
1✔
1083
                        log_debug("Got response from bad user. Ignoring.");
×
1084
                        continue;
×
1085
                }
1086

1087
                if (passphrase[0] == '+') {
1✔
1088
                        /* An empty message refers to the empty password */
1089
                        if (n == 1)
1✔
1090
                                l = strv_new("");
×
1091
                        else
1092
                                l = strv_parse_nulstr(passphrase+1, n-1);
1✔
1093
                        if (!l) {
1✔
1094
                                r = -ENOMEM;
×
1095
                                goto finish;
×
1096
                        }
1097

1098
                        if (strv_isempty(l)) {
1✔
1099
                                l = strv_free(l);
×
1100
                                log_debug("Invalid packet");
×
1101
                                continue;
×
1102
                        }
1103

1104
                        break;
1✔
1105
                }
1106

1107
                if (passphrase[0] == '-') {
×
1108
                        r = -ECANCELED;
×
1109
                        goto finish;
×
1110
                }
1111

1112
                log_debug("Invalid packet");
×
1113
        }
1114

1115
        if (req->keyring)
1✔
1116
                (void) add_to_keyring_and_log(req->keyring, flags, l);
×
1117

1118
        *ret = TAKE_PTR(l);
1✔
1119
        r = 0;
1✔
1120

1121
finish:
1✔
1122
        if (temp) {
1✔
1123
                assert(dfd >= 0);
×
1124
                (void) unlinkat(dfd, temp, 0);
×
1125
        } else if (final) {
1✔
1126
                assert(dfd >= 0);
1✔
1127
                (void) unlinkat(dfd, final, 0);
1✔
1128
        }
1129

1130
        assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
1✔
1131
        return r;
1132
}
1133

1134
static int ask_password_credential(const AskPasswordRequest *req, AskPasswordFlags flags, char ***ret) {
82✔
1135
        _cleanup_(erase_and_freep) char *buffer = NULL;
82✔
1136
        _cleanup_strv_free_erase_ char **l = NULL;
×
1137
        size_t size;
82✔
1138
        int r;
82✔
1139

1140
        assert(req);
82✔
1141
        assert(req->credential);
82✔
1142
        assert(ret);
82✔
1143

1144
        r = read_credential(req->credential, (void**) &buffer, &size);
82✔
1145
        if (IN_SET(r, -ENXIO, -ENOENT)) /* No credentials passed or this credential not defined? */
83✔
1146
                return -ENOKEY;
1147

1148
        l = strv_parse_nulstr(buffer, size);
1✔
1149
        if (!l)
1✔
1150
                return -ENOMEM;
1151

1152
        if (strv_isempty(l))
1✔
1153
                return log_debug_errno(SYNTHETIC_ERRNO(ENOKEY), "Found an empty password in credential.");
×
1154

1155
        *ret = TAKE_PTR(l);
1✔
1156
        return 0;
1✔
1157
}
1158

1159
int ask_password_auto(
82✔
1160
                const AskPasswordRequest *req,
1161
                AskPasswordFlags flags,
1162
                char ***ret) {
1163

1164
        int r;
82✔
1165

1166
        assert(req);
82✔
1167
        assert(ret);
82✔
1168

1169
        /* Returns the following well-known errors:
1170
         *
1171
         *      -ETIME → a timeout was specified and hit
1172
         *    -EUNATCH → couldn't ask interactively and no cached password available either
1173
         *     -ENOENT → the specified flag file disappeared
1174
         *  -ECANCELED → the user explicitly cancelled the request
1175
         *      -EINTR → SIGINT/SIGTERM where received during the query
1176
         *    -ENOEXEC → headless mode was requested but no password could be acquired non-interactively
1177
         * -ECONNRESET → a POLLHUP has been seen on the specified hup_fd
1178
         */
1179

1180
        if (!FLAGS_SET(flags, ASK_PASSWORD_NO_CREDENTIAL) && req->credential) {
82✔
1181
                r = ask_password_credential(req, flags, ret);
82✔
1182
                if (r != -ENOKEY)
82✔
1183
                        return r;
1184
        }
1185

1186
        if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) &&
81✔
1187
            req->keyring &&
4✔
1188
            (FLAGS_SET(flags, ASK_PASSWORD_NO_TTY) || !isatty_safe(STDIN_FILENO)) &&
4✔
1189
            FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT)) {
4✔
1190
                r = ask_password_keyring(req, flags, ret);
4✔
1191
                if (r != -ENOKEY)
4✔
1192
                        return r;
1193
        }
1194

1195
        if (!FLAGS_SET(flags, ASK_PASSWORD_NO_TTY) && isatty_safe(STDIN_FILENO))
81✔
1196
                return ask_password_tty(req, flags, ret);
×
1197

1198
        if (!FLAGS_SET(flags, ASK_PASSWORD_NO_AGENT))
81✔
1199
                return ask_password_agent(req, flags, ret);
1✔
1200

1201
        return -EUNATCH;
1202
}
1203

1204
int acquire_user_ask_password_directory(char **ret) {
3✔
1205
        int r;
3✔
1206

1207
        r = xdg_user_runtime_dir("systemd/ask-password", ret);
3✔
1208
        if (r == -ENXIO) {
3✔
1209
                if (ret)
3✔
1210
                        *ret = NULL;
3✔
1211
                return 0;
3✔
1212
        }
1213
        if (r < 0)
×
1214
                return r;
×
1215

1216
        return 1;
1217
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc