1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <stddef.h>
4 #include <sys/epoll.h>
5 #include <unistd.h>
6 
7 #include "sd-messages.h"
8 
9 #include "alloc-util.h"
10 #include "fd-util.h"
11 #include "format-util.h"
12 #include "io-util.h"
13 #include "journald-console.h"
14 #include "journald-kmsg.h"
15 #include "journald-server.h"
16 #include "journald-syslog.h"
17 #include "journald-wall.h"
18 #include "process-util.h"
19 #include "selinux-util.h"
20 #include "socket-util.h"
21 #include "stdio-util.h"
22 #include "string-util.h"
23 #include "syslog-util.h"
24 
25 /* Warn once every 30s if we missed syslog message */
26 #define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
27 
forward_syslog_iovec(Server * s,const struct iovec * iovec,unsigned n_iovec,const struct ucred * ucred,const struct timeval * tv)28 static void forward_syslog_iovec(
29                 Server *s,
30                 const struct iovec *iovec,
31                 unsigned n_iovec,
32                 const struct ucred *ucred,
33                 const struct timeval *tv) {
34 
35         union sockaddr_union sa;
36 
37         struct msghdr msghdr = {
38                 .msg_iov = (struct iovec *) iovec,
39                 .msg_iovlen = n_iovec,
40         };
41         struct cmsghdr *cmsg;
42         CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
43         const char *j;
44         int r;
45 
46         assert(s);
47         assert(iovec);
48         assert(n_iovec > 0);
49 
50         j = strjoina(s->runtime_directory, "/syslog");
51         r = sockaddr_un_set_path(&sa.un, j);
52         if (r < 0) {
53                 log_debug_errno(r, "Forwarding socket path %s too long for AF_UNIX, not forwarding: %m", j);
54                 return;
55         }
56 
57         msghdr.msg_name = &sa.sa;
58         msghdr.msg_namelen = r;
59 
60         if (ucred) {
61                 zero(control);
62                 msghdr.msg_control = &control;
63                 msghdr.msg_controllen = sizeof(control);
64 
65                 cmsg = CMSG_FIRSTHDR(&msghdr);
66                 cmsg->cmsg_level = SOL_SOCKET;
67                 cmsg->cmsg_type = SCM_CREDENTIALS;
68                 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
69                 memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred));
70                 msghdr.msg_controllen = cmsg->cmsg_len;
71         }
72 
73         /* Forward the syslog message we received via /dev/log to /run/systemd/syslog. Unfortunately we
74          * currently can't set the SO_TIMESTAMP auxiliary data, and hence we don't. */
75 
76         if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
77                 return;
78 
79         /* The socket is full? I guess the syslog implementation is
80          * too slow, and we shouldn't wait for that... */
81         if (errno == EAGAIN) {
82                 s->n_forward_syslog_missed++;
83                 return;
84         }
85 
86         if (ucred && IN_SET(errno, ESRCH, EPERM)) {
87                 struct ucred u;
88 
89                 /* Hmm, presumably the sender process vanished
90                  * by now, or we don't have CAP_SYS_AMDIN, so
91                  * let's fix it as good as we can, and retry */
92 
93                 u = *ucred;
94                 u.pid = getpid_cached();
95                 memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred));
96 
97                 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
98                         return;
99 
100                 if (errno == EAGAIN) {
101                         s->n_forward_syslog_missed++;
102                         return;
103                 }
104         }
105 
106         if (errno != ENOENT)
107                 log_debug_errno(errno, "Failed to forward syslog message: %m");
108 }
109 
forward_syslog_raw(Server * s,int priority,const char * buffer,size_t buffer_len,const struct ucred * ucred,const struct timeval * tv)110 static void forward_syslog_raw(Server *s, int priority, const char *buffer, size_t buffer_len, const struct ucred *ucred, const struct timeval *tv) {
111         struct iovec iovec;
112 
113         assert(s);
114         assert(buffer);
115 
116         if (LOG_PRI(priority) > s->max_level_syslog)
117                 return;
118 
119         iovec = IOVEC_MAKE((char *) buffer, buffer_len);
120         forward_syslog_iovec(s, &iovec, 1, ucred, tv);
121 }
122 
server_forward_syslog(Server * s,int priority,const char * identifier,const char * message,const struct ucred * ucred,const struct timeval * tv)123 void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv) {
124         struct iovec iovec[5];
125         char header_priority[DECIMAL_STR_MAX(priority) + 3], header_time[64],
126              header_pid[STRLEN("[]: ") + DECIMAL_STR_MAX(pid_t) + 1];
127         int n = 0;
128         time_t t;
129         struct tm tm;
130         _cleanup_free_ char *ident_buf = NULL;
131 
132         assert(s);
133         assert(priority >= 0);
134         assert(priority <= 999);
135         assert(message);
136 
137         if (LOG_PRI(priority) > s->max_level_syslog)
138                 return;
139 
140         /* First: priority field */
141         xsprintf(header_priority, "<%i>", priority);
142         iovec[n++] = IOVEC_MAKE_STRING(header_priority);
143 
144         /* Second: timestamp */
145         t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC));
146         if (!localtime_r(&t, &tm))
147                 return;
148         if (strftime(header_time, sizeof(header_time), "%h %e %T ", &tm) <= 0)
149                 return;
150         iovec[n++] = IOVEC_MAKE_STRING(header_time);
151 
152         /* Third: identifier and PID */
153         if (ucred) {
154                 if (!identifier) {
155                         (void) get_process_comm(ucred->pid, &ident_buf);
156                         identifier = ident_buf;
157                 }
158 
159                 xsprintf(header_pid, "["PID_FMT"]: ", ucred->pid);
160 
161                 if (identifier)
162                         iovec[n++] = IOVEC_MAKE_STRING(identifier);
163 
164                 iovec[n++] = IOVEC_MAKE_STRING(header_pid);
165         } else if (identifier) {
166                 iovec[n++] = IOVEC_MAKE_STRING(identifier);
167                 iovec[n++] = IOVEC_MAKE_STRING(": ");
168         }
169 
170         /* Fourth: message */
171         iovec[n++] = IOVEC_MAKE_STRING(message);
172 
173         forward_syslog_iovec(s, iovec, n, ucred, tv);
174 }
175 
syslog_fixup_facility(int priority)176 int syslog_fixup_facility(int priority) {
177 
178         if ((priority & LOG_FACMASK) == 0)
179                 return (priority & LOG_PRIMASK) | LOG_USER;
180 
181         return priority;
182 }
183 
syslog_parse_identifier(const char ** buf,char ** identifier,char ** pid)184 size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) {
185         const char *p;
186         char *t;
187         size_t l, e;
188 
189         assert(buf);
190         assert(identifier);
191         assert(pid);
192 
193         p = *buf;
194 
195         p += strspn(p, WHITESPACE);
196         l = strcspn(p, WHITESPACE);
197 
198         if (l <= 0 ||
199             p[l-1] != ':')
200                 return 0;
201 
202         e = l;
203         l--;
204 
205         if (l > 0 && p[l-1] == ']') {
206                 size_t k = l-1;
207 
208                 for (;;) {
209 
210                         if (p[k] == '[') {
211                                 t = strndup(p+k+1, l-k-2);
212                                 if (t)
213                                         *pid = t;
214 
215                                 l = k;
216                                 break;
217                         }
218 
219                         if (k == 0)
220                                 break;
221 
222                         k--;
223                 }
224         }
225 
226         t = strndup(p, l);
227         if (t)
228                 *identifier = t;
229 
230         /* Single space is used as separator */
231         if (p[e] != '\0' && strchr(WHITESPACE, p[e]))
232                 e++;
233 
234         l = (p - *buf) + e;
235         *buf = p + e;
236         return l;
237 }
238 
syslog_skip_timestamp(const char ** buf)239 static int syslog_skip_timestamp(const char **buf) {
240         enum {
241                 LETTER,
242                 SPACE,
243                 NUMBER,
244                 SPACE_OR_NUMBER,
245                 COLON
246         } sequence[] = {
247                 LETTER, LETTER, LETTER,
248                 SPACE,
249                 SPACE_OR_NUMBER, NUMBER,
250                 SPACE,
251                 SPACE_OR_NUMBER, NUMBER,
252                 COLON,
253                 SPACE_OR_NUMBER, NUMBER,
254                 COLON,
255                 SPACE_OR_NUMBER, NUMBER,
256                 SPACE
257         };
258 
259         const char *p, *t;
260         unsigned i;
261 
262         assert(buf);
263         assert(*buf);
264 
265         for (i = 0, p = *buf; i < ELEMENTSOF(sequence); i++, p++) {
266                 if (!*p)
267                         return 0;
268 
269                 switch (sequence[i]) {
270 
271                 case SPACE:
272                         if (*p != ' ')
273                                 return 0;
274                         break;
275 
276                 case SPACE_OR_NUMBER:
277                         if (*p == ' ')
278                                 break;
279 
280                         _fallthrough_;
281                 case NUMBER:
282                         if (*p < '0' || *p > '9')
283                                 return 0;
284 
285                         break;
286 
287                 case LETTER:
288                         if (!(*p >= 'A' && *p <= 'Z') &&
289                             !(*p >= 'a' && *p <= 'z'))
290                                 return 0;
291 
292                         break;
293 
294                 case COLON:
295                         if (*p != ':')
296                                 return 0;
297                         break;
298 
299                 }
300         }
301 
302         t = *buf;
303         *buf = p;
304         return p - t;
305 }
306 
server_process_syslog_message(Server * s,const char * buf,size_t raw_len,const struct ucred * ucred,const struct timeval * tv,const char * label,size_t label_len)307 void server_process_syslog_message(
308                 Server *s,
309                 const char *buf,
310                 size_t raw_len,
311                 const struct ucred *ucred,
312                 const struct timeval *tv,
313                 const char *label,
314                 size_t label_len) {
315 
316         char *t, syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
317                  syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)];
318         const char *msg, *syslog_ts, *a;
319         _cleanup_free_ char *identifier = NULL, *pid = NULL,
320                 *dummy = NULL, *msg_msg = NULL, *msg_raw = NULL;
321         int priority = LOG_USER | LOG_INFO, r;
322         ClientContext *context = NULL;
323         struct iovec *iovec;
324         size_t n = 0, m, i, leading_ws, syslog_ts_len;
325         bool store_raw;
326 
327         assert(s);
328         assert(buf);
329         /* The message cannot be empty. */
330         assert(raw_len > 0);
331         /* The buffer NUL-terminated and can be used a string. raw_len is the length
332          * without the terminating NUL byte, the buffer is actually one bigger. */
333         assert(buf[raw_len] == '\0');
334 
335         if (ucred && pid_is_valid(ucred->pid)) {
336                 r = client_context_get(s, ucred->pid, ucred, label, label_len, NULL, &context);
337                 if (r < 0)
338                         log_warning_errno(r, "Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m", ucred->pid);
339         }
340 
341         /* We are creating a copy of the message because we want to forward the original message
342            verbatim to the legacy syslog implementation */
343         for (i = raw_len; i > 0; i--)
344                 if (!strchr(WHITESPACE, buf[i-1]))
345                         break;
346 
347         leading_ws = strspn(buf, WHITESPACE);
348 
349         if (i == 0)
350                 /* The message contains only whitespaces */
351                 msg = buf + raw_len;
352         else if (i == raw_len)
353                 /* Nice! No need to strip anything on the end, let's optimize this a bit */
354                 msg = buf + leading_ws;
355         else {
356                 msg = dummy = new(char, i - leading_ws + 1);
357                 if (!dummy) {
358                         log_oom();
359                         return;
360                 }
361 
362                 memcpy(dummy, buf + leading_ws, i - leading_ws);
363                 dummy[i - leading_ws] = 0;
364         }
365 
366         /* We will add the SYSLOG_RAW= field when we stripped anything
367          * _or_ if the input message contained NUL bytes. */
368         store_raw = msg != buf || strlen(msg) != raw_len;
369 
370         syslog_parse_priority(&msg, &priority, true);
371 
372         if (!client_context_test_priority(context, priority))
373                 return;
374 
375         syslog_ts = msg;
376         syslog_ts_len = syslog_skip_timestamp(&msg);
377         if (syslog_ts_len == 0)
378                 /* We failed to parse the full timestamp, store the raw message too */
379                 store_raw = true;
380 
381         syslog_parse_identifier(&msg, &identifier, &pid);
382 
383         if (s->forward_to_syslog)
384                 forward_syslog_raw(s, priority, buf, raw_len, ucred, tv);
385 
386         if (s->forward_to_kmsg)
387                 server_forward_kmsg(s, priority, identifier, msg, ucred);
388 
389         if (s->forward_to_console)
390                 server_forward_console(s, priority, identifier, msg, ucred);
391 
392         if (s->forward_to_wall)
393                 server_forward_wall(s, priority, identifier, msg, ucred);
394 
395         m = N_IOVEC_META_FIELDS + 8 + client_context_extra_fields_n_iovec(context);
396         iovec = newa(struct iovec, m);
397 
398         iovec[n++] = IOVEC_MAKE_STRING("_TRANSPORT=syslog");
399 
400         xsprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK);
401         iovec[n++] = IOVEC_MAKE_STRING(syslog_priority);
402 
403         if (priority & LOG_FACMASK) {
404                 xsprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority));
405                 iovec[n++] = IOVEC_MAKE_STRING(syslog_facility);
406         }
407 
408         if (identifier) {
409                 a = strjoina("SYSLOG_IDENTIFIER=", identifier);
410                 iovec[n++] = IOVEC_MAKE_STRING(a);
411         }
412 
413         if (pid) {
414                 a = strjoina("SYSLOG_PID=", pid);
415                 iovec[n++] = IOVEC_MAKE_STRING(a);
416         }
417 
418         if (syslog_ts_len > 0) {
419                 const size_t hlen = STRLEN("SYSLOG_TIMESTAMP=");
420 
421                 t = newa(char, hlen + syslog_ts_len);
422                 memcpy(t, "SYSLOG_TIMESTAMP=", hlen);
423                 memcpy(t + hlen, syslog_ts, syslog_ts_len);
424 
425                 iovec[n++] = IOVEC_MAKE(t, hlen + syslog_ts_len);
426         }
427 
428         msg_msg = strjoin("MESSAGE=", msg);
429         if (!msg_msg) {
430                 log_oom();
431                 return;
432         }
433         iovec[n++] = IOVEC_MAKE_STRING(msg_msg);
434 
435         if (store_raw) {
436                 const size_t hlen = STRLEN("SYSLOG_RAW=");
437 
438                 msg_raw = new(char, hlen + raw_len);
439                 if (!msg_raw) {
440                         log_oom();
441                         return;
442                 }
443 
444                 memcpy(msg_raw, "SYSLOG_RAW=", hlen);
445                 memcpy(msg_raw + hlen, buf, raw_len);
446 
447                 iovec[n++] = IOVEC_MAKE(msg_raw, hlen + raw_len);
448         }
449 
450         server_dispatch_message(s, iovec, n, m, context, tv, priority, 0);
451 }
452 
server_open_syslog_socket(Server * s,const char * syslog_socket)453 int server_open_syslog_socket(Server *s, const char *syslog_socket) {
454         int r;
455 
456         assert(s);
457         assert(syslog_socket);
458 
459         if (s->syslog_fd < 0) {
460                 union sockaddr_union sa;
461                 socklen_t sa_len;
462 
463                 r = sockaddr_un_set_path(&sa.un, syslog_socket);
464                 if (r < 0)
465                         return log_error_errno(r, "Unable to use namespace path %s for AF_UNIX socket: %m", syslog_socket);
466                 sa_len = r;
467 
468                 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
469                 if (s->syslog_fd < 0)
470                         return log_error_errno(errno, "socket() failed: %m");
471 
472                 (void) sockaddr_un_unlink(&sa.un);
473 
474                 r = bind(s->syslog_fd, &sa.sa, sa_len);
475                 if (r < 0)
476                         return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
477 
478                 (void) chmod(sa.un.sun_path, 0666);
479         } else
480                 (void) fd_nonblock(s->syslog_fd, true);
481 
482         r = setsockopt_int(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, true);
483         if (r < 0)
484                 return log_error_errno(r, "SO_PASSCRED failed: %m");
485 
486         if (mac_selinux_use()) {
487                 r = setsockopt_int(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, true);
488                 if (r < 0)
489                         log_warning_errno(r, "SO_PASSSEC failed: %m");
490         }
491 
492         r = setsockopt_int(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, true);
493         if (r < 0)
494                 return log_error_errno(r, "SO_TIMESTAMP failed: %m");
495 
496         r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, server_process_datagram, s);
497         if (r < 0)
498                 return log_error_errno(r, "Failed to add syslog server fd to event loop: %m");
499 
500         r = sd_event_source_set_priority(s->syslog_event_source, SD_EVENT_PRIORITY_NORMAL+5);
501         if (r < 0)
502                 return log_error_errno(r, "Failed to adjust syslog event source priority: %m");
503 
504         return 0;
505 }
506 
server_maybe_warn_forward_syslog_missed(Server * s)507 void server_maybe_warn_forward_syslog_missed(Server *s) {
508         usec_t n;
509 
510         assert(s);
511 
512         if (s->n_forward_syslog_missed <= 0)
513                 return;
514 
515         n = now(CLOCK_MONOTONIC);
516         if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
517                 return;
518 
519         server_driver_message(s, 0,
520                               "MESSAGE_ID=" SD_MESSAGE_FORWARD_SYSLOG_MISSED_STR,
521                               LOG_MESSAGE("Forwarding to syslog missed %u messages.",
522                                           s->n_forward_syslog_missed),
523                               NULL);
524 
525         s->n_forward_syslog_missed = 0;
526         s->last_warn_forward_syslog_missed = n;
527 }
528