]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journald-native.c
journald: don't accept arbitrarily sized journal data fields
[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 "journald.h"
28 #include "journald-native.h"
29 #include "journald-kmsg.h"
30 #include "journald-console.h"
31 #include "journald-syslog.h"
32
33 #define ENTRY_SIZE_MAX (1024*1024*64)
34 #define DATA_SIZE_MAX (1024*1024*64)
35
36 static bool valid_user_field(const char *p, size_t l) {
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 (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 void server_process_native_message(
72 Server *s,
73 const void *buffer, size_t buffer_size,
74 struct ucred *ucred,
75 struct timeval *tv,
76 const char *label, size_t label_len) {
77
78 struct iovec *iovec = NULL;
79 unsigned n = 0, m = 0, j, tn = (unsigned) -1;
80 const char *p;
81 size_t remaining;
82 int priority = LOG_INFO;
83 char *identifier = NULL, *message = NULL;
84
85 assert(s);
86 assert(buffer || buffer_size == 0);
87
88 p = buffer;
89 remaining = buffer_size;
90
91 while (remaining > 0) {
92 const char *e, *q;
93
94 e = memchr(p, '\n', remaining);
95
96 if (!e) {
97 /* Trailing noise, let's ignore it, and flush what we collected */
98 log_debug("Received message with trailing noise, ignoring.");
99 break;
100 }
101
102 if (e == p) {
103 /* Entry separator */
104 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority);
105 n = 0;
106 priority = LOG_INFO;
107
108 p++;
109 remaining--;
110 continue;
111 }
112
113 if (*p == '.' || *p == '#') {
114 /* Ignore control commands for now, and
115 * comments too. */
116 remaining -= (e - p) + 1;
117 p = e + 1;
118 continue;
119 }
120
121 /* A property follows */
122
123 if (n+N_IOVEC_META_FIELDS >= m) {
124 struct iovec *c;
125 unsigned u;
126
127 u = MAX((n+N_IOVEC_META_FIELDS+1) * 2U, 4U);
128 c = realloc(iovec, u * sizeof(struct iovec));
129 if (!c) {
130 log_oom();
131 break;
132 }
133
134 iovec = c;
135 m = u;
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 memcmp(p, "PRIORITY=", 9) == 0 &&
158 p[9] >= '0' && p[9] <= '9')
159 priority = (priority & LOG_FACMASK) | (p[9] - '0');
160
161 else if (l == 17 &&
162 memcmp(p, "SYSLOG_FACILITY=", 16) == 0 &&
163 p[16] >= '0' && p[16] <= '9')
164 priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
165
166 else if (l == 18 &&
167 memcmp(p, "SYSLOG_FACILITY=", 16) == 0 &&
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 memcmp(p, "SYSLOG_IDENTIFIER=", 18) == 0) {
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 memcmp(p, "MESSAGE=", 8) == 0) {
183 char *t;
184
185 t = strndup(p + 8, l - 8);
186 if (t) {
187 free(message);
188 message = t;
189 }
190 }
191 }
192
193 remaining -= (e - p) + 1;
194 p = e + 1;
195 continue;
196 } else {
197 le64_t l_le;
198 uint64_t l;
199 char *k;
200
201 if (remaining < e - p + 1 + sizeof(uint64_t) + 1) {
202 log_debug("Failed to parse message, ignoring.");
203 break;
204 }
205
206 memcpy(&l_le, e + 1, sizeof(uint64_t));
207 l = le64toh(l_le);
208
209 if (l > DATA_SIZE_MAX) {
210 log_debug("Received binary data block too large, ignoring.");
211 break;
212 }
213
214 if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
215 e[1+sizeof(uint64_t)+l] != '\n') {
216 log_debug("Failed to parse message, ignoring.");
217 break;
218 }
219
220 k = malloc((e - p) + 1 + l);
221 if (!k) {
222 log_oom();
223 break;
224 }
225
226 memcpy(k, p, e - p);
227 k[e - p] = '=';
228 memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
229
230 if (valid_user_field(p, e - p)) {
231 iovec[n].iov_base = k;
232 iovec[n].iov_len = (e - p) + 1 + l;
233 n++;
234 } else
235 free(k);
236
237 remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1;
238 p = e + 1 + sizeof(uint64_t) + l + 1;
239 }
240 }
241
242 if (n <= 0)
243 goto finish;
244
245 tn = n++;
246 IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal");
247
248 if (message) {
249 if (s->forward_to_syslog)
250 server_forward_syslog(s, priority, identifier, message, ucred, tv);
251
252 if (s->forward_to_kmsg)
253 server_forward_kmsg(s, priority, identifier, message, ucred);
254
255 if (s->forward_to_console)
256 server_forward_console(s, priority, identifier, message, ucred);
257 }
258
259 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority);
260
261 finish:
262 for (j = 0; j < n; j++) {
263 if (j == tn)
264 continue;
265
266 if (iovec[j].iov_base < buffer ||
267 (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size)
268 free(iovec[j].iov_base);
269 }
270
271 free(iovec);
272 free(identifier);
273 free(message);
274 }
275
276 void server_process_native_file(
277 Server *s,
278 int fd,
279 struct ucred *ucred,
280 struct timeval *tv,
281 const char *label, size_t label_len) {
282
283 struct stat st;
284 void *p;
285 ssize_t n;
286
287 assert(s);
288 assert(fd >= 0);
289
290 /* Data is in the passed file, since it didn't fit in a
291 * datagram. We can't map the file here, since clients might
292 * then truncate it and trigger a SIGBUS for us. So let's
293 * stupidly read it */
294
295 if (fstat(fd, &st) < 0) {
296 log_error("Failed to stat passed file, ignoring: %m");
297 return;
298 }
299
300 if (!S_ISREG(st.st_mode)) {
301 log_error("File passed is not regular. Ignoring.");
302 return;
303 }
304
305 if (st.st_size <= 0)
306 return;
307
308 if (st.st_size > ENTRY_SIZE_MAX) {
309 log_error("File passed too large. Ignoring.");
310 return;
311 }
312
313 p = malloc(st.st_size);
314 if (!p) {
315 log_oom();
316 return;
317 }
318
319 n = pread(fd, p, st.st_size, 0);
320 if (n < 0)
321 log_error("Failed to read file, ignoring: %s", strerror(-n));
322 else if (n > 0)
323 server_process_native_message(s, p, n, ucred, tv, label, label_len);
324
325 free(p);
326 }
327
328 int server_open_native_socket(Server*s) {
329 union sockaddr_union sa;
330 int one, r;
331 struct epoll_event ev;
332
333 assert(s);
334
335 if (s->native_fd < 0) {
336
337 s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
338 if (s->native_fd < 0) {
339 log_error("socket() failed: %m");
340 return -errno;
341 }
342
343 zero(sa);
344 sa.un.sun_family = AF_UNIX;
345 strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path));
346
347 unlink(sa.un.sun_path);
348
349 r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
350 if (r < 0) {
351 log_error("bind() failed: %m");
352 return -errno;
353 }
354
355 chmod(sa.un.sun_path, 0666);
356 } else
357 fd_nonblock(s->native_fd, 1);
358
359 one = 1;
360 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
361 if (r < 0) {
362 log_error("SO_PASSCRED failed: %m");
363 return -errno;
364 }
365
366 #ifdef HAVE_SELINUX
367 one = 1;
368 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
369 if (r < 0)
370 log_warning("SO_PASSSEC failed: %m");
371 #endif
372
373 one = 1;
374 r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
375 if (r < 0) {
376 log_error("SO_TIMESTAMP failed: %m");
377 return -errno;
378 }
379
380 zero(ev);
381 ev.events = EPOLLIN;
382 ev.data.fd = s->native_fd;
383 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->native_fd, &ev) < 0) {
384 log_error("Failed to add native server fd to epoll object: %m");
385 return -errno;
386 }
387
388 return 0;
389 }