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