1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2015 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include <sys/prctl.h>
23 #include "alloc-util.h"
24 #include "btrfs-util.h"
25 #include "capability-util.h"
27 #include "dirent-util.h"
31 #include "path-util.h"
32 #include "process-util.h"
33 #include "pull-common.h"
36 #include "signal-util.h"
37 #include "siphash24.h"
38 #include "string-util.h"
43 #define FILENAME_ESCAPE "/.#\"\'"
44 #define HASH_URL_THRESHOLD_LENGTH (_POSIX_PATH_MAX - 16)
46 int pull_find_old_etags(
48 const char *image_root
,
54 _cleanup_free_
char *escaped_url
= NULL
;
55 _cleanup_closedir_
DIR *d
= NULL
;
56 _cleanup_strv_free_
char **l
= NULL
;
64 image_root
= "/var/lib/machines";
66 escaped_url
= xescape(url
, FILENAME_ESCAPE
);
70 d
= opendir(image_root
);
72 if (errno
== ENOENT
) {
80 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
84 if (de
->d_type
!= DT_UNKNOWN
&&
89 a
= startswith(de
->d_name
, prefix
);
95 a
= startswith(a
, escaped_url
);
99 a
= startswith(a
, ".");
104 b
= endswith(de
->d_name
, suffix
);
108 b
= strchr(de
->d_name
, 0);
113 r
= cunescape_length(a
, b
- a
, 0, &u
);
117 if (!http_etag_is_valid(u
)) {
122 r
= strv_consume(&l
, u
);
133 int pull_make_local_copy(const char *final
, const char *image_root
, const char *local
, bool force_local
) {
141 image_root
= "/var/lib/machines";
143 p
= strjoina(image_root
, "/", local
);
146 (void) rm_rf(p
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
148 r
= btrfs_subvol_snapshot(final
, p
,
149 BTRFS_SNAPSHOT_QUOTA
|
150 BTRFS_SNAPSHOT_FALLBACK_COPY
|
151 BTRFS_SNAPSHOT_FALLBACK_DIRECTORY
|
152 BTRFS_SNAPSHOT_RECURSIVE
);
154 return log_error_errno(r
, "Failed to create local image: %m");
156 log_info("Created new local image '%s'.", local
);
161 static int hash_url(const char *url
, char **ret
) {
163 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
);
167 h
= siphash24(url
, strlen(url
), k
.bytes
);
168 if (asprintf(ret
, "%"PRIx64
, h
) < 0)
174 int pull_make_path(const char *url
, const char *etag
, const char *image_root
, const char *prefix
, const char *suffix
, char **ret
) {
175 _cleanup_free_
char *escaped_url
= NULL
, *escaped_etag
= NULL
;
182 image_root
= "/var/lib/machines";
184 escaped_url
= xescape(url
, FILENAME_ESCAPE
);
189 escaped_etag
= xescape(etag
, FILENAME_ESCAPE
);
194 path
= strjoin(image_root
, "/", strempty(prefix
), escaped_url
, escaped_etag
? "." : "",
195 strempty(escaped_etag
), strempty(suffix
));
199 /* URLs might make the path longer than the maximum allowed length for a file name.
200 * When that happens, a URL hash is used instead. Paths returned by this function
201 * can be later used with tempfn_random() which adds 16 bytes to the resulting name. */
202 if (strlen(path
) >= HASH_URL_THRESHOLD_LENGTH
) {
203 _cleanup_free_
char *hash
= NULL
;
208 r
= hash_url(url
, &hash
);
212 path
= strjoin(image_root
, "/", strempty(prefix
), hash
, escaped_etag
? "." : "",
213 strempty(escaped_etag
), strempty(suffix
));
222 int pull_make_auxiliary_job(
225 int (*strip_suffixes
)(const char *name
, char **ret
),
228 PullJobFinished on_finished
,
231 _cleanup_free_
char *last_component
= NULL
, *ll
= NULL
, *auxiliary_url
= NULL
;
232 _cleanup_(pull_job_unrefp
) PullJob
*job
= NULL
;
238 assert(strip_suffixes
);
241 r
= import_url_last_component(url
, &last_component
);
245 r
= strip_suffixes(last_component
, &ll
);
249 q
= strjoina(ll
, suffix
);
251 r
= import_url_change_last_component(url
, q
, &auxiliary_url
);
255 r
= pull_job_new(&job
, auxiliary_url
, glue
, userdata
);
259 job
->on_finished
= on_finished
;
260 job
->compressed_max
= job
->uncompressed_max
= 1ULL * 1024ULL * 1024ULL;
268 int pull_make_verification_jobs(
269 PullJob
**ret_checksum_job
,
270 PullJob
**ret_signature_job
,
274 PullJobFinished on_finished
,
277 _cleanup_(pull_job_unrefp
) PullJob
*checksum_job
= NULL
, *signature_job
= NULL
;
279 const char *chksums
= NULL
;
281 assert(ret_checksum_job
);
282 assert(ret_signature_job
);
284 assert(verify
< _IMPORT_VERIFY_MAX
);
288 if (verify
!= IMPORT_VERIFY_NO
) {
289 _cleanup_free_
char *checksum_url
= NULL
, *fn
= NULL
;
291 /* Queue jobs for the checksum file for the image. */
292 r
= import_url_last_component(url
, &fn
);
296 chksums
= strjoina(fn
, ".sha256");
298 r
= import_url_change_last_component(url
, chksums
, &checksum_url
);
302 r
= pull_job_new(&checksum_job
, checksum_url
, glue
, userdata
);
306 checksum_job
->on_finished
= on_finished
;
307 checksum_job
->uncompressed_max
= checksum_job
->compressed_max
= 1ULL * 1024ULL * 1024ULL;
310 if (verify
== IMPORT_VERIFY_SIGNATURE
) {
311 _cleanup_free_
char *signature_url
= NULL
;
313 /* Queue job for the SHA256SUMS.gpg file for the image. */
314 r
= import_url_change_last_component(url
, "SHA256SUMS.gpg", &signature_url
);
318 r
= pull_job_new(&signature_job
, signature_url
, glue
, userdata
);
322 signature_job
->on_finished
= on_finished
;
323 signature_job
->uncompressed_max
= signature_job
->compressed_max
= 1ULL * 1024ULL * 1024ULL;
326 *ret_checksum_job
= checksum_job
;
327 *ret_signature_job
= signature_job
;
329 checksum_job
= signature_job
= NULL
;
334 static int verify_one(PullJob
*checksum_job
, PullJob
*job
) {
335 _cleanup_free_
char *fn
= NULL
;
336 const char *line
, *p
;
339 assert(checksum_job
);
344 assert(IN_SET(job
->state
, PULL_JOB_DONE
, PULL_JOB_FAILED
));
346 /* Don't verify the checksum if we didn't actually successfully download something new */
347 if (job
->state
!= PULL_JOB_DONE
)
351 if (job
->etag_exists
)
354 assert(job
->calc_checksum
);
355 assert(job
->checksum
);
357 r
= import_url_last_component(job
->url
, &fn
);
361 if (!filename_is_valid(fn
)) {
362 log_error("Cannot verify checksum, could not determine server-side file name.");
366 line
= strjoina(job
->checksum
, " *", fn
, "\n");
368 p
= memmem(checksum_job
->payload
,
369 checksum_job
->payload_size
,
374 line
= strjoina(job
->checksum
, " ", fn
, "\n");
376 p
= memmem(checksum_job
->payload
,
377 checksum_job
->payload_size
,
382 if (!p
|| (p
!= (char*) checksum_job
->payload
&& p
[-1] != '\n')) {
383 log_error("DOWNLOAD INVALID: Checksum of %s file did not checkout, file has been tampered with.", fn
);
387 log_info("SHA256 checksum of %s is valid.", job
->url
);
391 int pull_verify(PullJob
*main_job
,
392 PullJob
*roothash_job
,
393 PullJob
*settings_job
,
394 PullJob
*checksum_job
,
395 PullJob
*signature_job
) {
397 _cleanup_close_pair_
int gpg_pipe
[2] = { -1, -1 };
398 _cleanup_close_
int sig_file
= -1;
399 char sig_file_path
[] = "/tmp/sigXXXXXX", gpg_home
[] = "/tmp/gpghomeXXXXXX";
400 _cleanup_(sigkill_waitp
) pid_t pid
= 0;
401 bool gpg_home_created
= false;
405 assert(main_job
->state
== PULL_JOB_DONE
);
410 assert(main_job
->calc_checksum
);
411 assert(main_job
->checksum
);
413 assert(checksum_job
->state
== PULL_JOB_DONE
);
415 if (!checksum_job
->payload
|| checksum_job
->payload_size
<= 0) {
416 log_error("Checksum is empty, cannot verify.");
420 r
= verify_one(checksum_job
, main_job
);
424 r
= verify_one(checksum_job
, roothash_job
);
428 r
= verify_one(checksum_job
, settings_job
);
435 if (checksum_job
->style
== VERIFICATION_PER_FILE
)
436 signature_job
= checksum_job
;
438 assert(signature_job
->state
== PULL_JOB_DONE
);
440 if (!signature_job
->payload
|| signature_job
->payload_size
<= 0) {
441 log_error("Signature is empty, cannot verify.");
445 r
= pipe2(gpg_pipe
, O_CLOEXEC
);
447 return log_error_errno(errno
, "Failed to create pipe for gpg: %m");
449 sig_file
= mkostemp(sig_file_path
, O_RDWR
);
451 return log_error_errno(errno
, "Failed to create temporary file: %m");
453 r
= loop_write(sig_file
, signature_job
->payload
, signature_job
->payload_size
, false);
455 log_error_errno(r
, "Failed to write to temporary file: %m");
459 if (!mkdtemp(gpg_home
)) {
460 r
= log_error_errno(errno
, "Failed to create tempory home for gpg: %m");
464 gpg_home_created
= true;
468 return log_error_errno(errno
, "Failed to fork off gpg: %m");
470 const char *cmd
[] = {
473 "--no-default-keyring",
474 "--no-auto-key-locate",
475 "--no-auto-check-trustdb",
477 "--trust-model=always",
478 NULL
, /* --homedir= */
479 NULL
, /* --keyring= */
481 NULL
, /* signature file */
483 NULL
/* trailing NULL */
485 unsigned k
= ELEMENTSOF(cmd
) - 6;
490 (void) reset_all_signal_handlers();
491 (void) reset_signal_mask();
492 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
494 gpg_pipe
[1] = safe_close(gpg_pipe
[1]);
496 r
= move_fd(gpg_pipe
[0], STDIN_FILENO
, false);
498 log_error_errno(errno
, "Failed to move fd: %m");
502 null_fd
= open("/dev/null", O_WRONLY
|O_NOCTTY
);
504 log_error_errno(errno
, "Failed to open /dev/null: %m");
508 r
= move_fd(null_fd
, STDOUT_FILENO
, false);
510 log_error_errno(errno
, "Failed to move fd: %m");
514 cmd
[k
++] = strjoina("--homedir=", gpg_home
);
516 /* We add the user keyring only to the command line
517 * arguments, if it's around since gpg fails
519 if (access(USER_KEYRING_PATH
, F_OK
) >= 0)
520 cmd
[k
++] = "--keyring=" USER_KEYRING_PATH
;
522 cmd
[k
++] = "--keyring=" VENDOR_KEYRING_PATH
;
524 cmd
[k
++] = "--verify";
525 if (checksum_job
->style
== VERIFICATION_PER_DIRECTORY
) {
526 cmd
[k
++] = sig_file_path
;
531 stdio_unset_cloexec();
533 execvp("gpg2", (char * const *) cmd
);
534 execvp("gpg", (char * const *) cmd
);
535 log_error_errno(errno
, "Failed to execute gpg: %m");
539 gpg_pipe
[0] = safe_close(gpg_pipe
[0]);
541 r
= loop_write(gpg_pipe
[1], checksum_job
->payload
, checksum_job
->payload_size
, false);
543 log_error_errno(r
, "Failed to write to pipe: %m");
547 gpg_pipe
[1] = safe_close(gpg_pipe
[1]);
549 r
= wait_for_terminate_and_warn("gpg", pid
, true);
554 log_error("DOWNLOAD INVALID: Signature verification failed.");
557 log_info("Signature verification succeeded.");
563 (void) unlink(sig_file_path
);
565 if (gpg_home_created
)
566 (void) rm_rf(gpg_home
, REMOVE_ROOT
|REMOVE_PHYSICAL
);