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

systemd / systemd / 13210122091

07 Feb 2025 10:56PM UTC coverage: 71.748% (-0.07%) from 71.813%
13210122091

push

github

web-flow
network: allow to configure routing policy rule even if requesting interface is not activated yet (#36257)

Fixes a regression caused by 4f8b153d9
(v257).
Fixes #36244.

16 of 16 new or added lines in 2 files covered. (100.0%)

3079 existing lines in 54 files now uncovered.

292917 of 408258 relevant lines covered (71.75%)

714241.77 hits per line

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

36.63
/src/import/pull-common.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <sys/prctl.h>
4

5
#include "alloc-util.h"
6
#include "btrfs-util.h"
7
#include "capability-util.h"
8
#include "copy.h"
9
#include "dirent-util.h"
10
#include "discover-image.h"
11
#include "escape.h"
12
#include "fd-util.h"
13
#include "hostname-util.h"
14
#include "io-util.h"
15
#include "memory-util.h"
16
#include "path-util.h"
17
#include "process-util.h"
18
#include "pull-common.h"
19
#include "pull-job.h"
20
#include "rm-rf.h"
21
#include "signal-util.h"
22
#include "siphash24.h"
23
#include "string-util.h"
24
#include "strv.h"
25
#include "tmpfile-util.h"
26
#include "web-util.h"
27

28
#define FILENAME_ESCAPE "/.#\"\'"
29
#define HASH_URL_THRESHOLD_LENGTH (_POSIX_PATH_MAX - 16)
30

31
int pull_find_old_etags(
5✔
32
                const char *url,
33
                const char *image_root,
34
                int dt,
35
                const char *prefix,
36
                const char *suffix,
37
                char ***etags) {
38

39
        int r;
5✔
40

41
        assert(url);
5✔
42
        assert(image_root);
5✔
43
        assert(etags);
5✔
44

45
        _cleanup_free_ char *escaped_url = xescape(url, FILENAME_ESCAPE);
10✔
46
        if (!escaped_url)
5✔
47
                return -ENOMEM;
48

49
        _cleanup_closedir_ DIR *d = opendir(image_root);
10✔
50
        if (!d) {
5✔
51
                if (errno == ENOENT) {
×
52
                        *etags = NULL;
×
53
                        return 0;
×
54
                }
55

56
                return -errno;
×
57
        }
58

59
        _cleanup_strv_free_ char **ans = NULL;
5✔
60

61
        FOREACH_DIRENT_ALL(de, d, return -errno) {
39✔
62
                _cleanup_free_ char *u = NULL;
34✔
63
                const char *a, *b;
34✔
64

65
                if (de->d_type != DT_UNKNOWN &&
34✔
66
                    de->d_type != dt)
34✔
67
                        continue;
10✔
68

69
                if (prefix) {
24✔
70
                        a = startswith(de->d_name, prefix);
24✔
71
                        if (!a)
24✔
72
                                continue;
24✔
73
                } else
74
                        a = de->d_name;
×
75

76
                a = startswith(a, escaped_url);
×
77
                if (!a)
×
78
                        continue;
×
79

80
                a = startswith(a, ".");
×
81
                if (!a)
×
82
                        continue;
×
83

84
                if (suffix) {
×
85
                        b = endswith(de->d_name, suffix);
×
86
                        if (!b)
×
87
                                continue;
×
88
                } else
89
                        b = strchr(de->d_name, 0);
×
90

91
                if (a >= b)
×
92
                        continue;
×
93

94
                ssize_t l = cunescape_length(a, b - a, 0, &u);
×
95
                if (l < 0) {
×
96
                        assert(l >= INT8_MIN);
×
97
                        return l;
×
98
                }
99

100
                if (!http_etag_is_valid(u))
×
101
                        continue;
×
102

103
                r = strv_consume(&ans, TAKE_PTR(u));
×
104
                if (r < 0)
×
105
                        return r;
106
        }
107

108
        *etags = TAKE_PTR(ans);
5✔
109

110
        return 0;
5✔
111
}
112

113
static int hash_url(const char *url, char **ret) {
×
114
        uint64_t h;
×
115
        static const sd_id128_t k = SD_ID128_ARRAY(df,89,16,87,01,cc,42,30,98,ab,4a,19,a6,a5,63,4f);
×
116

117
        assert(url);
×
118

119
        h = siphash24(url, strlen(url), k.bytes);
×
120
        if (asprintf(ret, "%"PRIx64, h) < 0)
×
121
                return -ENOMEM;
×
122

123
        return 0;
124
}
125

126
int pull_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret) {
7✔
127
        _cleanup_free_ char *escaped_url = NULL, *escaped_etag = NULL;
7✔
128
        char *path;
7✔
129

130
        assert(url);
7✔
131
        assert(image_root);
7✔
132
        assert(ret);
7✔
133

134
        escaped_url = xescape(url, FILENAME_ESCAPE);
7✔
135
        if (!escaped_url)
7✔
136
                return -ENOMEM;
137

138
        if (etag) {
7✔
139
                escaped_etag = xescape(etag, FILENAME_ESCAPE);
×
140
                if (!escaped_etag)
×
141
                        return -ENOMEM;
142
        }
143

144
        path = strjoin(image_root, "/", strempty(prefix), escaped_url, escaped_etag ? "." : "",
10✔
145
                       strempty(escaped_etag), strempty(suffix));
146
        if (!path)
7✔
147
                return -ENOMEM;
148

149
        /* URLs might make the path longer than the maximum allowed length for a file name.
150
         * When that happens, a URL hash is used instead. Paths returned by this function
151
         * can be later used with tempfn_random() which adds 16 bytes to the resulting name. */
152
        if (strlen(path) >= HASH_URL_THRESHOLD_LENGTH) {
7✔
153
                _cleanup_free_ char *hash = NULL;
×
154
                int r;
×
155

156
                free(path);
×
157

158
                r = hash_url(url, &hash);
×
159
                if (r < 0)
×
160
                        return r;
161

162
                path = strjoin(image_root, "/", strempty(prefix), hash, escaped_etag ? "." : "",
×
163
                               strempty(escaped_etag), strempty(suffix));
164
                if (!path)
×
165
                        return -ENOMEM;
166
        }
167

168
        *ret = path;
7✔
169
        return 0;
7✔
170
}
171

