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