]> git.proxmox.com Git - systemd.git/blame - src/journal/journald-native.c
Imported Upstream version 217
[systemd.git] / src / journal / journald-native.c
CommitLineData
663996b3
MS
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"
60f067b4 28#include "selinux-util.h"
663996b3
MS
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"
60f067b4 34#include "journald-wall.h"
663996b3 35
60f067b4 36bool valid_user_field(const char *p, size_t l, bool allow_protected) {
663996b3
MS
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 */
60f067b4 54 if (!allow_protected && p[0] == '_')
663996b3
MS
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++)
60f067b4
JS
63 if ((*a < 'A' || *a > 'Z') &&
64 (*a < '0' || *a > '9') &&
65 *a != '_')
663996b3
MS
66 return false;
67
68 return true;
69}
70
14228c0d
MB
71static bool allow_object_pid(struct ucred *ucred) {
72 return ucred && ucred->uid == 0;
73}
74
663996b3
MS
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;
14228c0d 83 unsigned n = 0, j, tn = (unsigned) -1;
663996b3 84 const char *p;
5eef597e 85 size_t remaining, m = 0, entry_size = 0;
663996b3
MS
86 int priority = LOG_INFO;
87 char *identifier = NULL, *message = NULL;
14228c0d 88 pid_t object_pid = 0;
663996b3
MS
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 */
5eef597e
MP
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
14228c0d 115 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
663996b3
MS
116 n = 0;
117 priority = LOG_INFO;
5eef597e 118 entry_size = 0;
663996b3
MS
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 */
5eef597e 136 if (!GREEDY_REALLOC(iovec, m, n + 1 + N_IOVEC_META_FIELDS + !!object_pid * N_IOVEC_OBJECT_FIELDS)) {
14228c0d
MB
137 log_oom();
138 break;
663996b3
MS
139 }
140
141 q = memchr(p, '=', e - p);
142 if (q) {
60f067b4 143 if (valid_user_field(p, q - p, false)) {
663996b3
MS
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;
5eef597e 154 entry_size += iovec[n].iov_len;
663996b3
MS
155 n++;
156
157 /* We need to determine the priority
158 * of this entry for the rate limiting
159 * logic */
160 if (l == 10 &&
14228c0d 161 startswith(p, "PRIORITY=") &&
663996b3
MS
162 p[9] >= '0' && p[9] <= '9')
163 priority = (priority & LOG_FACMASK) | (p[9] - '0');
164
165 else if (l == 17 &&
14228c0d 166 startswith(p, "SYSLOG_FACILITY=") &&
663996b3
MS
167 p[16] >= '0' && p[16] <= '9')
168 priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
169
170 else if (l == 18 &&
14228c0d 171 startswith(p, "SYSLOG_FACILITY=") &&
663996b3
MS
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 &&
14228c0d 177 startswith(p, "SYSLOG_IDENTIFIER=")) {
663996b3
MS
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 &&
14228c0d 186 startswith(p, "MESSAGE=")) {
663996b3
MS
187 char *t;
188
189 t = strndup(p + 8, l - 8);
190 if (t) {
191 free(message);
192 message = t;
193 }
14228c0d
MB
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);
663996b3
MS
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) {
5eef597e 224 log_debug("Received binary data block of %"PRIu64" bytes is too large, ignoring.", l);
663996b3
MS
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
60f067b4 244 if (valid_user_field(p, e - p, false)) {
663996b3
MS
245 iovec[n].iov_base = k;
246 iovec[n].iov_len = (e - p) + 1 + l;
5eef597e 247 entry_size += iovec[n].iov_len;
663996b3
MS
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");
5eef597e
MP
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 }
663996b3
MS
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);
60f067b4
JS
279
280 if (s->forward_to_wall)
281 server_forward_wall(s, priority, identifier, message, ucred);
663996b3
MS
282 }
283
14228c0d 284 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
663996b3
MS
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;
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
383int server_open_native_socket(Server*s) {
663996b3 384 int one, r;
663996b3
MS
385
386 assert(s);
387
388 if (s->native_fd < 0) {
5eef597e
MP
389 union sockaddr_union sa = {
390 .un.sun_family = AF_UNIX,
391 .un.sun_path = "/run/systemd/journal/socket",
392 };
663996b3
MS
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
663996b3
MS
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) {
5eef597e 404 log_error("bind(%s) failed: %m", sa.un.sun_path);
663996b3
MS
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
5eef597e 420 if (mac_selinux_use()) {
60f067b4
JS
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 }
663996b3
MS
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
60f067b4
JS
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;
663996b3
MS
439 }
440
441 return 0;
442}