172
int pull_make_auxiliary_job(
3✔
173
                PullJob **ret,
174
                const char *url,
175
                int (*strip_suffixes)(const char *name, char **ret),
176
                const char *suffix,
177
                ImportVerify verify,
178
                CurlGlue *glue,
179
                PullJobOpenDisk on_open_disk,
180
                PullJobFinished on_finished,
181
                void *userdata) {
182

183
        _cleanup_free_ char *last_component = NULL, *ll = NULL, *auxiliary_url = NULL;
3✔
184
        _cleanup_(pull_job_unrefp) PullJob *job = NULL;
3✔
185
        const char *q;
3✔
186
        int r;
3✔
187

188
        assert(ret);
3✔
189
        assert(url);
3✔
190
        assert(strip_suffixes);
3✔
191
        assert(glue);
3✔
192

193
        r = import_url_last_component(url, &last_component);
3✔
194
        if (r < 0)
3✔
195
                return r;
196

197
        r = strip_suffixes(last_component, &ll);
3✔
198
        if (r < 0)
3✔
199
                return r;
200

201
        q = strjoina(ll, suffix);
15✔
202

203
        r = import_url_change_last_component(url, q, &auxiliary_url);
3✔
204
        if (r < 0)
3✔
205
                return r;
206

207
        r = pull_job_new(&job, auxiliary_url, glue, userdata);
3✔
208
        if (r < 0)
3✔
209
                return r;
210

211
        job->on_open_disk = on_open_disk;
3✔
212
        job->on_finished = on_finished;
3✔
213
        job->compressed_max = job->uncompressed_max = 1ULL * 1024ULL * 1024ULL;
3✔
214
        job->calc_checksum = IN_SET(verify, IMPORT_VERIFY_CHECKSUM, IMPORT_VERIFY_SIGNATURE);
3✔
215

216
        *ret = TAKE_PTR(job);
3✔
217
        return 0;
3✔
218
}
219

