1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2015 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/prctl.h>
24 #include "btrfs-util.h"
25 #include "capability.h"
30 #include "path-util.h"
31 #include "process-util.h"
32 #include "pull-common.h"
35 #include "signal-util.h"
36 #include "siphash24.h"
37 #include "string-util.h"
41 #define FILENAME_ESCAPE "/.#\"\'"
42 #define HASH_URL_THRESHOLD_LENGTH (_POSIX_PATH_MAX - 16)
44 int pull_find_old_etags(
46 const char *image_root
,
52 _cleanup_free_
char *escaped_url
= NULL
;
53 _cleanup_closedir_
DIR *d
= NULL
;
54 _cleanup_strv_free_
char **l
= NULL
;
62 image_root
= "/var/lib/machines";
64 escaped_url
= xescape(url
, FILENAME_ESCAPE
);
68 d
= opendir(image_root
);
70 if (errno
== ENOENT
) {
78 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
82 if (de
->d_type
!= DT_UNKNOWN
&&
87 a
= startswith(de
->d_name
, prefix
);
93 a
= startswith(a
, escaped_url
);
97 a
= startswith(a
, ".");
102 b
= endswith(de
->d_name
, suffix
);
106 b
= strchr(de
->d_name
, 0);
111 r
= cunescape_length(a
, b
- a
, 0, &u
);
115 if (!http_etag_is_valid(u
)) {
120 r
= strv_consume(&l
, u
);
131 int pull_make_local_copy(const char *final
, const char *image_root
, const char *local
, bool force_local
) {
139 image_root
= "/var/lib/machines";
141 p
= strjoina(image_root
, "/", local
);
144 (void) rm_rf(p
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
146 r
= btrfs_subvol_snapshot(final
, p
, BTRFS_SNAPSHOT_QUOTA
);
148 r
= copy_tree(final
, p
, false);
150 return log_error_errno(r
, "Failed to copy image: %m");
152 return log_error_errno(r
, "Failed to create local image: %m");
154 log_info("Created new local image '%s'.", local
);
159 static int hash_url(const char *url
, char **ret
) {
161 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
);
165 siphash24((uint8_t *) &h
, url
, strlen(url
), k
.bytes
);
166 if (asprintf(ret
, "%"PRIx64
, h
) < 0)
172 int pull_make_path(const char *url
, const char *etag
, const char *image_root
, const char *prefix
, const char *suffix
, char **ret
) {
173 _cleanup_free_
char *escaped_url
= NULL
, *escaped_etag
= NULL
;
180 image_root
= "/var/lib/machines";
182 escaped_url
= xescape(url
, FILENAME_ESCAPE
);
187 escaped_etag
= xescape(etag
, FILENAME_ESCAPE
);
192 path
= strjoin(image_root
, "/", strempty(prefix
), escaped_url
, escaped_etag
? "." : "",
193 strempty(escaped_etag
), strempty(suffix
), NULL
);
197 /* URLs might make the path longer than the maximum allowed length for a file name.
198 * When that happens, a URL hash is used instead. Paths returned by this function
199 * can be later used with tempfn_random() which adds 16 bytes to the resulting name. */
200 if (strlen(path
) >= HASH_URL_THRESHOLD_LENGTH
) {
201 _cleanup_free_
char *hash
= NULL
;
206 r
= hash_url(url
, &hash
);
210 path
= strjoin(image_root
, "/", strempty(prefix
), hash
, escaped_etag
? "." : "",
211 strempty(escaped_etag
), strempty(suffix
), NULL
);
220 int pull_make_settings_job(
224 PullJobFinished on_finished
,
227 _cleanup_free_
char *last_component
= NULL
, *ll
= NULL
, *settings_url
= NULL
;
228 _cleanup_(pull_job_unrefp
) PullJob
*job
= NULL
;
236 r
= import_url_last_component(url
, &last_component
);
240 r
= tar_strip_suffixes(last_component
, &ll
);
244 q
= strjoina(ll
, ".nspawn");
246 r
= import_url_change_last_component(url
, q
, &settings_url
);
250 r
= pull_job_new(&job
, settings_url
, glue
, userdata
);
254 job
->on_finished
= on_finished
;
255 job
->compressed_max
= job
->uncompressed_max
= 1ULL * 1024ULL * 1024ULL;
263 int pull_make_verification_jobs(
264 PullJob
**ret_checksum_job
,
265 PullJob
**ret_signature_job
,
269 PullJobFinished on_finished
,
272 _cleanup_(pull_job_unrefp
) PullJob
*checksum_job
= NULL
, *signature_job
= NULL
;
275 assert(ret_checksum_job
);
276 assert(ret_signature_job
);
278 assert(verify
< _IMPORT_VERIFY_MAX
);
282 if (verify
!= IMPORT_VERIFY_NO
) {
283 _cleanup_free_
char *checksum_url
= NULL
;
285 /* Queue job for the SHA256SUMS file for the image */
286 r
= import_url_change_last_component(url
, "SHA256SUMS", &checksum_url
);
290 r
= pull_job_new(&checksum_job
, checksum_url
, glue
, userdata
);
294 checksum_job
->on_finished
= on_finished
;
295 checksum_job
->uncompressed_max
= checksum_job
->compressed_max
= 1ULL * 1024ULL * 1024ULL;
298 if (verify
== IMPORT_VERIFY_SIGNATURE
) {
299 _cleanup_free_
char *signature_url
= NULL
;
301 /* Queue job for the SHA256SUMS.gpg file for the image. */
302 r
= import_url_change_last_component(url
, "SHA256SUMS.gpg", &signature_url
);
306 r
= pull_job_new(&signature_job
, signature_url
, glue
, userdata
);
310 signature_job
->on_finished
= on_finished
;
311 signature_job
->uncompressed_max
= signature_job
->compressed_max
= 1ULL * 1024ULL * 1024ULL;
314 *ret_checksum_job
= checksum_job
;
315 *ret_signature_job
= signature_job
;
317 checksum_job
= signature_job
= NULL
;
322 int pull_verify(PullJob
*main_job
,
323 PullJob
*settings_job
,
324 PullJob
*checksum_job
,
325 PullJob
*signature_job
) {
327 _cleanup_close_pair_
int gpg_pipe
[2] = { -1, -1 };
328 _cleanup_free_
char *fn
= NULL
;
329 _cleanup_close_
int sig_file
= -1;
330 const char *p
, *line
;
331 char sig_file_path
[] = "/tmp/sigXXXXXX", gpg_home
[] = "/tmp/gpghomeXXXXXX";
332 _cleanup_sigkill_wait_ pid_t pid
= 0;
333 bool gpg_home_created
= false;
337 assert(main_job
->state
== PULL_JOB_DONE
);
342 assert(main_job
->calc_checksum
);
343 assert(main_job
->checksum
);
344 assert(checksum_job
->state
== PULL_JOB_DONE
);
346 if (!checksum_job
->payload
|| checksum_job
->payload_size
<= 0) {
347 log_error("Checksum is empty, cannot verify.");
351 r
= import_url_last_component(main_job
->url
, &fn
);
355 if (!filename_is_valid(fn
)) {
356 log_error("Cannot verify checksum, could not determine valid server-side file name.");
360 line
= strjoina(main_job
->checksum
, " *", fn
, "\n");
362 p
= memmem(checksum_job
->payload
,
363 checksum_job
->payload_size
,
367 if (!p
|| (p
!= (char*) checksum_job
->payload
&& p
[-1] != '\n')) {
368 log_error("DOWNLOAD INVALID: Checksum did not check out, payload has been tampered with.");
372 log_info("SHA256 checksum of %s is valid.", main_job
->url
);
374 assert(!settings_job
|| IN_SET(settings_job
->state
, PULL_JOB_DONE
, PULL_JOB_FAILED
));
377 settings_job
->state
== PULL_JOB_DONE
&&
378 settings_job
->error
== 0 &&
379 !settings_job
->etag_exists
) {
381 _cleanup_free_
char *settings_fn
= NULL
;
383 assert(settings_job
->calc_checksum
);
384 assert(settings_job
->checksum
);
386 r
= import_url_last_component(settings_job
->url
, &settings_fn
);
390 if (!filename_is_valid(settings_fn
)) {
391 log_error("Cannot verify checksum, could not determine server-side settings file name.");
395 line
= strjoina(settings_job
->checksum
, " *", settings_fn
, "\n");
397 p
= memmem(checksum_job
->payload
,
398 checksum_job
->payload_size
,
402 if (!p
|| (p
!= (char*) checksum_job
->payload
&& p
[-1] != '\n')) {
403 log_error("DOWNLOAD INVALID: Checksum of settings file did not checkout, settings file has been tampered with.");
407 log_info("SHA256 checksum of %s is valid.", settings_job
->url
);
413 assert(signature_job
->state
== PULL_JOB_DONE
);
415 if (!signature_job
->payload
|| signature_job
->payload_size
<= 0) {
416 log_error("Signature is empty, cannot verify.");
420 r
= pipe2(gpg_pipe
, O_CLOEXEC
);
422 return log_error_errno(errno
, "Failed to create pipe for gpg: %m");
424 sig_file
= mkostemp(sig_file_path
, O_RDWR
);
426 return log_error_errno(errno
, "Failed to create temporary file: %m");
428 r
= loop_write(sig_file
, signature_job
->payload
, signature_job
->payload_size
, false);
430 log_error_errno(r
, "Failed to write to temporary file: %m");
434 if (!mkdtemp(gpg_home
)) {
435 r
= log_error_errno(errno
, "Failed to create tempory home for gpg: %m");
439 gpg_home_created
= true;
443 return log_error_errno(errno
, "Failed to fork off gpg: %m");
445 const char *cmd
[] = {
448 "--no-default-keyring",
449 "--no-auto-key-locate",
450 "--no-auto-check-trustdb",
452 "--trust-model=always",
453 NULL
, /* --homedir= */
454 NULL
, /* --keyring= */
456 NULL
, /* signature file */
458 NULL
/* trailing NULL */
460 unsigned k
= ELEMENTSOF(cmd
) - 6;
465 (void) reset_all_signal_handlers();
466 (void) reset_signal_mask();
467 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
469 gpg_pipe
[1] = safe_close(gpg_pipe
[1]);
471 if (dup2(gpg_pipe
[0], STDIN_FILENO
) != STDIN_FILENO
) {
472 log_error_errno(errno
, "Failed to dup2() fd: %m");
476 if (gpg_pipe
[0] != STDIN_FILENO
)
477 gpg_pipe
[0] = safe_close(gpg_pipe
[0]);
479 null_fd
= open("/dev/null", O_WRONLY
|O_NOCTTY
);
481 log_error_errno(errno
, "Failed to open /dev/null: %m");
485 if (dup2(null_fd
, STDOUT_FILENO
) != STDOUT_FILENO
) {
486 log_error_errno(errno
, "Failed to dup2() fd: %m");
490 if (null_fd
!= STDOUT_FILENO
)
491 null_fd
= safe_close(null_fd
);
493 cmd
[k
++] = strjoina("--homedir=", gpg_home
);
495 /* We add the user keyring only to the command line
496 * arguments, if it's around since gpg fails
498 if (access(USER_KEYRING_PATH
, F_OK
) >= 0)
499 cmd
[k
++] = "--keyring=" USER_KEYRING_PATH
;
501 cmd
[k
++] = "--keyring=" VENDOR_KEYRING_PATH
;
503 cmd
[k
++] = "--verify";
504 cmd
[k
++] = sig_file_path
;
508 fd_cloexec(STDIN_FILENO
, false);
509 fd_cloexec(STDOUT_FILENO
, false);
510 fd_cloexec(STDERR_FILENO
, false);
512 execvp("gpg2", (char * const *) cmd
);
513 execvp("gpg", (char * const *) cmd
);
514 log_error_errno(errno
, "Failed to execute gpg: %m");
518 gpg_pipe
[0] = safe_close(gpg_pipe
[0]);
520 r
= loop_write(gpg_pipe
[1], checksum_job
->payload
, checksum_job
->payload_size
, false);
522 log_error_errno(r
, "Failed to write to pipe: %m");
526 gpg_pipe
[1] = safe_close(gpg_pipe
[1]);
528 r
= wait_for_terminate_and_warn("gpg", pid
, true);
533 log_error("DOWNLOAD INVALID: Signature verification failed.");
536 log_info("Signature verification succeeded.");
542 (void) unlink(sig_file_path
);
544 if (gpg_home_created
)
545 (void) rm_rf(gpg_home
, REMOVE_ROOT
|REMOVE_PHYSICAL
);