]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journald-native.c
CODING_STYLE: clarify that we really should use O_CLOEXEC everywhere
[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
22#include <unistd.h>
4871690d 23#include <stddef.h>
0153028a
LP
24#include <sys/epoll.h>
25
26#include "socket-util.h"
1dfa7e79 27#include "path-util.h"
d682b3a7 28#include "selinux-util.h"
d025f1e4 29#include "journald-server.h"
0153028a
LP
30#include "journald-native.h"
31#include "journald-kmsg.h"
32#include "journald-console.h"
33#include "journald-syslog.h"
40b71e89 34#include "journald-wall.h"
0153028a 35
d18d46ec 36bool valid_user_field(const char *p, size_t l, bool allow_protected) {
0153028a
LP
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 */
d18d46ec 54 if (!allow_protected && p[0] == '_')
0153028a
LP
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++)
d18d46ec
ZJS
63 if ((*a < 'A' || *a > 'Z') &&
64 (*a < '0' || *a > '9') &&
65 *a != '_')
0153028a
LP
66 return false;
67
68 return true;
69}
70
968f3196
ZJS
71static bool allow_object_pid(struct ucred *ucred) {
72 return ucred && ucred->uid == 0;
73}
74
0153028a
LP
75void 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;
968f3196 83 unsigned n = 0, j, tn = (unsigned) -1;
0153028a 84 const char *p;
874bc134 85 size_t remaining, m = 0, entry_size = 0;
0153028a
LP
86 int priority = LOG_INFO;
87 char *identifier = NULL, *message = NULL;
968f3196 88 pid_t object_pid = 0;
0153028a
LP
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 */
874bc134
ZJS
109
110 if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */
8c0b803b 111 log_debug("Entry is too big with %u properties and %zu bytes, ignoring.", n, entry_size);
874bc134
ZJS
112 continue;
113 }
114
968f3196 115 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
0153028a
LP
116 n = 0;
117 priority = LOG_INFO;
874bc134 118 entry_size = 0;
0153028a
LP
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
f6422def 135 /* n received properties, +1 for _TRANSPORT */
8c0b803b 136 if (!GREEDY_REALLOC(iovec, m, n + 1 + N_IOVEC_META_FIELDS + !!object_pid * N_IOVEC_OBJECT_FIELDS)) {
968f3196
ZJS
137 log_oom();
138 break;
0153028a
LP
139 }
140
141 q = memchr(p, '=', e - p);
142 if (q) {
d18d46ec 143 if (valid_user_field(p, q - p, false)) {
0153028a
LP
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;
874bc134 154 entry_size += iovec[n].iov_len;
a174f94d 155 n++;
0153028a
LP
156
157 /* We need to determine the priority
158 * of this entry for the rate limiting
159 * logic */
160 if (l == 10 &&
2a0e0692 161 startswith(p, "PRIORITY=") &&
0153028a
LP
162 p[9] >= '0' && p[9] <= '9')
163 priority = (priority & LOG_FACMASK) | (p[9] - '0');
164
165 else if (l == 17 &&
2a0e0692 166 startswith(p, "SYSLOG_FACILITY=") &&
0153028a
LP
167 p[16] >= '0' && p[16] <= '9')
168 priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
169
170 else if (l == 18 &&
2a0e0692 171 startswith(p, "SYSLOG_FACILITY=") &&
0153028a
LP
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 &&
2a0e0692 177 startswith(p, "SYSLOG_IDENTIFIER=")) {
0153028a
LP
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 &&
2a0e0692 186 startswith(p, "MESSAGE=")) {
0153028a
LP
187 char *t;
188
189 t = strndup(p + 8, l - 8);
190 if (t) {
191 free(message);
192 message = t;
193 }
968f3196
ZJS
194 } else if (l > strlen("OBJECT_PID=") &&
195 l < strlen("OBJECT_PID=") + DECIMAL_STR_MAX(pid_t) &&
2a0e0692 196 startswith(p, "OBJECT_PID=") &&
968f3196
ZJS
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);
0153028a
LP
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
505b6a61 223 if (l > DATA_SIZE_MAX) {
fa1c4b51 224 log_debug("Received binary data block of %"PRIu64" bytes is too large, ignoring.", l);
505b6a61
LP
225 break;
226 }
227
228 if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
0153028a
LP
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
d18d46ec 244 if (valid_user_field(p, e - p, false)) {
0153028a
LP
245 iovec[n].iov_base = k;
246 iovec[n].iov_len = (e - p) + 1 + l;
874bc134 247 entry_size += iovec[n].iov_len;
a174f94d 248 n++;
0153028a
LP
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");
874bc134
ZJS
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 }
0153028a
LP
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);
40b71e89
ST
279
280 if (s->forward_to_wall)
281 server_forward_wall(s, priority, identifier, message, ucred);
0153028a
LP
282 }
283
968f3196 284 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
0153028a
LP
285
286finish:
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
301void 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;
1dfa7e79 309 _cleanup_free_ void *p = NULL;
0153028a 310 ssize_t n;
1dfa7e79 311 int r;
0153028a
LP
312
313 assert(s);
314 assert(fd >= 0);
315
1dfa7e79
LP
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
0b507b17 341 if (!filename_is_safe(e)) {
1dfa7e79
LP
342 log_error("Received file in subdirectory of allowed directories. Refusing.");
343 return;
344 }
345 }
346
0153028a
LP
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);
0153028a
LP
381}
382
383int server_open_native_socket(Server*s) {
0153028a 384 int one, r;
0153028a
LP
385
386 assert(s);
387
388 if (s->native_fd < 0) {
f7a5bb28
ZJS
389 union sockaddr_union sa = {
390 .un.sun_family = AF_UNIX,
391 .un.sun_path = "/run/systemd/journal/socket",
392 };
0153028a
LP
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
0153028a
LP
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) {
6b9732b2 404 log_error("bind(%s) failed: %m", sa.un.sun_path);
0153028a
LP
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
6baa7db0 420 if (mac_selinux_use()) {
d682b3a7
LP
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 }
0153028a
LP
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
151b9b96 435 r = sd_event_add_io(s->event, &s->native_event_source, s->native_fd, EPOLLIN, process_datagram, s);
f9a810be
LP
436 if (r < 0) {
437 log_error("Failed to add native server fd to event loop: %s", strerror(-r));
438 return r;
0153028a
LP
439 }
440
441 return 0;
442}