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

systemd / systemd / 16062852561

03 Jul 2025 10:04PM UTC coverage: 72.193% (+0.1%) from 72.096%
16062852561

push

github

bluca
pcrlock: process components outside of location window properly

So far, when we tried to match a component to eent log entries we
skipped those components if they were outside of our location window.
That however is too aggressive, since it means any components that are
already in the logs, but outside of the location window will be
considered unrecognized in the logs, and thus removed from the PCR
policy.

Change things around: always try to match up all components, regardless
if inside the location window or outside, but then make it non-fatal we
can't find a component outside of the location window.

Fixes: #36079

7 of 9 new or added lines in 1 file covered. (77.78%)

4116 existing lines in 75 files now uncovered.

301219 of 417241 relevant lines covered (72.19%)

730820.5 hits per line

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

0.0
/src/ssh-generator/ssh-generator.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <unistd.h>
4

5
#include "alloc-util.h"
6
#include "creds-util.h"
7
#include "errno-util.h"
8
#include "fd-util.h"
9
#include "fileio.h"
10
#include "generator.h"
11
#include "install.h"
12
#include "log.h"
13
#include "parse-util.h"
14
#include "path-lookup.h"
15
#include "path-util.h"
16
#include "proc-cmdline.h"
17
#include "socket-netlink.h"
18
#include "socket-util.h"
19
#include "special.h"
20
#include "string-util.h"
21
#include "strv.h"
22
#include "virt.h"
23

24
/* A small generator binding potentially five or more SSH sockets:
25
 *
26
 *     1. Listen on AF_VSOCK port 22 if we run in a VM with AF_VSOCK enabled
27
 *     2. Listen on AF_UNIX socket /run/host/unix-export/ssh if we run in a container with /run/host/ support
28
 *     3. Listen on AF_UNIX socket /run/ssh-unix-local/socket (always)
29
 *     4. Listen on any socket specified via kernel command line option systemd.ssh_listen=
30
 *     5. Similar, but from system credential ssh.listen
31
 *
32
 * The first two provide a nice way for hosts to connect to containers and VMs they invoke via the usual SSH
33
 * logic, but without waiting for networking or suchlike. The third allows the same for local clients. */
34

35
static const char *arg_dest = NULL;
36
static bool arg_auto = true;
37
static char **arg_listen_extra = NULL;
38

UNCOV
39
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
×
40
        int r;
×
41

UNCOV
42
        assert(key);
×
43

UNCOV
44
        if (proc_cmdline_key_streq(key, "systemd.ssh_auto")) {
×
45
                r = value ? parse_boolean(value) : 1;
×
46
                if (r < 0)
×
47
                        log_warning_errno(r, "Failed to parse systemd.ssh_auto switch \"%s\", ignoring: %m", value);
×
48
                else
UNCOV
49
                        arg_auto = r;
×
50

UNCOV
51
        } else if (proc_cmdline_key_streq(key, "systemd.ssh_listen")) {
×
52

UNCOV
53
                if (proc_cmdline_value_missing(key, value))
×
54
                        return 0;
×
55

UNCOV
56
                SocketAddress sa;
×
57
                r = socket_address_parse(&sa, value);
×
58
                if (r < 0)
×
59
                        log_warning_errno(r, "Failed to parse systemd.ssh_listen= expression, ignoring: %s", value);
×
60
                else {
UNCOV
61
                        _cleanup_free_ char *s = NULL;
×
62
                        r = socket_address_print(&sa, &s);
×
63
                        if (r < 0)
×
64
                                return log_error_errno(r, "Failed to format socket address: %m");
×
65

UNCOV
66
                        if (strv_consume(&arg_listen_extra, TAKE_PTR(s)) < 0)
×
67
                                return log_oom();
×
68
                }
69
        }
70

71
        return 0;
72
}
73

