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