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"
35 #define FILENAME_ESCAPE "/.#\"\'"
37 int pull_find_old_etags(
39 const char *image_root
,
45 _cleanup_free_
char *escaped_url
= NULL
;
46 _cleanup_closedir_
DIR *d
= NULL
;
47 _cleanup_strv_free_
char **l
= NULL
;
55 image_root
= "/var/lib/machines";
57 escaped_url
= xescape(url
, FILENAME_ESCAPE
);
61 d
= opendir(image_root
);
63 if (errno
== ENOENT
) {
71 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
75 if (de
->d_type
!= DT_UNKNOWN
&&
80 a
= startswith(de
->d_name
, prefix
);
86 a
= startswith(a
, escaped_url
);
90 a
= startswith(a
, ".");
95 b
= endswith(de
->d_name
, suffix
);
99 b
= strchr(de
->d_name
, 0);
104 r
= cunescape_length(a
, b
- a
, 0, &u
);
108 if (!http_etag_is_valid(u
)) {
113 r
= strv_consume(&l
, u
);
124 int pull_make_local_copy(const char *final
, const char *image_root
, const char *local
, bool force_local
) {
132 image_root
= "/var/lib/machines";
134 p
= strjoina(image_root
, "/", local
);
137 (void) rm_rf(p
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
139 r
= btrfs_subvol_snapshot(final
, p
, 0);
141 r
= copy_tree(final
, p
, false);
143 return log_error_errno(r
, "Failed to copy image: %m");
145 return log_error_errno(r
, "Failed to create local image: %m");
147 log_info("Created new local image '%s'.", local
);
152 int pull_make_path(const char *url
, const char *etag
, const char *image_root
, const char *prefix
, const char *suffix
, char **ret
) {
153 _cleanup_free_
char *escaped_url
= NULL
;
160 image_root
= "/var/lib/machines";
162 escaped_url
= xescape(url
, FILENAME_ESCAPE
);
167 _cleanup_free_
char *escaped_etag
= NULL
;
169 escaped_etag
= xescape(etag
, FILENAME_ESCAPE
);
173 path
= strjoin(image_root
, "/", strempty(prefix
), escaped_url
, ".", escaped_etag
, strempty(suffix
), NULL
);
175 path
= strjoin(image_root
, "/", strempty(prefix
), escaped_url
, strempty(suffix
), NULL
);
183 int pull_make_settings_job(
187 PullJobFinished on_finished
,
190 _cleanup_free_
char *last_component
= NULL
, *ll
= NULL
, *settings_url
= NULL
;
191 _cleanup_(pull_job_unrefp
) PullJob
*job
= NULL
;
199 r
= import_url_last_component(url
, &last_component
);
203 r
= tar_strip_suffixes(last_component
, &ll
);
207 q
= strjoina(ll
, ".nspawn");
209 r
= import_url_change_last_component(url
, q
, &settings_url
);
213 r
= pull_job_new(&job
, settings_url
, glue
, userdata
);
217 job
->on_finished
= on_finished
;
218 job
->compressed_max
= job
->uncompressed_max
= 1ULL * 1024ULL * 1024ULL;
226 int pull_make_verification_jobs(
227 PullJob
**ret_checksum_job
,
228 PullJob
**ret_signature_job
,
232 PullJobFinished on_finished
,
235 _cleanup_(pull_job_unrefp
) PullJob
*checksum_job
= NULL
, *signature_job
= NULL
;
238 assert(ret_checksum_job
);
239 assert(ret_signature_job
);
241 assert(verify
< _IMPORT_VERIFY_MAX
);
245 if (verify
!= IMPORT_VERIFY_NO
) {
246 _cleanup_free_
char *checksum_url
= NULL
;
248 /* Queue job for the SHA256SUMS file for the image */
249 r
= import_url_change_last_component(url
, "SHA256SUMS", &checksum_url
);
253 r
= pull_job_new(&checksum_job
, checksum_url
, glue
, userdata
);
257 checksum_job
->on_finished
= on_finished
;
258 checksum_job
->uncompressed_max
= checksum_job
->compressed_max
= 1ULL * 1024ULL * 1024ULL;
261 if (verify
== IMPORT_VERIFY_SIGNATURE
) {
262 _cleanup_free_
char *signature_url
= NULL
;
264 /* Queue job for the SHA256SUMS.gpg file for the image. */
265 r
= import_url_change_last_component(url
, "SHA256SUMS.gpg", &signature_url
);
269 r
= pull_job_new(&signature_job
, signature_url
, glue
, userdata
);
273 signature_job
->on_finished
= on_finished
;
274 signature_job
->uncompressed_max
= signature_job
->compressed_max
= 1ULL * 1024ULL * 1024ULL;
277 *ret_checksum_job
= checksum_job
;
278 *ret_signature_job
= signature_job
;
280 checksum_job
= signature_job
= NULL
;
285 int pull_verify(PullJob
*main_job
,
286 PullJob
*settings_job
,
287 PullJob
*checksum_job
,
288 PullJob
*signature_job
) {
290 _cleanup_close_pair_
int gpg_pipe
[2] = { -1, -1 };
291 _cleanup_free_
char *fn
= NULL
;
292 _cleanup_close_
int sig_file
= -1;
293 const char *p
, *line
;
294 char sig_file_path
[] = "/tmp/sigXXXXXX", gpg_home
[] = "/tmp/gpghomeXXXXXX";
295 _cleanup_sigkill_wait_ pid_t pid
= 0;
296 bool gpg_home_created
= false;
300 assert(main_job
->state
== PULL_JOB_DONE
);
305 assert(main_job
->calc_checksum
);
306 assert(main_job
->checksum
);
307 assert(checksum_job
->state
== PULL_JOB_DONE
);
309 if (!checksum_job
->payload
|| checksum_job
->payload_size
<= 0) {
310 log_error("Checksum is empty, cannot verify.");
314 r
= import_url_last_component(main_job
->url
, &fn
);
318 if (!filename_is_valid(fn
)) {
319 log_error("Cannot verify checksum, could not determine valid server-side file name.");
323 line
= strjoina(main_job
->checksum
, " *", fn
, "\n");
325 p
= memmem(checksum_job
->payload
,
326 checksum_job
->payload_size
,
330 if (!p
|| (p
!= (char*) checksum_job
->payload
&& p
[-1] != '\n')) {
331 log_error("DOWNLOAD INVALID: Checksum did not check out, payload has been tempered with.");
335 log_info("SHA256 checksum of %s is valid.", main_job
->url
);
337 assert(!settings_job
|| settings_job
->state
== PULL_JOB_DONE
);
340 settings_job
->error
== 0 &&
341 !settings_job
->etag_exists
) {
343 _cleanup_free_
char *settings_fn
= NULL
;
345 assert(settings_job
->calc_checksum
);
346 assert(settings_job
->checksum
);
348 r
= import_url_last_component(settings_job
->url
, &settings_fn
);
352 if (!filename_is_valid(settings_fn
)) {
353 log_error("Cannot verify checksum, could not determine server-side settings file name.");
357 line
= strjoina(settings_job
->checksum
, " *", settings_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 of settings file did not checkout, settings file has been tempered with.");
369 log_info("SHA256 checksum of %s is valid.", settings_job
->url
);
375 assert(signature_job
->state
== PULL_JOB_DONE
);
377 if (!signature_job
->payload
|| signature_job
->payload_size
<= 0) {
378 log_error("Signature is empty, cannot verify.");
382 r
= pipe2(gpg_pipe
, O_CLOEXEC
);
384 return log_error_errno(errno
, "Failed to create pipe for gpg: %m");
386 sig_file
= mkostemp(sig_file_path
, O_RDWR
);
388 return log_error_errno(errno
, "Failed to create temporary file: %m");
390 r
= loop_write(sig_file
, signature_job
->payload
, signature_job
->payload_size
, false);
392 log_error_errno(r
, "Failed to write to temporary file: %m");
396 if (!mkdtemp(gpg_home
)) {
397 r
= log_error_errno(errno
, "Failed to create tempory home for gpg: %m");
401 gpg_home_created
= true;
405 return log_error_errno(errno
, "Failed to fork off gpg: %m");
407 const char *cmd
[] = {
410 "--no-default-keyring",
411 "--no-auto-key-locate",
412 "--no-auto-check-trustdb",
414 "--trust-model=always",
415 NULL
, /* --homedir= */
416 NULL
, /* --keyring= */
418 NULL
, /* signature file */
420 NULL
/* trailing NULL */
422 unsigned k
= ELEMENTSOF(cmd
) - 6;
427 (void) reset_all_signal_handlers();
428 (void) reset_signal_mask();
429 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
431 gpg_pipe
[1] = safe_close(gpg_pipe
[1]);
433 if (dup2(gpg_pipe
[0], STDIN_FILENO
) != STDIN_FILENO
) {
434 log_error_errno(errno
, "Failed to dup2() fd: %m");
438 if (gpg_pipe
[0] != STDIN_FILENO
)
439 gpg_pipe
[0] = safe_close(gpg_pipe
[0]);
441 null_fd
= open("/dev/null", O_WRONLY
|O_NOCTTY
);
443 log_error_errno(errno
, "Failed to open /dev/null: %m");
447 if (dup2(null_fd
, STDOUT_FILENO
) != STDOUT_FILENO
) {
448 log_error_errno(errno
, "Failed to dup2() fd: %m");
452 if (null_fd
!= STDOUT_FILENO
)
453 null_fd
= safe_close(null_fd
);
455 cmd
[k
++] = strjoina("--homedir=", gpg_home
);
457 /* We add the user keyring only to the command line
458 * arguments, if it's around since gpg fails
460 if (access(USER_KEYRING_PATH
, F_OK
) >= 0)
461 cmd
[k
++] = "--keyring=" USER_KEYRING_PATH
;
463 cmd
[k
++] = "--keyring=" VENDOR_KEYRING_PATH
;
465 cmd
[k
++] = "--verify";
466 cmd
[k
++] = sig_file_path
;
470 fd_cloexec(STDIN_FILENO
, false);
471 fd_cloexec(STDOUT_FILENO
, false);
472 fd_cloexec(STDERR_FILENO
, false);
474 execvp("gpg2", (char * const *) cmd
);
475 execvp("gpg", (char * const *) cmd
);
476 log_error_errno(errno
, "Failed to execute gpg: %m");
480 gpg_pipe
[0] = safe_close(gpg_pipe
[0]);
482 r
= loop_write(gpg_pipe
[1], checksum_job
->payload
, checksum_job
->payload_size
, false);
484 log_error_errno(r
, "Failed to write to pipe: %m");
488 gpg_pipe
[1] = safe_close(gpg_pipe
[1]);
490 r
= wait_for_terminate_and_warn("gpg", pid
, true);
495 log_error("DOWNLOAD INVALID: Signature verification failed.");
498 log_info("Signature verification succeeded.");
504 (void) unlink(sig_file_path
);
506 if (gpg_home_created
)
507 (void) rm_rf(gpg_home
, REMOVE_ROOT
|REMOVE_PHYSICAL
);