Branch data Line data Source code
1 : : /*
2 : : *****************************************************************************
3 : : *
4 : : * File: fwknopd.c
5 : : *
6 : : * Purpose: An implementation of an fwknop server.
7 : : *
8 : : * Fwknop is developed primarily by the people listed in the file 'AUTHORS'.
9 : : * Copyright (C) 2009-2014 fwknop developers and contributors. For a full
10 : : * list of contributors, see the file 'CREDITS'.
11 : : *
12 : : * License (GNU General Public License):
13 : : *
14 : : * This program is free software; you can redistribute it and/or
15 : : * modify it under the terms of the GNU General Public License
16 : : * as published by the Free Software Foundation; either version 2
17 : : * of the License, or (at your option) any later version.
18 : : *
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.
23 : : *
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
27 : : * USA
28 : : *
29 : : *****************************************************************************
30 : : */
31 : : #include "fwknopd.h"
32 : : #include "access.h"
33 : : #include "config_init.h"
34 : : #include "log_msg.h"
35 : : #include "utils.h"
36 : : #include "fw_util.h"
37 : : #include "sig_handler.h"
38 : : #include "replay_cache.h"
39 : : #include "tcp_server.h"
40 : : #include "udp_server.h"
41 : :
42 : : #if USE_LIBPCAP
43 : : #include "pcap_capture.h"
44 : : #endif
45 : :
46 : : /* Prototypes
47 : : */
48 : : static int check_dir_path(const char * const path,
49 : : const char * const path_name, const unsigned char use_basename);
50 : : static int make_dir_path(const char * const path);
51 : : static void daemonize_process(fko_srv_options_t * const opts);
52 : : static int stop_fwknopd(fko_srv_options_t * const opts);
53 : : static int status_fwknopd(fko_srv_options_t * const opts);
54 : : static int restart_fwknopd(fko_srv_options_t * const opts);
55 : : static int write_pid_file(fko_srv_options_t *opts);
56 : : static int handle_signals(fko_srv_options_t *opts);
57 : : static void setup_pid(fko_srv_options_t *opts);
58 : : static void init_digest_cache(fko_srv_options_t *opts);
59 : : static void set_locale(fko_srv_options_t *opts);
60 : : static pid_t get_running_pid(const fko_srv_options_t *opts);
61 : : #if AFL_FUZZING
62 : : static void afl_pkt_from_stdin(fko_srv_options_t *opts);
63 : : #endif
64 : :
65 : : #if HAVE_LIBFIU
66 : : static void enable_fault_injections(fko_srv_options_t * const opts);
67 : : #endif
68 : :
69 : : #if AFL_FUZZING
70 : : #define AFL_MAX_PKT_SIZE 1024
71 : : #define AFL_DUMP_CTX_SIZE 4096
72 : : #endif
73 : :
74 : : int
75 : 11010 : main(int argc, char **argv)
76 : : {
77 : : fko_srv_options_t opts;
78 : :
79 : : while(1)
80 : : {
81 : : /* Handle command line
82 : : */
83 : 5506 : config_init(&opts, argc, argv);
84 : :
85 : : #if HAVE_LIBFIU
86 : : /* Set any fault injection points early
87 : : */
88 : 3708 : enable_fault_injections(&opts);
89 : : #endif
90 : :
91 : : /* Process any options that do their thing and exit.
92 : : */
93 : :
94 : : /* Kill the currently running fwknopd process?
95 : : */
96 [ + + ]: 3708 : if(opts.kill == 1)
97 : 371 : clean_exit(&opts, NO_FW_CLEANUP, stop_fwknopd(&opts));
98 : :
99 : : /* Status of the currently running fwknopd process?
100 : : */
101 [ + + ]: 3337 : if(opts.status == 1)
102 : 739 : clean_exit(&opts, NO_FW_CLEANUP, status_fwknopd(&opts));
103 : :
104 : : /* Restart the currently running fwknopd process?
105 : : */
106 [ + + ]: 2598 : if(opts.restart == 1)
107 : 4 : clean_exit(&opts, NO_FW_CLEANUP, restart_fwknopd(&opts));
108 : :
109 : : /* Initialize logging.
110 : : */
111 : 2594 : init_logging(&opts);
112 : :
113 : : /* Update the verbosity level for the log module */
114 : 2582 : log_set_verbosity(LOG_DEFAULT_VERBOSITY + opts.verbose);
115 : :
116 : : #if HAVE_LOCALE_H
117 : : /* Set the locale if specified.
118 : : */
119 : 2582 : set_locale(&opts);
120 : : #endif
121 : :
122 : : /* Make sure we have a valid run dir and path leading to digest file
123 : : * in case it configured to be somewhere other than the run dir.
124 : : */
125 [ + - ]: 2582 : if(!opts.afl_fuzzing
126 [ + + ]: 2582 : && ! check_dir_path((const char *)opts.config[CONF_FWKNOP_RUN_DIR], "Run", 0))
127 : 2 : clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE);
128 : :
129 : : /* Initialize the firewall rules handler based on the fwknopd.conf
130 : : * file, but (for iptables firewalls) don't flush any rules or create
131 : : * any chains yet. This allows us to dump the current firewall rules
132 : : * via fw_rules_dump() in --fw-list mode before changing around any rules
133 : : * of an existing fwknopd process.
134 : : */
135 [ + + ]: 2580 : if(fw_config_init(&opts) != 1)
136 : 3 : clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE);
137 : :
138 [ + + ][ - + ]: 2577 : if(opts.fw_list == 1 || opts.fw_list_all == 1)
139 : : {
140 : 891 : fw_dump_rules(&opts);
141 : 891 : clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS);
142 : : }
143 : :
144 [ + + ]: 1686 : if(opts.fw_flush == 1)
145 : : {
146 : 18 : fprintf(stdout, "Deleting any existing firewall rules...\n");
147 : 18 : clean_exit(&opts, FW_CLEANUP, EXIT_SUCCESS);
148 : : }
149 : :
150 : : /* Process the access.conf file.
151 : : */
152 : 1668 : parse_access_file(&opts);
153 : :
154 : : /* Show config (including access.conf vars) and exit dump config was
155 : : * wanted.
156 : : */
157 [ + + ]: 1587 : if(opts.dump_config == 1)
158 : : {
159 : 15 : dump_config(&opts);
160 : 15 : dump_access_list(&opts);
161 : 15 : clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS);
162 : : }
163 : :
164 : : /* Acquire pid, become a daemon or run in the foreground, write pid
165 : : * to pid file.
166 : : */
167 : 1572 : setup_pid(&opts);
168 : :
169 [ + + ][ + + ]: 1572 : if(opts.verbose > 1 && opts.foreground)
170 : : {
171 : 1565 : dump_config(&opts);
172 : 1565 : dump_access_list(&opts);
173 : : }
174 : :
175 : : /* Initialize the digest cache for replay attack detection (either
176 : : * with dbm support or with the default simple cache file strategy)
177 : : * if so configured.
178 : : */
179 : 1572 : init_digest_cache(&opts);
180 : :
181 [ + + ]: 1572 : if(opts.exit_after_parse_config)
182 : : {
183 : 1103 : log_msg(LOG_INFO, "Configs parsed, exiting.");
184 : 1103 : clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS);
185 : : }
186 : :
187 : : #if AFL_FUZZING
188 : : /* SPA data from STDIN. */
189 : : if(opts.afl_fuzzing)
190 : : afl_pkt_from_stdin(&opts);
191 : : #endif
192 : :
193 : : /* Prepare the firewall - i.e. flush any old rules and (for iptables)
194 : : * create fwknop chains.
195 : : */
196 [ + + ][ + + ]: 469 : if(!opts.test && (fw_initialize(&opts) != 1))
197 : 3 : clean_exit(&opts, FW_CLEANUP, EXIT_FAILURE);
198 : :
199 : : /* If we are to acquire SPA data via a UDP socket, start it up here.
200 : : */
201 [ + + ][ + + ]: 466 : if(opts.enable_udp_server ||
202 : 457 : strncasecmp(opts.config[CONF_ENABLE_UDP_SERVER], "Y", 1) == 0)
203 : : {
204 [ - + ]: 11 : if(run_udp_server(&opts) < 0)
205 : : {
206 : 0 : log_msg(LOG_ERR, "Fatal run_udp_server() error");
207 : 0 : clean_exit(&opts, FW_CLEANUP, EXIT_FAILURE);
208 : : }
209 : : else
210 : : {
211 : : break;
212 : : }
213 : : }
214 : :
215 : : /* If the TCP server option was set, fire it up here. Note that in
216 : : * this mode, fwknopd still acquires SPA packets via libpcap. If you
217 : : * want to use UDP only without the libpcap dependency, see the FIXME...
218 : : */
219 [ + + ]: 455 : if(strncasecmp(opts.config[CONF_ENABLE_TCP_SERVER], "Y", 1) == 0)
220 : : {
221 [ - + ]: 1 : if(run_tcp_server(&opts) < 0)
222 : : {
223 : 0 : log_msg(LOG_ERR, "Fatal run_tcp_server() error");
224 : 0 : clean_exit(&opts, FW_CLEANUP, EXIT_FAILURE);
225 : : }
226 : : }
227 : :
228 : : #if USE_LIBPCAP
229 : : /* Intiate pcap capture mode...
230 : : */
231 [ + - ]: 455 : if(strncasecmp(opts.config[CONF_ENABLE_UDP_SERVER], "N", 1) == 0)
232 : 455 : pcap_capture(&opts);
233 : : #endif
234 : :
235 : : /* Deal with any signals that we've received and break out
236 : : * of the loop for any terminating signals
237 : : */
238 [ + + ]: 384 : if(handle_signals(&opts) == 1)
239 : : break;
240 : : }
241 : :
242 : 394 : log_msg(LOG_INFO, "Shutting Down fwknopd.");
243 : :
244 : : /* Kill the TCP server (if we have one running).
245 : : */
246 [ - + ]: 394 : if(opts.tcp_server_pid > 0)
247 : : {
248 : 0 : log_msg(LOG_INFO, "Killing the TCP server (pid=%i)",
249 : : opts.tcp_server_pid);
250 : :
251 : 0 : kill(opts.tcp_server_pid, SIGTERM);
252 : :
253 : : /* --DSS XXX: This seems to be necessary if the tcp server
254 : : * was restarted by this program. We need to
255 : : * investigate and fix this. For now, this works
256 : : * (it is kludgy, but does no harm afaik).
257 : : */
258 : 0 : kill(opts.tcp_server_pid, SIGKILL);
259 : : }
260 : :
261 : 394 : clean_exit(&opts, FW_CLEANUP, EXIT_SUCCESS);
262 : :
263 : : return(EXIT_SUCCESS); /* This never gets called */
264 : : }
265 : :
266 : 2582 : static void set_locale(fko_srv_options_t *opts)
267 : : {
268 : : char *locale;
269 : :
270 [ + + ]: 2582 : if(opts->config[CONF_LOCALE] != NULL
271 [ + - ]: 2 : && strncasecmp(opts->config[CONF_LOCALE], "NONE", 4) != 0)
272 : : {
273 : 2 : locale = setlocale(LC_ALL, opts->config[CONF_LOCALE]);
274 : :
275 [ + + ]: 2 : if(locale == NULL)
276 : : {
277 : 1 : log_msg(LOG_ERR,
278 : : "WARNING: Unable to set locale to '%s'.",
279 : : opts->config[CONF_LOCALE]
280 : : );
281 : : }
282 : : else
283 : : {
284 : 1 : log_msg(LOG_INFO,
285 : : "Locale set to '%s'.", opts->config[CONF_LOCALE]
286 : : );
287 : : }
288 : : }
289 : 2582 : return;
290 : : }
291 : :
292 : : #if AFL_FUZZING
293 : : static void afl_pkt_from_stdin(fko_srv_options_t *opts)
294 : : {
295 : : FILE *fp = NULL;
296 : : fko_ctx_t decode_ctx = NULL;
297 : : unsigned char spa_pkt[AFL_MAX_PKT_SIZE] = {0};
298 : : int res = 0, es = EXIT_SUCCESS;
299 : : char dump_buf[AFL_DUMP_CTX_SIZE];
300 : :
301 : : fp = fdopen(STDIN_FILENO, "r");
302 : : if(fp != NULL)
303 : : {
304 : : if(fgets((char *)spa_pkt, AFL_MAX_PKT_SIZE, fp) == NULL)
305 : : {
306 : : fclose(fp);
307 : : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
308 : : }
309 : :
310 : : fclose(fp);
311 : :
312 : : fko_new(&decode_ctx);
313 : :
314 : : res = fko_set_encoded_data(decode_ctx, (char *) spa_pkt,
315 : : strlen((char *)spa_pkt), 0, FKO_DIGEST_SHA256);
316 : :
317 : : if(res == FKO_SUCCESS)
318 : : res = fko_set_spa_data(decode_ctx, (const char *) spa_pkt);
319 : : if(res == FKO_SUCCESS)
320 : : res = fko_decode_spa_data(decode_ctx);
321 : : if(res == FKO_SUCCESS)
322 : : res = dump_ctx_to_buffer(decode_ctx, dump_buf, sizeof(dump_buf));
323 : : if(res == FKO_SUCCESS)
324 : : log_msg(LOG_INFO, "%s", dump_buf);
325 : :
326 : : fko_destroy(decode_ctx);
327 : :
328 : : if(res == FKO_SUCCESS)
329 : : {
330 : : log_msg(LOG_INFO, "SPA packet decode: %s", fko_errstr(res));
331 : : es = EXIT_SUCCESS;
332 : : }
333 : : else
334 : : {
335 : : log_msg(LOG_ERR, "Could not decode SPA packet: %s", fko_errstr(res));
336 : : es = EXIT_FAILURE;
337 : : }
338 : : }
339 : : else
340 : : log_msg(LOG_ERR, "Could not acquire SPA packet from stdin.");
341 : :
342 : : clean_exit(opts, NO_FW_CLEANUP, es);
343 : : }
344 : : #endif
345 : :
346 : 1572 : static void init_digest_cache(fko_srv_options_t *opts)
347 : : {
348 : : int rp_cache_count;
349 : :
350 : : #if AFL_FUZZING
351 : : if(opts->afl_fuzzing)
352 : : return;
353 : : #endif
354 : :
355 [ + - ]: 1572 : if(strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0)
356 : : {
357 : 1572 : rp_cache_count = replay_cache_init(opts);
358 : :
359 [ + + ]: 1572 : if(rp_cache_count < 0)
360 : : {
361 : 8 : log_msg(LOG_WARNING,
362 : : "Error opening digest cache file. Incoming digests will not be remembered."
363 : : );
364 : : /* Destination points to heap memory, and is guaranteed to be
365 : : * at least two bytes large via validate_options(),
366 : : * DEF_ENABLE_DIGEST_PERSISTENCE, and set_config_entry()
367 : : */
368 : 8 : strlcpy(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "N", 2);
369 : : }
370 : :
371 [ + + ]: 1572 : if(opts->verbose)
372 : 1566 : log_msg(LOG_ERR,
373 : : "Using Digest Cache: '%s' (entry count = %i)",
374 : : #if USE_FILE_CACHE
375 : : opts->config[CONF_DIGEST_FILE], rp_cache_count
376 : : #else
377 : : opts->config[CONF_DIGEST_DB_FILE], rp_cache_count
378 : : #endif
379 : : );
380 : : }
381 : 1572 : return;
382 : : }
383 : :
384 : 1572 : static void setup_pid(fko_srv_options_t *opts)
385 : : {
386 : : pid_t old_pid;
387 : :
388 : : #if AFL_FUZZING
389 : : if(opts->afl_fuzzing)
390 : : return;
391 : : #endif
392 : :
393 : : /* If we are a new process (just being started), proceed with normal
394 : : * start-up. Otherwise, we are here as a result of a signal sent to an
395 : : * existing process and we want to restart.
396 : : */
397 [ + - ]: 1572 : if(get_running_pid(opts) != getpid())
398 : : {
399 : : /* If foreground mode is not set, then fork off and become a daemon.
400 : : * Otherwise, attempt to get the pid file lock and go on.
401 : : */
402 [ + + ]: 1572 : if(opts->foreground == 0)
403 : : {
404 : 1 : daemonize_process(opts);
405 : : }
406 : : else
407 : : {
408 : 1571 : old_pid = write_pid_file(opts);
409 [ - + ]: 1571 : if(old_pid > 0)
410 : : {
411 : 0 : fprintf(stderr,
412 : : "[*] An instance of fwknopd is already running: (PID=%i).\n", old_pid
413 : : );
414 : :
415 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
416 : : }
417 [ - + ]: 1571 : else if(old_pid < 0)
418 : : {
419 : 0 : fprintf(stderr, "[*] PID file error. The lock may not be effective.\n");
420 : : }
421 : : }
422 : :
423 : 1572 : log_msg(LOG_INFO, "Starting %s", MY_NAME);
424 : : }
425 : : else
426 : : {
427 : 0 : log_msg(LOG_INFO, "Re-starting %s", MY_NAME);
428 : : }
429 : :
430 : 1572 : return;
431 : : }
432 : :
433 : 4 : static int restart_fwknopd(fko_srv_options_t * const opts)
434 : : {
435 : 4 : int res = 0;
436 : : pid_t old_pid;
437 : :
438 : 4 : old_pid = get_running_pid(opts);
439 : :
440 [ + - ]: 4 : if(old_pid > 0)
441 : : {
442 : 4 : res = kill(old_pid, SIGHUP);
443 [ + + ]: 4 : if(res == 0)
444 : : {
445 : 1 : fprintf(stdout, "Sent restart signal to fwknopd (pid=%i)\n", old_pid);
446 : 1 : return EXIT_SUCCESS;
447 : : }
448 : : else
449 : : {
450 : 3 : perror("Unable to send signal to fwknop: ");
451 : 3 : return EXIT_FAILURE;
452 : : }
453 : : }
454 : :
455 : 0 : fprintf(stdout, "No running fwknopd detected.\n");
456 : 0 : return EXIT_FAILURE;
457 : : }
458 : :
459 : 739 : static int status_fwknopd(fko_srv_options_t * const opts)
460 : : {
461 : : pid_t old_pid;
462 : :
463 : 739 : old_pid = write_pid_file(opts);
464 : :
465 [ + + ]: 739 : if(old_pid > 0)
466 : : {
467 : 366 : fprintf(stdout, "Detected fwknopd is running (pid=%i).\n", old_pid);
468 : 366 : return EXIT_SUCCESS;
469 : : }
470 : :
471 : 373 : fprintf(stdout, "No running fwknopd detected.\n");
472 : 373 : return EXIT_FAILURE;
473 : : }
474 : :
475 : 384 : static int handle_signals(fko_srv_options_t *opts)
476 : : {
477 : 384 : int last_sig = 0, rv = 1;
478 : :
479 [ + + ]: 384 : if(got_signal) {
480 : 360 : last_sig = got_signal;
481 : 360 : got_signal = 0;
482 : :
483 [ + + ]: 360 : if(got_sighup)
484 : : {
485 : 1 : log_msg(LOG_WARNING, "Got SIGHUP. Re-reading configs.");
486 : 1 : free_configs(opts);
487 : 1 : kill(opts->tcp_server_pid, SIGTERM);
488 : 1 : usleep(1000000);
489 : 1 : got_sighup = 0;
490 : 1 : rv = 0; /* this means fwknopd will not exit */
491 : : }
492 [ + + ]: 359 : else if(got_sigint)
493 : : {
494 : 1 : log_msg(LOG_WARNING, "Got SIGINT. Exiting...");
495 : 1 : got_sigint = 0;
496 : : }
497 [ + - ]: 358 : else if(got_sigterm)
498 : : {
499 : 358 : log_msg(LOG_WARNING, "Got SIGTERM. Exiting...");
500 : 358 : got_sigterm = 0;
501 : : }
502 : : else
503 : : {
504 : 0 : log_msg(LOG_WARNING,
505 : : "Got signal %i. No defined action but to exit.", last_sig);
506 : : }
507 : : }
508 [ + - ]: 24 : else if (opts->packet_ctr_limit > 0
509 [ + - ]: 24 : && opts->packet_ctr >= opts->packet_ctr_limit)
510 : : {
511 : 24 : log_msg(LOG_INFO,
512 : : "Packet count limit (%d) reached. Exiting...",
513 : : opts->packet_ctr_limit);
514 : : }
515 : : else /* got_signal was not set (should be if we are here) */
516 : : {
517 : 0 : log_msg(LOG_WARNING,
518 : : "Capture ended without signal. Exiting...");
519 : : }
520 : 384 : return rv;
521 : : }
522 : :
523 : 371 : static int stop_fwknopd(fko_srv_options_t * const opts)
524 : : {
525 : 371 : int res = 0, is_err = 0;
526 : : pid_t old_pid;
527 : :
528 : 371 : old_pid = get_running_pid(opts);
529 : :
530 [ + - ]: 371 : if(old_pid > 0)
531 : : {
532 : 371 : res = kill(old_pid, SIGTERM);
533 : 371 : is_err = kill(old_pid, 0);
534 : :
535 [ - + ]: 371 : if(res == 0 && is_err != 0)
536 : : {
537 : 0 : fprintf(stdout, "Killed fwknopd (pid=%i)\n", old_pid);
538 : 0 : return EXIT_SUCCESS;
539 : : }
540 : : else
541 : : {
542 : : /* give a bit of time for process shutdown and check again
543 : : */
544 : 371 : sleep(1);
545 : 371 : is_err = kill(old_pid, 0);
546 [ + - ]: 371 : if(is_err != 0)
547 : : {
548 : 371 : fprintf(stdout, "Killed fwknopd (pid=%i) via SIGTERM\n",
549 : : old_pid);
550 : 371 : return EXIT_SUCCESS;
551 : : }
552 : : else
553 : : {
554 : 0 : res = kill(old_pid, SIGKILL);
555 : 0 : is_err = kill(old_pid, 0);
556 [ # # ]: 0 : if(res == 0 && is_err != 0)
557 : : {
558 : 0 : fprintf(stdout,
559 : : "Killed fwknopd (pid=%i) via SIGKILL\n",
560 : : old_pid);
561 : 0 : return EXIT_SUCCESS;
562 : : }
563 : : else
564 : : {
565 : 0 : sleep(1);
566 : 0 : is_err = kill(old_pid, 0);
567 [ # # ]: 0 : if(is_err != 0)
568 : : {
569 : 0 : fprintf(stdout,
570 : : "Killed fwknopd (pid=%i) via SIGKILL\n",
571 : : old_pid);
572 : 0 : return EXIT_SUCCESS;
573 : : }
574 : : else
575 : : {
576 : 0 : perror("Unable to kill fwknop: ");
577 : 0 : return EXIT_FAILURE;
578 : : }
579 : : }
580 : : }
581 : : }
582 : : }
583 : :
584 : 0 : fprintf(stderr, "No running fwknopd detected.\n");
585 : 0 : return EXIT_FAILURE;
586 : : }
587 : :
588 : : /* Ensure the specified directory exists. If not, create it or die.
589 : : */
590 : : static int
591 : 2582 : check_dir_path(const char * const filepath, const char * const fp_desc, const unsigned char use_basename)
592 : : {
593 : : struct stat st;
594 : 2582 : int res = 0;
595 : : char tmp_path[MAX_PATH_LEN];
596 : : char *ndx;
597 : :
598 : : /*
599 : : * FIXME: We shouldn't use a hard-coded dir-separator here.
600 : : */
601 : : /* But first make sure we are using an absolute path.
602 : : */
603 [ + + ]: 2582 : if(*filepath != PATH_SEP)
604 : : {
605 : 1 : log_msg(LOG_ERR,
606 : : "Path '%s' is not absolute.", filepath
607 : : );
608 : 1 : return 0;
609 : : }
610 : :
611 : : /* If this is a file path that we want to use only the basename, strip
612 : : * the trailing filename here.
613 : : */
614 [ - + ][ # # ]: 2581 : if(use_basename && ((ndx = strrchr(filepath, PATH_SEP)) != NULL))
615 : 0 : strlcpy(tmp_path, filepath, (ndx-filepath)+1);
616 : : else
617 : 2581 : strlcpy(tmp_path, filepath, sizeof(tmp_path));
618 : :
619 : : /* At this point, we should make the path is more than just the
620 : : * PATH_SEP. If it is not, silently return.
621 : : */
622 [ + - ]: 2581 : if(strlen(tmp_path) < 2)
623 : : return 1;
624 : :
625 : : /* Make sure we have a valid directory.
626 : : */
627 : 2581 : res = stat(tmp_path, &st);
628 [ + + ]: 2581 : if(res != 0)
629 : : {
630 [ + - ]: 1 : if(errno == ENOENT)
631 : : {
632 : 1 : log_msg(LOG_WARNING,
633 : : "%s directory: %s does not exist. Attempting to create it.",
634 : : fp_desc, tmp_path
635 : : );
636 : :
637 : : /* Directory does not exist, so attempt to create it.
638 : : */
639 : 1 : res = make_dir_path(tmp_path);
640 [ - + ]: 1 : if(res != 0)
641 : : {
642 : 0 : log_msg(LOG_ERR,
643 : : "Unable to create %s directory: %s (error: %i)",
644 : : fp_desc, tmp_path, errno
645 : : );
646 : 0 : return 0;
647 : : }
648 : :
649 : 1 : log_msg(LOG_ERR,
650 : : "Successfully created %s directory: %s", fp_desc, tmp_path
651 : : );
652 : : }
653 : : else
654 : : {
655 : 0 : log_msg(LOG_ERR,
656 : : "Stat of %s returned error %i", tmp_path, errno
657 : : );
658 : 0 : return 0;
659 : : }
660 : : }
661 : : else
662 : : {
663 : : /* It is a file, but is it a directory?
664 : : */
665 [ + + ]: 2580 : if(! S_ISDIR(st.st_mode))
666 : : {
667 : 1 : log_msg(LOG_ERR,
668 : : "Specified %s directory: %s is NOT a directory", fp_desc, tmp_path
669 : : );
670 : 1 : return 0;
671 : : }
672 : : }
673 : : return 1;
674 : : }
675 : :
676 : : static int
677 : 1 : make_dir_path(const char * const run_dir)
678 : : {
679 : : struct stat st;
680 : 1 : int res = 0, len = 0;
681 : : char tmp_path[MAX_PATH_LEN];
682 : : char *ndx;
683 : :
684 : 1 : strlcpy(tmp_path, run_dir, sizeof(tmp_path));
685 : :
686 : 1 : len = strlen(tmp_path);
687 : :
688 : : /* Strip any trailing dir sep char.
689 : : */
690 [ - + ]: 1 : if(tmp_path[len-1] == PATH_SEP)
691 : 1 : tmp_path[len-1] = '\0';
692 : :
693 [ + + ]: 36 : for(ndx = tmp_path+1; *ndx; ndx++)
694 : : {
695 [ + + ]: 35 : if(*ndx == '/')
696 : : {
697 : 5 : *ndx = '\0';
698 : :
699 : : /* Stat this part of the path to see if it is a valid directory.
700 : : * If it does not exist, attempt to create it. If it does, and
701 : : * it is a directory, go on. Otherwise, any other error cause it
702 : : * to bail.
703 : : */
704 [ - + ]: 5 : if(stat(tmp_path, &st) != 0)
705 : : {
706 [ # # ]: 0 : if(errno == ENOENT)
707 : : {
708 : 0 : res = mkdir(tmp_path, S_IRWXU);
709 [ # # ]: 0 : if(res != 0)
710 : : return res;
711 : :
712 : : /* run stat() against the component since we just
713 : : * created it
714 : : */
715 [ # # ]: 0 : if(stat(tmp_path, &st) != 0)
716 : : {
717 : 0 : log_msg(LOG_ERR,
718 : : "Could not create component: %s of %s", tmp_path, run_dir
719 : : );
720 : 0 : return(ENOTDIR);
721 : : }
722 : : }
723 : : }
724 : :
725 [ - + ]: 5 : if(! S_ISDIR(st.st_mode))
726 : : {
727 : 0 : log_msg(LOG_ERR,
728 : : "Component: %s of %s is NOT a directory", tmp_path, run_dir
729 : : );
730 : 0 : return(ENOTDIR);
731 : : }
732 : :
733 : 5 : *ndx = '/';
734 : : }
735 : : }
736 : :
737 : 1 : res = mkdir(tmp_path, S_IRWXU);
738 : :
739 : 1 : return(res);
740 : : }
741 : :
742 : : /* Become a daemon: fork(), start a new session, chdir "/",
743 : : * and close unneeded standard filehandles.
744 : : */
745 : : static void
746 : 1 : daemonize_process(fko_srv_options_t * const opts)
747 : : {
748 : : pid_t pid, old_pid;
749 : :
750 : : /* Reset the our umask
751 : : */
752 : 1 : umask(0);
753 : :
754 [ - + ]: 1 : if ((pid = fork()) < 0)
755 : : {
756 : 0 : perror("Unable to fork: ");
757 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
758 : : }
759 [ + + ]: 2 : else if (pid != 0) /* parent */
760 : : {
761 : 1 : clean_exit(opts, NO_FW_CLEANUP, EXIT_SUCCESS);
762 : : }
763 : :
764 : : /* Child process from here on out */
765 : :
766 : : /* Start a new session
767 : : */
768 : 1 : setsid();
769 : :
770 : : /* Create the PID file (or be blocked by an existing one).
771 : : */
772 : 1 : old_pid = write_pid_file(opts);
773 [ - + ]: 1 : if(old_pid > 0)
774 : : {
775 : 0 : fprintf(stderr,
776 : : "[*] An instance of fwknopd is already running: (PID=%i).\n", old_pid
777 : : );
778 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
779 : : }
780 [ - + ]: 1 : else if(old_pid < 0)
781 : : {
782 : 0 : fprintf(stderr,
783 : : "[*] PID file error. The lock may not be effective.\n");
784 : : }
785 : :
786 : : /* Chdir to the root of the filesystem
787 : : */
788 [ - + ]: 1 : if ((chdir("/")) < 0) {
789 : 0 : perror("Could not chdir() to /: ");
790 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
791 : : }
792 : :
793 : : /* Close un-needed file handles
794 : : */
795 : 1 : close(STDIN_FILENO);
796 : 1 : close(STDOUT_FILENO);
797 : 1 : close(STDERR_FILENO);
798 : :
799 : 1 : return;
800 : : }
801 : :
802 : : static int
803 : 2311 : write_pid_file(fko_srv_options_t *opts)
804 : : {
805 : : pid_t old_pid, my_pid;
806 : : int op_fd, lck_res, num_bytes;
807 : 2311 : char buf[PID_BUFLEN] = {0};
808 : :
809 : : /* Reset errno (just in case)
810 : : */
811 : 2311 : errno = 0;
812 : :
813 : : /* Open the PID file
814 : : */
815 : 2311 : op_fd = open(
816 : 2311 : opts->config[CONF_FWKNOP_PID_FILE], O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR
817 : : );
818 : :
819 [ - + ]: 2311 : if(op_fd == -1)
820 : : {
821 : 0 : perror("Error trying to open PID file: ");
822 : 0 : return -1;
823 : : }
824 : :
825 [ - + ]: 2311 : if(fcntl(op_fd, F_SETFD, FD_CLOEXEC) == -1)
826 : : {
827 : 0 : close(op_fd);
828 : 0 : perror("Unexpected error from fcntl: ");
829 : 0 : return -1;
830 : : }
831 : :
832 : : /* Attempt to lock the PID file. If we get an EWOULDBLOCK
833 : : * error, another instance already has the lock. So we grab
834 : : * the pid from the existing lock file, complain and bail.
835 : : */
836 : 2311 : lck_res = lockf(op_fd, F_TLOCK, 0);
837 [ + + ]: 2311 : if(lck_res == -1)
838 : : {
839 : 366 : close(op_fd);
840 : :
841 [ - + ]: 366 : if(errno != EAGAIN)
842 : : {
843 : 0 : perror("Unexpected error from lockf: ");
844 : 0 : return -1;
845 : : }
846 : :
847 : : /* Look for an existing lock holder. If we get a pid return it.
848 : : */
849 : 366 : old_pid = get_running_pid(opts);
850 [ - + ]: 366 : if(old_pid)
851 : : return old_pid;
852 : :
853 : : /* Otherwise, consider it an error.
854 : : */
855 : 0 : perror("Unable read existing PID file: ");
856 : 0 : return -1;
857 : : }
858 : :
859 : : /* Write our PID to the file
860 : : */
861 : 1945 : my_pid = getpid();
862 : : snprintf(buf, PID_BUFLEN, "%i\n", my_pid);
863 : :
864 : 1945 : log_msg(LOG_DEBUG, "[+] Writing my PID (%i) to the lock file: %s",
865 : : my_pid, opts->config[CONF_FWKNOP_PID_FILE]);
866 : :
867 : 1945 : num_bytes = write(op_fd, buf, strlen(buf));
868 : :
869 [ + + ][ + + ]: 1945 : if(errno || num_bytes != strlen(buf))
870 : 81 : perror("Lock may not be valid. PID file write error: ");
871 : :
872 : : /* Sync/flush regardless...
873 : : */
874 : 1945 : fsync(op_fd);
875 : :
876 : : /* Put the lock file discriptor in out options struct so any
877 : : * child processes we my spawn can close and release it.
878 : : */
879 : 1945 : opts->lock_fd = op_fd;
880 : :
881 : 1945 : return 0;
882 : : }
883 : :
884 : : static pid_t
885 : 2313 : get_running_pid(const fko_srv_options_t *opts)
886 : : {
887 : 2313 : int op_fd, is_err, bytes_read = 0;
888 : 2313 : char buf[PID_BUFLEN] = {0};
889 : 2313 : pid_t rpid = 0;
890 : :
891 : :
892 [ - + ]: 2313 : if(verify_file_perms_ownership(opts->config[CONF_FWKNOP_PID_FILE]) != 1)
893 : : {
894 : 0 : fprintf(stderr, "verify_file_perms_ownership() error\n");
895 : 0 : return(rpid);
896 : : }
897 : :
898 : 4626 : op_fd = open(opts->config[CONF_FWKNOP_PID_FILE], O_RDONLY);
899 : :
900 [ - + ]: 2313 : if(op_fd == -1)
901 : : {
902 [ # # ][ # # ]: 0 : if((opts->foreground != 0) && (opts->verbose != 0))
903 : 0 : perror("Error trying to open PID file: ");
904 : : return(rpid);
905 : : }
906 : :
907 : 2313 : bytes_read = read(op_fd, buf, PID_BUFLEN);
908 [ + + ]: 2313 : if (bytes_read > 0)
909 : : {
910 : 2263 : buf[PID_BUFLEN-1] = '\0';
911 : : /* max pid value is configurable on Linux
912 : : */
913 : 2263 : rpid = (pid_t) strtol_wrapper(buf, 0, (2 << 30),
914 : : NO_EXIT_UPON_ERR, &is_err);
915 [ - + ]: 2263 : if(is_err != FKO_SUCCESS)
916 : 0 : rpid = 0;
917 : : }
918 [ + - ]: 50 : else if (bytes_read < 0)
919 : 50 : perror("Error trying to read() PID file: ");
920 : :
921 : 2313 : close(op_fd);
922 : :
923 : 2313 : return(rpid);
924 : : }
925 : :
926 : : #if HAVE_LIBFIU
927 : : static void
928 : 3708 : enable_fault_injections(fko_srv_options_t * const opts)
929 : : {
930 [ + + ]: 3708 : if(opts->config[CONF_FAULT_INJECTION_TAG] != NULL)
931 : : {
932 : 35 : fiu_init(0);
933 [ - + ]: 35 : if (fiu_enable(opts->config[CONF_FAULT_INJECTION_TAG], 1, NULL, 0) != 0)
934 : : {
935 : 0 : fprintf(stderr, "[*] Could not enable fault injection: %s\n",
936 : : opts->config[CONF_FAULT_INJECTION_TAG]);
937 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
938 : : }
939 : : }
940 : 3708 : return;
941 : : }
942 : : #endif
943 : :
944 : : void
945 : 5499 : clean_exit(fko_srv_options_t *opts, unsigned int fw_cleanup_flag, unsigned int exit_status)
946 : : {
947 : : #if HAVE_LIBFIU
948 [ + + ]: 5499 : if(opts->config[CONF_FAULT_INJECTION_TAG] != NULL)
949 : : {
950 : 35 : fiu_disable(opts->config[CONF_FAULT_INJECTION_TAG]);
951 : : }
952 : : #endif
953 : :
954 [ + + ][ + + ]: 5499 : if(!opts->test && (fw_cleanup_flag == FW_CLEANUP))
955 : 391 : fw_cleanup(opts);
956 : :
957 : : #if USE_FILE_CACHE
958 : 5499 : free_replay_list(opts);
959 : : #endif
960 : :
961 : 5499 : free_logging();
962 : 5499 : free_configs(opts);
963 : 5499 : exit(exit_status);
964 : : }
965 : :
966 : : /***EOF***/
|