syslog: Fix integer overflow in __vsyslog_internal (CVE-2023-6780)
[platform/upstream/glibc.git] / misc / syslog.c
1 /*
2  * Copyright (c) 1983, 1988, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 4. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #if defined(LIBC_SCCS) && !defined(lint)
31 static char sccsid[] = "@(#)syslog.c    8.4 (Berkeley) 3/18/94";
32 #endif /* LIBC_SCCS and not lint */
33
34 #include <libio/libioP.h>
35 #include <paths.h>
36 #include <stdarg.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <stdio_ext.h>
40 #include <sys/socket.h>
41 #include <sys/uio.h>
42 #include <sys/un.h>
43 #include <syslog.h>
44 #include <limits.h>
45
46 static int LogType = SOCK_DGRAM;        /* type of socket connection */
47 static int LogFile = -1;                /* fd for log */
48 static bool connected;                  /* have done connect */
49 static int LogStat;                     /* status bits, set by openlog() */
50 static const char *LogTag;              /* string to tag the entry with */
51 static int LogFacility = LOG_USER;      /* default facility code */
52 static int LogMask = 0xff;              /* mask of priorities to be logged */
53 extern char *__progname;                /* Program name, from crt0. */
54
55 /* Define the lock.  */
56 __libc_lock_define_initialized (static, syslog_lock)
57 static void openlog_internal (const char *, int, int);
58 static void closelog_internal (void);
59
60 struct cleanup_arg
61 {
62   void *buf;
63   struct sigaction *oldaction;
64 };
65
66 static void
67 cancel_handler (void *ptr)
68 {
69   /* Restore the old signal handler.  */
70   struct cleanup_arg *clarg = (struct cleanup_arg *) ptr;
71
72   if (clarg != NULL)
73     /* Free the memstream buffer,  */
74     free (clarg->buf);
75
76   /* Free the lock.  */
77   __libc_lock_unlock (syslog_lock);
78 }
79
80
81 /*
82  * syslog, vsyslog --
83  *      print message on log file; output is intended for syslogd(8).
84  */
85 void
86 __syslog (int pri, const char *fmt, ...)
87 {
88   va_list ap;
89
90   va_start (ap, fmt);
91   __vsyslog_internal (pri, fmt, ap, 0);
92   va_end (ap);
93 }
94 ldbl_hidden_def (__syslog, syslog)
95 ldbl_strong_alias (__syslog, syslog)
96
97 void
98 __vsyslog (int pri, const char *fmt, va_list ap)
99 {
100   __vsyslog_internal (pri, fmt, ap, 0);
101 }
102 ldbl_weak_alias (__vsyslog, vsyslog)
103
104 void
105 ___syslog_chk (int pri, int flag, const char *fmt, ...)
106 {
107   va_list ap;
108
109   va_start (ap, fmt);
110   __vsyslog_internal (pri, fmt, ap, (flag > 0) ? PRINTF_FORTIFY : 0);
111   va_end (ap);
112 }
113 ldbl_hidden_def (___syslog_chk, __syslog_chk)
114 ldbl_strong_alias (___syslog_chk, __syslog_chk)
115
116 void
117 __vsyslog_chk (int pri, int flag, const char *fmt, va_list ap)
118 {
119   __vsyslog_internal (pri, fmt, ap, (flag > 0) ? PRINTF_FORTIFY : 0);
120 }
121
122 void
123 __vsyslog_internal (int pri, const char *fmt, va_list ap,
124                     unsigned int mode_flags)
125 {
126   /* Try to use a static buffer as an optimization.  */
127   char bufs[1024];
128   char *buf = bufs;
129   size_t bufsize;
130
131   int msgoff;
132   int saved_errno = errno;
133
134 #define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
135   /* Check for invalid bits. */
136   if (pri & ~(LOG_PRIMASK | LOG_FACMASK))
137     {
138       syslog (INTERNALLOG, "syslog: unknown facility/priority: %x", pri);
139       pri &= LOG_PRIMASK | LOG_FACMASK;
140     }
141
142   /* Prepare for multiple users.  We have to take care: most syscalls we are
143      using are cancellation points.  */
144   struct cleanup_arg clarg = { NULL, NULL };
145   __libc_cleanup_push (cancel_handler, &clarg);
146   __libc_lock_lock (syslog_lock);
147
148   /* Check priority against setlogmask values. */
149   if ((LOG_MASK (LOG_PRI (pri)) & LogMask) == 0)
150     goto out;
151
152   /* Set default facility if none specified. */
153   if ((pri & LOG_FACMASK) == 0)
154     pri |= LogFacility;
155
156   pid_t pid = LogStat & LOG_PID ? __getpid () : 0;
157
158   /* "%b %e %H:%M:%S "  */
159   char timestamp[sizeof "MMM DD hh:mm:ss "];
160   __time64_t now = time64_now ();
161   struct tm now_tm;
162   struct tm *now_tmp = __localtime64_r (&now, &now_tm);
163   bool has_ts = now_tmp != NULL;
164
165   /* In the unlikely case of localtime_r failure (tm_year out of int range)
166      skip the hostname so the message is handled as valid PRI but without
167      TIMESTAMP or invalid TIMESTAMP (which should force the relay to add the
168      timestamp itself).  */
169   if (has_ts)
170     __strftime_l (timestamp, sizeof timestamp, "%h %e %T ", now_tmp,
171                   _nl_C_locobj_ptr);
172
173 #define SYSLOG_HEADER(__pri, __timestamp, __msgoff, pid) \
174   "<%d>%s%n%s%s%.0d%s: ",                                \
175   __pri, __timestamp, __msgoff,                          \
176   LogTag == NULL ? __progname : LogTag,                  \
177   "[" + (pid == 0), pid, "]" + (pid == 0)
178
179 #define SYSLOG_HEADER_WITHOUT_TS(__pri, __msgoff)        \
180   "<%d>: %n", __pri, __msgoff
181
182   int l, vl;
183   if (has_ts)
184     l = __snprintf (bufs, sizeof bufs,
185                     SYSLOG_HEADER (pri, timestamp, &msgoff, pid));
186   else
187     l = __snprintf (bufs, sizeof bufs,
188                     SYSLOG_HEADER_WITHOUT_TS (pri, &msgoff));
189   if (l < 0)
190     goto out;
191
192   char *pos;
193   size_t len;
194
195   if (l < sizeof bufs)
196     {
197       /* At this point, there is still a chance that we can print the
198          remaining part of the log into bufs and use that.  */
199       pos = bufs + l;
200       len = sizeof (bufs) - l;
201     }
202   else
203     {
204       buf = NULL;
205       /* We already know that bufs is too small to use for this log message.
206          The next vsnprintf into bufs is used only to calculate the total
207          required buffer length.  We will discard bufs contents and allocate
208          an appropriately sized buffer later instead.  */
209       pos = bufs;
210       len = sizeof (bufs);
211     }
212
213   {
214     va_list apc;
215     va_copy (apc, ap);
216
217     /* Restore errno for %m format.  */
218     __set_errno (saved_errno);
219
220     vl = __vsnprintf_internal (pos, len, fmt, apc, mode_flags);
221     va_end (apc);
222
223     if (vl < 0 || vl >= INT_MAX - l)
224       goto out;
225
226     if (vl >= len)
227       buf = NULL;
228
229     bufsize = l + vl;
230   }
231
232   if (buf == NULL)
233     {
234       buf = malloc ((bufsize + 1) * sizeof (char));
235       if (buf != NULL)
236         {
237           /* Tell the cancellation handler to free this buffer.  */
238           clarg.buf = buf;
239
240           int cl;
241           if (has_ts)
242             cl = __snprintf (buf, l + 1,
243                              SYSLOG_HEADER (pri, timestamp, &msgoff, pid));
244           else
245             cl = __snprintf (buf, l + 1,
246                              SYSLOG_HEADER_WITHOUT_TS (pri, &msgoff));
247           if (cl != l)
248             goto out;
249
250           va_list apc;
251           va_copy (apc, ap);
252           cl = __vsnprintf_internal (buf + l, bufsize - l + 1, fmt, apc,
253                                      mode_flags);
254           va_end (apc);
255
256           if (cl != vl)
257             goto out;
258         }
259       else
260         {
261           int bl;
262           /* Nothing much to do but emit an error message.  */
263           bl = __snprintf (bufs, sizeof bufs,
264                            "out of memory[%d]", __getpid ());
265           if (bl < 0 || bl >= sizeof bufs)
266             goto out;
267
268           bufsize = bl;
269           buf = bufs;
270           msgoff = 0;
271         }
272     }
273
274   /* Output to stderr if requested. */
275   if (LogStat & LOG_PERROR)
276     __dprintf (STDERR_FILENO, "%s%s", buf + msgoff,
277                "\n" + (buf[bufsize - 1] == '\n'));
278
279   /* Get connected, output the message to the local logger.  */
280   if (!connected)
281     openlog_internal (NULL, LogStat | LOG_NDELAY, LogFacility);
282
283   /* If we have a SOCK_STREAM connection, also send ASCII NUL as a record
284      terminator.  */
285   if (LogType == SOCK_STREAM)
286     ++bufsize;
287
288   if (!connected || __send (LogFile, buf, bufsize, MSG_NOSIGNAL) < 0)
289     {
290       if (connected)
291         {
292           /* Try to reopen the syslog connection.  Maybe it went down.  */
293           closelog_internal ();
294           openlog_internal (NULL, LogStat | LOG_NDELAY, LogFacility);
295         }
296
297       if (!connected || __send (LogFile, buf, bufsize, MSG_NOSIGNAL) < 0)
298         {
299           closelog_internal (); /* attempt re-open next time */
300           /*
301            * Output the message to the console; don't worry
302            * about blocking, if console blocks everything will.
303            * Make sure the error reported is the one from the
304            * syslogd failure.
305            */
306           int fd;
307           if (LogStat & LOG_CONS &&
308               (fd = __open (_PATH_CONSOLE, O_WRONLY | O_NOCTTY
309                             | O_CLOEXEC, 0)) >= 0)
310             {
311               __dprintf (fd, "%s\r\n", buf + msgoff);
312               __close (fd);
313             }
314         }
315     }
316
317 out:
318   /* End of critical section.  */
319   __libc_cleanup_pop (0);
320   __libc_lock_unlock (syslog_lock);
321
322   if (buf != bufs)
323     free (buf);
324 }
325
326 /* AF_UNIX address of local logger  */
327 static const struct sockaddr_un SyslogAddr =
328   {
329     .sun_family = AF_UNIX,
330     .sun_path = _PATH_LOG
331   };
332
333 static void
334 openlog_internal (const char *ident, int logstat, int logfac)
335 {
336   if (ident != NULL)
337     LogTag = ident;
338   LogStat = logstat;
339   if ((logfac & ~LOG_FACMASK) == 0)
340     LogFacility = logfac;
341
342   int retry = 0;
343   while (retry < 2)
344     {
345       if (LogFile == -1)
346         {
347           if (LogStat & LOG_NDELAY)
348             {
349               LogFile = __socket (AF_UNIX, LogType | SOCK_CLOEXEC, 0);
350               if (LogFile == -1)
351                 return;
352             }
353         }
354       if (LogFile != -1 && !connected)
355         {
356           int old_errno = errno;
357           if (__connect (LogFile, &SyslogAddr, sizeof (SyslogAddr)) == -1)
358             {
359               int saved_errno = errno;
360               int fd = LogFile;
361               LogFile = -1;
362               __close (fd);
363               __set_errno (old_errno);
364               if (saved_errno == EPROTOTYPE)
365                 {
366                   /* retry with the other type: */
367                   LogType = LogType == SOCK_DGRAM ? SOCK_STREAM : SOCK_DGRAM;
368                   ++retry;
369                   continue;
370                 }
371             }
372           else
373             connected = true;
374         }
375       break;
376     }
377 }
378
379 void
380 openlog (const char *ident, int logstat, int logfac)
381 {
382   /* Protect against multiple users and cancellation.  */
383   __libc_cleanup_push (cancel_handler, NULL);
384   __libc_lock_lock (syslog_lock);
385
386   openlog_internal (ident, logstat, logfac);
387
388   __libc_cleanup_pop (1);
389 }
390
391 static void
392 closelog_internal (void)
393 {
394   if (!connected)
395     return;
396
397   __close (LogFile);
398   LogFile = -1;
399   connected = false;
400 }
401
402 void
403 closelog (void)
404 {
405   /* Protect against multiple users and cancellation.  */
406   __libc_cleanup_push (cancel_handler, NULL);
407   __libc_lock_lock (syslog_lock);
408
409   closelog_internal ();
410   LogTag = NULL;
411   LogType = SOCK_DGRAM; /* this is the default */
412
413   /* Free the lock.  */
414   __libc_cleanup_pop (1);
415 }
416
417 /* setlogmask -- set the log mask level */
418 int
419 setlogmask (int pmask)
420 {
421   int omask;
422
423   /* Protect against multiple users.  */
424   __libc_lock_lock (syslog_lock);
425
426   omask = LogMask;
427   if (pmask != 0)
428     LogMask = pmask;
429
430   __libc_lock_unlock (syslog_lock);
431
432   return (omask);
433 }