Coverage Report

Created: 2023-03-26 06:19

/src/proftpd/modules/mod_log.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 1997, 1998 Public Flood Software
4
 * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
5
 * Copyright (c) 2001-2023 The ProFTPD Project team
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
20
 *
21
 * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
22
 * and other respective copyright holders give permission to link this program
23
 * with OpenSSL, and distribute the resulting executable, without including
24
 * the source code for OpenSSL in the source distribution.
25
 */
26
27
/* Flexible logging module for proftpd */
28
29
#include "conf.h"
30
#include "privs.h"
31
#include "logfmt.h"
32
#include "jot.h"
33
34
0
#define MOD_LOG_VERSION       "mod_log/1.0"
35
36
module log_module;
37
38
/* Max path length plus 128 bytes for additional info. */
39
#define EXTENDED_LOG_BUFFER_SIZE    (PR_TUNABLE_PATH_MAX + 128)
40
41
0
#define EXTENDED_LOG_MODE     0644
42
0
#define EXTENDED_LOG_FORMAT_DEFAULT   "default"
43
44
typedef struct logformat_struc  logformat_t;
45
typedef struct logfile_struc  logfile_t;
46
47
struct logformat_struc {
48
  logformat_t *next, *prev;
49
50
  char *lf_fmt_name;
51
  unsigned char *lf_format;
52
};
53
54
struct logfile_struc {
55
  logfile_t   *next, *prev;
56
57
  char      *lf_filename;
58
  int     lf_fd;
59
  int     lf_syslog_level;
60
61
  logformat_t   *lf_format;
62
  pr_jot_filters_t  *lf_jot_filters;
63
64
  /* Pointer to the "owning" configuration */
65
  config_rec    *lf_conf;
66
};
67
68
/* Value for lf_fd signalling that data should be logged via syslog, rather
69
 * than written to a file.
70
 */
71
0
#define EXTENDED_LOG_SYSLOG -4
72
73
static pool *log_pool = NULL;
74
static logformat_t *formats = NULL;
75
static xaset_t *format_set = NULL;
76
static logfile_t *logs = NULL;
77
static xaset_t *log_set = NULL;
78
79
static const char *trace_channel = "extlog";
80
81
/* format string args:
82
   %A     - Anonymous username (password given)
83
   %a     - Remote client IP address
84
   %b     - Bytes sent for request
85
   %{basename}    - Basename of path
86
   %c     - Class
87
   %D     - full directory path
88
   %d     - directory (for client)
89
   %E     - End-of-session reason
90
   %{FOOBAR}e   - Contents of environment variable FOOBAR
91
   %F     - Transfer path (filename for client)
92
   %f     - Filename
93
   %g     - Local user's primary group name
94
   %H                   - Local IP address of server handling session
95
   %h     - Remote client DNS name
96
   %I                   - Total number of "raw" bytes read in from network
97
   %J                   - Request (command) arguments (file.txt, etc)
98
   %L                   - Local IP address contacted by client
99
   %l     - Remote logname (from identd)
100
   %m     - Request (command) method (RETR, etc)
101
   %O                   - Total number of "raw" bytes written out to network
102
   %P                   - Process ID of child serving request
103
   %p     - Port of server serving request
104
   %R                   - Response time for command/request, in milliseconds
105
   %r     - Full request (command)
106
   %s     - Response code (status)
107
   %S                   - Response string
108
   %T     - Time taken to transfer file, in seconds
109
   %t     - Time
110
   %{format}t   - Formatted time (strftime(3) format)
111
   %U                   - Original username sent by client
112
   %u     - Local user
113
   %V                   - DNS name of server serving request
114
   %v     - ServerName of server serving request
115
   %w                   - RNFR path ("whence" a rename comes, i.e. the source)
116
   %{epoch}             - Unix epoch (seconds since Jan 1 1970)
117
   %{file-modified}     - Indicates whether a file is being modified
118
                          (i.e. already exists) or not.
119
   %{file-offset}       - Contains the offset at which the file is read/written
120
   %{file-size}         - Contains the file size at the end of the transfer
121
   %{iso8601}           - ISO-8601 timestamp: YYYY-MM-dd HH:mm:ss,SSS
122
                            for example: "1999-11-27 15:49:37,459"
123
   %{microsecs}         - 6 digits of microseconds of current time
124
   %{millisecs}         - 3 digits of milliseconds of current time
125
   %{protocol}          - Current protocol (e.g. "ftp", "sftp", etc)
126
   %{uid}               - UID of logged-in user
127
   %{gid}               - Primary GID of logged-in user
128
   %{transfer-failure}  - reason, or "-"
129
   %{transfer-millisecs}- Time taken to transfer file, in milliseconds
130
   %{transfer-status}   - "success", "failed", "cancelled", "timeout", or "-"
131
   %{transfer-type}     - "binary" or "ASCII"
132
   %{version}           - ProFTPD version
133
*/
134
135
/* Necessary prototypes */
136
static int log_sess_init(void);
137
static void log_xfer_stalled_ev(const void *, void *);
138
139
static void parse_logformat(const char *directive, char *fmt_name,
140
0
    char *fmt_text) {
141
0
  int res;
142
0
  pool *tmp_pool;
143
0
  pr_jot_ctx_t *jot_ctx;
144
0
  pr_jot_parsed_t *jot_parsed;
145
0
  unsigned char format_buf[4096] = {'\0'};
146
0
  size_t fmt_len;
147
0
  logformat_t *lf;
148
149
  /* This function can cause potential problems.  Custom LogFormats
150
   * might overrun the format buffer.  Fixing this problem involves a
151
   * rewrite of most of this module.  This will happen post 1.2.0.
152
   */
153
154
0
  tmp_pool = make_sub_pool(log_pool);
155
0
  jot_ctx = pcalloc(tmp_pool, sizeof(pr_jot_ctx_t));
156
0
  jot_parsed = pcalloc(tmp_pool, sizeof(pr_jot_parsed_t));
157
0
  jot_parsed->bufsz = jot_parsed->buflen = sizeof(format_buf);
158
0
  jot_parsed->ptr = jot_parsed->buf = format_buf;
159
160
0
  jot_ctx->log = jot_parsed;
161
162
0
  res = pr_jot_parse_logfmt(tmp_pool, fmt_text, jot_ctx, pr_jot_parse_on_meta,
163
0
    pr_jot_parse_on_unknown, pr_jot_parse_on_other, 0);
164
0
  if (res < 0) {
165
0
    pr_log_pri(PR_LOG_NOTICE, MOD_LOG_VERSION
166
0
      ": error parsing LogFormat '%s': %s", fmt_text, strerror(errno));
167
168
0
    destroy_pool(tmp_pool);
169
0
    return;
170
0
  }
171
172
0
  fmt_len = jot_parsed->bufsz - jot_parsed->buflen;
173
174
0
  lf = (logformat_t *) pcalloc(log_pool, sizeof(logformat_t));
175
0
  lf->lf_fmt_name = pstrdup(log_pool, fmt_name);
176
0
  lf->lf_format = palloc(log_pool, fmt_len + 1);
177
0
  memcpy(lf->lf_format, format_buf, fmt_len);
178
0
  lf->lf_format[fmt_len] = '\0';
179
180
0
  if (format_set == NULL) {
181
0
    format_set = xaset_create(log_pool, NULL);
182
0
  }
183
184
0
  xaset_insert_end(format_set, (xasetmember_t *) lf);
185
0
  formats = (logformat_t *) format_set->xas_list;
186
187
0
  if (directive != NULL) {
188
0
    config_rec *c;
189
0
    char *ptr;
190
191
    /* Store the parsed format in the config tree as well, for use by other
192
     * logging-related modules.
193
     */
194
0
    c = add_config_param(directive, 2, NULL, NULL);
195
0
    c->argv[0] = pstrdup(c->pool, fmt_name);
196
0
    c->argv[1] = palloc(c->pool, fmt_len + 1);
197
198
0
    ptr = c->argv[1];
199
0
    memcpy(ptr, format_buf, fmt_len);
200
0
    ptr[fmt_len] = '\0';
201
0
  }
202
203
0
  destroy_pool(tmp_pool);
204
0
}
205
206
/* Syntax: LogFormat name "format string" */
207
0
MODRET set_logformat(cmd_rec *cmd) {
208
0
  CHECK_ARGS(cmd, 2);
209
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL);
210
211
0
  if (strlen(cmd->argv[1]) == 0) {
212
0
    CONF_ERROR(cmd, "missing required name parameter");
213
0
  }
