1 /*
2  * mod_log_forensic - a buffering log module for aiding in server behavior
3  *                    forensic analysis
4  * Copyright (c) 2011-2017 TJ Saunders
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
19  *
20  * As a special exemption, TJ Saunders and other respective copyright holders
21  * give permission to link this program with OpenSSL, and distribute the
22  * resulting executable, without including the source code for OpenSSL in the
23  * source distribution.
24  */
25 
26 #include "conf.h"
27 #include "privs.h"
28 
29 #include <sys/uio.h>
30 
31 #define MOD_LOG_FORENSIC_VERSION		"mod_log_forensic/0.2"
32 
33 /* Make sure the version of proftpd is as necessary. */
34 #if PROFTPD_VERSION_NUMBER < 0x0001030403
35 # error "ProFTPD 1.3.4rc3 or later required"
36 #endif
37 
38 module log_forensic_module;
39 
40 static pool *forensic_pool = NULL;
41 static int forensic_engine = FALSE;
42 static int forensic_logfd = -1;
43 
44 /* Criteria for flushing out the "forensic" logs. */
45 #define FORENSIC_CRIT_FAILED_LOGIN		0x00001
46 #define FORENSIC_CRIT_MODULE_CONFIG		0x00002
47 #define FORENSIC_CRIT_UNTIMELY_DEATH		0x00004
48 
49 #define FORENSIC_CRIT_DEFAULT \
50   (FORENSIC_CRIT_FAILED_LOGIN|FORENSIC_CRIT_UNTIMELY_DEATH)
51 
52 static unsigned long forensic_criteria = FORENSIC_CRIT_DEFAULT;
53 
54 /* Use a ring buffer for the cached/buffered log messages; the index pointing
55  * to where to stash the next message then moves around the ring.
56  *
57  * Overwritten messages will be allocated of a module-specific pool.  To
58  * prevent this pool from growing unboundedly, we need to clear/destroy it
59  * periodically.  But doing this without having to re-copy all of the
60  * buffered log lines could be expensive.
61  *
62  * Instead, what if we use subpools, for every 1/10th of the ring.  When
63  * the last message for a subpool is purged/overwritten, that subpool can
64  * be destroyed without effecting any existing message in the ring.
65  */
66 
67 #define FORENSIC_DEFAULT_NMSGS		1024
68 
69 /* Regardless of the configured ForensicLogBufferSize, this defines the
70  * number of messages per sub-pool.
71  *
72  * Why 256 messages per sub-pool?
73  *
74  *  80 chars (avg) per message * 256 messages = 20 KB
75  *
76  * This means that a given sub-pool will hold roughly 20 KB.  Which means
77  * that 20 KB + ring max size is the largest memory that mod_log_forensic
78  * should hold, before releasing a sub-pool back to the Pool API.
79  */
80 
81 #define FORENSIC_DEFAULT_MSGS_PER_POOL		256
82 static unsigned int forensic_msgs_per_pool = FORENSIC_DEFAULT_MSGS_PER_POOL;
83 
84 struct forensic_msg {
85   pool *fm_pool;
86   unsigned int fm_pool_msgno;
87 
88   unsigned int fm_log_type;
89   int fm_log_level;
90   const char *fm_msg;
91   size_t fm_msglen;
92 };
93 
94 static struct forensic_msg **forensic_msgs = NULL;
95 static unsigned int forensic_nmsgs = FORENSIC_DEFAULT_NMSGS;
96 static unsigned int forensic_msg_idx = 0;
97 
98 static pool *forensic_subpool = NULL;
99 static unsigned int forensic_subpool_msgno = 1;
100 
101 #define FORENSIC_MAX_LEVELS	50
102 static const char *forensic_log_levels[] = {
103   "0",   "1",  "2",  "3",  "4",  "5",  "6",  "7",  "8",  "9",
104   "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
105   "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
106   "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
107   "40", "41", "42", "43", "44", "45", "46", "47", "48", "49"
108 };
109 
110 /* Necessary prototypes */
111 static int forensic_sess_init(void);
112 
forensic_add_msg(unsigned int log_type,int log_level,const char * log_msg,size_t log_msglen)113 static void forensic_add_msg(unsigned int log_type, int log_level,
114     const char *log_msg, size_t log_msglen) {
115   struct forensic_msg *fm;
116   pool *sub_pool;
117   char *fm_msg;
118 
119   /* Get the message that's currently in the ring where we want add our new
120    * one.
121    */
122   fm = forensic_msgs[forensic_msg_idx];
123   if (fm) {
124     /* If this message is the last one of it subpool, destroy that pool. */
125     if (fm->fm_pool_msgno == forensic_msgs_per_pool) {
126       destroy_pool(fm->fm_pool);
127     }
128 
129     forensic_msgs[forensic_msg_idx] = NULL;
130   }
131 
132   /* Add this message into the ring. */
133   sub_pool = pr_pool_create_sz(forensic_subpool, 128);
134   fm = pcalloc(sub_pool, sizeof(struct forensic_msg));
135   fm->fm_pool = sub_pool;
136   fm->fm_pool_msgno = forensic_subpool_msgno;
137   fm->fm_log_type = log_type;
138   fm->fm_log_level = log_level;
139 
140   fm_msg = palloc(fm->fm_pool, log_msglen + 1);
141   memcpy(fm_msg, log_msg, log_msglen);
142   fm_msg[log_msglen] = '\0';
143 
144   fm->fm_msg = fm_msg;
145   fm->fm_msglen = log_msglen;
146 
147   forensic_msgs[forensic_msg_idx] = fm;
148 
149   forensic_msg_idx += 1;
150   if (forensic_msg_idx == forensic_nmsgs) {
151     /* Wrap around */
152     forensic_msg_idx = 0;
153   }
154 
155   if (forensic_subpool_msgno == forensic_msgs_per_pool) {
156     /* Time to create a new subpool */
157     forensic_subpool = pr_pool_create_sz(forensic_pool, 256);
158     forensic_subpool_msgno = 1;
159 
160   } else {
161     forensic_subpool_msgno++;
162   }
163 }
164 
forensic_get_begin_marker(unsigned int criterion,size_t * markerlen)165 static const char *forensic_get_begin_marker(unsigned int criterion,
166     size_t *markerlen) {
167   const char *marker = NULL;
168 
169   switch (criterion) {
170     case FORENSIC_CRIT_FAILED_LOGIN:
171       marker = "-----BEGIN FAILED LOGIN FORENSICS-----\n";
172       break;
173 
174     case FORENSIC_CRIT_MODULE_CONFIG:
175       marker = "-----BEGIN MODULE CONFIG FORENSICS-----\n";
176       break;
177 
178     case FORENSIC_CRIT_UNTIMELY_DEATH:
179       marker = "-----BEGIN UNTIMELY DEATH FORENSICS-----\n";
180       break;
181   }
182 
183   if (marker != NULL) {
184     *markerlen = strlen(marker);
185   }
186 
187   return marker;
188 }
189 
forensic_get_end_marker(unsigned int criterion,size_t * markerlen)190 static const char *forensic_get_end_marker(unsigned int criterion,
191     size_t *markerlen) {
192   const char *marker = NULL;
193 
194   switch (criterion) {
195     case FORENSIC_CRIT_FAILED_LOGIN:
196       marker = "-----END FAILED LOGIN FORENSICS-----\n";
197       break;
198 
199     case FORENSIC_CRIT_MODULE_CONFIG:
200       marker = "-----END MODULE CONFIG FORENSICS-----\n";
201       break;
202 
203     case FORENSIC_CRIT_UNTIMELY_DEATH:
204       marker = "-----END UNTIMELY DEATH FORENSICS-----\n";
205       break;
206   }
207 
208   if (marker != NULL) {
209     *markerlen = strlen(marker);
210   }
211 
212   return marker;
213 }
214 
215 /* Rather than deal with some homegrown itoa() sort of thing for converting
216  * the log level number to an easily printable string, I'm using the level
217  * as an index in a precomputed array of strings.
218  */
forensic_get_level_str(int log_level)219 static const char *forensic_get_level_str(int log_level) {
220   int i;
221 
222   i = log_level;
223   if (i < 0) {
224     i = 0;
225   }
226 
227   if (i >= FORENSIC_MAX_LEVELS) {
228     return "N";
229   }
230 
231   return forensic_log_levels[i];
232 }
233 
forensic_write_metadata(void)234 static void forensic_write_metadata(void) {
235   const char *client_ip, *server_ip, *proto, *unique_id;
236   int server_port;
237   char server_port_str[32], uid_str[32], gid_str[32], elapsed_str[64],
238     raw_bytes_in_str[64], raw_bytes_out_str[64],
239     total_bytes_in_str[64], total_bytes_out_str[64],
240     total_files_in_str[64], total_files_out_str[64];
241   size_t unique_idlen = 0;
242 
243   /* 64 vectors is currently more than necessary, but it's better to have
244    * too many than too little.
245    */
246   struct iovec iov[64];
247   int niov = 0, res;
248   uint64_t now;
249   unsigned long elapsed_ms;
250 
251   /* Write session metadata in key/value message headers:
252    *
253    * Client-Address:
254    * Server-Address:
255    * Elapsed:
256    * Protocol:
257    * User:
258    * UID:
259    * GID:
260    * [UNIQUE_ID:]
261    * Raw-Bytes-In:
262    * Raw-Bytes-Out:
263    * Total-Bytes-In:
264    * Total-Bytes-Out:
265    * Total-Files-In:
266    * Total-Files-Out:
267    */
268 
269   client_ip = pr_netaddr_get_ipstr(pr_netaddr_get_sess_remote_addr());
270   server_ip = pr_netaddr_get_ipstr(pr_netaddr_get_sess_local_addr());
271   server_port = ntohs(pr_netaddr_get_port(pr_netaddr_get_sess_local_addr()));
272 
273   /* Client address */
274   iov[niov].iov_base = "Client-Address: ";
275   iov[niov].iov_len = 16;
276   niov++;
277 
278   iov[niov].iov_base = (void *) client_ip;
279   iov[niov].iov_len = strlen(client_ip);
280   niov++;
281 
282   iov[niov].iov_base = "\n";
283   iov[niov].iov_len = 1;
284   niov++;
285 
286   /* Server address */
287   iov[niov].iov_base = "Server-Address: ";
288   iov[niov].iov_len = 16;
289   niov++;
290 
291   iov[niov].iov_base = (void *) server_ip;
292   iov[niov].iov_len = strlen(server_ip);
293   niov++;
294 
295   memset(server_port_str, '\0', sizeof(server_port_str));
296   res = pr_snprintf(server_port_str, sizeof(server_port_str)-1, ":%d\n",
297     server_port);
298   iov[niov].iov_base = server_port_str;
299   iov[niov].iov_len = res;
300   niov++;
301 
302   /* Elapsed (in ms) */
303   iov[niov].iov_base = "Elapsed: ";
304   iov[niov].iov_len = 9;
305   niov++;
306 
307   pr_gettimeofday_millis(&now);
308   elapsed_ms = (unsigned long) (now - session.connect_time_ms);
309 
310   memset(elapsed_str, '\0', sizeof(elapsed_str));
311   res = pr_snprintf(elapsed_str, sizeof(elapsed_str)-1, "%lu\n", elapsed_ms);
312   iov[niov].iov_base = (void *) elapsed_str;
313   iov[niov].iov_len = res;
314   niov++;
315 
316   /* Protocol */
317   proto = pr_session_get_protocol(0);
318   iov[niov].iov_base = "Protocol: ";
319   iov[niov].iov_len = 10;
320   niov++;
321 
322   iov[niov].iov_base = (char *) proto;
323   iov[niov].iov_len = strlen(proto);
324   niov++;
325 
326   iov[niov].iov_base = "\n";
327   iov[niov].iov_len = 1;
328   niov++;
329 
330   /* User */
331   if (session.user) {
332     iov[niov].iov_base = "User: ";
333     iov[niov].iov_len = 6;
334     niov++;
335 
336     iov[niov].iov_base = (void *) session.user;
337     iov[niov].iov_len = strlen(session.user);
338     niov++;
339 
340     iov[niov].iov_base = "\n";
341     iov[niov].iov_len = 1;
342     niov++;
343   }
344 
345   /* UID */
346   iov[niov].iov_base = "UID: ";
347   iov[niov].iov_len = 5;
348   niov++;
349 
350   memset(uid_str, '\0', sizeof(uid_str));
351   res = pr_snprintf(uid_str, sizeof(uid_str)-1, "%lu\n",
352     (unsigned long) geteuid());
353   iov[niov].iov_base = uid_str;
354   iov[niov].iov_len = res;
355   niov++;
356 
357   /* GID */
358   iov[niov].iov_base = "GID: ";
359   iov[niov].iov_len = 5;
360   niov++;
361 
362   memset(gid_str, '\0', sizeof(gid_str));
363   res = pr_snprintf(gid_str, sizeof(gid_str)-1, "%lu\n",
364     (unsigned long) getegid());
365   iov[niov].iov_base = gid_str;
366   iov[niov].iov_len = res;
367   niov++;
368 
369   /* UNIQUE_ID (from mod_unique_id), if present. */
370   unique_id = pr_table_get(session.notes, "UNIQUE_ID", &unique_idlen);
371   if (unique_id != NULL) {
372     iov[niov].iov_base = "UNIQUE_ID: ";
373     iov[niov].iov_len = 11;
374     niov++;
375 
376     iov[niov].iov_base = (char *) unique_id;
377     iov[niov].iov_len = unique_idlen;
378     niov++;
379 
380     iov[niov].iov_base = "\n";
381     iov[niov].iov_len = 1;
382     niov++;
383   }
384 
385   /* Raw bytes in */
386   iov[niov].iov_base = "Raw-Bytes-In: ";
387   iov[niov].iov_len = 14;
388   niov++;
389 
390   if (session.total_raw_in == 0) {
391     iov[niov].iov_base = "0\n";
392     iov[niov].iov_len = 2;
393     niov++;
394 
395   } else {
396     memset(raw_bytes_in_str, '\0', sizeof(raw_bytes_in_str));
397     res = pr_snprintf(raw_bytes_in_str, sizeof(raw_bytes_in_str)-1,
398       "%" PR_LU "\n", (pr_off_t) session.total_raw_in);
399     iov[niov].iov_base = raw_bytes_in_str;
400     iov[niov].iov_len = res;
401     niov++;
402   }
403 
404   /* Raw bytes out */
405   iov[niov].iov_base = "Raw-Bytes-Out: ";
406   iov[niov].iov_len = 15;
407   niov++;
408 
409   if (session.total_raw_out == 0) {
410     iov[niov].iov_base = "0\n";
411     iov[niov].iov_len = 2;
412     niov++;
413 
414   } else {
415     memset(raw_bytes_out_str, '\0', sizeof(raw_bytes_out_str));
416     res = pr_snprintf(raw_bytes_out_str, sizeof(raw_bytes_out_str)-1,
417       "%" PR_LU "\n", (pr_off_t) session.total_raw_out);
418     iov[niov].iov_base = raw_bytes_out_str;
419     iov[niov].iov_len = res;
420     niov++;
421   }
422 
423   /* Total bytes in */
424   iov[niov].iov_base = "Total-Bytes-In: ";
425   iov[niov].iov_len = 16;
426   niov++;
427 
428   if (session.total_bytes_in == 0) {
429     iov[niov].iov_base = "0\n";
430     iov[niov].iov_len = 2;
431     niov++;
432 
433   } else {
434     memset(total_bytes_in_str, '\0', sizeof(total_bytes_in_str));
435     res = pr_snprintf(total_bytes_in_str, sizeof(total_bytes_in_str)-1,
436       "%" PR_LU "\n", (pr_off_t) session.total_bytes_in);
437     iov[niov].iov_base = total_bytes_in_str;
438     iov[niov].iov_len = res;
439     niov++;
440   }
441 
442   /* Total bytes out */
443   iov[niov].iov_base = "Total-Bytes-Out: ";
444   iov[niov].iov_len = 17;
445   niov++;
446 
447   if (session.total_bytes_out == 0) {
448     iov[niov].iov_base = "0\n";
449     iov[niov].iov_len = 2;
450     niov++;
451 
452   } else {
453     memset(total_bytes_out_str, '\0', sizeof(total_bytes_out_str));
454     res = pr_snprintf(total_bytes_out_str, sizeof(total_bytes_out_str)-1,
455       "%" PR_LU "\n", (pr_off_t) session.total_bytes_out);
456     iov[niov].iov_base = total_bytes_out_str;
457     iov[niov].iov_len = res;
458     niov++;
459   }
460 
461   /* Total files in */
462   iov[niov].iov_base = "Total-Files-In: ";
463   iov[niov].iov_len = 16;
464   niov++;
465 
466   if (session.total_files_in == 0) {
467     iov[niov].iov_base = "0\n";
468     iov[niov].iov_len = 2;
469     niov++;
470 
471   } else {
472     memset(total_files_in_str, '\0', sizeof(total_files_in_str));
473     res = pr_snprintf(total_files_in_str, sizeof(total_files_in_str)-1,
474       "%u\n", session.total_files_in);
475     iov[niov].iov_base = total_files_in_str;
476     iov[niov].iov_len = res;
477     niov++;
478   }
479 
480   /* Total files out */
481   iov[niov].iov_base = "Total-Files-Out: ";
482   iov[niov].iov_len = 17;
483   niov++;
484 
485   if (session.total_files_out == 0) {
486     iov[niov].iov_base = "0\n";
487     iov[niov].iov_len = 2;
488     niov++;
489 
490   } else {
491     memset(total_files_out_str, '\0', sizeof(total_files_out_str));
492     res = pr_snprintf(total_files_out_str, sizeof(total_files_out_str)-1,
493       "%u\n", session.total_files_out);
494     iov[niov].iov_base = total_files_out_str;
495     iov[niov].iov_len = res;
496     niov++;
497   }
498 
499   iov[niov].iov_base = "\n";
500   iov[niov].iov_len = 1;
501   niov++;
502 
503   res = writev(forensic_logfd, iov, niov);
504 }
505 
forensic_write_msgs(unsigned int criterion)506 static void forensic_write_msgs(unsigned int criterion) {
507   register unsigned int i;
508   unsigned int start_idx, end_idx;
509   int res;
510   const char *crit_marker = NULL;
511   size_t crit_markerlen = 0;
512 
513   /* XXX An interesting optimization would be to rework this code so that
514    * we used writev(2) to write out the buffer as quickly as possible,
515    * taking IOV_MAX into account.
516    */
517 
518   crit_marker = forensic_get_begin_marker(criterion, &crit_markerlen);
519   if (crit_marker != NULL) {
520     res = write(forensic_logfd, crit_marker, crit_markerlen);
521   }
522 
523   forensic_write_metadata();
524 
525   /* The head of the log messages (i.e. the oldest message) is always where we
526    * want to place the newest message.
527    */
528   start_idx = forensic_msg_idx;
529   end_idx = forensic_msg_idx - 1;
530   if (forensic_msg_idx == 0) {
531     end_idx = forensic_nmsgs - 1;
532   }
533 
534   i = start_idx;
535   while (i != end_idx) {
536     struct forensic_msg *fm;
537 
538     pr_signals_handle();
539 
540     fm = forensic_msgs[i];
541     if (fm != NULL) {
542       const char *level;
543       size_t level_len;
544 
545       level = forensic_get_level_str(fm->fm_log_level);
546       level_len = strlen(level);
547 
548       switch (fm->fm_log_type) {
549         case PR_LOG_TYPE_UNSPEC:
550           res = write(forensic_logfd, "[Unspec:", 8);
551           res = write(forensic_logfd, level, level_len);
552           res = write(forensic_logfd, "] ", 2);
553           break;
554 
555         case PR_LOG_TYPE_XFERLOG:
556           res = write(forensic_logfd, "[TransferLog:", 13);
557           res = write(forensic_logfd, level, level_len);
558           res = write(forensic_logfd, "] ", 2);
559           break;
560 
561         case PR_LOG_TYPE_SYSLOG: {
562           char pid_str[32];
563 
564           res = write(forensic_logfd, "[syslog:", 8);
565           res = write(forensic_logfd, level, level_len);
566 
567           /* syslogd normally adds the PID; we thus need to add the PID in
568            * here as well, to aid in the correlation of these log lines
569            * with other tools/diagnostics.
570            */
571           res = write(forensic_logfd, ", PID ", 6);
572 
573           memset(pid_str, '\0', sizeof(pid_str));
574           res = pr_snprintf(pid_str, sizeof(pid_str)-1, "%lu",
575             (unsigned long) (session.pid ? session.pid : getpid()));
576           res = write(forensic_logfd, pid_str, res);
577 
578           res = write(forensic_logfd, "] ", 2);
579           break;
580         }
581 
582         case PR_LOG_TYPE_SYSTEMLOG:
583           res = write(forensic_logfd, "[SystemLog:", 11);
584           res = write(forensic_logfd, level, level_len);
585           res = write(forensic_logfd, "] ", 2);
586           break;
587 
588         case PR_LOG_TYPE_EXTLOG:
589           res = write(forensic_logfd, "[ExtendedLog:", 13);
590           res = write(forensic_logfd, level, level_len);
591           res = write(forensic_logfd, "] ", 2);
592           break;
593 
594         case PR_LOG_TYPE_TRACELOG:
595           res = write(forensic_logfd, "[TraceLog:", 10);
596           res = write(forensic_logfd, level, level_len);
597           res = write(forensic_logfd, "] ", 2);
598           break;
599       }
600 
601       res = write(forensic_logfd, fm->fm_msg, fm->fm_msglen);
602       while (res < 0) {
603         if (errno == EINTR) {
604           pr_signals_handle();
605           res = write(forensic_logfd, fm->fm_msg, fm->fm_msglen);
606           continue;
607         }
608       }
609 
610       /* syslog-type messages don't have a newline appended to them, since
611        * syslogd handles that.  So we then need to add our own newline here.
612        */
613       if (fm->fm_log_type == PR_LOG_TYPE_SYSLOG) {
614         res = write(forensic_logfd, "\n", 1);
615       }
616 
617       if (fm->fm_pool_msgno == forensic_msgs_per_pool) {
618         destroy_pool(fm->fm_pool);
619       }
620 
621       forensic_msgs[i] = NULL;
622     }
623 
624     i++;
625     if (i == forensic_nmsgs) {
626       /* Wrap around */
627       i = 0;
628     }
629   }
630 
631   crit_marker = forensic_get_end_marker(criterion, &crit_markerlen);
632   if (crit_marker != NULL) {
633     res = write(forensic_logfd, crit_marker, crit_markerlen);
634   }
635 }
636 
637 /* Configuration handlers
638  */
639 
640 /* usage: ForensicLogBufferSize count */
set_forensiclogbuffersize(cmd_rec * cmd)641 MODRET set_forensiclogbuffersize(cmd_rec *cmd) {
642   config_rec *c;
643   unsigned int count;
644   char *ptr = NULL;
645 
646   CHECK_ARGS(cmd, 1);
647   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
648 
649   count = strtoul(cmd->argv[1], &ptr, 10);
650   if (ptr && *ptr) {
651     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "badly formatted number: ",
652       cmd->argv[1], NULL));
653   }
654 
655   if (count == 0) {
656     CONF_ERROR(cmd, "size must be greater than zero");
657   }
658 
659   c = add_config_param(cmd->argv[0], 1, NULL);
660   c->argv[0] = palloc(c->pool, sizeof(unsigned int));
661   *((unsigned int *) c->argv[0]) = count;
662 
663   return PR_HANDLED(cmd);
664 }
665 
666 /* usage: ForensicLogCapture type1 ... typeN */
set_forensiclogcapture(cmd_rec * cmd)667 MODRET set_forensiclogcapture(cmd_rec *cmd) {
668   config_rec *c;
669   int unspec_listen = FALSE;
670   int xferlog_listen = FALSE;
671   int syslog_listen = FALSE;
672   int systemlog_listen = FALSE;
673   int extlog_listen = FALSE;
674   int tracelog_listen = FALSE;
675   register unsigned int i;
676 
677   if (cmd->argc-1 < 1) {
678     CONF_ERROR(cmd, "wrong number of parameters");
679   }
680 
681   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
682 
683   for (i = 1; i < cmd->argc; i++) {
684     if (strncasecmp(cmd->argv[i], "Unspec", 7) == 0 ||
685         strncasecmp(cmd->argv[i], "Unknown", 8) == 0) {
686       unspec_listen = TRUE;
687 
688     } else if (strncasecmp(cmd->argv[i], "TransferLog", 12) == 0) {
689       xferlog_listen = TRUE;
690 
691     } else if (strncasecmp(cmd->argv[i], "Syslog", 7) == 0) {
692       syslog_listen = TRUE;
693 
694     } else if (strncasecmp(cmd->argv[i], "SystemLog", 10) == 0) {
695       systemlog_listen = TRUE;
696 
697     } else if (strncasecmp(cmd->argv[i], "ExtendedLog", 12) == 0) {
698       extlog_listen = TRUE;
699 
700     } else if (strncasecmp(cmd->argv[i], "TraceLog", 9) == 0) {
701       tracelog_listen = TRUE;
702 
703     } else {
704       CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown log type: ",
705         cmd->argv[i], NULL));
706     }
707   }
708 
709   c = add_config_param(cmd->argv[0], 6, NULL, NULL, NULL, NULL, NULL, NULL);
710   c->argv[0] = palloc(c->pool, sizeof(int));
711   *((int *) c->argv[0]) = unspec_listen;
712   c->argv[1] = palloc(c->pool, sizeof(int));
713   *((int *) c->argv[1]) = xferlog_listen;
714   c->argv[2] = palloc(c->pool, sizeof(int));
715   *((int *) c->argv[2]) = syslog_listen;
716   c->argv[3] = palloc(c->pool, sizeof(int));
717   *((int *) c->argv[3]) = systemlog_listen;
718   c->argv[4] = palloc(c->pool, sizeof(int));
719   *((int *) c->argv[4]) = extlog_listen;
720   c->argv[5] = palloc(c->pool, sizeof(int));
721   *((int *) c->argv[5]) = tracelog_listen;
722 
723   return PR_HANDLED(cmd);
724 }
725 
726 /* usage: ForensicLogCriteria ... */
set_forensiclogcriteria(cmd_rec * cmd)727 MODRET set_forensiclogcriteria(cmd_rec *cmd) {
728   config_rec *c;
729   unsigned long criteria = 0UL;
730   register unsigned int i;
731 
732   if (cmd->argc-1 < 1) {
733     CONF_ERROR(cmd, "wrong number of parameters");
734   }
735 
736   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
737 
738   /* Possible criteria:
739    *
740    *  FailedLogin
741    *  ModuleConfig
742    *  UntimelyDeath
743    */
744 
745   for (i = 1; i < cmd->argc; i++) {
746     if (strncasecmp(cmd->argv[i], "FailedLogin", 12) == 0) {
747       criteria |= FORENSIC_CRIT_FAILED_LOGIN;
748 
749     } else if (strncasecmp(cmd->argv[i], "ModuleConfig", 13) == 0) {
750       criteria |= FORENSIC_CRIT_MODULE_CONFIG;
751 
752     } else if (strncasecmp(cmd->argv[i], "UntimelyDeath", 14) == 0) {
753       criteria |= FORENSIC_CRIT_UNTIMELY_DEATH;
754 
755     } else {
756       CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown criterion: ",
757         cmd->argv[i], NULL));
758     }
759   }
760 
761   c = add_config_param(cmd->argv[0], 1, NULL);
762   c->argv[0] = palloc(c->pool, sizeof(unsigned long));
763   *((unsigned long *) c->argv[0]) = criteria;
764 
765   return PR_HANDLED(cmd);
766 }
767 
768 /* usage: ForensicLogEngine on|off */
set_forensiclogengine(cmd_rec * cmd)769 MODRET set_forensiclogengine(cmd_rec *cmd) {
770   config_rec *c;
771   int bool;
772 
773   CHECK_ARGS(cmd, 1);
774   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
775 
776   bool = get_boolean(cmd, 1);
777   if (bool == -1) {
778     CONF_ERROR(cmd, "expected Boolean parameter");
779   }
780 
781   c = add_config_param(cmd->argv[0], 1, NULL);
782   c->argv[0] = palloc(c->pool, sizeof(int));
783   *((int *) c->argv[0]) = bool;
784 
785   return PR_HANDLED(cmd);
786 }
787 
788 /* usage: ForensicLogFile path */
set_forensiclogfile(cmd_rec * cmd)789 MODRET set_forensiclogfile(cmd_rec *cmd) {
790   CHECK_ARGS(cmd, 1);
791   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
792 
793   if (pr_fs_valid_path(cmd->argv[1]) < 0) {
794     CONF_ERROR(cmd, "must be an absolute path");
795   }
796 
797   (void) add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
798   return PR_HANDLED(cmd);
799 }
800 
801 /* Command handlers
802  */
803 
forensic_pass_err(cmd_rec * cmd)804 MODRET forensic_pass_err(cmd_rec *cmd) {
805   if (!forensic_engine) {
806     return PR_DECLINED(cmd);
807   }
808 
809   if (forensic_criteria & FORENSIC_CRIT_FAILED_LOGIN) {
810     forensic_write_msgs(FORENSIC_CRIT_FAILED_LOGIN);
811   }
812 
813   return PR_DECLINED(cmd);
814 }
815 
816 /* Event Listeners
817  */
818 
forensic_exit_ev(const void * event_data,void * user_data)819 static void forensic_exit_ev(const void *event_data, void *user_data) {
820 
821   switch (session.disconnect_reason) {
822     case PR_SESS_DISCONNECT_SIGNAL:
823       if (forensic_criteria & FORENSIC_CRIT_UNTIMELY_DEATH) {
824         forensic_write_msgs(FORENSIC_CRIT_UNTIMELY_DEATH);
825       }
826       break;
827 
828     case PR_SESS_DISCONNECT_MODULE_ACL:
829       if (forensic_criteria & FORENSIC_CRIT_MODULE_CONFIG) {
830         forensic_write_msgs(FORENSIC_CRIT_MODULE_CONFIG);
831       }
832       break;
833   }
834 
835   return;
836 }
837 
forensic_log_ev(const void * event_data,void * user_data)838 static void forensic_log_ev(const void *event_data, void *user_data) {
839   const pr_log_event_t *le;
840 
841   le = event_data;
842   forensic_add_msg(le->log_type, le->log_level, le->log_msg, le->log_msglen);
843 }
844 
845 #if defined(PR_SHARED_MODULE)
forensic_mod_unload_ev(const void * event_data,void * user_data)846 static void forensic_mod_unload_ev(const void *event_data, void *user_data) {
847   if (strcmp("mod_log_forensic.c", (const char *) event_data) == 0) {
848     pr_event_unregister(&log_forensic_module, NULL, NULL);
849   }
850 }
851 #endif /* PR_SHARED_MODULE */
852 
forensic_sess_reinit_ev(const void * event_data,void * user_data)853 static void forensic_sess_reinit_ev(const void *event_data, void *user_data) {
854   int res;
855 
856   /* A HOST command changed the main_server pointer, reinitialize ourselves. */
857 
858   pr_event_unregister(&log_forensic_module, "core.exit", forensic_exit_ev);
859   pr_event_unregister(&log_forensic_module, "core.log.unspec", forensic_log_ev);
860   pr_event_unregister(&log_forensic_module, "core.log.xferlog",
861     forensic_log_ev);
862   pr_event_unregister(&log_forensic_module, "core.log.syslog", forensic_log_ev);
863   pr_event_unregister(&log_forensic_module, "core.log.systemlog",
864     forensic_log_ev);
865   pr_event_unregister(&log_forensic_module, "core.log.extlog", forensic_log_ev);
866   pr_event_unregister(&log_forensic_module, "core.log.tracelog",
867     forensic_log_ev);
868   pr_event_unregister(&log_forensic_module, "core.session-reinit",
869     forensic_sess_reinit_ev);
870 
871   forensic_engine = FALSE;
872   (void) close(forensic_logfd);
873   forensic_logfd = -1;
874   forensic_criteria = FORENSIC_CRIT_DEFAULT;
875   forensic_msgs = NULL;
876   forensic_nmsgs = FORENSIC_DEFAULT_NMSGS;
877   forensic_msg_idx = 0;
878 
879   if (forensic_subpool != NULL) {
880     destroy_pool(forensic_subpool);
881     forensic_subpool = NULL;
882   }
883 
884   forensic_subpool_msgno = 1;
885 
886   res = forensic_sess_init();
887   if (res < 0) {
888     pr_session_disconnect(&log_forensic_module,
889       PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL);
890   }
891 }
892 
893 /* Module Initialization
894  */
895 
forensic_init(void)896 static int forensic_init(void) {
897 #if defined(PR_SHARED_MODULE)
898   pr_event_register(&log_forensic_module, "core.module-unload",
899     forensic_mod_unload_ev, NULL);
900 #endif /* PR_SHARED_MODULE */
901 
902   return 0;
903 }
904 
forensic_sess_init(void)905 static int forensic_sess_init(void) {
906   config_rec *c;
907   int unspec_listen = TRUE;
908   int xferlog_listen = TRUE;
909   int syslog_listen = TRUE;
910   int systemlog_listen = TRUE;
911   int extlog_listen = TRUE;
912   int tracelog_listen = TRUE;
913   int res, xerrno;
914 
915   pr_event_register(&log_forensic_module, "core.session-reinit",
916     forensic_sess_reinit_ev, NULL);
917 
918   /* Is this module enabled? */
919   c = find_config(main_server->conf, CONF_PARAM, "ForensicLogEngine", FALSE);
920   if (c != NULL) {
921     forensic_engine = *((int *) c->argv[0]);
922   }
923 
924   if (forensic_engine != TRUE) {
925     return 0;
926   }
927 
928   /* Do we have the required path for our logging? */
929   c = find_config(main_server->conf, CONF_PARAM, "ForensicLogFile", FALSE);
930   if (c == NULL) {
931     pr_log_debug(DEBUG1, MOD_LOG_FORENSIC_VERSION
932       ": missing required ForensicLogFile setting, disabling module");
933     forensic_engine = FALSE;
934     return 0;
935   }
936 
937   pr_signals_block();
938   PRIVS_ROOT
939   res = pr_log_openfile((const char *) c->argv[0], &forensic_logfd, 0640);
940   xerrno = errno;
941   PRIVS_RELINQUISH
942   pr_signals_unblock();
943 
944   if (res < 0) {
945     const char *path;
946 
947     path = c->argv[0];
948 
949     if (res == -1) {
950       pr_log_pri(PR_LOG_NOTICE, MOD_LOG_FORENSIC_VERSION
951         ": notice: unable to open ForensicLogFile '%s': %s", path,
952         strerror(xerrno));
953 
954     } else if (res == PR_LOG_WRITABLE_DIR) {
955       pr_log_pri(PR_LOG_WARNING, MOD_LOG_FORENSIC_VERSION
956         ": notice: unable to open ForensicLogFile '%s': parent directory is "
957         "world-writable", path);
958 
959     } else if (res == PR_LOG_SYMLINK) {
960       pr_log_pri(PR_LOG_WARNING, MOD_LOG_FORENSIC_VERSION
961         ": notice: unable to open ForensicLogFile '%s': "
962         "cannot log to a symlink", path);
963     }
964 
965     pr_log_debug(DEBUG0, MOD_LOG_FORENSIC_VERSION
966       ": unable to ForensicLogFile '%s', disabling module", path);
967     forensic_engine = FALSE;
968     return 0;
969   }
970 
971   /* Are there any log types for which we shouldn't be listening? */
972   c = find_config(main_server->conf, CONF_PARAM, "ForensicLogCapture", FALSE);
973   if (c) {
974     unspec_listen = *((int *) c->argv[0]);
975     xferlog_listen = *((int *) c->argv[1]);
976     syslog_listen = *((int *) c->argv[2]);
977     systemlog_listen = *((int *) c->argv[3]);
978     extlog_listen = *((int *) c->argv[4]);
979     tracelog_listen = *((int *) c->argv[5]);
980   }
981 
982   /* What criteria are we to use for logging our captured log messages */
983   c = find_config(main_server->conf, CONF_PARAM, "ForensicLogCriteria", FALSE);
984   if (c) {
985     forensic_criteria = *((unsigned long *) c->argv[0]);
986   }
987 
988   if (forensic_pool == NULL) {
989     forensic_pool = make_sub_pool(session.pool);
990     pr_pool_tag(forensic_pool, MOD_LOG_FORENSIC_VERSION);
991   }
992 
993   c = find_config(main_server->conf, CONF_PARAM, "ForensicLogBufferSize",
994     FALSE);
995   if (c) {
996     forensic_nmsgs = *((unsigned int *) c->argv[0]);
997 
998     if (forensic_nmsgs < forensic_msgs_per_pool) {
999       forensic_msgs_per_pool = forensic_nmsgs;
1000     }
1001   }
1002 
1003   forensic_msgs = pcalloc(forensic_pool,
1004     sizeof(struct forensic_msg) * forensic_nmsgs);
1005   forensic_subpool = pr_pool_create_sz(forensic_pool, 256);
1006 
1007   /* We register our event listeners as the last thing we do. */
1008 
1009   if ((forensic_criteria & FORENSIC_CRIT_MODULE_CONFIG) ||
1010       (forensic_criteria & FORENSIC_CRIT_UNTIMELY_DEATH)) {
1011     pr_event_register(&log_forensic_module, "core.exit", forensic_exit_ev,
1012       NULL);
1013   }
1014 
1015   if (unspec_listen) {
1016     pr_event_register(&log_forensic_module, "core.log.unspec", forensic_log_ev,
1017       NULL);
1018   }
1019 
1020   if (xferlog_listen) {
1021     pr_event_register(&log_forensic_module, "core.log.xferlog", forensic_log_ev,
1022       NULL);
1023   }
1024 
1025   if (syslog_listen) {
1026     pr_event_register(&log_forensic_module, "core.log.syslog", forensic_log_ev,
1027       NULL);
1028   }
1029 
1030   if (systemlog_listen) {
1031     pr_event_register(&log_forensic_module, "core.log.systemlog",
1032       forensic_log_ev, NULL);
1033   }
1034 
1035   if (extlog_listen) {
1036     pr_event_register(&log_forensic_module, "core.log.extlog", forensic_log_ev,
1037       NULL);
1038   }
1039 
1040   if (tracelog_listen) {
1041     pr_event_register(&log_forensic_module, "core.log.tracelog",
1042       forensic_log_ev, NULL);
1043   }
1044 
1045   return 0;
1046 }
1047 
1048 /* Module API tables
1049  */
1050 
1051 static conftable forensic_conftab[] = {
1052   { "ForensicLogBufferSize",	set_forensiclogbuffersize,	NULL },
1053   { "ForensicLogCapture",	set_forensiclogcapture,		NULL },
1054   { "ForensicLogCriteria",	set_forensiclogcriteria,	NULL },
1055   { "ForensicLogEngine",	set_forensiclogengine,		NULL },
1056   { "ForensicLogFile",		set_forensiclogfile,		NULL },
1057 
1058   { NULL, NULL, NULL }
1059 };
1060 
1061 static cmdtable forensic_cmdtab[] = {
1062   { LOG_CMD_ERR, C_PASS,	G_NONE,	forensic_pass_err, FALSE, FALSE },
1063 
1064   { 0, NULL }
1065 };
1066 
1067 module log_forensic_module = {
1068   /* Always NULL */
1069   NULL, NULL,
1070 
1071   /* Module API version */
1072   0x20,
1073 
1074   /* Module name */
1075   "log_forensic",
1076 
1077   /* Module configuration directive table */
1078   forensic_conftab,
1079 
1080   /* Module command handler table */
1081   forensic_cmdtab,
1082 
1083   /* Module auth handler table */
1084   NULL,
1085 
1086   /* Module initialization */
1087   forensic_init,
1088 
1089   /* Session initialization */
1090   forensic_sess_init,
1091 
1092   /* Module version */
1093   MOD_LOG_FORENSIC_VERSION
1094 };
1095 
1096