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 = strjoina(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", gpg_home[] = "/tmp/gpghomeXXXXXX";
285 _cleanup_sigkill_wait_ pid_t pid = 0;
286 bool gpg_home_created = false;
290 assert(main_job->state == IMPORT_JOB_DONE);
295 assert(main_job->calc_checksum);
296 assert(main_job->checksum);
297 assert(checksum_job->state == IMPORT_JOB_DONE);
299 if (!checksum_job->payload || checksum_job->payload_size <= 0) {
300 log_error("Checksum is empty, cannot verify.");
304 r = import_url_last_component(main_job->url, &fn);
308 if (!filename_is_valid(fn)) {
309 log_error("Cannot verify checksum, could not determine valid server-side file name.");
313 line = strjoina(main_job->checksum, " *", fn, "\n");
315 p = memmem(checksum_job->payload,
316 checksum_job->payload_size,
320 if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) {
321 log_error("Checksum did not check out, payload has been tempered with.");
325 log_info("SHA256 checksum of %s is valid.", main_job->url);
330 assert(signature_job->state == IMPORT_JOB_DONE);
332 if (!signature_job->payload || signature_job->payload_size <= 0) {
333 log_error("Signature is empty, cannot verify.");
337 r = pipe2(gpg_pipe, O_CLOEXEC);
339 return log_error_errno(errno, "Failed to create pipe for gpg: %m");
341 sig_file = mkostemp(sig_file_path, O_RDWR);
343 return log_error_errno(errno, "Failed to create temporary file: %m");
345 r = loop_write(sig_file, signature_job->payload, signature_job->payload_size, false);
347 log_error_errno(r, "Failed to write to temporary file: %m");
351 if (!mkdtemp(gpg_home)) {
352 r = log_error_errno(errno, "Failed to create tempory home for gpg: %m");
356 gpg_home_created = true;
360 return log_error_errno(errno, "Failed to fork off gpg: %m");
362 const char *cmd[] = {
365 "--no-default-keyring",
366 "--no-auto-key-locate",
367 "--no-auto-check-trustdb",
369 "--trust-model=always",
370 NULL, /* --homedir= */
371 NULL, /* --keyring= */
373 NULL, /* signature file */
375 NULL /* trailing NULL */
377 unsigned k = ELEMENTSOF(cmd) - 6;
382 reset_all_signal_handlers();
384 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
386 gpg_pipe[1] = safe_close(gpg_pipe[1]);
388 if (dup2(gpg_pipe[0], STDIN_FILENO) != STDIN_FILENO) {
389 log_error_errno(errno, "Failed to dup2() fd: %m");
393 if (gpg_pipe[0] != STDIN_FILENO)
394 gpg_pipe[0] = safe_close(gpg_pipe[0]);
396 null_fd = open("/dev/null", O_WRONLY|O_NOCTTY);
398 log_error_errno(errno, "Failed to open /dev/null: %m");
402 if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) {
403 log_error_errno(errno, "Failed to dup2() fd: %m");
407 if (null_fd != STDOUT_FILENO)
408 null_fd = safe_close(null_fd);
410 cmd[k++] = strjoina("--homedir=", gpg_home);
412 /* We add the user keyring only to the command line
413 * arguments, if it's around since gpg fails
415 if (access(USER_KEYRING_PATH, F_OK) >= 0)
416 cmd[k++] = "--keyring=" USER_KEYRING_PATH;
418 cmd[k++] = "--keyring=" VENDOR_KEYRING_PATH;
420 cmd[k++] = "--verify";
421 cmd[k++] = sig_file_path;
425 fd_cloexec(STDIN_FILENO, false);
426 fd_cloexec(STDOUT_FILENO, false);
427 fd_cloexec(STDERR_FILENO, false);
429 execvp("gpg2", (char * const *) cmd);
430 execvp("gpg", (char * const *) cmd);
431 log_error_errno(errno, "Failed to execute gpg: %m");
435 gpg_pipe[0] = safe_close(gpg_pipe[0]);
437 r = loop_write(gpg_pipe[1], checksum_job->payload, checksum_job->payload_size, false);
439 log_error_errno(r, "Failed to write to pipe: %m");
443 gpg_pipe[1] = safe_close(gpg_pipe[1]);
445 r = wait_for_terminate_and_warn("gpg", pid, true);
450 log_error("Signature verification failed.");
453 log_info("Signature verification succeeded.");
459 unlink(sig_file_path);
461 if (gpg_home_created)
462 rm_rf_dangerous(gpg_home, false, true, false);
467 int import_fork_tar(const char *path, pid_t *ret) {
468 _cleanup_close_pair_ int pipefd[2] = { -1, -1 };
475 if (pipe2(pipefd, O_CLOEXEC) < 0)
476 return log_error_errno(errno, "Failed to create pipe for tar: %m");
480 return log_error_errno(errno, "Failed to fork off tar: %m");
485 (1ULL << CAP_CHOWN) |
486 (1ULL << CAP_FOWNER) |
487 (1ULL << CAP_FSETID) |
488 (1ULL << CAP_MKNOD) |
489 (1ULL << CAP_SETFCAP) |
490 (1ULL << CAP_DAC_OVERRIDE);
494 reset_all_signal_handlers();
496 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
498 pipefd[1] = safe_close(pipefd[1]);
500 if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) {
501 log_error_errno(errno, "Failed to dup2() fd: %m");
505 if (pipefd[0] != STDIN_FILENO)
506 pipefd[0] = safe_close(pipefd[0]);
508 null_fd = open("/dev/null", O_WRONLY|O_NOCTTY);
510 log_error_errno(errno, "Failed to open /dev/null: %m");
514 if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) {
515 log_error_errno(errno, "Failed to dup2() fd: %m");
519 if (null_fd != STDOUT_FILENO)
520 null_fd = safe_close(null_fd);
522 fd_cloexec(STDIN_FILENO, false);
523 fd_cloexec(STDOUT_FILENO, false);
524 fd_cloexec(STDERR_FILENO, false);
526 if (unshare(CLONE_NEWNET) < 0)
527 log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
529 r = capability_bounding_set_drop(~retain, true);
531 log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
533 execlp("tar", "tar", "--numeric-owner", "-C", path, "-px", NULL);
534 log_error_errno(errno, "Failed to execute tar: %m");
538 pipefd[0] = safe_close(pipefd[0]);