]> git.proxmox.com Git - systemd.git/blame - src/journal/journald-native.c
Imported Upstream version 227
[systemd.git] / src / journal / journald-native.c
CommitLineData
663996b3
MS
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>
f47781d8 25#include <sys/mman.h>
663996b3
MS
26
27#include "socket-util.h"
28#include "path-util.h"
60f067b4 29#include "selinux-util.h"
663996b3
MS
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"
60f067b4 35#include "journald-wall.h"
f47781d8 36#include "memfd-util.h"
663996b3 37
60f067b4 38bool valid_user_field(const char *p, size_t l, bool allow_protected) {
663996b3
MS
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 */
60f067b4 56 if (!allow_protected && p[0] == '_')
663996b3
MS
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++)
60f067b4
JS
65 if ((*a < 'A' || *a > 'Z') &&
66 (*a < '0' || *a > '9') &&
67 *a != '_')
663996b3
MS
68 return false;
69
70 return true;
71}
72
f47781d8 73static bool allow_object_pid(const struct ucred *ucred) {
14228c0d
MB
74 return ucred && ucred->uid == 0;
75}
76
663996b3
MS
77void server_process_native_message(
78 Server *s,
79 const void *buffer, size_t buffer_size,
f47781d8
MP
80 const struct ucred *ucred,
81 const struct timeval *tv,
663996b3
MS
82 const char *label, size_t label_len) {
83
84 struct iovec *iovec = NULL;
14228c0d 85 unsigned n = 0, j, tn = (unsigned) -1;
663996b3 86 const char *p;
5eef597e 87 size_t remaining, m = 0, entry_size = 0;
663996b3
MS
88 int priority = LOG_INFO;
89 char *identifier = NULL, *message = NULL;
14228c0d 90 pid_t object_pid = 0;
663996b3
MS
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 */
5eef597e
MP
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
14228c0d 117 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
663996b3
MS
118 n = 0;
119 priority = LOG_INFO;
5eef597e 120 entry_size = 0;
663996b3
MS
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
e735f4d4
MP
137 /* n existing properties, 1 new, +1 for _TRANSPORT */
138 if (!GREEDY_REALLOC(iovec, m, n + 2 + N_IOVEC_META_FIELDS + N_IOVEC_OBJECT_FIELDS)) {
14228c0d
MB
139 log_oom();
140 break;
663996b3
MS
141 }
142
143 q = memchr(p, '=', e - p);
144 if (q) {
60f067b4 145 if (valid_user_field(p, q - p, false)) {
663996b3
MS
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;
5eef597e 156 entry_size += iovec[n].iov_len;
663996b3
MS
157 n++;
158
159 /* We need to determine the priority
160 * of this entry for the rate limiting
161 * logic */
162 if (l == 10 &&
14228c0d 163 startswith(p, "PRIORITY=") &&
663996b3
MS
164 p[9] >= '0' && p[9] <= '9')
165 priority = (priority & LOG_FACMASK) | (p[9] - '0');
166
167 else if (l == 17 &&
14228c0d 168 startswith(p, "SYSLOG_FACILITY=") &&
663996b3
MS
169 p[16] >= '0' && p[16] <= '9')
170 priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
171
172 else if (l == 18 &&
14228c0d 173 startswith(p, "SYSLOG_FACILITY=") &&
663996b3
MS
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 &&
14228c0d 179 startswith(p, "SYSLOG_IDENTIFIER=")) {
663996b3
MS
180 char *t;
181
182 t = strndup(p + 18, l - 18);
183 if (t) {
184 free(identifier);
185 identifier = t;
186 }
6300502b 187
663996b3 188 } else if (l >= 8 &&
14228c0d 189 startswith(p, "MESSAGE=")) {
663996b3
MS
190 char *t;
191
192 t = strndup(p + 8, l - 8);
193 if (t) {
194 free(message);
195 message = t;
196 }
6300502b 197
14228c0d
MB
198 } else if (l > strlen("OBJECT_PID=") &&
199 l < strlen("OBJECT_PID=") + DECIMAL_STR_MAX(pid_t) &&
200 startswith(p, "OBJECT_PID=") &&
201 allow_object_pid(ucred)) {
202 char buf[DECIMAL_STR_MAX(pid_t)];
203 memcpy(buf, p + strlen("OBJECT_PID="), l - strlen("OBJECT_PID="));
204 char_array_0(buf);
205
206 /* ignore error */
207 parse_pid(buf, &object_pid);
663996b3
MS
208 }
209 }
210
211 remaining -= (e - p) + 1;
212 p = e + 1;
213 continue;
214 } else {
215 le64_t l_le;
216 uint64_t l;
217 char *k;
218
219 if (remaining < e - p + 1 + sizeof(uint64_t) + 1) {
220 log_debug("Failed to parse message, ignoring.");
221 break;
222 }
223
224 memcpy(&l_le, e + 1, sizeof(uint64_t));
225 l = le64toh(l_le);
226
227 if (l > DATA_SIZE_MAX) {
5eef597e 228 log_debug("Received binary data block of %"PRIu64" bytes is too large, ignoring.", l);
663996b3
MS
229 break;
230 }
231
232 if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
233 e[1+sizeof(uint64_t)+l] != '\n') {
234 log_debug("Failed to parse message, ignoring.");
235 break;
236 }
237
238 k = malloc((e - p) + 1 + l);
239 if (!k) {
240 log_oom();
241 break;
242 }
243
244 memcpy(k, p, e - p);
245 k[e - p] = '=';
246 memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
247
60f067b4 248 if (valid_user_field(p, e - p, false)) {
663996b3
MS
249 iovec[n].iov_base = k;
250 iovec[n].iov_len = (e - p) + 1 + l;
5eef597e 251 entry_size += iovec[n].iov_len;
663996b3
MS
252 n++;
253 } else
254 free(k);
255
256 remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1;
257 p = e + 1 + sizeof(uint64_t) + l + 1;
258 }
259 }
260
261 if (n <= 0)
262 goto finish;
263
264 tn = n++;
265 IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal");
5eef597e
MP
266 entry_size += strlen("_TRANSPORT=journal");
267
268 if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */
269 log_debug("Entry is too big with %u properties and %zu bytes, ignoring.",
270 n, entry_size);
271 goto finish;
272 }
663996b3
MS
273
274 if (message) {
275 if (s->forward_to_syslog)
276 server_forward_syslog(s, priority, identifier, message, ucred, tv);
277
278 if (s->forward_to_kmsg)
279 server_forward_kmsg(s, priority, identifier, message, ucred);
280
281 if (s->forward_to_console)
282 server_forward_console(s, priority, identifier, message, ucred);
60f067b4
JS
283
284 if (s->forward_to_wall)
285 server_forward_wall(s, priority, identifier, message, ucred);
663996b3
MS
286 }
287
14228c0d 288 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
663996b3
MS
289
290finish:
291 for (j = 0; j < n; j++) {
292 if (j == tn)
293 continue;
294
295 if (iovec[j].iov_base < buffer ||
296 (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size)
297 free(iovec[j].iov_base);
298 }
299
300 free(iovec);
301 free(identifier);
302 free(message);
303}
304
305void server_process_native_file(
306 Server *s,
307 int fd,
f47781d8
MP
308 const struct ucred *ucred,
309 const struct timeval *tv,
663996b3
MS
310 const char *label, size_t label_len) {
311
312 struct stat st;
f47781d8 313 bool sealed;
663996b3
MS
314 int r;
315
f47781d8
MP
316 /* Data is in the passed fd, since it didn't fit in a
317 * datagram. */
318
663996b3
MS
319 assert(s);
320 assert(fd >= 0);
321
f47781d8
MP
322 /* If it's a memfd, check if it is sealed. If so, we can just
323 * use map it and use it, and do not need to copy the data
324 * out. */
325 sealed = memfd_get_sealed(fd) > 0;
326
327 if (!sealed && (!ucred || ucred->uid != 0)) {
663996b3
MS
328 _cleanup_free_ char *sl = NULL, *k = NULL;
329 const char *e;
330
f47781d8
MP
331 /* If this is not a sealed memfd, and the peer is unknown or
332 * unprivileged, then verify the path. */
333
663996b3
MS
334 if (asprintf(&sl, "/proc/self/fd/%i", fd) < 0) {
335 log_oom();
336 return;
337 }
338
339 r = readlink_malloc(sl, &k);
340 if (r < 0) {
f47781d8 341 log_error_errno(errno, "readlink(%s) failed: %m", sl);
663996b3
MS
342 return;
343 }
344
345 e = path_startswith(k, "/dev/shm/");
346 if (!e)
347 e = path_startswith(k, "/tmp/");
348 if (!e)
349 e = path_startswith(k, "/var/tmp/");
350 if (!e) {
351 log_error("Received file outside of allowed directories. Refusing.");
352 return;
353 }
354
e735f4d4 355 if (!filename_is_valid(e)) {
663996b3
MS
356 log_error("Received file in subdirectory of allowed directories. Refusing.");
357 return;
358 }
359 }
360
663996b3 361 if (fstat(fd, &st) < 0) {
f47781d8 362 log_error_errno(errno, "Failed to stat passed file, ignoring: %m");
663996b3
MS
363 return;
364 }
365
366 if (!S_ISREG(st.st_mode)) {
367 log_error("File passed is not regular. Ignoring.");
368 return;
369 }
370
371 if (st.st_size <= 0)
372 return;
373
374 if (st.st_size > ENTRY_SIZE_MAX) {
375 log_error("File passed too large. Ignoring.");
376 return;
377 }
378
f47781d8
MP
379 if (sealed) {
380 void *p;
381 size_t ps;
382
383 /* The file is sealed, we can just map it and use it. */
663996b3 384
f47781d8
MP
385 ps = PAGE_ALIGN(st.st_size);
386 p = mmap(NULL, ps, PROT_READ, MAP_PRIVATE, fd, 0);
387 if (p == MAP_FAILED) {
388 log_error_errno(errno, "Failed to map memfd, ignoring: %m");
389 return;
390 }
391
392 server_process_native_message(s, p, st.st_size, ucred, tv, label, label_len);
393 assert_se(munmap(p, ps) >= 0);
394 } else {
395 _cleanup_free_ void *p = NULL;
396 ssize_t n;
397
398 /* The file is not sealed, we can't map the file here, since
399 * clients might then truncate it and trigger a SIGBUS for
400 * us. So let's stupidly read it */
401
402 p = malloc(st.st_size);
403 if (!p) {
404 log_oom();
405 return;
406 }
407
408 n = pread(fd, p, st.st_size, 0);
409 if (n < 0)
410 log_error_errno(n, "Failed to read file, ignoring: %m");
411 else if (n > 0)
412 server_process_native_message(s, p, n, ucred, tv, label, label_len);
413 }
663996b3
MS
414}
415
416int server_open_native_socket(Server*s) {
f47781d8
MP
417 static const int one = 1;
418 int r;
663996b3
MS
419
420 assert(s);
421
422 if (s->native_fd < 0) {
5eef597e
MP
423 union sockaddr_union sa = {
424 .un.sun_family = AF_UNIX,
425 .un.sun_path = "/run/systemd/journal/socket",
426 };
663996b3
MS
427
428 s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
f47781d8
MP
429 if (s->native_fd < 0)
430 return log_error_errno(errno, "socket() failed: %m");
663996b3 431
663996b3
MS
432 unlink(sa.un.sun_path);
433
434 r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
f47781d8
MP
435 if (r < 0)
436 return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
663996b3 437
e3bff60a 438 (void) chmod(sa.un.sun_path, 0666);
663996b3
MS
439 } else
440 fd_nonblock(s->native_fd, 1);
441
663996b3 442 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
f47781d8
MP
443 if (r < 0)
444 return log_error_errno(errno, "SO_PASSCRED failed: %m");
663996b3
MS
445
446#ifdef HAVE_SELINUX
5eef597e 447 if (mac_selinux_use()) {
60f067b4
JS
448 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
449 if (r < 0)
f47781d8 450 log_warning_errno(errno, "SO_PASSSEC failed: %m");
60f067b4 451 }
663996b3
MS
452#endif
453
663996b3 454 r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
f47781d8
MP
455 if (r < 0)
456 return log_error_errno(errno, "SO_TIMESTAMP failed: %m");
663996b3 457
e735f4d4 458 r = sd_event_add_io(s->event, &s->native_event_source, s->native_fd, EPOLLIN, server_process_datagram, s);
f47781d8
MP
459 if (r < 0)
460 return log_error_errno(r, "Failed to add native server fd to event loop: %m");
663996b3
MS
461
462 return 0;
463}