UNCOV
74
static int make_sshd_template_unit(
×
75
                const char *dest,
76
                const char *template,
77
                const char *sshd_binary,
78
                const char *found_sshd_template_service,
79
                char **generated_sshd_template_unit) {
80

UNCOV
81
        int r;
×
82

UNCOV
83
        assert(dest);
×
84
        assert(template);
×
85
        assert(sshd_binary);
×
86
        assert(generated_sshd_template_unit);
×
87

88
        /* If the system has a suitable template already, symlink it to the name we want to reuse it */
UNCOV
89
        if (found_sshd_template_service)
×
90
                return generator_add_symlink(
×
91
                                dest,
92
                                template,
93
                                /* dep_type= */ NULL,
94
                                found_sshd_template_service);
95

UNCOV
96
        if (!*generated_sshd_template_unit) {
×
97
                _cleanup_fclose_ FILE *f = NULL;
×
98

UNCOV
99
                r = generator_open_unit_file_full(
×
100
                                dest,
101
                                /* source= */ NULL,
102
                                "sshd-generated@.service", /* Give this generated unit a generic name, since we want to use it for both AF_UNIX and AF_VSOCK */
103
                                &f,
104
                                generated_sshd_template_unit,
105
                                /* ret_temp_path= */ NULL);
UNCOV
106
                if (r < 0)
×
107
                        return r;
108

UNCOV
109
                fprintf(f,
×
110
                        "[Unit]\n"
111
                        "Description=OpenSSH Per-Connection Server Daemon\n"
112
                        "Documentation=man:systemd-ssh-generator(8) man:sshd(8)\n"
113
                        "\n"
114
                        "[Service]\n"
115
                        "ExecStart=-%s -i -o \"AuthorizedKeysFile ${CREDENTIALS_DIRECTORY}/ssh.ephemeral-authorized_keys-all .ssh/authorized_keys\"\n"
116
                        "StandardInput=socket\n"
117
                        "ImportCredential=ssh.ephemeral-authorized_keys-all\n",
118
                        sshd_binary);
119

UNCOV
120
                r = fflush_and_check(f);
×
121
                if (r < 0)
×
122
                        return log_error_errno(r, "Failed to write sshd template: %m");
×
123
        }
124

UNCOV
125
        return generator_add_symlink(
×
126
                        dest,
127
                        template,
128
                        /* dep_type= */ NULL,
129
                        *generated_sshd_template_unit);
130
}
131

UNCOV
132
static int write_socket_unit(
×
133
                const char *dest,
134
                const char *unit,
135
                const char *listen_stream,
136
                const char *comment,
137
                bool with_ssh_access_target_dependency) {
138

UNCOV
139
        int r;
×
140

UNCOV
141
        assert(dest);
×
142
        assert(unit);
×
143
        assert(listen_stream);
×
144
        assert(comment);
×
145

UNCOV
146
        _cleanup_fclose_ FILE *f = NULL;
×
147
        r = generator_open_unit_file(
×
148
                        dest,
149
                        /* source= */ NULL,
150
                        unit,
151
                        &f);
UNCOV
152
        if (r < 0)
×
153
                return r;
154

UNCOV
155
        fprintf(f,
×
156
                "[Unit]\n"
157
                "Description=OpenSSH Server Socket (systemd-ssh-generator, %s)\n"
158
                "Documentation=man:systemd-ssh-generator(8)\n",
159
                comment);
160

161
        /* When this is a remotely accessible socket let's mark this with a milestone: ssh-access.target */
UNCOV
162
        if (with_ssh_access_target_dependency)
×
163
                fputs("Wants=ssh-access.target\n"
×
164
                      "Before=ssh-access.target\n",
165
                      f);
166

UNCOV
167
        fprintf(f,
×
168
                "\n[Socket]\n"
169
                "ListenStream=%s\n"
170
                "Accept=yes\n"
171
                "PollLimitIntervalSec=30s\n"
172
                "PollLimitBurst=50\n",
173
                listen_stream);
174

UNCOV
175
        r = fflush_and_check(f);
×
176
        if (r < 0)
×
177
                return log_error_errno(r, "Failed to write %s SSH socket unit: %m", comment);
×
178

UNCOV
179
        r = generator_add_symlink(
×
180
                        dest,
181
                        SPECIAL_SOCKETS_TARGET,
182
                        "wants",
183
                        unit);
UNCOV
184
        if (r < 0)
×
185
                return r;
×
186

187
        return 0;
188
}
189