220
static bool is_checksum_file(const char *fn) {
1✔
221
        /* Returns true if the specified filename refers to a checksum file we grok */
222

223
        if (!fn)
1✔
224
                return false;
225

226
        return streq(fn, "SHA256SUMS") || endswith(fn, ".sha256");
2✔
227
}
228

229
static SignatureStyle signature_style_from_filename(const char *fn) {
2✔
230
        /* Returns true if the specified filename refers to a signature file we grok */
231

232
        if (!fn)
2✔
233
                return _SIGNATURE_STYLE_INVALID;
234

235
        if (streq(fn, "SHA256SUMS.gpg"))
2✔
236
                return SIGNATURE_GPG_PER_DIRECTORY;
237

238
        if (streq(fn, "SHA256SUMS.asc"))
2✔
239
                return SIGNATURE_ASC_PER_DIRECTORY;
240

241
        if (endswith(fn, ".sha256.gpg"))
2✔
242
                return SIGNATURE_GPG_PER_FILE;
243

244
        if (endswith(fn, ".sha256.asc"))
2✔
UNCOV
245
                return SIGNATURE_ASC_PER_FILE;
×
246

247
        return _SIGNATURE_STYLE_INVALID;
248
}
249

250
int pull_make_verification_jobs(
25✔
251
                PullJob **ret_checksum_job,
252
                PullJob **ret_signature_job,
253
                ImportVerify verify,
254
                const char *checksum, /* set if literal checksum verification is requested, in which case 'verify' is set to _IMPORT_VERIFY_INVALID */
255
                const char *url,
256
                CurlGlue *glue,
257
                PullJobFinished on_finished,
258
                void *userdata) {
259

260
        _cleanup_(pull_job_unrefp) PullJob *checksum_job = NULL, *signature_job = NULL;
25✔
261
        _cleanup_free_ char *fn = NULL;
25✔
262
        int r;
25✔
263

264
        assert(ret_checksum_job);
25✔
265
        assert(ret_signature_job);
25✔
266
        assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX);
25✔
267
        assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0);
25✔
268
        assert((verify < 0) || !checksum);
25✔
269
        assert(url);
25✔
270
        assert(glue);
25✔
271

272
        /* If verification is turned off, or if the checksum to validate is already specified we don't need
273
         * to download a checksum file or signature, hence shortcut things */
274
        if (verify == IMPORT_VERIFY_NO || checksum) {
25✔
275
                *ret_checksum_job = *ret_signature_job = NULL;
24✔
276
                return 0;
24✔
277
        }
278

279
        r = import_url_last_component(url, &fn);
1✔
280
        if (r < 0 && r != -EADDRNOTAVAIL) /* EADDRNOTAVAIL means there was no last component, which is OK for
1✔
281
                                           * us, we'll just assume it's not a checksum/signature file */
282
                return r;
283

284
        /* Acquire the checksum file if verification or signature verification is requested and the main file
285
         * to acquire isn't a checksum or signature file anyway */
286
        if (verify != IMPORT_VERIFY_NO && !is_checksum_file(fn) && signature_style_from_filename(fn) < 0) {
1✔
287
                _cleanup_free_ char *checksum_url = NULL;
1✔
288
                const char *suffixed = NULL;
1✔
289

290
                /* Queue jobs for the checksum file for the image. */
291

292
                if (fn)
1✔
293
                        suffixed = strjoina(fn, ".sha256"); /* Start with the suse-style checksum (if there's a base filename) */
5✔
294
                else
295
                        suffixed = "SHA256SUMS";
296

297
                r = import_url_change_last_component(url, suffixed, &checksum_url);
1✔
298
                if (r < 0)
1✔
299
                        return r;
300

301
                r = pull_job_new(&checksum_job, checksum_url, glue, userdata);
1✔
302
                if (r < 0)
1✔
303
                        return r;
304

305
                checksum_job->on_finished = on_finished;
1✔
306
                checksum_job->uncompressed_max = checksum_job->compressed_max = 1ULL * 1024ULL * 1024ULL;
1✔
307
                checksum_job->on_not_found = pull_job_restart_with_sha256sum; /* if this fails, look for ubuntu-style checksum */
1✔
308
        }
