Fix for coverity (potential buffer overflow in radlog_request)
[freeradius.git] / src / main / log.c
1 /*
2  * log.c        Logging module.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2001,2006  The FreeRADIUS server project
21  * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
22  * Copyright 2000  Alan DeKok <aland@ox.org>
23  * Copyright 2001  Chad Miller <cmiller@surfsouth.com>
24  */
25
26 #include <freeradius-devel/ident.h>
27 RCSID("$Id$")
28
29 #include <freeradius-devel/radiusd.h>
30
31 #ifdef HAVE_SYS_STAT_H
32 #include <sys/stat.h>
33 #endif
34
35 #ifdef HAVE_SYSLOG_H
36 #       include <syslog.h>
37 #endif
38
39 /*
40  * Logging facility names
41  */
42 static const FR_NAME_NUMBER levels[] = {
43         { ": Debug: ",          L_DBG   },
44         { ": Auth: ",           L_AUTH  },
45         { ": Proxy: ",          L_PROXY },
46         { ": Info: ",           L_INFO  },
47         { ": Acct: ",           L_ACCT  },
48         { ": Error: ",          L_ERR   },
49         { NULL, 0 }
50 };
51
52 int log_dates_utc = 0;
53
54
55 /*
56  *      Log the message to the logfile. Include the severity and
57  *      a time stamp.
58  */
59 int vradlog(int lvl, const char *fmt, va_list ap)
60 {
61         struct main_config_t *myconfig = &mainconfig;
62         unsigned char *p;
63         char buffer[8192];
64         size_t len;
65
66         /*
67          *      NOT debugging, and trying to log debug messages.
68          *
69          *      Throw the message away.
70          */
71         if (!debug_flag && (lvl == L_DBG)) {
72                 return 0;
73         }
74
75         /*
76          *      If we don't want any messages, then
77          *      throw them away.
78          */
79         if (myconfig->radlog_dest == RADLOG_NULL) {
80                 return 0;
81         }
82
83         *buffer = '\0';
84         len = 0;
85
86         /*
87          *      Don't print timestamps to syslog, it does that for us.
88          *      Don't print timestamps for low levels of debugging.
89          *
90          *      Print timestamps for non-debugging, and for high levels
91          *      of debugging.
92          */
93         if ((myconfig->radlog_dest != RADLOG_SYSLOG) &&
94             (debug_flag != 1) && (debug_flag != 2)) {
95                 time_t timeval;
96
97                 timeval = time(NULL);
98                 CTIME_R(&timeval, buffer + len, sizeof(buffer) - len - 1);
99                 
100                 len = strlen(buffer);
101
102                 len += strlcpy(buffer + len,
103                                fr_int2str(levels, (lvl & ~L_CONS), ": "),
104                                sizeof(buffer) - len);
105         }
106
107         if (len < sizeof(buffer)) {
108                 len += vsnprintf(buffer + len,
109                                  sizeof(buffer) - len - 1, fmt, ap);
110         }
111         
112         /*
113          *      Filter out characters not in Latin-1.
114          */
115         for (p = (unsigned char *)buffer; *p != '\0'; p++) {
116                 if (*p == '\r' || *p == '\n')
117                         *p = ' ';
118                 else if (*p == '\t') continue;
119                 else if (*p < 32 || (*p >= 128 && *p <= 160))
120                         *p = '?';
121         }
122         
123         if (len < (sizeof(buffer) - 1)) {
124                 buffer[len]     = '\n';
125                 buffer[len + 1] = '\0';
126         } else {
127                 buffer[sizeof(buffer) - 1] = '\0';
128         }
129         
130         switch (myconfig->radlog_dest) {
131
132 #ifdef HAVE_SYSLOG_H
133         case RADLOG_SYSLOG:
134                 switch(lvl & ~L_CONS) {
135                         case L_DBG:
136                                 lvl = LOG_DEBUG;
137                                 break;
138                         case L_AUTH:
139                                 lvl = LOG_NOTICE;
140                                 break;
141                         case L_PROXY:
142                                 lvl = LOG_NOTICE;
143                                 break;
144                         case L_ACCT:
145                                 lvl = LOG_NOTICE;
146                                 break;
147                         case L_INFO:
148                                 lvl = LOG_INFO;
149                                 break;
150                         case L_ERR:
151                                 lvl = LOG_ERR;
152                                 break;
153                 }
154                 syslog(lvl, "%s", buffer);
155                 break;
156 #endif
157
158         case RADLOG_FILES:
159         case RADLOG_STDOUT:
160         case RADLOG_STDERR:
161                 write(myconfig->radlog_fd, buffer, strlen(buffer));
162                 break;
163
164         default:
165         case RADLOG_NULL:       /* should have been caught above */
166                 break;
167         }
168
169         return 0;
170 }
171
172 int log_debug(const char *msg, ...)
173 {
174         va_list ap;
175         int r;
176
177         va_start(ap, msg);
178         r = vradlog(L_DBG, msg, ap);
179         va_end(ap);
180
181         return r;
182 }
183
184 int radlog(int lvl, const char *msg, ...)
185 {
186         va_list ap;
187         int r;
188
189         va_start(ap, msg);
190         r = vradlog(lvl, msg, ap);
191         va_end(ap);
192
193         return r;
194 }
195
196
197 /*
198  *      Dump a whole list of attributes to DEBUG2
199  */
200 void vp_listdebug(VALUE_PAIR *vp)
201 {
202         char tmpPair[70];
203         for (; vp; vp = vp->next) {
204                 vp_prints(tmpPair, sizeof(tmpPair), vp);
205                 DEBUG2("     %s", tmpPair);
206         }
207 }
208
209 extern char *request_log_file;
210 #ifdef WITH_COMMAND_SOCKET
211 extern char *debug_log_file;
212 #endif
213
214 void radlog_request(int lvl, int priority, REQUEST *request, const char *msg, ...)
215 {
216         size_t len = 0;
217         const char *filename = request_log_file;
218         FILE *fp = NULL;
219         va_list ap;
220         char buffer[8192];
221         char *p;
222
223         va_start(ap, msg);
224
225         /*
226          *      Debug messages get treated specially.
227          */
228         if (lvl == L_DBG) {
229                 /*
230                  *      There is log function, but the debug level
231                  *      isn't high enough.  OR, we're in debug mode,
232                  *      and the debug level isn't high enough.  Return.
233                  */
234                 if ((request && request->radlog &&
235                      (priority > request->options)) ||
236                     ((debug_flag != 0) && (priority > debug_flag))) {
237                         va_end(ap);
238                         return;
239                 }
240
241                 /*
242                  *      Use the debug output file, if specified,
243                  *      otherwise leave it as "request_log_file".
244                  */
245 #ifdef WITH_COMMAND_SOCKET
246                 filename = debug_log_file;
247                 if (!filename)
248 #endif
249                   filename = request_log_file;
250
251                 /*
252                  *      Debug messages get mashed to L_INFO for
253                  *      radius.log.
254                  */
255                 if (!filename) lvl = L_INFO;
256         }
257
258         if (request && filename) {
259                 radlog_func_t rl = request->radlog;
260
261                 request->radlog = NULL;
262
263                 /*
264                  *      This is SLOW!  Doing it for every log message
265                  *      in every request is NOT recommended!
266                  */
267                 
268                 radius_xlat(buffer, sizeof(buffer), filename,
269                             request, NULL, NULL); /* FIXME: escape chars! */
270                 request->radlog = rl;
271                 
272                 p = strrchr(buffer, FR_DIR_SEP);
273                 if (p) {
274                         *p = '\0';
275                         if (rad_mkdir(buffer, S_IRWXU) < 0) {
276                                 radlog(L_ERR, "Failed creating %s: %s",
277                                        buffer,strerror(errno));
278                                 va_end(ap);
279                                 return;
280                         }
281                         *p = FR_DIR_SEP;
282                 }
283
284                 fp = fopen(buffer, "a");
285         }
286
287         /*
288          *      Print timestamps to the file.
289          */
290         if (fp) {
291                 time_t timeval;
292                 timeval = time(NULL);
293
294 #ifdef HAVE_GMTIME_R
295                 if (log_dates_utc) {
296                         struct tm utc;
297                         gmtime_r(&timeval, &utc);
298                         asctime_r(&utc, buffer);
299                 } else
300 #endif
301                         CTIME_R(&timeval, buffer, sizeof(buffer) - 1);
302                 
303                 len = strlen(buffer);
304                 p = strrchr(buffer, '\n');
305                 if (p) {
306                         p[0] = ' ';
307                         p[1] = '\0';
308                 }
309                 
310                 len += strlcpy(buffer + len, 
311                                fr_int2str(levels, (lvl & ~L_CONS), ": "), 
312                                sizeof(buffer) - len);
313                                
314                 if (len >= sizeof(buffer)) goto finish;
315         }
316         
317         if (request && request->module[0]) {
318                 len = snprintf(buffer + len, sizeof(buffer) - len, "%s : ",
319                                request->module);
320                                
321                 if (len >= sizeof(buffer)) goto finish;
322         }
323         
324         vsnprintf(buffer + len, sizeof(buffer) - len, msg, ap);
325         
326         finish:
327         if (!fp) {
328                 if (request) {
329                         radlog(lvl, "(%u) %s", request->number, buffer);
330                 } else {
331                         radlog(lvl, "%s", buffer);
332                 }
333         } else {
334                 if (request) fprintf(fp, "(%u) ", request->number);
335                 fputs(buffer, fp);
336                 fputc('\n', fp);
337                 fclose(fp);
338         }
339
340         va_end(ap);
341 }