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"
28 #include "process-util.h"
31 #include "signal-util.h"
32 #include "siphash24.h"
33 #include "string-util.h"
36 #include "pull-common.h"
38 #define FILENAME_ESCAPE "/.#\"\'"
39 #define HASH_URL_THRESHOLD_LENGTH (_POSIX_PATH_MAX - 16)
41 int pull_find_old_etags(
43 const char *image_root
,
49 _cleanup_free_
char *escaped_url
= NULL
;
50 _cleanup_closedir_
DIR *d
= NULL
;
51 _cleanup_strv_free_
char **l
= NULL
;
59 image_root
= "/var/lib/machines";
61 escaped_url
= xescape(url
, FILENAME_ESCAPE
);
65 d
= opendir(image_root
);
67 if (errno
== ENOENT
) {
75 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
79 if (de
->d_type
!= DT_UNKNOWN
&&
84 a
= startswith(de
->d_name
, prefix
);
90 a
= startswith(a
, escaped_url
);
94 a
= startswith(a
, ".");
99 b
= endswith(de
->d_name
, suffix
);
103 b
= strchr(de
->d_name
, 0);
108 r
= cunescape_length(a
, b
- a
, 0, &u
);
112 if (!http_etag_is_valid(u
)) {
117 r
= strv_consume(&l
, u
);
128 int pull_make_local_copy(const char *final
, const char *image_root
, const char *local
, bool force_local
) {
136 image_root
= "/var/lib/machines";
138 p
= strjoina(image_root
, "/", local
);
141 (void) rm_rf(p
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
143 r
= btrfs_subvol_snapshot(final
, p
, BTRFS_SNAPSHOT_QUOTA
);
145 r
= copy_tree(final
, p
, false);
147 return log_error_errno(r
, "Failed to copy image: %m");
149 return log_error_errno(r
, "Failed to create local image: %m");
151 log_info("Created new local image '%s'.", local
);
156 static int hash_url(const char *url
, char **ret
) {
158 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
);
162 siphash24((uint8_t *) &h
, url
, strlen(url
), k
.bytes
);
163 if (asprintf(ret
, "%"PRIx64
, h
) < 0)
169 int pull_make_path(const char *url
, const char *etag
, const char *image_root
, const char *prefix
, const char *suffix
, char **ret
) {
170 _cleanup_free_
char *escaped_url
= NULL
, *escaped_etag
= NULL
;
177 image_root
= "/var/lib/machines";
179 escaped_url
= xescape(url
, FILENAME_ESCAPE
);
184 escaped_etag
= xescape(etag
, FILENAME_ESCAPE
);
189 path
= strjoin(image_root
, "/", strempty(prefix
), escaped_url
, escaped_etag
? "." : "",
190 strempty(escaped_etag
), strempty(suffix
), NULL
);
194 /* URLs might make the path longer than the maximum allowed length for a file name.
195 * When that happens, a URL hash is used instead. Paths returned by this function
196 * can be later used with tempfn_random() which adds 16 bytes to the resulting name. */
197 if (strlen(path
) >= HASH_URL_THRESHOLD_LENGTH
) {
198 _cleanup_free_
char *hash
= NULL
;
203 r
= hash_url(url
, &hash
);
207 path
= strjoin(image_root
, "/", strempty(prefix
), hash
, escaped_etag
? "." : "",
208 strempty(escaped_etag
), strempty(suffix
), NULL
);
217 int pull_make_settings_job(
221 PullJobFinished on_finished
,
224 _cleanup_free_
char *last_component
= NULL
, *ll
= NULL
, *settings_url
= NULL
;
225 _cleanup_(pull_job_unrefp
) PullJob
*job
= NULL
;
233 r
= import_url_last_component(url
, &last_component
);
237 r
= tar_strip_suffixes(last_component
, &ll
);
241 q
= strjoina(ll
, ".nspawn");
243 r
= import_url_change_last_component(url
, q
, &settings_url
);
247 r
= pull_job_new(&job
, settings_url
, glue
, userdata
);
251 job
->on_finished
= on_finished
;
252 job
->compressed_max
= job
->uncompressed_max
= 1ULL * 1024ULL * 1024ULL;
260 int pull_make_verification_jobs(
261 PullJob
**ret_checksum_job
,
262 PullJob
**ret_signature_job
,
266 PullJobFinished on_finished
,
269 _cleanup_(pull_job_unrefp
) PullJob
*checksum_job
= NULL
, *signature_job
= NULL
;
272 assert(ret_checksum_job
);
273 assert(ret_signature_job
);
275 assert(verify
< _IMPORT_VERIFY_MAX
);
279 if (verify
!= IMPORT_VERIFY_NO
) {
280 _cleanup_free_
char *checksum_url
= NULL
;
282 /* Queue job for the SHA256SUMS file for the image */
283 r
= import_url_change_last_component(url
, "SHA256SUMS", &checksum_url
);
287 r
= pull_job_new(&checksum_job
, checksum_url
, glue
, userdata
);
291 checksum_job
->on_finished
= on_finished
;
292 checksum_job
->uncompressed_max
= checksum_job
->compressed_max
= 1ULL * 1024ULL * 1024ULL;
295 if (verify
== IMPORT_VERIFY_SIGNATURE
) {
296 _cleanup_free_
char *signature_url
= NULL
;
298 /* Queue job for the SHA256SUMS.gpg file for the image. */
299 r
= import_url_change_last_component(url
, "SHA256SUMS.gpg", &signature_url
);
303 r
= pull_job_new(&signature_job
, signature_url
, glue
, userdata
);
307 signature_job
->on_finished
= on_finished
;
308 signature_job
->uncompressed_max
= signature_job
->compressed_max
= 1ULL * 1024ULL * 1024ULL;
311 *ret_checksum_job
= checksum_job
;
312 *ret_signature_job
= signature_job
;
314 checksum_job
= signature_job
= NULL
;
319 int pull_verify(PullJob
*main_job
,
320 PullJob
*settings_job
,
321 PullJob
*checksum_job
,
322 PullJob
*signature_job
) {
324 _cleanup_close_pair_
int gpg_pipe
[2] = { -1, -1 };
325 _cleanup_free_
char *fn
= NULL
;
326 _cleanup_close_
int sig_file
= -1;
327 const char *p
, *line
;
328 char sig_file_path
[] = "/tmp/sigXXXXXX", gpg_home
[] = "/tmp/gpghomeXXXXXX";
329 _cleanup_sigkill_wait_ pid_t pid
= 0;
330 bool gpg_home_created
= false;
334 assert(main_job
->state
== PULL_JOB_DONE
);
339 assert(main_job
->calc_checksum
);
340 assert(main_job
->checksum
);
341 assert(checksum_job
->state
== PULL_JOB_DONE
);
343 if (!checksum_job
->payload
|| checksum_job
->payload_size
<= 0) {
344 log_error("Checksum is empty, cannot verify.");
348 r
= import_url_last_component(main_job
->url
, &fn
);
352 if (!filename_is_valid(fn
)) {
353 log_error("Cannot verify checksum, could not determine valid server-side file name.");
357 line
= strjoina(main_job
->checksum
, " *", fn
, "\n");
359 p
= memmem(checksum_job
->payload
,
360 checksum_job
->payload_size
,
364 if (!p
|| (p
!= (char*) checksum_job
->payload
&& p
[-1] != '\n')) {
365 log_error("DOWNLOAD INVALID: Checksum did not check out, payload has been tampered with.");
369 log_info("SHA256 checksum of %s is valid.", main_job
->url
);
371 assert(!settings_job
|| IN_SET(settings_job
->state
, PULL_JOB_DONE
, PULL_JOB_FAILED
));
374 settings_job
->state
== PULL_JOB_DONE
&&
375 settings_job
->error
== 0 &&
376 !settings_job
->etag_exists
) {
378 _cleanup_free_
char *settings_fn
= NULL
;
380 assert(settings_job
->calc_checksum
);
381 assert(settings_job
->checksum
);
383 r
= import_url_last_component(settings_job
->url
, &settings_fn
);
387 if (!filename_is_valid(settings_fn
)) {
388 log_error("Cannot verify checksum, could not determine server-side settings file name.");
392 line
= strjoina(settings_job
->checksum
, " *", settings_fn
, "\n");
394 p
= memmem(checksum_job
->payload
,
395 checksum_job
->payload_size
,
399 if (!p
|| (p
!= (char*) checksum_job
->payload
&& p
[-1] != '\n')) {
400 log_error("DOWNLOAD INVALID: Checksum of settings file did not checkout, settings file has been tampered with.");
404 log_info("SHA256 checksum of %s is valid.", settings_job
->url
);
410 assert(signature_job
->state
== PULL_JOB_DONE
);
412 if (!signature_job
->payload
|| signature_job
->payload_size
<= 0) {
413 log_error("Signature is empty, cannot verify.");
417 r
= pipe2(gpg_pipe
, O_CLOEXEC
);
419 return log_error_errno(errno
, "Failed to create pipe for gpg: %m");
421 sig_file
= mkostemp(sig_file_path
, O_RDWR
);
423 return log_error_errno(errno
, "Failed to create temporary file: %m");
425 r
= loop_write(sig_file
, signature_job
->payload
, signature_job
->payload_size
, false);
427 log_error_errno(r
, "Failed to write to temporary file: %m");
431 if (!mkdtemp(gpg_home
)) {
432 r
= log_error_errno(errno
, "Failed to create tempory home for gpg: %m");
436 gpg_home_created
= true;
440 return log_error_errno(errno
, "Failed to fork off gpg: %m");
442 const char *cmd
[] = {
445 "--no-default-keyring",
446 "--no-auto-key-locate",
447 "--no-auto-check-trustdb",
449 "--trust-model=always",
450 NULL
, /* --homedir= */
451 NULL
, /* --keyring= */
453 NULL
, /* signature file */
455 NULL
/* trailing NULL */
457 unsigned k
= ELEMENTSOF(cmd
) - 6;
462 (void) reset_all_signal_handlers();
463 (void) reset_signal_mask();
464 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
466 gpg_pipe
[1] = safe_close(gpg_pipe
[1]);
468 if (dup2(gpg_pipe
[0], STDIN_FILENO
) != STDIN_FILENO
) {
469 log_error_errno(errno
, "Failed to dup2() fd: %m");
473 if (gpg_pipe
[0] != STDIN_FILENO
)
474 gpg_pipe
[0] = safe_close(gpg_pipe
[0]);
476 null_fd
= open("/dev/null", O_WRONLY
|O_NOCTTY
);
478 log_error_errno(errno
, "Failed to open /dev/null: %m");
482 if (dup2(null_fd
, STDOUT_FILENO
) != STDOUT_FILENO
) {
483 log_error_errno(errno
, "Failed to dup2() fd: %m");
487 if (null_fd
!= STDOUT_FILENO
)
488 null_fd
= safe_close(null_fd
);
490 cmd
[k
++] = strjoina("--homedir=", gpg_home
);
492 /* We add the user keyring only to the command line
493 * arguments, if it's around since gpg fails
495 if (access(USER_KEYRING_PATH
, F_OK
) >= 0)
496 cmd
[k
++] = "--keyring=" USER_KEYRING_PATH
;
498 cmd
[k
++] = "--keyring=" VENDOR_KEYRING_PATH
;
500 cmd
[k
++] = "--verify";
501 cmd
[k
++] = sig_file_path
;
505 fd_cloexec(STDIN_FILENO
, false);
506 fd_cloexec(STDOUT_FILENO
, false);
507 fd_cloexec(STDERR_FILENO
, false);
509 execvp("gpg2", (char * const *) cmd
);
510 execvp("gpg", (char * const *) cmd
);
511 log_error_errno(errno
, "Failed to execute gpg: %m");
515 gpg_pipe
[0] = safe_close(gpg_pipe
[0]);
517 r
= loop_write(gpg_pipe
[1], checksum_job
->payload
, checksum_job
->payload_size
, false);
519 log_error_errno(r
, "Failed to write to pipe: %m");
523 gpg_pipe
[1] = safe_close(gpg_pipe
[1]);
525 r
= wait_for_terminate_and_warn("gpg", pid
, true);
530 log_error("DOWNLOAD INVALID: Signature verification failed.");
533 log_info("Signature verification succeeded.");
539 (void) unlink(sig_file_path
);
541 if (gpg_home_created
)
542 (void) rm_rf(gpg_home
, REMOVE_ROOT
|REMOVE_PHYSICAL
);