UNCOV
190
static int add_vsock_socket(
×
191
                const char *dest,
192
                const char *sshd_binary,
193
                const char *found_sshd_template_unit,
194
                char **generated_sshd_template_unit) {
195

UNCOV
196
        int r;
×
197

UNCOV
198
        assert(dest);
×
199
        assert(generated_sshd_template_unit);
×
200

UNCOV
201
        Virtualization v = detect_virtualization();
×
202
        if (v < 0)
×
203
                return log_error_errno(v, "Failed to detect if we run in a VM: %m");
×
204
        if (!VIRTUALIZATION_IS_VM(v)) {
×
205
                /* NB: if we are running in a container inside a VM, then we'll *not* do AF_VSOCK stuff */
UNCOV
206
                log_debug("Not running in a VM, not listening on AF_VSOCK.");
×
207
                return 0;
×
208
        }
209

UNCOV
210
        _cleanup_close_ int vsock_fd = socket(AF_VSOCK, SOCK_STREAM|SOCK_CLOEXEC, 0);
×
211
        if (vsock_fd < 0) {
×
212
                if (ERRNO_IS_NOT_SUPPORTED(errno)) {
×
213
                        log_debug("Not creating AF_VSOCK ssh listener, since AF_VSOCK is not available.");
×
214
                        return 0;
×
215
                }
216

UNCOV
217
                return log_error_errno(errno, "Unable to test if AF_VSOCK is available: %m");
×
218
        }
219

UNCOV
220
        vsock_fd = safe_close(vsock_fd);
×
221

222
        /* Determine the local CID so that we can log it to help users to connect to this VM */
UNCOV
223
        unsigned local_cid;
×
224
        r = vsock_get_local_cid(&local_cid);
×
225
        if (r < 0) {
×
226
                if (ERRNO_IS_DEVICE_ABSENT(r)) {
×
227
                        log_debug("Not creating AF_VSOCK ssh listener, since /dev/vsock is not available (even though AF_VSOCK is).");
×
228
                        return 0;
×
229
                }
230

UNCOV
231
                return log_error_errno(r, "Failed to query local AF_VSOCK CID: %m");
×
232
        }
233

UNCOV
234
        r = make_sshd_template_unit(
×
235
                        dest,
236
                        "sshd-vsock@.service",
237
                        sshd_binary,
238
                        found_sshd_template_unit,
239
                        generated_sshd_template_unit);
UNCOV
240
        if (r < 0)
×
241
                return r;
242

UNCOV
243
        r = write_socket_unit(
×
244
                        dest,
245
                        "sshd-vsock.socket",
246
                        "vsock::22",
247
                        "AF_VSOCK",
248
                        /* with_ssh_access_target_dependency= */ true);
UNCOV
249
        if (r < 0)
×
250
                return r;
251

UNCOV
252
        log_debug("Binding SSH to AF_VSOCK vsock::22.\n"
×
253
                  "→ connect via 'ssh vsock/%u' from host", local_cid);
254
        return 0;
255
}
256

UNCOV
257
static int add_local_unix_socket(
×
258
                const char *dest,
259
                const char *sshd_binary,
260
                const char *found_sshd_template_unit,
261
                char **generated_sshd_template_unit) {
262

UNCOV
263
        int r;
×
264

UNCOV
265
        assert(dest);
×
266
        assert(sshd_binary);
×
267
        assert(generated_sshd_template_unit);
×
268

UNCOV
269
        r = make_sshd_template_unit(
×
270
                        dest,
271
                        "sshd-unix-local@.service",
272
                        sshd_binary,
273
                        found_sshd_template_unit,
274
                        generated_sshd_template_unit);
UNCOV
275
        if (r < 0)
×
276
                return r;
277

UNCOV
278
        r = write_socket_unit(
×
279
                        dest,
280
                        "sshd-unix-local.socket",
281
                        "/run/ssh-unix-local/socket",
282
                        "AF_UNIX Local",
283
                        /* with_ssh_access_target_dependency= */ false);
UNCOV
284
        if (r < 0)
×
285
                return r;
286

UNCOV
287
        log_debug("Binding SSH to AF_UNIX socket /run/ssh-unix-local/socket.\n"
×
288
                  "→ connect via 'ssh .host' locally");
289
        return 0;
290
}
291

