chiark / gitweb /
1c5c99c39d8975cbed201cf576802d14d5520e6b
[elogind.git] / src / journal / coredump.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2012 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 <errno.h>
23 #include <unistd.h>
24 #include <stdio.h>
25 #include <sys/prctl.h>
26 #include <sys/types.h>
27 #include <sys/xattr.h>
28
29 #include <systemd/sd-journal.h>
30 #include <systemd/sd-login.h>
31
32 #include "log.h"
33 #include "util.h"
34 #include "strv.h"
35 #include "macro.h"
36 #include "mkdir.h"
37 #include "special.h"
38 #include "cgroup-util.h"
39 #include "journald-native.h"
40 #include "conf-parser.h"
41 #include "copy.h"
42 #include "stacktrace.h"
43 #include "path-util.h"
44 #include "compress.h"
45
46 #ifdef HAVE_ACL
47 #include <sys/acl.h>
48 #include "acl-util.h"
49 #endif
50
51 /* The maximum size up to which we process coredumps */
52 #define PROCESS_SIZE_MAX ((off_t) (2LLU*1024LLU*1024LLU*1024LLU))
53
54 /* The maximum size up to which we leave the coredump around on
55  * disk */
56 #define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX
57
58 /* The maximum size up to which we store the coredump in the
59  * journal */
60 #define JOURNAL_SIZE_MAX ((size_t) (767LU*1024LU*1024LU))
61
62 /* Make sure to not make this larger than the maximum journal entry
63  * size. See ENTRY_SIZE_MAX in journald-native.c. */
64 assert_cc(JOURNAL_SIZE_MAX <= ENTRY_SIZE_MAX);
65
66 enum {
67         INFO_PID,
68         INFO_UID,
69         INFO_GID,
70         INFO_SIGNAL,
71         INFO_TIMESTAMP,
72         INFO_COMM,
73         INFO_EXE,
74         _INFO_LEN
75 };
76
77 typedef enum CoredumpStorage {
78         COREDUMP_STORAGE_NONE,
79         COREDUMP_STORAGE_EXTERNAL,
80         COREDUMP_STORAGE_JOURNAL,
81         COREDUMP_STORAGE_BOTH,
82         _COREDUMP_STORAGE_MAX,
83         _COREDUMP_STORAGE_INVALID = -1
84 } CoredumpStorage;
85
86 static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL;
87 static off_t arg_process_size_max = PROCESS_SIZE_MAX;
88 static off_t arg_external_size_max = EXTERNAL_SIZE_MAX;
89 static size_t arg_journal_size_max = JOURNAL_SIZE_MAX;
90
91 static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = {
92         [COREDUMP_STORAGE_NONE] = "none",
93         [COREDUMP_STORAGE_EXTERNAL] = "external",
94         [COREDUMP_STORAGE_JOURNAL] = "journal",
95         [COREDUMP_STORAGE_BOTH] = "both",
96 };
97
98 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage);
99 static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage, CoredumpStorage, "Failed to parse storage setting");
100
101 static int parse_config(void) {
102
103         static const ConfigTableItem items[] = {
104                 { "Coredump", "ProcessSizeMax",  config_parse_iec_off,           0, &arg_process_size_max  },
105                 { "Coredump", "ExternalSizeMax", config_parse_iec_off,           0, &arg_external_size_max },
106                 { "Coredump", "JournalSizeMax",  config_parse_iec_size,          0, &arg_journal_size_max  },
107                 { "Coredump", "Storage",         config_parse_coredump_storage,  0, &arg_storage           },
108                 {}
109         };
110
111         return config_parse(
112                         NULL,
113                         "/etc/systemd/coredump.conf",
114                         NULL,
115                         "Coredump\0",
116                         config_item_table_lookup,
117                         (void*) items,
118                         false,
119                         false,
120                         NULL);
121 }
122
123 static int fix_acl(int fd, uid_t uid) {
124
125 #ifdef HAVE_ACL
126         _cleanup_(acl_freep) acl_t acl = NULL;
127         acl_entry_t entry;
128         acl_permset_t permset;
129
130         if (uid <= SYSTEM_UID_MAX)
131                 return 0;
132
133         /* Make sure normal users can read (but not write or delete)
134          * their own coredumps */
135
136         acl = acl_get_fd(fd);
137         if (!acl) {
138                 log_error("Failed to get ACL: %m");
139                 return -errno;
140         }
141
142         if (acl_create_entry(&acl, &entry) < 0 ||
143             acl_set_tag_type(entry, ACL_USER) < 0 ||
144             acl_set_qualifier(entry, &uid) < 0) {
145                 log_error("Failed to patch ACL: %m");
146                 return -errno;
147         }
148
149         if (acl_get_permset(entry, &permset) < 0 ||
150             acl_add_perm(permset, ACL_READ) < 0 ||
151             calc_acl_mask_if_needed(&acl) < 0) {
152                 log_warning("Failed to patch ACL: %m");
153                 return -errno;
154         }
155
156         if (acl_set_fd(fd, acl) < 0) {
157                 log_error("Failed to apply ACL: %m");
158                 return -errno;
159         }
160 #endif
161
162         return 0;
163 }
164
165 static int fix_xattr(int fd, const char *info[_INFO_LEN]) {
166
167         static const char * const xattrs[_INFO_LEN] = {
168                 [INFO_PID] = "user.coredump.pid",
169                 [INFO_UID] = "user.coredump.uid",
170                 [INFO_GID] = "user.coredump.gid",
171                 [INFO_SIGNAL] = "user.coredump.signal",
172                 [INFO_TIMESTAMP] = "user.coredump.timestamp",
173                 [INFO_COMM] = "user.coredump.comm",
174                 [INFO_EXE] = "user.coredump.exe",
175         };
176
177         int r = 0;
178         unsigned i;
179
180         /* Attach some metadata to coredumps via extended
181          * attributes. Just because we can. */
182
183         for (i = 0; i < _INFO_LEN; i++) {
184                 int k;
185
186                 if (isempty(info[i]) || !xattrs[i])
187                         continue;
188
189                 k = fsetxattr(fd, xattrs[i], info[i], strlen(info[i]), XATTR_CREATE);
190                 if (k < 0 && r == 0)
191                         r = -errno;
192         }
193
194         return r;
195 }
196
197 #define filename_escape(s) xescape((s), "./ ")
198
199 static int fix_permissions(int fd, const char *filename, const char *target,
200                            const char *info[_INFO_LEN], uid_t uid) {
201
202         /* Ignore errors on these */
203         fchmod(fd, 0640);
204         fix_acl(fd, uid);
205         fix_xattr(fd, info);
206
207         if (fsync(fd) < 0) {
208                 log_error("Failed to sync coredump %s: %m", filename);
209                 return -errno;
210         }
211
212         if (rename(filename, target) < 0) {
213                 log_error("Failed to rename coredump %s -> %s: %m", filename, target);
214                 return -errno;
215         }
216
217         return 0;
218 }
219
220 static int maybe_remove_external_coredump(const char *filename, off_t size) {
221
222         /* Returns 1 if might remove, 0 if will not remove, <0 on error. */
223
224         if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
225             size <= arg_external_size_max)
226                 return 0;
227
228         if (!filename)
229                 return 1;
230
231         if (unlink(filename) < 0) {
232                 log_error("Failed to unlink %s: %m", filename);
233                 return -errno;
234         }
235
236         return 1;
237 }
238
239
240 static int save_external_coredump(const char *info[_INFO_LEN],
241                                   uid_t uid,
242                                   char **ret_filename,
243                                   int *ret_fd,
244                                   off_t *ret_size) {
245
246         _cleanup_free_ char *p = NULL, *t = NULL, *c = NULL, *fn = NULL, *tmp = NULL;
247         _cleanup_close_ int fd = -1;
248         sd_id128_t boot;
249         struct stat st;
250         int r;
251
252         assert(info);
253         assert(ret_filename);
254         assert(ret_fd);
255         assert(ret_size);
256
257         c = filename_escape(info[INFO_COMM]);
258         if (!c)
259                 return log_oom();
260
261         p = filename_escape(info[INFO_PID]);
262         if (!p)
263                 return log_oom();
264
265         t = filename_escape(info[INFO_TIMESTAMP]);
266         if (!t)
267                 return log_oom();
268
269         r = sd_id128_get_boot(&boot);
270         if (r < 0) {
271                 log_error("Failed to determine boot ID: %s", strerror(-r));
272                 return r;
273         }
274
275         r = asprintf(&fn,
276                      "/var/lib/systemd/coredump/core.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
277                      c,
278                      SD_ID128_FORMAT_VAL(boot),
279                      p,
280                      t);
281         if (r < 0)
282                 return log_oom();
283
284         tmp = tempfn_random(fn);
285         if (!tmp)
286                 return log_oom();
287
288         mkdir_p_label("/var/lib/systemd/coredump", 0755);
289
290         fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
291         if (fd < 0) {
292                 log_error("Failed to create coredump file %s: %m", tmp);
293                 return -errno;
294         }
295
296         r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max);
297         if (r == -E2BIG) {
298                 log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.",
299                           info[INFO_PID], info[INFO_COMM]);
300                 goto fail;
301         } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
302                 log_error("Not enough disk space for coredump of %s (%s), refusing.",
303                           info[INFO_PID], info[INFO_COMM]);
304                 goto fail;
305         } else if (r < 0) {
306                 log_error("Failed to dump coredump to file: %s", strerror(-r));
307                 goto fail;
308         }
309
310         if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
311                 log_error("Failed to seek on %s: %m", tmp);
312                 goto uncompressed;
313         }
314
315         if (fstat(fd, &st) < 0) {
316                 log_error("Failed to fstat coredump %s: %m", tmp);
317                 goto fail;
318         }
319
320 #ifdef HAVE_XZ
321         /* If we will remove the coredump anyway, do not compress. */
322         if (maybe_remove_external_coredump(NULL, st.st_size) == 0) {
323
324                 _cleanup_free_ char *fn2 = NULL;
325                 char *tmp2;
326                 _cleanup_close_ int fd2 = -1;
327
328                 tmp2 = strappenda(tmp, ".xz");
329                 fd2 = open(tmp2, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
330                 if (fd2 < 0) {
331                         log_error("Failed to create file %s: %m", tmp2);
332                         goto uncompressed;
333                 }
334
335                 r = compress_stream(fd, fd2, -1);
336                 if (r < 0) {
337                         log_error("Failed to compress %s: %s", tmp2, strerror(-r));
338                         unlink_noerrno(tmp2);
339                         goto fail2;
340                 }
341
342                 fn2 = strappend(fn, ".xz");
343                 if (!fn2) {
344                         log_oom();
345                         goto fail2;
346                 }
347
348                 r = fix_permissions(fd2, tmp2, fn2, info, uid);
349                 if (r < 0)
350                         goto fail2;
351
352                 *ret_filename = fn2;    /* compressed */
353                 *ret_fd = fd;           /* uncompressed */
354                 *ret_size = st.st_size; /* uncompressed */
355
356                 fn2 = NULL;
357                 fd = -1;
358
359                 return 0;
360
361         fail2:
362                 unlink_noerrno(tmp2);
363         }
364 #endif
365
366 uncompressed:
367         r = fix_permissions(fd, tmp, fn, info, uid);
368         if (r < 0)
369                 goto fail;
370
371         *ret_filename = fn;
372         *ret_fd = fd;
373         *ret_size = st.st_size;
374
375         fn = NULL;
376         fd = -1;
377
378         return 0;
379
380 fail:
381         unlink_noerrno(tmp);
382         return r;
383 }
384
385 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
386         _cleanup_free_ char *field = NULL;
387         ssize_t n;
388
389         assert(fd >= 0);
390         assert(ret);
391         assert(ret_size);
392
393         if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
394                 log_warning("Failed to seek: %m");
395                 return -errno;
396         }
397
398         field = malloc(9 + size);
399         if (!field) {
400                 log_warning("Failed to allocate memory for coredump, coredump will not be stored.");
401                 return -ENOMEM;
402         }
403
404         memcpy(field, "COREDUMP=", 9);
405
406         n = read(fd, field + 9, size);
407         if (n < 0) {
408                 log_error("Failed to read core data: %s", strerror(-n));
409                 return (int) n;
410         }
411         if ((size_t) n < size) {
412                 log_error("Core data too short.");
413                 return -EIO;
414         }
415
416         *ret = field;
417         *ret_size = size + 9;
418
419         field = NULL;
420
421         return 0;
422 }
423
424 int main(int argc, char* argv[]) {
425
426         _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
427                 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
428                 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
429                 *coredump_filename = NULL, *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
430                 *exe = NULL, *comm = NULL;
431         const char *info[_INFO_LEN];
432
433         _cleanup_close_ int coredump_fd = -1;
434
435         struct iovec iovec[17];
436         off_t coredump_size;
437         int r, j = 0;
438         uid_t uid, owner_uid;
439         gid_t gid;
440         pid_t pid;
441         char *t;
442
443         /* Make sure we never enter a loop */
444         prctl(PR_SET_DUMPABLE, 0);
445
446         /* First, log to a safe place, since we don't know what
447          * crashed and it might be journald which we'd rather not log
448          * to then. */
449         log_set_target(LOG_TARGET_KMSG);
450         log_open();
451
452         if (argc < INFO_COMM + 1) {
453                 log_error("Not enough arguments passed from kernel (%d, expected %d).",
454                           argc - 1, INFO_COMM + 1 - 1);
455                 r = -EINVAL;
456                 goto finish;
457         }
458
459         /* Ignore all parse errors */
460         parse_config();
461         log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
462
463         r = parse_uid(argv[INFO_UID + 1], &uid);
464         if (r < 0) {
465                 log_error("Failed to parse UID.");
466                 goto finish;
467         }
468
469         r = parse_pid(argv[INFO_PID + 1], &pid);
470         if (r < 0) {
471                 log_error("Failed to parse PID.");
472                 goto finish;
473         }
474
475         r = parse_gid(argv[INFO_GID + 1], &gid);
476         if (r < 0) {
477                 log_error("Failed to parse GID.");
478                 goto finish;
479         }
480
481         if (get_process_comm(pid, &comm) < 0) {
482                 log_warning("Failed to get COMM, falling back to the commandline.");
483                 comm = strv_join(argv + INFO_COMM + 1, " ");
484         }
485
486         if (get_process_exe(pid, &exe) < 0)
487                 log_warning("Failed to get EXE.");
488
489         info[INFO_PID] = argv[INFO_PID + 1];
490         info[INFO_UID] = argv[INFO_UID + 1];
491         info[INFO_GID] = argv[INFO_GID + 1];
492         info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1];
493         info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1];
494         info[INFO_COMM] = comm;
495         info[INFO_EXE] = exe;
496
497         if (cg_pid_get_unit(pid, &t) >= 0) {
498
499                 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
500
501                         /* If we are journald, we cut things short,
502                          * don't write to the journal, but still
503                          * create a coredump. */
504
505                         if (arg_storage != COREDUMP_STORAGE_NONE)
506                                 arg_storage = COREDUMP_STORAGE_EXTERNAL;
507
508                         r = save_external_coredump(info, uid, &coredump_filename, &coredump_fd, &coredump_size);
509                         if (r < 0)
510                                 goto finish;
511
512                         r = maybe_remove_external_coredump(coredump_filename, coredump_size);
513                         if (r < 0)
514                                 goto finish;
515
516                         log_info("Detected coredump of the journal daemon itself, diverted to %s.", coredump_filename);
517                         goto finish;
518                 }
519
520                 core_unit = strappend("COREDUMP_UNIT=", t);
521         } else if (cg_pid_get_user_unit(pid, &t) >= 0)
522                 core_unit = strappend("COREDUMP_USER_UNIT=", t);
523
524         if (core_unit)
525                 IOVEC_SET_STRING(iovec[j++], core_unit);
526
527         /* OK, now we know it's not the journal, hence we can make use
528          * of it now. */
529         log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
530         log_open();
531
532         core_pid = strappend("COREDUMP_PID=", info[INFO_PID]);
533         if (core_pid)
534                 IOVEC_SET_STRING(iovec[j++], core_pid);
535
536         core_uid = strappend("COREDUMP_UID=", info[INFO_UID]);
537         if (core_uid)
538                 IOVEC_SET_STRING(iovec[j++], core_uid);
539
540         core_gid = strappend("COREDUMP_GID=", info[INFO_GID]);
541         if (core_gid)
542                 IOVEC_SET_STRING(iovec[j++], core_gid);
543
544         core_signal = strappend("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
545         if (core_signal)
546                 IOVEC_SET_STRING(iovec[j++], core_signal);
547
548         if (sd_pid_get_session(pid, &t) >= 0) {
549                 core_session = strappend("COREDUMP_SESSION=", t);
550                 free(t);
551
552                 if (core_session)
553                         IOVEC_SET_STRING(iovec[j++], core_session);
554         }
555
556         if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
557                 asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
558
559                 if (core_owner_uid)
560                         IOVEC_SET_STRING(iovec[j++], core_owner_uid);
561         }
562
563         if (sd_pid_get_slice(pid, &t) >= 0) {
564                 core_slice = strappend("COREDUMP_SLICE=", t);
565                 free(t);
566
567                 if (core_slice)
568                         IOVEC_SET_STRING(iovec[j++], core_slice);
569         }
570
571         if (comm) {
572                 core_comm = strappend("COREDUMP_COMM=", comm);
573                 if (core_comm)
574                         IOVEC_SET_STRING(iovec[j++], core_comm);
575         }
576
577         if (exe) {
578                 core_exe = strappend("COREDUMP_EXE=", exe);
579                 if (core_exe)
580                         IOVEC_SET_STRING(iovec[j++], core_exe);
581         }
582
583         if (get_process_cmdline(pid, 0, false, &t) >= 0) {
584                 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
585                 free(t);
586
587                 if (core_cmdline)
588                         IOVEC_SET_STRING(iovec[j++], core_cmdline);
589         }
590
591         if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
592                 core_cgroup = strappend("COREDUMP_CGROUP=", t);
593                 free(t);
594
595                 if (core_cgroup)
596                         IOVEC_SET_STRING(iovec[j++], core_cgroup);
597         }
598
599         core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
600         if (core_timestamp)
601                 IOVEC_SET_STRING(iovec[j++], core_timestamp);
602
603         IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
604         IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
605
606         /* Always stream the coredump to disk, if that's possible */
607         r = save_external_coredump(info, uid, &coredump_filename, &coredump_fd, &coredump_size);
608         if (r < 0)
609                 /* skip whole core dumping part */
610                 goto log;
611
612         /* If we don't want to keep the coredump on disk, remove it
613          * now, as later on we will lack the privileges for
614          * it. However, we keep the fd to it, so that we can still
615          * process it and log it. */
616         r = maybe_remove_external_coredump(coredump_filename, coredump_size);
617         if (r < 0)
618                 goto finish;
619
620         /* Now, let's drop privileges to become the user who owns the
621          * segfaulted process and allocate the coredump memory under
622          * his uid. This also ensures that the credentials journald
623          * will see are the ones of the coredumping user, thus making
624          * sure the user himself gets access to the core dump. */
625         if (setresgid(gid, gid, gid) < 0 ||
626             setresuid(uid, uid, uid) < 0) {
627                 log_error("Failed to drop privileges: %m");
628                 r = -errno;
629                 goto finish;
630         }
631
632 #ifdef HAVE_ELFUTILS
633         /* Try to get a strack trace if we can */
634         if (coredump_size <= arg_process_size_max) {
635                 _cleanup_free_ char *stacktrace = NULL;
636
637                 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
638                 if (r >= 0)
639                         core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
640                 else
641                         log_warning("Failed to generate stack trace: %s", strerror(-r));
642         }
643
644         if (!core_message)
645 #endif
646 log:
647         core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
648         if (core_message)
649                 IOVEC_SET_STRING(iovec[j++], core_message);
650
651         /* Optionally store the entire coredump in the journal */
652         if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
653             coredump_size <= (off_t) arg_journal_size_max) {
654                 size_t sz;
655
656                 /* Store the coredump itself in the journal */
657
658                 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
659                 if (r >= 0) {
660                         iovec[j].iov_base = coredump_data;
661                         iovec[j].iov_len = sz;
662                         j++;
663                 }
664         }
665
666         r = sd_journal_sendv(iovec, j);
667         if (r < 0)
668                 log_error("Failed to log coredump: %s", strerror(-r));
669
670 finish:
671         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
672 }