]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journald-native.c
man: bring resolved.conf up-to-date
[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 <unistd.h>
23 #include <stddef.h>
24 #include <sys/epoll.h>
25
26 #include "socket-util.h"
27 #include "path-util.h"
28 #include "selinux-util.h"
29 #include "journald-server.h"
30 #include "journald-native.h"
31 #include "journald-kmsg.h"
32 #include "journald-console.h"
33 #include "journald-syslog.h"
34 #include "journald-wall.h"
35
36 bool valid_user_field(const char *p, size_t l, bool allow_protected) {
37 const char *a;
38
39 /* We kinda enforce POSIX syntax recommendations for
40 environment variables here, but make a couple of additional
41 requirements.
42
43 http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
44
45 /* No empty field names */
46 if (l <= 0)
47 return false;
48
49 /* Don't allow names longer than 64 chars */
50 if (l > 64)
51 return false;
52
53 /* Variables starting with an underscore are protected */
54 if (!allow_protected && p[0] == '_')
55 return false;
56
57 /* Don't allow digits as first character */
58 if (p[0] >= '0' && p[0] <= '9')
59 return false;
60
61 /* Only allow A-Z0-9 and '_' */
62 for (a = p; a < p + l; a++)
63 if ((*a < 'A' || *a > 'Z') &&
64 (*a < '0' || *a > '9') &&
65 *a != '_')
66 return false;
67
68 return true;
69 }
70
71 static bool allow_object_pid(struct ucred *ucred) {
72 return ucred && ucred->uid == 0;
73 }
74
75 void server_process_native_message(
76 Server *s,
77 const void *buffer, size_t buffer_size,
78 struct ucred *ucred,
79 struct timeval *tv,
80 const char *label, size_t label_len) {
81
82 struct iovec *iovec = NULL;
83 unsigned n = 0, j, tn = (unsigned) -1;
84 const char *p;
85 size_t remaining, m = 0, entry_size = 0;
86 int priority = LOG_INFO;
87 char *identifier = NULL, *message = NULL;
88 pid_t object_pid = 0;
89
90 assert(s);
91 assert(buffer || buffer_size == 0);
92
93 p = buffer;
94 remaining = buffer_size;
95
96 while (remaining > 0) {
97 const char *e, *q;
98
99 e = memchr(p, '\n', remaining);
100
101 if (!e) {
102 /* Trailing noise, let's ignore it, and flush what we collected */
103 log_debug("Received message with trailing noise, ignoring.");
104 break;
105 }
106
107 if (e == p) {
108 /* Entry separator */
109
110 if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */
111 log_debug("Entry is too big with %u properties and %zu bytes, ignoring.",
112 n, entry_size);
113 continue;
114 }
115
116 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
117 n = 0;
118 priority = LOG_INFO;
119 entry_size = 0;
120
121 p++;
122 remaining--;
123 continue;
124 }
125
126 if (*p == '.' || *p == '#') {
127 /* Ignore control commands for now, and
128 * comments too. */
129 remaining -= (e - p) + 1;
130 p = e + 1;
131 continue;
132 }
133
134 /* A property follows */
135
136 /* n received properties, +1 for _TRANSPORT */
137 if (!GREEDY_REALLOC(iovec, m, n + 1 + N_IOVEC_META_FIELDS +
138 !!object_pid * N_IOVEC_OBJECT_FIELDS)) {
139 log_oom();
140 break;
141 }
142
143 q = memchr(p, '=', e - p);
144 if (q) {
145 if (valid_user_field(p, q - p, false)) {
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;
156 n++;
157 entry_size += iovec[n].iov_len;
158
159 /* We need to determine the priority
160 * of this entry for the rate limiting
161 * logic */
162 if (l == 10 &&
163 startswith(p, "PRIORITY=") &&
164 p[9] >= '0' && p[9] <= '9')
165 priority = (priority & LOG_FACMASK) | (p[9] - '0');
166
167 else if (l == 17 &&
168 startswith(p, "SYSLOG_FACILITY=") &&
169 p[16] >= '0' && p[16] <= '9')
170 priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
171
172 else if (l == 18 &&
173 startswith(p, "SYSLOG_FACILITY=") &&
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 &&
179 startswith(p, "SYSLOG_IDENTIFIER=")) {
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 &&
188 startswith(p, "MESSAGE=")) {
189 char *t;
190
191 t = strndup(p + 8, l - 8);
192 if (t) {
193 free(message);
194 message = t;
195 }
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);
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) {
226 log_debug("Received binary data block of %"PRIu64" bytes is too large, ignoring.", l);
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
246 if (valid_user_field(p, e - p, false)) {
247 iovec[n].iov_base = k;
248 iovec[n].iov_len = (e - p) + 1 + l;
249 n++;
250 entry_size += iovec[n].iov_len;
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");
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 }
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);
281
282 if (s->forward_to_wall)
283 server_forward_wall(s, priority, identifier, message, ucred);
284 }
285
286 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
287
288 finish:
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
303 void server_process_native_file(
304 Server *s,
305 int fd,
306 struct ucred *ucred,
307 struct timeval *tv,
308 const char *label, size_t label_len) {
309
310 struct stat st;
311 _cleanup_free_ void *p = NULL;
312 ssize_t n;
313 int r;
314
315 assert(s);
316 assert(fd >= 0);
317
318 if (!ucred || ucred->uid != 0) {
319 _cleanup_free_ char *sl = NULL, *k = NULL;
320 const char *e;
321
322 if (asprintf(&sl, "/proc/self/fd/%i", fd) < 0) {
323 log_oom();
324 return;
325 }
326
327 r = readlink_malloc(sl, &k);
328 if (r < 0) {
329 log_error("readlink(%s) failed: %m", sl);
330 return;
331 }
332
333 e = path_startswith(k, "/dev/shm/");
334 if (!e)
335 e = path_startswith(k, "/tmp/");
336 if (!e)
337 e = path_startswith(k, "/var/tmp/");
338 if (!e) {
339 log_error("Received file outside of allowed directories. Refusing.");
340 return;
341 }
342
343 if (!filename_is_safe(e)) {
344 log_error("Received file in subdirectory of allowed directories. Refusing.");
345 return;
346 }
347 }
348
349 /* Data is in the passed file, since it didn't fit in a
350 * datagram. We can't map the file here, since clients might
351 * then truncate it and trigger a SIGBUS for us. So let's
352 * stupidly read it */
353
354 if (fstat(fd, &st) < 0) {
355 log_error("Failed to stat passed file, ignoring: %m");
356 return;
357 }
358
359 if (!S_ISREG(st.st_mode)) {
360 log_error("File passed is not regular. Ignoring.");
361 return;
362 }
363
364 if (st.st_size <= 0)
365 return;
366
367 if (st.st_size > ENTRY_SIZE_MAX) {
368 log_error("File passed too large. Ignoring.");
369 return;
370 }
371
372 p = malloc(st.st_size);
373 if (!p) {
374 log_oom();
375 return;
376 }
377
378 n = pread(fd, p, st.st_size, 0);
379 if (n < 0)
380 log_error("Failed to read file, ignoring: %s", strerror(-n));
381 else if (n > 0)
382 server_process_native_message(s, p, n, ucred, tv, label, label_len);
383 }
384
385 int server_open_native_socket(Server*s) {
386 int one, r;
387
388 assert(s);
389
390 if (s->native_fd < 0) {
391 union sockaddr_union sa = {
392 .un.sun_family = AF_UNIX,
393 .un.sun_path = "/run/systemd/journal/socket",
394 };
395
396 s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
397 if (s->native_fd < 0) {
398 log_error("socket() failed: %m");
399 return -errno;
400 }
401
402 unlink(sa.un.sun_path);
403
404 r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
405 if (r < 0) {
406 log_error("bind(%s) failed: %m", sa.un.sun_path);
407 return -errno;
408 }
409
410 chmod(sa.un.sun_path, 0666);
411 } else
412 fd_nonblock(s->native_fd, 1);
413
414 one = 1;
415 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
416 if (r < 0) {
417 log_error("SO_PASSCRED failed: %m");
418 return -errno;
419 }
420
421 #ifdef HAVE_SELINUX
422 if (use_selinux()) {
423 one = 1;
424 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
425 if (r < 0)
426 log_warning("SO_PASSSEC failed: %m");
427 }
428 #endif
429
430 one = 1;
431 r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
432 if (r < 0) {
433 log_error("SO_TIMESTAMP failed: %m");
434 return -errno;
435 }
436
437 r = sd_event_add_io(s->event, &s->native_event_source, s->native_fd, EPOLLIN, process_datagram, s);
438 if (r < 0) {
439 log_error("Failed to add native server fd to event loop: %s", strerror(-r));
440 return r;
441 }
442
443 return 0;
444 }