]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journald-native.c
login: make sd_session_get_vt() actually work
[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"
d025f1e4 28#include "journald-server.h"
0153028a
LP
29#include "journald-native.h"
30#include "journald-kmsg.h"
31#include "journald-console.h"
32#include "journald-syslog.h"
33
c4aa09b0
LP
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)
0153028a
LP
38
39static 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
968f3196
ZJS
74static bool allow_object_pid(struct ucred *ucred) {
75 return ucred && ucred->uid == 0;
76}
77
0153028a
LP
78void server_process_native_message(
79 Server *s,
80 const void *buffer, size_t buffer_size,
81 struct ucred *ucred,
82 struct timeval *tv,
83 const char *label, size_t label_len) {
84
85 struct iovec *iovec = NULL;
968f3196 86 unsigned n = 0, j, tn = (unsigned) -1;
0153028a 87 const char *p;
968f3196 88 size_t remaining, m = 0;
0153028a
LP
89 int priority = LOG_INFO;
90 char *identifier = NULL, *message = NULL;
968f3196 91 pid_t object_pid = 0;
0153028a
LP
92
93 assert(s);
94 assert(buffer || buffer_size == 0);
95
96 p = buffer;
97 remaining = buffer_size;
98
99 while (remaining > 0) {
100 const char *e, *q;
101
102 e = memchr(p, '\n', remaining);
103
104 if (!e) {
105 /* Trailing noise, let's ignore it, and flush what we collected */
106 log_debug("Received message with trailing noise, ignoring.");
107 break;
108 }
109
110 if (e == p) {
111 /* Entry separator */
968f3196 112 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
0153028a
LP
113 n = 0;
114 priority = LOG_INFO;
115
116 p++;
117 remaining--;
118 continue;
119 }
120
121 if (*p == '.' || *p == '#') {
122 /* Ignore control commands for now, and
123 * comments too. */
124 remaining -= (e - p) + 1;
125 p = e + 1;
126 continue;
127 }
128
129 /* A property follows */
130
f6422def 131 /* n received properties, +1 for _TRANSPORT */
968f3196
ZJS
132 if (!GREEDY_REALLOC(iovec, m, n + 1 + N_IOVEC_META_FIELDS +
133 !!object_pid * N_IOVEC_OBJECT_FIELDS)) {
134 log_oom();
135 break;
0153028a
LP
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 &&
2a0e0692 157 startswith(p, "PRIORITY=") &&
0153028a
LP
158 p[9] >= '0' && p[9] <= '9')
159 priority = (priority & LOG_FACMASK) | (p[9] - '0');
160
161 else if (l == 17 &&
2a0e0692 162 startswith(p, "SYSLOG_FACILITY=") &&
0153028a
LP
163 p[16] >= '0' && p[16] <= '9')
164 priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
165
166 else if (l == 18 &&
2a0e0692 167 startswith(p, "SYSLOG_FACILITY=") &&
0153028a
LP
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 &&
2a0e0692 173 startswith(p, "SYSLOG_IDENTIFIER=")) {
0153028a
LP
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 &&
2a0e0692 182 startswith(p, "MESSAGE=")) {
0153028a
LP
183 char *t;
184
185 t = strndup(p + 8, l - 8);
186 if (t) {
187 free(message);
188 message = t;
189 }
968f3196
ZJS
190 } else if (l > strlen("OBJECT_PID=") &&
191 l < strlen("OBJECT_PID=") + DECIMAL_STR_MAX(pid_t) &&
2a0e0692 192 startswith(p, "OBJECT_PID=") &&
968f3196
ZJS
193 allow_object_pid(ucred)) {
194 char buf[DECIMAL_STR_MAX(pid_t)];
195 memcpy(buf, p + strlen("OBJECT_PID="), l - strlen("OBJECT_PID="));
196 char_array_0(buf);
197
198 /* ignore error */
199 parse_pid(buf, &object_pid);
0153028a
LP
200 }
201 }
202
203 remaining -= (e - p) + 1;
204 p = e + 1;
205 continue;
206 } else {
207 le64_t l_le;
208 uint64_t l;
209 char *k;
210
211 if (remaining < e - p + 1 + sizeof(uint64_t) + 1) {
212 log_debug("Failed to parse message, ignoring.");
213 break;
214 }
215
216 memcpy(&l_le, e + 1, sizeof(uint64_t));
217 l = le64toh(l_le);
218
505b6a61
LP
219 if (l > DATA_SIZE_MAX) {
220 log_debug("Received binary data block too large, ignoring.");
221 break;
222 }
223
224 if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
0153028a
LP
225 e[1+sizeof(uint64_t)+l] != '\n') {
226 log_debug("Failed to parse message, ignoring.");
227 break;
228 }
229
230 k = malloc((e - p) + 1 + l);
231 if (!k) {
232 log_oom();
233 break;
234 }
235
236 memcpy(k, p, e - p);
237 k[e - p] = '=';
238 memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
239
240 if (valid_user_field(p, e - p)) {
241 iovec[n].iov_base = k;
242 iovec[n].iov_len = (e - p) + 1 + l;
243 n++;
244 } else
245 free(k);
246
247 remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1;
248 p = e + 1 + sizeof(uint64_t) + l + 1;
249 }
250 }
251
252 if (n <= 0)
253 goto finish;
254
255 tn = n++;
256 IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal");
257
258 if (message) {
259 if (s->forward_to_syslog)
260 server_forward_syslog(s, priority, identifier, message, ucred, tv);
261
262 if (s->forward_to_kmsg)
263 server_forward_kmsg(s, priority, identifier, message, ucred);
264
265 if (s->forward_to_console)
266 server_forward_console(s, priority, identifier, message, ucred);
267 }
268
968f3196 269 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
0153028a
LP
270
271finish:
272 for (j = 0; j < n; j++) {
273 if (j == tn)
274 continue;
275
276 if (iovec[j].iov_base < buffer ||
277 (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size)
278 free(iovec[j].iov_base);
279 }
280
281 free(iovec);
282 free(identifier);
283 free(message);
284}
285
286void server_process_native_file(
287 Server *s,
288 int fd,
289 struct ucred *ucred,
290 struct timeval *tv,
291 const char *label, size_t label_len) {
292
293 struct stat st;
1dfa7e79 294 _cleanup_free_ void *p = NULL;
0153028a 295 ssize_t n;
1dfa7e79 296 int r;
0153028a
LP
297
298 assert(s);
299 assert(fd >= 0);
300
1dfa7e79
LP
301 if (!ucred || ucred->uid != 0) {
302 _cleanup_free_ char *sl = NULL, *k = NULL;
303 const char *e;
304
305 if (asprintf(&sl, "/proc/self/fd/%i", fd) < 0) {
306 log_oom();
307 return;
308 }
309
310 r = readlink_malloc(sl, &k);
311 if (r < 0) {
312 log_error("readlink(%s) failed: %m", sl);
313 return;
314 }
315
316 e = path_startswith(k, "/dev/shm/");
317 if (!e)
318 e = path_startswith(k, "/tmp/");
319 if (!e)
320 e = path_startswith(k, "/var/tmp/");
321 if (!e) {
322 log_error("Received file outside of allowed directories. Refusing.");
323 return;
324 }
325
0b507b17 326 if (!filename_is_safe(e)) {
1dfa7e79
LP
327 log_error("Received file in subdirectory of allowed directories. Refusing.");
328 return;
329 }
330 }
331
0153028a
LP
332 /* Data is in the passed file, since it didn't fit in a
333 * datagram. We can't map the file here, since clients might
334 * then truncate it and trigger a SIGBUS for us. So let's
335 * stupidly read it */
336
337 if (fstat(fd, &st) < 0) {
338 log_error("Failed to stat passed file, ignoring: %m");
339 return;
340 }
341
342 if (!S_ISREG(st.st_mode)) {
343 log_error("File passed is not regular. Ignoring.");
344 return;
345 }
346
347 if (st.st_size <= 0)
348 return;
349
350 if (st.st_size > ENTRY_SIZE_MAX) {
351 log_error("File passed too large. Ignoring.");
352 return;
353 }
354
355 p = malloc(st.st_size);
356 if (!p) {
357 log_oom();
358 return;
359 }
360
361 n = pread(fd, p, st.st_size, 0);
362 if (n < 0)
363 log_error("Failed to read file, ignoring: %s", strerror(-n));
364 else if (n > 0)
365 server_process_native_message(s, p, n, ucred, tv, label, label_len);
0153028a
LP
366}
367
368int server_open_native_socket(Server*s) {
369 union sockaddr_union sa;
370 int one, r;
371 struct epoll_event ev;
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
407 one = 1;
be2155ce 408 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
0153028a
LP
409 if (r < 0)
410 log_warning("SO_PASSSEC failed: %m");
411#endif
412
413 one = 1;
414 r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
415 if (r < 0) {
416 log_error("SO_TIMESTAMP failed: %m");
417 return -errno;
418 }
419
420 zero(ev);
421 ev.events = EPOLLIN;
422 ev.data.fd = s->native_fd;
423 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->native_fd, &ev) < 0) {
424 log_error("Failed to add native server fd to epoll object: %m");
425 return -errno;
426 }
427
428 return 0;
429}