309

310
        if (verify == IMPORT_VERIFY_SIGNATURE && signature_style_from_filename(fn) < 0) {
1✔
311
                _cleanup_free_ char *signature_url = NULL;
1✔
312
                const char *suffixed = NULL;
1✔
313

314
                if (fn)
1✔
315
                        suffixed = strjoina(fn, ".sha256.asc"); /* Start with the suse-style checksum (if there's a base filename) */
5✔
316
                else
317
                        suffixed = "SHA256SUMS.gpg";
318

319
                /* Queue job for the signature file for the image. */
320
                r = import_url_change_last_component(url, suffixed, &signature_url);
1✔
321
                if (r < 0)
1✔
322
                        return r;
323

324
                r = pull_job_new(&signature_job, signature_url, glue, userdata);
1✔
325
                if (r < 0)
1✔
326
                        return r;
327

328
                signature_job->on_finished = on_finished;
1✔
329
                signature_job->uncompressed_max = signature_job->compressed_max = 1ULL * 1024ULL * 1024ULL;
1✔
330
                signature_job->on_not_found = pull_job_restart_with_signature;
1✔
331
        }
332

333
        *ret_checksum_job = TAKE_PTR(checksum_job);
1✔
334
        *ret_signature_job = TAKE_PTR(signature_job);
1✔
335
        return 0;
1✔
336
}
337

UNCOV
338
static int verify_one(PullJob *checksum_job, PullJob *job) {
×
UNCOV
339
        _cleanup_free_ char *fn = NULL;
×
340
        const char *line, *p;
×
341
        int r;
×
342

343
        assert(checksum_job);
×
344

345
        if (!job)
×
346
                return 0;
347

348
        assert(IN_SET(job->state, PULL_JOB_DONE, PULL_JOB_FAILED));
×
349

350
        /* Don't verify the checksum if we didn't actually successfully download something new */
351
        if (job->state != PULL_JOB_DONE)
×
352
                return 0;
UNCOV
353
        if (job->error != 0)
×
354
                return 0;
355
        if (job->etag_exists)
×
356
                return 0;
357

UNCOV
358
        assert(job->calc_checksum);
×
UNCOV
359
        assert(job->checksum);
×
360

361
        r = import_url_last_component(job->url, &fn);
×
362
        if (r < 0)
×
UNCOV
363
                return log_error_errno(r, "Failed to extract filename from URL '%s': %m", job->url);
×
364

UNCOV
365
        if (!filename_is_valid(fn))
×
UNCOV
366
                return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
×
367
                                       "Cannot verify checksum, could not determine server-side file name.");
368

369
        if (is_checksum_file(fn) || signature_style_from_filename(fn) >= 0) /* We cannot verify checksum files or signature files with a checksum file */
×
370
                return log_error_errno(SYNTHETIC_ERRNO(ELOOP),
×
371
                                       "Cannot verify checksum/signature files via themselves.");
372

373
        line = strjoina(job->checksum, " *", fn, "\n"); /* string for binary mode */
×
UNCOV
374
        p = memmem_safe(checksum_job->payload,
×
375
                        checksum_job->payload_size,
376
                        line,
377
                        strlen(line));
UNCOV
378
        if (!p) {
×
UNCOV
379
                line = strjoina(job->checksum, "  ", fn, "\n"); /* string for text mode */
×
UNCOV
380
                p = memmem_safe(checksum_job->payload,
×
381
                                checksum_job->payload_size,
382
                                line,
383
                                strlen(line));
384
        }
385

386
        /* Only counts if found at beginning of a line */
387
        if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n'))
×
388
                return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
×
389
                                       "DOWNLOAD INVALID: Checksum of %s file did not check out, file has been tampered with.", fn);
390

391
        log_info("SHA256 checksum of %s is valid.", job->url);
×
392
        return 1;
393
}
394

