chiark / gitweb /
c54f6475d330dc70c18eefc02cd635e70a0b1696
[elogind.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 }