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>
27 #include "btrfs-util.h"
28 #include "capability.h"
29 #include "import-job.h"
30 #include "import-common.h"
32 #define FILENAME_ESCAPE "/.#\"\'"
34 int import_find_old_etags(const char *url, const char *image_root, int dt, const char *prefix, const char *suffix, char ***etags) {
35 _cleanup_free_ char *escaped_url = NULL;
36 _cleanup_closedir_ DIR *d = NULL;
37 _cleanup_strv_free_ char **l = NULL;
45 image_root = "/var/lib/machines";
47 escaped_url = xescape(url, FILENAME_ESCAPE);
51 d = opendir(image_root);
53 if (errno == ENOENT) {
61 FOREACH_DIRENT_ALL(de, d, return -errno) {
65 if (de->d_type != DT_UNKNOWN &&
70 a = startswith(de->d_name, prefix);
76 a = startswith(a, escaped_url);
80 a = startswith(a, ".");
85 b = endswith(de->d_name, suffix);
89 b = strchr(de->d_name, 0);
94 u = cunescape_length(a, b - a);
98 if (!http_etag_is_valid(u)) {
103 r = strv_consume(&l, u);
114 int import_make_local_copy(const char *final, const char *image_root, const char *local, bool force_local) {
122 image_root = "/var/lib/machines";
124 p = strappenda(image_root, "/", local);
127 (void) btrfs_subvol_remove(p);
128 (void) rm_rf_dangerous(p, false, true, false);
131 r = btrfs_subvol_snapshot(final, p, false, false);
133 r = copy_tree(final, p, false);
135 return log_error_errno(r, "Failed to copy image: %m");
137 return log_error_errno(r, "Failed to create local image: %m");
139 log_info("Created new local image '%s'.", local);
144 int import_make_read_only_fd(int fd) {
149 /* First, let's make this a read-only subvolume if it refers
151 r = btrfs_subvol_set_read_only_fd(fd, true);
152 if (r == -ENOTTY || r == -ENOTDIR || r == -EINVAL) {
155 /* This doesn't refer to a subvolume, or the file
156 * system isn't even btrfs. In that, case fall back to
161 return log_error_errno(errno, "Failed to stat temporary image: %m");
164 if (fchmod(fd, st.st_mode & 07555) < 0)
165 return log_error_errno(errno, "Failed to chmod() final image: %m");
170 return log_error_errno(r, "Failed to make subvolume read-only: %m");
175 int import_make_read_only(const char *path) {
176 _cleanup_close_ int fd = 1;
178 fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
180 return log_error_errno(errno, "Failed to open %s: %m", path);
182 return import_make_read_only_fd(fd);
185 int import_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret) {
186 _cleanup_free_ char *escaped_url = NULL;
193 image_root = "/var/lib/machines";
195 escaped_url = xescape(url, FILENAME_ESCAPE);
200 _cleanup_free_ char *escaped_etag = NULL;
202 escaped_etag = xescape(etag, FILENAME_ESCAPE);
206 path = strjoin(image_root, "/", strempty(prefix), escaped_url, ".", escaped_etag, strempty(suffix), NULL);
208 path = strjoin(image_root, "/", strempty(prefix), escaped_url, strempty(suffix), NULL);
216 int import_make_verification_jobs(
217 ImportJob **ret_checksum_job,
218 ImportJob **ret_signature_job,
222 ImportJobFinished on_finished,
225 _cleanup_(import_job_unrefp) ImportJob *checksum_job = NULL, *signature_job = NULL;
228 assert(ret_checksum_job);
229 assert(ret_signature_job);
231 assert(verify < _IMPORT_VERIFY_MAX);
235 if (verify != IMPORT_VERIFY_NO) {
236 _cleanup_free_ char *checksum_url = NULL;
238 /* Queue job for the SHA256SUMS file for the image */
239 r = import_url_change_last_component(url, "SHA256SUMS", &checksum_url);
243 r = import_job_new(&checksum_job, checksum_url, glue, userdata);
247 checksum_job->on_finished = on_finished;
248 checksum_job->uncompressed_max = checksum_job->compressed_max = 1ULL * 1024ULL * 1024ULL;
251 if (verify == IMPORT_VERIFY_SIGNATURE) {
252 _cleanup_free_ char *signature_url = NULL;
254 /* Queue job for the SHA256SUMS.gpg file for the image. */
255 r = import_url_change_last_component(url, "SHA256SUMS.gpg", &signature_url);
259 r = import_job_new(&signature_job, signature_url, glue, userdata);
263 signature_job->on_finished = on_finished;
264 signature_job->uncompressed_max = signature_job->compressed_max = 1ULL * 1024ULL * 1024ULL;
267 *ret_checksum_job = checksum_job;
268 *ret_signature_job = signature_job;
270 checksum_job = signature_job = NULL;
277 ImportJob *checksum_job,
278 ImportJob *signature_job) {
280 _cleanup_close_pair_ int gpg_pipe[2] = { -1, -1 };
281 _cleanup_free_ char *fn = NULL;
282 _cleanup_close_ int sig_file = -1;
283 const char *p, *line;
284 char sig_file_path[] = "/tmp/sigXXXXXX";
285 _cleanup_sigkill_wait_ pid_t pid = 0;
289 assert(main_job->state == IMPORT_JOB_DONE);
294 assert(main_job->calc_checksum);
295 assert(main_job->checksum);
296 assert(checksum_job->state == IMPORT_JOB_DONE);
298 if (!checksum_job->payload || checksum_job->payload_size <= 0) {
299 log_error("Checksum is empty, cannot verify.");
303 r = import_url_last_component(main_job->url, &fn);
307 if (!filename_is_valid(fn)) {
308 log_error("Cannot verify checksum, could not determine valid server-side file name.");
312 line = strappenda(main_job->checksum, " *", fn, "\n");
314 p = memmem(checksum_job->payload,
315 checksum_job->payload_size,
319 if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) {
320 log_error("Checksum did not check out, payload has been tempered with.");
324 log_info("SHA256 checksum of %s is valid.", main_job->url);
329 assert(signature_job->state == IMPORT_JOB_DONE);
331 if (!signature_job->payload || signature_job->payload_size <= 0) {
332 log_error("Signature is empty, cannot verify.");
336 r = pipe2(gpg_pipe, O_CLOEXEC);
338 return log_error_errno(errno, "Failed to create pipe for gpg: %m");
340 sig_file = mkostemp(sig_file_path, O_RDWR);
342 return log_error_errno(errno, "Failed to create temporary file: %m");
344 r = loop_write(sig_file, signature_job->payload, signature_job->payload_size, false);
346 log_error_errno(r, "Failed to write to temporary file: %m");
352 return log_error_errno(errno, "Failed to fork off gpg: %m");
354 const char *cmd[] = {
357 "--no-default-keyring",
358 "--no-auto-key-locate",
359 "--no-auto-check-trustdb",
361 "--trust-model=always",
362 NULL, /* keyring to use */
364 NULL, /* signature file */
366 NULL /* trailing NULL */
368 unsigned k = ELEMENTSOF(cmd) - 5;
373 reset_all_signal_handlers();
375 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
377 gpg_pipe[1] = safe_close(gpg_pipe[1]);
379 if (dup2(gpg_pipe[0], STDIN_FILENO) != STDIN_FILENO) {
380 log_error_errno(errno, "Failed to dup2() fd: %m");
384 if (gpg_pipe[0] != STDIN_FILENO)
385 gpg_pipe[0] = safe_close(gpg_pipe[0]);
387 null_fd = open("/dev/null", O_WRONLY|O_NOCTTY);
389 log_error_errno(errno, "Failed to open /dev/null: %m");
393 if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) {
394 log_error_errno(errno, "Failed to dup2() fd: %m");
398 if (null_fd != STDOUT_FILENO)
399 null_fd = safe_close(null_fd);
401 /* We add the user keyring only to the command line
402 * arguments, if it's around since gpg fails
404 if (access(USER_KEYRING_PATH, F_OK) >= 0)
405 cmd[k++] = "--keyring=" USER_KEYRING_PATH;
407 cmd[k++] = "--keyring=" VENDOR_KEYRING_PATH;
409 cmd[k++] = "--verify";
410 cmd[k++] = sig_file_path;
414 fd_cloexec(STDIN_FILENO, false);
415 fd_cloexec(STDOUT_FILENO, false);
416 fd_cloexec(STDERR_FILENO, false);
418 execvp("gpg", (char * const *) cmd);
419 log_error_errno(errno, "Failed to execute gpg: %m");
423 gpg_pipe[0] = safe_close(gpg_pipe[0]);
425 r = loop_write(gpg_pipe[1], checksum_job->payload, checksum_job->payload_size, false);
427 log_error_errno(r, "Failed to write to pipe: %m");
431 gpg_pipe[1] = safe_close(gpg_pipe[1]);
433 r = wait_for_terminate_and_warn("gpg", pid, true);
438 log_error("Signature verification failed.");
441 log_info("Signature verification succeeded.");
447 unlink(sig_file_path);
452 int import_fork_tar(const char *path, pid_t *ret) {
453 _cleanup_close_pair_ int pipefd[2] = { -1, -1 };
460 if (pipe2(pipefd, O_CLOEXEC) < 0)
461 return log_error_errno(errno, "Failed to create pipe for tar: %m");
465 return log_error_errno(errno, "Failed to fork off tar: %m");
470 (1ULL << CAP_CHOWN) |
471 (1ULL << CAP_FOWNER) |
472 (1ULL << CAP_FSETID) |
473 (1ULL << CAP_MKNOD) |
474 (1ULL << CAP_SETFCAP) |
475 (1ULL << CAP_DAC_OVERRIDE);
479 reset_all_signal_handlers();
481 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
483 pipefd[1] = safe_close(pipefd[1]);
485 if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) {
486 log_error_errno(errno, "Failed to dup2() fd: %m");
490 if (pipefd[0] != STDIN_FILENO)
491 pipefd[0] = safe_close(pipefd[0]);
493 null_fd = open("/dev/null", O_WRONLY|O_NOCTTY);
495 log_error_errno(errno, "Failed to open /dev/null: %m");
499 if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) {
500 log_error_errno(errno, "Failed to dup2() fd: %m");
504 if (null_fd != STDOUT_FILENO)
505 null_fd = safe_close(null_fd);
507 fd_cloexec(STDIN_FILENO, false);
508 fd_cloexec(STDOUT_FILENO, false);
509 fd_cloexec(STDERR_FILENO, false);
511 if (unshare(CLONE_NEWNET) < 0)
512 log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
514 r = capability_bounding_set_drop(~retain, true);
516 log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
518 execlp("tar", "tar", "--numeric-owner", "-C", path, "-px", NULL);
519 log_error_errno(errno, "Failed to execute tar: %m");
523 pipefd[0] = safe_close(pipefd[0]);