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