/src/proftpd/lib/pr-syslog.c
Line | Count | Source (jump to first uncovered line) |
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 | | #include "conf.h" |
31 | | |
32 | | #if defined(SOLARIS2) || defined(IRIX6) || defined(SYSV5UNIXWARE7) |
33 | | # define HAVE_DEV_LOG_STREAMS 1 |
34 | | #endif /* SOLARIS2 or IRIX6 or SYSV5UNIXWARE7 */ |
35 | | |
36 | | #ifdef HAVE_DEV_LOG_STREAMS |
37 | | # include <sys/strlog.h> |
38 | | #endif |
39 | | |
40 | | static int sock_type = SOCK_DGRAM; |
41 | | static int log_opts = 0; |
42 | | static const char *log_ident = NULL; |
43 | | static int log_facility = LOG_USER; |
44 | | static int log_mask = 0xff; |
45 | | |
46 | | #ifdef HAVE___PROGNAME |
47 | | extern char *__progname; |
48 | | #endif /* HAVE___PROGNAME */ |
49 | | |
50 | | #if defined(SOLARIS2_9) || defined(SOLARIS2_10) |
51 | | /* These tables are used for populating the stupid Solaris 9/10 syslog |
52 | | * "header". |
53 | | */ |
54 | | |
55 | | struct { |
56 | | int facility; |
57 | | const char *name; |
58 | | |
59 | | } syslog_facility_names[] = { |
60 | | { LOG_AUTHPRIV, "auth" }, |
61 | | #ifdef HAVE_LOG_FTP |
62 | | { LOG_FTP, "ftp" }, |
63 | | #endif |
64 | | #ifdef HAVE_LOG_CRON |
65 | | { LOG_CRON, "cron" }, |
66 | | #endif |
67 | | { LOG_DAEMON, "daemon" }, |
68 | | { LOG_KERN, "kern" }, |
69 | | { LOG_LOCAL0, "local0" }, |
70 | | { LOG_LOCAL1, "local1" }, |
71 | | { LOG_LOCAL2, "local2" }, |
72 | | { LOG_LOCAL3, "local3" }, |
73 | | { LOG_LOCAL4, "local4" }, |
74 | | { LOG_LOCAL5, "local5" }, |
75 | | { LOG_LOCAL6, "local6" }, |
76 | | { LOG_LOCAL7, "local7" }, |
77 | | { LOG_LPR, "lpr" }, |
78 | | { LOG_MAIL, "mail" }, |
79 | | { LOG_NEWS, "news" }, |
80 | | { LOG_USER, "user" }, |
81 | | { LOG_UUCP, "uucp" }, |
82 | | { 0, NULL } |
83 | | }; |
84 | | |
85 | | struct { |
86 | | int level; |
87 | | const char *name; |
88 | | |
89 | | } syslog_level_names[] = { |
90 | | { PR_LOG_EMERG, "emerg" }, |
91 | | { PR_LOG_ALERT, "alert" }, |
92 | | { PR_LOG_CRIT, "crit" }, |
93 | | { PR_LOG_ERR, "error" }, |
94 | | { PR_LOG_ERR, "error" }, |
95 | | { PR_LOG_WARNING, "warn" }, |
96 | | { PR_LOG_NOTICE, "notice" }, |
97 | | { PR_LOG_INFO, "info" }, |
98 | | { PR_LOG_DEBUG, "debug" }, |
99 | | { 0, NULL } |
100 | | }; |
101 | | |
102 | | #endif /* Solaris 9 or 10 */ |
103 | | |
104 | | static void pr_vsyslog(int sockfd, int pri, register const char *fmt, |
105 | 0 | va_list ap) { |
106 | 0 | time_t now; |
107 | 0 | static char logbuf[PR_TUNABLE_BUFFER_SIZE] = {'\0'}; |
108 | 0 | size_t buflen = 0; |
109 | 0 | int len = 0, saved_errno = errno; |
110 | |
|
111 | | #ifdef HAVE_DEV_LOG_STREAMS |
112 | | struct strbuf ctl, dat; |
113 | | struct log_ctl lc; |
114 | | #else |
115 | 0 | # ifdef HAVE_CTIME_R |
116 | 0 | char timebuf[32]; |
117 | 0 | # endif /* HAVE_CTIME_R */ |
118 | 0 | char *timestr = NULL; |
119 | |
|
120 | 0 | # ifdef HAVE_TZNAME |
121 | 0 | char *saved_tzname[2]; |
122 | 0 | # endif /* HAVE_TZNAME */ |
123 | 0 | #endif |
124 | | |
125 | | /* Clear the buffer */ |
126 | 0 | memset(logbuf, '\0', sizeof(logbuf)); |
127 | | |
128 | | /* Check for invalid bits. */ |
129 | 0 | if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) { |
130 | 0 | pri &= LOG_PRIMASK|LOG_FACMASK; |
131 | 0 | } |
132 | | |
133 | | /* Check priority against setlogmask values. */ |
134 | 0 | if ((LOG_MASK(pri & LOG_PRIMASK) & log_mask) == 0) { |
135 | 0 | return; |
136 | 0 | } |
137 | | |
138 | | /* Set default facility if none specified. */ |
139 | 0 | if ((pri & LOG_FACMASK) == 0) { |
140 | 0 | pri |= log_facility; |
141 | 0 | } |
142 | |
|
143 | 0 | #ifndef HAVE_DEV_LOG_STREAMS |
144 | 0 | len = snprintf(logbuf, sizeof(logbuf), "<%d>", pri); |
145 | 0 | logbuf[sizeof(logbuf)-1] = '\0'; |
146 | 0 | buflen += len; |
147 | |
|
148 | 0 | # ifdef HAVE_TZNAME |
149 | | /* Preserve the old tzname setting. */ |
150 | 0 | memcpy(saved_tzname, tzname, sizeof(saved_tzname)); |
151 | 0 | # endif /* HAVE_TZNAME */ |
152 | |
|
153 | 0 | time(&now); |
154 | 0 | # ifdef HAVE_CTIME_R |
155 | 0 | memset(timebuf, '\0', sizeof(timebuf)); |
156 | 0 | timestr = ctime_r(&now, timebuf); |
157 | | # else |
158 | | timestr = ctime(&now); |
159 | | # endif /* HAVE_CTIME_R */ |
160 | |
|
161 | 0 | # ifdef HAVE_TZNAME |
162 | | /* Restore the old tzname setting, to prevent ctime(3) from inadvertently |
163 | | * affecting things, as when we're in a chroot, and ctime(3) loses the |
164 | | * timezone info. |
165 | | */ |
166 | 0 | memcpy(tzname, saved_tzname, sizeof(saved_tzname)); |
167 | 0 | # endif /* HAVE_TZNAME */ |
168 | | |
169 | | /* Remove the trailing newline from the time string returned by ctime(3). */ |
170 | 0 | timestr[strlen(timestr)-1] = '\0'; |
171 | | |
172 | | /* Skip past the leading "day of week" prefix. */ |
173 | 0 | timestr += 4; |
174 | |
|
175 | 0 | len = snprintf(&(logbuf[buflen]), sizeof(logbuf) - buflen, "%.15s ", timestr); |
176 | 0 | logbuf[sizeof(logbuf)-1] = '\0'; |
177 | 0 | buflen += len; |
178 | 0 | #endif |
179 | |
|
180 | 0 | time(&now); |
181 | |
|
182 | 0 | if (log_ident == NULL) { |
183 | 0 | #ifdef HAVE___PROGNAME |
184 | 0 | log_ident = __progname; |
185 | | #else |
186 | | log_ident = "proftpd"; |
187 | | #endif /* HAVE___PROGNAME */ |
188 | 0 | } |
189 | |
|
190 | 0 | if (buflen < sizeof(logbuf) && |
191 | 0 | log_ident != NULL) { |
192 | 0 | len = snprintf(&(logbuf[buflen]), sizeof(logbuf) - buflen, "%s", log_ident); |
193 | 0 | logbuf[sizeof(logbuf)-1] = '\0'; |
194 | 0 | buflen += len; |
195 | 0 | } |
196 | |
|
197 | 0 | if (buflen < sizeof(logbuf)-1 && |
198 | 0 | (log_opts & LOG_PID)) { |
199 | 0 | len = snprintf(&(logbuf[buflen]), sizeof(logbuf) - buflen, "[%d]", |
200 | 0 | (int) getpid()); |
201 | 0 | logbuf[sizeof(logbuf)-1] = '\0'; |
202 | 0 | buflen += len; |
203 | 0 | } |
204 | |
|
205 | 0 | if (buflen < sizeof(logbuf)-1 && |
206 | 0 | log_ident != NULL) { |
207 | 0 | len = snprintf(&(logbuf[buflen]), sizeof(logbuf) - buflen, ": "); |
208 | 0 | logbuf[sizeof(logbuf)-1] = '\0'; |
209 | 0 | buflen += len; |
210 | 0 | } |
211 | |
|
212 | | #if defined(SOLARIS2_9) || defined(SOLARIS2_10) |
213 | | /* Add in the (IMHO stupid and nonportable) syslog "header" that was added |
214 | | * to the Solaris 9/10 libc syslog(3) function. Some sites apparently |
215 | | * think that trying to use this header to generate reports of logging |
216 | | * is a Good Idea; I'll have the last laugh when those sites try to move |
217 | | * to a different platform with different syslog logging. |
218 | | * |
219 | | * The header to be added looks like: |
220 | | * |
221 | | * "[ID %lu %s.%s]" |
222 | | * |
223 | | * where the ID is generated using STRLOG_MAKE_MSGID(), a macro defined |
224 | | * in <sys/strlog.h>, and the following two strings are the syslog |
225 | | * facility and level, respectively. |
226 | | */ |
227 | | |
228 | | if (buflen < sizeof(logbuf)) { |
229 | | register unsigned int i; |
230 | | uint32_t msgid; |
231 | | const char *facility_name = "unknown", *level_name = "unknown"; |
232 | | |
233 | | STRLOG_MAKE_MSGID(fmt, msgid); |
234 | | |
235 | | for (i = 0; syslog_facility_names[i].name; i++) { |
236 | | if (syslog_facility_names[i].facility == log_facility) { |
237 | | facility_name = syslog_facility_names[i].name; |
238 | | break; |
239 | | } |
240 | | } |
241 | | |
242 | | for (i = 0; syslog_level_names[i].name; i++) { |
243 | | if (syslog_level_names[i].level == (pri & LOG_PRIMASK)) { |
244 | | level_name = syslog_level_names[i].name; |
245 | | break; |
246 | | } |
247 | | } |
248 | | |
249 | | len = snprintf(&(logbuf[buflen]), sizeof(logbuf) - buflen, |
250 | | "[ID %lu %s.%s] ", (unsigned long) msgid, facility_name, level_name); |
251 | | logbuf[sizeof(logbuf)-1] = '\0'; |
252 | | buflen += len; |
253 | | } |
254 | | #endif /* Solaris 9 or 10 */ |
255 | | |
256 | | /* Restore errno for %m format. */ |
257 | 0 | errno = saved_errno; |
258 | | |
259 | | /* We have the header. Print the user's format into the buffer. */ |
260 | 0 | if (buflen < sizeof(logbuf)) { |
261 | 0 | len = vsnprintf(&(logbuf[buflen]), sizeof(logbuf) - buflen, fmt, ap); |
262 | 0 | logbuf[sizeof(logbuf)-1] = '\0'; |
263 | 0 | buflen += len; |
264 | 0 | } |
265 | | |
266 | | /* Always make sure the buffer is NUL-terminated |
267 | | */ |
268 | 0 | logbuf[sizeof(logbuf)-1] = '\0'; |
269 | | |
270 | | /* If we have a SOCK_STREAM connection, also send ASCII NUL as a record |
271 | | * terminator. |
272 | | */ |
273 | 0 | if (sock_type == SOCK_STREAM) { |
274 | 0 | ++buflen; |
275 | 0 | } |
276 | | |
277 | | /* If we have exceeded the capacity of the buffer, we're done here. */ |
278 | 0 | if (buflen >= sizeof(logbuf)) { |
279 | 0 | return; |
280 | 0 | } |
281 | | |
282 | 0 | #ifndef HAVE_DEV_LOG_STREAMS |
283 | 0 | if (sockfd >= 0 && |
284 | 0 | send(sockfd, logbuf, buflen, 0) < 0) { |
285 | 0 | fprintf(stderr, "error sending log message '%s' to socket fd %d: %s\n", |
286 | 0 | logbuf, sockfd, strerror(errno)); |
287 | 0 | } |
288 | | #else |
289 | | |
290 | | /* Prepare the structs for use by putmsg(). As /dev/log (or /dev/conslog) |
291 | | * is a STREAMS device on Solaris (and possibly other platforms?), putmsg() is |
292 | | * used so that syslog facility and level are properly honored; write() |
293 | | * does not seem to work as desired. |
294 | | */ |
295 | | ctl.len = ctl.maxlen = sizeof(lc); |
296 | | ctl.buf = (char *) &lc; |
297 | | dat.len = dat.maxlen = buflen; |
298 | | dat.buf = logbuf; |
299 | | lc.level = 0; |
300 | | lc.flags = SL_CONSOLE; |
301 | | lc.pri = pri; |
302 | | |
303 | | putmsg(sockfd, &ctl, &dat, 0); |
304 | | #endif |
305 | 0 | } |
306 | | |
307 | 0 | void pr_syslog(int sockfd, int pri, const char *fmt, ...) { |
308 | 0 | va_list ap; |
309 | 0 | va_start(ap, fmt); |
310 | 0 | pr_vsyslog(sockfd, pri, fmt, ap); |
311 | 0 | va_end(ap); |
312 | 0 | } |
313 | | |
314 | | #ifndef HAVE_DEV_LOG_STREAMS |
315 | | /* AF_UNIX address of local logger */ |
316 | | static struct sockaddr_un syslog_addr; |
317 | | #endif |
318 | | |
319 | 0 | int pr_openlog(const char *ident, int opts, int facility) { |
320 | 0 | int sockfd; |
321 | |
|
322 | 0 | if (ident != NULL) |
323 | 0 | log_ident = ident; |
324 | |
|
325 | 0 | log_opts = opts; |
326 | |
|
327 | 0 | if (facility != 0 && (facility &~ LOG_FACMASK) == 0) |
328 | 0 | log_facility = facility; |
329 | |
|
330 | 0 | #ifndef HAVE_DEV_LOG_STREAMS |
331 | 0 | sockfd = -1; |
332 | 0 | while (1) { |
333 | 0 | socklen_t addrlen = 0; |
334 | |
|
335 | 0 | if (sockfd == -1) { |
336 | 0 | syslog_addr.sun_family = AF_UNIX; |
337 | |
|
338 | 0 | sstrncpy(syslog_addr.sun_path, PR_PATH_LOG, sizeof(syslog_addr.sun_path)); |
339 | 0 | syslog_addr.sun_path[sizeof(syslog_addr.sun_path)-1] = '\0'; |
340 | 0 | addrlen = sizeof(syslog_addr); |
341 | |
|
342 | 0 | if (log_opts & LOG_NDELAY) { |
343 | 0 | sockfd = socket(AF_UNIX, sock_type, 0); |
344 | 0 | if (sockfd < 0) { |
345 | 0 | return -1; |
346 | 0 | } |
347 | | |
348 | 0 | (void) fcntl(sockfd, F_SETFD, 1); |
349 | 0 | } |
350 | 0 | } |
351 | | |
352 | 0 | if (sockfd != -1) { |
353 | 0 | int old_errno = errno; |
354 | |
|
355 | 0 | if (connect(sockfd, (struct sockaddr *) &syslog_addr, addrlen) == -1) { |
356 | 0 | int saved_errno = errno; |
357 | 0 | close(sockfd); |
358 | 0 | sockfd = -1; |
359 | |
|
360 | 0 | if (sock_type == SOCK_DGRAM && saved_errno == EPROTOTYPE) { |
361 | | /* retry with next SOCK_STREAM */ |
362 | 0 | sock_type = SOCK_STREAM; |
363 | 0 | errno = old_errno; |
364 | 0 | continue; |
365 | 0 | } |
366 | 0 | } |
367 | 0 | } |
368 | 0 | break; |
369 | 0 | } |
370 | | #else |
371 | | sockfd = open(PR_PATH_LOG, O_WRONLY); |
372 | | |
373 | | if (sockfd < 0) { |
374 | | fprintf(stderr, "error opening '%s': %s\n", PR_PATH_LOG, strerror(errno)); |
375 | | } |
376 | | #endif |
377 | | |
378 | 0 | return sockfd; |
379 | 0 | } |
380 | | |
381 | 0 | void pr_closelog(int sockfd) { |
382 | 0 | close(sockfd); |
383 | 0 | sockfd = -1; |
384 | | |
385 | | /* Clear the identity prefix string. */ |
386 | 0 | log_ident = NULL; |
387 | | |
388 | | /* default */ |
389 | 0 | sock_type = SOCK_DGRAM; |
390 | 0 | } |
391 | | |
392 | | /* setlogmask -- set the log mask level */ |
393 | 0 | int pr_setlogmask(int new_mask) { |
394 | 0 | int old_mask; |
395 | |
|
396 | 0 | old_mask = log_mask; |
397 | 0 | if (new_mask != 0) |
398 | 0 | log_mask = new_mask; |
399 | |
|
400 | 0 | return old_mask; |
401 | 0 | } |
402 | | |
403 | 0 | int pr_setlogfacility(int new_facility) { |
404 | 0 | int old_facility; |
405 | |
|
406 | 0 | old_facility = log_facility; |
407 | 0 | if (new_facility > 0) |
408 | 0 | log_facility = new_facility; |
409 | |
|
410 | 0 | return old_facility; |
411 | 0 | } |