chiark / gitweb /
b490c435d2bff0ff60db1e11797e7d4ae311a2a4
[elogind.git] / src / import / import-common.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2015 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <sys/prctl.h>
23
24 #include "util.h"
25 #include "strv.h"
26 #include "copy.h"
27 #include "btrfs-util.h"
28 #include "capability.h"
29 #include "import-job.h"
30 #include "import-common.h"
31
32 #define FILENAME_ESCAPE "/.#\"\'"
33
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;
38         struct dirent *de;
39         int r;
40
41         assert(url);
42         assert(etags);
43
44         if (!image_root)
45                 image_root = "/var/lib/machines";
46
47         escaped_url = xescape(url, FILENAME_ESCAPE);
48         if (!escaped_url)
49                 return -ENOMEM;
50
51         d = opendir(image_root);
52         if (!d) {
53                 if (errno == ENOENT) {
54                         *etags = NULL;
55                         return 0;
56                 }
57
58                 return -errno;
59         }
60
61         FOREACH_DIRENT_ALL(de, d, return -errno) {
62                 const char *a, *b;
63                 char *u;
64
65                 if (de->d_type != DT_UNKNOWN &&
66                     de->d_type != dt)
67                         continue;
68
69                 if (prefix) {
70                         a = startswith(de->d_name, prefix);
71                         if (!a)
72                                 continue;
73                 } else
74                         a = de->d_name;
75
76                 a = startswith(a, escaped_url);
77                 if (!a)
78                         continue;
79
80                 a = startswith(a, ".");
81                 if (!a)
82                         continue;
83
84                 if (suffix) {
85                         b = endswith(de->d_name, suffix);
86                         if (!b)
87                                 continue;
88                 } else
89                         b = strchr(de->d_name, 0);
90
91                 if (a >= b)
92                         continue;
93
94                 u = cunescape_length(a, b - a);
95                 if (!u)
96                         return -ENOMEM;
97
98                 if (!http_etag_is_valid(u)) {
99                         free(u);
100                         continue;
101                 }
102
103                 r = strv_consume(&l, u);
104                 if (r < 0)
105                         return r;
106         }
107
108         *etags = l;
109         l = NULL;
110
111         return 0;
112 }
113
114 int import_make_local_copy(const char *final, const char *image_root, const char *local, bool force_local) {
115         const char *p;
116         int r;
117
118         assert(final);
119         assert(local);
120
121         if (!image_root)
122                 image_root = "/var/lib/machines";
123
124         p = strappenda(image_root, "/", local);
125
126         if (force_local) {
127                 (void) btrfs_subvol_remove(p);
128                 (void) rm_rf_dangerous(p, false, true, false);
129         }
130
131         r = btrfs_subvol_snapshot(final, p, false, false);
132         if (r == -ENOTTY) {
133                 r = copy_tree(final, p, false);
134                 if (r < 0)
135                         return log_error_errno(r, "Failed to copy image: %m");
136         } else if (r < 0)
137                 return log_error_errno(r, "Failed to create local image: %m");
138
139         log_info("Created new local image '%s'.", local);
140
141         return 0;
142 }
143
144 int import_make_read_only_fd(int fd) {
145         int r;
146
147         assert(fd >= 0);
148
149         /* First, let's make this a read-only subvolume if it refers
150          * to a subvolume */
151         r = btrfs_subvol_set_read_only_fd(fd, true);
152         if (r == -ENOTTY || r == -ENOTDIR || r == -EINVAL) {
153                 struct stat st;
154
155                 /* This doesn't refer to a subvolume, or the file
156                  * system isn't even btrfs. In that, case fall back to
157                  * chmod()ing */
158
159                 r = fstat(fd, &st);
160                 if (r < 0)
161                         return log_error_errno(errno, "Failed to stat temporary image: %m");
162
163                 /* Drop "w" flag */
164                 if (fchmod(fd, st.st_mode & 07555) < 0)
165                         return log_error_errno(errno, "Failed to chmod() final image: %m");
166
167                 return 0;
168
169         } else if (r < 0)
170                 return log_error_errno(r, "Failed to make subvolume read-only: %m");
171
172         return 0;
173 }
174
175 int import_make_read_only(const char *path) {
176         _cleanup_close_ int fd = 1;
177
178         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
179         if (fd < 0)
180                 return log_error_errno(errno, "Failed to open %s: %m", path);
181
182         return import_make_read_only_fd(fd);
183 }
184
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;
187         char *path;
188
189         assert(url);
190         assert(ret);
191
192         if (!image_root)
193                 image_root = "/var/lib/machines";
194
195         escaped_url = xescape(url, FILENAME_ESCAPE);
196         if (!escaped_url)
197                 return -ENOMEM;
198
199         if (etag) {
200                 _cleanup_free_ char *escaped_etag = NULL;
201
202                 escaped_etag = xescape(etag, FILENAME_ESCAPE);
203                 if (!escaped_etag)
204                         return -ENOMEM;
205
206                 path = strjoin(image_root, "/", strempty(prefix), escaped_url, ".", escaped_etag, strempty(suffix), NULL);
207         } else
208                 path = strjoin(image_root, "/", strempty(prefix), escaped_url, strempty(suffix), NULL);
209         if (!path)
210                 return -ENOMEM;
211
212         *ret = path;
213         return 0;
214 }
215
216 int import_make_verification_jobs(
217                 ImportJob **ret_checksum_job,
218                 ImportJob **ret_signature_job,
219                 ImportVerify verify,
220                 const char *url,
221                 CurlGlue *glue,
222                 ImportJobFinished on_finished,
223                 void *userdata) {
224
225         _cleanup_(import_job_unrefp) ImportJob *checksum_job = NULL, *signature_job = NULL;
226         int r;
227
228         assert(ret_checksum_job);
229         assert(ret_signature_job);
230         assert(verify >= 0);
231         assert(verify < _IMPORT_VERIFY_MAX);
232         assert(url);
233         assert(glue);
234
235         if (verify != IMPORT_VERIFY_NO) {
236                 _cleanup_free_ char *checksum_url = NULL;
237
238                 /* Queue job for the SHA256SUMS file for the image */
239                 r = import_url_change_last_component(url, "SHA256SUMS", &checksum_url);
240                 if (r < 0)
241                         return r;
242
243                 r = import_job_new(&checksum_job, checksum_url, glue, userdata);
244                 if (r < 0)
245                         return r;
246
247                 checksum_job->on_finished = on_finished;
248                 checksum_job->uncompressed_max = checksum_job->compressed_max = 1ULL * 1024ULL * 1024ULL;
249         }
250
251         if (verify == IMPORT_VERIFY_SIGNATURE) {
252                 _cleanup_free_ char *signature_url = NULL;
253
254                 /* Queue job for the SHA256SUMS.gpg file for the image. */
255                 r = import_url_change_last_component(url, "SHA256SUMS.gpg", &signature_url);
256                 if (r < 0)
257                         return r;
258
259                 r = import_job_new(&signature_job, signature_url, glue, userdata);
260                 if (r < 0)
261                         return r;
262
263                 signature_job->on_finished = on_finished;
264                 signature_job->uncompressed_max = signature_job->compressed_max = 1ULL * 1024ULL * 1024ULL;
265         }
266
267         *ret_checksum_job = checksum_job;
268         *ret_signature_job = signature_job;
269
270         checksum_job = signature_job = NULL;
271
272         return 0;
273 }
274
275 int import_verify(
276                 ImportJob *main_job,
277                 ImportJob *checksum_job,
278                 ImportJob *signature_job) {
279
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;
286         int r;
287
288         assert(main_job);
289         assert(main_job->state == IMPORT_JOB_DONE);
290
291         if (!checksum_job)
292                 return 0;
293
294         assert(main_job->calc_checksum);
295         assert(main_job->checksum);
296         assert(checksum_job->state == IMPORT_JOB_DONE);
297
298         if (!checksum_job->payload || checksum_job->payload_size <= 0) {
299                 log_error("Checksum is empty, cannot verify.");
300                 return -EBADMSG;
301         }
302
303         r = import_url_last_component(main_job->url, &fn);
304         if (r < 0)
305                 return log_oom();
306
307         if (!filename_is_valid(fn)) {
308                 log_error("Cannot verify checksum, could not determine valid server-side file name.");
309                 return -EBADMSG;
310         }
311
312         line = strappenda(main_job->checksum, " *", fn, "\n");
313
314         p = memmem(checksum_job->payload,
315                    checksum_job->payload_size,
316                    line,
317                    strlen(line));
318
319         if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) {
320                 log_error("Checksum did not check out, payload has been tempered with.");
321                 return -EBADMSG;
322         }
323
324         log_info("SHA256 checksum of %s is valid.", main_job->url);
325
326         if (!signature_job)
327                 return 0;
328
329         assert(signature_job->state == IMPORT_JOB_DONE);
330
331         if (!signature_job->payload || signature_job->payload_size <= 0) {
332                 log_error("Signature is empty, cannot verify.");
333                 return -EBADMSG;
334         }
335
336         r = pipe2(gpg_pipe, O_CLOEXEC);
337         if (r < 0)
338                 return log_error_errno(errno, "Failed to create pipe for gpg: %m");
339
340         sig_file = mkostemp(sig_file_path, O_RDWR);
341         if (sig_file < 0)
342                 return log_error_errno(errno, "Failed to create temporary file: %m");
343
344         r = loop_write(sig_file, signature_job->payload, signature_job->payload_size, false);
345         if (r < 0) {
346                 log_error_errno(r, "Failed to write to temporary file: %m");
347                 goto finish;
348         }
349
350         pid = fork();
351         if (pid < 0)
352                 return log_error_errno(errno, "Failed to fork off gpg: %m");
353         if (pid == 0) {
354                 const char *cmd[] = {
355                         "gpg",
356                         "--no-options",
357                         "--no-default-keyring",
358                         "--no-auto-key-locate",
359                         "--no-auto-check-trustdb",
360                         "--batch",
361                         "--trust-model=always",
362                         NULL, /* keyring to use */
363                         NULL, /* --verify */
364                         NULL, /* signature file */
365                         NULL, /* dash */
366                         NULL  /* trailing NULL */
367                 };
368                 unsigned k = ELEMENTSOF(cmd) - 5;
369                 int null_fd;
370
371                 /* Child */
372
373                 reset_all_signal_handlers();
374                 reset_signal_mask();
375                 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
376
377                 gpg_pipe[1] = safe_close(gpg_pipe[1]);
378
379                 if (dup2(gpg_pipe[0], STDIN_FILENO) != STDIN_FILENO) {
380                         log_error_errno(errno, "Failed to dup2() fd: %m");
381                         _exit(EXIT_FAILURE);
382                 }
383
384                 if (gpg_pipe[0] != STDIN_FILENO)
385                         gpg_pipe[0] = safe_close(gpg_pipe[0]);
386
387                 null_fd = open("/dev/null", O_WRONLY|O_NOCTTY);
388                 if (null_fd < 0) {
389                         log_error_errno(errno, "Failed to open /dev/null: %m");
390                         _exit(EXIT_FAILURE);
391                 }
392
393                 if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) {
394                         log_error_errno(errno, "Failed to dup2() fd: %m");
395                         _exit(EXIT_FAILURE);
396                 }
397
398                 if (null_fd != STDOUT_FILENO)
399                         null_fd = safe_close(null_fd);
400
401                 /* We add the user keyring only to the command line
402                  * arguments, if it's around since gpg fails
403                  * otherwise. */
404                 if (access(USER_KEYRING_PATH, F_OK) >= 0)
405                         cmd[k++] = "--keyring=" USER_KEYRING_PATH;
406                 else
407                         cmd[k++] = "--keyring=" VENDOR_KEYRING_PATH;
408
409                 cmd[k++] = "--verify";
410                 cmd[k++] = sig_file_path;
411                 cmd[k++] = "-";
412                 cmd[k++] = NULL;
413
414                 fd_cloexec(STDIN_FILENO, false);
415                 fd_cloexec(STDOUT_FILENO, false);
416                 fd_cloexec(STDERR_FILENO, false);
417
418                 execvp("gpg", (char * const *) cmd);
419                 log_error_errno(errno, "Failed to execute gpg: %m");
420                 _exit(EXIT_FAILURE);
421         }
422
423         gpg_pipe[0] = safe_close(gpg_pipe[0]);
424
425         r = loop_write(gpg_pipe[1], checksum_job->payload, checksum_job->payload_size, false);
426         if (r < 0) {
427                 log_error_errno(r, "Failed to write to pipe: %m");
428                 goto finish;
429         }
430
431         gpg_pipe[1] = safe_close(gpg_pipe[1]);
432
433         r = wait_for_terminate_and_warn("gpg", pid, true);
434         pid = 0;
435         if (r < 0)
436                 goto finish;
437         if (r > 0) {
438                 log_error("Signature verification failed.");
439                 r = -EBADMSG;
440         } else {
441                 log_info("Signature verification succeeded.");
442                 r = 0;
443         }
444
445 finish:
446         if (sig_file >= 0)
447                 unlink(sig_file_path);
448
449         return r;
450 }
451
452 int import_fork_tar(const char *path, pid_t *ret) {
453         _cleanup_close_pair_ int pipefd[2] = { -1, -1 };
454         pid_t pid;
455         int r;
456
457         assert(path);
458         assert(ret);
459
460         if (pipe2(pipefd, O_CLOEXEC) < 0)
461                 return log_error_errno(errno, "Failed to create pipe for tar: %m");
462
463         pid = fork();
464         if (pid < 0)
465                 return log_error_errno(errno, "Failed to fork off tar: %m");
466
467         if (pid == 0) {
468                 int null_fd;
469                 uint64_t retain =
470                         (1ULL << CAP_CHOWN) |
471                         (1ULL << CAP_FOWNER) |
472                         (1ULL << CAP_FSETID) |
473                         (1ULL << CAP_MKNOD) |
474                         (1ULL << CAP_SETFCAP);
475
476                 /* Child */
477
478                 reset_all_signal_handlers();
479                 reset_signal_mask();
480                 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
481
482                 pipefd[1] = safe_close(pipefd[1]);
483
484                 if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) {
485                         log_error_errno(errno, "Failed to dup2() fd: %m");
486                         _exit(EXIT_FAILURE);
487                 }
488
489                 if (pipefd[0] != STDIN_FILENO)
490                         pipefd[0] = safe_close(pipefd[0]);
491
492                 null_fd = open("/dev/null", O_WRONLY|O_NOCTTY);
493                 if (null_fd < 0) {
494                         log_error_errno(errno, "Failed to open /dev/null: %m");
495                         _exit(EXIT_FAILURE);
496                 }
497
498                 if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) {
499                         log_error_errno(errno, "Failed to dup2() fd: %m");
500                         _exit(EXIT_FAILURE);
501                 }
502
503                 if (null_fd != STDOUT_FILENO)
504                         null_fd = safe_close(null_fd);
505
506                 fd_cloexec(STDIN_FILENO, false);
507                 fd_cloexec(STDOUT_FILENO, false);
508                 fd_cloexec(STDERR_FILENO, false);
509
510                 r = capability_bounding_set_drop(~retain, true);
511                 if (r < 0) {
512                         log_error_errno(errno, "Failed to drop capabilities, ignoring: %m");
513                         _exit(EXIT_FAILURE);
514                 }
515
516                 execlp("tar", "tar", "--numeric-owner", "-C", path, "-px", NULL);
517                 log_error_errno(errno, "Failed to execute tar: %m");
518                 _exit(EXIT_FAILURE);
519         }
520
521         pipefd[0] = safe_close(pipefd[0]);
522         r = pipefd[1];
523         pipefd[1] = -1;
524
525         *ret = pid;
526
527         return r;
528 }