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

systemd / systemd / 17253546277

26 Aug 2025 06:24PM UTC coverage: 72.232% (-0.005%) from 72.237%
17253546277

push

github

bluca
shell-completion: support -i option for journalctl

Follow-up for dde54b8a8.

302507 of 418798 relevant lines covered (72.23%)

653328.97 hits per line

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

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

3
#include "sd-id128.h"
4

5
#include "alloc-util.h"
6
#include "dirent-util.h"
7
#include "escape.h"
8
#include "fd-util.h"
9
#include "hexdecoct.h"
10
#include "io-util.h"
11
#include "log.h"
12
#include "memory-util.h"
13
#include "os-util.h"
14
#include "path-util.h"
15
#include "process-util.h"
16
#include "pull-common.h"
17
#include "pull-job.h"
18
#include "rm-rf.h"
19
#include "siphash24.h"
20
#include "string-util.h"
21
#include "strv.h"
22
#include "tmpfile-util.h"
23
#include "web-util.h"
24

25
#define FILENAME_ESCAPE "/.#\"\'"
26
#define HASH_URL_THRESHOLD_LENGTH (_POSIX_PATH_MAX - 16)
27

28
int pull_find_old_etags(
5✔
29
                const char *url,
30
                const char *image_root,
31
                int dt,
32
                const char *prefix,
33
                const char *suffix,
34
                char ***etags) {
35

36
        int r;
5✔
37

38
        assert(url);
5✔
39
        assert(image_root);
5✔
40
        assert(etags);
5✔
41

42
        _cleanup_free_ char *escaped_url = xescape(url, FILENAME_ESCAPE);
10✔
43
        if (!escaped_url)
5✔
44
                return -ENOMEM;
45

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

53
                return -errno;
×
54
        }
55

56
        _cleanup_strv_free_ char **ans = NULL;
5✔
57

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

62
                if (de->d_type != DT_UNKNOWN &&
34✔
63
                    de->d_type != dt)
34✔
64
                        continue;
10✔
65

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

73
                a = startswith(a, escaped_url);
×
74
                if (!a)
×
75
                        continue;
×
76

77
                a = startswith(a, ".");
×
78
                if (!a)
×
79
                        continue;
×
80

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

88
                if (a >= b)
×
89
                        continue;
×
90

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

97
                if (!http_etag_is_valid(u))
×
98
                        continue;
×
99

100
                r = strv_consume(&ans, TAKE_PTR(u));
×
101
                if (r < 0)
×
102
                        return r;
103
        }
104

105
        *etags = TAKE_PTR(ans);
5✔
106

107
        return 0;
5✔
108
}
109

110
static int hash_url(const char *url, char **ret) {
×
111
        uint64_t h;
×
112
        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);
×
113

114
        assert(url);
×
115

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

120
        return 0;
121
}
122

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

127
        assert(url);
7✔
128
        assert(image_root);
7✔
129
        assert(ret);
7✔
130

131
        escaped_url = xescape(url, FILENAME_ESCAPE);
7✔
132
        if (!escaped_url)
7✔
133
                return -ENOMEM;
134

135
        if (etag) {
7✔
136
                escaped_etag = xescape(etag, FILENAME_ESCAPE);
×
137
                if (!escaped_etag)
×
138
                        return -ENOMEM;
139
        }
140

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

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

153
                free(path);
×
154

155
                r = hash_url(url, &hash);
×
156
                if (r < 0)
×
157
                        return r;
158

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

165
        *ret = path;
7✔
166
        return 0;
7✔
167
}
168

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

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

185
        assert(ret);
3✔
186
        assert(url);
3✔
187
        assert(strip_suffixes);
3✔
188
        assert(glue);
3✔
189

190
        r = import_url_last_component(url, &last_component);
3✔
191
        if (r < 0)
3✔
192
                return r;
193

194
        r = strip_suffixes(last_component, &ll);
3✔
195
        if (r < 0)
3✔
196
                return r;
197

198
        q = strjoina(ll, suffix);
15✔
199

200
        r = import_url_change_last_component(url, q, &auxiliary_url);
3✔
201
        if (r < 0)
3✔
202
                return r;
203

204
        r = pull_job_new(&job, auxiliary_url, glue, userdata);
3✔
205
        if (r < 0)
3✔
206
                return r;
