chiark / gitweb /
6674f3bd50ff291fff011263f22ee25c23392806
[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.",
112                                           n, entry_size);
113                                 continue;
114                         }
115
116                         server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
117                         n = 0;
118                         priority = LOG_INFO;
119                         entry_size = 0;
120
121                         p++;
122                         remaining--;
123                         continue;
124                 }
125
126                 if (*p == '.' || *p == '#') {
127                         /* Ignore control commands for now, and
128                          * comments too. */
129                         remaining -= (e - p) + 1;
130                         p = e + 1;
131                         continue;
132                 }
133
134                 /* A property follows */
135
136                 /* n received properties, +1 for _TRANSPORT */
137                 if (!GREEDY_REALLOC(iovec, m, n + 1 + N_IOVEC_META_FIELDS +
138                                               !!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                                 n++;
157                                 entry_size += iovec[n].iov_len;
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 %zu 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                                 n++;
250                                 entry_size += iovec[n].iov_len;
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                 struct ucred *ucred,
307                 struct timeval *tv,
308                 const char *label, size_t label_len) {
309
310         struct stat st;
311         _cleanup_free_ void *p = NULL;
312         ssize_t n;
313         int r;
314
315         assert(s);
316         assert(fd >= 0);
317
318         if (!ucred || ucred->uid != 0) {
319                 _cleanup_free_ char *sl = NULL, *k = NULL;
320                 const char *e;
321
322                 if (asprintf(&sl, "/proc/self/fd/%i", fd) < 0) {
323                         log_oom();
324                         return;
325                 }
326
327                 r = readlink_malloc(sl, &k);
328                 if (r < 0) {
329                         log_error("readlink(%s) failed: %m", sl);
330                         return;
331                 }
332
333                 e = path_startswith(k, "/dev/shm/");
334                 if (!e)
335                         e = path_startswith(k, "/tmp/");
336                 if (!e)
337                         e = path_startswith(k, "/var/tmp/");
338                 if (!e) {
339                         log_error("Received file outside of allowed directories. Refusing.");
340                         return;
341                 }
342
343                 if (!filename_is_safe(e)) {
344                         log_error("Received file in subdirectory of allowed directories. Refusing.");
345                         return;
346                 }
347         }
348
349         /* Data is in the passed file, since it didn't fit in a
350          * datagram. We can't map the file here, since clients might
351          * then truncate it and trigger a SIGBUS for us. So let's
352          * stupidly read it */
353
354         if (fstat(fd, &st) < 0) {
355                 log_error("Failed to stat passed file, ignoring: %m");
356                 return;
357         }
358
359         if (!S_ISREG(st.st_mode)) {
360                 log_error("File passed is not regular. Ignoring.");
361                 return;
362         }
363
364         if (st.st_size <= 0)
365                 return;
366
367         if (st.st_size > ENTRY_SIZE_MAX) {
368                 log_error("File passed too large. Ignoring.");
369                 return;
370         }
371
372         p = malloc(st.st_size);
373         if (!p) {
374                 log_oom();
375                 return;
376         }
377
378         n = pread(fd, p, st.st_size, 0);
379         if (n < 0)
380                 log_error("Failed to read file, ignoring: %s", strerror(-n));
381         else if (n > 0)
382                 server_process_native_message(s, p, n, ucred, tv, label, label_len);
383 }
384
385 int server_open_native_socket(Server*s) {
386         union sockaddr_union sa;
387         int one, r;
388
389         assert(s);
390
391         if (s->native_fd < 0) {
392
393                 s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
394                 if (s->native_fd < 0) {
395                         log_error("socket() failed: %m");
396                         return -errno;
397                 }
398
399                 zero(sa);
400                 sa.un.sun_family = AF_UNIX;
401                 strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path));
402
403                 unlink(sa.un.sun_path);
404
405                 r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
406                 if (r < 0) {
407                         log_error("bind() failed: %m");
408                         return -errno;
409                 }
410
411                 chmod(sa.un.sun_path, 0666);
412         } else
413                 fd_nonblock(s->native_fd, 1);
414
415         one = 1;
416         r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
417         if (r < 0) {
418                 log_error("SO_PASSCRED failed: %m");
419                 return -errno;
420         }
421
422 #ifdef HAVE_SELINUX
423         if (use_selinux()) {
424                 one = 1;
425                 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
426                 if (r < 0)
427                         log_warning("SO_PASSSEC failed: %m");
428         }
429 #endif
430
431         one = 1;
432         r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
433         if (r < 0) {
434                 log_error("SO_TIMESTAMP failed: %m");
435                 return -errno;
436         }
437
438         r = sd_event_add_io(s->event, &s->native_event_source, s->native_fd, EPOLLIN, process_datagram, s);
439         if (r < 0) {
440                 log_error("Failed to add native server fd to event loop: %s", strerror(-r));
441                 return r;
442         }
443
444         return 0;
445 }