214
215
0
  parse_logformat(cmd->argv[0], cmd->argv[1], cmd->argv[2]);
216
0
  return PR_HANDLED(cmd);
217
0
}
218
219
/* usage: LogOptions opt1 ... */
220
0
MODRET set_logoptions(cmd_rec *cmd) {
221
0
  register unsigned int i;
222
0
  int ctx;
223
0
  unsigned long log_opts = PR_LOG_OPT_DEFAULT;
224
0
  config_rec *c;
225
226
0
  if (cmd->argc < 2) {
227
0
    CONF_ERROR(cmd, "wrong number of parameters");
228
0
  }
229
230
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL|CONF_VIRTUAL);
231
232
0
  for (i = 1; i < cmd->argc; i++) {
233
0
    char action, *opt;
234
235
0
    opt = cmd->argv[i];
236
0
    action = *opt;
237
238
0
    if (action != '+' &&
239
0
        action != '-') {
240
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "bad LogOption: '", opt, "'",
241
0
        NULL));
242
0
    }
243
244
0
    opt++;
245
246
0
    if (strcasecmp(opt, "Timestamp") == 0) {
247
0
      switch (action) {
248
0
        case '-':
249
0
          log_opts &= ~PR_LOG_OPT_USE_TIMESTAMP;
250
0
          break;
251
252
0
        case '+':
253
0
          log_opts |= PR_LOG_OPT_USE_TIMESTAMP;
254
0
          break;
255
0
      }
256
257
0
    } else if (strcasecmp(opt, "Hostname") == 0) {
258
0
      switch (action) {
259
0
        case '-':
260
0
          log_opts &= ~PR_LOG_OPT_USE_HOSTNAME;
261
0
          break;
262
263
0
        case '+':
264
0
          log_opts |= PR_LOG_OPT_USE_HOSTNAME;
265
0
          break;
266
0
      }
267
268
0
    } else if (strcasecmp(opt, "VirtualHost") == 0) {
269
0
      switch (action) {
270
0
        case '-':
271
0
          log_opts &= ~PR_LOG_OPT_USE_VHOST;
272
0
          break;
273
274
0
        case '+':
275
0
          log_opts |= PR_LOG_OPT_USE_VHOST;
276
0
          break;
277
0
      }
278
279
0
    } else if (strcasecmp(opt, "RoleBasedProcessLabels") == 0) {
280
0
      switch (action) {
281
0
        case '-':
282
0
          log_opts &= ~PR_LOG_OPT_USE_ROLE_BASED_PROCESS_LABELS;
283
0
          break;
284
285
0
        case '+':
286
0
          log_opts |= PR_LOG_OPT_USE_ROLE_BASED_PROCESS_LABELS;
287
0
          break;
288
0
      }
289
290
0
    } else {
291
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown LogOption: '",
292
0
        opt, "'", NULL));
293
0
    }
294
0
  }
295
296
0
  c = add_config_param(cmd->argv[0], 1, NULL);
297
0
  c->argv[0] = palloc(c->pool, sizeof(unsigned long));
298
0
  *((unsigned long *) c->argv[0]) = log_opts;
299
300
0
  ctx = (cmd->config && cmd->config->config_type != CONF_PARAM ?
301
0
    cmd->config->config_type : cmd->server->config_type ?
302
0
    cmd->server->config_type : CONF_ROOT);
303
304
0
  if (ctx == CONF_ROOT) {
305
    /* If we're the "server config" context, set the LogOptions here,
306
     * too.  This will apply these LogOptions to the daemon process.
307
     */
308
0
    if (pr_log_set_options(log_opts) < 0) {
309
0
      pr_log_debug(DEBUG6, "%s: error setting LogOptions (%lu): %s",
310
0
        (char *) cmd->argv[0], log_opts, strerror(errno));
311
0
    }
312
0
  }
313
314
0
  return PR_HANDLED(cmd);
315
0
}
316
317
/* Syntax: ExtendedLog file [<cmd-classes> [<name>]] */
318
0
MODRET set_extendedlog(cmd_rec *cmd) {
319
0
  config_rec *c = NULL;
320
0
  int argc;
321
0
  char *path;
322
323
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
324
325
0
  argc = cmd->argc;
326
327
0
  if (argc < 2) {
328
0
    CONF_ERROR(cmd, "Syntax: ExtendedLog file [<cmd-classes> [<name>]]");
329
0
  }
330
331
0
  c = add_config_param(cmd->argv[0], 3, NULL, NULL, NULL);
332
333
0
  path = cmd->argv[1];
334
0
  if (strncasecmp(path, "syslog:", 7) == 0) {
335
0
    char *ptr;
336
337
0
    ptr = strchr(path, ':');
338
339
0
    if (pr_log_str2sysloglevel(++ptr) < 0) {
340
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown syslog level: '",
341
0
        ptr, "'", NULL));
342
0
    }
343
344
0
    c->argv[0] = pstrdup(log_pool, path);
345
346
0
  } else if (path[0] != '/') {
347
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "relative paths not allowed: '",
348
0
      path, "'", NULL));
349
350
0
  } else {
351
0
    c->argv[0] = pstrdup(log_pool, path);
352
0
  }
353
354
0
  if (argc > 2) {
355
0
    pr_jot_filters_t *jot_filters;
356
0
    const char *rules;
357
358
0
    rules = cmd->argv[2];
359
0
    jot_filters = pr_jot_filters_create(c->pool, rules,
360
0
      PR_JOT_FILTER_TYPE_CLASSES, 0);
361
0
    if (jot_filters == NULL) {
362
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "invalid log class in '", rules,
363
0
        "': ", strerror(errno), NULL));
364
0
    }
365
366
0
    c->argv[1] = jot_filters;
367
0
  }
368
369
0
  if (argc > 3) {
370
0
    c->argv[2] = pstrdup(log_pool, cmd->argv[3]);
371
0
  }