395
static int verify_gpg(
×
396
                const void *payload, size_t payload_size,
397
                const void *signature, size_t signature_size) {
398

399
        _cleanup_close_pair_ int gpg_pipe[2] = EBADF_PAIR;
×
UNCOV
400
        _cleanup_(rm_rf_physical_and_freep) char *gpg_home = NULL;
×
401
        char sig_file_path[] = "/tmp/sigXXXXXX";
×
402
        _cleanup_(sigkill_waitp) pid_t pid = 0;
×
403
        int r;
×
404

UNCOV
405
        assert(payload || payload_size == 0);
×
UNCOV
406
        assert(signature || signature_size == 0);
×
407

408
        r = pipe2(gpg_pipe, O_CLOEXEC);
×
409
        if (r < 0)
×
410
                return log_error_errno(errno, "Failed to create pipe for gpg: %m");
×
411

UNCOV
412
        if (signature_size > 0) {
×
UNCOV
413
                _cleanup_close_ int sig_file = -EBADF;
×
414

415
                sig_file = mkostemp(sig_file_path, O_RDWR);
×
UNCOV
416
                if (sig_file < 0)
×
UNCOV
417
                        return log_error_errno(errno, "Failed to create temporary file: %m");
×
418

419
                r = loop_write(sig_file, signature, signature_size);
×
UNCOV
420
                if (r < 0) {
×
421
                        log_error_errno(r, "Failed to write to temporary file: %m");
×
422
                        goto finish;
×
423
                }
424
        }
425

UNCOV
426
        r = mkdtemp_malloc("/tmp/gpghomeXXXXXX", &gpg_home);
×
UNCOV
427
        if (r < 0) {
×
UNCOV
428
                log_error_errno(r, "Failed to create temporary home for gpg: %m");
×
UNCOV
429
                goto finish;
×
430
        }
431

UNCOV
432
        r = safe_fork_full("(gpg)",
×
UNCOV
433
                           (int[]) { gpg_pipe[0], -EBADF, STDERR_FILENO },
×
434
                           NULL, 0,
435
                           FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE,
436
                           &pid);
437
        if (r < 0)
×
438
                return r;
UNCOV
439
        if (r == 0) {
×
UNCOV
440
                const char *cmd[] = {
×
441
                        "gpg",
442
                        "--no-options",
443
                        "--no-default-keyring",
444
                        "--no-auto-key-locate",
445
                        "--no-auto-check-trustdb",
446
                        "--batch",
447
                        "--trust-model=always",
448
                        NULL, /* --homedir=  */
449
                        NULL, /* --keyring= */
450
                        NULL, /* --verify */
451
                        NULL, /* signature file */
452
                        NULL, /* dash */
453
                        NULL  /* trailing NULL */
454
                };
UNCOV
455
                size_t k = ELEMENTSOF(cmd) - 6;
×
456

457
                /* Child */
458

459
                cmd[k++] = strjoina("--homedir=", gpg_home);
×
460

461
                /* We add the user keyring only to the command line arguments, if it's around since gpg fails
462
                 * otherwise. */
463
                if (access(USER_KEYRING_PATH, F_OK) >= 0)
×
UNCOV
464
                        cmd[k++] = "--keyring=" USER_KEYRING_PATH;
×
465
                else
466
                        cmd[k++] = "--keyring=" VENDOR_KEYRING_PATH;
×
467

468
                cmd[k++] = "--verify";
×
UNCOV
469
                if (signature) {
×
UNCOV
470
                        cmd[k++] = sig_file_path;
×
471
                        cmd[k++] = "-";
×
UNCOV
472
                        cmd[k++] = NULL;
×
473
                }
474

475
                execvp("gpg2", (char * const *) cmd);
×
476
                execvp("gpg", (char * const *) cmd);
×
477
                log_error_errno(errno, "Failed to execute gpg: %m");
×
UNCOV
478
                _exit(EXIT_FAILURE);
×
479
        }
480

UNCOV
481
        gpg_pipe[0] = safe_close(gpg_pipe[0]);
×
482

UNCOV
483
        r = loop_write(gpg_pipe[1], payload, payload_size);
×
484
        if (r < 0) {
×
485
                log_error_errno(r, "Failed to write to pipe: %m");
×
486
                goto finish;
×
487
        }
488

UNCOV
489
        gpg_pipe[1] = safe_close(gpg_pipe[1]);
×
490

UNCOV
491
        r = wait_for_terminate_and_check("gpg", TAKE_PID(pid), WAIT_LOG_ABNORMAL);
×
UNCOV
492
        if (r < 0)
×
UNCOV
493
                goto finish;
×
UNCOV
494
        if (r != EXIT_SUCCESS)
×
UNCOV
495
                r = log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
×
496
                                    "DOWNLOAD INVALID: Signature verification failed.");
