]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journald-syslog.c
security: rework selinux, smack, ima, apparmor detection logic
[thirdparty/systemd.git] / src / journal / journald-syslog.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2011 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 <unistd.h>
23 #include <stddef.h>
24 #include <sys/epoll.h>
25
26 #include "systemd/sd-messages.h"
27 #include "socket-util.h"
28 #include "selinux-util.h"
29 #include "journald-server.h"
30 #include "journald-syslog.h"
31 #include "journald-kmsg.h"
32 #include "journald-console.h"
33
34 /* Warn once every 30s if we missed syslog message */
35 #define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
36
37 static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, struct ucred *ucred, struct timeval *tv) {
38
39 union sockaddr_union sa = {
40 .un.sun_family = AF_UNIX,
41 .un.sun_path = "/run/systemd/journal/syslog",
42 };
43 struct msghdr msghdr = {
44 .msg_iov = (struct iovec *) iovec,
45 .msg_iovlen = n_iovec,
46 .msg_name = &sa,
47 .msg_namelen = offsetof(union sockaddr_union, un.sun_path)
48 + sizeof("/run/systemd/journal/syslog") - 1,
49 };
50 struct cmsghdr *cmsg;
51 union {
52 struct cmsghdr cmsghdr;
53 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
54 } control;
55
56 assert(s);
57 assert(iovec);
58 assert(n_iovec > 0);
59
60 if (ucred) {
61 zero(control);
62 msghdr.msg_control = &control;
63 msghdr.msg_controllen = sizeof(control);
64
65 cmsg = CMSG_FIRSTHDR(&msghdr);
66 cmsg->cmsg_level = SOL_SOCKET;
67 cmsg->cmsg_type = SCM_CREDENTIALS;
68 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
69 memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred));
70 msghdr.msg_controllen = cmsg->cmsg_len;
71 }
72
73 /* Forward the syslog message we received via /dev/log to
74 * /run/systemd/syslog. Unfortunately we currently can't set
75 * the SO_TIMESTAMP auxiliary data, and hence we don't. */
76
77 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
78 return;
79
80 /* The socket is full? I guess the syslog implementation is
81 * too slow, and we shouldn't wait for that... */
82 if (errno == EAGAIN) {
83 s->n_forward_syslog_missed++;
84 return;
85 }
86
87 if (ucred && errno == ESRCH) {
88 struct ucred u;
89
90 /* Hmm, presumably the sender process vanished
91 * by now, so let's fix it as good as we
92 * can, and retry */
93
94 u = *ucred;
95 u.pid = getpid();
96 memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred));
97
98 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
99 return;
100
101 if (errno == EAGAIN) {
102 s->n_forward_syslog_missed++;
103 return;
104 }
105 }
106
107 if (errno != ENOENT)
108 log_debug("Failed to forward syslog message: %m");
109 }
110
111 static void forward_syslog_raw(Server *s, int priority, const char *buffer, struct ucred *ucred, struct timeval *tv) {
112 struct iovec iovec;
113
114 assert(s);
115 assert(buffer);
116
117 if (LOG_PRI(priority) > s->max_level_syslog)
118 return;
119
120 IOVEC_SET_STRING(iovec, buffer);
121 forward_syslog_iovec(s, &iovec, 1, ucred, tv);
122 }
123
124 void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, struct ucred *ucred, struct timeval *tv) {
125 struct iovec iovec[5];
126 char header_priority[6], header_time[64], header_pid[16];
127 int n = 0;
128 time_t t;
129 struct tm *tm;
130 char *ident_buf = NULL;
131
132 assert(s);
133 assert(priority >= 0);
134 assert(priority <= 999);
135 assert(message);
136
137 if (LOG_PRI(priority) > s->max_level_syslog)
138 return;
139
140 /* First: priority field */
141 snprintf(header_priority, sizeof(header_priority), "<%i>", priority);
142 char_array_0(header_priority);
143 IOVEC_SET_STRING(iovec[n++], header_priority);
144
145 /* Second: timestamp */
146 t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC));
147 tm = localtime(&t);
148 if (!tm)
149 return;
150 if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
151 return;
152 IOVEC_SET_STRING(iovec[n++], header_time);
153
154 /* Third: identifier and PID */
155 if (ucred) {
156 if (!identifier) {
157 get_process_comm(ucred->pid, &ident_buf);
158 identifier = ident_buf;
159 }
160
161 snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) ucred->pid);
162 char_array_0(header_pid);
163
164 if (identifier)
165 IOVEC_SET_STRING(iovec[n++], identifier);
166
167 IOVEC_SET_STRING(iovec[n++], header_pid);
168 } else if (identifier) {
169 IOVEC_SET_STRING(iovec[n++], identifier);
170 IOVEC_SET_STRING(iovec[n++], ": ");
171 }
172
173 /* Fourth: message */
174 IOVEC_SET_STRING(iovec[n++], message);
175
176 forward_syslog_iovec(s, iovec, n, ucred, tv);
177
178 free(ident_buf);
179 }
180
181 int syslog_fixup_facility(int priority) {
182
183 if ((priority & LOG_FACMASK) == 0)
184 return (priority & LOG_PRIMASK) | LOG_USER;
185
186 return priority;
187 }
188
189 size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) {
190 const char *p;
191 char *t;
192 size_t l, e;
193
194 assert(buf);
195 assert(identifier);
196 assert(pid);
197
198 p = *buf;
199
200 p += strspn(p, WHITESPACE);
201 l = strcspn(p, WHITESPACE);
202
203 if (l <= 0 ||
204 p[l-1] != ':')
205 return 0;
206
207 e = l;
208 l--;
209
210 if (p[l-1] == ']') {
211 size_t k = l-1;
212
213 for (;;) {
214
215 if (p[k] == '[') {
216 t = strndup(p+k+1, l-k-2);
217 if (t)
218 *pid = t;
219
220 l = k;
221 break;
222 }
223
224 if (k == 0)
225 break;
226
227 k--;
228 }
229 }
230
231 t = strndup(p, l);
232 if (t)
233 *identifier = t;
234
235 e += strspn(p + e, WHITESPACE);
236 *buf = p + e;
237 return e;
238 }
239
240 void syslog_parse_priority(char **p, int *priority, bool with_facility) {
241 int a = 0, b = 0, c = 0;
242 int k;
243
244 assert(p);
245 assert(*p);
246 assert(priority);
247
248 if ((*p)[0] != '<')
249 return;
250
251 if (!strchr(*p, '>'))
252 return;
253
254 if ((*p)[2] == '>') {
255 c = undecchar((*p)[1]);
256 k = 3;
257 } else if ((*p)[3] == '>') {
258 b = undecchar((*p)[1]);
259 c = undecchar((*p)[2]);
260 k = 4;
261 } else if ((*p)[4] == '>') {
262 a = undecchar((*p)[1]);
263 b = undecchar((*p)[2]);
264 c = undecchar((*p)[3]);
265 k = 5;
266 } else
267 return;
268
269 if (a < 0 || b < 0 || c < 0 ||
270 (!with_facility && (a || b || c > 7)))
271 return;
272
273 if (with_facility)
274 *priority = a*100 + b*10 + c;
275 else
276 *priority = (*priority & LOG_FACMASK) | c;
277 *p += k;
278 }
279
280 static void syslog_skip_date(char **buf) {
281 enum {
282 LETTER,
283 SPACE,
284 NUMBER,
285 SPACE_OR_NUMBER,
286 COLON
287 } sequence[] = {
288 LETTER, LETTER, LETTER,
289 SPACE,
290 SPACE_OR_NUMBER, NUMBER,
291 SPACE,
292 SPACE_OR_NUMBER, NUMBER,
293 COLON,
294 SPACE_OR_NUMBER, NUMBER,
295 COLON,
296 SPACE_OR_NUMBER, NUMBER,
297 SPACE
298 };
299
300 char *p;
301 unsigned i;
302
303 assert(buf);
304 assert(*buf);
305
306 p = *buf;
307
308 for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
309
310 if (!*p)
311 return;
312
313 switch (sequence[i]) {
314
315 case SPACE:
316 if (*p != ' ')
317 return;
318 break;
319
320 case SPACE_OR_NUMBER:
321 if (*p == ' ')
322 break;
323
324 /* fall through */
325
326 case NUMBER:
327 if (*p < '0' || *p > '9')
328 return;
329
330 break;
331
332 case LETTER:
333 if (!(*p >= 'A' && *p <= 'Z') &&
334 !(*p >= 'a' && *p <= 'z'))
335 return;
336
337 break;
338
339 case COLON:
340 if (*p != ':')
341 return;
342 break;
343
344 }
345 }
346
347 *buf = p;
348 }
349
350 void server_process_syslog_message(
351 Server *s,
352 const char *buf,
353 struct ucred *ucred,
354 struct timeval *tv,
355 const char *label,
356 size_t label_len) {
357
358 char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
359 struct iovec iovec[N_IOVEC_META_FIELDS + 6];
360 unsigned n = 0;
361 int priority = LOG_USER | LOG_INFO;
362 char *identifier = NULL, *pid = NULL;
363 const char *orig;
364
365 assert(s);
366 assert(buf);
367
368 orig = buf;
369 syslog_parse_priority((char**) &buf, &priority, true);
370
371 if (s->forward_to_syslog)
372 forward_syslog_raw(s, priority, orig, ucred, tv);
373
374 syslog_skip_date((char**) &buf);
375 syslog_parse_identifier(&buf, &identifier, &pid);
376
377 if (s->forward_to_kmsg)
378 server_forward_kmsg(s, priority, identifier, buf, ucred);
379
380 if (s->forward_to_console)
381 server_forward_console(s, priority, identifier, buf, ucred);
382
383 IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
384
385 if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
386 IOVEC_SET_STRING(iovec[n++], syslog_priority);
387
388 if (priority & LOG_FACMASK)
389 if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
390 IOVEC_SET_STRING(iovec[n++], syslog_facility);
391
392 if (identifier) {
393 syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier);
394 if (syslog_identifier)
395 IOVEC_SET_STRING(iovec[n++], syslog_identifier);
396 }
397
398 if (pid) {
399 syslog_pid = strappend("SYSLOG_PID=", pid);
400 if (syslog_pid)
401 IOVEC_SET_STRING(iovec[n++], syslog_pid);
402 }
403
404 message = strappend("MESSAGE=", buf);
405 if (message)
406 IOVEC_SET_STRING(iovec[n++], message);
407
408 server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0);
409
410 free(message);
411 free(identifier);
412 free(pid);
413 free(syslog_priority);
414 free(syslog_facility);
415 free(syslog_identifier);
416 free(syslog_pid);
417 }
418
419 int server_open_syslog_socket(Server *s) {
420 int one, r;
421 struct epoll_event ev;
422
423 assert(s);
424
425 if (s->syslog_fd < 0) {
426 union sockaddr_union sa = {
427 .un.sun_family = AF_UNIX,
428 .un.sun_path = "/dev/log",
429 };
430
431 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
432 if (s->syslog_fd < 0) {
433 log_error("socket() failed: %m");
434 return -errno;
435 }
436
437 unlink(sa.un.sun_path);
438
439 r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
440 if (r < 0) {
441 log_error("bind() failed: %m");
442 return -errno;
443 }
444
445 chmod(sa.un.sun_path, 0666);
446 } else
447 fd_nonblock(s->syslog_fd, 1);
448
449 one = 1;
450 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
451 if (r < 0) {
452 log_error("SO_PASSCRED failed: %m");
453 return -errno;
454 }
455
456 #ifdef HAVE_SELINUX
457 if (use_selinux()) {
458 one = 1;
459 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
460 if (r < 0)
461 log_warning("SO_PASSSEC failed: %m");
462 }
463 #endif
464
465 one = 1;
466 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
467 if (r < 0) {
468 log_error("SO_TIMESTAMP failed: %m");
469 return -errno;
470 }
471
472 zero(ev);
473 ev.events = EPOLLIN;
474 ev.data.fd = s->syslog_fd;
475 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->syslog_fd, &ev) < 0) {
476 log_error("Failed to add syslog server fd to epoll object: %m");
477 return -errno;
478 }
479
480 return 0;
481 }
482
483 void server_maybe_warn_forward_syslog_missed(Server *s) {
484 usec_t n;
485 assert(s);
486
487 if (s->n_forward_syslog_missed <= 0)
488 return;
489
490 n = now(CLOCK_MONOTONIC);
491 if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
492 return;
493
494 server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, "Forwarding to syslog missed %u messages.", s->n_forward_syslog_missed);
495
496 s->n_forward_syslog_missed = 0;
497 s->last_warn_forward_syslog_missed = n;
498 }