]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journald-native.c
journald: only accept fds from certain directories
[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.h"
29 #include "journald-native.h"
30 #include "journald-kmsg.h"
31 #include "journald-console.h"
32 #include "journald-syslog.h"
33
34 #define ENTRY_SIZE_MAX (1024*1024*64)
35 #define DATA_SIZE_MAX (1024*1024*64)
36
37 static bool valid_user_field(const char *p, size_t l) {
38 const char *a;
39
40 /* We kinda enforce POSIX syntax recommendations for
41 environment variables here, but make a couple of additional
42 requirements.
43
44 http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
45
46 /* No empty field names */
47 if (l <= 0)
48 return false;
49
50 /* Don't allow names longer than 64 chars */
51 if (l > 64)
52 return false;
53
54 /* Variables starting with an underscore are protected */
55 if (p[0] == '_')
56 return false;
57
58 /* Don't allow digits as first character */
59 if (p[0] >= '0' && p[0] <= '9')
60 return false;
61
62 /* Only allow A-Z0-9 and '_' */
63 for (a = p; a < p + l; a++)
64 if (!((*a >= 'A' && *a <= 'Z') ||
65 (*a >= '0' && *a <= '9') ||
66 *a == '_'))
67 return false;
68
69 return true;
70 }
71
72 void server_process_native_message(
73 Server *s,
74 const void *buffer, size_t buffer_size,
75 struct ucred *ucred,
76 struct timeval *tv,
77 const char *label, size_t label_len) {
78
79 struct iovec *iovec = NULL;
80 unsigned n = 0, m = 0, j, tn = (unsigned) -1;
81 const char *p;
82 size_t remaining;
83 int priority = LOG_INFO;
84 char *identifier = NULL, *message = NULL;
85
86 assert(s);
87 assert(buffer || buffer_size == 0);
88
89 p = buffer;
90 remaining = buffer_size;
91
92 while (remaining > 0) {
93 const char *e, *q;
94
95 e = memchr(p, '\n', remaining);
96
97 if (!e) {
98 /* Trailing noise, let's ignore it, and flush what we collected */
99 log_debug("Received message with trailing noise, ignoring.");
100 break;
101 }
102
103 if (e == p) {
104 /* Entry separator */
105 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority);
106 n = 0;
107 priority = LOG_INFO;
108
109 p++;
110 remaining--;
111 continue;
112 }
113
114 if (*p == '.' || *p == '#') {
115 /* Ignore control commands for now, and
116 * comments too. */
117 remaining -= (e - p) + 1;
118 p = e + 1;
119 continue;
120 }
121
122 /* A property follows */
123
124 if (n+N_IOVEC_META_FIELDS >= m) {
125 struct iovec *c;
126 unsigned u;
127
128 u = MAX((n+N_IOVEC_META_FIELDS+1) * 2U, 4U);
129 c = realloc(iovec, u * sizeof(struct iovec));
130 if (!c) {
131 log_oom();
132 break;
133 }
134
135 iovec = c;
136 m = u;
137 }
138
139 q = memchr(p, '=', e - p);
140 if (q) {
141 if (valid_user_field(p, q - p)) {
142 size_t l;
143
144 l = e - p;
145
146 /* If the field name starts with an
147 * underscore, skip the variable,
148 * since that indidates a trusted
149 * field */
150 iovec[n].iov_base = (char*) p;
151 iovec[n].iov_len = l;
152 n++;
153
154 /* We need to determine the priority
155 * of this entry for the rate limiting
156 * logic */
157 if (l == 10 &&
158 memcmp(p, "PRIORITY=", 9) == 0 &&
159 p[9] >= '0' && p[9] <= '9')
160 priority = (priority & LOG_FACMASK) | (p[9] - '0');
161
162 else if (l == 17 &&
163 memcmp(p, "SYSLOG_FACILITY=", 16) == 0 &&
164 p[16] >= '0' && p[16] <= '9')
165 priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
166
167 else if (l == 18 &&
168 memcmp(p, "SYSLOG_FACILITY=", 16) == 0 &&
169 p[16] >= '0' && p[16] <= '9' &&
170 p[17] >= '0' && p[17] <= '9')
171 priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3);
172
173 else if (l >= 19 &&
174 memcmp(p, "SYSLOG_IDENTIFIER=", 18) == 0) {
175 char *t;
176
177 t = strndup(p + 18, l - 18);
178 if (t) {
179 free(identifier);
180 identifier = t;
181 }
182 } else if (l >= 8 &&
183 memcmp(p, "MESSAGE=", 8) == 0) {
184 char *t;
185
186 t = strndup(p + 8, l - 8);
187 if (t) {
188 free(message);
189 message = t;
190 }
191 }
192 }
193
194 remaining -= (e - p) + 1;
195 p = e + 1;
196 continue;
197 } else {
198 le64_t l_le;
199 uint64_t l;
200 char *k;
201
202 if (remaining < e - p + 1 + sizeof(uint64_t) + 1) {
203 log_debug("Failed to parse message, ignoring.");
204 break;
205 }
206
207 memcpy(&l_le, e + 1, sizeof(uint64_t));
208 l = le64toh(l_le);
209
210 if (l > DATA_SIZE_MAX) {
211 log_debug("Received binary data block too large, ignoring.");
212 break;
213 }
214
215 if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
216 e[1+sizeof(uint64_t)+l] != '\n') {
217 log_debug("Failed to parse message, ignoring.");
218 break;
219 }
220
221 k = malloc((e - p) + 1 + l);
222 if (!k) {
223 log_oom();
224 break;
225 }
226
227 memcpy(k, p, e - p);
228 k[e - p] = '=';
229 memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
230
231 if (valid_user_field(p, e - p)) {
232 iovec[n].iov_base = k;
233 iovec[n].iov_len = (e - p) + 1 + l;
234 n++;
235 } else
236 free(k);
237
238 remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1;
239 p = e + 1 + sizeof(uint64_t) + l + 1;
240 }
241 }
242
243 if (n <= 0)
244 goto finish;
245
246 tn = n++;
247 IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal");
248
249 if (message) {
250 if (s->forward_to_syslog)
251 server_forward_syslog(s, priority, identifier, message, ucred, tv);
252
253 if (s->forward_to_kmsg)
254 server_forward_kmsg(s, priority, identifier, message, ucred);
255
256 if (s->forward_to_console)
257 server_forward_console(s, priority, identifier, message, ucred);
258 }
259
260 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority);
261
262 finish:
263 for (j = 0; j < n; j++) {
264 if (j == tn)
265 continue;
266
267 if (iovec[j].iov_base < buffer ||
268 (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size)
269 free(iovec[j].iov_base);
270 }
271
272 free(iovec);
273 free(identifier);
274 free(message);
275 }
276
277 void server_process_native_file(
278 Server *s,
279 int fd,
280 struct ucred *ucred,
281 struct timeval *tv,
282 const char *label, size_t label_len) {
283
284 struct stat st;
285 _cleanup_free_ void *p = NULL;
286 ssize_t n;
287 int r;
288
289 assert(s);
290 assert(fd >= 0);
291
292 if (!ucred || ucred->uid != 0) {
293 _cleanup_free_ char *sl = NULL, *k = NULL;
294 const char *e;
295
296 if (asprintf(&sl, "/proc/self/fd/%i", fd) < 0) {
297 log_oom();
298 return;
299 }
300
301 r = readlink_malloc(sl, &k);
302 if (r < 0) {
303 log_error("readlink(%s) failed: %m", sl);
304 return;
305 }
306
307 e = path_startswith(k, "/dev/shm/");
308 if (!e)
309 e = path_startswith(k, "/tmp/");
310 if (!e)
311 e = path_startswith(k, "/var/tmp/");
312 if (!e) {
313 log_error("Received file outside of allowed directories. Refusing.");
314 return;
315 }
316
317 if (strchr(e, '/')) {
318 log_error("Received file in subdirectory of allowed directories. Refusing.");
319 return;
320 }
321 }
322
323 /* Data is in the passed file, since it didn't fit in a
324 * datagram. We can't map the file here, since clients might
325 * then truncate it and trigger a SIGBUS for us. So let's
326 * stupidly read it */
327
328 if (fstat(fd, &st) < 0) {
329 log_error("Failed to stat passed file, ignoring: %m");
330 return;
331 }
332
333 if (!S_ISREG(st.st_mode)) {
334 log_error("File passed is not regular. Ignoring.");
335 return;
336 }
337
338 if (st.st_size <= 0)
339 return;
340
341 if (st.st_size > ENTRY_SIZE_MAX) {
342 log_error("File passed too large. Ignoring.");
343 return;
344 }
345
346 p = malloc(st.st_size);
347 if (!p) {
348 log_oom();
349 return;
350 }
351
352 n = pread(fd, p, st.st_size, 0);
353 if (n < 0)
354 log_error("Failed to read file, ignoring: %s", strerror(-n));
355 else if (n > 0)
356 server_process_native_message(s, p, n, ucred, tv, label, label_len);
357 }
358
359 int server_open_native_socket(Server*s) {
360 union sockaddr_union sa;
361 int one, r;
362 struct epoll_event ev;
363
364 assert(s);
365
366 if (s->native_fd < 0) {
367
368 s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
369 if (s->native_fd < 0) {
370 log_error("socket() failed: %m");
371 return -errno;
372 }
373
374 zero(sa);
375 sa.un.sun_family = AF_UNIX;
376 strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path));
377
378 unlink(sa.un.sun_path);
379
380 r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
381 if (r < 0) {
382 log_error("bind() failed: %m");
383 return -errno;
384 }
385
386 chmod(sa.un.sun_path, 0666);
387 } else
388 fd_nonblock(s->native_fd, 1);
389
390 one = 1;
391 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
392 if (r < 0) {
393 log_error("SO_PASSCRED failed: %m");
394 return -errno;
395 }
396
397 #ifdef HAVE_SELINUX
398 one = 1;
399 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
400 if (r < 0)
401 log_warning("SO_PASSSEC failed: %m");
402 #endif
403
404 one = 1;
405 r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
406 if (r < 0) {
407 log_error("SO_TIMESTAMP failed: %m");
408 return -errno;
409 }
410
411 zero(ev);
412 ev.events = EPOLLIN;
413 ev.data.fd = s->native_fd;
414 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->native_fd, &ev) < 0) {
415 log_error("Failed to add native server fd to epoll object: %m");
416 return -errno;
417 }
418
419 return 0;
420 }