497
        else {
UNCOV
498
                log_info("Signature verification succeeded.");
×
499
                r = 0;
500
        }
501

UNCOV
502
finish:
×
UNCOV
503
        if (signature_size > 0)
×
UNCOV
504
                (void) unlink(sig_file_path);
×
505

506
        return r;
507
}
508

509
int pull_verify(ImportVerify verify,
24✔
510
                const char *checksum, /* Verify with literal checksum */
511
                PullJob *main_job,
512
                PullJob *checksum_job,
513
                PullJob *signature_job,
514
                PullJob *settings_job,
515
                PullJob *roothash_job,
516
                PullJob *roothash_signature_job,
517
                PullJob *verity_job) {
518

519
        _cleanup_free_ char *fn = NULL;
24✔
520
        VerificationStyle style;
24✔
521
        PullJob *verify_job;
24✔
522
        int r;
24✔
523

524
        assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX);
24✔
525
        assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0);
24✔
526
        assert((verify < 0) || !checksum);
24✔
527
        assert(main_job);
24✔
528
        assert(main_job->state == PULL_JOB_DONE);
24✔
529

530
        if (verify == IMPORT_VERIFY_NO) /* verification turned off */
24✔
531
                return 0;
532

533
        if (checksum) {
8✔
534
                /* Verification by literal checksum */
535
                assert(!checksum_job);
8✔
536
                assert(!signature_job);
8✔
537
                assert(!settings_job);
8✔
538
                assert(!roothash_job);
8✔
539
                assert(!roothash_signature_job);
8✔
540
                assert(!verity_job);
8✔
541

542
                assert(main_job->calc_checksum);
8✔
543
                assert(main_job->checksum);
8✔
544

545
                if (!strcaseeq(checksum, main_job->checksum))
8✔
UNCOV
546
                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
×
547
                                               "DOWNLOAD INVALID: Checksum of %s file did not check out, file has been tampered with.",
548
                                               main_job->url);
549

550
                return 0;
551
        }
552

553
        r = import_url_last_component(main_job->url, &fn);
×
UNCOV
554
        if (r < 0)
×
UNCOV
555
                return log_error_errno(r, "Failed to extract filename from URL '%s': %m", main_job->url);
×
556

557
        if (signature_style_from_filename(fn) >= 0)
×
558
                return log_error_errno(SYNTHETIC_ERRNO(ELOOP),
×
559
                                       "Main download is a signature file, can't verify it.");
560

UNCOV
561
        if (is_checksum_file(fn)) {
×
UNCOV
562
                log_debug("Main download is a checksum file, can't validate its checksum with itself, skipping.");
×
563
                verify_job = main_job;
564
        } else {
UNCOV
565
                assert(main_job->calc_checksum);
×
566
                assert(main_job->checksum);
×
UNCOV
567
                assert(checksum_job);
×
UNCOV
568
                assert(checksum_job->state == PULL_JOB_DONE);
×
569

UNCOV
570
                if (!checksum_job->payload || checksum_job->payload_size <= 0)
×
571
                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
×
572
                                               "Checksum is empty, cannot verify.");
573

UNCOV
574
                PullJob *j;
×
575
                FOREACH_ARGUMENT(j, main_job, settings_job, roothash_job, roothash_signature_job, verity_job) {
×
576
                        r = verify_one(checksum_job, j);
×
577
                        if (r < 0)
×
UNCOV
578
                                return r;
×
579
                }
