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"
33 #define FILENAME_ESCAPE "/.#\"\'"
35 int pull_find_old_etags(const char *url
, const char *image_root
, int dt
, const char *prefix
, const char *suffix
, char ***etags
) {
36 _cleanup_free_
char *escaped_url
= NULL
;
37 _cleanup_closedir_
DIR *d
= NULL
;
38 _cleanup_strv_free_
char **l
= NULL
;
46 image_root
= "/var/lib/machines";
48 escaped_url
= xescape(url
, FILENAME_ESCAPE
);
52 d
= opendir(image_root
);
54 if (errno
== ENOENT
) {
62 FOREACH_DIRENT_ALL(de
, d
, return -errno
) {
66 if (de
->d_type
!= DT_UNKNOWN
&&
71 a
= startswith(de
->d_name
, prefix
);
77 a
= startswith(a
, escaped_url
);
81 a
= startswith(a
, ".");
86 b
= endswith(de
->d_name
, suffix
);
90 b
= strchr(de
->d_name
, 0);
95 u
= cunescape_length(a
, b
- a
);
99 if (!http_etag_is_valid(u
)) {
104 r
= strv_consume(&l
, u
);
115 int pull_make_local_copy(const char *final
, const char *image_root
, const char *local
, bool force_local
) {
123 image_root
= "/var/lib/machines";
125 p
= strjoina(image_root
, "/", local
);
128 (void) btrfs_subvol_remove(p
);
129 (void) rm_rf(p
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
132 r
= btrfs_subvol_snapshot(final
, p
, false, false);
134 r
= copy_tree(final
, p
, false);
136 return log_error_errno(r
, "Failed to copy image: %m");
138 return log_error_errno(r
, "Failed to create local image: %m");
140 log_info("Created new local image '%s'.", local
);
145 int pull_make_path(const char *url
, const char *etag
, const char *image_root
, const char *prefix
, const char *suffix
, char **ret
) {
146 _cleanup_free_
char *escaped_url
= NULL
;
153 image_root
= "/var/lib/machines";
155 escaped_url
= xescape(url
, FILENAME_ESCAPE
);
160 _cleanup_free_
char *escaped_etag
= NULL
;
162 escaped_etag
= xescape(etag
, FILENAME_ESCAPE
);
166 path
= strjoin(image_root
, "/", strempty(prefix
), escaped_url
, ".", escaped_etag
, strempty(suffix
), NULL
);
168 path
= strjoin(image_root
, "/", strempty(prefix
), escaped_url
, strempty(suffix
), NULL
);
176 int pull_make_verification_jobs(
177 PullJob
**ret_checksum_job
,
178 PullJob
**ret_signature_job
,
182 PullJobFinished on_finished
,
185 _cleanup_(pull_job_unrefp
) PullJob
*checksum_job
= NULL
, *signature_job
= NULL
;
188 assert(ret_checksum_job
);
189 assert(ret_signature_job
);
191 assert(verify
< _IMPORT_VERIFY_MAX
);
195 if (verify
!= IMPORT_VERIFY_NO
) {
196 _cleanup_free_
char *checksum_url
= NULL
;
198 /* Queue job for the SHA256SUMS file for the image */
199 r
= import_url_change_last_component(url
, "SHA256SUMS", &checksum_url
);
203 r
= pull_job_new(&checksum_job
, checksum_url
, glue
, userdata
);
207 checksum_job
->on_finished
= on_finished
;
208 checksum_job
->uncompressed_max
= checksum_job
->compressed_max
= 1ULL * 1024ULL * 1024ULL;
211 if (verify
== IMPORT_VERIFY_SIGNATURE
) {
212 _cleanup_free_
char *signature_url
= NULL
;
214 /* Queue job for the SHA256SUMS.gpg file for the image. */
215 r
= import_url_change_last_component(url
, "SHA256SUMS.gpg", &signature_url
);
219 r
= pull_job_new(&signature_job
, signature_url
, glue
, userdata
);
223 signature_job
->on_finished
= on_finished
;
224 signature_job
->uncompressed_max
= signature_job
->compressed_max
= 1ULL * 1024ULL * 1024ULL;
227 *ret_checksum_job
= checksum_job
;
228 *ret_signature_job
= signature_job
;
230 checksum_job
= signature_job
= NULL
;
237 PullJob
*checksum_job
,
238 PullJob
*signature_job
) {
240 _cleanup_close_pair_
int gpg_pipe
[2] = { -1, -1 };
241 _cleanup_free_
char *fn
= NULL
;
242 _cleanup_close_
int sig_file
= -1;
243 const char *p
, *line
;
244 char sig_file_path
[] = "/tmp/sigXXXXXX", gpg_home
[] = "/tmp/gpghomeXXXXXX";
245 _cleanup_sigkill_wait_ pid_t pid
= 0;
246 bool gpg_home_created
= false;
250 assert(main_job
->state
== PULL_JOB_DONE
);
255 assert(main_job
->calc_checksum
);
256 assert(main_job
->checksum
);
257 assert(checksum_job
->state
== PULL_JOB_DONE
);
259 if (!checksum_job
->payload
|| checksum_job
->payload_size
<= 0) {
260 log_error("Checksum is empty, cannot verify.");
264 r
= import_url_last_component(main_job
->url
, &fn
);
268 if (!filename_is_valid(fn
)) {
269 log_error("Cannot verify checksum, could not determine valid server-side file name.");
273 line
= strjoina(main_job
->checksum
, " *", fn
, "\n");
275 p
= memmem(checksum_job
->payload
,
276 checksum_job
->payload_size
,
280 if (!p
|| (p
!= (char*) checksum_job
->payload
&& p
[-1] != '\n')) {
281 log_error("Checksum did not check out, payload has been tempered with.");
285 log_info("SHA256 checksum of %s is valid.", main_job
->url
);
290 assert(signature_job
->state
== PULL_JOB_DONE
);
292 if (!signature_job
->payload
|| signature_job
->payload_size
<= 0) {
293 log_error("Signature is empty, cannot verify.");
297 r
= pipe2(gpg_pipe
, O_CLOEXEC
);
299 return log_error_errno(errno
, "Failed to create pipe for gpg: %m");
301 sig_file
= mkostemp(sig_file_path
, O_RDWR
);
303 return log_error_errno(errno
, "Failed to create temporary file: %m");
305 r
= loop_write(sig_file
, signature_job
->payload
, signature_job
->payload_size
, false);
307 log_error_errno(r
, "Failed to write to temporary file: %m");
311 if (!mkdtemp(gpg_home
)) {
312 r
= log_error_errno(errno
, "Failed to create tempory home for gpg: %m");
316 gpg_home_created
= true;
320 return log_error_errno(errno
, "Failed to fork off gpg: %m");
322 const char *cmd
[] = {
325 "--no-default-keyring",
326 "--no-auto-key-locate",
327 "--no-auto-check-trustdb",
329 "--trust-model=always",
330 NULL
, /* --homedir= */
331 NULL
, /* --keyring= */
333 NULL
, /* signature file */
335 NULL
/* trailing NULL */
337 unsigned k
= ELEMENTSOF(cmd
) - 6;
342 reset_all_signal_handlers();
344 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
346 gpg_pipe
[1] = safe_close(gpg_pipe
[1]);
348 if (dup2(gpg_pipe
[0], STDIN_FILENO
) != STDIN_FILENO
) {
349 log_error_errno(errno
, "Failed to dup2() fd: %m");
353 if (gpg_pipe
[0] != STDIN_FILENO
)
354 gpg_pipe
[0] = safe_close(gpg_pipe
[0]);
356 null_fd
= open("/dev/null", O_WRONLY
|O_NOCTTY
);
358 log_error_errno(errno
, "Failed to open /dev/null: %m");
362 if (dup2(null_fd
, STDOUT_FILENO
) != STDOUT_FILENO
) {
363 log_error_errno(errno
, "Failed to dup2() fd: %m");
367 if (null_fd
!= STDOUT_FILENO
)
368 null_fd
= safe_close(null_fd
);
370 cmd
[k
++] = strjoina("--homedir=", gpg_home
);
372 /* We add the user keyring only to the command line
373 * arguments, if it's around since gpg fails
375 if (access(USER_KEYRING_PATH
, F_OK
) >= 0)
376 cmd
[k
++] = "--keyring=" USER_KEYRING_PATH
;
378 cmd
[k
++] = "--keyring=" VENDOR_KEYRING_PATH
;
380 cmd
[k
++] = "--verify";
381 cmd
[k
++] = sig_file_path
;
385 fd_cloexec(STDIN_FILENO
, false);
386 fd_cloexec(STDOUT_FILENO
, false);
387 fd_cloexec(STDERR_FILENO
, false);
389 execvp("gpg2", (char * const *) cmd
);
390 execvp("gpg", (char * const *) cmd
);
391 log_error_errno(errno
, "Failed to execute gpg: %m");
395 gpg_pipe
[0] = safe_close(gpg_pipe
[0]);
397 r
= loop_write(gpg_pipe
[1], checksum_job
->payload
, checksum_job
->payload_size
, false);
399 log_error_errno(r
, "Failed to write to pipe: %m");
403 gpg_pipe
[1] = safe_close(gpg_pipe
[1]);
405 r
= wait_for_terminate_and_warn("gpg", pid
, true);
410 log_error("Signature verification failed.");
413 log_info("Signature verification succeeded.");
419 unlink(sig_file_path
);
421 if (gpg_home_created
)
422 (void) rm_rf(gpg_home
, REMOVE_ROOT
|REMOVE_PHYSICAL
);