]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journald-native.c
Remove unnecessary casts in printfs
[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 "selinux-util.h"
29 #include "journald-server.h"
30 #include "journald-native.h"
31 #include "journald-kmsg.h"
32 #include "journald-console.h"
33 #include "journald-syslog.h"
34 #include "journald-wall.h"
35
36 bool valid_user_field(const char *p, size_t l, bool allow_protected) {
37 const char *a;
38
39 /* We kinda enforce POSIX syntax recommendations for
40 environment variables here, but make a couple of additional
41 requirements.
42
43 http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
44
45 /* No empty field names */
46 if (l <= 0)
47 return false;
48
49 /* Don't allow names longer than 64 chars */
50 if (l > 64)
51 return false;
52
53 /* Variables starting with an underscore are protected */
54 if (!allow_protected && p[0] == '_')
55 return false;
56
57 /* Don't allow digits as first character */
58 if (p[0] >= '0' && p[0] <= '9')
59 return false;
60
61 /* Only allow A-Z0-9 and '_' */
62 for (a = p; a < p + l; a++)
63 if ((*a < 'A' || *a > 'Z') &&
64 (*a < '0' || *a > '9') &&
65 *a != '_')
66 return false;
67
68 return true;
69 }
70
71 static bool allow_object_pid(struct ucred *ucred) {
72 return ucred && ucred->uid == 0;
73 }
74
75 void server_process_native_message(
76 Server *s,
77 const void *buffer, size_t buffer_size,
78 struct ucred *ucred,
79 struct timeval *tv,
80 const char *label, size_t label_len) {
81
82 struct iovec *iovec = NULL;
83 unsigned n = 0, j, tn = (unsigned) -1;
84 const char *p;
85 size_t remaining, m = 0;
86 int priority = LOG_INFO;
87 char *identifier = NULL, *message = NULL;
88 pid_t object_pid = 0;
89
90 assert(s);
91 assert(buffer || buffer_size == 0);
92
93 p = buffer;
94 remaining = buffer_size;
95
96 while (remaining > 0) {
97 const char *e, *q;
98
99 e = memchr(p, '\n', remaining);
100
101 if (!e) {
102 /* Trailing noise, let's ignore it, and flush what we collected */
103 log_debug("Received message with trailing noise, ignoring.");
104 break;
105 }
106
107 if (e == p) {
108 /* Entry separator */
109 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
110 n = 0;
111 priority = LOG_INFO;
112
113 p++;
114 remaining--;
115 continue;
116 }
117
118 if (*p == '.' || *p == '#') {
119 /* Ignore control commands for now, and
120 * comments too. */
121 remaining -= (e - p) + 1;
122 p = e + 1;
123 continue;
124 }
125
126 /* A property follows */
127
128 /* n received properties, +1 for _TRANSPORT */
129 if (!GREEDY_REALLOC(iovec, m, n + 1 + N_IOVEC_META_FIELDS +
130 !!object_pid * N_IOVEC_OBJECT_FIELDS)) {
131 log_oom();
132 break;
133 }
134
135 q = memchr(p, '=', e - p);
136 if (q) {
137 if (valid_user_field(p, q - p, false)) {
138 size_t l;
139
140 l = e - p;
141
142 /* If the field name starts with an
143 * underscore, skip the variable,
144 * since that indidates a trusted
145 * field */
146 iovec[n].iov_base = (char*) p;
147 iovec[n].iov_len = l;
148 n++;
149
150 /* We need to determine the priority
151 * of this entry for the rate limiting
152 * logic */
153 if (l == 10 &&
154 startswith(p, "PRIORITY=") &&
155 p[9] >= '0' && p[9] <= '9')
156 priority = (priority & LOG_FACMASK) | (p[9] - '0');
157
158 else if (l == 17 &&
159 startswith(p, "SYSLOG_FACILITY=") &&
160 p[16] >= '0' && p[16] <= '9')
161 priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
162
163 else if (l == 18 &&
164 startswith(p, "SYSLOG_FACILITY=") &&
165 p[16] >= '0' && p[16] <= '9' &&
166 p[17] >= '0' && p[17] <= '9')
167 priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3);
168
169 else if (l >= 19 &&
170 startswith(p, "SYSLOG_IDENTIFIER=")) {
171 char *t;
172
173 t = strndup(p + 18, l - 18);
174 if (t) {
175 free(identifier);
176 identifier = t;
177 }
178 } else if (l >= 8 &&
179 startswith(p, "MESSAGE=")) {
180 char *t;
181
182 t = strndup(p + 8, l - 8);
183 if (t) {
184 free(message);
185 message = t;
186 }
187 } else if (l > strlen("OBJECT_PID=") &&
188 l < strlen("OBJECT_PID=") + DECIMAL_STR_MAX(pid_t) &&
189 startswith(p, "OBJECT_PID=") &&
190 allow_object_pid(ucred)) {
191 char buf[DECIMAL_STR_MAX(pid_t)];
192 memcpy(buf, p + strlen("OBJECT_PID="), l - strlen("OBJECT_PID="));
193 char_array_0(buf);
194
195 /* ignore error */
196 parse_pid(buf, &object_pid);
197 }
198 }
199
200 remaining -= (e - p) + 1;
201 p = e + 1;
202 continue;
203 } else {
204 le64_t l_le;
205 uint64_t l;
206 char *k;
207
208 if (remaining < e - p + 1 + sizeof(uint64_t) + 1) {
209 log_debug("Failed to parse message, ignoring.");
210 break;
211 }
212
213 memcpy(&l_le, e + 1, sizeof(uint64_t));
214 l = le64toh(l_le);
215
216 if (l > DATA_SIZE_MAX) {
217 log_debug("Received binary data block too large, ignoring.");
218 break;
219 }
220
221 if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
222 e[1+sizeof(uint64_t)+l] != '\n') {
223 log_debug("Failed to parse message, ignoring.");
224 break;
225 }
226
227 k = malloc((e - p) + 1 + l);
228 if (!k) {
229 log_oom();
230 break;
231 }
232
233 memcpy(k, p, e - p);
234 k[e - p] = '=';
235 memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
236
237 if (valid_user_field(p, e - p, false)) {
238 iovec[n].iov_base = k;
239 iovec[n].iov_len = (e - p) + 1 + l;
240 n++;
241 } else
242 free(k);
243
244 remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1;
245 p = e + 1 + sizeof(uint64_t) + l + 1;
246 }
247 }
248
249 if (n <= 0)
250 goto finish;
251
252 tn = n++;
253 IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal");
254
255 if (message) {
256 if (s->forward_to_syslog)
257 server_forward_syslog(s, priority, identifier, message, ucred, tv);
258
259 if (s->forward_to_kmsg)
260 server_forward_kmsg(s, priority, identifier, message, ucred);
261
262 if (s->forward_to_console)
263 server_forward_console(s, priority, identifier, message, ucred);
264
265 if (s->forward_to_wall)
266 server_forward_wall(s, priority, identifier, message, ucred);
267 }
268
269 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
270
271 finish:
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
286 void 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;
294 _cleanup_free_ void *p = NULL;
295 ssize_t n;
296 int r;
297
298 assert(s);
299 assert(fd >= 0);
300
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
326 if (!filename_is_safe(e)) {
327 log_error("Received file in subdirectory of allowed directories. Refusing.");
328 return;
329 }
330 }
331
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);
366 }
367
368 int server_open_native_socket(Server*s) {
369 union sockaddr_union sa;
370 int one, r;
371
372 assert(s);
373
374 if (s->native_fd < 0) {
375
376 s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
377 if (s->native_fd < 0) {
378 log_error("socket() failed: %m");
379 return -errno;
380 }
381
382 zero(sa);
383 sa.un.sun_family = AF_UNIX;
384 strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path));
385
386 unlink(sa.un.sun_path);
387
388 r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
389 if (r < 0) {
390 log_error("bind() failed: %m");
391 return -errno;
392 }
393
394 chmod(sa.un.sun_path, 0666);
395 } else
396 fd_nonblock(s->native_fd, 1);
397
398 one = 1;
399 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
400 if (r < 0) {
401 log_error("SO_PASSCRED failed: %m");
402 return -errno;
403 }
404
405 #ifdef HAVE_SELINUX
406 if (use_selinux()) {
407 one = 1;
408 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
409 if (r < 0)
410 log_warning("SO_PASSSEC failed: %m");
411 }
412 #endif
413
414 one = 1;
415 r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
416 if (r < 0) {
417 log_error("SO_TIMESTAMP failed: %m");
418 return -errno;
419 }
420
421 r = sd_event_add_io(s->event, &s->native_event_source, s->native_fd, EPOLLIN, process_datagram, s);
422 if (r < 0) {
423 log_error("Failed to add native server fd to event loop: %s", strerror(-r));
424 return r;
425 }
426
427 return 0;
428 }