580

UNCOV
581
                verify_job = checksum_job;
×
582
        }
583

UNCOV
584
        if (verify != IMPORT_VERIFY_SIGNATURE)
×
585
                return 0;
586

UNCOV
587
        assert(verify_job);
×
588

589
        r = verification_style_from_url(verify_job->url, &style);
×
590
        if (r < 0)
×
UNCOV
591
                return log_error_errno(r, "Failed to determine verification style from URL '%s': %m", verify_job->url);
×
592

593
        assert(signature_job);
×
UNCOV
594
        assert(signature_job->state == PULL_JOB_DONE);
×
595

UNCOV
596
        if (!signature_job->payload || signature_job->payload_size <= 0)
×
597
                return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
×
598
                                       "Signature is empty, cannot verify.");
599

UNCOV
600
        return verify_gpg(verify_job->payload, verify_job->payload_size, signature_job->payload, signature_job->payload_size);
×
601
}
602

603
int verification_style_from_url(const char *url, VerificationStyle *ret) {
×
UNCOV
604
        _cleanup_free_ char *last = NULL;
×
UNCOV
605
        int r;
×
606

607
        assert(url);
×
608
        assert(ret);
×
609

610
        /* Determines which kind of verification style is appropriate for this url */
611

UNCOV
612
        r = import_url_last_component(url, &last);
×
UNCOV
613
        if (r < 0)
×
614
                return r;
615

616
        if (streq(last, "SHA256SUMS")) {
×
UNCOV
617
                *ret = VERIFICATION_PER_DIRECTORY;
×
618
                return 0;
×
619
        }
620

UNCOV
621
        if (endswith(last, ".sha256")) {
×
622
                *ret = VERIFICATION_PER_FILE;
×
623
                return 0;
×
624
        }
625

626
        return -EINVAL;
627
}
628

629
int pull_job_restart_with_sha256sum(PullJob *j, char **ret) {
×
UNCOV
630
        VerificationStyle style;
×
631
        int r;
×
632

633
        assert(j);
×
634

635
        /* Generic implementation of a PullJobNotFound handler, that restarts the job requesting SHA256SUMS */
636

UNCOV
637
        r = verification_style_from_url(j->url, &style);
×
UNCOV
638
        if (r < 0)
×
UNCOV
639
                return log_error_errno(r, "Failed to determine verification style of URL '%s': %m", j->url);
×
640

UNCOV
641
        if (style == VERIFICATION_PER_DIRECTORY) { /* Nothing to do anymore */
×
UNCOV
642
                *ret = NULL;
×
UNCOV
643
                return 0;
×
644
        }
645

UNCOV
646
        assert(style == VERIFICATION_PER_FILE); /* This must have been .sha256 style URL before */
×
647

648
        log_debug("Got 404 for '%s', now trying to get SHA256SUMS instead.", j->url);
×
649

650
        r = import_url_change_last_component(j->url, "SHA256SUMS", ret);
×
UNCOV
651
        if (r < 0)
×
UNCOV
652
                return log_error_errno(r, "Failed to replace SHA256SUMS suffix: %m");
×
653

654
        return 1;
655
}
656

657
int signature_style_from_url(const char *url, SignatureStyle *ret, char **ret_filename) {
×
UNCOV
658
        _cleanup_free_ char *last = NULL;
×
659
        SignatureStyle style;
×
UNCOV
660
        int r;
×
661

662
        assert(url);
×
UNCOV
663
        assert(ret);
×
UNCOV
664
        assert(ret_filename);
×
665

666
        /* Determines which kind of signature style is appropriate for this url */
667

UNCOV
668
        r = import_url_last_component(url, &last);
×
UNCOV
669
        if (r < 0)
×
670
                return r;
671

UNCOV
672
        style = signature_style_from_filename(last);
×
UNCOV
673
        if (style < 0)
×
674
                return style;
675

UNCOV
676
        *ret_filename = TAKE_PTR(last);
×
UNCOV
677
        *ret = style;
×
UNCOV
678
        return 0;
×
679
}
680

