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