372
373
0
  return PR_HANDLED(cmd);
374
0
}
375
376
/* Syntax: AllowLogSymlinks <on|off> */
377
0
MODRET set_allowlogsymlinks(cmd_rec *cmd) {
378
0
  int bool = -1;
379
0
  config_rec *c = NULL;
380
381
0
  CHECK_ARGS(cmd, 1);
382
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
383
384
0
  bool = get_boolean(cmd, 1);
385
0
  if (bool == -1)
386
0
    CONF_ERROR(cmd, "expected Boolean parameter");
387
388
0
  c = add_config_param(cmd->argv[0], 1, NULL);
389
0
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
390
0
  *((unsigned char *) c->argv[0]) = bool;
391
392
0
  return PR_HANDLED(cmd);
393
0
}
394
395
/* Syntax: ServerLog <filename> */
396
0
MODRET set_serverlog(cmd_rec *cmd) {
397
0
  CHECK_ARGS(cmd, 1);
398
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
399
400
0
  add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
401
402
0
  return PR_HANDLED(cmd);
403
0
}
404
405
/* Syntax: SystemLog <filename> */
406
0
MODRET set_systemlog(cmd_rec *cmd) {
407
0
  CHECK_ARGS(cmd, 1);
408
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL);
409
410
0
  (void) add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
411
0
  return PR_HANDLED(cmd);
