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