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