chiark / gitweb /
coredump: also escape spaces in comm fields when formatting file names
[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 "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
163         static const char * const xattrs[_ARG_MAX] = {
164                 [ARG_PID] = "user.coredump.pid",
165                 [ARG_UID] = "user.coredump.uid",
166                 [ARG_GID] = "user.coredump.gid",
167                 [ARG_SIGNAL] = "user.coredump.signal",
168                 [ARG_TIMESTAMP] = "user.coredump.timestamp",
169                 [ARG_COMM] = "user.coredump.comm",
170         };
171
172         int r = 0;
173         unsigned i;
174
175         /* Attach some metadate to coredumps via extended
176          * attributes. Just because we can. */
177
178         for (i = 0; i < _ARG_MAX; i++) {
179                 if (isempty(argv[i]))
180                         continue;
181
182                 if (fsetxattr(fd, xattrs[i], argv[i], strlen(argv[i]), XATTR_CREATE) < 0)
183                         r = -errno;
184         }
185
186         return r;
187 }
188
189 #define filename_escape(s) xescape((s), "./ ")
190
191 static int save_external_coredump(char **argv, uid_t uid, char **ret_filename, int *ret_fd, off_t *ret_size) {
192         _cleanup_free_ char *p = NULL, *t = NULL, *c = NULL, *fn = NULL, *tmp = NULL;
193         _cleanup_close_ int fd = -1;
194         sd_id128_t boot;
195         struct stat st;
196         int r;
197
198         assert(argv);
199         assert(ret_filename);
200         assert(ret_fd);
201         assert(ret_size);
202
203         c = filename_escape(argv[ARG_COMM]);
204         if (!c)
205                 return log_oom();
206
207         p = filename_escape(argv[ARG_PID]);
208         if (!p)
209                 return log_oom();
210
211         t = filename_escape(argv[ARG_TIMESTAMP]);
212         if (!t)
213                 return log_oom();
214
215         r = sd_id128_get_boot(&boot);
216         if (r < 0) {
217                 log_error("Failed to determine boot ID: %s", strerror(-r));
218                 return r;
219         }
220
221         r = asprintf(&fn,
222                      "/var/lib/systemd/coredump/core.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
223                      c,
224                      SD_ID128_FORMAT_VAL(boot),
225                      p,
226                      t);
227         if (r < 0)
228                 return log_oom();
229
230         tmp = tempfn_random(fn);
231         if (!tmp)
232                 return log_oom();
233
234         mkdir_p_label("/var/lib/systemd/coredump", 0755);
235
236         fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
237         if (fd < 0) {
238                 log_error("Failed to create coredump file: %m");
239                 return -errno;
240         }
241
242         r = copy_bytes(STDIN_FILENO, fd);
243         if (r < 0) {
244                 log_error("Failed to dump coredump to file: %s", strerror(-r));
245                 goto fail;
246         }
247
248         /* Ignore errors on these */
249         fchmod(fd, 0640);
250         fix_acl(fd, uid);
251         fix_xattr(fd, argv);
252
253         if (fsync(fd) < 0) {
254                 log_error("Failed to sync coredump: %m");
255                 r = -errno;
256                 goto fail;
257         }
258
259         if (fstat(fd, &st) < 0) {
260                 log_error("Failed to fstat coredump: %m");
261                 r = -errno;
262                 goto fail;
263         }
264
265         if (rename(tmp, fn) < 0) {
266                 log_error("Failed to rename coredump: %m");
267                 r = -errno;
268                 goto fail;
269         }
270
271         *ret_filename = fn;
272         *ret_fd = fd;
273         *ret_size = st.st_size;
274
275         fn = NULL;
276         fd = -1;
277
278         return 0;
279
280 fail:
281         unlink_noerrno(tmp);
282         return r;
283 }
284
285 static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
286         _cleanup_free_ char *field = NULL;
287         ssize_t n;
288
289         assert(fd >= 0);
290         assert(ret);
291         assert(ret_size);
292
293         if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
294                 log_warning("Failed to seek: %m");
295                 return -errno;
296         }
297
298         field = malloc(9 + size);
299         if (!field) {
300                 log_warning("Failed to allocate memory fore coredump, coredump will not be stored.");
301                 return -ENOMEM;
302         }
303
304         memcpy(field, "COREDUMP=", 9);
305
306         n = read(fd, field + 9, size);
307         if (n < 0) {
308                 log_error("Failed to read core data: %s", strerror(-n));
309                 return (int) n;
310         }
311         if ((size_t) n < size) {
312                 log_error("Core data too short.");
313                 return -EIO;
314         }
315
316         *ret = field;
317         *ret_size = size + 9;
318
319         field = NULL;
320
321         return 0;
322 }
323
324 static int maybe_remove_external_coredump(const char *filename, off_t size) {
325
326         if (!filename)
327                 return 0;
328
329         if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) &&
330             size <= arg_external_size_max)
331                 return 0;
332
333         if (unlink(filename) < 0) {
334                 log_error("Failed to unlink %s: %m", filename);
335                 return -errno;
336         }
337
338         return 0;
339 }
340
341 int main(int argc, char* argv[]) {
342
343         _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
344                 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
345                 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
346                 *coredump_filename = NULL, *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
347                 *exe = NULL;
348
349         _cleanup_close_ int coredump_fd = -1;
350
351         struct iovec iovec[17];
352         off_t coredump_size;
353         int r, j = 0;
354         uid_t uid, owner_uid;
355         gid_t gid;
356         pid_t pid;
357         char *t;
358
359         /* Make sure we never enter a loop */
360         prctl(PR_SET_DUMPABLE, 0);
361
362         /* First, log to a safe place, since we don't know what
363          * crashed and it might be journald which we'd rather not log
364          * to then. */
365         log_set_target(LOG_TARGET_KMSG);
366         log_open();
367
368         if (argc != _ARG_MAX) {
369                 log_error("Invalid number of arguments passed from kernel.");
370                 r = -EINVAL;
371                 goto finish;
372         }
373
374         /* Ignore all parse errors */
375         parse_config();
376         log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
377
378         r = parse_uid(argv[ARG_UID], &uid);
379         if (r < 0) {
380                 log_error("Failed to parse UID.");
381                 goto finish;
382         }
383
384         r = parse_pid(argv[ARG_PID], &pid);
385         if (r < 0) {
386                 log_error("Failed to parse PID.");
387                 goto finish;
388         }
389
390         r = parse_gid(argv[ARG_GID], &gid);
391         if (r < 0) {
392                 log_error("Failed to parse GID.");
393                 goto finish;
394         }
395
396         if (cg_pid_get_unit(pid, &t) >= 0) {
397
398                 if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
399
400                         /* If we are journald, we cut things short,
401                          * don't write to the journal, but still
402                          * create a coredump. */
403
404                         if (arg_storage != COREDUMP_STORAGE_NONE)
405                                 arg_storage = COREDUMP_STORAGE_EXTERNAL;
406
407                         r = save_external_coredump(argv, uid, &coredump_filename, &coredump_fd, &coredump_size);
408                         if (r < 0)
409                                 goto finish;
410
411                         r = maybe_remove_external_coredump(coredump_filename, coredump_size);
412                         if (r < 0)
413                                 goto finish;
414
415                         log_info("Detected coredump of the journal daemon itself, diverted to %s.", coredump_filename);
416                         goto finish;
417                 }
418
419                 core_unit = strappend("COREDUMP_UNIT=", t);
420         } else if (cg_pid_get_user_unit(pid, &t) >= 0)
421                 core_unit = strappend("COREDUMP_USER_UNIT=", t);
422
423         if (core_unit)
424                 IOVEC_SET_STRING(iovec[j++], core_unit);
425
426         /* OK, now we know it's not the journal, hence we can make use
427          * of it now. */
428         log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
429         log_open();
430
431         core_pid = strappend("COREDUMP_PID=", argv[ARG_PID]);
432         if (core_pid)
433                 IOVEC_SET_STRING(iovec[j++], core_pid);
434
435         core_uid = strappend("COREDUMP_UID=", argv[ARG_UID]);
436         if (core_uid)
437                 IOVEC_SET_STRING(iovec[j++], core_uid);
438
439         core_gid = strappend("COREDUMP_GID=", argv[ARG_GID]);
440         if (core_gid)
441                 IOVEC_SET_STRING(iovec[j++], core_gid);
442
443         core_signal = strappend("COREDUMP_SIGNAL=", argv[ARG_SIGNAL]);
444         if (core_signal)
445                 IOVEC_SET_STRING(iovec[j++], core_signal);
446
447         core_comm = strappend("COREDUMP_COMM=", argv[ARG_COMM]);
448         if (core_comm)
449                 IOVEC_SET_STRING(iovec[j++], core_comm);
450
451         if (sd_pid_get_session(pid, &t) >= 0) {
452                 core_session = strappend("COREDUMP_SESSION=", t);
453                 free(t);
454
455                 if (core_session)
456                         IOVEC_SET_STRING(iovec[j++], core_session);
457         }
458
459         if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
460                 asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
461
462                 if (core_owner_uid)
463                         IOVEC_SET_STRING(iovec[j++], core_owner_uid);
464         }
465
466         if (sd_pid_get_slice(pid, &t) >= 0) {
467                 core_slice = strappend("COREDUMP_SLICE=", t);
468                 free(t);
469
470                 if (core_slice)
471                         IOVEC_SET_STRING(iovec[j++], core_slice);
472         }
473
474         if (get_process_exe(pid, &exe) >= 0) {
475                 core_exe = strappend("COREDUMP_EXE=", exe);
476                 if (core_exe)
477                         IOVEC_SET_STRING(iovec[j++], core_exe);
478         }
479
480         if (get_process_cmdline(pid, 0, false, &t) >= 0) {
481                 core_cmdline = strappend("COREDUMP_CMDLINE=", t);
482                 free(t);
483
484                 if (core_cmdline)
485                         IOVEC_SET_STRING(iovec[j++], core_cmdline);
486         }
487
488         if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
489                 core_cgroup = strappend("COREDUMP_CGROUP=", t);
490                 free(t);
491
492                 if (core_cgroup)
493                         IOVEC_SET_STRING(iovec[j++], core_cgroup);
494         }
495
496         core_timestamp = strjoin("COREDUMP_TIMESTAMP=", argv[ARG_TIMESTAMP], "000000", NULL);
497         if (core_timestamp)
498                 IOVEC_SET_STRING(iovec[j++], core_timestamp);
499
500         IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
501         IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
502
503         /* Always stream the coredump to disk, if that's possible */
504         r = save_external_coredump(argv, uid, &coredump_filename, &coredump_fd, &coredump_size);
505         if (r < 0)
506                 goto finish;
507
508         /* If we don't want to keep the coredump on disk, remove it
509          * now, as later on we will lack the privileges for
510          * it. However, we keep the fd to it, so that we can still
511          * process it and log it. */
512         r = maybe_remove_external_coredump(coredump_filename, coredump_size);
513         if (r < 0)
514                 goto finish;
515
516         /* Now, let's drop privileges to become the user who owns the
517          * segfaulted process and allocate the coredump memory under
518          * his uid. This also ensures that the credentials journald
519          * will see are the ones of the coredumping user, thus making
520          * sure the user himself gets access to the core dump. */
521         if (setresgid(gid, gid, gid) < 0 ||
522             setresuid(uid, uid, uid) < 0) {
523                 log_error("Failed to drop privileges: %m");
524                 r = -errno;
525                 goto finish;
526         }
527
528 #ifdef HAVE_ELFUTILS
529         /* Try to get a strack trace if we can */
530         if (coredump_size <= arg_process_size_max) {
531                 _cleanup_free_ char *stacktrace = NULL;
532
533                 r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
534                 if (r >= 0)
535                         core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") of user ", argv[ARG_UID], " dumped core.\n\n", stacktrace, NULL);
536                 else
537                         log_warning("Failed to generate stack trace: %s", strerror(-r));
538         }
539
540         if (!core_message)
541 #endif
542         core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") of user ", argv[ARG_UID], " dumped core.", NULL);
543         if (core_message)
544                 IOVEC_SET_STRING(iovec[j++], core_message);
545
546         /* Optionally store the entire coredump in the journal */
547         if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
548             coredump_size <= (off_t) arg_journal_size_max) {
549                 size_t sz;
550
551                 /* Store the coredump itself in the journal */
552
553                 r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
554                 if (r >= 0) {
555                         iovec[j].iov_base = coredump_data;
556                         iovec[j].iov_len = sz;
557                         j++;
558                 }
559         }
560
561         r = sd_journal_sendv(iovec, j);
562         if (r < 0)
563                 log_error("Failed to log coredump: %s", strerror(-r));
564
565 finish:
566         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
567 }