Coverage Report

Created: 2025-07-12 06:57

/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
}