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