chiark / gitweb /
memfd: always use our internal utility functions where we have them
[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 #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.h"
37
38 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         if (l <= 0)
49                 return false;
50
51         /* Don't allow names longer than 64 chars */
52         if (l > 64)
53                 return false;
54
55         /* Variables starting with an underscore are protected */
56         if (!allow_protected && p[0] == '_')
57                 return false;
58
59         /* Don't allow digits as first character */
60         if (p[0] >= '0' && p[0] <= '9')
61                 return false;
62
63         /* Only allow A-Z0-9 and '_' */
64         for (a = p; a < p + l; a++)
65                 if ((*a < 'A' || *a > 'Z') &&
66                     (*a < '0' || *a > '9') &&
67                     *a != '_')
68                         return false;
69
70         return true;
71 }
72
73 static bool allow_object_pid(struct ucred *ucred) {
74         return ucred && ucred->uid == 0;
75 }
76
77 void server_process_native_message(
78                 Server *s,
79                 const void *buffer, size_t buffer_size,
80                 struct ucred *ucred,
81                 struct timeval *tv,
82                 const char *label, size_t label_len) {
83
84         struct iovec *iovec = NULL;
85         unsigned n = 0, j, tn = (unsigned) -1;
86         const char *p;
87         size_t remaining, m = 0, entry_size = 0;
88         int priority = LOG_INFO;
89         char *identifier = NULL, *message = NULL;
90         pid_t object_pid = 0;
91
92         assert(s);
93         assert(buffer || buffer_size == 0);
94
95         p = buffer;
96         remaining = buffer_size;
97
98         while (remaining > 0) {
99                 const char *e, *q;
100
101                 e = memchr(p, '\n', remaining);
102
103                 if (!e) {
104                         /* Trailing noise, let's ignore it, and flush what we collected */
105                         log_debug("Received message with trailing noise, ignoring.");
106                         break;
107                 }
108
109                 if (e == p) {
110                         /* Entry separator */
111
112                         if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */
113                                 log_debug("Entry is too big with %u properties and %zu bytes, ignoring.", n, entry_size);
114                                 continue;
115                         }
116
117                         server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
118                         n = 0;
119                         priority = LOG_INFO;
120                         entry_size = 0;
121
122                         p++;
123                         remaining--;
124                         continue;
125                 }
126
127                 if (*p == '.' || *p == '#') {
128                         /* Ignore control commands for now, and
129                          * comments too. */
130                         remaining -= (e - p) + 1;
131                         p = e + 1;
132                         continue;
133                 }
134
135                 /* A property follows */
136
137                 /* n received properties, +1 for _TRANSPORT */
138                 if (!GREEDY_REALLOC(iovec, m, n + 1 + N_IOVEC_META_FIELDS + !!object_pid * N_IOVEC_OBJECT_FIELDS)) {
139                         log_oom();
140                         break;
141                 }
142
143                 q = memchr(p, '=', e - p);
144                 if (q) {
145                         if (valid_user_field(p, q - p, false)) {
146                                 size_t l;
147
148                                 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                                 iovec[n].iov_base = (char*) p;
155                                 iovec[n].iov_len = l;
156                                 entry_size += iovec[n].iov_len;
157                                 n++;
158
159                                 /* We need to determine the priority
160                                  * of this entry for the rate limiting
161                                  * logic */
162                                 if (l == 10 &&
163                                     startswith(p, "PRIORITY=") &&
164                                     p[9] >= '0' && p[9] <= '9')
165                                         priority = (priority & LOG_FACMASK) | (p[9] - '0');
166
167                                 else if (l == 17 &&
168                                          startswith(p, "SYSLOG_FACILITY=") &&
169                                          p[16] >= '0' && p[16] <= '9')
170                                         priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
171
172                                 else if (l == 18 &&
173                                          startswith(p, "SYSLOG_FACILITY=") &&
174                                          p[16] >= '0' && p[16] <= '9' &&
175                                          p[17] >= '0' && p[17] <= '9')
176                                         priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3);
177
178                                 else if (l >= 19 &&
179                                          startswith(p, "SYSLOG_IDENTIFIER=")) {
180                                         char *t;
181
182                                         t = strndup(p + 18, l - 18);
183                                         if (t) {
184                                                 free(identifier);
185                                                 identifier = t;
186                                         }
187                                 } else if (l >= 8 &&
188                                            startswith(p, "MESSAGE=")) {
189                                         char *t;
190
191                                         t = strndup(p + 8, l - 8);
192                                         if (t) {
193                                                 free(message);
194                                                 message = t;
195                                         }
196                                 } else if (l > strlen("OBJECT_PID=") &&
197                                            l < strlen("OBJECT_PID=")  + DECIMAL_STR_MAX(pid_t) &&
198                                            startswith(p, "OBJECT_PID=") &&
199                                            allow_object_pid(ucred)) {
200                                         char buf[DECIMAL_STR_MAX(pid_t)];
201                                         memcpy(buf, p + strlen("OBJECT_PID="), l - strlen("OBJECT_PID="));
202                                         char_array_0(buf);
203
204                                         /* ignore error */
205                                         parse_pid(buf, &object_pid);
206                                 }
207                         }
208
209                         remaining -= (e - p) + 1;
210                         p = e + 1;
211                         continue;
212                 } else {
213                         le64_t l_le;
214                         uint64_t l;
215                         char *k;
216
217                         if (remaining < e - p + 1 + sizeof(uint64_t) + 1) {
218                                 log_debug("Failed to parse message, ignoring.");
219                                 break;
220                         }
221
222                         memcpy(&l_le, e + 1, sizeof(uint64_t));
223                         l = le64toh(l_le);
224
225                         if (l > DATA_SIZE_MAX) {
226                                 log_debug("Received binary data block of %"PRIu64" bytes is too large, ignoring.", l);
227                                 break;
228                         }
229
230                         if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
231                             e[1+sizeof(uint64_t)+l] != '\n') {
232                                 log_debug("Failed to parse message, ignoring.");
233                                 break;
234                         }
235
236                         k = malloc((e - p) + 1 + l);
237                         if (!k) {
238                                 log_oom();
239                                 break;
240                         }
241
242                         memcpy(k, p, e - p);
243                         k[e - p] = '=';
244                         memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
245
246                         if (valid_user_field(p, e - p, false)) {
247                                 iovec[n].iov_base = k;
248                                 iovec[n].iov_len = (e - p) + 1 + l;
249                                 entry_size += iovec[n].iov_len;
250                                 n++;
251                         } else
252                                 free(k);
253
254                         remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1;
255                         p = e + 1 + sizeof(uint64_t) + l + 1;
256                 }
257         }
258
259         if (n <= 0)
260                 goto finish;
261
262         tn = n++;
263         IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal");
264         entry_size += strlen("_TRANSPORT=journal");
265
266         if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */
267                 log_debug("Entry is too big with %u properties and %zu bytes, ignoring.",
268                           n, entry_size);
269                 goto finish;
270         }
271
272         if (message) {
273                 if (s->forward_to_syslog)
274                         server_forward_syslog(s, priority, identifier, message, ucred, tv);
275
276                 if (s->forward_to_kmsg)
277                         server_forward_kmsg(s, priority, identifier, message, ucred);
278
279                 if (s->forward_to_console)
280                         server_forward_console(s, priority, identifier, message, ucred);
281
282                 if (s->forward_to_wall)
283                         server_forward_wall(s, priority, identifier, message, ucred);
284         }
285
286         server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
287
288 finish:
289         for (j = 0; j < n; j++)  {
290                 if (j == tn)
291                         continue;
292
293                 if (iovec[j].iov_base < buffer ||
294                     (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size)
295                         free(iovec[j].iov_base);
296         }
297
298         free(iovec);
299         free(identifier);
300         free(message);
301 }
302
303 void server_process_native_file(
304                 Server *s,
305                 int fd,
306                 struct ucred *ucred,
307                 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         assert(s);
318         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         sealed = memfd_get_sealed(fd) > 0;
324
325         if (!sealed && (!ucred || ucred->uid != 0)) {
326                 _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                 if (asprintf(&sl, "/proc/self/fd/%i", fd) < 0) {
333                         log_oom();
334                         return;
335                 }
336
337                 r = readlink_malloc(sl, &k);
338                 if (r < 0) {
339                         log_error("readlink(%s) failed: %m", sl);
340                         return;
341                 }
342
343                 e = path_startswith(k, "/dev/shm/");
344                 if (!e)
345                         e = path_startswith(k, "/tmp/");
346                 if (!e)
347                         e = path_startswith(k, "/var/tmp/");
348                 if (!e) {
349                         log_error("Received file outside of allowed directories. Refusing.");
350                         return;
351                 }
352
353                 if (!filename_is_safe(e)) {
354                         log_error("Received file in subdirectory of allowed directories. Refusing.");
355                         return;
356                 }
357         }
358
359         if (fstat(fd, &st) < 0) {
360                 log_error("Failed to stat passed file, ignoring: %m");
361                 return;
362         }
363
364         if (!S_ISREG(st.st_mode)) {
365                 log_error("File passed is not regular. Ignoring.");
366                 return;
367         }
368
369         if (st.st_size <= 0)
370                 return;
371
372         if (st.st_size > ENTRY_SIZE_MAX) {
373                 log_error("File passed too large. Ignoring.");
374                 return;
375         }
376
377         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                 ps = PAGE_ALIGN(st.st_size);
384                 p = mmap(NULL, ps, PROT_READ, MAP_PRIVATE, fd, 0);
385                 if (p == MAP_FAILED) {
386                         log_error("Failed to map memfd, ignoring: %m");
387                         return;
388                 }
389
390                 server_process_native_message(s, p, st.st_size, ucred, tv, label, label_len);
391                 assert_se(munmap(p, ps) >= 0);
392         } else {
393                 _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                 p = malloc(st.st_size);
401                 if (!p) {
402                         log_oom();
403                         return;
404                 }
405
406                 n = pread(fd, p, st.st_size, 0);
407                 if (n < 0)
408                         log_error("Failed to read file, ignoring: %s", strerror(-n));
409                 else if (n > 0)
410                         server_process_native_message(s, p, n, ucred, tv, label, label_len);
411         }
412 }
413
414 int server_open_native_socket(Server*s) {
415         int one, r;
416
417         assert(s);
418
419         if (s->native_fd < 0) {
420                 union sockaddr_union sa = {
421                         .un.sun_family = AF_UNIX,
422                         .un.sun_path = "/run/systemd/journal/socket",
423                 };
424
425                 s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
426                 if (s->native_fd < 0) {
427                         log_error("socket() failed: %m");
428                         return -errno;
429                 }
430
431                 unlink(sa.un.sun_path);
432
433                 r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
434                 if (r < 0) {
435                         log_error("bind(%s) failed: %m", sa.un.sun_path);
436                         return -errno;
437                 }
438
439                 chmod(sa.un.sun_path, 0666);
440         } else
441                 fd_nonblock(s->native_fd, 1);
442
443         one = 1;
444         r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
445         if (r < 0) {
446                 log_error("SO_PASSCRED failed: %m");
447                 return -errno;
448         }
449
450 #ifdef HAVE_SELINUX
451         if (mac_selinux_use()) {
452                 one = 1;
453                 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
454                 if (r < 0)
455                         log_warning("SO_PASSSEC failed: %m");
456         }
457 #endif
458
459         one = 1;
460         r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
461         if (r < 0) {
462                 log_error("SO_TIMESTAMP failed: %m");
463                 return -errno;
464         }
465
466         r = sd_event_add_io(s->event, &s->native_event_source, s->native_fd, EPOLLIN, process_datagram, s);
467         if (r < 0) {
468                 log_error("Failed to add native server fd to event loop: %s", strerror(-r));
469                 return r;
470         }
471
472         return 0;
473 }