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