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