UNCOV
681
int pull_job_restart_with_signature(PullJob *j, char **ret) {
×
UNCOV
682
        _cleanup_free_ char *last = NULL;
×
UNCOV
683
        SignatureStyle style;
×
UNCOV
684
        int r;
×
685

UNCOV
686
        assert(j);
×
687

688
        /* Generic implementation of a PullJobNotFound handler, that restarts the job requesting a different
689
         * signature file. After the initial file, additional *.sha256.gpg, SHA256SUMS.gpg and SHA256SUMS.asc
690
         * are tried in this order. */
691

UNCOV
692
        r = signature_style_from_url(j->url, &style, &last);
×
UNCOV
693
        if (r < 0)
×
UNCOV
694
                return log_error_errno(r, "Failed to determine signature style of URL '%s': %m", j->url);
×
695

UNCOV
696
        switch (style) {
×
697

UNCOV
698
        case SIGNATURE_ASC_PER_DIRECTORY: /* Nothing to do anymore */
×
UNCOV
699
                *ret = NULL;
×
UNCOV
700
                return 0;
×
701

702
        case SIGNATURE_ASC_PER_FILE: { /* Try .sha256.gpg next */
UNCOV
703
                char *ext;
×
704

UNCOV
705
                log_debug("Got 404 for '%s', now trying to get .sha256.gpg instead.", j->url);
×
706

UNCOV
707
                ext = endswith(last, ".asc");
×
UNCOV
708
                assert(ext);
×
UNCOV
709
                strcpy(ext, ".gpg");
×
710

UNCOV
711
                r = import_url_change_last_component(j->url, last, ret);
×
UNCOV
712
                if (r < 0)
×
UNCOV
713
                        return log_error_errno(r, "Failed to replace .sha256.asc suffix: %m");
×
714
                break;
715
        }
716

717
        case SIGNATURE_GPG_PER_FILE: /* Try SHA256SUMS.gpg next */
UNCOV
718
                log_debug("Got 404 for '%s', now trying to get SHA256SUMS.gpg instead.", j->url);
×
UNCOV
719
                r = import_url_change_last_component(j->url, "SHA256SUMS.gpg", ret);
×
UNCOV
720
                if (r < 0)
×
UNCOV
721
                        return log_error_errno(r, "Failed to replace SHA256SUMS suffix: %m");
×
722
                break;
723

724
        case SIGNATURE_GPG_PER_DIRECTORY:
UNCOV
725
                log_debug("Got 404 for '%s', now trying to get SHA256SUMS.asc instead.", j->url);
×
UNCOV
726
                r = import_url_change_last_component(j->url, "SHA256SUMS.asc", ret);
×
UNCOV
727
                if (r < 0)
×
UNCOV
728
                        return log_error_errno(r, "Failed to replace SHA256SUMS.gpg suffix: %m");
×
729
                break;
730

UNCOV
731
        default:
×
UNCOV
732
                assert_not_reached();
×
733
        }
734

735
        return 1;
736
}
737

738
bool pull_validate_local(const char *name, ImportFlags flags) {
13✔
739

740
        if (FLAGS_SET(flags, IMPORT_DIRECT))
13✔
741
                return path_is_valid(name);
8✔
742

743
        return image_name_is_valid(name);
5✔
744
}
745

UNCOV
746
int pull_url_needs_checksum(const char *url) {
×
UNCOV
747
        _cleanup_free_ char *fn = NULL;
×
UNCOV
748
        int r;
×
749

750
        /* Returns true if we need to validate this resource via a hash value. This returns true for all
751
         * files — except for gpg signature files and SHA256SUMS files and the like, which are validated with
752
         * a validation tool like gpg. */
753

UNCOV
754
        r = import_url_last_component(url, &fn);
×
UNCOV
755
        if (r == -EADDRNOTAVAIL) /* no last component? then let's assume it's not a signature/checksum file */
×
756
                return false;
UNCOV
757
        if (r < 0)
×
758
                return r;
759

UNCOV
760
        return !is_checksum_file(fn) && signature_style_from_filename(fn) < 0;
×
761
}
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