]> git.proxmox.com Git - systemd.git/blame - src/journal/journald-native.c
Imported Upstream version 220
[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 }
187 } else if (l >= 8 &&
14228c0d 188 startswith(p, "MESSAGE=")) {
663996b3
MS
189 char *t;
190
191 t = strndup(p + 8, l - 8);
192 if (t) {
193 free(message);
194 message = t;
195 }
14228c0d
MB
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);
663996b3
MS
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) {
5eef597e 226 log_debug("Received binary data block of %"PRIu64" bytes is too large, ignoring.", l);
663996b3
MS
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
60f067b4 246 if (valid_user_field(p, e - p, false)) {
663996b3
MS
247 iovec[n].iov_base = k;
248 iovec[n].iov_len = (e - p) + 1 + l;
5eef597e 249 entry_size += iovec[n].iov_len;
663996b3
MS
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");
5eef597e
MP
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 }
663996b3
MS
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);
60f067b4
JS
281
282 if (s->forward_to_wall)
283 server_forward_wall(s, priority, identifier, message, ucred);
663996b3
MS
284 }
285
14228c0d 286 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
663996b3
MS
287
288finish:
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
303void server_process_native_file(
304 Server *s,
305 int fd,
f47781d8
MP
306 const struct ucred *ucred,
307 const struct timeval *tv,
663996b3
MS
308 const char *label, size_t label_len) {
309
310 struct stat st;
f47781d8 311 bool sealed;
663996b3
MS
312 int r;
313
f47781d8
MP
314 /* Data is in the passed fd, since it didn't fit in a
315 * datagram. */
316
663996b3
MS
317 assert(s);
318 assert(fd >= 0);
319
f47781d8
MP
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)) {
663996b3
MS
326 _cleanup_free_ char *sl = NULL, *k = NULL;
327 const char *e;
328
f47781d8
MP
329 /* If this is not a sealed memfd, and the peer is unknown or
330 * unprivileged, then verify the path. */
331
663996b3
MS
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) {
f47781d8 339 log_error_errno(errno, "readlink(%s) failed: %m", sl);
663996b3
MS
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
e735f4d4 353 if (!filename_is_valid(e)) {
663996b3
MS
354 log_error("Received file in subdirectory of allowed directories. Refusing.");
355 return;
356 }
357 }
358
663996b3 359 if (fstat(fd, &st) < 0) {
f47781d8 360 log_error_errno(errno, "Failed to stat passed file, ignoring: %m");
663996b3
MS
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
f47781d8
MP
377 if (sealed) {
378 void *p;
379 size_t ps;
380
381 /* The file is sealed, we can just map it and use it. */
663996b3 382
f47781d8
MP
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 }
663996b3
MS
412}
413
414int server_open_native_socket(Server*s) {
f47781d8
MP
415 static const int one = 1;
416 int r;
663996b3
MS
417
418 assert(s);
419
420 if (s->native_fd < 0) {
5eef597e
MP
421 union sockaddr_union sa = {
422 .un.sun_family = AF_UNIX,
423 .un.sun_path = "/run/systemd/journal/socket",
424 };
663996b3
MS
425
426 s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
f47781d8
MP
427 if (s->native_fd < 0)
428 return log_error_errno(errno, "socket() failed: %m");
663996b3 429
663996b3
MS
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));
f47781d8
MP
433 if (r < 0)
434 return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
663996b3 435
e3bff60a 436 (void) chmod(sa.un.sun_path, 0666);
663996b3
MS
437 } else
438 fd_nonblock(s->native_fd, 1);
439
663996b3 440 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
f47781d8
MP
441 if (r < 0)
442 return log_error_errno(errno, "SO_PASSCRED failed: %m");
663996b3
MS
443
444#ifdef HAVE_SELINUX
5eef597e 445 if (mac_selinux_use()) {
60f067b4
JS
446 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
447 if (r < 0)
f47781d8 448 log_warning_errno(errno, "SO_PASSSEC failed: %m");
60f067b4 449 }
663996b3
MS
450#endif
451
663996b3 452 r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
f47781d8
MP
453 if (r < 0)
454 return log_error_errno(errno, "SO_TIMESTAMP failed: %m");
663996b3 455
e735f4d4 456 r = sd_event_add_io(s->event, &s->native_event_source, s->native_fd, EPOLLIN, server_process_datagram, s);
f47781d8
MP
457 if (r < 0)
458 return log_error_errno(r, "Failed to add native server fd to event loop: %m");
663996b3
MS
459
460 return 0;
461}