2 ******************************************************************************
6 * Purpose: knopmd separates iptables messages from all other kernel
7 * messages. NOTE: This daemon is obselete since the main method
8 * of passive authorization is Single Packet Authorization (SPA),
9 * which offers better security properties than port knocking.
11 * Author: Michael Rash (mbr@cipherdyne.org)
15 * Copyright (C) 2004-2009 Michael Rash (mbr@cipherdyne.org)
17 * License (GNU Public License):
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
29 * NOTE: This code is essentially borrowed from kmsgsd.c that is bundled
30 * psad: http://www.cipherdyne.org/psad/
32 ******************************************************************************
34 * $Id: knopmd.c 1519 2009-08-11 23:56:57Z mbr $
42 #define FWKNOP_CONF "/etc/fwknop/fwknop.conf"
45 static volatile sig_atomic_t received_sighup = 0;
46 extern char *optarg; /* for getopt */
47 extern int optind; /* for getopt */
48 char *fw_msg_search[MAX_GEN_LEN];
49 int num_fw_search_strings = 0;
50 int fw_search_all_flag = 1; /* default to parse all iptables messages */
51 char fwknopfifo_file[MAX_PATH_LEN];
52 char fwdata_file[MAX_PATH_LEN];
53 char config_file[MAX_PATH_LEN];
54 char fw_search_file[MAX_PATH_LEN];
55 char knopmd_pid_file[MAX_PATH_LEN];
56 char fwknop_dir[MAX_PATH_LEN];
57 char fwknop_fifo_dir[MAX_PATH_LEN];
58 char fwknop_run_dir[MAX_PATH_LEN];
61 static void parse_config(void);
62 static void find_sub_var_value(
68 static void expand_config_vars(void);
69 static void check_auth_mode(void);
70 static int match_fw_msg(char *fw_mgs);
71 static void sighup_handler(int sig);
74 int main(int argc, char *argv[]) {
75 char buf[MAX_LINE_BUF];
76 int fifo_fd, fwdata_fd; /* file descriptors */
77 int cmdlopt, numbytes;
79 int matched_ipt_log_msg = 0;
84 fprintf(stderr, "[+] Entering DEBUG mode\n");
85 fprintf(stderr, "[+] Firewall messages will be written to both ");
86 fprintf(stderr, "STDOUT _and_ to fwdata.\n\n");
89 strlcpy(config_file, FWKNOP_CONF, MAX_PATH_LEN);
91 while((cmdlopt = getopt(argc, argv, "c:")) != -1) {
94 strlcpy(config_file, optarg, MAX_PATH_LEN);
97 printf("[+] Usage: knopmd [-c <config file>] ");
103 fprintf(stderr, "[+] parsing config_file: %s\n", config_file);
105 /* parse config file (knopmd.conf) */
108 /* make sure there isn't another knopmd already running */
109 check_unique_pid(knopmd_pid_file, "knopmd");
112 /* become a daemon */
113 daemonize_process(knopmd_pid_file);
116 /* install signal handler for HUP signals */
117 signal(SIGHUP, sighup_handler);
119 /* start doing the real work now that the daemon is running and
120 * the config file has been processed */
122 /* open the fwknopfifo named pipe. Note that we are opening the pipe
123 * _without_ the O_NONBLOCK flag since we want the read on the file
124 * descriptor to block until there is something new in the pipe.
125 * Also, not that we are opening with O_RDWR, since this seems to
126 * fix the problem with knopmd not blocking on the read() if the
127 * system logger dies (and hence closes its file descriptor for the
129 if ((fifo_fd = open(fwknopfifo_file, O_RDWR)) < 0) {
130 fprintf(stderr, "[-] Could not open %s for reading.\n",
132 exit(EXIT_FAILURE); /* could not open fwknopfifo named pipe */
135 /* open the fwdata file in append mode so we can write messages from
136 * the pipe into this file. */
137 if ((fwdata_fd = open(fwdata_file,
138 O_CREAT|O_WRONLY|O_APPEND, 0600)) < 0) {
139 fprintf(stderr, "[-] Could not open %s for writing.\n", fwdata_file);
140 exit(EXIT_FAILURE); /* could not open fwdata file */
144 * Read data from the pipe indefinitely (we opened it _without_
145 * O_NONBLOCK) and write it to the fwdata file if it is a firewall message
147 while ((numbytes = read(fifo_fd, buf, MAX_LINE_BUF-1)) >= 0) {
151 "read %d bytes from %s fifo.\n", numbytes, fwknopfifo_file);
154 /* make sure the buf contents qualifies as a string */
155 buf[numbytes] = '\0';
157 if (received_sighup) {
158 /* clear the signal flag */
161 /* re-parse the config file after receiving HUP signal */
164 /* close file descriptors and re-open them after
165 * re-reading config file */
169 /* re-open fwknopfifo and fwdata files */
170 if ((fifo_fd = open(fwknopfifo_file, O_RDONLY)) < 0) {
171 fprintf(stderr, "[-] Could not open %s for reading.\n",
173 exit(EXIT_FAILURE); /* could not open fwknopfifo named pipe */
176 if ((fwdata_fd = open(fwdata_file, O_CREAT|O_WRONLY|O_APPEND,
178 fprintf(stderr, "[-] Could not open %s for writing.\n",
180 exit(EXIT_FAILURE); /* could not open fwdata file */
182 slogr("fwknop(knopmd)",
183 "[+] received HUP signal, re-imported knopmd.conf");
186 /* see if we matched a firewall message and write it to the
188 if ((strstr(buf, "OUT") != NULL
189 && strstr(buf, "IN") != NULL)) {
190 if (! fw_search_all_flag) { /* we are looking for specific log prefixes */
191 if (match_fw_msg(buf)) {
192 if (write(fwdata_fd, buf, numbytes) < 0) {
193 exit(EXIT_FAILURE); /* could not write to the fwdata file */
196 matched_ipt_log_msg = 1;
200 if (write(fwdata_fd, buf, numbytes) < 0)
201 exit(EXIT_FAILURE); /* could not write to the fwdata file */
203 matched_ipt_log_msg = 1;
207 if (matched_ipt_log_msg) {
209 fprintf(stderr, "[+] Line matched search strings.\n");
211 if (fwlinectr % 50 == 0)
213 "[+] Processed %d firewall lines.\n", fwlinectr);
214 matched_ipt_log_msg = 0;
217 printf("[-] Line did not match search strings.\n");
223 /* these statements don't get executed, but for completeness... */
229 /******************** end main ********************/
231 static int match_fw_msg(char *fw_msg)
234 for (i=0; i < num_fw_search_strings; i++)
235 if (strstr(fw_msg, fw_msg_search[i]) != NULL)
240 static void parse_config(void)
242 FILE *config_ptr; /* FILE pointer to the config file */
243 unsigned int linectr = 0, i;
244 char config_buf[MAX_LINE_BUF], tmp_fw_search_buf[MAX_GEN_LEN];
247 /* first check to see if knopmd should not be running (i.e.
248 * AUTH_MODE in the fwknop.conf file is set to a pcap-based
252 for (i=0; i < num_fw_search_strings; i++)
253 free(fw_msg_search[i]);
255 num_fw_search_strings = 0;
256 fw_msg_search[num_fw_search_strings] = NULL;
258 if ((config_ptr = fopen(config_file, "r")) == NULL) {
259 fprintf(stderr, "[-] Could not open %s for reading.\n",
264 /* increment through each line of the config file */
265 while ((fgets(config_buf, MAX_LINE_BUF, config_ptr)) != NULL) {
267 /* set the index pointer to the beginning of the line */
270 /* advance the index pointer through any whitespace
271 * at the beginning of the line */
272 while (*index == ' ' || *index == '\t') index++;
274 /* skip comments and blank lines, etc. */
275 if ((*index != '#') && (*index != '\n') &&
276 (*index != ';') && (index != NULL)) {
278 find_char_var("FWKNOP_DIR", fwknop_dir, index);
279 find_char_var("KNOPMD_FIFO", fwknopfifo_file, index);
280 find_char_var("FW_DATA_FILE", fwdata_file, index);
281 find_char_var("KNOPMD_PID_FILE", knopmd_pid_file, index);
282 find_char_var("FWKNOP_LIB_DIR", fwknop_fifo_dir, index);
283 find_char_var("FWKNOP_RUN_DIR", fwknop_run_dir, index);
285 if (find_char_var("FW_MSG_SEARCH", tmp_fw_search_buf, index)) {
286 fw_msg_search[num_fw_search_strings]
287 = (char *) safe_malloc(strlen(tmp_fw_search_buf)+1);
288 strlcpy(fw_msg_search[num_fw_search_strings],
289 tmp_fw_search_buf, MAX_GEN_LEN);
290 num_fw_search_strings++;
292 if (find_char_var("FW_SEARCH_ALL", tmp_fw_search_buf, index)) {
293 if (tmp_fw_search_buf[0] == 'N')
294 fw_search_all_flag = 0;
300 /* resolve any embedded variables */
301 expand_config_vars();
304 fprintf(stderr, "[+] FWKNOP_DIR: %s\n", fwknop_dir);
305 fprintf(stderr, "[+] KNOPMD_FIFO: %s\n", fwknopfifo_file);
306 fprintf(stderr, "[+] FW_DATA_FILE: %s\n", fwdata_file);
307 fprintf(stderr, "[+] KNOPMD_PID_FILE: %s\n", knopmd_pid_file);
312 static void check_auth_mode(void)
314 FILE *config_ptr; /* FILE pointer to the config file */
315 char config_buf[MAX_LINE_BUF];
316 char auth_mode[MAX_GEN_LEN];
319 if ((config_ptr = fopen(FWKNOP_CONF, "r")) == NULL) {
320 fprintf(stderr, "[-] Could not open %s for reading.\n",
327 /* increment through each line of the config file */
328 while ((fgets(config_buf, MAX_LINE_BUF, config_ptr)) != NULL) {
329 /* set the index pointer to the beginning of the line */
332 /* advance the index pointer through any whitespace
333 * at the beginning of the line */
334 while (*index == ' ' || *index == '\t') index++;
336 /* skip comments and blank lines, etc. */
337 if ((*index != '#') && (*index != '\n') &&
338 (*index != ';') && (index != NULL)) {
340 find_char_var("AUTH_MODE ", auth_mode, index);
345 /* see if we are using the ULOG_PCAP mode */
346 if (strncmp(auth_mode, "ULOG_PCAP", MAX_GEN_LEN) == 0)
349 /* see if we are using the PCAP mode */
350 if (strncmp(auth_mode, "PCAP", MAX_GEN_LEN) == 0)
356 static void expand_config_vars(void)
358 char sub_var[MAX_GEN_LEN] = "";
359 char pre_str[MAX_GEN_LEN] = "";
360 char post_str[MAX_GEN_LEN] = "";
361 int found_sub_var = 1, resolve_ctr = 0;
363 while (found_sub_var) {
365 if (resolve_ctr >= 20) {
366 fprintf(stderr, "[*] Exceeded maximum variable resolution attempts.\n");
371 if (has_sub_var("FW_DATA_FILE", fwdata_file, sub_var,
372 pre_str, post_str)) {
373 find_sub_var_value(fwdata_file, sub_var, pre_str, post_str);
377 if (has_sub_var("KNOPMD_FIFO", fwknopfifo_file, sub_var,
378 pre_str, post_str)) {
379 find_sub_var_value(fwknopfifo_file, sub_var, pre_str, post_str);
383 if (has_sub_var("KNOPMD_PID_FILE", knopmd_pid_file, sub_var,
384 pre_str, post_str)) {
385 find_sub_var_value(knopmd_pid_file, sub_var, pre_str, post_str);
392 static void find_sub_var_value(char *value, char *sub_var, char *pre_str,
396 if (strncmp(sub_var, "FWKNOP_DIR", MAX_GEN_LEN) == 0) {
397 strlcpy(sub_var, fwknop_dir, MAX_GEN_LEN);
399 } else if (strncmp(sub_var, "FWKNOP_LIB_DIR", MAX_GEN_LEN) == 0) {
400 strlcpy(sub_var, fwknop_fifo_dir, MAX_GEN_LEN);
402 } else if (strncmp(sub_var, "FWKNOP_RUN_DIR", MAX_GEN_LEN) == 0) {
403 strlcpy(sub_var, fwknop_run_dir, MAX_GEN_LEN);
405 } else if (strncmp(sub_var, "FW_DATA_FILE", MAX_GEN_LEN) == 0) {
406 strlcpy(sub_var, fwdata_file, MAX_GEN_LEN);
408 } else if (strncmp(sub_var, "KNOPMD_FIFO", MAX_GEN_LEN) == 0) {
409 strlcpy(sub_var, fwknopfifo_file, MAX_GEN_LEN);
411 } else if (strncmp(sub_var, "KNOPMD_PID_FILE", MAX_GEN_LEN) == 0) {
412 strlcpy(sub_var, knopmd_pid_file, MAX_GEN_LEN);
418 /* substitute the variable value */
419 expand_sub_var_value(value, sub_var, pre_str, post_str);
422 fprintf(stderr, "[*] Could not resolve sub-var: %s to a value.\n",
429 static void sighup_handler(int sig)