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