chiark / gitweb /
journald: add support for wall forwarding
[elogind.git] / src / journal / journald-native.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 "socket-util.h"
27 #include "path-util.h"
28 #include "selinux-util.h"
29 #include "journald-server.h"
30 #include "journald-native.h"
31 #include "journald-kmsg.h"
32 #include "journald-console.h"
33 #include "journald-syslog.h"
34 #include "journald-wall.h"
35
36 /* Make sure not to make this smaller than the maximum coredump
37  * size. See COREDUMP_MAX in coredump.c */
38 #define ENTRY_SIZE_MAX (1024*1024*768)
39 #define DATA_SIZE_MAX (1024*1024*768)
40
41 static bool valid_user_field(const char *p, size_t l) {
42         const char *a;
43
44         /* We kinda enforce POSIX syntax recommendations for
45            environment variables here, but make a couple of additional
46            requirements.
47
48            http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
49
50         /* No empty field names */
51         if (l <= 0)
52                 return false;
53
54         /* Don't allow names longer than 64 chars */
55         if (l > 64)
56                 return false;
57
58         /* Variables starting with an underscore are protected */
59         if (p[0] == '_')
60                 return false;
61
62         /* Don't allow digits as first character */
63         if (p[0] >= '0' && p[0] <= '9')
64                 return false;
65
66         /* Only allow A-Z0-9 and '_' */
67         for (a = p; a < p + l; a++)
68                 if (!((*a >= 'A' && *a <= 'Z') ||
69                       (*a >= '0' && *a <= '9') ||
70                       *a == '_'))
71                         return false;
72
73         return true;
74 }
75
76 static bool allow_object_pid(struct ucred *ucred) {
77         return ucred && ucred->uid == 0;
78 }
79
80 void server_process_native_message(
81                 Server *s,
82                 const void *buffer, size_t buffer_size,
83                 struct ucred *ucred,
84                 struct timeval *tv,
85                 const char *label, size_t label_len) {
86
87         struct iovec *iovec = NULL;
88         unsigned n = 0, j, tn = (unsigned) -1;
89         const char *p;
90         size_t remaining, m = 0;
91         int priority = LOG_INFO;
92         char *identifier = NULL, *message = NULL;
93         pid_t object_pid = 0;
94
95         assert(s);
96         assert(buffer || buffer_size == 0);
97
98         p = buffer;
99         remaining = buffer_size;
100
101         while (remaining > 0) {
102                 const char *e, *q;
103
104                 e = memchr(p, '\n', remaining);
105
106                 if (!e) {
107                         /* Trailing noise, let's ignore it, and flush what we collected */
108                         log_debug("Received message with trailing noise, ignoring.");
109                         break;
110                 }
111
112                 if (e == p) {
113                         /* Entry separator */
114                         server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
115                         n = 0;
116                         priority = LOG_INFO;
117
118                         p++;
119                         remaining--;
120                         continue;
121                 }
122
123                 if (*p == '.' || *p == '#') {
124                         /* Ignore control commands for now, and
125                          * comments too. */
126                         remaining -= (e - p) + 1;
127                         p = e + 1;
128                         continue;
129                 }
130
131                 /* A property follows */
132
133                 /* n received properties, +1 for _TRANSPORT */
134                 if (!GREEDY_REALLOC(iovec, m, n + 1 + N_IOVEC_META_FIELDS +
135                                               !!object_pid * N_IOVEC_OBJECT_FIELDS)) {
136                         log_oom();
137                         break;
138                 }
139
140                 q = memchr(p, '=', e - p);
141                 if (q) {
142                         if (valid_user_field(p, q - p)) {
143                                 size_t l;
144
145                                 l = e - p;
146
147                                 /* If the field name starts with an
148                                  * underscore, skip the variable,
149                                  * since that indidates a trusted
150                                  * field */
151                                 iovec[n].iov_base = (char*) p;
152                                 iovec[n].iov_len = l;
153                                 n++;
154
155                                 /* We need to determine the priority
156                                  * of this entry for the rate limiting
157                                  * logic */
158                                 if (l == 10 &&
159                                     startswith(p, "PRIORITY=") &&
160                                     p[9] >= '0' && p[9] <= '9')
161                                         priority = (priority & LOG_FACMASK) | (p[9] - '0');
162
163                                 else if (l == 17 &&
164                                          startswith(p, "SYSLOG_FACILITY=") &&
165                                          p[16] >= '0' && p[16] <= '9')
166                                         priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
167
168                                 else if (l == 18 &&
169                                          startswith(p, "SYSLOG_FACILITY=") &&
170                                          p[16] >= '0' && p[16] <= '9' &&
171                                          p[17] >= '0' && p[17] <= '9')
172                                         priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3);
173
174                                 else if (l >= 19 &&
175                                          startswith(p, "SYSLOG_IDENTIFIER=")) {
176                                         char *t;
177
178                                         t = strndup(p + 18, l - 18);
179                                         if (t) {
180                                                 free(identifier);
181                                                 identifier = t;
182                                         }
183                                 } else if (l >= 8 &&
184                                            startswith(p, "MESSAGE=")) {
185                                         char *t;
186
187                                         t = strndup(p + 8, l - 8);
188                                         if (t) {
189                                                 free(message);
190                                                 message = t;
191                                         }
192                                 } else if (l > strlen("OBJECT_PID=") &&
193                                            l < strlen("OBJECT_PID=")  + DECIMAL_STR_MAX(pid_t) &&
194                                            startswith(p, "OBJECT_PID=") &&
195                                            allow_object_pid(ucred)) {
196                                         char buf[DECIMAL_STR_MAX(pid_t)];
197                                         memcpy(buf, p + strlen("OBJECT_PID="), l - strlen("OBJECT_PID="));
198                                         char_array_0(buf);
199
200                                         /* ignore error */
201                                         parse_pid(buf, &object_pid);
202                                 }
203                         }
204
205                         remaining -= (e - p) + 1;
206                         p = e + 1;
207                         continue;
208                 } else {
209                         le64_t l_le;
210                         uint64_t l;
211                         char *k;
212
213                         if (remaining < e - p + 1 + sizeof(uint64_t) + 1) {
214                                 log_debug("Failed to parse message, ignoring.");
215                                 break;
216                         }
217
218                         memcpy(&l_le, e + 1, sizeof(uint64_t));
219                         l = le64toh(l_le);
220
221                         if (l > DATA_SIZE_MAX) {
222                                 log_debug("Received binary data block too large, ignoring.");
223                                 break;
224                         }
225
226                         if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
227                             e[1+sizeof(uint64_t)+l] != '\n') {
228                                 log_debug("Failed to parse message, ignoring.");
229                                 break;
230                         }
231
232                         k = malloc((e - p) + 1 + l);
233                         if (!k) {
234                                 log_oom();
235                                 break;
236                         }
237
238                         memcpy(k, p, e - p);
239                         k[e - p] = '=';
240                         memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
241
242                         if (valid_user_field(p, e - p)) {
243                                 iovec[n].iov_base = k;
244                                 iovec[n].iov_len = (e - p) + 1 + l;
245                                 n++;
246                         } else
247                                 free(k);
248
249                         remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1;
250                         p = e + 1 + sizeof(uint64_t) + l + 1;
251                 }
252         }
253
254         if (n <= 0)
255                 goto finish;
256
257         tn = n++;
258         IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal");
259
260         if (message) {
261                 if (s->forward_to_syslog)
262                         server_forward_syslog(s, priority, identifier, message, ucred, tv);
263
264                 if (s->forward_to_kmsg)
265                         server_forward_kmsg(s, priority, identifier, message, ucred);
266
267                 if (s->forward_to_console)
268                         server_forward_console(s, priority, identifier, message, ucred);
269
270                 if (s->forward_to_wall)
271                         server_forward_wall(s, priority, identifier, message, ucred);
272         }
273
274         server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
275
276 finish:
277         for (j = 0; j < n; j++)  {
278                 if (j == tn)
279                         continue;
280
281                 if (iovec[j].iov_base < buffer ||
282                     (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size)
283                         free(iovec[j].iov_base);
284         }
285
286         free(iovec);
287         free(identifier);
288         free(message);
289 }
290
291 void server_process_native_file(
292                 Server *s,
293                 int fd,
294                 struct ucred *ucred,
295                 struct timeval *tv,
296                 const char *label, size_t label_len) {
297
298         struct stat st;
299         _cleanup_free_ void *p = NULL;
300         ssize_t n;
301         int r;
302
303         assert(s);
304         assert(fd >= 0);
305
306         if (!ucred || ucred->uid != 0) {
307                 _cleanup_free_ char *sl = NULL, *k = NULL;
308                 const char *e;
309
310                 if (asprintf(&sl, "/proc/self/fd/%i", fd) < 0) {
311                         log_oom();
312                         return;
313                 }
314
315                 r = readlink_malloc(sl, &k);
316                 if (r < 0) {
317                         log_error("readlink(%s) failed: %m", sl);
318                         return;
319                 }
320
321                 e = path_startswith(k, "/dev/shm/");
322                 if (!e)
323                         e = path_startswith(k, "/tmp/");
324                 if (!e)
325                         e = path_startswith(k, "/var/tmp/");
326                 if (!e) {
327                         log_error("Received file outside of allowed directories. Refusing.");
328                         return;
329                 }
330
331                 if (!filename_is_safe(e)) {
332                         log_error("Received file in subdirectory of allowed directories. Refusing.");
333                         return;
334                 }
335         }
336
337         /* Data is in the passed file, since it didn't fit in a
338          * datagram. We can't map the file here, since clients might
339          * then truncate it and trigger a SIGBUS for us. So let's
340          * stupidly read it */
341
342         if (fstat(fd, &st) < 0) {
343                 log_error("Failed to stat passed file, ignoring: %m");
344                 return;
345         }
346
347         if (!S_ISREG(st.st_mode)) {
348                 log_error("File passed is not regular. Ignoring.");
349                 return;
350         }
351
352         if (st.st_size <= 0)
353                 return;
354
355         if (st.st_size > ENTRY_SIZE_MAX) {
356                 log_error("File passed too large. Ignoring.");
357                 return;
358         }
359
360         p = malloc(st.st_size);
361         if (!p) {
362                 log_oom();
363                 return;
364         }
365
366         n = pread(fd, p, st.st_size, 0);
367         if (n < 0)
368                 log_error("Failed to read file, ignoring: %s", strerror(-n));
369         else if (n > 0)
370                 server_process_native_message(s, p, n, ucred, tv, label, label_len);
371 }
372
373 int server_open_native_socket(Server*s) {
374         union sockaddr_union sa;
375         int one, r;
376
377         assert(s);
378
379         if (s->native_fd < 0) {
380
381                 s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
382                 if (s->native_fd < 0) {
383                         log_error("socket() failed: %m");
384                         return -errno;
385                 }
386
387                 zero(sa);
388                 sa.un.sun_family = AF_UNIX;
389                 strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path));
390
391                 unlink(sa.un.sun_path);
392
393                 r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
394                 if (r < 0) {
395                         log_error("bind() failed: %m");
396                         return -errno;
397                 }
398
399                 chmod(sa.un.sun_path, 0666);
400         } else
401                 fd_nonblock(s->native_fd, 1);
402
403         one = 1;
404         r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
405         if (r < 0) {
406                 log_error("SO_PASSCRED failed: %m");
407                 return -errno;
408         }
409
410 #ifdef HAVE_SELINUX
411         if (use_selinux()) {
412                 one = 1;
413                 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
414                 if (r < 0)
415                         log_warning("SO_PASSSEC failed: %m");
416         }
417 #endif
418
419         one = 1;
420         r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
421         if (r < 0) {
422                 log_error("SO_TIMESTAMP failed: %m");
423                 return -errno;
424         }
425
426         r = sd_event_add_io(s->event, &s->native_event_source, s->native_fd, EPOLLIN, process_datagram, s);
427         if (r < 0) {
428                 log_error("Failed to add native server fd to event loop: %s", strerror(-r));
429                 return r;
430         }
431
432         return 0;
433 }