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