412
0
}
413
414
0
static struct tm *get_gmtoff(pool *p, int *tz) {
415
0
  time_t now;
416
0
  struct tm *gmt, *tm = NULL;
417
418
  /* Note that the ordering of the calls to gmtime(3) and pr_localtime()
419
   * here are IMPORTANT; gmtime(3) MUST be called first.  Otherwise,
420
   * the TZ environment variable may not be honored as one would expect;
421
   * see:
422
   *  https://forums.proftpd.org/smf/index.php/topic,11971.0.html
423
   */
424
0
  time(&now);
425
426
0
#if defined(HAVE_GMTIME_R)
427
0
  gmt = gmtime_r(&now, pcalloc(p, sizeof(struct tm)));
428
#else
429
  gmt = gmtime(&now);
430
#endif /* HAVE_GMTIME_R */
431
0
  if (gmt != NULL) {
432
0
    tm = pr_localtime(p, &now);
433
0
    if (tm != NULL) {
434
0
      int days, hours, minutes;
435
436
0
      days = tm->tm_yday - gmt->tm_yday;
437
0
      hours = ((days < -1 ? 24 : 1 < days ? -24 : days * 24)
438
0
              + tm->tm_hour - gmt->tm_hour);
439
0
      minutes = hours * 60 + tm->tm_min - gmt->tm_min;
440
0
      *tz = minutes;
441
0
    }
442
0
  }
443
444
0
  return tm;
445
0
}
446
447
/* Note: maybe the pr_buffer_t should be made to look like this? */
448
struct extlog_buffer {
449
  char *ptr, *buf;
450
  size_t bufsz, buflen;
451
};
452
453
static void extlog_buffer_append(struct extlog_buffer *log, const char *text,
454
0
    size_t text_len) {
455
0
  if (text == NULL ||
456
0
      text_len == 0) {
457
0
    return;
458
0
  }
459
460
0
  if (text_len > log->buflen) {
461
0
    text_len = log->buflen;
462
0
  }
463
464
0
  pr_trace_msg(trace_channel, 19, "appending text '%.*s' (%lu) to buffer",
465
0
    (int) text_len, text, (unsigned long) text_len);
466
0
  memcpy(log->buf, text, text_len);
467
0
  log->buf += text_len;
468
0
  log->buflen -= text_len;
469
0
}
470
471
static int resolve_on_meta(pool *p, pr_jot_ctx_t *jot_ctx,
472
0
    unsigned char logfmt_id, const char *jot_hint, const void *val) {
473
0
  struct extlog_buffer *log;
474
475
0
  log = jot_ctx->log;
476
0
  if (log->buflen > 0) {
477
0
    const char *text = NULL;
478
0
    size_t text_len = 0;
479
0
    char buf[1024];
480
481
0
    switch (logfmt_id) {
482
0
      case LOGFMT_META_MICROSECS: {
483
0
        unsigned long num;
484
485
0
        num = *((double *) val);
486
0
        text_len = pr_snprintf(buf, sizeof(buf)-1, "%06lu", num);
487
0
        buf[text_len] = '\0';
488
0
        text = buf;
489
0
        break;
490
0
      }
491
492
0
      case LOGFMT_META_MILLISECS: {
493
0
        unsigned long num;
494
495
0
        num = *((double *) val);
496
0
        text_len = pr_snprintf(buf, sizeof(buf)-1, "%03lu", num);
497
0
        buf[text_len] = '\0';
498
0
        text = buf;
499
0
        break;
500
0
      }
501
502
0
      case LOGFMT_META_LOCAL_PORT:
503
0
      case LOGFMT_META_REMOTE_PORT:
504
0
      case LOGFMT_META_RESPONSE_CODE:
505
0
      case LOGFMT_META_XFER_PORT: {
506
0
        int num;
507
508
0
        num = *((double *) val);
509
0
        text_len = pr_snprintf(buf, sizeof(buf)-1, "%d", num);
510
0
        buf[text_len] = '\0';
511
0
        text = buf;
512
0
        break;
513
0
      }
514
515
0
      case LOGFMT_META_UID: {
516
0
        uid_t uid;
517
518
0
        uid = *((double *) val);
519
0
        text = pr_uid2str(p, uid);
520
0
        break;
521
0
      }
522
523
0
      case LOGFMT_META_GID: {
524
0
        gid_t gid;
525
526
0
        gid = *((double *) val);
527
0
        text = pr_gid2str(p, gid);
528
0
        break;
529
0
      }
530
531
0
      case LOGFMT_META_BYTES_SENT:
532
0
      case LOGFMT_META_FILE_OFFSET:
533
0
      case LOGFMT_META_FILE_SIZE:
534
0
      case LOGFMT_META_RAW_BYTES_IN:
535
0
      case LOGFMT_META_RAW_BYTES_OUT:
536
0
      case LOGFMT_META_RESPONSE_MS:
537
0
      case LOGFMT_META_XFER_MS: {
538
0
        off_t num;
539
540
0
        num = *((double *) val);
541
0
        text_len = pr_snprintf(buf, sizeof(buf)-1, "%" PR_LU, (pr_off_t) num);
542
0
        buf[text_len] = '\0';
543
0
        text = buf;
544
0
        break;
545
0
      }
546
547
0
      case LOGFMT_META_EPOCH:
548
0
      case LOGFMT_META_PID: {
549
0
        unsigned long num;
550
551
0
        num = *((double *) val);
552
0
        text_len = pr_snprintf(buf, sizeof(buf)-1, "%lu", num);
553
0
        buf[text_len] = '\0';
554
0
        text = buf;
555
0
        break;
556
0
      }
557
558
0
      case LOGFMT_META_FILE_MODIFIED: {
559
0
        int truth;
560
561
0
        truth = *((int *) val);
562
0
        text = truth ? "true" : "false";
563
0
        break;
564
0
      }
565
566
0
      case LOGFMT_META_SECONDS: {
567
0
        float num;
568
569
0
        num = *((double *) val);
570
0
        text_len = pr_snprintf(buf, sizeof(buf)-1, "%0.3f", num);
571
0
        buf[text_len] = '\0';
572
0
        text = buf;
573
0
        break;
574
0
      }
575
576
      /* mod_log has a different implementation of META_TIME than the Jot
577
       * API.  Thus we do it ourselves here.
578
       */
579
0
      case LOGFMT_META_TIME: {
580
0
        char sign, *time_fmt = "[%d/%b/%Y:%H:%M:%S ";
581
0
        struct tm t;
582
0
        int internal_fmt = TRUE, with_tz = FALSE;
583
584
0
        if (jot_hint != NULL) {
585
0
          time_fmt = (char *) jot_hint;
586
0
          internal_fmt = FALSE;
587
0
        }
588
589
0
        t = *get_gmtoff(p, &with_tz);
590
0
        sign = (with_tz < 0 ? '-' : '+');
591
0
        if (with_tz < 0) {
592
0
          with_tz = -with_tz;
593
0
        }
594
595
0
        if (time_fmt != NULL) {
596
0
          memset(buf, '\0', sizeof(buf));
597
0
          text_len = strftime(buf, sizeof(buf) - 1, time_fmt, &t);
598
0
          if (internal_fmt == TRUE) {
599
0
            if (text_len < sizeof(buf)) {
600
0
              text_len += pr_snprintf(buf + text_len,
601
0
                sizeof(buf) - text_len - 1, "%c%.2d%.2d]", sign,
602
0
                (with_tz / 60), (with_tz % 60));
603
0
            }
604
0
          }
605
606
0
          text = buf;
607
0
        }
608
609
0
        break;
610
0
      }
611
612
0
      case LOGFMT_META_ANON_PASS:
613
0
      case LOGFMT_META_BASENAME:
614
0
      case LOGFMT_META_CLASS:
615
0
      case LOGFMT_META_CMD_PARAMS:
616
0
      case LOGFMT_META_COMMAND:
617
0
      case LOGFMT_META_DIR_NAME:
618
0
      case LOGFMT_META_DIR_PATH:
619
0
      case LOGFMT_META_ENV_VAR:
620
0
      case LOGFMT_META_EOS_REASON:
621
0
      case LOGFMT_META_FILENAME:
622
0
      case LOGFMT_META_GROUP:
623
0
      case LOGFMT_META_IDENT_USER:
624
0
      case LOGFMT_META_ISO8601:
625
0
      case LOGFMT_META_LOCAL_FQDN:
626
0
      case LOGFMT_META_LOCAL_IP:
627
0
      case LOGFMT_META_LOCAL_NAME:
628
0
      case LOGFMT_META_METHOD:
629
0
      case LOGFMT_META_NOTE_VAR:
630
0
      case LOGFMT_META_ORIGINAL_USER:
631
0
      case LOGFMT_META_PROTOCOL:
632
0
      case LOGFMT_META_REMOTE_HOST:
633
0
      case LOGFMT_META_REMOTE_IP:
634
0
      case LOGFMT_META_RENAME_FROM:
635
0
      case LOGFMT_META_RESPONSE_STR:
636
0
      case LOGFMT_META_USER:
637
0
      case LOGFMT_META_VERSION:
638
0
      case LOGFMT_META_VHOST_IP:
639
0
      case LOGFMT_META_XFER_FAILURE:
640
0
      case LOGFMT_META_XFER_PATH:
641
0
      case LOGFMT_META_XFER_SPEED:
642
0
      case LOGFMT_META_XFER_STATUS:
643
0
      case LOGFMT_META_XFER_TYPE:
644
0
      default:
645
0
        text = val;
646
0
        break;
647
0
    }
648
649
0
    if (text != NULL &&
650
0
        text_len == 0) {
651
0
      text_len = strlen(text);
652
0
    }
653
654
0
    extlog_buffer_append(log, text, text_len);
655
0
  }
656
657
0
  return 0;
658
0
}
659
660
static int resolve_on_default(pool *p, pr_jot_ctx_t *jot_ctx,
661
0
    unsigned char logfmt_id) {
662
0
  struct extlog_buffer *log;
663
664
0
  log = jot_ctx->log;
665
0
  if (log->buflen > 0) {
666
0
    const char *text = NULL;
667
0
    size_t text_len = 0;
668
669
0
    switch (logfmt_id) {
670
0
      case LOGFMT_META_ANON_PASS:
671
0
      case LOGFMT_META_IDENT_USER:
672
0
        text = "UNKNOWN";
673
0
        text_len = strlen(text);
674
0
        break;
675
676
0
      case LOGFMT_META_BASENAME:
677
0
      case LOGFMT_META_BYTES_SENT:
678
0
      case LOGFMT_META_CLASS:
679
0
      case LOGFMT_META_FILENAME:
680
0
      case LOGFMT_META_FILE_OFFSET:
681
0
      case LOGFMT_META_FILE_SIZE:
682
0
      case LOGFMT_META_GROUP:
683
0
      case LOGFMT_META_ORIGINAL_USER:
684
0
      case LOGFMT_META_RENAME_FROM:
685
0
      case LOGFMT_META_RESPONSE_CODE:
686
0
      case LOGFMT_META_RESPONSE_MS:
687
0
      case LOGFMT_META_RESPONSE_STR:
688
0
      case LOGFMT_META_SECONDS:
689
0
      case LOGFMT_META_USER:
690
0
      case LOGFMT_META_XFER_FAILURE:
691
0
      case LOGFMT_META_XFER_MS:
692
0
      case LOGFMT_META_XFER_PATH:
693
0
      case LOGFMT_META_XFER_PORT:
694
0
      case LOGFMT_META_XFER_SPEED:
695
0
      case LOGFMT_META_XFER_STATUS:
696
0
      case LOGFMT_META_XFER_TYPE:
697
0
        text = "-";
698
0
        text_len = 1;
699
0
        break;
700
701
      /* These explicitly do NOT have default values. */
702
0
      case LOGFMT_META_CMD_PARAMS:
703
0
      case LOGFMT_META_COMMAND:
704
0
      case LOGFMT_META_DIR_NAME:
705
0
      case LOGFMT_META_DIR_PATH:
706
0
      case LOGFMT_META_ENV_VAR:
707
0
      case LOGFMT_META_EOS_REASON:
708
0
      case LOGFMT_META_NOTE_VAR:
709
0
      case LOGFMT_META_METHOD:
710
0
      default:
711
0
        break;
712
0
    }
713
714
0
    extlog_buffer_append(log, text, text_len);
715
0
  }
716
717
0
  return 0;
718
0
}
719
720
static int resolve_on_other(pool *p, pr_jot_ctx_t *jot_ctx,
721
0
    unsigned char *text, size_t text_len) {
722
0
  struct extlog_buffer *log;
723
724
0
  log = jot_ctx->log;
725
0
  if (log->buflen > 0) {
726
0
    pr_trace_msg(trace_channel, 19, "appending text '%.*s' (%lu) to buffer",
727
0
      (int) text_len, text, (unsigned long) text_len);
728
0
    memcpy(log->buf, text, text_len);
729
0
    log->buf += text_len;
730
0
    log->buflen -= text_len;
731
0
  }
732
733
0
  return 0;
734
0
}
735
736
/* from src/log.c */
737
extern int syslog_sockfd;
738
739
0
static void log_event(cmd_rec *cmd, logfile_t *lf) {
740
0
  int res;
741
0
  unsigned char *f = NULL;
742
0
  char logbuf[EXTENDED_LOG_BUFFER_SIZE] = {'\0'};
743
0
  logformat_t *fmt = NULL;
744
0
  size_t logbuflen;
745
0
  pool *tmp_pool;
746
0
  pr_jot_ctx_t *jot_ctx;
747
0
  struct extlog_buffer *log;
748
749
0
  fmt = lf->lf_format;
750
0
  f = fmt->lf_format;
751
752
0
  tmp_pool = make_sub_pool(cmd->tmp_pool);
753
0
  jot_ctx = pcalloc(tmp_pool, sizeof(pr_jot_ctx_t));
754
0
  log = pcalloc(tmp_pool, sizeof(struct extlog_buffer));
755
0
  log->bufsz = log->buflen = sizeof(logbuf) - 1;
756
0
  log->ptr = log->buf = logbuf;
757
758
0
  jot_ctx->log = log;
759
760
0
  res = pr_jot_resolve_logfmt(tmp_pool, cmd, lf->lf_jot_filters, f, jot_ctx,
761
0
    resolve_on_meta, resolve_on_default, resolve_on_other);
762
0
  if (res < 0) {
763
    /* EPERM indicates that the event was filtered, thus is not necessarily
764
     * an unexpected condition.
765
     */
766
0
    if (errno != EPERM) {
767
0
      pr_log_pri(PR_LOG_NOTICE, MOD_LOG_VERSION
768
0
        ": error formatting ExtendedLog message: %s", strerror(errno));
769
0
    }
770
771
0
    destroy_pool(tmp_pool);
772
0
    return;
773
0
  }
774
775
0
  extlog_buffer_append(log, "\n", 1);
776
0
  logbuflen = (log->bufsz - log->buflen);
777
778
0
  if (lf->lf_fd != EXTENDED_LOG_SYSLOG) {
779
0
    pr_log_event_generate(PR_LOG_TYPE_EXTLOG, lf->lf_fd, -1, logbuf, logbuflen);
780
781
    /* What about short writes? */
782
0
    if (write(lf->lf_fd, logbuf, logbuflen) < 0) {
783
0
      pr_log_pri(PR_LOG_ALERT, "error: cannot write ExtendedLog '%s': %s",
784
0
        lf->lf_filename, strerror(errno));
785
0
    }
786
787
0
  } else {
788
0
    pr_log_event_generate(PR_LOG_TYPE_EXTLOG, syslog_sockfd,
789
0
      lf->lf_syslog_level, logbuf, logbuflen);
790
0
    pr_syslog(syslog_sockfd, lf->lf_syslog_level, "%s", logbuf);
791
0
  }
792
793
0
  destroy_pool(tmp_pool);
794
0
}
795
796
0
MODRET log_any(cmd_rec *cmd) {
797
0
  logfile_t *lf = NULL;
798
799
  /* If not in anon mode, only handle logs for main servers */
800
0
  for (lf = logs; lf; lf = lf->next) {
801
0
    pr_signals_handle();
802
803
    /* Skip any unopened files (obviously); make sure that special fd
804
     * for syslog is NOT skipped, though.
805
     */
806
0
    if (lf->lf_fd < 0 &&
807
0
        lf->lf_fd != EXTENDED_LOG_SYSLOG) {
808
0
      continue;
809
0
    }
810
811
    /* If this is not an <Anonymous> section, and this IS an <Anonymous>
812
     * ExtendedLog, skip it.
813
     */
814
0
    if (session.anon_config == NULL &&
815
0
        lf->lf_conf != NULL &&
816
0
        lf->lf_conf->config_type == CONF_ANON) {
817
0
      continue;
818
0
    }
819
820
0
    log_event(cmd, lf);
821
0
  }
822
823
0
  return PR_DECLINED(cmd);
824
0
}
825
826
/* Event handlers
827
 */
