1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
6 #include "btrfs-util.h"
7 #include "capability-util.h"
9 #include "dirent-util.h"
13 #include "path-util.h"
14 #include "process-util.h"
15 #include "pull-common.h"
17 #include "rlimit-util.h"
19 #include "signal-util.h"
20 #include "siphash24.h"
21 #include "string-util.h"
26 #define FILENAME_ESCAPE "/.#\"\'"
27 #define HASH_URL_THRESHOLD_LENGTH (_POSIX_PATH_MAX - 16)
29 int pull_find_old_etags(
31 const char *image_root
,
37 _cleanup_free_
char *escaped_url
= NULL
;
38 _cleanup_closedir_
DIR *d
= NULL
;
39 _cleanup_strv_free_
char **l
= NULL
;
47 image_root
= "/var/lib/machines";
49 escaped_url
= xescape(url
, FILENAME_ESCAPE
);
53 d
= opendir(image_root
);
55 if (errno
== ENOENT
) {
63 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
64 _cleanup_free_
char *u
= NULL
;
67 if (de
->d_type
!= DT_UNKNOWN
&&
72 a
= startswith(de
->d_name
, prefix
);
78 a
= startswith(a
, escaped_url
);
82 a
= startswith(a
, ".");
87 b
= endswith(de
->d_name
, suffix
);
91 b
= strchr(de
->d_name
, 0);
96 r
= cunescape_length(a
, b
- a
, 0, &u
);
100 if (!http_etag_is_valid(u
))
103 r
= strv_consume(&l
, TAKE_PTR(u
));
108 *etags
= TAKE_PTR(l
);
113 int pull_make_local_copy(const char *final
, const char *image_root
, const char *local
, PullFlags flags
) {
121 image_root
= "/var/lib/machines";
123 p
= prefix_roota(image_root
, local
);
125 if (FLAGS_SET(flags
, PULL_FORCE
))
126 (void) rm_rf(p
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
128 r
= btrfs_subvol_snapshot(final
, p
,
129 BTRFS_SNAPSHOT_QUOTA
|
130 BTRFS_SNAPSHOT_FALLBACK_COPY
|
131 BTRFS_SNAPSHOT_FALLBACK_DIRECTORY
|
132 BTRFS_SNAPSHOT_RECURSIVE
);
134 return log_error_errno(r
, "Failed to create local image: %m");
136 log_info("Created new local image '%s'.", local
);
141 static int hash_url(const char *url
, char **ret
) {
143 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
);
147 h
= siphash24(url
, strlen(url
), k
.bytes
);
148 if (asprintf(ret
, "%"PRIx64
, h
) < 0)
154 int pull_make_path(const char *url
, const char *etag
, const char *image_root
, const char *prefix
, const char *suffix
, char **ret
) {
155 _cleanup_free_
char *escaped_url
= NULL
, *escaped_etag
= NULL
;
162 image_root
= "/var/lib/machines";
164 escaped_url
= xescape(url
, FILENAME_ESCAPE
);
169 escaped_etag
= xescape(etag
, FILENAME_ESCAPE
);
174 path
= strjoin(image_root
, "/", strempty(prefix
), escaped_url
, escaped_etag
? "." : "",
175 strempty(escaped_etag
), strempty(suffix
));
179 /* URLs might make the path longer than the maximum allowed length for a file name.
180 * When that happens, a URL hash is used instead. Paths returned by this function
181 * can be later used with tempfn_random() which adds 16 bytes to the resulting name. */
182 if (strlen(path
) >= HASH_URL_THRESHOLD_LENGTH
) {
183 _cleanup_free_
char *hash
= NULL
;
188 r
= hash_url(url
, &hash
);
192 path
= strjoin(image_root
, "/", strempty(prefix
), hash
, escaped_etag
? "." : "",
193 strempty(escaped_etag
), strempty(suffix
));
202 int pull_make_auxiliary_job(
205 int (*strip_suffixes
)(const char *name
, char **ret
),
208 PullJobFinished on_finished
,
211 _cleanup_free_
char *last_component
= NULL
, *ll
= NULL
, *auxiliary_url
= NULL
;
212 _cleanup_(pull_job_unrefp
) PullJob
*job
= NULL
;
218 assert(strip_suffixes
);
221 r
= import_url_last_component(url
, &last_component
);
225 r
= strip_suffixes(last_component
, &ll
);
229 q
= strjoina(ll
, suffix
);
231 r
= import_url_change_last_component(url
, q
, &auxiliary_url
);
235 r
= pull_job_new(&job
, auxiliary_url
, glue
, userdata
);
239 job
->on_finished
= on_finished
;
240 job
->compressed_max
= job
->uncompressed_max
= 1ULL * 1024ULL * 1024ULL;
242 *ret
= TAKE_PTR(job
);
247 int pull_make_verification_jobs(
248 PullJob
**ret_checksum_job
,
249 PullJob
**ret_signature_job
,
253 PullJobFinished on_finished
,
256 _cleanup_(pull_job_unrefp
) PullJob
*checksum_job
= NULL
, *signature_job
= NULL
;
259 assert(ret_checksum_job
);
260 assert(ret_signature_job
);
262 assert(verify
< _IMPORT_VERIFY_MAX
);
266 if (verify
!= IMPORT_VERIFY_NO
) {
267 _cleanup_free_
char *checksum_url
= NULL
, *fn
= NULL
;
268 const char *chksums
= NULL
;
270 /* Queue jobs for the checksum file for the image. */
271 r
= import_url_last_component(url
, &fn
);
275 chksums
= strjoina(fn
, ".sha256");
277 r
= import_url_change_last_component(url
, chksums
, &checksum_url
);
281 r
= pull_job_new(&checksum_job
, checksum_url
, glue
, userdata
);
285 checksum_job
->on_finished
= on_finished
;
286 checksum_job
->uncompressed_max
= checksum_job
->compressed_max
= 1ULL * 1024ULL * 1024ULL;
289 if (verify
== IMPORT_VERIFY_SIGNATURE
) {
290 _cleanup_free_
char *signature_url
= NULL
;
292 /* Queue job for the SHA256SUMS.gpg file for the image. */
293 r
= import_url_change_last_component(url
, "SHA256SUMS.gpg", &signature_url
);
297 r
= pull_job_new(&signature_job
, signature_url
, glue
, userdata
);
301 signature_job
->on_finished
= on_finished
;
302 signature_job
->uncompressed_max
= signature_job
->compressed_max
= 1ULL * 1024ULL * 1024ULL;
305 *ret_checksum_job
= TAKE_PTR(checksum_job
);
306 *ret_signature_job
= TAKE_PTR(signature_job
);
311 static int verify_one(PullJob
*checksum_job
, PullJob
*job
) {
312 _cleanup_free_
char *fn
= NULL
;
313 const char *line
, *p
;
316 assert(checksum_job
);
321 assert(IN_SET(job
->state
, PULL_JOB_DONE
, PULL_JOB_FAILED
));
323 /* Don't verify the checksum if we didn't actually successfully download something new */
324 if (job
->state
!= PULL_JOB_DONE
)
328 if (job
->etag_exists
)
331 assert(job
->calc_checksum
);
332 assert(job
->checksum
);
334 r
= import_url_last_component(job
->url
, &fn
);
338 if (!filename_is_valid(fn
))
339 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
340 "Cannot verify checksum, could not determine server-side file name.");
342 line
= strjoina(job
->checksum
, " *", fn
, "\n");
344 p
= memmem(checksum_job
->payload
,
345 checksum_job
->payload_size
,
350 line
= strjoina(job
->checksum
, " ", fn
, "\n");
352 p
= memmem(checksum_job
->payload
,
353 checksum_job
->payload_size
,
358 if (!p
|| (p
!= (char*) checksum_job
->payload
&& p
[-1] != '\n'))
359 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
360 "DOWNLOAD INVALID: Checksum of %s file did not checkout, file has been tampered with.", fn
);
362 log_info("SHA256 checksum of %s is valid.", job
->url
);
366 static int verify_gpg(
367 const void *payload
, size_t payload_size
,
368 const void *signature
, size_t signature_size
) {
370 _cleanup_close_pair_
int gpg_pipe
[2] = { -1, -1 };
371 char sig_file_path
[] = "/tmp/sigXXXXXX", gpg_home
[] = "/tmp/gpghomeXXXXXX";
372 _cleanup_(sigkill_waitp
) pid_t pid
= 0;
373 bool gpg_home_created
= false;
376 assert(payload
|| payload_size
== 0);
377 assert(signature
|| signature_size
== 0);
379 r
= pipe2(gpg_pipe
, O_CLOEXEC
);
381 return log_error_errno(errno
, "Failed to create pipe for gpg: %m");
383 if (signature_size
> 0) {
384 _cleanup_close_
int sig_file
= -1;
386 sig_file
= mkostemp(sig_file_path
, O_RDWR
);
388 return log_error_errno(errno
, "Failed to create temporary file: %m");
390 r
= loop_write(sig_file
, signature
, signature_size
, false);
392 log_error_errno(r
, "Failed to write to temporary file: %m");
397 if (!mkdtemp(gpg_home
)) {
398 r
= log_error_errno(errno
, "Failed to create temporary home for gpg: %m");
402 gpg_home_created
= true;
404 r
= safe_fork("(gpg)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
|FORK_LOG
, &pid
);
408 const char *cmd
[] = {
411 "--no-default-keyring",
412 "--no-auto-key-locate",
413 "--no-auto-check-trustdb",
415 "--trust-model=always",
416 NULL
, /* --homedir= */
417 NULL
, /* --keyring= */
419 NULL
, /* signature file */
421 NULL
/* trailing NULL */
423 size_t k
= ELEMENTSOF(cmd
) - 6;
427 gpg_pipe
[1] = safe_close(gpg_pipe
[1]);
429 r
= rearrange_stdio(gpg_pipe
[0], -1, STDERR_FILENO
);
431 log_error_errno(r
, "Failed to rearrange stdin/stdout: %m");
435 (void) rlimit_nofile_safe();
437 cmd
[k
++] = strjoina("--homedir=", gpg_home
);
439 /* We add the user keyring only to the command line arguments, if it's around since gpg fails
441 if (access(USER_KEYRING_PATH
, F_OK
) >= 0)
442 cmd
[k
++] = "--keyring=" USER_KEYRING_PATH
;
444 cmd
[k
++] = "--keyring=" VENDOR_KEYRING_PATH
;
446 cmd
[k
++] = "--verify";
448 cmd
[k
++] = sig_file_path
;
453 execvp("gpg2", (char * const *) cmd
);
454 execvp("gpg", (char * const *) cmd
);
455 log_error_errno(errno
, "Failed to execute gpg: %m");
459 gpg_pipe
[0] = safe_close(gpg_pipe
[0]);
461 r
= loop_write(gpg_pipe
[1], payload
, payload_size
, false);
463 log_error_errno(r
, "Failed to write to pipe: %m");
467 gpg_pipe
[1] = safe_close(gpg_pipe
[1]);
469 r
= wait_for_terminate_and_check("gpg", pid
, WAIT_LOG_ABNORMAL
);
473 if (r
!= EXIT_SUCCESS
) {
474 log_error("DOWNLOAD INVALID: Signature verification failed.");
477 log_info("Signature verification succeeded.");
482 if (signature_size
> 0)
483 (void) unlink(sig_file_path
);
485 if (gpg_home_created
)
486 (void) rm_rf(gpg_home
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
491 int pull_verify(ImportVerify verify
,
493 PullJob
*checksum_job
,
494 PullJob
*signature_job
,
495 PullJob
*settings_job
,
496 PullJob
*roothash_job
,
497 PullJob
*roothash_signature_job
,
498 PullJob
*verity_job
) {
500 VerificationStyle style
;
505 assert(main_job
->state
== PULL_JOB_DONE
);
507 if (verify
== IMPORT_VERIFY_NO
)
510 assert(main_job
->calc_checksum
);
511 assert(main_job
->checksum
);
512 assert(checksum_job
);
513 assert(checksum_job
->state
== PULL_JOB_DONE
);
515 if (!checksum_job
->payload
|| checksum_job
->payload_size
<= 0)
516 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
517 "Checksum is empty, cannot verify.");
519 FOREACH_POINTER(j
, main_job
, settings_job
, roothash_job
, roothash_signature_job
, verity_job
) {
520 r
= verify_one(checksum_job
, j
);
525 if (verify
== IMPORT_VERIFY_CHECKSUM
)
528 r
= verification_style_from_url(checksum_job
->url
, &style
);
530 return log_error_errno(r
, "Failed to determine verification style from URL '%s': %m", checksum_job
->url
);
532 if (style
== VERIFICATION_PER_DIRECTORY
) {
533 assert(signature_job
);
534 assert(signature_job
->state
== PULL_JOB_DONE
);
536 if (!signature_job
->payload
|| signature_job
->payload_size
<= 0)
537 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
538 "Signature is empty, cannot verify.");
540 return verify_gpg(checksum_job
->payload
, checksum_job
->payload_size
, signature_job
->payload
, signature_job
->payload_size
);
542 return verify_gpg(checksum_job
->payload
, checksum_job
->payload_size
, NULL
, 0);
545 int verification_style_from_url(const char *url
, VerificationStyle
*ret
) {
546 _cleanup_free_
char *last
= NULL
;
552 /* Determines which kind of verification style is appropriate for this url */
554 r
= import_url_last_component(url
, &last
);
558 if (streq(last
, "SHA256SUMS")) {
559 *ret
= VERIFICATION_PER_DIRECTORY
;
563 if (endswith(last
, ".sha256")) {
564 *ret
= VERIFICATION_PER_FILE
;
571 int pull_job_restart_with_sha256sum(PullJob
*j
, char **ret
) {
572 VerificationStyle style
;
577 /* Generic implementation of a PullJobNotFound handler, that restarts the job requesting SHA256SUMS */
579 r
= verification_style_from_url(j
->url
, &style
);
581 return log_error_errno(r
, "Failed to determine verification style of URL '%s': %m", j
->url
);
583 if (style
== VERIFICATION_PER_DIRECTORY
) /* Nothing to do anymore */
586 assert(style
== VERIFICATION_PER_FILE
); /* This must have been .sha256 style URL before */
588 log_debug("Got 404 for %s, now trying to get SHA256SUMS instead.", j
->url
);
590 r
= import_url_change_last_component(j
->url
, "SHA256SUMS", ret
);
592 return log_error_errno(r
, "Failed to replace SHA256SUMS suffix: %m");