]> git.ipfire.org Git - thirdparty/systemd.git/blob - 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
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 "journald-console.h"
28 #include "journald-kmsg.h"
29 #include "journald-server.h"
30 #include "journald-syslog.h"
31 #include "journald-wall.h"
32 #include "memfd-util.h"
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"
38
39 bool valid_user_field(const char *p, size_t l, bool allow_protected) {
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 */
57 if (!allow_protected && p[0] == '_')
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++)
66 if ((*a < 'A' || *a > 'Z') &&
67 (*a < '0' || *a > '9') &&
68 *a != '_')
69 return false;
70
71 return true;
72 }
73
74 static bool allow_object_pid(const struct ucred *ucred) {
75 return ucred && ucred->uid == 0;
76 }
77
78 void server_process_native_message(
79 Server *s,
80 const void *buffer, size_t buffer_size,
81 const struct ucred *ucred,
82 const struct timeval *tv,
83 const char *label, size_t label_len) {
84
85 struct iovec *iovec = NULL;
86 unsigned n = 0, j, tn = (unsigned) -1;
87 const char *p;
88 size_t remaining, m = 0, entry_size = 0;
89 int priority = LOG_INFO;
90 char *identifier = NULL, *message = NULL;
91 pid_t object_pid = 0;
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 */
112
113 if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */
114 log_debug("Entry is too big with %u properties and %zu bytes, ignoring.", n, entry_size);
115 continue;
116 }
117
118 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
119 n = 0;
120 priority = LOG_INFO;
121 entry_size = 0;
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
138 /* n existing properties, 1 new, +1 for _TRANSPORT */
139 if (!GREEDY_REALLOC(iovec, m, n + 2 + N_IOVEC_META_FIELDS + N_IOVEC_OBJECT_FIELDS)) {
140 log_oom();
141 break;
142 }
143
144 q = memchr(p, '=', e - p);
145 if (q) {
146 if (valid_user_field(p, q - p, false)) {
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;
157 entry_size += iovec[n].iov_len;
158 n++;
159
160 /* We need to determine the priority
161 * of this entry for the rate limiting
162 * logic */
163 if (l == 10 &&
164 startswith(p, "PRIORITY=") &&
165 p[9] >= '0' && p[9] <= '9')
166 priority = (priority & LOG_FACMASK) | (p[9] - '0');
167
168 else if (l == 17 &&
169 startswith(p, "SYSLOG_FACILITY=") &&
170 p[16] >= '0' && p[16] <= '9')
171 priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
172
173 else if (l == 18 &&
174 startswith(p, "SYSLOG_FACILITY=") &&
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 &&
180 startswith(p, "SYSLOG_IDENTIFIER=")) {
181 char *t;
182
183 t = strndup(p + 18, l - 18);
184 if (t) {
185 free(identifier);
186 identifier = t;
187 }
188
189 } else if (l >= 8 &&
190 startswith(p, "MESSAGE=")) {
191 char *t;
192
193 t = strndup(p + 8, l - 8);
194 if (t) {
195 free(message);
196 message = t;
197 }
198
199 } else if (l > strlen("OBJECT_PID=") &&
200 l < strlen("OBJECT_PID=") + DECIMAL_STR_MAX(pid_t) &&
201 startswith(p, "OBJECT_PID=") &&
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);
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
228 if (l > DATA_SIZE_MAX) {
229 log_debug("Received binary data block of %"PRIu64" bytes is too large, ignoring.", l);
230 break;
231 }
232
233 if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
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
249 if (valid_user_field(p, e - p, false)) {
250 iovec[n].iov_base = k;
251 iovec[n].iov_len = (e - p) + 1 + l;
252 entry_size += iovec[n].iov_len;
253 n++;
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");
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 }
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);
284
285 if (s->forward_to_wall)
286 server_forward_wall(s, priority, identifier, message, ucred);
287 }
288
289 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
290
291 finish:
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
306 void server_process_native_file(
307 Server *s,
308 int fd,
309 const struct ucred *ucred,
310 const struct timeval *tv,
311 const char *label, size_t label_len) {
312
313 struct stat st;
314 bool sealed;
315 int r;
316
317 /* Data is in the passed fd, since it didn't fit in a
318 * datagram. */
319
320 assert(s);
321 assert(fd >= 0);
322
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. */
326 sealed = memfd_get_sealed(fd) > 0;
327
328 if (!sealed && (!ucred || ucred->uid != 0)) {
329 _cleanup_free_ char *sl = NULL, *k = NULL;
330 const char *e;
331
332 /* If this is not a sealed memfd, and the peer is unknown or
333 * unprivileged, then verify the path. */
334
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) {
342 log_error_errno(errno, "readlink(%s) failed: %m", sl);
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
356 if (!filename_is_valid(e)) {
357 log_error("Received file in subdirectory of allowed directories. Refusing.");
358 return;
359 }
360 }
361
362 if (fstat(fd, &st) < 0) {
363 log_error_errno(errno, "Failed to stat passed file, ignoring: %m");
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
380 if (sealed) {
381 void *p;
382 size_t ps;
383
384 /* The file is sealed, we can just map it and use it. */
385
386 ps = PAGE_ALIGN(st.st_size);
387 p = mmap(NULL, ps, PROT_READ, MAP_PRIVATE, fd, 0);
388 if (p == MAP_FAILED) {
389 log_error_errno(errno, "Failed to map memfd, ignoring: %m");
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)
411 log_error_errno(n, "Failed to read file, ignoring: %m");
412 else if (n > 0)
413 server_process_native_message(s, p, n, ucred, tv, label, label_len);
414 }
415 }
416
417 int server_open_native_socket(Server*s) {
418 static const int one = 1;
419 int r;
420
421 assert(s);
422
423 if (s->native_fd < 0) {
424 union sockaddr_union sa = {
425 .un.sun_family = AF_UNIX,
426 .un.sun_path = "/run/systemd/journal/socket",
427 };
428
429 s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
430 if (s->native_fd < 0)
431 return log_error_errno(errno, "socket() failed: %m");
432
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));
436 if (r < 0)
437 return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
438
439 (void) chmod(sa.un.sun_path, 0666);
440 } else
441 fd_nonblock(s->native_fd, 1);
442
443 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
444 if (r < 0)
445 return log_error_errno(errno, "SO_PASSCRED failed: %m");
446
447 #ifdef HAVE_SELINUX
448 if (mac_selinux_use()) {
449 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
450 if (r < 0)
451 log_warning_errno(errno, "SO_PASSSEC failed: %m");
452 }
453 #endif
454
455 r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
456 if (r < 0)
457 return log_error_errno(errno, "SO_TIMESTAMP failed: %m");
458
459 r = sd_event_add_io(s->event, &s->native_event_source, s->native_fd, EPOLLIN, server_process_datagram, s);
460 if (r < 0)
461 return log_error_errno(r, "Failed to add native server fd to event loop: %m");
462
463 return 0;
464 }