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