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 install_root[MAX_PATH_LEN];
53 char psad_dir[MAX_PATH_LEN];
54 char psad_fifo_dir[MAX_PATH_LEN];
55 char psad_run_dir[MAX_PATH_LEN];
56 char kmsgsd_pid_file[MAX_PATH_LEN];
57 int num_fw_search_strings;
58 int fw_search_all_flag;
59 unsigned char dump_cfg;
62 static void usage(void);
63 static void clean_settings(void);
64 static void parse_config(char *file);
65 static void check_config(void);
66 static void dump_config(void);
67 static int match_fw_msg(char *fw_mgs);
68 static void find_sub_var_value(
75 static void expand_config_vars(void);
76 static void sighup_handler(int sig);
79 int main(int argc, char *argv[]) {
82 char *overwrite_files[MAX_OVW_FILES+1];
83 char overwrite_cmd[MAX_PATH_LEN];
84 char config_file[MAX_PATH_LEN];
85 char buf[MAX_LINE_BUF];
86 int fifo_fd, fwdata_fd; /* file descriptors */
87 int cmdlopt, numbytes;
89 int matched_ipt_log_msg = 0;
94 fprintf(stderr, "[+] Entering DEBUG mode\n");
95 fprintf(stderr, "[+] Firewall messages will be written to both ");
96 fprintf(stderr, "STDOUT _and_ to fwdata.\n\n");
99 overwrite_files[0] = NULL;
100 strlcpy(config_file, CONFIG_FILE, MAX_PATH_LEN);
103 while((cmdlopt = getopt(argc, argv, "c:O:Dh")) != -1) {
106 strlcpy(config_file, optarg, MAX_PATH_LEN);
109 strlcpy(overwrite_cmd, optarg, MAX_PATH_LEN);
110 list_to_array(overwrite_cmd, ',', overwrite_files, MAX_OVW_FILES);
120 /* clean our settings */
123 /* Parse both the overwrite and configuration file */
124 for (ovw_file_ptr=overwrite_files; *ovw_file_ptr!=NULL; ovw_file_ptr++)
125 parse_config(*ovw_file_ptr);
126 parse_config(config_file);
128 /* Check our settings */
134 /* make sure there isn't another kmsgsd already running */
135 check_unique_pid(kmsgsd_pid_file, "kmsgsd");
138 /* become a daemon */
139 daemonize_process(kmsgsd_pid_file);
142 /* install signal handler for HUP signals */
143 signal(SIGHUP, sighup_handler);
145 /* start doing the real work now that the daemon is running and
146 * the config file has been processed */
148 /* open the psadfifo named pipe. Note that we are opening the pipe
149 * _without_ the O_NONBLOCK flag since we want the read on the file
150 * descriptor to block until there is something new in the pipe.
151 * Also, note that we are opening with O_RDWR, since this seems to
152 * fix the problem with kmsgsd not blocking on the read() if the
153 * system logger dies (and hence closes its file descriptor for the
155 if ((fifo_fd = open(psadfifo_file, O_RDWR)) < 0) {
156 fprintf(stderr, "[*] Could not open %s for reading.\n",
158 exit(EXIT_FAILURE); /* could not open psadfifo named pipe */
161 /* open the fwdata file in append mode so we can write messages from
162 * the pipe into this file. */
163 if ((fwdata_fd = open(fwdata_file,
164 O_CREAT|O_WRONLY|O_APPEND, 0600)) < 0) {
165 fprintf(stderr, "[*] Could not open %s for writing.\n", fwdata_file);
166 exit(EXIT_FAILURE); /* could not open fwdata file */
170 * Read data from the pipe indefinitely (we opened it _without_
171 * O_NONBLOCK) and write it to the fwdata file if it is a firewall message
173 while ((numbytes = read(fifo_fd, buf, MAX_LINE_BUF-1)) >= 0) {
177 "read %d bytes from %s fifo.\n", numbytes, psadfifo_file);
180 /* make sure the buf contents qualifies as a string */
181 buf[numbytes] = '\0';
183 if (received_sighup) {
185 /* clear the signal flag */
188 /* clean our settings */
191 /* reparse the config file since we received a HUP signal */
192 for (ovw_file_ptr=overwrite_files; *ovw_file_ptr!=NULL; ovw_file_ptr++)
193 parse_config(*ovw_file_ptr);
194 parse_config(config_file);
198 /* close file descriptors and re-open them after
199 * re-reading config file */
203 /* re-open psadfifo and fwdata files */
204 if ((fifo_fd = open(psadfifo_file, O_RDWR)) < 0) {
205 fprintf(stderr, "[*] Could not open %s for reading.\n",
207 exit(EXIT_FAILURE); /* could not open psadfifo named pipe */
210 if ((fwdata_fd = open(fwdata_file, O_CREAT|O_WRONLY|O_APPEND,
212 fprintf(stderr, "[*] Could not open %s for writing.\n",
214 exit(EXIT_FAILURE); /* could not open fwdata file */
216 slogr("psad(kmsgsd)", "received HUP signal");
219 /* see if we matched a firewall message and write it to the
221 if ((strstr(buf, "OUT=") != NULL
222 && strstr(buf, "IN=") != NULL)) {
223 if (! fw_search_all_flag) {
224 /* we are looking for specific log prefixes */
225 if (match_fw_msg(buf) || strstr(buf, snort_sid_str) != NULL) {
226 if (write(fwdata_fd, buf, numbytes) < 0) {
227 exit(EXIT_FAILURE); /* could not write to the fwdata file */
230 matched_ipt_log_msg = 1;
234 if (write(fwdata_fd, buf, numbytes) < 0)
235 exit(EXIT_FAILURE); /* could not write to the fwdata file */
237 matched_ipt_log_msg = 1;
241 if (matched_ipt_log_msg) {
243 fprintf(stderr, "[+] Line matched search strings.\n");
245 if (fwlinectr % 50 == 0)
247 "[+] Processed %d firewall lines.\n", fwlinectr);
248 matched_ipt_log_msg = 0;
251 fprintf(stderr, "[-] Line did not match search strings.\n");
257 /* these statements don't get executed, but for completeness... */
263 /******************** end main ********************/
265 static int match_fw_msg(char *fw_msg)
268 for (i=0; i < num_fw_search_strings; i++)
269 if (strstr(fw_msg, fw_msg_search[i]) != NULL)
274 static void parse_config(char * file)
276 FILE *config_ptr; /* FILE pointer to the config file */
278 char config_buf[MAX_LINE_BUF];
279 char tmp_fw_search_buf[MAX_GEN_LEN], *index;
281 for (i=0; i < num_fw_search_strings; i++)
282 if (fw_msg_search[i] != NULL)
283 free(fw_msg_search[i]);
285 num_fw_search_strings = 0;
286 fw_msg_search[num_fw_search_strings] = NULL;
289 fprintf(stderr, "[+] Parsing file %s\n", file);
292 if ((config_ptr = fopen(file, "r")) == NULL) {
293 perror("[*] Could not open config file");
297 /* increment through each line of the config file */
298 while ((fgets(config_buf, MAX_LINE_BUF, config_ptr)) != NULL) {
300 /* set the index pointer to the beginning of the line */
303 /* advance the index pointer through any whitespace
304 * at the beginning of the line */
305 while (*index == ' ' || *index == '\t') index++;
307 /* skip comments and blank lines, etc. */
308 if ((*index != '#') && (*index != '\n') &&
309 (*index != ';') && (index != NULL)) {
311 find_char_var("INSTALL_ROOT", install_root, index);
312 find_char_var("PSAD_DIR", psad_dir, index);
313 find_char_var("PSAD_FIFO_DIR", psad_fifo_dir, index);
314 find_char_var("PSAD_RUN_DIR", psad_run_dir, index);
315 find_char_var("SNORT_SID_STR", snort_sid_str, index);
316 find_char_var("PSAD_FIFO_FILE", psadfifo_file, index);
317 find_char_var("FW_DATA_FILE", fwdata_file, index);
318 find_char_var("KMSGSD_PID_FILE", kmsgsd_pid_file, index);
319 if (find_char_var("FW_MSG_SEARCH", tmp_fw_search_buf, index)) {
320 fw_msg_search[num_fw_search_strings]
321 = (char *) safe_malloc(strlen(tmp_fw_search_buf)+1);
322 strlcpy(fw_msg_search[num_fw_search_strings],
323 tmp_fw_search_buf, strlen(tmp_fw_search_buf)+1);
324 num_fw_search_strings++;
326 if (find_char_var("FW_SEARCH_ALL", tmp_fw_search_buf, index)) {
327 if (tmp_fw_search_buf[0] == 'N')
328 fw_search_all_flag = 0;
337 static void expand_config_vars(void)
339 char sub_var[MAX_GEN_LEN] = "";
340 char pre_str[MAX_GEN_LEN] = "";
341 char post_str[MAX_GEN_LEN] = "";
342 int found_sub_var = 1, resolve_ctr = 0;
344 while (found_sub_var) {
346 if (resolve_ctr >= 20) {
347 fprintf(stderr, "[*] Exceeded maximum variable resolution attempts.\n");
352 if (has_sub_var("INSTALL_ROOT", install_root, sub_var,
353 pre_str, post_str)) {
354 find_sub_var_value(install_root, sub_var, pre_str, post_str);
358 if (has_sub_var("SNORT_SID_STR", snort_sid_str, sub_var,
359 pre_str, post_str)) {
360 find_sub_var_value(snort_sid_str, sub_var, pre_str, post_str);
364 if (has_sub_var("FW_DATA_FILE", fwdata_file, sub_var,
365 pre_str, post_str)) {
366 find_sub_var_value(fwdata_file, sub_var, pre_str, post_str);
370 if (has_sub_var("PSAD_FIFO_FILE", psadfifo_file, sub_var,
371 pre_str, post_str)) {
372 find_sub_var_value(psadfifo_file, sub_var, pre_str, post_str);
376 if (has_sub_var("KMSGSD_PID_FILE", kmsgsd_pid_file, sub_var,
377 pre_str, post_str)) {
378 find_sub_var_value(kmsgsd_pid_file, sub_var, pre_str, post_str);
385 static void find_sub_var_value(char *value, char *sub_var, char *pre_str,
389 if (strncmp(sub_var, "PSAD_DIR", MAX_GEN_LEN) == 0) {
390 strlcpy(sub_var, psad_dir, MAX_GEN_LEN);
392 } else if (strncmp(sub_var, "PSAD_FIFO_DIR", MAX_GEN_LEN) == 0) {
393 strlcpy(sub_var, psad_fifo_dir, MAX_GEN_LEN);
395 } else if (strncmp(sub_var, "INSTALL_ROOT", MAX_GEN_LEN) == 0) {
396 strlcpy(sub_var, install_root, MAX_GEN_LEN);
398 } else if (strncmp(sub_var, "PSAD_RUN_DIR", MAX_GEN_LEN) == 0) {
399 strlcpy(sub_var, psad_run_dir, MAX_GEN_LEN);
401 } else if (strncmp(sub_var, "SNORT_SID_STR", MAX_GEN_LEN) == 0) {
402 strlcpy(sub_var, snort_sid_str, MAX_GEN_LEN);
404 } else if (strncmp(sub_var, "FW_DATA_FILE", MAX_GEN_LEN) == 0) {
405 strlcpy(sub_var, fwdata_file, MAX_GEN_LEN);
407 } else if (strncmp(sub_var, "PSAD_FIFO_FILE", MAX_GEN_LEN) == 0) {
408 strlcpy(sub_var, psadfifo_file, MAX_GEN_LEN);
410 } else if (strncmp(sub_var, "KMSGSD_PID_FILE", MAX_GEN_LEN) == 0) {
411 strlcpy(sub_var, kmsgsd_pid_file, MAX_GEN_LEN);
417 /* substitute the variable value */
418 expand_sub_var_value(value, sub_var, pre_str, post_str);
421 fprintf(stderr, "[*] Could not resolve sub-var: %s to a value.\n",
428 static void dump_config(void)
430 fprintf(stderr, "[+] dump_config()\n");
431 fprintf(stderr, " INSTALL_ROOT: %s\n", install_root);
432 fprintf(stderr, " PSAD_DIR: %s\n", psad_dir);
433 fprintf(stderr, " PSAD_RUN_DIR: %s\n", psad_run_dir);
434 fprintf(stderr, " PSAD_FIFO_FILE: %s\n", psadfifo_file);
435 fprintf(stderr, " FW_DATA_FILE: %s\n", fwdata_file);
436 fprintf(stderr, " SNORT_SID_STR: %s\n", snort_sid_str);
437 fprintf(stderr, " KMSGSD_PID_FILE: %s\n", kmsgsd_pid_file);
442 static void check_config(void)
447 fprintf(stderr, "[+] Checking configuration...\n");
451 if (psad_dir[0] == '\0')
452 fprintf(stderr, "[*] Could not find PSAD_DIR\n");
454 else if (install_root[0] == '\0')
455 fprintf(stderr, "[*] Could not find INSTALL_ROOT\n");
457 else if (psad_run_dir[0] == '\0')
458 fprintf(stderr, "[*] Could not find PSAD_RUN_DIR\n");
460 else if (psadfifo_file[0] == '\0')
461 fprintf(stderr, "[*] Could not find PSAD_FIFO_FILE\n");
463 else if (fwdata_file[0] == '\0')
464 fprintf(stderr, "[*] Could not find FW_DATA_FILE\n");
466 else if (snort_sid_str[0] == '\0')
467 fprintf(stderr, "[*] Could not find SNORT_SID_STR\n");
469 else if (kmsgsd_pid_file[0] == '\0')
470 fprintf(stderr, "[*] Could not find KMSGSD_PID_FILE\n");
472 /* Resolve any embedded variables */
474 expand_config_vars();
476 /* there are no FW_MSG_SEARCH vars in fw_search.conf; default
477 * to "DROP". Psad will generate a syslog warning. */
478 if (! fw_search_all_flag && num_fw_search_strings == 0) {
479 fw_msg_search[num_fw_search_strings]
480 = (char *) safe_malloc(strlen("DROP")+1);
481 strlcpy(fw_msg_search[0], "DROP", strlen("DROP")+1);
482 num_fw_search_strings++;
492 static void clean_settings (void)
496 fprintf(stderr, "[+] Cleaning settings\n");
499 /* default to parse all iptables messages */
500 num_fw_search_strings = 0;
501 fw_search_all_flag = 1;
504 *psad_fifo_dir = '\0';
505 *install_root = '\0';
506 *psad_run_dir = '\0';
507 *psadfifo_file = '\0';
509 *snort_sid_str = '\0';
510 *kmsgsd_pid_file = '\0';
514 static void sighup_handler(int sig)
520 * Usage message to be displayed when -h option is supplied or a bad option
521 * is passed to the daemon. This function ends the execution of the program.
523 static void usage (void)
526 "kmsgsd - separates iptables messages from all other kernel messages\n\n");
528 fprintf(stderr, "[+] Version: %s\n", PSAD_VERSION);
530 " By Michael Rash (mbr@cipherdyne.org)\n"
531 " URL: http://www.cipherdyne.org/psad/\n\n");
533 fprintf(stderr, "Usage: kmsgsd [options]\n\n");
537 " -c <file> - Specify path to config file instead of using the\n"
538 " default $config_file.\n"
539 " -D - Dump the configuration values that psad\n"
540 " derives from the /etc/psad/psad.conf (or other\n"
541 " override files) on STDERR\n"
542 " -h - Display this usage message and exit\n"
543 " -O <file> - Override config variable values that are normally\n"
544 " read from the /etc/psad/psad.conf file with\n"
545 " values from the specified file\n");