2 ******************************************************************************
6 * Purpose: kmsgsd separates iptables messages from all other
9 * Strategy: read messages from the /var/log/psadfifo named pipe and
10 * print any firewall related dop/reject/deny messages to
11 * the psad data file "/var/log/psad/fwdata".
13 * Author: Michael Rash (mbr@cipherdyne.org)
15 * Credits: (see the CREDITS file)
17 * Copyright (C) 1999-2007 Michael Rash (mbr@cipherdyne.org)
19 * License (GNU Public License):
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
30 ******************************************************************************
38 #define CONFIG_FILE "/etc/psad/psad.conf"
40 /* Maximum number of overwrite files allowed on the command line */
41 #define MAX_OVW_FILES 3
44 static volatile sig_atomic_t received_sighup = 0;
45 extern char *optarg; /* for getopt */
46 extern int optind; /* for getopt */
47 char *fw_msg_search[MAX_GEN_LEN];
48 char psadfifo_file[MAX_PATH_LEN];
49 char fwdata_file[MAX_PATH_LEN];
50 char fw_search_file[MAX_PATH_LEN];
51 char snort_sid_str[MAX_PATH_LEN];
52 char psad_dir[MAX_PATH_LEN];
53 char psad_fifo_dir[MAX_PATH_LEN];
54 char psad_run_dir[MAX_PATH_LEN];
55 char kmsgsd_pid_file[MAX_PATH_LEN];
56 int num_fw_search_strings;
57 int fw_search_all_flag;
58 unsigned char dump_cfg;
61 static void usage(void);
62 static void clean_settings(void);
63 static void parse_config(char *file);
64 static void check_config(void);
65 static void dump_config(void);
66 static int match_fw_msg(char *fw_mgs);
67 static void find_sub_var_value(
74 static void expand_config_vars(void);
75 static void sighup_handler(int sig);
78 int main(int argc, char *argv[]) {
81 char *overwrite_files[MAX_OVW_FILES+1];
82 char overwrite_cmd[MAX_PATH_LEN];
83 char config_file[MAX_PATH_LEN];
84 char buf[MAX_LINE_BUF];
85 int fifo_fd, fwdata_fd; /* file descriptors */
86 int cmdlopt, numbytes;
88 int matched_ipt_log_msg = 0;
93 fprintf(stderr, "[+] Entering DEBUG mode\n");
94 fprintf(stderr, "[+] Firewall messages will be written to both ");
95 fprintf(stderr, "STDOUT _and_ to fwdata.\n\n");
98 overwrite_files[0] = NULL;
99 strlcpy(config_file, CONFIG_FILE, MAX_PATH_LEN);
102 while((cmdlopt = getopt(argc, argv, "c:O:Dh")) != -1) {
105 strlcpy(config_file, optarg, MAX_PATH_LEN);
108 strlcpy(overwrite_cmd, optarg, MAX_PATH_LEN);
109 list_to_array(overwrite_cmd, ',', overwrite_files, MAX_OVW_FILES);
119 /* clean our settings */
122 /* Parse both the overwrite and configuration file */
123 for (ovw_file_ptr=overwrite_files; *ovw_file_ptr!=NULL; ovw_file_ptr++)
124 parse_config(*ovw_file_ptr);
125 parse_config(config_file);
127 /* Check our settings */
133 /* make sure there isn't another kmsgsd already running */
134 check_unique_pid(kmsgsd_pid_file, "kmsgsd");
137 /* become a daemon */
138 daemonize_process(kmsgsd_pid_file);
141 /* install signal handler for HUP signals */
142 signal(SIGHUP, sighup_handler);
144 /* start doing the real work now that the daemon is running and
145 * the config file has been processed */
147 /* open the psadfifo named pipe. Note that we are opening the pipe
148 * _without_ the O_NONBLOCK flag since we want the read on the file
149 * descriptor to block until there is something new in the pipe.
150 * Also, note that we are opening with O_RDWR, since this seems to
151 * fix the problem with kmsgsd not blocking on the read() if the
152 * system logger dies (and hence closes its file descriptor for the
154 if ((fifo_fd = open(psadfifo_file, O_RDWR)) < 0) {
155 fprintf(stderr, "[*] Could not open %s for reading.\n",
157 exit(EXIT_FAILURE); /* could not open psadfifo named pipe */
160 /* open the fwdata file in append mode so we can write messages from
161 * the pipe into this file. */
162 if ((fwdata_fd = open(fwdata_file,
163 O_CREAT|O_WRONLY|O_APPEND, 0600)) < 0) {
164 fprintf(stderr, "[*] Could not open %s for writing.\n", fwdata_file);
165 exit(EXIT_FAILURE); /* could not open fwdata file */
169 * Read data from the pipe indefinitely (we opened it _without_
170 * O_NONBLOCK) and write it to the fwdata file if it is a firewall message
172 while ((numbytes = read(fifo_fd, buf, MAX_LINE_BUF-1)) >= 0) {
176 "read %d bytes from %s fifo.\n", numbytes, psadfifo_file);
179 /* make sure the buf contents qualifies as a string */
180 buf[numbytes] = '\0';
182 if (received_sighup) {
184 /* clear the signal flag */
187 /* clean our settings */
190 /* reparse the config file since we received a HUP signal */
191 for (ovw_file_ptr=overwrite_files; *ovw_file_ptr!=NULL; ovw_file_ptr++)
192 parse_config(*ovw_file_ptr);
193 parse_config(config_file);
197 /* close file descriptors and re-open them after
198 * re-reading config file */
202 /* re-open psadfifo and fwdata files */
203 if ((fifo_fd = open(psadfifo_file, O_RDWR)) < 0) {
204 fprintf(stderr, "[*] Could not open %s for reading.\n",
206 exit(EXIT_FAILURE); /* could not open psadfifo named pipe */
209 if ((fwdata_fd = open(fwdata_file, O_CREAT|O_WRONLY|O_APPEND,
211 fprintf(stderr, "[*] Could not open %s for writing.\n",
213 exit(EXIT_FAILURE); /* could not open fwdata file */
215 slogr("psad(kmsgsd)", "received HUP signal");
218 /* see if we matched a firewall message and write it to the
220 if ((strstr(buf, "OUT=") != NULL
221 && strstr(buf, "IN=") != NULL)) {
222 if (! fw_search_all_flag) {
223 /* we are looking for specific log prefixes */
224 if (match_fw_msg(buf) || strstr(buf, snort_sid_str) != NULL) {
225 if (write(fwdata_fd, buf, numbytes) < 0) {
226 exit(EXIT_FAILURE); /* could not write to the fwdata file */
229 matched_ipt_log_msg = 1;
233 if (write(fwdata_fd, buf, numbytes) < 0)
234 exit(EXIT_FAILURE); /* could not write to the fwdata file */
236 matched_ipt_log_msg = 1;
240 if (matched_ipt_log_msg) {
242 fprintf(stderr, "[+] Line matched search strings.\n");
244 if (fwlinectr % 50 == 0)
246 "[+] Processed %d firewall lines.\n", fwlinectr);
247 matched_ipt_log_msg = 0;
250 fprintf(stderr, "[-] Line did not match search strings.\n");
256 /* these statements don't get executed, but for completeness... */
262 /******************** end main ********************/
264 static int match_fw_msg(char *fw_msg)
267 for (i=0; i < num_fw_search_strings; i++)
268 if (strstr(fw_msg, fw_msg_search[i]) != NULL)
273 static void parse_config(char * file)
275 FILE *config_ptr; /* FILE pointer to the config file */
277 char config_buf[MAX_LINE_BUF];
278 char tmp_fw_search_buf[MAX_GEN_LEN], *index;
280 for (i=0; i < num_fw_search_strings; i++)
281 if (fw_msg_search[i] != NULL)
282 free(fw_msg_search[i]);
284 num_fw_search_strings = 0;
285 fw_msg_search[num_fw_search_strings] = NULL;
288 fprintf(stderr, "[+] Parsing file %s\n", file);
291 if ((config_ptr = fopen(file, "r")) == NULL) {
292 perror("[*] Could not open config file");
296 /* increment through each line of the config file */
297 while ((fgets(config_buf, MAX_LINE_BUF, config_ptr)) != NULL) {
299 /* set the index pointer to the beginning of the line */
302 /* advance the index pointer through any whitespace
303 * at the beginning of the line */
304 while (*index == ' ' || *index == '\t') index++;
306 /* skip comments and blank lines, etc. */
307 if ((*index != '#') && (*index != '\n') &&
308 (*index != ';') && (index != NULL)) {
310 find_char_var("PSAD_DIR", psad_dir, index);
311 find_char_var("PSAD_FIFO_DIR", psad_fifo_dir, index);
312 find_char_var("PSAD_RUN_DIR", psad_run_dir, index);
313 find_char_var("SNORT_SID_STR", snort_sid_str, index);
314 find_char_var("PSAD_FIFO_FILE", psadfifo_file, index);
315 find_char_var("FW_DATA_FILE", fwdata_file, index);
316 find_char_var("KMSGSD_PID_FILE", kmsgsd_pid_file, index);
317 if (find_char_var("FW_MSG_SEARCH", tmp_fw_search_buf, index)) {
318 fw_msg_search[num_fw_search_strings]
319 = (char *) safe_malloc(strlen(tmp_fw_search_buf)+1);
320 strlcpy(fw_msg_search[num_fw_search_strings],
321 tmp_fw_search_buf, strlen(tmp_fw_search_buf)+1);
322 num_fw_search_strings++;
324 if (find_char_var("FW_SEARCH_ALL", tmp_fw_search_buf, index)) {
325 if (tmp_fw_search_buf[0] == 'N')
326 fw_search_all_flag = 0;
335 static void expand_config_vars(void)
337 char sub_var[MAX_GEN_LEN] = "";
338 char pre_str[MAX_GEN_LEN] = "";
339 char post_str[MAX_GEN_LEN] = "";
340 int found_sub_var = 1, resolve_ctr = 0;
342 while (found_sub_var) {
344 if (resolve_ctr >= 20) {
345 fprintf(stderr, "[*] Exceeded maximum variable resolution attempts.\n");
350 if (has_sub_var("SNORT_SID_STR", snort_sid_str, sub_var,
351 pre_str, post_str)) {
352 find_sub_var_value(snort_sid_str, sub_var, pre_str, post_str);
356 if (has_sub_var("FW_DATA_FILE", fwdata_file, sub_var,
357 pre_str, post_str)) {
358 find_sub_var_value(fwdata_file, sub_var, pre_str, post_str);
362 if (has_sub_var("PSAD_FIFO_FILE", psadfifo_file, sub_var,
363 pre_str, post_str)) {
364 find_sub_var_value(psadfifo_file, sub_var, pre_str, post_str);
368 if (has_sub_var("KMSGSD_PID_FILE", kmsgsd_pid_file, sub_var,
369 pre_str, post_str)) {
370 find_sub_var_value(kmsgsd_pid_file, sub_var, pre_str, post_str);
377 static void find_sub_var_value(char *value, char *sub_var, char *pre_str,
381 if (strncmp(sub_var, "PSAD_DIR", MAX_GEN_LEN) == 0) {
382 strlcpy(sub_var, psad_dir, MAX_GEN_LEN);
384 } else if (strncmp(sub_var, "PSAD_FIFO_DIR", MAX_GEN_LEN) == 0) {
385 strlcpy(sub_var, psad_fifo_dir, MAX_GEN_LEN);
387 } else if (strncmp(sub_var, "PSAD_RUN_DIR", MAX_GEN_LEN) == 0) {
388 strlcpy(sub_var, psad_run_dir, MAX_GEN_LEN);
390 } else if (strncmp(sub_var, "SNORT_SID_STR", MAX_GEN_LEN) == 0) {
391 strlcpy(sub_var, snort_sid_str, MAX_GEN_LEN);
393 } else if (strncmp(sub_var, "FW_DATA_FILE", MAX_GEN_LEN) == 0) {
394 strlcpy(sub_var, fwdata_file, MAX_GEN_LEN);
396 } else if (strncmp(sub_var, "PSAD_FIFO_FILE", MAX_GEN_LEN) == 0) {
397 strlcpy(sub_var, psadfifo_file, MAX_GEN_LEN);
399 } else if (strncmp(sub_var, "KMSGSD_PID_FILE", MAX_GEN_LEN) == 0) {
400 strlcpy(sub_var, kmsgsd_pid_file, MAX_GEN_LEN);
406 /* substitute the variable value */
407 expand_sub_var_value(value, sub_var, pre_str, post_str);
410 fprintf(stderr, "[*] Could not resolve sub-var: %s to a value.\n",
417 static void dump_config(void)
419 fprintf(stderr, "[+] dump_config()\n");
420 fprintf(stderr, " PSAD_DIR: %s\n", psad_dir);
421 fprintf(stderr, " PSAD_FIFO_FILE: %s\n", psadfifo_file);
422 fprintf(stderr, " FW_DATA_FILE: %s\n", fwdata_file);
423 fprintf(stderr, " SNORT_SID_STR: %s\n", snort_sid_str);
424 fprintf(stderr, " KMSGSD_PID_FILE: %s\n", kmsgsd_pid_file);
429 static void check_config(void)
434 fprintf(stderr, "[+] Checking configuration...\n");
438 if (psad_dir[0] == '\0')
439 fprintf(stderr, "[*] Could not find PSAD_DIR\n");
441 else if (psadfifo_file[0] == '\0')
442 fprintf(stderr, "[*] Could not find PSAD_FIFO_FILE\n");
444 else if (fwdata_file[0] == '\0')
445 fprintf(stderr, "[*] Could not find FW_DATA_FILE\n");
447 else if (snort_sid_str[0] == '\0')
448 fprintf(stderr, "[*] Could not find SNORT_SID_STR\n");
450 else if (kmsgsd_pid_file[0] == '\0')
451 fprintf(stderr, "[*] Could not find KMSGSD_PID_FILE\n");
453 /* Resolve any embedded variables */
455 expand_config_vars();
457 /* there are no FW_MSG_SEARCH vars in fw_search.conf; default
458 * to "DROP". Psad will generate a syslog warning. */
459 if (! fw_search_all_flag && num_fw_search_strings == 0) {
460 fw_msg_search[num_fw_search_strings]
461 = (char *) safe_malloc(strlen("DROP")+1);
462 strlcpy(fw_msg_search[0], "DROP", strlen("DROP")+1);
463 num_fw_search_strings++;
473 static void clean_settings (void)
477 fprintf(stderr, "[+] Cleaning settings\n");
480 /* default to parse all iptables messages */
481 num_fw_search_strings = 0;
482 fw_search_all_flag = 1;
485 *psad_fifo_dir = '\0';
486 *psad_run_dir = '\0';
487 *psadfifo_file = '\0';
489 *snort_sid_str = '\0';
490 *kmsgsd_pid_file = '\0';
494 static void sighup_handler(int sig)
500 * Usage message to be displayed when -h option is supplied or a bad option
501 * is passed to the daemon. This function ends the execution of the program.
503 static void usage (void)
506 "kmsgsd - separates iptables messages from all other kernel messages\n\n");
508 fprintf(stderr, "[+] Version: %s\n", PSAD_VERSION);
510 " By Michael Rash (mbr@cipherdyne.org)\n"
511 " URL: http://www.cipherdyne.org/psad/\n\n");
513 fprintf(stderr, "Usage: kmsgsd [options]\n\n");
517 " -c <file> - Specify path to config file instead of using the\n"
518 " default $config_file.\n"
519 " -D - Dump the configuration values that psad\n"
520 " derives from the /etc/psad/psad.conf (or other\n"
521 " override files) on STDERR\n"
522 " -h - Display this usage message and exit\n"
523 " -O <file> - Override config variable values that are normally\n"
524 " read from the /etc/psad/psad.conf file with\n"
525 " values from the specified file\n");