UNCOV
292
static int add_export_unix_socket(
×
293
                const char *dest,
294
                const char *sshd_binary,
295
                const char *found_sshd_template_unit,
296
                char **generated_sshd_template_unit) {
297

UNCOV
298
        int r;
×
299

UNCOV
300
        assert(dest);
×
301
        assert(sshd_binary);
×
302
        assert(generated_sshd_template_unit);
×
303

UNCOV
304
        Virtualization v = detect_container();
×
305
        if (v < 0)
×
306
                return log_error_errno(v, "Failed to detect if we run in a container: %m");
×
307
        if (v == VIRTUALIZATION_NONE) {
×
308
                log_debug("Not running in container, not listening on /run/host/unix-export/ssh");
×
309
                return 0;
×
310
        }
311

UNCOV
312
        if (access("/run/host/unix-export/", W_OK) < 0) {
×
313
                if (errno == ENOENT) {
×
314
                        log_debug("Container manager does not provide /run/host/unix-export/ mount, not binding AF_UNIX socket there.");
×
315
                        return 0;
×
316
                }
UNCOV
317
                if (errno == EROFS || ERRNO_IS_PRIVILEGE(errno)) {
×
318
                        log_debug("Container manager does not provide write access to /run/host/unix-export/, not binding AF_UNIX socket there.");
×
319
                        return 0;
×
320
                }
321

UNCOV
322
                return log_error_errno(errno, "Unable to check if /run/host/unix-export exists: %m");
×
323
        }
324

UNCOV
325
        r = make_sshd_template_unit(
×
326
                        dest,
327
                        "sshd-unix-export@.service",
328
                        sshd_binary,
329
                        found_sshd_template_unit,
330
                        generated_sshd_template_unit);
UNCOV
331
        if (r < 0)
×
332
                return r;
333

UNCOV
334
        r = write_socket_unit(
×
335
                        dest,
336
                        "sshd-unix-export.socket",
337
                        "/run/host/unix-export/ssh",
338
                        "AF_UNIX Export",
339
                        /* with_ssh_access_target_dependency= */ true);
UNCOV
340
        if (r < 0)
×
341
                return r;
342

UNCOV
343
        log_debug("Binding SSH to AF_UNIX socket /run/host/unix-export/ssh\n"
×
344
                  "→ connect via 'ssh unix/run/systemd/nspawn/unix-export/\?\?\?/ssh' from host");
345

346
        return 0;
347
}
348

UNCOV
349
static int add_extra_sockets(
×
350
                const char *dest,
351
                const char *sshd_binary,
352
                const char *found_sshd_template_unit,
353
                char **generated_sshd_template_unit) {
354

UNCOV
355
        unsigned n = 1;
×
356
        int r;
×
357

UNCOV
358
        assert(dest);
×
359
        assert(sshd_binary);
×
360
        assert(generated_sshd_template_unit);
×
361

UNCOV
362
        if (strv_isempty(arg_listen_extra))
×
363
                return 0;
364

UNCOV
365
        STRV_FOREACH(i, arg_listen_extra) {
×
366
                _cleanup_free_ char *service = NULL, *socket = NULL;
×
367

UNCOV
368
                if (n > 1) {
×
369
                        if (asprintf(&service, "sshd-extra-%u@.service", n) < 0)
×
370
                                return log_oom();
×
371

UNCOV
372
                        if (asprintf(&socket, "sshd-extra-%u.socket", n) < 0)
×
373
                                return log_oom();
×
374
                }
375

UNCOV
376
                r = make_sshd_template_unit(
×
377
                                dest,
UNCOV
378
                                service ?: "sshd-extra@.service",
×
379
                                sshd_binary,
380
                                found_sshd_template_unit,
381
                                generated_sshd_template_unit);
UNCOV
382
                if (r < 0)
×
383
                        return r;
384

UNCOV
385
                r = write_socket_unit(
×
386
                                dest,
UNCOV
387
                                socket ?: "sshd-extra.socket",
×
388
                                *i,
389
                                *i,
390
                                /* with_ssh_access_target_dependency= */ true);
UNCOV
391
                if (r < 0)
×
392
                        return r;
393

UNCOV
394
                log_debug("Binding SSH to socket %s.", *i);
×
395
                n++;
×
396
        }
397

398
        return 0;
399
}
400

