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>
28 #include "btrfs-util.h"
29 #include "capability.h"
31 #include "pull-common.h"
32 #include "process-util.h"
33 #include "signal-util.h"
34 #include "siphash24.h"
36 #define FILENAME_ESCAPE "/.#\"\'"
37 #define HASH_URL_THRESHOLD_LENGTH (_POSIX_PATH_MAX - 16)
39 int pull_find_old_etags(
41 const char *image_root
,
47 _cleanup_free_
char *escaped_url
= NULL
;
48 _cleanup_closedir_
DIR *d
= NULL
;
49 _cleanup_strv_free_
char **l
= NULL
;
57 image_root
= "/var/lib/machines";
59 escaped_url
= xescape(url
, FILENAME_ESCAPE
);
63 d
= opendir(image_root
);
65 if (errno
== ENOENT
) {
73 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
77 if (de
->d_type
!= DT_UNKNOWN
&&
82 a
= startswith(de
->d_name
, prefix
);
88 a
= startswith(a
, escaped_url
);
92 a
= startswith(a
, ".");
97 b
= endswith(de
->d_name
, suffix
);
101 b
= strchr(de
->d_name
, 0);
106 r
= cunescape_length(a
, b
- a
, 0, &u
);
110 if (!http_etag_is_valid(u
)) {
115 r
= strv_consume(&l
, u
);
126 int pull_make_local_copy(const char *final
, const char *image_root
, const char *local
, bool force_local
) {
134 image_root
= "/var/lib/machines";
136 p
= strjoina(image_root
, "/", local
);
139 (void) rm_rf(p
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
141 r
= btrfs_subvol_snapshot(final
, p
, BTRFS_SNAPSHOT_QUOTA
);
143 r
= copy_tree(final
, p
, false);
145 return log_error_errno(r
, "Failed to copy image: %m");
147 return log_error_errno(r
, "Failed to create local image: %m");
149 log_info("Created new local image '%s'.", local
);
154 static int hash_url(const char *url
, char **ret
) {
156 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
);
160 siphash24((uint8_t *) &h
, url
, strlen(url
), k
.bytes
);
161 if (asprintf(ret
, "%"PRIx64
, h
) < 0)
167 int pull_make_path(const char *url
, const char *etag
, const char *image_root
, const char *prefix
, const char *suffix
, char **ret
) {
168 _cleanup_free_
char *escaped_url
= NULL
, *escaped_etag
= NULL
;
175 image_root
= "/var/lib/machines";
177 escaped_url
= xescape(url
, FILENAME_ESCAPE
);
182 escaped_etag
= xescape(etag
, FILENAME_ESCAPE
);
187 path
= strjoin(image_root
, "/", strempty(prefix
), escaped_url
, escaped_etag
? "." : "",
188 strempty(escaped_etag
), strempty(suffix
), NULL
);
192 /* URLs might make the path longer than the maximum allowed length for a file name.
193 * When that happens, a URL hash is used instead. Paths returned by this function
194 * can be later used with tempfn_random() which adds 16 bytes to the resulting name. */
195 if (strlen(path
) >= HASH_URL_THRESHOLD_LENGTH
) {
196 _cleanup_free_
char *hash
= NULL
;
201 r
= hash_url(url
, &hash
);
205 path
= strjoin(image_root
, "/", strempty(prefix
), hash
, escaped_etag
? "." : "",
206 strempty(escaped_etag
), strempty(suffix
), NULL
);
215 int pull_make_settings_job(
219 PullJobFinished on_finished
,
222 _cleanup_free_
char *last_component
= NULL
, *ll
= NULL
, *settings_url
= NULL
;
223 _cleanup_(pull_job_unrefp
) PullJob
*job
= NULL
;
231 r
= import_url_last_component(url
, &last_component
);
235 r
= tar_strip_suffixes(last_component
, &ll
);
239 q
= strjoina(ll
, ".nspawn");
241 r
= import_url_change_last_component(url
, q
, &settings_url
);
245 r
= pull_job_new(&job
, settings_url
, glue
, userdata
);
249 job
->on_finished
= on_finished
;
250 job
->compressed_max
= job
->uncompressed_max
= 1ULL * 1024ULL * 1024ULL;
258 int pull_make_verification_jobs(
259 PullJob
**ret_checksum_job
,
260 PullJob
**ret_signature_job
,
264 PullJobFinished on_finished
,
267 _cleanup_(pull_job_unrefp
) PullJob
*checksum_job
= NULL
, *signature_job
= NULL
;
270 assert(ret_checksum_job
);
271 assert(ret_signature_job
);
273 assert(verify
< _IMPORT_VERIFY_MAX
);
277 if (verify
!= IMPORT_VERIFY_NO
) {
278 _cleanup_free_
char *checksum_url
= NULL
;
280 /* Queue job for the SHA256SUMS file for the image */
281 r
= import_url_change_last_component(url
, "SHA256SUMS", &checksum_url
);
285 r
= pull_job_new(&checksum_job
, checksum_url
, glue
, userdata
);
289 checksum_job
->on_finished
= on_finished
;
290 checksum_job
->uncompressed_max
= checksum_job
->compressed_max
= 1ULL * 1024ULL * 1024ULL;
293 if (verify
== IMPORT_VERIFY_SIGNATURE
) {
294 _cleanup_free_
char *signature_url
= NULL
;
296 /* Queue job for the SHA256SUMS.gpg file for the image. */
297 r
= import_url_change_last_component(url
, "SHA256SUMS.gpg", &signature_url
);
301 r
= pull_job_new(&signature_job
, signature_url
, glue
, userdata
);
305 signature_job
->on_finished
= on_finished
;
306 signature_job
->uncompressed_max
= signature_job
->compressed_max
= 1ULL * 1024ULL * 1024ULL;
309 *ret_checksum_job
= checksum_job
;
310 *ret_signature_job
= signature_job
;
312 checksum_job
= signature_job
= NULL
;
317 int pull_verify(PullJob
*main_job
,
318 PullJob
*settings_job
,
319 PullJob
*checksum_job
,
320 PullJob
*signature_job
) {
322 _cleanup_close_pair_
int gpg_pipe
[2] = { -1, -1 };
323 _cleanup_free_
char *fn
= NULL
;
324 _cleanup_close_
int sig_file
= -1;
325 const char *p
, *line
;
326 char sig_file_path
[] = "/tmp/sigXXXXXX", gpg_home
[] = "/tmp/gpghomeXXXXXX";
327 _cleanup_sigkill_wait_ pid_t pid
= 0;
328 bool gpg_home_created
= false;
332 assert(main_job
->state
== PULL_JOB_DONE
);
337 assert(main_job
->calc_checksum
);
338 assert(main_job
->checksum
);
339 assert(checksum_job
->state
== PULL_JOB_DONE
);
341 if (!checksum_job
->payload
|| checksum_job
->payload_size
<= 0) {
342 log_error("Checksum is empty, cannot verify.");
346 r
= import_url_last_component(main_job
->url
, &fn
);
350 if (!filename_is_valid(fn
)) {
351 log_error("Cannot verify checksum, could not determine valid server-side file name.");
355 line
= strjoina(main_job
->checksum
, " *", fn
, "\n");
357 p
= memmem(checksum_job
->payload
,
358 checksum_job
->payload_size
,
362 if (!p
|| (p
!= (char*) checksum_job
->payload
&& p
[-1] != '\n')) {
363 log_error("DOWNLOAD INVALID: Checksum did not check out, payload has been tampered with.");
367 log_info("SHA256 checksum of %s is valid.", main_job
->url
);
369 assert(!settings_job
|| IN_SET(settings_job
->state
, PULL_JOB_DONE
, PULL_JOB_FAILED
));
372 settings_job
->state
== PULL_JOB_DONE
&&
373 settings_job
->error
== 0 &&
374 !settings_job
->etag_exists
) {
376 _cleanup_free_
char *settings_fn
= NULL
;
378 assert(settings_job
->calc_checksum
);
379 assert(settings_job
->checksum
);
381 r
= import_url_last_component(settings_job
->url
, &settings_fn
);
385 if (!filename_is_valid(settings_fn
)) {
386 log_error("Cannot verify checksum, could not determine server-side settings file name.");
390 line
= strjoina(settings_job
->checksum
, " *", settings_fn
, "\n");
392 p
= memmem(checksum_job
->payload
,
393 checksum_job
->payload_size
,
397 if (!p
|| (p
!= (char*) checksum_job
->payload
&& p
[-1] != '\n')) {
398 log_error("DOWNLOAD INVALID: Checksum of settings file did not checkout, settings file has been tampered with.");
402 log_info("SHA256 checksum of %s is valid.", settings_job
->url
);
408 assert(signature_job
->state
== PULL_JOB_DONE
);
410 if (!signature_job
->payload
|| signature_job
->payload_size
<= 0) {
411 log_error("Signature is empty, cannot verify.");
415 r
= pipe2(gpg_pipe
, O_CLOEXEC
);
417 return log_error_errno(errno
, "Failed to create pipe for gpg: %m");
419 sig_file
= mkostemp(sig_file_path
, O_RDWR
);
421 return log_error_errno(errno
, "Failed to create temporary file: %m");
423 r
= loop_write(sig_file
, signature_job
->payload
, signature_job
->payload_size
, false);
425 log_error_errno(r
, "Failed to write to temporary file: %m");
429 if (!mkdtemp(gpg_home
)) {
430 r
= log_error_errno(errno
, "Failed to create tempory home for gpg: %m");
434 gpg_home_created
= true;
438 return log_error_errno(errno
, "Failed to fork off gpg: %m");
440 const char *cmd
[] = {
443 "--no-default-keyring",
444 "--no-auto-key-locate",
445 "--no-auto-check-trustdb",
447 "--trust-model=always",
448 NULL
, /* --homedir= */
449 NULL
, /* --keyring= */
451 NULL
, /* signature file */
453 NULL
/* trailing NULL */
455 unsigned k
= ELEMENTSOF(cmd
) - 6;
460 (void) reset_all_signal_handlers();
461 (void) reset_signal_mask();
462 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
464 gpg_pipe
[1] = safe_close(gpg_pipe
[1]);
466 if (dup2(gpg_pipe
[0], STDIN_FILENO
) != STDIN_FILENO
) {
467 log_error_errno(errno
, "Failed to dup2() fd: %m");
471 if (gpg_pipe
[0] != STDIN_FILENO
)
472 gpg_pipe
[0] = safe_close(gpg_pipe
[0]);
474 null_fd
= open("/dev/null", O_WRONLY
|O_NOCTTY
);
476 log_error_errno(errno
, "Failed to open /dev/null: %m");
480 if (dup2(null_fd
, STDOUT_FILENO
) != STDOUT_FILENO
) {
481 log_error_errno(errno
, "Failed to dup2() fd: %m");
485 if (null_fd
!= STDOUT_FILENO
)
486 null_fd
= safe_close(null_fd
);
488 cmd
[k
++] = strjoina("--homedir=", gpg_home
);
490 /* We add the user keyring only to the command line
491 * arguments, if it's around since gpg fails
493 if (access(USER_KEYRING_PATH
, F_OK
) >= 0)
494 cmd
[k
++] = "--keyring=" USER_KEYRING_PATH
;
496 cmd
[k
++] = "--keyring=" VENDOR_KEYRING_PATH
;
498 cmd
[k
++] = "--verify";
499 cmd
[k
++] = sig_file_path
;
503 fd_cloexec(STDIN_FILENO
, false);
504 fd_cloexec(STDOUT_FILENO
, false);
505 fd_cloexec(STDERR_FILENO
, false);
507 execvp("gpg2", (char * const *) cmd
);
508 execvp("gpg", (char * const *) cmd
);
509 log_error_errno(errno
, "Failed to execute gpg: %m");
513 gpg_pipe
[0] = safe_close(gpg_pipe
[0]);
515 r
= loop_write(gpg_pipe
[1], checksum_job
->payload
, checksum_job
->payload_size
, false);
517 log_error_errno(r
, "Failed to write to pipe: %m");
521 gpg_pipe
[1] = safe_close(gpg_pipe
[1]);
523 r
= wait_for_terminate_and_warn("gpg", pid
, true);
528 log_error("DOWNLOAD INVALID: Signature verification failed.");
531 log_info("Signature verification succeeded.");
537 (void) unlink(sig_file_path
);
539 if (gpg_home_created
)
540 (void) rm_rf(gpg_home
, REMOVE_ROOT
|REMOVE_PHYSICAL
);