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