chiark / gitweb /
Add hasprefix macro to check prefixes of fixed length
[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 "journald-server.h"
29 #include "journald-native.h"
30 #include "journald-kmsg.h"
31 #include "journald-console.h"
32 #include "journald-syslog.h"
33
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)
38
39 static 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
74 void server_process_native_message(
75                 Server *s,
76                 const void *buffer, size_t buffer_size,
77                 struct ucred *ucred,
78                 struct timeval *tv,
79                 const char *label, size_t label_len) {
80
81         struct iovec *iovec = NULL;
82         unsigned n = 0, m = 0, j, tn = (unsigned) -1;
83         const char *p;
84         size_t remaining;
85         int priority = LOG_INFO;
86         char *identifier = NULL, *message = NULL;
87
88         assert(s);
89         assert(buffer || buffer_size == 0);
90
91         p = buffer;
92         remaining = buffer_size;
93
94         while (remaining > 0) {
95                 const char *e, *q;
96
97                 e = memchr(p, '\n', remaining);
98
99                 if (!e) {
100                         /* Trailing noise, let's ignore it, and flush what we collected */
101                         log_debug("Received message with trailing noise, ignoring.");
102                         break;
103                 }
104
105                 if (e == p) {
106                         /* Entry separator */
107                         server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority);
108                         n = 0;
109                         priority = LOG_INFO;
110
111                         p++;
112                         remaining--;
113                         continue;
114                 }
115
116                 if (*p == '.' || *p == '#') {
117                         /* Ignore control commands for now, and
118                          * comments too. */
119                         remaining -= (e - p) + 1;
120                         p = e + 1;
121                         continue;
122                 }
123
124                 /* A property follows */
125
126                 /* n received properties, +1 for _TRANSPORT */
127                 if (n + 1 + N_IOVEC_META_FIELDS >= m) {
128                         struct iovec *c;
129                         unsigned u;
130
131                         u = MAX((n + 1 + N_IOVEC_META_FIELDS) * 2U, 4U);
132                         c = realloc(iovec, u * sizeof(struct iovec));
133                         if (!c) {
134                                 log_oom();
135                                 break;
136                         }
137
138                         iovec = c;
139                         m = u;
140                 }
141
142                 q = memchr(p, '=', e - p);
143                 if (q) {
144                         if (valid_user_field(p, q - p)) {
145                                 size_t l;
146
147                                 l = e - p;
148
149                                 /* If the field name starts with an
150                                  * underscore, skip the variable,
151                                  * since that indidates a trusted
152                                  * field */
153                                 iovec[n].iov_base = (char*) p;
154                                 iovec[n].iov_len = l;
155                                 n++;
156
157                                 /* We need to determine the priority
158                                  * of this entry for the rate limiting
159                                  * logic */
160                                 if (l == 10 &&
161                                     hasprefix(p, "PRIORITY=") &&
162                                     p[9] >= '0' && p[9] <= '9')
163                                         priority = (priority & LOG_FACMASK) | (p[9] - '0');
164
165                                 else if (l == 17 &&
166                                          hasprefix(p, "SYSLOG_FACILITY=") &&
167                                          p[16] >= '0' && p[16] <= '9')
168                                         priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
169
170                                 else if (l == 18 &&
171                                          hasprefix(p, "SYSLOG_FACILITY=") &&
172                                          p[16] >= '0' && p[16] <= '9' &&
173                                          p[17] >= '0' && p[17] <= '9')
174                                         priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3);
175
176                                 else if (l >= 19 &&
177                                          hasprefix(p, "SYSLOG_IDENTIFIER=")) {
178                                         char *t;
179
180                                         t = strndup(p + 18, l - 18);
181                                         if (t) {
182                                                 free(identifier);
183                                                 identifier = t;
184                                         }
185                                 } else if (l >= 8 &&
186                                            hasprefix(p, "MESSAGE=")) {
187                                         char *t;
188
189                                         t = strndup(p + 8, l - 8);
190                                         if (t) {
191                                                 free(message);
192                                                 message = t;
193                                         }
194                                 }
195                         }
196
197                         remaining -= (e - p) + 1;
198                         p = e + 1;
199                         continue;
200                 } else {
201                         le64_t l_le;
202                         uint64_t l;
203                         char *k;
204
205                         if (remaining < e - p + 1 + sizeof(uint64_t) + 1) {
206                                 log_debug("Failed to parse message, ignoring.");
207                                 break;
208                         }
209
210                         memcpy(&l_le, e + 1, sizeof(uint64_t));
211                         l = le64toh(l_le);
212
213                         if (l > DATA_SIZE_MAX) {
214                                 log_debug("Received binary data block too large, ignoring.");
215                                 break;
216                         }
217
218                         if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
219                             e[1+sizeof(uint64_t)+l] != '\n') {
220                                 log_debug("Failed to parse message, ignoring.");
221                                 break;
222                         }
223
224                         k = malloc((e - p) + 1 + l);
225                         if (!k) {
226                                 log_oom();
227                                 break;
228                         }
229
230                         memcpy(k, p, e - p);
231                         k[e - p] = '=';
232                         memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
233
234                         if (valid_user_field(p, e - p)) {
235                                 iovec[n].iov_base = k;
236                                 iovec[n].iov_len = (e - p) + 1 + l;
237                                 n++;
238                         } else
239                                 free(k);
240
241                         remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1;
242                         p = e + 1 + sizeof(uint64_t) + l + 1;
243                 }
244         }
245
246         if (n <= 0)
247                 goto finish;
248
249         tn = n++;
250         IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal");
251
252         if (message) {
253                 if (s->forward_to_syslog)
254                         server_forward_syslog(s, priority, identifier, message, ucred, tv);
255
256                 if (s->forward_to_kmsg)
257                         server_forward_kmsg(s, priority, identifier, message, ucred);
258
259                 if (s->forward_to_console)
260                         server_forward_console(s, priority, identifier, message, ucred);
261         }
262
263         server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority);
264
265 finish:
266         for (j = 0; j < n; j++)  {
267                 if (j == tn)
268                         continue;
269
270                 if (iovec[j].iov_base < buffer ||
271                     (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size)
272                         free(iovec[j].iov_base);
273         }
274
275         free(iovec);
276         free(identifier);
277         free(message);
278 }
279
280 void server_process_native_file(
281                 Server *s,
282                 int fd,
283                 struct ucred *ucred,
284                 struct timeval *tv,
285                 const char *label, size_t label_len) {
286
287         struct stat st;
288         _cleanup_free_ void *p = NULL;
289         ssize_t n;
290         int r;
291
292         assert(s);
293         assert(fd >= 0);
294
295         if (!ucred || ucred->uid != 0) {
296                 _cleanup_free_ char *sl = NULL, *k = NULL;
297                 const char *e;
298
299                 if (asprintf(&sl, "/proc/self/fd/%i", fd) < 0) {
300                         log_oom();
301                         return;
302                 }
303
304                 r = readlink_malloc(sl, &k);
305                 if (r < 0) {
306                         log_error("readlink(%s) failed: %m", sl);
307                         return;
308                 }
309
310                 e = path_startswith(k, "/dev/shm/");
311                 if (!e)
312                         e = path_startswith(k, "/tmp/");
313                 if (!e)
314                         e = path_startswith(k, "/var/tmp/");
315                 if (!e) {
316                         log_error("Received file outside of allowed directories. Refusing.");
317                         return;
318                 }
319
320                 if (!filename_is_safe(e)) {
321                         log_error("Received file in subdirectory of allowed directories. Refusing.");
322                         return;
323                 }
324         }
325
326         /* Data is in the passed file, since it didn't fit in a
327          * datagram. We can't map the file here, since clients might
328          * then truncate it and trigger a SIGBUS for us. So let's
329          * stupidly read it */
330
331         if (fstat(fd, &st) < 0) {
332                 log_error("Failed to stat passed file, ignoring: %m");
333                 return;
334         }
335
336         if (!S_ISREG(st.st_mode)) {
337                 log_error("File passed is not regular. Ignoring.");
338                 return;
339         }
340
341         if (st.st_size <= 0)
342                 return;
343
344         if (st.st_size > ENTRY_SIZE_MAX) {
345                 log_error("File passed too large. Ignoring.");
346                 return;
347         }
348
349         p = malloc(st.st_size);
350         if (!p) {
351                 log_oom();
352                 return;
353         }
354
355         n = pread(fd, p, st.st_size, 0);
356         if (n < 0)
357                 log_error("Failed to read file, ignoring: %s", strerror(-n));
358         else if (n > 0)
359                 server_process_native_message(s, p, n, ucred, tv, label, label_len);
360 }
361
362 int server_open_native_socket(Server*s) {
363         union sockaddr_union sa;
364         int one, r;
365         struct epoll_event ev;
366
367         assert(s);
368
369         if (s->native_fd < 0) {
370
371                 s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
372                 if (s->native_fd < 0) {
373                         log_error("socket() failed: %m");
374                         return -errno;
375                 }
376
377                 zero(sa);
378                 sa.un.sun_family = AF_UNIX;
379                 strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path));
380
381                 unlink(sa.un.sun_path);
382
383                 r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
384                 if (r < 0) {
385                         log_error("bind() failed: %m");
386                         return -errno;
387                 }
388
389                 chmod(sa.un.sun_path, 0666);
390         } else
391                 fd_nonblock(s->native_fd, 1);
392
393         one = 1;
394         r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
395         if (r < 0) {
396                 log_error("SO_PASSCRED failed: %m");
397                 return -errno;
398         }
399
400 #ifdef HAVE_SELINUX
401         one = 1;
402         r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
403         if (r < 0)
404                 log_warning("SO_PASSSEC failed: %m");
405 #endif
406
407         one = 1;
408         r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
409         if (r < 0) {
410                 log_error("SO_TIMESTAMP failed: %m");
411                 return -errno;
412         }
413
414         zero(ev);
415         ev.events = EPOLLIN;
416         ev.data.fd = s->native_fd;
417         if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->native_fd, &ev) < 0) {
418                 log_error("Failed to add native server fd to epoll object: %m");
419                 return -errno;
420         }
421
422         return 0;
423 }