207

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

213
        *ret = TAKE_PTR(job);
3✔
214
        return 0;
3✔
215
}
216

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

220
        if (!fn)
1✔
221
                return false;
222

223
        return streq(fn, "SHA256SUMS") || endswith(fn, ".sha256");
2✔
224
}
225

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

229
        if (!fn)
2✔
230
                return _SIGNATURE_STYLE_INVALID;
231

232
        if (streq(fn, "SHA256SUMS.gpg"))
2✔
233
                return SIGNATURE_GPG_PER_DIRECTORY;
234

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

238
        if (endswith(fn, ".sha256.gpg"))
2✔
239
                return SIGNATURE_GPG_PER_FILE;
240

241
        if (endswith(fn, ".sha256.asc"))
2✔
242
                return SIGNATURE_ASC_PER_FILE;
×
243

244
        return _SIGNATURE_STYLE_INVALID;
245
}
246

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

257
        _cleanup_(pull_job_unrefp) PullJob *checksum_job = NULL, *signature_job = NULL;
25✔
258
        _cleanup_free_ char *fn = NULL;
25✔
259
        int r;
25✔
260

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

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

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

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

287
                /* Queue jobs for the checksum file for the image. */
288

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

294
                r = import_url_change_last_component(url, suffixed, &checksum_url);
1✔
295
                if (r < 0)
1✔
296
                        return r;
297

298
                r = pull_job_new(&checksum_job, checksum_url, glue, userdata);
1✔
299
                if (r < 0)
1✔
300
                        return r;
301

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

307
        if (verify == IMPORT_VERIFY_SIGNATURE && signature_style_from_filename(fn) < 0) {
1✔
308
                _cleanup_free_ char *signature_url = NULL;
1✔
309
                const char *suffixed = NULL;
1✔
310

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

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

321
                r = pull_job_new(&signature_job, signature_url, glue, userdata);
1✔
322
                if (r < 0)
1✔
323
                        return r;
324

325
                signature_job->on_finished = on_finished;
1✔
326
                signature_job->uncompressed_max = signature_job->compressed_max = 1ULL * 1024ULL * 1024ULL;
1✔
327
                signature_job->on_not_found = pull_job_restart_with_signature;
1✔
328
        }
329

330
        *ret_checksum_job = TAKE_PTR(checksum_job);
1✔
331
        *ret_signature_job = TAKE_PTR(signature_job);
1✔
332
        return 0;
1✔
333
}
334

335
static int verify_one(PullJob *checksum_job, PullJob *job) {
×
336
        _cleanup_free_ char *fn = NULL;
×
337
        int r;
×
338

339
        assert(checksum_job);
×
340

341
        if (!job)
×
342
                return 0;
343

344
        assert(IN_SET(job->state, PULL_JOB_DONE, PULL_JOB_FAILED));
×
345

346
        /* Don't verify the checksum if we didn't actually successfully download something new */
347
        if (job->state != PULL_JOB_DONE)
×
348
                return 0;
349
        if (job->error != 0)
×
350
                return 0;
351
        if (job->etag_exists)
×
352
                return 0;
353

354
        assert(job->calc_checksum);
×
355
        assert(job->checksum);
×
356

357
        r = import_url_last_component(job->url, &fn);
×
358
        if (r < 0)
×
359
                return log_error_errno(r, "Failed to extract filename from URL '%s': %m", job->url);
×
360

361
        if (!filename_is_valid(fn))
×
362
                return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
×
363
                                       "Cannot verify checksum, could not determine server-side file name.");
364

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

369
        const char *p = NULL;
×
370
        FOREACH_STRING(separator,
×
371
                       " *", /* separator for binary mode */
372
                       "  ", /* separator for text mode */
373
                       " "   /* non-standard separator used by linuxcontainers.org */) {
374
                _cleanup_free_ char *line = NULL;
×
375

376
                line = strjoin(job->checksum, separator, fn, "\n");
×
377
                if (!line)
×
378
                        return log_oom();
×
379

380
                p = memmem_safe(checksum_job->payload,
×
381
                                checksum_job->payload_size,
382
                                line,
383
                                strlen(line));
384
                if (p)
×
385
                        break;
386
        }
387

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

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

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

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

407
        assert(payload || payload_size == 0);
×
408
        assert(signature || signature_size == 0);
