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