828
829
0
static void log_exit_ev(const void *event_data, void *user_data) {
830
0
  pool *tmp_pool;
831
0
  cmd_rec *cmd;
832
0
  int responses_blocked;
833
834
0
  tmp_pool = make_sub_pool(session.pool);
835
0
  cmd = pr_cmd_alloc(tmp_pool, 1, pstrdup(tmp_pool, "EXIT"));
836
0
  cmd->cmd_class |= CL_DISCONNECT;
837
838
0
  responses_blocked = pr_response_blocked();
839
0
  if (responses_blocked == FALSE) {
840
0
    (void) pr_response_block(TRUE);
841
0
  }
842
843
0
  (void) pr_cmd_dispatch_phase(cmd, LOG_CMD,
844
0
    PR_CMD_DISPATCH_FL_CLEAR_RESPONSE);
845
846
0
  pr_response_block(responses_blocked);
847
0
  destroy_pool(tmp_pool);
848
0
}
849
850
0
static void log_postparse_ev(const void *event_data, void *user_data) {
851
0
  config_rec *c;
852
853
0
  c = find_config(main_server->conf, CONF_PARAM, "SystemLog", FALSE);
854
0
  if (c != NULL) {
855
0
    char *path;
856
857
0
    path = c->argv[0];
858
0
    log_closesyslog();
859
860
0
    if (strcasecmp(path, "none") != 0) {
861
0
      int res, xerrno;
862
863
0
      path = dir_canonical_path(main_server->pool, path);
864
865
0
      pr_signals_block();
866
0
      PRIVS_ROOT
867
0
      res = log_opensyslog(path);
868
0
      xerrno = errno;
869
0
      PRIVS_RELINQUISH
870
0
      pr_signals_unblock();
871
872
0
      if (res < 0) {
873
0
        if (res == PR_LOG_WRITABLE_DIR) {
874
0
          pr_log_pri(PR_LOG_ERR,
875
0
            "unable to open SystemLog '%s': %s is a world-writable directory",
876
0
            path, path);
877
878
0
        } else if (res == PR_LOG_SYMLINK) {
879
0
          pr_log_pri(PR_LOG_ERR,
880
0
            "unable to open SystemLog '%s': %s is a symbolic link", path, path);
881
882
0
        } else {
883
0
          if (xerrno != ENXIO) {
884
0
            pr_log_pri(PR_LOG_ERR,
885
0
              "unable to open SystemLog '%s': %s", path, strerror(xerrno));
886
887
0
          } else {
888
0
            pr_log_pri(PR_LOG_ERR,
889
0
              "unable to open SystemLog '%s': "
890
0
              "FIFO reader process must be running first", path);
891
0
          }
892
0
        }
893
894
0
        exit(1);
895
0
      }
896
897
0
    } else {
898
0
      log_discard();
899
0
    }
900
0
  }
901
0
}
902
903
0
static void log_restart_ev(const void *event_data, void *user_data) {
904
0
  destroy_pool(log_pool);
905
906
0
  formats = NULL;
907
0
  format_set = NULL;
908
0
  logs = NULL;
909
0
  log_set = NULL;
910
911
0
  log_pool = make_sub_pool(permanent_pool);
912
0
  pr_pool_tag(log_pool, "mod_log pool");
913
914
0
  parse_logformat(NULL, "", "%h %l %u %t \"%r\" %s %b");
915
0
}
916
917
0
static void log_sess_reinit_ev(const void *event_data, void *user_data) {
918
0
  int res;
919
0
  logfile_t *lf = NULL;
920
921
  /* A HOST command changed the main_server pointer, reinitialize ourselves. */
922
923
0
  pr_event_unregister(&log_module, "core.exit", log_exit_ev);
924
0
  pr_event_unregister(&log_module, "core.session-reinit", log_sess_reinit_ev);
925
0
  pr_event_unregister(&log_module, "core.timeout-stalled", log_xfer_stalled_ev);
926
927
  /* XXX If ServerLog configured, close/reopen syslog? */
928
929
  /* Close all ExtendedLog files, to prevent duplicate fds. */
930
0
  for (lf = logs; lf; lf = lf->next) {
931
0
    if (lf->lf_fd > -1) {
932
      /* No need to close the special EXTENDED_LOG_SYSLOG (i.e. fake) fd. */
933
0
      if (lf->lf_fd != EXTENDED_LOG_SYSLOG) {
934
0
        (void) close(lf->lf_fd);
935
0
      }
936
937
0
      lf->lf_fd = -1;
938
0
    }
939
0
  }
940
941
  /* Restore original LogOptions settings. */
942
0
  (void) pr_log_set_options(PR_LOG_OPT_DEFAULT);
943
944
0
  res = log_sess_init();
945
0
  if (res < 0) {
946
0
    pr_session_disconnect(&log_module,
947
0
      PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL);
948
0
  }
949
0
}
950
951
0
static void log_xfer_stalled_ev(const void *event_data, void *user_data) {
952
0
  if (session.curr_cmd_rec != NULL) {
953
    /* Automatically dispatch the current command, at the LOG_CMD_ERR phase,
954
     * so that the ExtendedLog entry for the command gets written out.  This
955
     * should handle any LIST/MLSD/NLST commands as well (Bug#3696).
956
     */
957
0
    (void) log_any(session.curr_cmd_rec);
958
0
  }
959
0
}
960
961
/* Initialization handlers
962
 */
