pull-common.c revision 1f9aa80a59e581dee9fc886eb6d7f3ca40bc609a
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering/***
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering This file is part of systemd.
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering Copyright 2015 Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering systemd is free software; you can redistribute it and/or modify it
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering under the terms of the GNU Lesser General Public License as published by
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering (at your option) any later version.
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering systemd is distributed in the hope that it will be useful, but
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering Lesser General Public License for more details.
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering You should have received a copy of the GNU Lesser General Public License
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering***/
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include <sys/prctl.h>
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include "util.h"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include "strv.h"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include "copy.h"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include "rm-rf.h"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include "btrfs-util.h"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include "capability.h"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include "pull-job.h"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include "pull-common.h"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include "process-util.h"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include "signal-util.h"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#include "siphash24.h"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#define FILENAME_ESCAPE "/.#\"\'"
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering#define HASH_URL_THRESHOLD_LENGTH (_POSIX_PATH_MAX - 16)
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poetteringint pull_find_old_etags(
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering const char *url,
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering const char *image_root,
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering int dt,
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering const char *prefix,
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering const char *suffix,
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering char ***etags) {
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering _cleanup_free_ char *escaped_url = NULL;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering _cleanup_closedir_ DIR *d = NULL;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering _cleanup_strv_free_ char **l = NULL;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering struct dirent *de;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering int r;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering assert(url);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering assert(etags);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (!image_root)
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering image_root = "/var/lib/machines";
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering escaped_url = xescape(url, FILENAME_ESCAPE);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (!escaped_url)
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering return -ENOMEM;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering d = opendir(image_root);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (!d) {
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (errno == ENOENT) {
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering *etags = NULL;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering return 0;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering }
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering return -errno;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering }
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering FOREACH_DIRENT_ALL(de, d, return -errno) {
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering const char *a, *b;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering char *u;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (de->d_type != DT_UNKNOWN &&
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering de->d_type != dt)
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering continue;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (prefix) {
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering a = startswith(de->d_name, prefix);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (!a)
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering continue;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering } else
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering a = de->d_name;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering a = startswith(a, escaped_url);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (!a)
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering continue;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering a = startswith(a, ".");
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (!a)
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering continue;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (suffix) {
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering b = endswith(de->d_name, suffix);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (!b)
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering continue;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering } else
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering b = strchr(de->d_name, 0);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (a >= b)
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering continue;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering r = cunescape_length(a, b - a, 0, &u);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (r < 0)
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering return r;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (!http_etag_is_valid(u)) {
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering free(u);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering continue;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering }
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering r = strv_consume(&l, u);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (r < 0)
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering return r;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering }
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering *etags = l;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering l = NULL;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering return 0;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering}
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poetteringint pull_make_local_copy(const char *final, const char *image_root, const char *local, bool force_local) {
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering const char *p;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering int r;
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering assert(final);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering assert(local);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (!image_root)
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering image_root = "/var/lib/machines";
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering p = strjoina(image_root, "/", local);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (force_local)
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering r = btrfs_subvol_snapshot(final, p, BTRFS_SNAPSHOT_QUOTA);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (r == -ENOTTY) {
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering r = copy_tree(final, p, false);
4e731273edfe852a3eee2949cd20f49fd5b4f6d7Lennart Poettering if (r < 0)
return log_error_errno(r, "Failed to copy image: %m");
} else if (r < 0)
return log_error_errno(r, "Failed to create local image: %m");
log_info("Created new local image '%s'.", local);
return 0;
}
static int hash_url(const char *url, char **ret) {
uint64_t h;
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);
assert(url);
siphash24((uint8_t *) &h, url, strlen(url), k.bytes);
if (asprintf(ret, "%"PRIx64, h) < 0)
return -ENOMEM;
return 0;
}
int pull_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret) {
_cleanup_free_ char *escaped_url = NULL, *escaped_etag = NULL;
char *path;
assert(url);
assert(ret);
if (!image_root)
image_root = "/var/lib/machines";
escaped_url = xescape(url, FILENAME_ESCAPE);
if (!escaped_url)
return -ENOMEM;
if (etag) {
escaped_etag = xescape(etag, FILENAME_ESCAPE);
if (!escaped_etag)
return -ENOMEM;
}
path = strjoin(image_root, "/", strempty(prefix), escaped_url, escaped_etag ? "." : "",
strempty(escaped_etag), strempty(suffix), NULL);
if (!path)
return -ENOMEM;
/* URLs might make the path longer than the maximum allowed length for a file name.
* When that happens, a URL hash is used instead. Paths returned by this function
* can be later used with tempfn_random() which adds 16 bytes to the resulting name. */
if (strlen(path) >= HASH_URL_THRESHOLD_LENGTH) {
_cleanup_free_ char *hash = NULL;
int r;
free(path);
r = hash_url(url, &hash);
if (r < 0)
return r;
path = strjoin(image_root, "/", strempty(prefix), hash, escaped_etag ? "." : "",
strempty(escaped_etag), strempty(suffix), NULL);
if (!path)
return -ENOMEM;
}
*ret = path;
return 0;
}
int pull_make_settings_job(
PullJob **ret,
const char *url,
CurlGlue *glue,
PullJobFinished on_finished,
void *userdata) {
_cleanup_free_ char *last_component = NULL, *ll = NULL, *settings_url = NULL;
_cleanup_(pull_job_unrefp) PullJob *job = NULL;
const char *q;
int r;
assert(ret);
assert(url);
assert(glue);
r = import_url_last_component(url, &last_component);
if (r < 0)
return r;
r = tar_strip_suffixes(last_component, &ll);
if (r < 0)
return r;
q = strjoina(ll, ".nspawn");
r = import_url_change_last_component(url, q, &settings_url);
if (r < 0)
return r;
r = pull_job_new(&job, settings_url, glue, userdata);
if (r < 0)
return r;
job->on_finished = on_finished;
job->compressed_max = job->uncompressed_max = 1ULL * 1024ULL * 1024ULL;
*ret = job;
job = NULL;
return 0;
}
int pull_make_verification_jobs(
PullJob **ret_checksum_job,
PullJob **ret_signature_job,
ImportVerify verify,
const char *url,
CurlGlue *glue,
PullJobFinished on_finished,
void *userdata) {
_cleanup_(pull_job_unrefp) PullJob *checksum_job = NULL, *signature_job = NULL;
int r;
assert(ret_checksum_job);
assert(ret_signature_job);
assert(verify >= 0);
assert(verify < _IMPORT_VERIFY_MAX);
assert(url);
assert(glue);
if (verify != IMPORT_VERIFY_NO) {
_cleanup_free_ char *checksum_url = NULL;
/* Queue job for the SHA256SUMS file for the image */
r = import_url_change_last_component(url, "SHA256SUMS", &checksum_url);
if (r < 0)
return r;
r = pull_job_new(&checksum_job, checksum_url, glue, userdata);
if (r < 0)
return r;
checksum_job->on_finished = on_finished;
checksum_job->uncompressed_max = checksum_job->compressed_max = 1ULL * 1024ULL * 1024ULL;
}
if (verify == IMPORT_VERIFY_SIGNATURE) {
_cleanup_free_ char *signature_url = NULL;
/* Queue job for the SHA256SUMS.gpg file for the image. */
r = import_url_change_last_component(url, "SHA256SUMS.gpg", &signature_url);
if (r < 0)
return r;
r = pull_job_new(&signature_job, signature_url, glue, userdata);
if (r < 0)
return r;
signature_job->on_finished = on_finished;
signature_job->uncompressed_max = signature_job->compressed_max = 1ULL * 1024ULL * 1024ULL;
}
*ret_checksum_job = checksum_job;
*ret_signature_job = signature_job;
checksum_job = signature_job = NULL;
return 0;
}
int pull_verify(PullJob *main_job,
PullJob *settings_job,
PullJob *checksum_job,
PullJob *signature_job) {
_cleanup_close_pair_ int gpg_pipe[2] = { -1, -1 };
_cleanup_free_ char *fn = NULL;
_cleanup_close_ int sig_file = -1;
const char *p, *line;
char sig_file_path[] = "/tmp/sigXXXXXX", gpg_home[] = "/tmp/gpghomeXXXXXX";
_cleanup_sigkill_wait_ pid_t pid = 0;
bool gpg_home_created = false;
int r;
assert(main_job);
assert(main_job->state == PULL_JOB_DONE);
if (!checksum_job)
return 0;
assert(main_job->calc_checksum);
assert(main_job->checksum);
assert(checksum_job->state == PULL_JOB_DONE);
if (!checksum_job->payload || checksum_job->payload_size <= 0) {
log_error("Checksum is empty, cannot verify.");
return -EBADMSG;
}
r = import_url_last_component(main_job->url, &fn);
if (r < 0)
return log_oom();
if (!filename_is_valid(fn)) {
log_error("Cannot verify checksum, could not determine valid server-side file name.");
return -EBADMSG;
}
line = strjoina(main_job->checksum, " *", fn, "\n");
p = memmem(checksum_job->payload,
checksum_job->payload_size,
line,
strlen(line));
if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) {
log_error("DOWNLOAD INVALID: Checksum did not check out, payload has been tampered with.");
return -EBADMSG;
}
log_info("SHA256 checksum of %s is valid.", main_job->url);
assert(!settings_job || IN_SET(settings_job->state, PULL_JOB_DONE, PULL_JOB_FAILED));
if (settings_job &&
settings_job->state == PULL_JOB_DONE &&
settings_job->error == 0 &&
!settings_job->etag_exists) {
_cleanup_free_ char *settings_fn = NULL;
assert(settings_job->calc_checksum);
assert(settings_job->checksum);
r = import_url_last_component(settings_job->url, &settings_fn);
if (r < 0)
return log_oom();
if (!filename_is_valid(settings_fn)) {
log_error("Cannot verify checksum, could not determine server-side settings file name.");
return -EBADMSG;
}
line = strjoina(settings_job->checksum, " *", settings_fn, "\n");
p = memmem(checksum_job->payload,
checksum_job->payload_size,
line,
strlen(line));
if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) {
log_error("DOWNLOAD INVALID: Checksum of settings file did not checkout, settings file has been tampered with.");
return -EBADMSG;
}
log_info("SHA256 checksum of %s is valid.", settings_job->url);
}
if (!signature_job)
return 0;
assert(signature_job->state == PULL_JOB_DONE);
if (!signature_job->payload || signature_job->payload_size <= 0) {
log_error("Signature is empty, cannot verify.");
return -EBADMSG;
}
r = pipe2(gpg_pipe, O_CLOEXEC);
if (r < 0)
return log_error_errno(errno, "Failed to create pipe for gpg: %m");
sig_file = mkostemp(sig_file_path, O_RDWR);
if (sig_file < 0)
return log_error_errno(errno, "Failed to create temporary file: %m");
r = loop_write(sig_file, signature_job->payload, signature_job->payload_size, false);
if (r < 0) {
log_error_errno(r, "Failed to write to temporary file: %m");
goto finish;
}
if (!mkdtemp(gpg_home)) {
r = log_error_errno(errno, "Failed to create tempory home for gpg: %m");
goto finish;
}
gpg_home_created = true;
pid = fork();
if (pid < 0)
return log_error_errno(errno, "Failed to fork off gpg: %m");
if (pid == 0) {
const char *cmd[] = {
"gpg",
"--no-options",
"--no-default-keyring",
"--no-auto-key-locate",
"--no-auto-check-trustdb",
"--batch",
"--trust-model=always",
NULL, /* --homedir= */
NULL, /* --keyring= */
NULL, /* --verify */
NULL, /* signature file */
NULL, /* dash */
NULL /* trailing NULL */
};
unsigned k = ELEMENTSOF(cmd) - 6;
int null_fd;
/* Child */
(void) reset_all_signal_handlers();
(void) reset_signal_mask();
assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
gpg_pipe[1] = safe_close(gpg_pipe[1]);
if (dup2(gpg_pipe[0], STDIN_FILENO) != STDIN_FILENO) {
log_error_errno(errno, "Failed to dup2() fd: %m");
_exit(EXIT_FAILURE);
}
if (gpg_pipe[0] != STDIN_FILENO)
gpg_pipe[0] = safe_close(gpg_pipe[0]);
null_fd = open("/dev/null", O_WRONLY|O_NOCTTY);
if (null_fd < 0) {
log_error_errno(errno, "Failed to open /dev/null: %m");
_exit(EXIT_FAILURE);
}
if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) {
log_error_errno(errno, "Failed to dup2() fd: %m");
_exit(EXIT_FAILURE);
}
if (null_fd != STDOUT_FILENO)
null_fd = safe_close(null_fd);
cmd[k++] = strjoina("--homedir=", gpg_home);
/* We add the user keyring only to the command line
* arguments, if it's around since gpg fails
* otherwise. */
if (access(USER_KEYRING_PATH, F_OK) >= 0)
cmd[k++] = "--keyring=" USER_KEYRING_PATH;
else
cmd[k++] = "--keyring=" VENDOR_KEYRING_PATH;
cmd[k++] = "--verify";
cmd[k++] = sig_file_path;
cmd[k++] = "-";
cmd[k++] = NULL;
fd_cloexec(STDIN_FILENO, false);
fd_cloexec(STDOUT_FILENO, false);
fd_cloexec(STDERR_FILENO, false);
execvp("gpg2", (char * const *) cmd);
execvp("gpg", (char * const *) cmd);
log_error_errno(errno, "Failed to execute gpg: %m");
_exit(EXIT_FAILURE);
}
gpg_pipe[0] = safe_close(gpg_pipe[0]);
r = loop_write(gpg_pipe[1], checksum_job->payload, checksum_job->payload_size, false);
if (r < 0) {
log_error_errno(r, "Failed to write to pipe: %m");
goto finish;
}
gpg_pipe[1] = safe_close(gpg_pipe[1]);
r = wait_for_terminate_and_warn("gpg", pid, true);
pid = 0;
if (r < 0)
goto finish;
if (r > 0) {
log_error("DOWNLOAD INVALID: Signature verification failed.");
r = -EBADMSG;
} else {
log_info("Signature verification succeeded.");
r = 0;
}
finish:
if (sig_file >= 0)
(void) unlink(sig_file_path);
if (gpg_home_created)
(void) rm_rf(gpg_home, REMOVE_ROOT|REMOVE_PHYSICAL);
return r;
}