chiark / gitweb /
38380fc0185c0033d879ba11c9caf8fe9dd8d354
[elogind.git] / src / import / pull-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 "pull-job.h"
30 #include "pull-common.h"
31
32 #define FILENAME_ESCAPE "/.#\"\'"
33
34 int pull_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 pull_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 = strjoina(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 pull_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 pull_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 pull_make_read_only_fd(fd);
183 }
184
185 int pull_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 pull_make_verification_jobs(
217                 PullJob **ret_checksum_job,
218                 PullJob **ret_signature_job,
219                 ImportVerify verify,
220                 const char *url,
221                 CurlGlue *glue,
222                 PullJobFinished on_finished,
223                 void *userdata) {
224
225         _cleanup_(pull_job_unrefp) PullJob *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 = pull_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 = pull_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 pull_verify(
276                 PullJob *main_job,
277                 PullJob *checksum_job,
278                 PullJob *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", gpg_home[] = "/tmp/gpghomeXXXXXX";
285         _cleanup_sigkill_wait_ pid_t pid = 0;
286         bool gpg_home_created = false;
287         int r;
288
289         assert(main_job);
290         assert(main_job->state == PULL_JOB_DONE);
291
292         if (!checksum_job)
293                 return 0;
294
295         assert(main_job->calc_checksum);
296         assert(main_job->checksum);
297         assert(checksum_job->state == PULL_JOB_DONE);
298
299         if (!checksum_job->payload || checksum_job->payload_size <= 0) {
300                 log_error("Checksum is empty, cannot verify.");
301                 return -EBADMSG;
302         }
303
304         r = import_url_last_component(main_job->url, &fn);
305         if (r < 0)
306                 return log_oom();
307
308         if (!filename_is_valid(fn)) {
309                 log_error("Cannot verify checksum, could not determine valid server-side file name.");
310                 return -EBADMSG;
311         }
312
313         line = strjoina(main_job->checksum, " *", fn, "\n");
314
315         p = memmem(checksum_job->payload,
316                    checksum_job->payload_size,
317                    line,
318                    strlen(line));
319
320         if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) {
321                 log_error("Checksum did not check out, payload has been tempered with.");
322                 return -EBADMSG;
323         }
324
325         log_info("SHA256 checksum of %s is valid.", main_job->url);
326
327         if (!signature_job)
328                 return 0;
329
330         assert(signature_job->state == PULL_JOB_DONE);
331
332         if (!signature_job->payload || signature_job->payload_size <= 0) {
333                 log_error("Signature is empty, cannot verify.");
334                 return -EBADMSG;
335         }
336
337         r = pipe2(gpg_pipe, O_CLOEXEC);
338         if (r < 0)
339                 return log_error_errno(errno, "Failed to create pipe for gpg: %m");
340
341         sig_file = mkostemp(sig_file_path, O_RDWR);
342         if (sig_file < 0)
343                 return log_error_errno(errno, "Failed to create temporary file: %m");
344
345         r = loop_write(sig_file, signature_job->payload, signature_job->payload_size, false);
346         if (r < 0) {
347                 log_error_errno(r, "Failed to write to temporary file: %m");
348                 goto finish;
349         }
350
351         if (!mkdtemp(gpg_home)) {
352                 r = log_error_errno(errno, "Failed to create tempory home for gpg: %m");
353                 goto finish;
354         }
355
356         gpg_home_created = true;
357
358         pid = fork();
359         if (pid < 0)
360                 return log_error_errno(errno, "Failed to fork off gpg: %m");
361         if (pid == 0) {
362                 const char *cmd[] = {
363                         "gpg",
364                         "--no-options",
365                         "--no-default-keyring",
366                         "--no-auto-key-locate",
367                         "--no-auto-check-trustdb",
368                         "--batch",
369                         "--trust-model=always",
370                         NULL, /* --homedir=  */
371                         NULL, /* --keyring= */
372                         NULL, /* --verify */
373                         NULL, /* signature file */
374                         NULL, /* dash */
375                         NULL  /* trailing NULL */
376                 };
377                 unsigned k = ELEMENTSOF(cmd) - 6;
378                 int null_fd;
379
380                 /* Child */
381
382                 reset_all_signal_handlers();
383                 reset_signal_mask();
384                 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
385
386                 gpg_pipe[1] = safe_close(gpg_pipe[1]);
387
388                 if (dup2(gpg_pipe[0], STDIN_FILENO) != STDIN_FILENO) {
389                         log_error_errno(errno, "Failed to dup2() fd: %m");
390                         _exit(EXIT_FAILURE);
391                 }
392
393                 if (gpg_pipe[0] != STDIN_FILENO)
394                         gpg_pipe[0] = safe_close(gpg_pipe[0]);
395
396                 null_fd = open("/dev/null", O_WRONLY|O_NOCTTY);
397                 if (null_fd < 0) {
398                         log_error_errno(errno, "Failed to open /dev/null: %m");
399                         _exit(EXIT_FAILURE);
400                 }
401
402                 if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) {
403                         log_error_errno(errno, "Failed to dup2() fd: %m");
404                         _exit(EXIT_FAILURE);
405                 }
406
407                 if (null_fd != STDOUT_FILENO)
408                         null_fd = safe_close(null_fd);
409
410                 cmd[k++] = strjoina("--homedir=", gpg_home);
411
412                 /* We add the user keyring only to the command line
413                  * arguments, if it's around since gpg fails
414                  * otherwise. */
415                 if (access(USER_KEYRING_PATH, F_OK) >= 0)
416                         cmd[k++] = "--keyring=" USER_KEYRING_PATH;
417                 else
418                         cmd[k++] = "--keyring=" VENDOR_KEYRING_PATH;
419
420                 cmd[k++] = "--verify";
421                 cmd[k++] = sig_file_path;
422                 cmd[k++] = "-";
423                 cmd[k++] = NULL;
424
425                 fd_cloexec(STDIN_FILENO, false);
426                 fd_cloexec(STDOUT_FILENO, false);
427                 fd_cloexec(STDERR_FILENO, false);
428
429                 execvp("gpg2", (char * const *) cmd);
430                 execvp("gpg", (char * const *) cmd);
431                 log_error_errno(errno, "Failed to execute gpg: %m");
432                 _exit(EXIT_FAILURE);
433         }
434
435         gpg_pipe[0] = safe_close(gpg_pipe[0]);
436
437         r = loop_write(gpg_pipe[1], checksum_job->payload, checksum_job->payload_size, false);
438         if (r < 0) {
439                 log_error_errno(r, "Failed to write to pipe: %m");
440                 goto finish;
441         }
442
443         gpg_pipe[1] = safe_close(gpg_pipe[1]);
444
445         r = wait_for_terminate_and_warn("gpg", pid, true);
446         pid = 0;
447         if (r < 0)
448                 goto finish;
449         if (r > 0) {
450                 log_error("Signature verification failed.");
451                 r = -EBADMSG;
452         } else {
453                 log_info("Signature verification succeeded.");
454                 r = 0;
455         }
456
457 finish:
458         if (sig_file >= 0)
459                 unlink(sig_file_path);
460
461         if (gpg_home_created)
462                 rm_rf_dangerous(gpg_home, false, true, false);
463
464         return r;
465 }
466
467 int pull_fork_tar(const char *path, pid_t *ret) {
468         _cleanup_close_pair_ int pipefd[2] = { -1, -1 };
469         pid_t pid;
470         int r;
471
472         assert(path);
473         assert(ret);
474
475         if (pipe2(pipefd, O_CLOEXEC) < 0)
476                 return log_error_errno(errno, "Failed to create pipe for tar: %m");
477
478         pid = fork();
479         if (pid < 0)
480                 return log_error_errno(errno, "Failed to fork off tar: %m");
481
482         if (pid == 0) {
483                 int null_fd;
484                 uint64_t retain =
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);
491
492                 /* Child */
493
494                 reset_all_signal_handlers();
495                 reset_signal_mask();
496                 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
497
498                 pipefd[1] = safe_close(pipefd[1]);
499
500                 if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) {
501                         log_error_errno(errno, "Failed to dup2() fd: %m");
502                         _exit(EXIT_FAILURE);
503                 }
504
505                 if (pipefd[0] != STDIN_FILENO)
506                         pipefd[0] = safe_close(pipefd[0]);
507
508                 null_fd = open("/dev/null", O_WRONLY|O_NOCTTY);
509                 if (null_fd < 0) {
510                         log_error_errno(errno, "Failed to open /dev/null: %m");
511                         _exit(EXIT_FAILURE);
512                 }
513
514                 if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) {
515                         log_error_errno(errno, "Failed to dup2() fd: %m");
516                         _exit(EXIT_FAILURE);
517                 }
518
519                 if (null_fd != STDOUT_FILENO)
520                         null_fd = safe_close(null_fd);
521
522                 fd_cloexec(STDIN_FILENO, false);
523                 fd_cloexec(STDOUT_FILENO, false);
524                 fd_cloexec(STDERR_FILENO, false);
525
526                 if (unshare(CLONE_NEWNET) < 0)
527                         log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
528
529                 r = capability_bounding_set_drop(~retain, true);
530                 if (r < 0)
531                         log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
532
533                 execlp("tar", "tar", "--numeric-owner", "-C", path, "-px", NULL);
534                 log_error_errno(errno, "Failed to execute tar: %m");
535                 _exit(EXIT_FAILURE);
536         }
537
538         pipefd[0] = safe_close(pipefd[0]);
539         r = pipefd[1];
540         pipefd[1] = -1;
541
542         *ret = pid;
543
544         return r;
545 }