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"
29 #include "process-util.h"
30 #include "pull-common.h"
33 #include "signal-util.h"
34 #include "siphash24.h"
35 #include "string-util.h"
39 #define FILENAME_ESCAPE "/.#\"\'"
40 #define HASH_URL_THRESHOLD_LENGTH (_POSIX_PATH_MAX - 16)
42 int pull_find_old_etags(
44 const char *image_root
,
50 _cleanup_free_
char *escaped_url
= NULL
;
51 _cleanup_closedir_
DIR *d
= NULL
;
52 _cleanup_strv_free_
char **l
= NULL
;
60 image_root
= "/var/lib/machines";
62 escaped_url
= xescape(url
, FILENAME_ESCAPE
);
66 d
= opendir(image_root
);
68 if (errno
== ENOENT
) {
76 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
80 if (de
->d_type
!= DT_UNKNOWN
&&
85 a
= startswith(de
->d_name
, prefix
);
91 a
= startswith(a
, escaped_url
);
95 a
= startswith(a
, ".");
100 b
= endswith(de
->d_name
, suffix
);
104 b
= strchr(de
->d_name
, 0);
109 r
= cunescape_length(a
, b
- a
, 0, &u
);
113 if (!http_etag_is_valid(u
)) {
118 r
= strv_consume(&l
, u
);
129 int pull_make_local_copy(const char *final
, const char *image_root
, const char *local
, bool force_local
) {
137 image_root
= "/var/lib/machines";
139 p
= strjoina(image_root
, "/", local
);
142 (void) rm_rf(p
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
144 r
= btrfs_subvol_snapshot(final
, p
, BTRFS_SNAPSHOT_QUOTA
);
146 r
= copy_tree(final
, p
, false);
148 return log_error_errno(r
, "Failed to copy image: %m");
150 return log_error_errno(r
, "Failed to create local image: %m");
152 log_info("Created new local image '%s'.", local
);
157 static int hash_url(const char *url
, char **ret
) {
159 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
);
163 siphash24((uint8_t *) &h
, url
, strlen(url
), k
.bytes
);
164 if (asprintf(ret
, "%"PRIx64
, h
) < 0)
170 int pull_make_path(const char *url
, const char *etag
, const char *image_root
, const char *prefix
, const char *suffix
, char **ret
) {
171 _cleanup_free_
char *escaped_url
= NULL
, *escaped_etag
= NULL
;
178 image_root
= "/var/lib/machines";
180 escaped_url
= xescape(url
, FILENAME_ESCAPE
);
185 escaped_etag
= xescape(etag
, FILENAME_ESCAPE
);
190 path
= strjoin(image_root
, "/", strempty(prefix
), escaped_url
, escaped_etag
? "." : "",
191 strempty(escaped_etag
), strempty(suffix
), NULL
);
195 /* URLs might make the path longer than the maximum allowed length for a file name.
196 * When that happens, a URL hash is used instead. Paths returned by this function
197 * can be later used with tempfn_random() which adds 16 bytes to the resulting name. */
198 if (strlen(path
) >= HASH_URL_THRESHOLD_LENGTH
) {
199 _cleanup_free_
char *hash
= NULL
;
204 r
= hash_url(url
, &hash
);
208 path
= strjoin(image_root
, "/", strempty(prefix
), hash
, escaped_etag
? "." : "",
209 strempty(escaped_etag
), strempty(suffix
), NULL
);
218 int pull_make_settings_job(
222 PullJobFinished on_finished
,
225 _cleanup_free_
char *last_component
= NULL
, *ll
= NULL
, *settings_url
= NULL
;
226 _cleanup_(pull_job_unrefp
) PullJob
*job
= NULL
;
234 r
= import_url_last_component(url
, &last_component
);
238 r
= tar_strip_suffixes(last_component
, &ll
);
242 q
= strjoina(ll
, ".nspawn");
244 r
= import_url_change_last_component(url
, q
, &settings_url
);
248 r
= pull_job_new(&job
, settings_url
, glue
, userdata
);
252 job
->on_finished
= on_finished
;
253 job
->compressed_max
= job
->uncompressed_max
= 1ULL * 1024ULL * 1024ULL;
261 int pull_make_verification_jobs(
262 PullJob
**ret_checksum_job
,
263 PullJob
**ret_signature_job
,
267 PullJobFinished on_finished
,
270 _cleanup_(pull_job_unrefp
) PullJob
*checksum_job
= NULL
, *signature_job
= NULL
;
273 assert(ret_checksum_job
);
274 assert(ret_signature_job
);
276 assert(verify
< _IMPORT_VERIFY_MAX
);
280 if (verify
!= IMPORT_VERIFY_NO
) {
281 _cleanup_free_
char *checksum_url
= NULL
;
283 /* Queue job for the SHA256SUMS file for the image */
284 r
= import_url_change_last_component(url
, "SHA256SUMS", &checksum_url
);
288 r
= pull_job_new(&checksum_job
, checksum_url
, glue
, userdata
);
292 checksum_job
->on_finished
= on_finished
;
293 checksum_job
->uncompressed_max
= checksum_job
->compressed_max
= 1ULL * 1024ULL * 1024ULL;
296 if (verify
== IMPORT_VERIFY_SIGNATURE
) {
297 _cleanup_free_
char *signature_url
= NULL
;
299 /* Queue job for the SHA256SUMS.gpg file for the image. */
300 r
= import_url_change_last_component(url
, "SHA256SUMS.gpg", &signature_url
);
304 r
= pull_job_new(&signature_job
, signature_url
, glue
, userdata
);
308 signature_job
->on_finished
= on_finished
;
309 signature_job
->uncompressed_max
= signature_job
->compressed_max
= 1ULL * 1024ULL * 1024ULL;
312 *ret_checksum_job
= checksum_job
;
313 *ret_signature_job
= signature_job
;
315 checksum_job
= signature_job
= NULL
;
320 int pull_verify(PullJob
*main_job
,
321 PullJob
*settings_job
,
322 PullJob
*checksum_job
,
323 PullJob
*signature_job
) {
325 _cleanup_close_pair_
int gpg_pipe
[2] = { -1, -1 };
326 _cleanup_free_
char *fn
= NULL
;
327 _cleanup_close_
int sig_file
= -1;
328 const char *p
, *line
;
329 char sig_file_path
[] = "/tmp/sigXXXXXX", gpg_home
[] = "/tmp/gpghomeXXXXXX";
330 _cleanup_sigkill_wait_ pid_t pid
= 0;
331 bool gpg_home_created
= false;
335 assert(main_job
->state
== PULL_JOB_DONE
);
340 assert(main_job
->calc_checksum
);
341 assert(main_job
->checksum
);
342 assert(checksum_job
->state
== PULL_JOB_DONE
);
344 if (!checksum_job
->payload
|| checksum_job
->payload_size
<= 0) {
345 log_error("Checksum is empty, cannot verify.");
349 r
= import_url_last_component(main_job
->url
, &fn
);
353 if (!filename_is_valid(fn
)) {
354 log_error("Cannot verify checksum, could not determine valid server-side file name.");
358 line
= strjoina(main_job
->checksum
, " *", fn
, "\n");
360 p
= memmem(checksum_job
->payload
,
361 checksum_job
->payload_size
,
365 if (!p
|| (p
!= (char*) checksum_job
->payload
&& p
[-1] != '\n')) {
366 log_error("DOWNLOAD INVALID: Checksum did not check out, payload has been tampered with.");
370 log_info("SHA256 checksum of %s is valid.", main_job
->url
);
372 assert(!settings_job
|| IN_SET(settings_job
->state
, PULL_JOB_DONE
, PULL_JOB_FAILED
));
375 settings_job
->state
== PULL_JOB_DONE
&&
376 settings_job
->error
== 0 &&
377 !settings_job
->etag_exists
) {
379 _cleanup_free_
char *settings_fn
= NULL
;
381 assert(settings_job
->calc_checksum
);
382 assert(settings_job
->checksum
);
384 r
= import_url_last_component(settings_job
->url
, &settings_fn
);
388 if (!filename_is_valid(settings_fn
)) {
389 log_error("Cannot verify checksum, could not determine server-side settings file name.");
393 line
= strjoina(settings_job
->checksum
, " *", settings_fn
, "\n");
395 p
= memmem(checksum_job
->payload
,
396 checksum_job
->payload_size
,
400 if (!p
|| (p
!= (char*) checksum_job
->payload
&& p
[-1] != '\n')) {
401 log_error("DOWNLOAD INVALID: Checksum of settings file did not checkout, settings file has been tampered with.");
405 log_info("SHA256 checksum of %s is valid.", settings_job
->url
);
411 assert(signature_job
->state
== PULL_JOB_DONE
);
413 if (!signature_job
->payload
|| signature_job
->payload_size
<= 0) {
414 log_error("Signature is empty, cannot verify.");
418 r
= pipe2(gpg_pipe
, O_CLOEXEC
);
420 return log_error_errno(errno
, "Failed to create pipe for gpg: %m");
422 sig_file
= mkostemp(sig_file_path
, O_RDWR
);
424 return log_error_errno(errno
, "Failed to create temporary file: %m");
426 r
= loop_write(sig_file
, signature_job
->payload
, signature_job
->payload_size
, false);
428 log_error_errno(r
, "Failed to write to temporary file: %m");
432 if (!mkdtemp(gpg_home
)) {
433 r
= log_error_errno(errno
, "Failed to create tempory home for gpg: %m");
437 gpg_home_created
= true;
441 return log_error_errno(errno
, "Failed to fork off gpg: %m");
443 const char *cmd
[] = {
446 "--no-default-keyring",
447 "--no-auto-key-locate",
448 "--no-auto-check-trustdb",
450 "--trust-model=always",
451 NULL
, /* --homedir= */
452 NULL
, /* --keyring= */
454 NULL
, /* signature file */
456 NULL
/* trailing NULL */
458 unsigned k
= ELEMENTSOF(cmd
) - 6;
463 (void) reset_all_signal_handlers();
464 (void) reset_signal_mask();
465 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
467 gpg_pipe
[1] = safe_close(gpg_pipe
[1]);
469 if (dup2(gpg_pipe
[0], STDIN_FILENO
) != STDIN_FILENO
) {
470 log_error_errno(errno
, "Failed to dup2() fd: %m");
474 if (gpg_pipe
[0] != STDIN_FILENO
)
475 gpg_pipe
[0] = safe_close(gpg_pipe
[0]);
477 null_fd
= open("/dev/null", O_WRONLY
|O_NOCTTY
);
479 log_error_errno(errno
, "Failed to open /dev/null: %m");
483 if (dup2(null_fd
, STDOUT_FILENO
) != STDOUT_FILENO
) {
484 log_error_errno(errno
, "Failed to dup2() fd: %m");
488 if (null_fd
!= STDOUT_FILENO
)
489 null_fd
= safe_close(null_fd
);
491 cmd
[k
++] = strjoina("--homedir=", gpg_home
);
493 /* We add the user keyring only to the command line
494 * arguments, if it's around since gpg fails
496 if (access(USER_KEYRING_PATH
, F_OK
) >= 0)
497 cmd
[k
++] = "--keyring=" USER_KEYRING_PATH
;
499 cmd
[k
++] = "--keyring=" VENDOR_KEYRING_PATH
;
501 cmd
[k
++] = "--verify";
502 cmd
[k
++] = sig_file_path
;
506 fd_cloexec(STDIN_FILENO
, false);
507 fd_cloexec(STDOUT_FILENO
, false);
508 fd_cloexec(STDERR_FILENO
, false);
510 execvp("gpg2", (char * const *) cmd
);
511 execvp("gpg", (char * const *) cmd
);
512 log_error_errno(errno
, "Failed to execute gpg: %m");
516 gpg_pipe
[0] = safe_close(gpg_pipe
[0]);
518 r
= loop_write(gpg_pipe
[1], checksum_job
->payload
, checksum_job
->payload_size
, false);
520 log_error_errno(r
, "Failed to write to pipe: %m");
524 gpg_pipe
[1] = safe_close(gpg_pipe
[1]);
526 r
= wait_for_terminate_and_warn("gpg", pid
, true);
531 log_error("DOWNLOAD INVALID: Signature verification failed.");
534 log_info("Signature verification succeeded.");
540 (void) unlink(sig_file_path
);
542 if (gpg_home_created
)
543 (void) rm_rf(gpg_home
, REMOVE_ROOT
|REMOVE_PHYSICAL
);