]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journald-native.c
journald: remove redundant newlines
[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.", n, entry_size);
112 continue;
113 }
114
115 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
116 n = 0;
117 priority = LOG_INFO;
118 entry_size = 0;
119
120 p++;
121 remaining--;
122 continue;
123 }
124
125 if (*p == '.' || *p == '#') {
126 /* Ignore control commands for now, and
127 * comments too. */
128 remaining -= (e - p) + 1;
129 p = e + 1;
130 continue;
131 }
132
133 /* A property follows */
134
135 /* n received properties, +1 for _TRANSPORT */
136 if (!GREEDY_REALLOC(iovec, m, n + 1 + N_IOVEC_META_FIELDS + !!object_pid * N_IOVEC_OBJECT_FIELDS)) {
137 log_oom();
138 break;
139 }
140
141 q = memchr(p, '=', e - p);
142 if (q) {
143 if (valid_user_field(p, q - p, false)) {
144 size_t l;
145
146 l = e - p;
147
148 /* If the field name starts with an
149 * underscore, skip the variable,
150 * since that indidates a trusted
151 * field */
152 iovec[n].iov_base = (char*) p;
153 iovec[n].iov_len = l;
154 entry_size += iovec[n].iov_len;
155 n++;
156
157 /* We need to determine the priority
158 * of this entry for the rate limiting
159 * logic */
160 if (l == 10 &&
161 startswith(p, "PRIORITY=") &&
162 p[9] >= '0' && p[9] <= '9')
163 priority = (priority & LOG_FACMASK) | (p[9] - '0');
164
165 else if (l == 17 &&
166 startswith(p, "SYSLOG_FACILITY=") &&
167 p[16] >= '0' && p[16] <= '9')
168 priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
169
170 else if (l == 18 &&
171 startswith(p, "SYSLOG_FACILITY=") &&
172 p[16] >= '0' && p[16] <= '9' &&
173 p[17] >= '0' && p[17] <= '9')
174 priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3);
175
176 else if (l >= 19 &&
177 startswith(p, "SYSLOG_IDENTIFIER=")) {
178 char *t;
179
180 t = strndup(p + 18, l - 18);
181 if (t) {
182 free(identifier);
183 identifier = t;
184 }
185 } else if (l >= 8 &&
186 startswith(p, "MESSAGE=")) {
187 char *t;
188
189 t = strndup(p + 8, l - 8);
190 if (t) {
191 free(message);
192 message = t;
193 }
194 } else if (l > strlen("OBJECT_PID=") &&
195 l < strlen("OBJECT_PID=") + DECIMAL_STR_MAX(pid_t) &&
196 startswith(p, "OBJECT_PID=") &&
197 allow_object_pid(ucred)) {
198 char buf[DECIMAL_STR_MAX(pid_t)];
199 memcpy(buf, p + strlen("OBJECT_PID="), l - strlen("OBJECT_PID="));
200 char_array_0(buf);
201
202 /* ignore error */
203 parse_pid(buf, &object_pid);
204 }
205 }
206
207 remaining -= (e - p) + 1;
208 p = e + 1;
209 continue;
210 } else {
211 le64_t l_le;
212 uint64_t l;
213 char *k;
214
215 if (remaining < e - p + 1 + sizeof(uint64_t) + 1) {
216 log_debug("Failed to parse message, ignoring.");
217 break;
218 }
219
220 memcpy(&l_le, e + 1, sizeof(uint64_t));
221 l = le64toh(l_le);
222
223 if (l > DATA_SIZE_MAX) {
224 log_debug("Received binary data block of %"PRIu64" bytes is too large, ignoring.", l);
225 break;
226 }
227
228 if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
229 e[1+sizeof(uint64_t)+l] != '\n') {
230 log_debug("Failed to parse message, ignoring.");
231 break;
232 }
233
234 k = malloc((e - p) + 1 + l);
235 if (!k) {
236 log_oom();
237 break;
238 }
239
240 memcpy(k, p, e - p);
241 k[e - p] = '=';
242 memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
243
244 if (valid_user_field(p, e - p, false)) {
245 iovec[n].iov_base = k;
246 iovec[n].iov_len = (e - p) + 1 + l;
247 entry_size += iovec[n].iov_len;
248 n++;
249 } else
250 free(k);
251
252 remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1;
253 p = e + 1 + sizeof(uint64_t) + l + 1;
254 }
255 }
256
257 if (n <= 0)
258 goto finish;
259
260 tn = n++;
261 IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal");
262 entry_size += strlen("_TRANSPORT=journal");
263
264 if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */
265 log_debug("Entry is too big with %u properties and %zu bytes, ignoring.",
266 n, entry_size);
267 goto finish;
268 }
269
270 if (message) {
271 if (s->forward_to_syslog)
272 server_forward_syslog(s, priority, identifier, message, ucred, tv);
273
274 if (s->forward_to_kmsg)
275 server_forward_kmsg(s, priority, identifier, message, ucred);
276
277 if (s->forward_to_console)
278 server_forward_console(s, priority, identifier, message, ucred);
279
280 if (s->forward_to_wall)
281 server_forward_wall(s, priority, identifier, message, ucred);
282 }
283
284 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
285
286 finish:
287 for (j = 0; j < n; j++) {
288 if (j == tn)
289 continue;
290
291 if (iovec[j].iov_base < buffer ||
292 (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size)
293 free(iovec[j].iov_base);
294 }
295
296 free(iovec);
297 free(identifier);
298 free(message);
299 }
300
301 void server_process_native_file(
302 Server *s,
303 int fd,
304 struct ucred *ucred,
305 struct timeval *tv,
306 const char *label, size_t label_len) {
307
308 struct stat st;
309 _cleanup_free_ void *p = NULL;
310 ssize_t n;
311 int r;
312
313 assert(s);
314 assert(fd >= 0);
315
316 if (!ucred || ucred->uid != 0) {
317 _cleanup_free_ char *sl = NULL, *k = NULL;
318 const char *e;
319
320 if (asprintf(&sl, "/proc/self/fd/%i", fd) < 0) {
321 log_oom();
322 return;
323 }
324
325 r = readlink_malloc(sl, &k);
326 if (r < 0) {
327 log_error("readlink(%s) failed: %m", sl);
328 return;
329 }
330
331 e = path_startswith(k, "/dev/shm/");
332 if (!e)
333 e = path_startswith(k, "/tmp/");
334 if (!e)
335 e = path_startswith(k, "/var/tmp/");
336 if (!e) {
337 log_error("Received file outside of allowed directories. Refusing.");
338 return;
339 }
340
341 if (!filename_is_safe(e)) {
342 log_error("Received file in subdirectory of allowed directories. Refusing.");
343 return;
344 }
345 }
346
347 /* Data is in the passed file, since it didn't fit in a
348 * datagram. We can't map the file here, since clients might
349 * then truncate it and trigger a SIGBUS for us. So let's
350 * stupidly read it */
351
352 if (fstat(fd, &st) < 0) {
353 log_error("Failed to stat passed file, ignoring: %m");
354 return;
355 }
356
357 if (!S_ISREG(st.st_mode)) {
358 log_error("File passed is not regular. Ignoring.");
359 return;
360 }
361
362 if (st.st_size <= 0)
363 return;
364
365 if (st.st_size > ENTRY_SIZE_MAX) {
366 log_error("File passed too large. Ignoring.");
367 return;
368 }
369
370 p = malloc(st.st_size);
371 if (!p) {
372 log_oom();
373 return;
374 }
375
376 n = pread(fd, p, st.st_size, 0);
377 if (n < 0)
378 log_error("Failed to read file, ignoring: %s", strerror(-n));
379 else if (n > 0)
380 server_process_native_message(s, p, n, ucred, tv, label, label_len);
381 }
382
383 int server_open_native_socket(Server*s) {
384 int one, r;
385
386 assert(s);
387
388 if (s->native_fd < 0) {
389 union sockaddr_union sa = {
390 .un.sun_family = AF_UNIX,
391 .un.sun_path = "/run/systemd/journal/socket",
392 };
393
394 s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
395 if (s->native_fd < 0) {
396 log_error("socket() failed: %m");
397 return -errno;
398 }
399
400 unlink(sa.un.sun_path);
401
402 r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
403 if (r < 0) {
404 log_error("bind(%s) failed: %m", sa.un.sun_path);
405 return -errno;
406 }
407
408 chmod(sa.un.sun_path, 0666);
409 } else
410 fd_nonblock(s->native_fd, 1);
411
412 one = 1;
413 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
414 if (r < 0) {
415 log_error("SO_PASSCRED failed: %m");
416 return -errno;
417 }
418
419 #ifdef HAVE_SELINUX
420 if (use_selinux()) {
421 one = 1;
422 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
423 if (r < 0)
424 log_warning("SO_PASSSEC failed: %m");
425 }
426 #endif
427
428 one = 1;
429 r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
430 if (r < 0) {
431 log_error("SO_TIMESTAMP failed: %m");
432 return -errno;
433 }
434
435 r = sd_event_add_io(s->event, &s->native_event_source, s->native_fd, EPOLLIN, process_datagram, s);
436 if (r < 0) {
437 log_error("Failed to add native server fd to event loop: %s", strerror(-r));
438 return r;
439 }
440
441 return 0;
442 }