963
964
0
static int log_init(void) {
965
0
  log_pool = make_sub_pool(permanent_pool);
966
0
  pr_pool_tag(log_pool, "mod_log pool");
967
968
  /* Add the "default" extendedlog format */
969
0
  parse_logformat(NULL, "", "%h %l %u %t \"%r\" %s %b");
970
971
0
  pr_event_register(&log_module, "core.postparse", log_postparse_ev, NULL);
972
0
  pr_event_register(&log_module, "core.restart", log_restart_ev, NULL);
973
974
0
  return 0;
975
0
}
976
977
0
static void find_extendedlogs(void) {
978
0
  config_rec *c;
979
0
  char *logfname, *logfmt_name = NULL;
980
0
  logformat_t *logfmt;
981
0
  logfile_t *extlog = NULL;
982
0
  unsigned long config_flags = (PR_CONFIG_FIND_FL_SKIP_DIR|PR_CONFIG_FIND_FL_SKIP_LIMIT|PR_CONFIG_FIND_FL_SKIP_DYNDIR);
983
984
  /* We DO actually want the recursion here.  The reason is that we want
985
   * to find ALL_ ExtendedLog directives in the configuration, including
986
   * those in <Anonymous> sections.  We have the ability to use root privs
987
   * now, to make sure these files can be opened, but after the user has
988
   * authenticated (and we know for sure whether they're anonymous or not),
989
   * root privs may be permanently revoked.
990
   *
991
   * We mitigate the cost of the recursive search (especially for configs
992
   * with thousands of <Directory>/<Limit> sections) by specifying the
993
   * find_config() flags to skip those sections; we are only interested
994
   * in the top-level (CONF_ROOT, CONF_VIRTUAL) and <Anonymous> sections.
995
   */
996
997
0
  c = find_config2(main_server->conf, CONF_PARAM, "ExtendedLog", TRUE,
998
0
    config_flags);
999
0
  while (c != NULL) {
1000
0
    pr_jot_filters_t *jot_filters = NULL;
1001
1002
0
    pr_signals_handle();
1003
1004
0
    logfname = c->argv[0];
1005
0
    logfmt_name = NULL;
1006
1007
0
    if (c->argc > 1) {
1008
0
      jot_filters = c->argv[1];
1009
1010
0
      if (c->argc > 2) {
1011
0
        if (c->argv[2] != NULL) {
1012
0
          logfmt_name = c->argv[2];
1013
0
        }
1014
0
      }
1015
0
    }
1016
1017
    /* No logging for this round.  If, however, this was found in an
1018
     * <Anonymous> section, add a logfile entry for it anyway; the anonymous
1019
     * directive might be trying to override a higher-level config; see
1020
     * Bug#1908.
1021
     */
1022
0
    if (c->parent != NULL &&
1023
0
        c->parent->config_type != CONF_ANON) {
1024
0
      goto loop_extendedlogs;
1025
0
    }
1026
1027
0
    if (logfmt_name != NULL) {
1028
      /* Search for the format name */
1029
0
      for (logfmt = formats; logfmt; logfmt = logfmt->next) {
1030
0
        if (strcmp(logfmt->lf_fmt_name, logfmt_name) == 0) {
1031
0
          break;
1032
0
        }
1033
0
      }
1034
1035
0
      if (logfmt == NULL) {
1036
0
        if (strcasecmp(logfmt_name, EXTENDED_LOG_FORMAT_DEFAULT) == 0) {
1037
          /* Try again, this time looking for the default LogFormat
1038
           * name, which is registered using a name of "".
1039
           */
1040
0
          for (logfmt = formats; logfmt; logfmt = logfmt->next) {
1041
0
            if (strcmp(logfmt->lf_fmt_name, "") == 0) {
1042
0
              break;
1043
0
            }
1044
0
          }
1045
0
        }
1046
0
      }
1047
1048
0
      if (logfmt == NULL) {
1049
0
        pr_log_pri(PR_LOG_NOTICE,
1050
0
          "ExtendedLog '%s' uses unknown format name '%s'", logfname,
1051
0
          logfmt_name);
1052
0
        goto loop_extendedlogs;
1053
0
      }
1054
1055
0
    } else {
1056
0
      logfmt = formats;
1057
0
    }
1058
1059
0
    extlog = (logfile_t *) pcalloc(session.pool, sizeof(logfile_t));
1060
1061
0
    extlog->lf_filename = pstrdup(session.pool, logfname);
1062
0
    extlog->lf_fd = -1;
1063
0
    extlog->lf_syslog_level = -1;
1064
0
    extlog->lf_jot_filters = jot_filters;
1065
0
    extlog->lf_format = logfmt;
1066
0
    extlog->lf_conf = c->parent;
1067
0
    if (log_set == NULL) {
1068
0
      log_set = xaset_create(session.pool, NULL);
1069
0
    }
1070
1071
0
    xaset_insert(log_set, (xasetmember_t *) extlog);
1072
0
    logs = (logfile_t *) log_set->xas_list;
1073
1074
0
loop_extendedlogs:
1075
0
    c = find_config_next2(c, c->next, CONF_PARAM, "ExtendedLog", TRUE,
1076
0
      config_flags);
1077
0
  }
1078
0
}
1079
1080
0
MODRET log_pre_dele(cmd_rec *cmd) {
1081
0
  char *path;
1082
1083
0
  jot_set_deleted_filesz(0);
1084
1085
0
  path = dir_canonical_path(cmd->tmp_pool,
1086
0
    pr_fs_decode_path(cmd->tmp_pool, cmd->arg));
1087
0
  if (path != NULL) {
1088
0
    struct stat st;
1089
1090
    /* Briefly cache the size of the file being deleted, so that it can be
1091
     * logged properly using %b.
1092
     */
1093
0
    pr_fs_clear_cache2(path);
1094
0
    if (pr_fsio_stat(path, &st) == 0) {
1095
0
      jot_set_deleted_filesz(st.st_size);
1096
0
    }
1097
0
  }
1098
1099
0
  return PR_DECLINED(cmd);
1100
0
}
1101
1102
0
MODRET log_post_pass(cmd_rec *cmd) {
1103
0
  logfile_t *lf;
1104
1105
  /* Authentication is complete, if we aren't in anon-mode, close
1106
   * all extendedlogs opened inside <Anonymous> blocks.
1107
   */
1108
0
  if (!session.anon_config) {
1109
0
    for (lf = logs; lf; lf = lf->next) {
1110
0
      if (lf->lf_fd != -1 &&
1111
0
          lf->lf_fd != EXTENDED_LOG_SYSLOG &&
1112
0
          lf->lf_conf &&
1113
0
          lf->lf_conf->config_type == CONF_ANON) {
1114
0
        pr_log_debug(DEBUG7, "mod_log: closing ExtendedLog '%s' (fd %d)",
1115
0
          lf->lf_filename, lf->lf_fd);
1116
0
        (void) close(lf->lf_fd);
1117
0
        lf->lf_fd = -1;
1118
0
      }
1119
0
    }
1120
1121
0
  } else {
1122
    /* Close all logs which were opened inside a _different_ anonymous
1123
     * context.
1124
     */
1125
0
    for (lf = logs; lf; lf = lf->next) {
1126
0
      if (lf->lf_fd != -1 &&
1127
0
          lf->lf_fd != EXTENDED_LOG_SYSLOG &&
1128
0
          lf->lf_conf &&
1129
0
          lf->lf_conf != session.anon_config) {
1130
0
        pr_log_debug(DEBUG7, "mod_log: closing ExtendedLog '%s' (fd %d)",
1131
0
          lf->lf_filename, lf->lf_fd);
1132
0
        (void) close(lf->lf_fd);
1133
0
        lf->lf_fd = -1;
1134
0
      }
1135
0
    }
1136
1137
    /* If any ExtendedLogs set inside our context match an outer log,
1138
     * close the outer (this allows overriding inside <Anonymous>).
1139
     */
1140
0
    for (lf = logs; lf; lf = lf->next) {
1141
0
      if (lf->lf_conf &&
1142
0
          lf->lf_conf == session.anon_config) {
1143
        /* This should "override" any lower-level extendedlog with the
1144
         * same filename.
1145
         */
1146
0
        logfile_t *lfi = NULL;
1147
1148
0
        for (lfi = logs; lfi; lfi = lfi->next) {
1149
0
          if (lfi->lf_fd != -1 &&
1150
0
              lfi->lf_fd != EXTENDED_LOG_SYSLOG &&
1151
0
              !lfi->lf_conf &&
1152
0
              strcmp(lfi->lf_filename, lf->lf_filename) == 0) {
1153
0
            pr_log_debug(DEBUG7, "mod_log: closing ExtendedLog '%s' (fd %d)",
1154
0
              lf->lf_filename, lfi->lf_fd);
1155
0
            (void) close(lfi->lf_fd);
1156
0
            lfi->lf_fd = -1;
1157
0
          }
1158
0
        }
1159
1160
        /* Go ahead and close the log if it's CL_NONE */
1161
0
        if (lf->lf_fd != -1 &&
1162
0
            lf->lf_fd != EXTENDED_LOG_SYSLOG &&
1163
0
            pr_jot_filters_include_classes(lf->lf_jot_filters, CL_NONE) == TRUE) {
1164
0
          (void) close(lf->lf_fd);
1165
0
          lf->lf_fd = -1;
1166
0
        }
1167
0
      }
1168
0
    }
1169
0
  }
1170
1171
0
  return PR_DECLINED(cmd);
1172
0
}
1173
1174
/* Open all the log files */
1175
static int dispatched_connect = FALSE;
1176
1177
0
static int log_sess_init(void) {
1178
0
  config_rec *c;
1179
0
  char *serverlog_name = NULL;
1180
0
  logfile_t *lf = NULL;
1181
1182
0
  pr_event_register(&log_module, "core.session-reinit", log_sess_reinit_ev,
1183
0
    NULL);
1184
1185
0
  c = find_config(main_server->conf, CONF_PARAM, "LogOptions", FALSE);
1186
0
  if (c != NULL) {
1187
0
    unsigned long log_opts;
1188
1189
0
    log_opts = *((unsigned long *) c->argv[0]);
1190
0
    if (pr_log_set_options(log_opts) < 0) {
1191
0
      pr_log_debug(DEBUG6, "%s: error setting LogOptions (%lu): %s",
1192
0
        c->name, log_opts, strerror(errno));
1193
0
    }
1194
0
  }
1195
1196
  /* Open the ServerLog, if present. */
1197
0
  serverlog_name = get_param_ptr(main_server->conf, "ServerLog", FALSE);
1198
0
  if (serverlog_name != NULL) {
1199
0
    log_closesyslog();
1200
1201
0
    if (strncasecmp(serverlog_name, "none", 5) != 0) {
1202
0
      int res, xerrno;
1203
1204
0
      PRIVS_ROOT
1205
0
      res = log_opensyslog(serverlog_name);
1206
0
      xerrno = errno;
1207
0
      PRIVS_RELINQUISH
1208
1209
0
      if (res < 0) {
1210
0
        if (xerrno != ENXIO) {
1211
0
          pr_log_debug(DEBUG4, "unable to open ServerLog '%s': %s",
1212
0
            serverlog_name, strerror(xerrno));
1213
1214
0
        } else {
1215
0
          pr_log_debug(DEBUG4,
1216
0
            "unable to open ServerLog '%s': "
1217
0
            "FIFO reader process must be running first", serverlog_name);
1218
0
        }
1219
0
      }
1220
0
    }
1221
1222
0
  } else {
1223
0
    c = find_config(main_server->conf, CONF_PARAM, "SystemLog", FALSE);
1224
0
    if (c != NULL) {
1225
0
      char *path;
1226
1227
0
      path = c->argv[0];
1228
0
      log_closesyslog();
1229
1230
0
      if (strcasecmp(path, "none") != 0) {
1231
0
        int res, xerrno;
1232
1233
0
        path = dir_canonical_path(main_server->pool, path);
1234
1235
0
        pr_signals_block();
1236
0
        PRIVS_ROOT
1237
0
        res = log_opensyslog(path);
1238
0
        xerrno = errno;
1239
0
        PRIVS_RELINQUISH
1240
0
        pr_signals_unblock();
1241
1242
0
        if (res < 0) {
1243
0
          if (res == PR_LOG_WRITABLE_DIR) {
1244
0
            pr_log_pri(PR_LOG_ERR,
1245
0
              "unable to open SystemLog '%s': %s is a world-writable directory",
1246
0
              path, path);
1247
1248
0
          } else if (res == PR_LOG_SYMLINK) {
1249
0
            pr_log_pri(PR_LOG_ERR,
1250
0
              "unable to open SystemLog '%s': %s is a symbolic link", path,
1251
0
              path);
1252
1253
0
          } else {
1254
0
            if (xerrno != ENXIO) {
1255
0
              pr_log_pri(PR_LOG_ERR,
1256
0
                "unable to open SystemLog '%s': %s", path, strerror(xerrno));
1257
1258
0
            } else {
1259
0
              pr_log_pri(PR_LOG_ERR,
1260
0
                "unable to open SystemLog '%s': "
1261
0
                "FIFO reader process must be running first", path);
1262
0
            }
1263
0
          }
1264
0
        }
1265
1266
0
      } else {
1267
0
        log_discard();
1268
0
      }
1269
0
    }
1270
0
  }
1271
1272
  /* Open all the ExtendedLog files. */
1273
0
  find_extendedlogs();
1274
1275
0
  for (lf = logs; lf; lf = lf->next) {
1276
0
    if (lf->lf_fd == -1) {
1277
1278
      /* Is this ExtendedLog to be written to a file, or to syslog? */
1279
0
      if (strncasecmp(lf->lf_filename, "syslog:", 7) != 0) {
1280
0
        int res = 0, xerrno;
1281
1282
0
        pr_log_debug(DEBUG7, "mod_log: opening ExtendedLog '%s'",
1283
0
          lf->lf_filename);
1284
1285
0
        pr_signals_block();
1286
0
        PRIVS_ROOT
1287
0
        res = pr_log_openfile(lf->lf_filename, &(lf->lf_fd), EXTENDED_LOG_MODE);
1288
0
        xerrno = errno;
1289
0
        PRIVS_RELINQUISH
1290
0
        pr_signals_unblock();
1291
1292
0
        if (res < 0) {
1293
0
          if (res == -1) {
1294
0
            if (xerrno != ENXIO) {
1295
0
              pr_log_pri(PR_LOG_NOTICE, "unable to open ExtendedLog '%s': %s",
1296
0
                lf->lf_filename, strerror(xerrno));
1297
1298
0
            } else {
1299
0
              pr_log_pri(PR_LOG_NOTICE, "unable to open ExtendedLog '%s': "
1300
0
                "FIFO reader process must be running first", lf->lf_filename);
1301
0
            }
1302
1303
0
          } else if (res == PR_LOG_WRITABLE_DIR) {
1304
0
            pr_log_pri(PR_LOG_WARNING, "unable to open ExtendedLog '%s': "
1305
0
              "parent directory is world-writable", lf->lf_filename);
1306
1307
0
          } else if (res == PR_LOG_SYMLINK) {
1308
0
            pr_log_pri(PR_LOG_WARNING, "unable to open ExtendedLog '%s': "
1309
0
              "%s is a symbolic link", lf->lf_filename, lf->lf_filename);
1310
0
          }
1311
0
        }
1312
1313
0
      } else {
1314
0
        char *tmp = strchr(lf->lf_filename, ':');
1315
1316
0
        lf->lf_syslog_level = pr_log_str2sysloglevel(++tmp);
1317
0
        lf->lf_fd = EXTENDED_LOG_SYSLOG;
1318
0
      }
1319
0
    }
1320
0
  }
1321
1322
  /* Register event handlers for the session. */
1323
0
  pr_event_register(&log_module, "core.exit", log_exit_ev, NULL);
1324
0
  pr_event_register(&log_module, "core.timeout-stalled", log_xfer_stalled_ev,
1325
0
    NULL);
1326
1327
  /* Have we send our CONNECT event yet? */
1328
0
  if (dispatched_connect == FALSE) {
1329
0
    pool *tmp_pool;
1330
0
    cmd_rec *cmd;
1331
0
    int responses_blocked;
1332
1333
0
    tmp_pool = make_sub_pool(session.pool);
1334
0
    cmd = pr_cmd_alloc(tmp_pool, 1, pstrdup(tmp_pool, "CONNECT"));
1335
0
    cmd->cmd_class |= CL_CONNECT;
1336
1337
0
    responses_blocked = pr_response_blocked();
1338
0
    if (responses_blocked == FALSE) {
1339
0
      (void) pr_response_block(TRUE);
1340
0
    }
1341
1342
0
    (void) pr_cmd_dispatch_phase(cmd, LOG_CMD,
1343
0
      PR_CMD_DISPATCH_FL_CLEAR_RESPONSE);
1344
1345
0
    pr_response_block(responses_blocked);
1346
0
    destroy_pool(tmp_pool);
1347
0
    dispatched_connect = TRUE;
1348
0
  }
1349
1350
0
  return 0;
1351
0
}
1352
1353
/* Module API tables
1354
 */