×
409

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

414
        if (signature_size > 0) {
×
415
                _cleanup_close_ int sig_file = -EBADF;
×
416

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

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

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

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

459
                /* Child */
460

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

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

472
                cmd[k++] = "--verify";
×
473
                if (signature) {
×
474
                        cmd[k++] = sig_file_path;
×
475
                        cmd[k++] = "-";
×
476
                        cmd[k++] = NULL;
×
477
                }
478

479
                execvp("gpg2", (char * const *) cmd);
×
480
                execvp("gpg", (char * const *) cmd);
×
481
                log_error_errno(errno, "Failed to execute gpg: %m");
×
482
                _exit(EXIT_FAILURE);
×
483
        }
484

485
        gpg_pipe[0] = safe_close(gpg_pipe[0]);
×
486

487
        r = loop_write(gpg_pipe[1], payload, payload_size);
×
488
        if (r < 0) {
×
489
                log_error_errno(r, "Failed to write to pipe: %m");
×
490
                goto finish;
×
491
        }
492

493
        gpg_pipe[1] = safe_close(gpg_pipe[1]);
×
494

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

506
finish:
×
507
        if (signature_size > 0)
×
508
                (void) unlink(sig_file_path);
×
509

510
        return r;
511
}
512

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

523
        _cleanup_free_ char *fn = NULL;
24✔
524
        VerificationStyle style;
24✔
525
        PullJob *verify_job;
24✔
526
        int r;
24✔
527

528
        assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX);
24✔
529
        assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0);
24✔
530
        assert((verify < 0) || !checksum);
24✔
531
        assert(main_job);
24✔
532
        assert(main_job->state == PULL_JOB_DONE);
24✔
533

534
        if (verify == IMPORT_VERIFY_NO) /* verification turned off */
24✔
535
                return 0;
536

537
        if (checksum) {
8✔
538
                /* Verification by literal checksum */
539
                assert(!checksum_job);
8✔
540
                assert(!signature_job);
8✔
541
                assert(!settings_job);
8✔
542
                assert(!roothash_job);
8✔
543
                assert(!roothash_signature_job);
8✔
544
                assert(!verity_job);
8✔
545

546
                assert(main_job->calc_checksum);
8✔
547
                assert(main_job->checksum);
8✔
548

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

554
                return 0;
555
        }
556

557
        r = import_url_last_component(main_job->url, &fn);
×
558
        if (r < 0)
×
559
                return log_error_errno(r, "Failed to extract filename from URL '%s': %m", main_job->url);
×
560

561
        if (signature_style_from_filename(fn) >= 0)
×
562
                return log_error_errno(SYNTHETIC_ERRNO(ELOOP),
×
563
                                       "Main download is a signature file, can't verify it.");
564

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

574
                if (!checksum_job->payload || checksum_job->payload_size <= 0)
×
575
                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
×
576
                                               "Checksum is empty, cannot verify.");
577

578
                PullJob *j;
×
579
                FOREACH_ARGUMENT(j, main_job, settings_job, roothash_job, roothash_signature_job, verity_job) {
×
580
                        r = verify_one(checksum_job, j);
×
581
                        if (r < 0)
×
582
                                return r;
×
583
                }
584

585
                verify_job = checksum_job;
×
586
        }
587

588
        if (verify != IMPORT_VERIFY_SIGNATURE)
×
589
                return 0;
590

591
        assert(verify_job);
×
592

593
        r = verification_style_from_url(verify_job->url, &style);
×
594
        if (r < 0)
×
595
                return log_error_errno(r, "Failed to determine verification style from URL '%s': %m", verify_job->url);
×
596

597
        assert(signature_job);
×
598
        assert(signature_job->state == PULL_JOB_DONE);
×
599

600
        if (!signature_job->payload || signature_job->payload_size <= 0)
×
601
                return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
×
602
                                       "Signature is empty, cannot verify.");
603

604
        return verify_gpg(verify_job->payload, verify_job->payload_size, signature_job->payload, signature_job->payload_size);
×
605
}
606