UNCOV
401
static int parse_credentials(void) {
×
402
        _cleanup_free_ char *b = NULL;
×
403
        size_t sz = 0;
×
404
        int r;
×
405

UNCOV
406
        r = read_credential_with_decryption("ssh.listen", (void**) &b, &sz);
×
407
        if (r <= 0)
×
408
                return r;
409

UNCOV
410
        _cleanup_fclose_ FILE *f = NULL;
×
411
        f = fmemopen_unlocked(b, sz, "r");
×
412
        if (!f)
×
413
                return log_oom();
×
414

UNCOV
415
        for (;;) {
×
416
                _cleanup_free_ char *item = NULL;
×
417

UNCOV
418
                r = read_stripped_line(f, LINE_MAX, &item);
×
419
                if (r == 0)
×
420
                        break;
UNCOV
421
                if (r < 0) {
×
422
                        log_error_errno(r, "Failed to parse credential 'ssh.listen': %m");
×
423
                        break;
424
                }
425

UNCOV
426
                if (startswith(item, "#"))
×
427
                        continue;
×
428

UNCOV
429
                SocketAddress sa;
×
430
                r = socket_address_parse(&sa, item);
×
431
                if (r < 0) {
×
432
                        log_warning_errno(r, "Failed to parse systemd.ssh_listen= expression, ignoring: %s", item);
×
433
                        continue;
×
434
                }
435

UNCOV
436
                _cleanup_free_ char *s = NULL;
×
437
                r = socket_address_print(&sa, &s);
×
438
                if (r < 0)
×
439
                        return log_error_errno(r, "Failed to format socket address: %m");
×
440

UNCOV
441
                if (strv_consume(&arg_listen_extra, TAKE_PTR(s)) < 0)
×
442
                        return log_oom();
×
443
        }
444

UNCOV
445
        return 0;
×
446
}
447

UNCOV
448
static int run(const char *dest, const char *dest_early, const char *dest_late) {
×
449
        int r;
×
450

UNCOV
451
        assert_se(arg_dest = dest);
×
452

UNCOV
453
        r = proc_cmdline_parse(parse_proc_cmdline_item, /* userdata= */ NULL, /* flags= */ 0);
×
454
        if (r < 0)
×
455
                log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
×
456

UNCOV
457
        (void) parse_credentials();
×
458

UNCOV
459
        strv_sort_uniq(arg_listen_extra);
×
460

UNCOV
461
        if (!arg_auto && strv_isempty(arg_listen_extra)) {
×
462
                log_debug("Disabling SSH generator logic, because it has been turned off explicitly.");
×
463
                return 0;
×
464
        }
465

UNCOV
466
        _cleanup_free_ char *sshd_binary = NULL;
×
467
        r = find_executable("sshd", &sshd_binary);
×
468
        if (r == -ENOENT) {
×
469
                log_debug("Disabling SSH generator logic, since sshd is not installed.");
×
470
                return 0;
×
471
        }
UNCOV
472
        if (r < 0)
×
473
                return log_error_errno(r, "Failed to determine if sshd is installed: %m");
×
474

UNCOV
475
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
×
476
        r = lookup_paths_init_or_warn(&lp, RUNTIME_SCOPE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, /* root_dir= */ NULL);
×
477
        if (r < 0)
×
478
                return r;
479

UNCOV
480
        _cleanup_free_ char *found_sshd_template_unit = NULL;
×
481
        r = unit_file_exists_full(RUNTIME_SCOPE_SYSTEM, &lp, "sshd@.service", &found_sshd_template_unit);
×
482
        if (r < 0)
×
483
                return log_error_errno(r, "Unable to detect if sshd@.service exists: %m");
×
484

UNCOV
485
        _cleanup_free_ char *generated_sshd_template_unit = NULL;
×
486
        RET_GATHER(r, add_extra_sockets(dest, sshd_binary, found_sshd_template_unit, &generated_sshd_template_unit));
×
487

UNCOV
488
        if (arg_auto) {
×
489
                RET_GATHER(r, add_vsock_socket(dest, sshd_binary, found_sshd_template_unit, &generated_sshd_template_unit));
×
490
                RET_GATHER(r, add_local_unix_socket(dest, sshd_binary, found_sshd_template_unit, &generated_sshd_template_unit));
×
491
                RET_GATHER(r, add_export_unix_socket(dest, sshd_binary, found_sshd_template_unit, &generated_sshd_template_unit));
×
492
        }
493

UNCOV
494
        return r;
×
495
}
496

UNCOV
497
DEFINE_MAIN_GENERATOR_FUNCTION(run);
×
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