1355
1356
static conftable log_conftab[] = {
1357
  { "AllowLogSymlinks", set_allowlogsymlinks,     NULL },
1358
  { "ExtendedLog",  set_extendedlog,      NULL },
1359
  { "LogFormat",  set_logformat,        NULL },
1360
  { "LogOptions", set_logoptions,       NULL },
1361
  { "ServerLog",  set_serverlog,        NULL },
1362
  { "SystemLog",  set_systemlog,        NULL },
1363
  { NULL,   NULL,         NULL }
1364
};
1365
1366
static cmdtable log_cmdtab[] = {
1367
  { PRE_CMD,    C_DELE, G_NONE, log_pre_dele, FALSE, FALSE },
1368
  { LOG_CMD,    C_ANY,  G_NONE, log_any,  FALSE, FALSE },
1369
  { LOG_CMD_ERR,  C_ANY,  G_NONE, log_any,  FALSE, FALSE },
1370
  { POST_CMD,   C_PASS, G_NONE, log_post_pass,  FALSE, FALSE },
1371
  { 0, NULL }
1372
};
1373
1374
module log_module = {
1375
  NULL, NULL,
1376
1377
  /* Module API version */
1378
  0x20,
1379
1380
  /* Module name */
1381
  "log",
1382
1383
  /* Module configuration handler table */
1384
  log_conftab,
1385
1386
  /* Module command handler table */
1387
  log_cmdtab,
1388
1389
  /* Module authentication handler table */
1390
  NULL,
1391
1392
  /* Module initialization */
1393
  log_init,
1394
1395
  /* Session initialization */
1396
  log_sess_init,
1397
1398
  /* Module version */
1399
  MOD_LOG_VERSION
1400
};