ec49d322afffb953ce948d9478243d06a31feb42
[fwknop.git] / server / fwknopd.c
1 /*
2  *****************************************************************************
3  *
4  * File:    fwknopd.c
5  *
6  * Author:  Damien S. Stuart
7  *
8  * Purpose: An implementation of an fwknop server.
9  *
10  * Copyright 2010 Damien Stuart (dstuart@dstuart.org)
11  *
12  *  License (GNU 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 void check_dir_path(const char *path, const char *path_name, const unsigned char use_basename);
46 static int make_dir_path(const char *path);
47 static void daemonize_process(fko_srv_options_t *opts);
48 static int write_pid_file(fko_srv_options_t *opts);
49 static pid_t get_running_pid(const fko_srv_options_t *opts);
50
51 int
52 main(int argc, char **argv)
53 {
54     int                 res, last_sig, rp_cache_count;
55     char               *locale;
56     pid_t               old_pid;
57
58     fko_srv_options_t   opts;
59
60     while(1)
61     {
62         /* Handle command line
63         */
64         config_init(&opts, argc, argv);
65
66         /* Process any options that do their thing and exit.
67         */
68
69         /* Kill the currently running fwknopd?
70         */
71         if(opts.kill == 1)
72         {
73             old_pid = get_running_pid(&opts);
74
75             if(old_pid > 0)
76             {
77                 res = kill(old_pid, SIGTERM);
78                 if(res == 0)
79                 {
80                     fprintf(stdout, "Killed fwknopd (pid=%i)\n", old_pid);
81                     clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS);
82                 }
83                 else
84                 {
85                     perror("Unable to kill fwknop: ");
86                     clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE);
87                 }
88             }
89             else
90             {
91                 fprintf(stderr, "No running fwknopd detected.\n");
92                 clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE);
93             }
94         }
95
96         /* Status of the currently running fwknopd?
97         */
98         if(opts.status == 1)
99         {
100             old_pid = write_pid_file(&opts);
101
102             if(old_pid > 0)
103                 fprintf(stdout, "Detected fwknopd is running (pid=%i).\n", old_pid);
104             else
105                 fprintf(stdout, "No running fwknopd detected.\n");
106
107             clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS);
108         }
109
110         /* Restart the currently running fwknopd?
111         */
112         if(opts.restart == 1 || opts.status == 1)
113         {
114             old_pid = get_running_pid(&opts);
115
116             if(old_pid > 0)
117             {
118                 res = kill(old_pid, SIGHUP);
119                 if(res == 0)
120                 {
121                     fprintf(stdout, "Sent restart signal to fwknopd (pid=%i)\n", old_pid);
122                     clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS);
123                 }
124                 else
125                 {
126                     perror("Unable to send signal to fwknop: ");
127                     clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE);
128                 }
129             }
130             else
131             {
132                 fprintf(stdout, "No running fwknopd detected.\n");
133                 clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE);
134             }
135         }
136
137         /* Initialize logging.
138         */
139         init_logging(&opts);
140
141 #if HAVE_LOCALE_H
142         /* Set the locale if specified.
143         */
144         if(opts.config[CONF_LOCALE] != NULL
145           && strncasecmp(opts.config[CONF_LOCALE], "NONE", 4) != 0)
146         {
147             locale = setlocale(LC_ALL, opts.config[CONF_LOCALE]);
148
149             if(locale == NULL)
150             {
151                 log_msg(LOG_ERR,
152                     "WARNING: Unable to set locale to '%s'.",
153                     opts.config[CONF_LOCALE]
154                 );
155             }
156             else
157             {
158                 if(opts.verbose)
159                     log_msg(LOG_INFO,
160                         "Locale set to '%s'.", opts.config[CONF_LOCALE]
161                     );
162             }
163         }
164 #endif
165
166         /* Make sure we have a valid run dir and path leading to digest file
167          * in case it configured to be somewhere other than the run dir.
168         */
169         check_dir_path((const char *)opts.config[CONF_FWKNOP_RUN_DIR], "Run", 0);
170
171         /* Initialize the firewall rules handler based on the fwknopd.conf
172          * file, but (for iptables firewalls) don't flush any rules or create
173          * any chains yet.  This allows us to dump the current firewall rules
174          * via fw_rules_dump() in --fw-list mode before changing around any rules
175          * of an existing fwknopd process.
176         */
177         fw_config_init(&opts);
178
179         if(opts.fw_list == 1 || opts.fw_list_all == 1)
180         {
181             fw_dump_rules(&opts);
182             clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS);
183         }
184
185         if(opts.fw_flush == 1)
186         {
187             fprintf(stdout, "Deleting any existing firewall rules...\n");
188             clean_exit(&opts, FW_CLEANUP, EXIT_SUCCESS);
189         }
190
191         /* Process the access.conf file.
192         */
193         parse_access_file(&opts);
194
195         /* Show config (including access.conf vars) and exit dump config was
196          * wanted.
197         */
198         if(opts.dump_config == 1)
199         {
200             dump_config(&opts);
201             dump_access_list(&opts);
202             clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS);
203         }
204
205         /* If we are a new process (just being started), proceed with normal
206          * start-up.  Otherwise, we are here as a result of a signal sent to an
207          * existing process and we want to restart.
208         */
209         if(get_running_pid(&opts) != getpid())
210         {
211             /* If foreground mode is not set, the fork off and become a daemon.
212             * Otherwise, attempt to get the pid file lock and go on.
213             */
214             if(opts.foreground == 0)
215             {
216                 daemonize_process(&opts);
217             }
218             else
219             {
220                 old_pid = write_pid_file(&opts);
221                 if(old_pid > 0)
222                 {
223                     fprintf(stderr,
224                         "* An instance of fwknopd is already running: (PID=%i).\n", old_pid
225                     );
226
227                     clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE);
228                 }
229                 else if(old_pid < 0)
230                 {
231                     fprintf(stderr, "* PID file error. The lock may not be effective.\n");
232                 }
233             }
234
235             log_msg(LOG_INFO, "Starting %s", MY_NAME);
236         }
237         else
238         {
239             log_msg(LOG_INFO, "Re-starting %s", MY_NAME);
240         }
241
242         if(opts.verbose > 1 && opts.foreground)
243         {
244             dump_config(&opts);
245             dump_access_list(&opts);
246         }
247
248         /* Initialize the digest cache for replay attack detection (either
249          * with dbm support or with the default simple cache file strategy)
250          * if so configured.
251         */
252         if(strncasecmp(opts.config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0)
253         {
254             rp_cache_count = replay_cache_init(&opts);
255
256             if(rp_cache_count < 0)
257             {
258                 log_msg(LOG_WARNING,
259                     "Error opening digest cache file. Incoming digests will not be remembered."
260                 );
261                 strlcpy(opts.config[CONF_ENABLE_DIGEST_PERSISTENCE], "N", 2);
262             }
263
264             if(opts.verbose)
265                 log_msg(LOG_ERR,
266                     "Using Digest Cache: '%s' (entry count = %i)",
267 #if USE_FILE_CACHE
268                     opts.config[CONF_DIGEST_FILE], rp_cache_count
269 #else
270                     opts.config[CONF_DIGEST_DB_FILE], rp_cache_count
271 #endif
272                 );
273         }
274
275         /* Prepare the firewall - i.e. flush any old rules and (for iptables)
276          * create fwknop chains.
277         */
278         fw_initialize(&opts);
279
280         /* If the TCP server option was set, fire it up here.
281         */
282         if(strncasecmp(opts.config[CONF_ENABLE_TCP_SERVER], "Y", 1) == 0)
283         {
284             if(atoi(opts.config[CONF_TCPSERV_PORT]) <= 0
285               || atoi(opts.config[CONF_TCPSERV_PORT]) > MAX_PORT)
286             {
287                 log_msg(LOG_WARNING,
288                     "WARNING: ENABLE_TCP_SERVER is set, but TCPSERV_PORT is not valid. TCP server not started!"
289                 );
290             }
291             else
292             {
293                 run_tcp_server(&opts);
294             }
295         }
296
297         /* Intiate pcap capture mode...
298         */
299         pcap_capture(&opts);
300
301         if(got_signal) {
302             last_sig   = got_signal;
303             got_signal = 0;
304
305             if(got_sighup)
306             {
307                 log_msg(LOG_WARNING, "Got SIGHUP.  Re-reading configs.");
308                 free_configs(&opts);
309                 kill(opts.tcp_server_pid, SIGTERM);
310                 usleep(1000000);
311                 got_sighup = 0;
312             }
313             else if(got_sigint)
314             {
315                 log_msg(LOG_WARNING, "Got SIGINT.  Exiting...");
316                 got_sigint = 0;
317                 break;
318             }
319             else if(got_sigterm)
320             {
321                 log_msg(LOG_WARNING, "Got SIGTERM.  Exiting...");
322                 got_sigterm = 0;
323                 break;
324             }
325             else
326             {
327                 log_msg(LOG_WARNING,
328                     "Got signal %i. No defined action but to exit.", last_sig);
329                 break;
330             }
331         }
332         else if (opts.packet_ctr_limit > 0
333             && opts.packet_ctr >= opts.packet_ctr_limit)
334         {
335             log_msg(LOG_INFO,
336                 "Packet count limit (%d) reached.  Exiting...",
337                 opts.packet_ctr_limit);
338             break;
339         }
340         else    /* got_signal was not set (should be if we are here) */
341         {
342             log_msg(LOG_WARNING,
343                 "Capture ended without signal.  Exiting...");
344             break;
345         }
346     }
347
348     log_msg(LOG_INFO, "Shutting Down fwknopd.");
349
350     /* Kill the TCP server (if we have one running).
351     */
352     if(opts.tcp_server_pid > 0)
353     {
354         log_msg(LOG_INFO, "Killing the TCP server (pid=%i)",
355             opts.tcp_server_pid);
356
357         kill(opts.tcp_server_pid, SIGTERM);
358
359         /* --DSS XXX: This seems to be necessary if the tcp server
360          *            was restarted by this program.  We need to
361          *            investigate and fix this. For now, this works
362          *            (it is kludgy, but does no harm afaik).
363         */
364         kill(opts.tcp_server_pid, SIGKILL);
365     }
366
367     /* Other cleanup.
368     */
369     fw_cleanup(&opts);
370     free_logging();
371
372 #if USE_FILE_CACHE
373     free_replay_list(&opts);
374 #endif
375
376     free_configs(&opts);
377
378     return(0);
379 }
380
381 /* Ensure the specified directory exists.  If not, create it or die.
382 */
383 static void
384 check_dir_path(const char *filepath, const char *fp_desc, const unsigned char use_basename)
385 {
386     struct stat     st;
387     int             res = 0;
388     char            tmp_path[MAX_PATH_LEN];
389     char            *ndx;
390
391     /*
392      * FIXME:  We shouldn't use a hard-coded dir-separator here.
393     */
394     /* But first make sure we are using an absolute path.
395     */
396     if(*filepath != PATH_SEP)
397     {
398         log_msg(LOG_ERR,
399             "Path '%s' is not absolute.", filepath
400         );
401         exit(EXIT_FAILURE);
402     }
403
404     /* If this is a file path that we want to use only the basename, strip
405      * the trailing filename here.
406     */
407     if(use_basename && ((ndx = strrchr(filepath, PATH_SEP)) != NULL))
408         strlcpy(tmp_path, filepath, (ndx-filepath)+1);
409     else
410         strlcpy(tmp_path, filepath, MAX_PATH_LEN);
411
412     /* At this point, we should make the path is more than just the
413      * PATH_SEP.  If it is not, silently return.
414     */
415     if(strlen(tmp_path) < 2)
416         return;
417
418     /* Make sure we have a valid directory.
419     */
420     res = stat(tmp_path, &st);
421     if(res != 0)
422     {
423         if(errno == ENOENT)
424         {
425             log_msg(LOG_WARNING,
426                 "%s directory: %s does not exist.  Attempting to create it.",
427                 fp_desc, tmp_path
428             );
429
430             /* Directory does not exist, so attempt to create it.
431             */
432             res = make_dir_path(tmp_path);
433             if(res != 0)
434             {
435                 log_msg(LOG_ERR,
436                     "Unable to create %s directory: %s (error: %i)",
437                     fp_desc, tmp_path, errno
438                 );
439                 exit(EXIT_FAILURE);
440             }
441
442             log_msg(LOG_ERR,
443                 "Successfully created %s directory: %s", fp_desc, tmp_path
444             );
445         }
446         else
447         {
448             log_msg(LOG_ERR,
449                 "Stat of %s returned error %i", tmp_path, errno
450             );
451             exit(EXIT_FAILURE);
452         }
453     }
454     else
455     {
456         /* It is a file, but is it a directory?
457         */
458         if(! S_ISDIR(st.st_mode))
459         {
460             log_msg(LOG_ERR,
461                 "Specified %s directory: %s is NOT a directory\n\n", fp_desc, tmp_path
462             );
463             exit(EXIT_FAILURE);
464         }
465     }
466 }
467
468 static int
469 make_dir_path(const char *run_dir)
470 {
471     struct stat     st;
472     int             res = 0, len = 0;
473     char            tmp_path[MAX_PATH_LEN];
474     char            *ndx;
475
476     strlcpy(tmp_path, run_dir, MAX_PATH_LEN);
477
478     len = strlen(tmp_path);
479
480     /* Strip any trailing dir sep char.
481     */
482     if(tmp_path[len-1] == PATH_SEP)
483         tmp_path[len-1] = '\0';
484
485     for(ndx = tmp_path+1; *ndx; ndx++)
486     {
487         if(*ndx == '/')
488         {
489             *ndx = '\0';
490
491             /* Stat this part of the path to see if it is a valid directory.
492              * If it does not exist, attempt to create it. If it does, and
493              * it is a directory, go on.  Otherwise, any other error cause it
494              * to bail.
495             */
496             if(stat(tmp_path, &st) != 0)
497             {
498                 if(errno == ENOENT)
499                     res = mkdir(tmp_path, S_IRWXU);
500
501                 if(res != 0)
502                     return res;
503             }
504
505             if(! S_ISDIR(st.st_mode))
506             {
507                 log_msg(LOG_ERR,
508                     "Component: %s of %s is NOT a directory\n\n", tmp_path, run_dir
509                 );
510                 return(ENOTDIR);
511             }
512
513             *ndx = '/';
514         }
515     }
516
517     res = mkdir(tmp_path, S_IRWXU);
518
519     return(res);
520 }
521
522 /* Become a daemon: fork(), start a new session, chdir "/",
523  * and close unneeded standard filehandles.
524 */
525 static void
526 daemonize_process(fko_srv_options_t *opts)
527 {
528     pid_t pid, old_pid;
529
530     /* Reset the our umask
531     */
532     umask(0);
533
534     if ((pid = fork()) < 0)
535     {
536         perror("Unable to fork: ");
537         exit(EXIT_FAILURE);
538     }
539     else if (pid != 0) /* parent */
540     {
541         exit(EXIT_SUCCESS);
542     }
543
544     /* Child process from here on out */
545
546     /* Start a new session
547     */
548     setsid();
549
550     /* Create the PID file (or be blocked by an existing one).
551     */
552     old_pid = write_pid_file(opts);
553     if(old_pid > 0)
554     {
555         fprintf(stderr,
556             "* An instance of fwknopd is already running: (PID=%i).\n", old_pid
557         );
558
559         exit(EXIT_FAILURE);
560     }
561     else if(old_pid < 0)
562     {
563         fprintf(stderr, "* PID file error. The lock may not be effective.\n");
564     }
565
566     /* Chdir to the root of the filesystem
567     */
568     if ((chdir("/")) < 0) {
569         perror("Could not chdir() to /: ");
570         exit(EXIT_FAILURE);
571     }
572
573     /* Close un-needed file handles
574     */
575     close(STDIN_FILENO);
576     close(STDOUT_FILENO);
577     close(STDERR_FILENO);
578
579     return;
580 }
581
582 static int
583 write_pid_file(fko_srv_options_t *opts)
584 {
585     pid_t   old_pid, my_pid;
586     int     op_fd, lck_res, num_bytes;
587     char    buf[PID_BUFLEN] = {0};
588
589     /* Reset errno (just in case)
590     */
591     errno = 0;
592
593     /* Open the PID file
594     */
595     op_fd = open(
596         opts->config[CONF_FWKNOP_PID_FILE], O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR
597     );
598
599     if(op_fd == -1)
600     {
601         perror("Error trying to open PID file: ");
602         return -1;
603     }
604
605     fcntl(op_fd, F_SETFD, FD_CLOEXEC);
606
607     /* Attempt to lock the PID file.  If we get an EWOULDBLOCK
608      * error, another instance already has the lock. So we grab
609      * the pid from the existing lock file, complain and bail.
610     */
611     lck_res = lockf(op_fd, F_TLOCK, 0);
612     if(lck_res == -1)
613     {
614         if(errno != EAGAIN)
615         {
616             perror("Unexpected error from lockf: ");
617             return -1;
618         }
619
620         close(op_fd);
621
622         /* Look for an existing lock holder. If we get a pid return it.
623         */
624         old_pid = get_running_pid(opts);
625         if(old_pid)
626             return old_pid;
627
628         /* Otherwise, consider it an error.
629         */
630         perror("Unable read existing PID file: ");
631         return -1;
632     }
633
634     /* Write our PID to the file
635     */
636     my_pid = getpid();
637     snprintf(buf, PID_BUFLEN, "%i\n", my_pid);
638
639     if(opts->verbose > 1)
640         log_msg(LOG_INFO, "[+] Writing my PID (%i) to the lock file: %s\n",
641             my_pid, opts->config[CONF_FWKNOP_PID_FILE]);
642
643     num_bytes = write(op_fd, buf, strlen(buf));
644
645     if(errno || num_bytes != strlen(buf))
646         perror("Lock may not be valid. PID file write error: ");
647
648     /* Sync/flush regardless...
649     */
650     fsync(op_fd);
651
652     /* Put the lock file discriptor in out options struct so any
653      * child processes we my spawn can close and release it.
654     */
655     opts->lock_fd = op_fd;
656
657     return 0;
658 }
659
660 static pid_t
661 get_running_pid(const fko_srv_options_t *opts)
662 {
663     int     op_fd;
664     char    buf[PID_BUFLEN] = {0};
665     pid_t   rpid            = 0;
666
667     verify_file_perms_ownership(opts->config[CONF_FWKNOP_PID_FILE]);
668
669     op_fd = open(opts->config[CONF_FWKNOP_PID_FILE], O_RDONLY);
670
671     if(op_fd > 0)
672     {
673         if (read(op_fd, buf, PID_BUFLEN) > 0)
674         {
675             buf[PID_BUFLEN-1] = '\0';
676             rpid = (pid_t)atoi(buf);
677         }
678
679         close(op_fd);
680     }
681
682     return(rpid);
683 }
684
685 void
686 clean_exit(fko_srv_options_t *opts, unsigned int fw_cleanup_flag, unsigned int exit_status)
687 {
688     if(fw_cleanup_flag == FW_CLEANUP)
689         fw_cleanup(opts);
690
691 #if USE_FILE_CACHE
692     free_replay_list(opts);
693 #endif
694
695     free_logging();
696     free_configs(opts);
697     exit(exit_status);
698 }
699
700 /***EOF***/