607
int verification_style_from_url(const char *url, VerificationStyle *ret) {
×
608
        _cleanup_free_ char *last = NULL;
×
609
        int r;
×
610

611
        assert(url);
×
612
        assert(ret);
×
613

614
        /* Determines which kind of verification style is appropriate for this url */
615

616
        r = import_url_last_component(url, &last);
×
617
        if (r < 0)
×
618
                return r;
619

620
        if (streq(last, "SHA256SUMS")) {
×
621
                *ret = VERIFICATION_PER_DIRECTORY;
×
622
                return 0;
×
623
        }
624

625
        if (endswith(last, ".sha256")) {
×
626
                *ret = VERIFICATION_PER_FILE;
×
627
                return 0;
×
628
        }
629

630
        return -EINVAL;
631
}
632

633
int pull_job_restart_with_sha256sum(PullJob *j, char **ret) {
×
634
        VerificationStyle style;
×
635
        int r;
×
636

637
        assert(j);
×
638

639
        /* Generic implementation of a PullJobNotFound handler, that restarts the job requesting SHA256SUMS */
640

641
        r = verification_style_from_url(j->url, &style);
×
642
        if (r < 0)
×
643
                return log_error_errno(r, "Failed to determine verification style of URL '%s': %m", j->url);
×
644

645
        if (style == VERIFICATION_PER_DIRECTORY) { /* Nothing to do anymore */
×
646
                *ret = NULL;
×
647
                return 0;
×
648
        }
649

650
        assert(style == VERIFICATION_PER_FILE); /* This must have been .sha256 style URL before */
×
651

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

654
        r = import_url_change_last_component(j->url, "SHA256SUMS", ret);
×
655
        if (r < 0)
×
656
                return log_error_errno(r, "Failed to replace SHA256SUMS suffix: %m");
×
657

658
        return 1;
659
}
660

661
int signature_style_from_url(const char *url, SignatureStyle *ret, char **ret_filename) {
×
662
        _cleanup_free_ char *last = NULL;
×
663
        SignatureStyle style;
×
664
        int r;
×
665

666
        assert(url);
×
667
        assert(ret);
×
668
        assert(ret_filename);
×
669

670
        /* Determines which kind of signature style is appropriate for this url */
671

672
        r = import_url_last_component(url, &last);
×
673
        if (r < 0)
×
674
                return r;
675

676
        style = signature_style_from_filename(last);
×
677
        if (style < 0)
×
678
                return style;
679

680
        *ret_filename = TAKE_PTR(last);
×
681
        *ret = style;
×
682
        return 0;
×
683
}
684

685
int pull_job_restart_with_signature(PullJob *j, char **ret) {
×
686
        _cleanup_free_ char *last = NULL;
×
687
        SignatureStyle style;
×
688
        int r;
×
689

690
        assert(j);
×
691

692
        /* Generic implementation of a PullJobNotFound handler, that restarts the job requesting a different
693
         * signature file. After the initial file, additional *.sha256.gpg, SHA256SUMS.gpg and SHA256SUMS.asc
694
         * are tried in this order. */
695

696
        r = signature_style_from_url(j->url, &style, &last);
×
697
        if (r < 0)
×
698
                return log_error_errno(r, "Failed to determine signature style of URL '%s': %m", j->url);
×
699

700
        switch (style) {
×
701

702
        case SIGNATURE_ASC_PER_DIRECTORY: /* Nothing to do anymore */
×
703
                *ret = NULL;
×
704
                return 0;
×
705

706
        case SIGNATURE_ASC_PER_FILE: { /* Try .sha256.gpg next */
707
                char *ext;
×
708

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

711
                ext = endswith(last, ".asc");
×
712
                assert(ext);
×
713
                strcpy(ext, ".gpg");
×
714

715
                r = import_url_change_last_component(j->url, last, ret);
×
716
                if (r < 0)
×
717
                        return log_error_errno(r, "Failed to replace .sha256.asc suffix: %m");
×
718
                break;
719
        }
720

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

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

735
        default:
×
736
                assert_not_reached();
×
737
        }
738

739
        return 1;
740
}
741

742
bool pull_validate_local(const char *name, ImportFlags flags) {
13✔
743

744
        if (FLAGS_SET(flags, IMPORT_DIRECT))
13✔
745
                return path_is_valid(name);
8✔
746

747
        return image_name_is_valid(name);
5✔
748
}
749

750
int pull_url_needs_checksum(const char *url) {
×
751
        _cleanup_free_ char *fn = NULL;
×
752
        int r;
×
753

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

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

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