3 #############################################################################
7 # URL: http://www.cipherdyne.org/fwknop
9 # Purpose: Installer for fwknop
11 # Credits: (see the CREDITS file)
13 # Copyright (C) 2004-2008 Michael Rash (mbr@cipherdyne.org)
15 # License (GNU Public License):
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 #############################################################################
29 # $Id: install.pl 1409 2009-02-23 05:38:33Z mbr $
39 #========================== config ===========================
40 my $USRBIN_DIR = '/usr/bin';
41 my $USRSBIN_DIR = '/usr/sbin';
43 my $fwknop_conf_file = 'fwknop.conf';
46 my $chkconfigCmd = '/sbin/chkconfig';
47 my $rcupdateCmd = '/sbin/rc-update'; ### Gentoo
48 my $updatercdCmd = '/usr/sbin/update-rc.d'; ### Ubuntu
49 my $makeCmd = '/usr/bin/make';
50 my $perlCmd = '/usr/bin/perl';
51 my $gzipCmd = '/bin/gzip';
52 my $killallCmd = '/usr/bin/killall';
53 my $mknodCmd = '/bin/mknod';
54 my $ifconfigCmd = '/sbin/ifconfig';
55 my $runlevelCmd = '/sbin/runlevel';
56 #======================== end config =========================
58 ### main configuration hash
61 my $client_install = 0;
63 my $cygwin_install = 0;
71 my $deps_dir = 'deps';
72 my $init_dir = '/etc/init.d';
73 my $init_name = 'fwknop';
74 my $force_mod_re = '';
75 my $exclude_mod_re = '';
76 my $force_path_update = 0;
77 my $sniff_interface = '';
78 my $cmdline_force_install = 0;
79 my $skip_module_install = 0;
80 my $install_syslog_fifo = 0;
81 my $single_module_install = '';
82 my $force_defaults = 0;
83 my $cmdline_os_type = '';
85 my $locale = 'C'; ### default LC_ALL env variable
88 ### unless --OS-type is used, install.pl will try to figure out the
89 ### OS where fwknop is being installed (this is usually best).
93 my $OS_DARWIN = 4; ### Mac OS X
98 'cygwin' => $OS_CYGWIN,
99 'darwin' => $OS_DARWIN
113 ### perl module directories
114 my @required_perl_modules = (
115 { 'module' =>'Class::MethodMaker', ### GnuPG::Interface dependency
116 'force-install' => 0,
117 'client-mode-install' => 1,
118 'mod-dir' => 'Class-MethodMaker'
120 { 'module' => 'GnuPG::Interface',
121 'force-install' => 0,
122 'client-mode-install' => 1,
123 'mod-dir' => 'GnuPG-Interface'
125 { 'module' => 'Unix::Syslog',
126 'force-install' => 0,
127 'client-mode-install' => 0,
128 'mod-dir' => 'Unix-Syslog'
130 { 'module' => 'Net::IPv4Addr',
131 'force-install' => 0,
132 'client-mode-install' => 1,
133 'mod-dir' => 'Net-IPv4Addr'
135 { 'module' => 'Net::Pcap',
136 'force-install' => 0,
137 'client-mode-install' => 0,
138 'mod-dir' => 'Net-Pcap'
140 { 'module' => 'Net::RawIP',
141 'force-install' => 0,
142 'client-mode-install' => 1,
143 'mod-dir' => 'Net-RawIP'
145 { 'module' => 'Net::Ping::External',
146 'force-install' => 0,
147 'client-mode-install' => 1,
148 'mod-dir' => 'Net-Ping-External'
150 { 'module' => 'Digest::SHA',
151 'force-install' => 0,
152 'client-mode-install' => 1,
153 'mod-dir' => 'Digest-SHA'
155 { 'module' => 'Crypt::Rijndael',
156 'force-install' => 0,
157 'client-mode-install' => 1,
158 'mod-dir' => 'Crypt-Rijndael'
160 { 'module' => 'Crypt::CBC',
161 'force-install' => 0,
162 'client-mode-install' => 1,
163 'mod-dir' => 'Crypt-CBC'
165 { 'module' => 'Term::ReadKey',
166 'force-install' => 0,
167 'client-mode-install' => 1,
168 'mod-dir' => 'TermReadKey'
170 { 'module' => 'IPTables::Parse',
171 'force-install' => 1,
172 'client-mode-install' => 0,
173 'mod-dir' => 'IPTables-Parse'
175 { 'module' => 'IPTables::ChainMgr',
176 'force-install' => 1,
177 'client-mode-install' => 0,
178 'mod-dir' => 'IPTables-ChainMgr'
186 'killall' => $killallCmd,
187 'mknod' => $mknodCmd,
188 'ifconfig' => $ifconfigCmd,
189 'runlevel' => $runlevelCmd
192 my @cmd_search_paths = qw(
202 my $ACCEPT_YES_DEFAULT = 1;
203 my $ACCEPT_NO_DEFAULT = 2;
204 my $NO_ANS_DEFAULT = 0;
206 ### make Getopts case sensitive
207 Getopt::Long::Configure('no_ignore_case');
209 &usage(1) unless (GetOptions(
210 'Single-mod-install=s' => \$single_module_install, ### install a single module
211 'force-mod-install' => \$cmdline_force_install, ### force install of all modules
212 'Force-mod-regex=s' => \$force_mod_re, ### force specific mod install with regex
213 'Exclude-mod-regex=s' => \$exclude_mod_re, ### exclude a particular perl module
214 'Skip-mod-install' => \$skip_module_install,
215 'OS-type=s' => \$cmdline_os_type,
216 'Cygwin-install' => \$cygwin_install,
217 'BSD-install' => \$bsd_install,
218 'Defaults' => \$force_defaults,
219 'client-only' => \$client_install, # Force client-only installation
220 'path-update' => \$force_path_update,
221 'uninstall' => \$uninstall, # Uninstall fwknop.
222 'syslog-conf=s' => \$syslog_conf, # Specify path to syslog config file.
223 'interface=s' => \$sniff_interface, # Specify interface to sniff from
224 'init-dir=s' => \$init_dir,
225 'init-name=s' => \$init_name,
226 'install-syslog-fifo' => \$install_syslog_fifo,
227 'runlevel=i' => \$runlevel,
228 'Home-dir=s' => \$homedir, # specify home directory manually
229 'LC_ALL=s' => \$locale,
230 'no-LC_ALL' => \$no_locale,
231 'help' => \$print_help # Display help.
233 &usage(0) if $print_help;
235 $force_mod_re = qr|$force_mod_re| if $force_mod_re;
236 $exclude_mod_re = qr|$exclude_mod_re| if $exclude_mod_re;
237 $single_module_install = qr|$single_module_install| if $single_module_install;
239 ### set LC_ALL env variable
240 $ENV{'LC_ALL'} = $locale unless $no_locale;
244 ### import paths from default fwknopd.conf
247 ### see if the deps/ directory exists, and if not then we are installing
248 ### from the -nodeps sources so don't install any perl modules
249 $skip_module_install = 1 unless -d $deps_dir;
251 ### check to see if we are installing as a non-root user
252 &check_non_root_user() unless $client_install;
255 &get_os() unless $os_type;
257 if ($os_type == $OS_LINUX) {
258 print "[+] OS: Linux\n";
259 } elsif ($os_type == $OS_CYGWIN) {
260 print "[+] OS: Cygwin\n";
261 } elsif ($os_type == $OS_DARWIN) {
262 print "[+] OS: Darwin\n";
263 } elsif ($os_type == $OS_BSD) {
264 print "[+] OS: BSD\n";
267 if ($client_install) {
269 ### we are installing as a normal user instead of root, so see
270 ### if it is ok to install within the user's home directory
272 $homedir = $ENV{'HOME'} or die '[*] Could not get home ',
273 "directory, use --Home-dir <directory>";
277 " The fwknop client will be installed at $homedir/bin/fwknop, and a few\n",
278 " perl modules needed by fwknop will be installed in $homedir/lib/fwknop/.\n\n",
280 $config{'FWKNOP_MOD_DIR'} = "$homedir/lib/fwknop";
281 $USRBIN_DIR = "$homedir/bin";
284 if ($os_type == $OS_LINUX) {
286 $distro = &get_linux_distro();
288 if ($distro eq 'redhat' or $distro eq 'fedora') {
289 ### add chkconfig only if we are runing on a redhat distro
290 $cmds{'chkconfig'} = $chkconfigCmd;
291 } elsif ($distro eq 'gentoo') {
292 ### add rc-update if we are running on a gentoo distro
293 $cmds{'rc-update'} = $rcupdateCmd;
294 } elsif ($distro eq 'ubuntu') {
295 ### add update-rc.d if we are running on an ubuntu distro
296 $cmds{'update-rc.d'} = $updatercdCmd;
298 print "[+] Distro: $distro\n";
301 ### make sure the system binaries are where we expect
305 my $hostname = hostname();
307 my $src_dir = getcwd() or die "[*] Could not get current working directory.";
309 if (not $uninstall) {
315 #======================= end main ==========================
318 print "[+] Installing fwknop on $hostname\n";
321 die "[*] $homedir does not exist" unless -d $homedir;
325 unless ($client_install) {
326 if (&ask_to_stop_fwknop()) {
330 for my $dir qw| /usr/lib /var/run /var/log /var/lib | {
332 mkdir $dir or die "[*] Could not mkdir $dir: $!";
335 unless (-d $USRSBIN_DIR) {
336 mkdir $USRSBIN_DIR or die "[*] Could not mkdir $USRSBIN_DIR: $!";
338 for my $dir qw/FWKNOP_DIR FWKNOP_RUN_DIR
340 unless (-d $config{$dir}) {
341 mkdir $config{$dir}, 0500 or
342 die "[*] Could not mkdir $config{$dir}: $!";
346 unless (-d $USRBIN_DIR) {
347 print "[+] Creating: $USRBIN_DIR\n";
348 mkdir $USRBIN_DIR or die "[*] Could not mkdir $USRBIN_DIR: $!";
352 unless ($client_install) {
353 unless (-d $config{'FWKNOP_CONF_DIR'}) {
354 ### Note that root will only be able to view files in
355 ### /etc/fwknop since fwknop only needs to view fwknop.conf
356 ### when being run as a daemon.
357 print "[+] Creating config directory: ",
358 "$config{'FWKNOP_CONF_DIR'}\n";
359 mkdir $config{'FWKNOP_CONF_DIR'}, 0500
360 or die "[*] Could not mkdir $config{'FWKNOP_CONF_DIR'}: $!";
363 ### archive directory for previously installed config files
364 unless (-d "$config{'FWKNOP_CONF_DIR'}/archive") {
365 print "[+] Creating config{'FWKNOP_CONF_DIR'}/archive ",
366 "directory: $config{'FWKNOP_CONF_DIR'}/archive\n";
367 mkdir "$config{'FWKNOP_CONF_DIR'}/archive", 0500 or die
368 "[*] Could not mkdir $config{'FWKNOP_CONF_DIR'}/archive: $!";
372 print "[+] Several perl modules needed by fwknop will be installed in\n",
373 " $config{'FWKNOP_MOD_DIR'}. Installing them here will keep the ",
374 "system perl\n library tree clean.\n";
376 ### make our library directory (for perl modules)
377 if ($client_install) {
378 unless (-d "$homedir/lib") {
379 print "[+] Creating directory $homedir/lib\n";
380 mkdir "$homedir/lib", 0755
381 or die "[*] Could not mkdir $homedir/lib: $!";
384 unless (-d $config{'FWKNOP_MOD_DIR'}) {
385 print "[+] Creating directory $config{'FWKNOP_MOD_DIR'}\n";
386 mkdir $config{'FWKNOP_MOD_DIR'}, 0755
387 or die "[*] Could not mkdir $config{'FWKNOP_MOD_DIR'}: $!";
390 ### install perl modules
391 unless ($skip_module_install) {
392 for my $mod_href (@required_perl_modules) {
393 &install_perl_module($mod_href);
396 chdir $src_dir or die "[*] Could not chdir $src_dir: $!";
398 if ($single_module_install) {
399 print " Finished module installation, exiting.\n";
403 unless ($client_install) {
405 ### install man pages
406 &install_manpage('knopmd.8');
407 &install_manpage('knopwatchd.8');
408 &install_manpage('fwknop.8');
409 &install_manpage('fwknopd.8');
411 if (-e "$config{'FWKNOP_CONF_DIR'}/fwknop.conf") {
412 $preserve_rv = &query_preserve_config();
415 print "[+] Compiling knopmd and knopwatchd daemons:\n";
417 ### remove any previously compiled knopmd
418 unlink 'knopmd' if -e 'knopmd';
420 ### remove any previously compiled knopwatchd
421 unlink 'knopwatchd' if -e 'knopwatchd';
423 ### compile the C fwknop daemons
424 system $cmds{'make'};
426 unless (-e 'knopmd' and -e 'knopwatchd') {
427 die "[*] Compilation failed.";
430 ### install fwknop server-side daemons/programs
431 for my $daemon qw(fwknopd knopmd knopwatchd knoptm fwknop_serv) {
432 if ($daemon eq 'fwknopd' or $daemon eq 'knoptm') {
433 unless (((system "$cmds{'perl'} -c $daemon")>>8) == 0) {
434 die "[*] $daemon does not compile with \"perl -c\". ",
435 "Download the latest sources ",
436 "from:\n\nhttp://www.cipherdyne.org/\n";
439 print "[+] Copying $daemon -> $USRSBIN_DIR\n";
440 copy $daemon, $USRSBIN_DIR or
441 die "[*] Could not cp $daemon to $USRSBIN_DIR: $!";
442 chmod 0500, "$USRSBIN_DIR/$daemon" or
443 die "[*] Could not chmod 500 $USRSBIN_DIR/$daemon: $!";
447 print "[+] Copying fwknop -> $USRBIN_DIR\n";
448 copy 'fwknop', $USRBIN_DIR or
449 die "[*] Could not cp fwknop to $USRBIN_DIR: $!";
451 if ($client_install) {
452 open F, "< $USRBIN_DIR/fwknop" or die "[*] Could not open ",
453 "$USRBIN_DIR/fwknop: $!";
456 open P, "> $USRBIN_DIR/fwknop.tmp" or die "[*] Could not open ",
457 "$USRBIN_DIR/fwknop.tmp: $!";
458 for my $line (@lines) {
459 ### change the lib dir to new homedir path
460 if ($line =~ m|^\s*use\s+lib\s+\'/usr/lib/fwknop\';|) {
461 print P "use lib '", $config{'FWKNOP_MOD_DIR'}, "';\n";
462 } elsif ($line =~ m|^\s*my\s+\$lib_dir\s+=\s+\'/usr/lib/fwknop\';|) {
463 print P 'my $lib_dir = ' . "'"
464 . $config{'FWKNOP_MOD_DIR'} . "';\n";
470 move "$USRBIN_DIR/fwknop.tmp", "$USRBIN_DIR/fwknop" or die "[*] Could ",
471 "not move $USRBIN_DIR/fwknop.tmp -> $USRBIN_DIR/fwknop: $!";
472 chmod 0700, "$USRBIN_DIR/fwknop" or
473 die "[*] Could not chmod 755 $USRBIN_DIR/fwknop: $!";
475 chmod 0755, "$USRBIN_DIR/fwknop" or
476 die "[*] Could not chmod 755 $USRBIN_DIR/fwknop: $!";
479 unless (((system "$cmds{'perl'} -c $USRBIN_DIR/fwknop")>>8) == 0) {
480 die "[*] $USRBIN_DIR/fwknop does not compile with \"perl -c\". ",
481 "Download the latest sources ",
482 "from:\n\nhttp://www.cipherdyne.org/\n";
486 if ($client_install) {
488 "\n[+] fwknop has been installed at $USRBIN_DIR/fwknop. Since this is a\n",
489 " client-only install, the man pages could not be installed. For more\n",
490 " information about how to use fwknop, execute \"$USRBIN_DIR/fwknop -h\" or\n",
492 " http://www.cipherdyne.org/fwknop/docs/manpages/index.html\n\n";
496 ### install config and access files
497 for my $file qw(fwknop.conf access.conf pf.os) {
498 if (-e "$config{'FWKNOP_CONF_DIR'}/$file") {
499 &archive("$config{'FWKNOP_CONF_DIR'}/$file");
501 if ($file eq 'access.conf') {
502 ### access.conf can have missing fields (i.e.
503 ### REQUIRE_OS_REGEX and REQUIRE_USERNAME),
504 ### and also it can have multiple sequences
505 ### defined. Hence we just use the old one.
506 print "[+] Using original access.conf\n";
507 } elsif ($file ne 'pf.os') {
508 &preserve_config($file);
511 print "[+] Copying $file -> $config{'FWKNOP_CONF_DIR'}\n";
512 copy $file, $config{'FWKNOP_CONF_DIR'} or
513 die "[*] Could not cp $file to $config{'FWKNOP_CONF_DIR'}";
516 print "[+] Copying $file -> $config{'FWKNOP_CONF_DIR'}\n";
517 copy $file, $config{'FWKNOP_CONF_DIR'} or
518 die "[*] Could not cp $file to $config{'FWKNOP_CONF_DIR'}";
521 if ($force_path_update or not $preserve_rv) {
522 &update_command_paths("$config{'FWKNOP_CONF_DIR'}/$file")
523 if ($file eq 'fwknop.conf');
526 if ($file eq 'fwknop.conf') {
527 &set_hostname("$config{'FWKNOP_CONF_DIR'}/$file");
529 chmod 0600, "$config{'FWKNOP_CONF_DIR'}/$file" or die
530 "[*] Could not chmod(600, $config{'FWKNOP_CONF_DIR'}/$file: $!";
531 chown 0, 0, "$config{'FWKNOP_CONF_DIR'}/$file" or die
532 "[*] Could not chown 0,0, $config{'FWKNOP_CONF_DIR'}/$file: $!";
535 ### get data acquisition method (e.g. syslogd, sysylog-ng, ulogd
537 $data_method = &query_data_method();
539 if ($data_method =~ /syslog/) {
541 &syslog_reconfig() if $install_syslog_fifo;
543 } elsif ($data_method =~ /pcap/i or $data_method =~ /ulog/i) {
545 if ($os_type == $OS_BSD or $os_type == $OS_DARWIN) {
546 ### update to use the ipfw firewall on *BSD systems
547 &put_string('FIREWALL_TYPE', 'ipfw',
548 "$config{'FWKNOP_CONF_DIR'}/fwknop.conf");
551 ### we are using a pcap method
552 if ($data_method eq 'ulogd' or $data_method eq 'file_pcap') {
554 "[+] By default, fwknop uses the file /var/log/sniff.pcap in order to\n",
555 " acquire packet data logged via a sniffer (or ulogd) to a pcap file,\n",
556 " but this path may be changed by altering the PCAP_PKT_FILE keyword\n",
557 " in $config{'FWKNOP_CONF_DIR'}/fwknop.conf.\n\n";
559 if ($data_method eq 'file_pcap') {
560 &put_string('AUTH_MODE', 'FILE_PCAP',
561 "$config{'FWKNOP_CONF_DIR'}/fwknop.conf");
563 &put_string('AUTH_MODE', 'ULOG_PCAP',
564 "$config{'FWKNOP_CONF_DIR'}/fwknop.conf");
567 unless ($sniff_interface) {
568 $sniff_interface = &get_pcap_intf();
570 if ($sniff_interface) {
571 &put_string('PCAP_INTF', $sniff_interface,
572 "$config{'FWKNOP_CONF_DIR'}/fwknop.conf");
574 print "[-] Could not get sniffing interface, edit the PCAP_INTF var in\n",
575 " config{'FWKNOP_CONF_DIR'}/fwknop.conf\n";
579 ### it is a client-only install, so don't reconfigure syslog
583 unless ($preserve_rv) {
584 my $email_str = &query_email();
586 for my $file qw(fwknop.conf) {
587 &put_string('EMAIL_ADDRESSES', $email_str,
588 "$config{'FWKNOP_CONF_DIR'}/$file");
594 if ($client_install) {
595 print "\n[+] fwknop has been installed!\n\n";
600 &enable_fwknop_at_boot($distro);
603 print "\n[+] fwknop has been installed!";
605 if ($os_type == $OS_LINUX) {
606 print " To start in server mode, run\n\n",
607 " \"$init_dir/$init_name start\"\n\n";
612 " You may want to consider running the fwknop test suite in the test/\n",
613 " directory to ensure that fwknop will function correctly on your system.\n\n",
615 " Note: You will need to edit $config{'FWKNOP_CONF_DIR'}/access.conf for fwknop to\n",
616 " function properly in server mode. More information can be found in\n",
617 " the fwknopd(8) manpage.\n\n";
618 if ($os_type == $OS_BSD or $os_type == $OS_DARWIN) {
620 " You may need to update your /etc/syslog.conf file to log local info\n",
621 " messages to a file in the /var/log/ directory in order to see syslog\n",
622 " messages from the fwknop daemons.\n\n";
629 sub syslog_reconfig() {
631 ### create the named pipe
632 unless (-e $config{'KNOPMD_FIFO'} and -p $config{'KNOPMD_FIFO'}) {
633 unlink $config{'KNOPMD_FIFO'} if -e $config{'KNOPMD_FIFO'};
634 print "[+] Creating named pipe $config{'KNOPMD_FIFO'}\n";
635 my $created_pipe = 1;
636 unless (((system "$cmds{'mknod'} -m 600 $config{'KNOPMD_FIFO'} p")>>8)
640 unless (-e $config{'KNOPMD_FIFO'} and -p $config{'KNOPMD_FIFO'}) {
643 unless ($created_pipe) {
645 "[*] Could not create the named pipe \"$config{'KNOPMD_FIFO'}\"!\n",
646 "[*] fwknop requires this file to exist! Aborting install.\n";
650 unless (-e "$config{'FW_DATA_FILE'}") {
651 print "[+] Creating $config{'FW_DATA_FILE'} file\n";
652 open F, "> $config{'FW_DATA_FILE'}" or die "[*] Could not open ",
653 "$config{'FW_DATA_FILE'}: $!";
655 &perms_ownership("$config{'FW_DATA_FILE'}", 0600);
658 ### we are acquiring data via syslog
659 &put_string('AUTH_MODE', 'KNOCK',
660 "$config{'FWKNOP_CONF_DIR'}/fwknop.conf");
662 if ($os_type == $OS_BSD or $os_type == $OS_DARWIN) {
663 ### update to use the ipfw firewall on *BSD systems
664 &put_string('FIREWALL_TYPE', 'ipfw',
665 "$config{'FWKNOP_CONF_DIR'}/fwknop.conf");
668 &put_string('SYSLOG_DAEMON', $data_method,
669 "$config{'FWKNOP_CONF_DIR'}/fwknop.conf");
671 my $restarted_syslog = 0;
672 if ($data_method eq 'syslogd') {
673 if (-e $syslog_conf) {
674 &append_fifo_syslog($syslog_conf);
675 if (((system "$cmds{'killall'} -HUP syslogd " .
676 "2> /dev/null")>>8) == 0) {
677 print "[+] HUP signal sent to syslogd.\n";
678 $restarted_syslog = 1;
681 } elsif ($data_method eq 'syslog-ng') {
682 if (-e $syslog_conf) {
683 &append_fifo_syslog_ng($syslog_conf);
684 if (((system "$cmds{'killall'} -HUP syslog-ng " .
685 "2> /dev/null")>>8) == 0) {
686 print "[+] HUP signal sent to syslog-ng.\n";
687 $restarted_syslog = 1;
692 unless ($restarted_syslog) {
693 print "[-] Could not restart any syslog daemons.\n";
700 print "[+] Uninstalling fwknop...\n";
702 ### stop any running fwknop daemons.
705 ### get the init directory
708 for my $daemon qw(fwknopd knopmd knopwatchd knoptm fwknop_serv) {
709 unlink "$USRSBIN_DIR/$daemon" if -e "$USRSBIN_DIR/$daemon";
711 unlink "$USRBIN_DIR/fwknop" if -e "$USRBIN_DIR/fwknop";
712 unlink "$init_dir/$init_name" if -e "$init_dir/$init_name";
713 rmtree $config{'FWKNOP_CONF_DIR'}, 1, 1 if -d $config{'FWKNOP_CONF_DIR'};
714 rmtree $config{'FWKNOP_LIB_DIR'}, 1, 1 if -d $config{'FWKNOP_LIB_DIR'};
715 rmtree $config{'FWKNOP_MOD_DIR'}, 1, 1 if -d $config{'FWKNOP_MOD_DIR'};
721 return if $client_install;
723 if ($os_type == $OS_DARWIN) {
725 ### Mac OS X init directory for user programs
726 $init_dir = '/Library/StartupItems';
727 die "[*] The $init_dir directory does not exist."
732 ### accommodates Linux and BSD systems
733 unless (-d $init_dir) {
734 if (-d '/etc/rc.d/init.d') {
735 $init_dir = '/etc/rc.d/init.d';
736 } elsif (-d '/etc/rc.d') {
737 $init_dir = '/etc/rc.d';
738 } elsif (-d '/etc/init.d') {
739 $init_dir = '/etc/init.d';
741 die "[*] Cannot find the init script directory, use ",
742 "--init-dir <path>\n";
748 ### check paths to commands and attempt to correct if any are wrong.
749 sub check_commands() {
751 return if $client_install;
753 if ($os_type == $OS_LINUX or $os_type == $OS_CYGWIN) {
754 $exclude_cmds{'ipfw'} = '';
756 ### we are installing on BSD or Mac OS X
757 $exclude_cmds{'iptables'} = '';
759 ### the runlevel does not apply here
760 $exclude_cmds{'runlevel'} = '';
763 CMD: for my $cmd (keys %cmds) {
764 unless (-x $cmds{$cmd}) {
766 PATH: for my $dir (@cmd_search_paths) {
767 if (-x "${dir}/${cmd}") {
768 $cmds{$cmd} = "${dir}/${cmd}";
774 next CMD if defined $exclude_cmds{$cmd};
775 if ($cmd eq 'runlevel') {
779 die "[*] Could not find the $cmd command, ",
780 "use --runlevel <N>";
783 die "[*] Could not find $cmd anywhere. ",
784 "Please edit the config section to include the path to ",
788 unless (-x $cmds{$cmd}) {
789 die "[*] $cmd is located at ",
790 "$cmds{$cmd} but is not executable by uid: $<\n"
791 unless $client_install;
799 return unless -e $file;
800 my $curr_pwd = cwd();
801 chdir "$config{'FWKNOP_CONF_DIR'}/archive" or die $!;
802 my ($filename) = ($file =~ m|.*/(.*)|);
803 my $base = "${filename}.old";
804 for (my $i = 5; $i > 1; $i--) { ### keep five copies of old config files
806 unlink "${base}${i}.gz" if -e "${base}${i}.gz";
807 move "${base}${j}.gz", "${base}${i}.gz" if -e "${base}${j}.gz";
809 print "[+] Archiving $file -> ${base}1\n";
810 unlink "${base}1.gz" if -e "${base}1.gz";
811 copy $file, "${base}1"; ### move $file into the archive directory
812 system "$cmds{'gzip'} ${base}1";
813 chdir $curr_pwd or die $!;
817 sub preserve_config() {
820 my %preserved_lines = ();
822 open C, "< $file" or die "[*] Could not open $file: $!";
826 open CO, "< $config{'FWKNOP_CONF_DIR'}/$file" or die "[*] Could not open ",
827 "$config{'FWKNOP_CONF_DIR'}}/$file: $!";
828 my @orig_lines = <CO>;
831 print "[+] Preserving existing config: $config{'FWKNOP_CONF_DIR'}/$file\n";
832 ### write to a tmp file and then move so any running fwknop daemon will
833 ### re-import a full config file if a HUP signal is received during
835 open CONF, "> $config{'FWKNOP_CONF_DIR'}/${file}.new" or die "[*] Could not open ",
836 "$config{'FWKNOP_CONF_DIR'}/${file}.new: $!";
837 for my $new_line (@new_lines) {
838 if ($new_line =~ /^\s*#/) {
839 print CONF $new_line; ### take comments from new file.
840 } elsif ($new_line =~ /^\s*(\S+)/) {
844 for my $orig_line (@orig_lines) {
845 if ($orig_line =~ /^\s*$var\s/) {
846 next if defined $preserved_lines{$ctr};
847 $preserved_lines{$ctr} = '';
848 print CONF $orig_line;
855 print CONF $new_line;
858 print CONF $new_line;
862 move "$config{'FWKNOP_CONF_DIR'}/${file}.new",
863 "$config{'FWKNOP_CONF_DIR'}/$file" or die "[*] ",
864 "Could not move $config{'FWKNOP_CONF_DIR'}/${file}.new -> ",
865 "$config{'FWKNOP_CONF_DIR'}/$file: $!";
869 sub append_fifo_syslog() {
870 print "[+] Modifying /etc/syslog.conf to write kern.info messages to\n",
871 " $config{'KNOPMD_FIFO'}\n";
872 unless (-e '/etc/syslog.conf.orig') {
873 copy '/etc/syslog.conf', '/etc/syslog.conf.orig';
875 &archive('/etc/syslog.conf');
876 open RS, '< /etc/syslog.conf' or
877 die "[*] Unable to open /etc/syslog.conf: $!\n";
880 open SYSLOG, '> /etc/syslog.conf' or
881 die "[*] Unable to open /etc/syslog.conf: $!\n";
882 for my $line (@slines) {
883 unless ($line =~ /fwknopfifo/) {
887 ### reinstate kernel logging to our named pipe
888 print SYSLOG '### Send kern.info messages to fwknopfifo for ',
889 "analysis by knopmd\n",
890 "kern.info\t\t|$config{'KNOPMD_FIFO'}\n";
895 sub append_fifo_syslog_ng() {
896 print "[+] Modifying /etc/syslog-ng/syslog-ng.conf to write kern.info ",
898 print " $config{'KNOPMD_FIFO'}\n";
899 unless (-e '/etc/syslog-ng/syslog-ng.conf.orig') {
900 copy '/etc/syslog-ng/syslog-ng.conf',
901 '/etc/syslog-ng/syslog-ng.conf.orig';
903 &archive('/etc/syslog-ng/syslog-ng.conf');
904 open RS, '< /etc/syslog-ng/syslog-ng.conf' or
905 die "[*] Unable to open /etc/syslog-ng/syslog-ng.conf: $!\n";
910 for my $line (@slines) {
911 $found_fifo = 1 if ($line =~ /fwknopfifo/);
914 unless ($found_fifo) {
915 open SYSLOGNG, '>> /etc/syslog-ng/syslog-ng.conf' or
916 die "[*] Unable to open /etc/syslog-ng/syslog-ng.conf: $!\n";
918 "destination fwknoppipe { pipe(\"/var/lib/fwknop/fwknopfifo\"); };\n",
919 "filter f_kerninfo { facility(kern) and level(info); };\n",
920 "log { source(src); filter(f_kerninfo); destination(fwknoppipe); };\n";
926 sub check_non_root_user() {
928 unless ($< == 0 && $> == 0) {
931 "[+] It looks like you are installing fwknop as a non-root user. This will\n",
932 " result in fwknop being installed in your local home directory.\n",
933 " If you want fwknop to act as a server (i.e. monitor the network for\n",
934 " Single Packet Authorization activity and modify firewall access or\n",
935 " execute commands accordingly), you will need to install as root.\n\n";
944 ### This function implements a set of simple heuristics to determine
945 ### the OS type. Note that the user can always just set the OS from
946 ### the command line with --OS-type
948 ### get OS output from uname
949 open UNAME, 'uname |' or die "[*] Could not execute 'uname', use ",
956 "[+] It looks like you are installing fwknop on a Mac OS X system.\n",
957 " Installation of iptables perl modules will be skipped.\n";
958 $os_type = $OS_DARWIN;
960 } elsif (/[a-z]BSD/) {
963 "[+] It looks like you are installing fwknop on a *BSD system. Installation\n",
964 " of iptables perl modules will be skipped.\n";
972 ### 'uname -o' does not work on Mac OS X or FreeBSD, so we had to check
975 open UNAME, 'uname -o |' or die "[*] Could not execute 'uname -o', ",
979 if (/Cygwin/ or /Gygwin/) {
982 "[+] It looks like you are installing fwknop in a Cygwin environment, so the\n",
983 " fwknop client will be installed (the fwknopd server does not yet\n",
984 " function with a Windows-based firewall).\n\n";
987 $os_type = $OS_CYGWIN;
995 $os_type = $OS_LINUX unless $os_type;
997 unless ($client_install) {
999 my $have_iptables = 0;
1000 $have_iptables = 1 if (-e '/usr/sbin/iptables' or -e '/sbin/iptables');
1002 if ($os_type == $OS_LINUX) {
1003 unless ($have_iptables) {
1004 die "[*] iptables does not seem to be installed, ",
1007 } elsif ($os_type == $OS_BSD or $os_type == $OS_DARWIN) {
1008 if ($have_iptables) {
1009 die "[*] iptables exists on what looks like a non-Linux ",
1010 "system, use --OS-type.";
1017 sub get_linux_distro() {
1018 return 'gentoo' if -e '/etc/gentoo-release';
1019 if (-e '/etc/issue') {
1020 ### Red Hat Linux release 6.2 (Zoot)
1021 open ISSUE, '< /etc/issue' or
1022 die "[*] Could not open /etc/issue: $!";
1023 my @lines = <ISSUE>;
1025 for my $line (@lines) {
1027 return 'redhat' if $line =~ /red\s*hat/i;
1028 return 'fedora' if $line =~ /fedora/i;
1029 return 'ubuntu' if $line =~ /ubuntu/i;
1035 sub install_manpage() {
1036 my $manpage = shift;
1038 ### default location to put man pages, but check with
1040 my $mpath = '/usr/share/man/man8';
1041 if (-e '/etc/man.config') {
1042 ### prefer to install $manpage in /usr/local/man/man8 if
1043 ### this directory is configured in /etc/man.config
1044 open M, '< /etc/man.config' or
1045 die "[*] Could not open /etc/man.config: $!";
1048 ### prefer the path "/usr/share/man"
1050 for my $line (@lines) {
1052 if ($line =~ m|^MANPATH\s+/usr/share/man|) {
1057 ### try to find "/usr/local/man" if we didn't find /usr/share/man
1059 for my $line (@lines) {
1061 if ($line =~ m|^MANPATH\s+/usr/local/man|) {
1062 $mpath = '/usr/local/man/man8';
1068 ### if we still have not found one of the above man paths,
1069 ### just select the first one out of /etc/man.config
1071 for my $line (@lines) {
1073 if ($line =~ m|^MANPATH\s+(\S+)|) {
1080 mkdir $mpath, 0755 unless -d $mpath;
1081 my $mfile = "${mpath}/${manpage}";
1082 print "[+] Installing $manpage man page at $mfile\n";
1083 copy $manpage, $mfile or die "[*] Could not copy $manpage to ",
1085 &perms_ownership($mfile, 0644);
1086 print "[+] Compressing manpage $mfile\n";
1087 ### remove the old one so gzip doesn't prompt us
1088 unlink "${mfile}.gz" if -e "${mfile}.gz";
1089 system "$cmds{'gzip'} $mfile";
1093 sub enable_fwknop_at_boot() {
1096 if (&query_yes_no("[+] Enable fwknop at boot time ([y]/n)? ",
1097 $ACCEPT_YES_DEFAULT)) {
1099 if ($os_type == $OS_LINUX) {
1101 } elsif ($os_type == $OS_DARWIN) {
1102 my $dir = "$init_dir/Fwknop";
1104 print "[+] mkdir $dir\n";
1105 mkdir $dir, 0755 or die "[*] Could not mkdir $dir";
1107 copy "init-scripts/OS_X/Fwknop", $dir or die "[*] Could not ",
1108 "copy init-scripts/OS_X/Fwknop -> $dir";
1109 copy "init-scripts/OS_X/StartupParameters.plist", $dir or die "[*] ",
1110 "Could not copy init-scripts/OS_X/StartupParameters.plist -> $dir";
1112 } elsif ($os_type == $OS_BSD) {
1113 print "[+] Copying init-scripts/fwknop-init.freebsd ",
1114 "-> ${init_dir}/$init_name\n";
1115 copy "init-scripts/fwknop-init.freebsd", "${init_dir}/$init_name"
1116 or die "[*] Could not cp fwknop init script to $init_dir: $!";
1117 &perms_ownership("${init_dir}/$init_name", 0744);
1120 if ($os_type == $OS_LINUX) {
1124 if ($distro eq 'redhat') {
1125 $init_file = 'fwknop-init.redhat-chkconfig-enable';
1126 } elsif ($distro eq 'fedora') {
1127 $init_file = 'fwknop-init.fedora';
1128 } elsif ($distro eq 'gentoo') {
1129 $init_file = 'fwknop-init.gentoo';
1131 $init_file = 'fwknop-init.generic';
1134 copy "init-scripts/$init_file", "${init_dir}/$init_name"
1135 or die "[*] Could not cp fwknop init script to $init_dir: $!";
1136 &perms_ownership("${init_dir}/$init_name", 0744);
1138 if ($distro eq 'redhat' or $distro eq 'fedora') {
1139 system "$cmds{'chkconfig'} --add $init_name";
1140 } elsif ($distro eq 'gentoo') {
1141 system "$cmds{'rc-update'} add $init_name default";
1142 } elsif ($distro eq 'ubuntu') {
1143 system "$cmds{'update-rc.d'} $init_name defaults 99";
1146 ### get the current run level
1150 ### the link already exists, so don't re-create it
1151 if (-d '/etc/rc.d' and -d "/etc/rc.d/rc${runlevel}.d") {
1152 unless (-e "/etc/rc.d/rc${runlevel}.d/S99$init_name") {
1153 symlink "$init_dir/$init_name",
1154 "/etc/rc.d/rc${runlevel}.d/S99$init_name";
1157 print "[-] The /etc/rc.d/rc${runlevel}.d directory does ",
1158 "exist, not sure how to enable fwknop at boot time.";
1167 sub install_perl_module() {
1170 chdir $src_dir or die "[*] Could not chdir $src_dir: $!";
1171 chdir $deps_dir or die "[*] Could not chdir($deps_dir): $!";
1173 for my $key qw/module force-install client-mode-install mod-dir/ {
1174 die "[*] Missing $key key in required_perl_modules hash."
1175 unless defined $mod_hr->{$key};
1177 my $mod_name = $mod_hr->{'module'};
1179 if ($client_install) {
1180 return unless $mod_hr->{'client-mode-install'};
1183 if ($os_type != $OS_LINUX) {
1184 return if $mod_name eq 'IPTables::Parse'
1185 or $mod_name eq 'IPTables::ChainMgr';
1188 if ($exclude_mod_re and $exclude_mod_re =~ /$mod_name/) {
1189 print "[+] Excluding installation of $mod_name module.\n";
1193 if ($single_module_install and $mod_name !~ /$single_module_install/) {
1194 print "[+] Excluding installation of $mod_name module.\n";
1198 my $version = '(NA)';
1200 my $mod_dir = $mod_hr->{'mod-dir'};
1202 if (-e "$mod_dir/VERSION") {
1203 open F, "< $mod_dir/VERSION" or
1204 die "[*] Could not open $mod_dir/VERSION: $!";
1209 print "[-] Warning: VERSION file does not exist in $mod_dir\n";
1212 my $install_module = 0;
1214 if ($mod_hr->{'force-install'}
1215 or $cmdline_force_install) {
1216 ### install regardless of whether the module may already be
1217 ### installed (this module may be a CPAN module that has been
1218 ### modified specifically for this project, or is a dedicated
1219 ### module for this project).
1220 $install_module = 1;
1221 } elsif ($force_mod_re and $force_mod_re =~ /$mod_name/) {
1222 print "[+] Forcing installation of $mod_name module.\n";
1223 $install_module = 1;
1225 if (has_perl_module($mod_name)) {
1226 print "[+] Module $mod_name is already installed in the ",
1227 "system perl tree, skipping.\n";
1229 ### install the module in the /usr/lib/fwknop directory because
1230 ### it is not already installed.
1231 $install_module = 1;
1235 if ($install_module) {
1236 unless (-d $config{'FWKNOP_MOD_DIR'}) {
1237 print "[+] Creating $config{'FWKNOP_MOD_DIR'}\n";
1238 mkdir $config{'FWKNOP_MOD_DIR'}, 0755
1239 or die "[*] Could not mkdir $config{'FWKNOP_MOD_DIR'}: $!";
1241 print "[+] Installing $mod_name $version perl module in ",
1242 "$config{'FWKNOP_MOD_DIR'}/\n";
1243 chdir $mod_dir or die "[*] Could not chdir to ",
1245 unless (-e 'Makefile.PL') {
1246 die "[*] Your $mod_name source directory appears to be incomplete!\n",
1247 " Download the latest sources from ",
1248 "http://www.cipherdyne.org\n";
1250 system "$cmds{'make'} clean" if -e 'Makefile';
1251 system "$cmds{'perl'} Makefile.PL PREFIX=$config{'FWKNOP_MOD_DIR'} " .
1252 "LIB=$config{'FWKNOP_MOD_DIR'}";
1253 system $cmds{'make'};
1254 # system "$cmds{'make'} test";
1255 system "$cmds{'make'} install";
1259 chdir $src_dir or die "[*] Could not chdir $src_dir: $!";
1263 sub set_hostname() {
1266 open P, "< $file" or die "[*] Could not open $file: $!";
1269 ### replace the "HOSTNAME CHANGE_ME" line
1270 open PH, "> $file" or die "[*] Could not open $file: $!";
1271 for my $line (@lines) {
1273 if ($line =~ /^\s*HOSTNAME(\s+)_?CHANGE.?ME_?/) {
1274 print PH "HOSTNAME${1}$hostname;\n";
1281 die "[*] Your source directory appears to be incomplete! $file ",
1282 "is missing.\n Download the latest sources from ",
1283 "http://www.cipherdyne.org\n";
1288 sub query_preserve_config() {
1290 "[+] It appears that there is an existing fwknop installation on the system.\n";
1292 return &query_yes_no(" Would you like to merge the " .
1293 "config from the existing\n fwknop installation ([y]/n)? ",
1294 $ACCEPT_YES_DEFAULT);
1299 open F, "< $config{'FWKNOP_CONF_DIR'}/fwknop.conf" or die "[*] Could not open ",
1300 "$config{'FWKNOP_CONF_DIR'}/fwknop.conf: $!";
1303 my $email_addresses;
1304 for my $line (@clines) {
1306 if ($line =~ /^\s*EMAIL_ADDRESSES\s+(.+);/) {
1307 $email_addresses = $1;
1311 unless ($email_addresses) {
1314 print "[+] fwknop access alerts will be sent to:\n\n",
1315 " $email_addresses\n\n";
1317 if ($force_defaults) {
1318 print " Setting default [$email_addresses].\n";
1319 return $email_addresses;
1322 if (&query_yes_no("[+] Would you like access alerts sent to a " .
1323 "different address ([y]/n)? ", $ACCEPT_YES_DEFAULT)) {
1326 "[+] To which email address(es) would you like fwknop alerts to be sent?\n",
1327 " You can enter as many email addresses as you like; each on its own line.\n\n",
1328 " End with a \".\" on a line by itself.\n\n";
1330 while ($ans !~ /^\s*\.\s*$/) {
1331 print " Email Address: ";
1334 if ($ans =~ m|^\s*(\S+\@\S+)$|) {
1335 $email_str .= "$1, ";
1336 } elsif ($ans !~ /^\s*\.\s*$/) {
1337 print "[-] Invalid email address \"$ans\"\n";
1340 $email_str =~ s/\,\s*$//;
1345 sub ask_to_stop_fwknop() {
1347 if (&is_fwknop_running()) {
1348 print "[+] An existing fwknop process is running.\n";
1350 if (&query_yes_no(" Can I stop the existing fwknop " .
1351 "process ([y]/n)? ", $ACCEPT_YES_DEFAULT)) {
1354 die "[*] Aborting install (you can run ./install.pl again).";
1361 sub is_fwknop_running() {
1362 for my $pid_file ($config{'FWKNOP_PID_FILE'},
1363 $config{'KNOPTM_PID_FILE'},
1364 $config{'KNOPMD_PID_FILE'},
1365 $config{'TCPSERV_PID_FILE'}) {
1367 next unless -e $pid_file;
1368 open P, "< $pid_file" or die "[*] Could not open $pid_file: $!";
1371 return 1 if kill 0, $pid;
1379 while (&is_fwknop_running()) {
1381 if (-d $init_dir and -e "$init_dir/$init_name") {
1382 system "$init_dir/$init_name stop";
1385 if (&is_fwknop_running()) {
1386 system "fwknopd -K";
1391 print "[-] Could not stop running fwknop processes, ",
1392 "continuing anyway";
1399 sub get_pcap_intf() {
1401 "\n[+] It appears that the following network interfaces are attached to the\n",
1403 open IFC, "$cmds{'ifconfig'} -a |" or die "[*] Could not execute ",
1404 "$cmds{'ifconfig'} -a: $!";
1405 my @ifconfig_out = <IFC>;
1407 my %interfaces = ();
1408 my $default_intf = '';
1410 if ($os_type == $OS_BSD or $os_type == $OS_DARWIN) {
1411 for my $line (@ifconfig_out) {
1412 if ($line =~ /^\s*(\w+):\s+flags=/) {
1413 $interfaces{$1} = '';
1415 $default_intf = $1 unless $default_intf;
1419 for my $line (@ifconfig_out) {
1420 if ($line =~ /^\s*(\w+)\s+Link/) {
1421 $interfaces{$1} = '';
1423 $default_intf = $1 unless $default_intf;
1428 ### return the default interface
1429 if ($force_defaults) {
1430 if (defined $interfaces{'eth0'}) {
1431 $default_intf = 'eth0';
1433 print " Setting default [$default_intf]\n";
1434 return $default_intf;
1439 while (not defined $interfaces{$ans}) {
1441 " Which network interface would you like fwknop to sniff packets from? ";
1445 if ($loop_ctr >= 10) {
1452 sub query_data_method() {
1454 "[+] fwknop can act as a server (i.e. monitoring authentication packets\n",
1455 " and sequences, and taking the appropriate action on the local system\n",
1456 " to alter the firewall policy or execute commands), or as a client (i.e.\n",
1457 " by manufacturing authentication packets and sequences).\n\n";
1461 " In which mode will fwknop be executed on the local system? (Note that\n",
1462 " fwknop can still be used as a client even if you select \"server\" here).\n";
1464 if ($force_defaults) {
1465 print " Setting default [server] mode.\n";
1469 while ($ans ne 'client' and $ans ne 'server') {
1470 print " (client/[server]): ";
1477 if ($ans eq 'client') {
1478 $client_install = 1;
1483 "\n[+] In server mode, fwknop can acquire packet through a pcap file that is\n",
1484 " generated by a sniffer (or through the Netfilter ulogd pcap writer), or\n",
1485 " by sniffing packets directly off the wire via the Net::Pcap perl module.\n",
1486 " Fwknop can also acquire packet data from iptables syslog messages, but\n",
1487 " this is only supported for the legacy port knocking mode; Single Packet\n",
1488 " Authorization (SPA), which is used in the pcap modes, is a better\n",
1489 " authorization strategy from every perspective (see the fwknop man page for\n",
1490 " more information). If you intend to use iptables log messages (only makes\n",
1491 " sense for the legacy port knocking mode), then fwknop will need to\n",
1492 " reconfigure your syslog daemon to write kern.info messages to the\n",
1493 " $config{'KNOPMD_FIFO'} named pipe. It is highly recommended\n",
1494 " to use one of the pcap modes unless you really want the old port knocking\n",
1499 if ($force_defaults) {
1500 print " Setting default [pcap] mode.\n";
1505 " Which of the following data acquistion methods would you like to use?\n";
1506 while ($ans ne 'syslogd' and $ans ne 'syslog-ng' and $ans ne 'ulogd'
1507 and $ans ne 'pcap' and $ans ne 'file_pcap') {
1509 print " ([pcap], file_pcap, ulogd, syslogd, syslog-ng): ";
1512 return 'pcap' if $ans eq "\n";
1515 if ($ans eq 'syslogd' or $ans eq 'syslog-ng') {
1516 if ($ans eq 'syslogd') {
1517 ### allow command line --syslog-conf arg to take over
1518 $syslog_conf = '/etc/syslog.conf' unless $syslog_conf;
1519 } elsif ($ans eq 'syslog-ng') {
1520 ### allow command line --syslog-conf arg to take over
1521 $syslog_conf = '/etc/syslog-ng/syslog-ng.conf' unless $syslog_conf;
1523 if ($syslog_conf and not -e $syslog_conf) {
1525 "[-] The config file $syslog_conf does not exist. Re-run install.pl\n",
1526 " with the --syslog-conf argument to specify the path to the syslog\n",
1527 " daemon config file.\n";
1530 print "[-] Invalid acquistion method \"$ans\"\n"
1532 ($ans eq 'syslogd' or $ans eq 'syslog-ng'
1533 or $ans eq 'file_pcap' or $ans eq 'ulogd' or $ans eq 'pcap'));
1539 my ($var, $value, $file) = @_;
1540 open RF, "< $file" or die "[*] Could not open $file: $!";
1543 open F, "> $file" or die "[*] Could not open $file: $!";
1544 for my $line (@lines) {
1545 if ($line =~ /^\s*$var\s+.*;/) {
1546 printf F "%-28s%s;\n", $var, $value;
1555 sub perms_ownership() { ### only gets called in full-installation mode
1556 my ($file, $perm_value) = @_;
1557 chmod $perm_value, $file or die "[*] Could not ",
1558 "chmod($perm_value, $file): $!";
1559 ### root (maybe should take the group assignment out?)
1560 chown 0, 0, $file or die "[*] Could not chown 0,0,$file: $!";
1564 sub has_perl_module() {
1567 # 5.8.0 has a bug with require Foo::Bar alone in an eval, so an
1568 # extra statement is a workaround.
1569 my $file = "$module.pm";
1571 eval { require $file };
1576 sub update_command_paths() {
1579 open F, "< $file" or die "[*] Could not open file: $!";
1585 CMD: for my $line (@lines) {
1587 if ($line =~ /^\s*(\w+)Cmd(\s+)(\S+);/) {
1591 unless (-e $path and -x $path) {
1592 ### the command is not at this path, try to find it
1593 my $cmd_minor_name = $cmd;
1594 if ($path =~ m|.*/(\S+)|) {
1595 $cmd_minor_name = $cmd if $cmd ne $1;
1597 DIR: for my $dir (@cmd_search_paths) {
1598 if (-e "$dir/$cmd_minor_name"
1599 and -x "$dir/$cmd_minor_name") {
1600 ### found the command
1602 "${cmd}Cmd${spaces}${dir}/${cmd_minor_name};\n";
1609 next CMD if ($cmd eq 'iptables' and $os_type != $OS_LINUX);
1610 next CMD if ($cmd eq 'ipfw' and $os_type == $OS_LINUX);
1612 "[-] Could not find the path to the $cmd command, you will need to manually\n",
1613 " edit the path for the ${cmd}Cmd variable in $file\n";
1618 push @newlines, $line;
1622 open C, "> $file" or die "[*] Could not open file: $!";
1623 print C for @newlines;
1629 sub import_config() {
1630 open C, "< $fwknop_conf_file"
1631 or die "[*] Could not open $fwknop_conf_file: $!";
1634 if (/^\s*(\S+)\s+(.*?)\;/) {
1637 if ($val =~ m|/.+| and $varname =~ /^\s*(\S+)Cmd$/) {
1641 $config{$varname} = $val;
1647 ### resolve internal vars within variable values
1657 my $has_sub_var = 1;
1658 my $resolve_ctr = 0;
1660 while ($has_sub_var) {
1663 if ($resolve_ctr >= 20) {
1664 die "[*] Exceeded maximum variable resolution counter.";
1666 for my $hr (\%config, \%cmds) {
1667 for my $var (keys %$hr) {
1668 my $val = $hr->{$var};
1669 if ($val =~ m|\$(\w+)|) {
1671 die "[*] sub-ver $sub_var not allowed within same ",
1672 "variable $var" if $sub_var eq $var;
1673 if (defined $config{$sub_var}) {
1674 $val =~ s|\$$sub_var|$config{$sub_var}|;
1677 die "[*] sub-var \"$sub_var\" not defined in ",
1678 "config for var: $var."
1688 sub handle_cmd_line() {
1689 if ($bsd_install and $cygwin_install) {
1690 die "[*] Cannot use --BSD-install and --Cygwin-install at the same time.";
1697 } elsif ($cygwin_install) {
1699 $os_type = $OS_CYGWIN;
1701 } elsif ($cmdline_os_type) {
1704 for my $type (keys %os_types) {
1705 if ($cmdline_os_type =~ /$type/i) {
1706 $os_type = $os_types{$type};
1710 print "[*] --OS-type accepts the following operating systems:\n";
1711 for my $type (keys %os_types) {
1721 sub get_runlevel() {
1722 die "[*] The runlevel cannot be greater than 6"
1725 return if $runlevel > 0;
1727 open RUN, "$cmds{'runlevel'} |" or die "[*] Could not execute the runlevel ",
1728 "command, use --runlevel <N>";
1730 if (/^\s*\S+\s+(\d+)/) {
1739 sub required_vars() {
1741 FWKNOP_DIR FWKNOP_RUN_DIR FWKNOP_LIB_DIR KNOPMD_FIFO FWKNOP_PID_FILE
1742 FWKNOP_CONF_DIR FW_DATA_FILE FWKNOP_MOD_DIR
1744 for my $var (@vars) {
1745 die "[*] Missing required var: $var in $fwknop_conf_file"
1746 unless defined $config{$var};
1751 sub query_yes_no() {
1752 my ($msg, $style) = @_;
1755 if ($force_defaults) {
1756 if ($style == $ACCEPT_YES_DEFAULT or $style == $NO_ANS_DEFAULT) {
1757 print " Setting default [y]\n";
1759 } elsif ($ACCEPT_NO_DEFAULT) {
1760 print " Setting default [n]\n";
1765 while ($ans ne 'y' and $ans ne 'n') {
1768 if ($style == $ACCEPT_YES_DEFAULT) {
1769 return 1 if $ans eq "\n";
1770 } elsif ($style == $ACCEPT_NO_DEFAULT) {
1771 return 0 if $ans eq "\n";
1775 return 1 if $ans eq 'y';
1780 my $exit_status = shift;
1783 Usage: install.pl [options]
1785 -c, --client-only - Only install fwknop client.
1786 -u, --uninstall - Uninstall fwknop.
1787 -O, --OS-type <OS> - Specify installation target OS (e.g.
1788 "linux", "cygwin", or "darwin").
1789 --Single-mod-install <re> - Install a particular perl module.
1790 --force-mod-install - Force all perl modules to be installed
1791 even if some already exist in the system
1792 /usr/lib/perl5 tree.
1793 --Force-mod-regex <re> - Specify a regex to match a module name
1794 and force the installation of such modules.
1795 -D, --Defaults - Force all default values without asking.
1796 -p, --path-update - Run path update code regardless of whether
1797 a previous config is being merged.
1798 --interface <intf> - Manually specify an interface to sniff
1800 --init-dir <path> - Specify path to the init directory (the
1801 default is $init_dir).
1802 --init-name <name> - Specify the name for the fwknop init
1803 script (the default is $init_name).
1804 -r, --runlevel <N> - Specify the current system runlevel.
1805 --Skip-mod-install - Do not install any perl modules.
1806 -s, --syslog <file> - Specify path to syslog.conf file.
1807 -L, --LANG <locale> - Specify LANG env variable (actually the
1809 -n, --no-LANG - Do not export the LANG env variable.
1810 -H, --Home-dir <dir> - Manually specify the home directory for
1811 client-mode installs.
1812 -h --help - Prints this help message.