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