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
pr_vsyslog(int sockfd,int pri,register const char * fmt,va_list ap)104 static void pr_vsyslog(int sockfd, int pri, register const char *fmt,
105 va_list ap) {
106 time_t now;
107 static char logbuf[PR_TUNABLE_BUFFER_SIZE] = {'\0'};
108 size_t buflen = 0;
109 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 # ifdef HAVE_CTIME_R
116 char timebuf[32];
117 # endif /* HAVE_CTIME_R */
118 char *timestr = NULL;
119
120 # ifdef HAVE_TZNAME
121 char *saved_tzname[2];
122 # endif /* HAVE_TZNAME */
123 #endif
124
125 /* Clear the buffer */
126 memset(logbuf, '\0', sizeof(logbuf));
127
128 /* Check for invalid bits. */
129 if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
130 pri &= LOG_PRIMASK|LOG_FACMASK;
131 }
132
133 /* Check priority against setlogmask values. */
134 if ((LOG_MASK(pri & LOG_PRIMASK) & log_mask) == 0) {
135 return;
136 }
137
138 /* Set default facility if none specified. */
139 if ((pri & LOG_FACMASK) == 0) {
140 pri |= log_facility;
141 }
142
143 #ifndef HAVE_DEV_LOG_STREAMS
144 len = snprintf(logbuf, sizeof(logbuf), "<%d>", pri);
145 logbuf[sizeof(logbuf)-1] = '\0';
146 buflen += len;
147
148 # ifdef HAVE_TZNAME
149 /* Preserve the old tzname setting. */
150 memcpy(saved_tzname, tzname, sizeof(saved_tzname));
151 # endif /* HAVE_TZNAME */
152
153 time(&now);
154 # ifdef HAVE_CTIME_R
155 memset(timebuf, '\0', sizeof(timebuf));
156 timestr = ctime_r(&now, timebuf);
157 # else
158 timestr = ctime(&now);
159 # endif /* HAVE_CTIME_R */
160
161 # 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 memcpy(tzname, saved_tzname, sizeof(saved_tzname));
167 # endif /* HAVE_TZNAME */
168
169 /* Remove the trailing newline from the time string returned by ctime(3). */
170 timestr[strlen(timestr)-1] = '\0';
171
172 /* Skip past the leading "day of week" prefix. */
173 timestr += 4;
174
175 len = snprintf(&(logbuf[buflen]), sizeof(logbuf) - buflen, "%.15s ", timestr);
176 logbuf[sizeof(logbuf)-1] = '\0';
177 buflen += len;
178 #endif
179
180 time(&now);
181
182 if (log_ident == NULL) {
183 #ifdef HAVE___PROGNAME
184 log_ident = __progname;
185 #else
186 log_ident = "proftpd";
187 #endif /* HAVE___PROGNAME */
188 }
189
190 if (buflen < sizeof(logbuf) &&
191 log_ident != NULL) {
192 len = snprintf(&(logbuf[buflen]), sizeof(logbuf) - buflen, "%s", log_ident);
193 logbuf[sizeof(logbuf)-1] = '\0';
194 buflen += len;
195 }
196
197 if (buflen < sizeof(logbuf)-1 &&
198 (log_opts & LOG_PID)) {
199 len = snprintf(&(logbuf[buflen]), sizeof(logbuf) - buflen, "[%d]",
200 (int) getpid());
201 logbuf[sizeof(logbuf)-1] = '\0';
202 buflen += len;
203 }
204
205 if (buflen < sizeof(logbuf)-1 &&
206 log_ident != NULL) {
207 len = snprintf(&(logbuf[buflen]), sizeof(logbuf) - buflen, ": ");
208 logbuf[sizeof(logbuf)-1] = '\0';
209 buflen += len;
210 }
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 errno = saved_errno;
258
259 /* We have the header. Print the user's format into the buffer. */
260 if (buflen < sizeof(logbuf)) {
261 len = vsnprintf(&(logbuf[buflen]), sizeof(logbuf) - buflen, fmt, ap);
262 logbuf[sizeof(logbuf)-1] = '\0';
263 buflen += len;
264 }
265
266 /* Always make sure the buffer is NUL-terminated
267 */
268 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 if (sock_type == SOCK_STREAM) {
274 ++buflen;
275 }
276
277 /* If we have exceeded the capacity of the buffer, we're done here. */
278 if (buflen >= sizeof(logbuf)) {
279 return;
280 }
281
282 #ifndef HAVE_DEV_LOG_STREAMS
283 if (sockfd >= 0 &&
284 send(sockfd, logbuf, buflen, 0) < 0) {
285 fprintf(stderr, "error sending log message '%s' to socket fd %d: %s\n",
286 logbuf, sockfd, strerror(errno));
287 }
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 }
306
pr_syslog(int sockfd,int pri,const char * fmt,...)307 void pr_syslog(int sockfd, int pri, const char *fmt, ...) {
308 va_list ap;
309 va_start(ap, fmt);
310 pr_vsyslog(sockfd, pri, fmt, ap);
311 va_end(ap);
312 }
313
314 #ifndef HAVE_DEV_LOG_STREAMS
315 /* AF_UNIX address of local logger */
316 static struct sockaddr_un syslog_addr;
317 #endif
318
pr_openlog(const char * ident,int opts,int facility)319 int pr_openlog(const char *ident, int opts, int facility) {
320 int sockfd;
321
322 if (ident != NULL)
323 log_ident = ident;
324
325 log_opts = opts;
326
327 if (facility != 0 && (facility &~ LOG_FACMASK) == 0)
328 log_facility = facility;
329
330 #ifndef HAVE_DEV_LOG_STREAMS
331 sockfd = -1;
332 while (1) {
333 socklen_t addrlen = 0;
334
335 if (sockfd == -1) {
336 syslog_addr.sun_family = AF_UNIX;
337
338 sstrncpy(syslog_addr.sun_path, PR_PATH_LOG, sizeof(syslog_addr.sun_path));
339 syslog_addr.sun_path[sizeof(syslog_addr.sun_path)-1] = '\0';
340 addrlen = sizeof(syslog_addr);
341
342 if (log_opts & LOG_NDELAY) {
343 sockfd = socket(AF_UNIX, sock_type, 0);
344 if (sockfd < 0) {
345 return -1;
346 }
347
348 (void) fcntl(sockfd, F_SETFD, 1);
349 }
350 }
351
352 if (sockfd != -1) {
353 int old_errno = errno;
354
355 if (connect(sockfd, (struct sockaddr *) &syslog_addr, addrlen) == -1) {
356 int saved_errno = errno;
357 close(sockfd);
358 sockfd = -1;
359
360 if (sock_type == SOCK_DGRAM && saved_errno == EPROTOTYPE) {
361 /* retry with next SOCK_STREAM */
362 sock_type = SOCK_STREAM;
363 errno = old_errno;
364 continue;
365 }
366 }
367 }
368 break;
369 }
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 return sockfd;
379 }
380
pr_closelog(int sockfd)381 void pr_closelog(int sockfd) {
382 close(sockfd);
383 sockfd = -1;
384
385 /* Clear the identity prefix string. */
386 log_ident = NULL;
387
388 /* default */
389 sock_type = SOCK_DGRAM;
390 }
391
392 /* setlogmask -- set the log mask level */
pr_setlogmask(int new_mask)393 int pr_setlogmask(int new_mask) {
394 int old_mask;
395
396 old_mask = log_mask;
397 if (new_mask != 0)
398 log_mask = new_mask;
399
400 return old_mask;
401 }
402
pr_setlogfacility(int new_facility)403 int pr_setlogfacility(int new_facility) {
404 int old_facility;
405
406 old_facility = log_facility;
407 if (new_facility > 0)
408 log_facility = new_facility;
409
410 return old_facility;
411 }
412