3 #######################################################################
7 # Purpose: This is the installation script for fwsnort.
9 # Copyright (C) 2003-2011 Michael Rash (mbr@cipherdyne.org)
11 # License (GNU Public License):
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
24 # - Write the uninstall() routine. :)
26 #######################################################################
36 #========================= config ========================
37 my $fwsnort_conf_file = 'fwsnort.conf';
39 my $sbin_dir = '/usr/sbin';
41 my $update_website = 'www.emergingthreats.net';
44 my $perlCmd = '/usr/bin/perl';
45 my $makeCmd = '/usr/bin/make';
46 my $wgetCmd = '/usr/bin/wget';
47 my $gzipCmd = '/bin/gzip';
48 my $tarCmd = '/bin/tar';
49 #======================= end config ======================
53 ### map perl modules to versions
54 my %required_perl_modules = (
57 'mod-dir' => 'NetAddr-IP'
59 'IPTables::Parse' => {
61 'mod-dir' => 'IPTables-Parse'
66 my $rules_url = 'http://rules.emergingthreats.net/open/snort-2.9.0/emerging-all.rules';
68 ### establish some defaults
70 my $skip_module_install = 0;
71 my $cmdline_force_install = 0;
72 my $force_mod_re = '';
73 my $exclude_mod_re = '';
74 my $deps_dir = 'deps';
76 my $locale = 'C'; ### default LC_ALL env variable
79 my $src_dir = getcwd() or die "[*] Could not get current working directory.";
89 ### make Getopts case sensitive
90 Getopt::Long::Configure('no_ignore_case');
92 &usage(1) unless (GetOptions(
93 'force-mod-install' => \$cmdline_force_install, ### force install of all modules
94 'Force-mod-regex=s' => \$force_mod_re, ### force specific mod install with regex
95 'Exclude-mod-regex=s' => \$exclude_mod_re, ### exclude a particular perl module
96 'Skip-mod-install' => \$skip_module_install,
97 'rules-url=s' => \$rules_url,
98 'uninstall' => \$uninstall, ### uninstall fwsnort
99 'LC_ALL=s' => \$locale,
100 'no-LC_ALL' => \$no_locale,
106 ### set LC_ALL env variable
107 $ENV{'LC_ALL'} = $locale unless $no_locale;
111 $force_mod_re = qr|$force_mod_re| if $force_mod_re;
112 $exclude_mod_re = qr|$exclude_mod_re| if $exclude_mod_re;
114 ### see if the deps/ directory exists, and if not then we are installing
115 ### from the -nodeps sources so don't install any perl modules
116 $skip_module_install = 1 unless -d $deps_dir;
118 ### make sure the system binaries are where we think they are.
121 ### check to make sure we are running as root
122 $< == 0 && $> == 0 or die "You need to be root (or equivalent UID 0",
123 " account) to install/uninstall fwsnort!\n";
131 #===================== end main ===================
134 die "[*] You must run install.pl from the fwsnort " .
135 "sources directory." unless -e 'fwsnort' and -e 'fwsnort.conf';
137 unless (-d $config{'CONF_DIR'}) {
138 print "[+] mkdir $config{'CONF_DIR'}\n";
139 mkdir $config{'CONF_DIR'}, 0500;
141 unless (-d $config{'RULES_DIR'}) {
142 print "[+] mkdir $config{'RULES_DIR'}\n";
143 mkdir $config{'RULES_DIR'}, 0500;
146 ### install perl modules
147 unless ($skip_module_install) {
148 for my $module (keys %required_perl_modules) {
149 &install_perl_module($module);
152 chdir $src_dir or die "[*] Could not chdir $src_dir: $!";
154 my $local_rules_dir = 'deps/snort_rules';
155 if (-d 'deps' and -d $local_rules_dir
156 and &query_get_emerging_threats_sigs()) {
157 chdir $local_rules_dir or die "[*] Could not chdir $local_rules_dir";
158 if (-e 'emerging-all.rules') {
159 move 'emerging-all.rules', 'emerging-all.rules.tmp'
160 or die "[*] Could not move emerging-all.rules -> ",
161 "emerging-all.rules.tmp";
163 system "$cmds{'wget'} $rules_url";
164 if (-e 'emerging-all.rules') { ### successful download
165 unlink 'emerging-all.rules.tmp';
167 print "[-] Could not download emerging-all.rules file.\n";
168 if (-e 'emerging-all.rules.tmp') {
169 ### move the original back
170 move 'emerging-all.rules', 'emerging-all.rules.tmp'
171 or die "[*] Could not move emerging-all.rules -> ",
172 "emerging-all.rules.tmp";
178 if (-d 'deps' and -d $local_rules_dir) {
179 opendir D, $local_rules_dir or die "[*] Could not open ",
180 "the $local_rules_dir directory: $!";
181 my @rfiles = readdir D;
184 print "[+] Copying all rules files to $config{'RULES_DIR'}\n";
185 for my $rfile (@rfiles) {
186 next unless $rfile =~ /\.rules$/;
187 print "[+] Installing $rfile\n";
188 copy "$local_rules_dir/${rfile}", "$config{'RULES_DIR'}/${rfile}"
189 or die "[*] Could not copy $local_rules_dir/${rfile} ",
190 "-> $config{'RULES_DIR'}/${rfile}";
196 ### install the fwsnort.8 man page
200 if (-e "$config{'CONF_DIR'}/fwsnort.conf") {
201 $preserve_rv = &query_preserve_config();
207 print "[+] Copying fwsnort.conf -> $config{'CONF_DIR'}/fwsnort.conf\n";
208 copy 'fwsnort.conf', "$config{'CONF_DIR'}/fwsnort.conf";
209 chmod 0600, "$config{'CONF_DIR'}/fwsnort.conf";
212 print "[+] Copying fwsnort -> ${sbin_dir}/fwsnort\n";
213 copy 'fwsnort', "${sbin_dir}/fwsnort";
214 chmod 0500, "${sbin_dir}/fwsnort";
216 print "\n========================================================\n",
217 "\n[+] fwsnort will generate an iptables script located at:\n",
218 " /var/lib/fwsnort.sh when executed.\n",
219 "\n[+] fwsnort has been successfully installed!\n\n";
224 sub install_perl_module() {
225 my $mod_name = shift;
227 chdir $src_dir or die "[*] Could not chdir $src_dir: $!";
228 chdir $deps_dir or die "[*] Could not chdir($deps_dir): $!";
230 die '[*] Missing force-install key in required_perl_modules hash.'
231 unless defined $required_perl_modules{$mod_name}{'force-install'};
232 die '[*] Missing mod-dir key in required_perl_modules hash.'
233 unless defined $required_perl_modules{$mod_name}{'mod-dir'};
235 if ($exclude_mod_re and $exclude_mod_re =~ /$mod_name/) {
236 print "[+] Excluding installation of $mod_name module.\n";
240 my $version = '(NA)';
242 my $mod_dir = $required_perl_modules{$mod_name}{'mod-dir'};
244 if (-e "$mod_dir/VERSION") {
245 open F, "< $mod_dir/VERSION" or
246 die "[*] Could not open $mod_dir/VERSION: $!";
251 print "[-] Warning: VERSION file does not exist in $mod_dir\n";
254 my $install_module = 0;
256 if ($required_perl_modules{$mod_name}{'force-install'}
257 or $cmdline_force_install) {
258 ### install regardless of whether the module may already be
261 } elsif ($force_mod_re and $force_mod_re =~ /$mod_name/) {
262 print "[+] Forcing installation of $mod_name module.\n";
265 if (has_perl_module($mod_name)) {
266 print "[+] Module $mod_name is already installed in the ",
267 "system perl tree, skipping.\n";
269 ### install the module in the /usr/lib/fwsnort directory because
270 ### it is not already installed.
275 if ($install_module) {
276 unless (-d $config{'LIBS_DIR'}) {
277 print "[+] Creating $config{'LIBS_DIR'}\n";
278 mkdir $config{'LIBS_DIR'}, 0755
279 or die "[*] Could not mkdir $config{'LIBS_DIR'}: $!";
281 print "[+] Installing the $mod_name $version perl " .
282 "module in $config{'LIBS_DIR'}/\n";
283 my $mod_dir = $required_perl_modules{$mod_name}{'mod-dir'};
284 chdir $mod_dir or die "[*] Could not chdir to ",
286 unless (-e 'Makefile.PL') {
287 die "[*] Your $mod_name source directory appears to be incomplete!\n",
288 " Download the latest sources from ",
289 "http://www.cipherdyne.org/\n";
291 system "$cmds{'make'} clean" if -e 'Makefile';
292 system "$cmds{'perl'} Makefile.PL " .
293 "PREFIX=$config{'LIBS_DIR'} LIB=$config{'LIBS_DIR'}";
294 system $cmds{'make'};
295 # system "$cmds{'make'} test";
296 system "$cmds{'make'} install";
300 chdir $src_dir or die "[*] Could not chdir $src_dir: $!";
304 sub has_perl_module() {
307 # 5.8.0 has a bug with require Foo::Bar alone in an eval, so an
308 # extra statement is a workaround.
309 my $file = "$module.pm";
311 eval { require $file };
321 sub install_manpage() {
322 my $manpage = 'fwsnort.8';
323 ### remove old man page
324 unlink "/usr/local/man/man8/${manpage}" if
325 (-e "/usr/local/man/man8/${manpage}");
327 ### default location to put the fwsnort man page, but check with
329 my $mpath = '/usr/share/man/man8';
330 if (-e '/etc/man.config') {
331 ### prefer to install $manpage in /usr/local/man/man8 if
332 ### this directory is configured in /etc/man.config
333 open M, '< /etc/man.config' or
334 die "[*] Could not open /etc/man.config: $!";
337 ### prefer the path "/usr/share/man"
339 for my $line (@lines) {
341 if ($line =~ m|^MANPATH\s+/usr/share/man|) {
346 ### try to find "/usr/local/man" if we didn't find /usr/share/man
348 for my $line (@lines) {
350 if ($line =~ m|^MANPATH\s+/usr/local/man|) {
351 $mpath = '/usr/local/man/man8';
357 ### if we still have not found one of the above man paths,
358 ### just select the first one out of /etc/man.config
360 for my $line (@lines) {
362 if ($line =~ m|^MANPATH\s+(\S+)|) {
369 mkdir $mpath, 0755 unless -d $mpath;
370 my $mfile = "${mpath}/${manpage}";
371 print "[+] Installing $manpage man page as $mfile\n";
372 copy $manpage, $mfile or die "[*] Could not copy $manpage to " .
375 print "[+] Compressing manpage $mfile\n";
376 ### remove the old one so gzip doesn't prompt us
377 unlink "${mfile}.gz" if -e "${mfile}.gz";
378 system "$gzipCmd $mfile";
382 sub query_get_emerging_threats_sigs() {
384 print "[+] Would you like to download the latest Snort rules from \n",
385 " http://$update_website/?\n";
386 while ($ans ne 'y' && $ans ne 'n') {
389 return 1 if $ans eq "\n";
398 sub import_config() {
399 open C, "< $fwsnort_conf_file"
400 or die "[*] Could not open $fwsnort_conf_file: $!";
403 if (/^\s*(\S+)\s+(.*?)\;/) {
406 if ($val =~ m|/.+| and $varname =~ /^\s*(\S+)Cmd$/) {
410 $config{$varname} = $val;
416 ### resolve internal vars within variable values
429 while ($has_sub_var) {
432 if ($resolve_ctr >= 20) {
433 die "[*] Exceeded maximum variable resolution counter.";
435 for my $hr (\%config, \%cmds) {
436 for my $var (keys %$hr) {
437 my $val = $hr->{$var};
438 if ($val =~ m|\$(\w+)|) {
440 die "[*] sub-ver $sub_var not allowed within same ",
441 "variable $var" if $sub_var eq $var;
442 if (defined $config{$sub_var}) {
443 $val =~ s|\$$sub_var|$config{$sub_var}|;
446 die "[*] sub-var \"$sub_var\" not defined in ",
447 "config for var: $var."
457 sub required_vars() {
458 my @required_vars = qw(
459 CONF_DIR RULES_DIR ARCHIVE_DIR QUEUE_RULES_DIR LOG_DIR LIBS_DIR
460 CONF_FILE FWSNORT_SCRIPT LOG_FILE
462 for my $var (@required_vars) {
463 die "[*] Variable $var not defined in $fwsnort_conf_file. Exiting.\n"
464 unless defined $config{$var};
469 sub check_commands() {
475 CMD: for my $cmd (keys %cmds) {
476 unless (-x $cmds{$cmd}) {
478 PATH: for my $dir (@path) {
479 if (-x "${dir}/${cmd}") {
480 $cmds{$cmd} = "${dir}/${cmd}";
486 die "[*] Could not find $cmd, edit the ",
487 "config section of install.pl";
495 sub query_preserve_config() {
497 while ($ans ne 'y' && $ans ne 'n') {
498 print "[+] Would you like to preserve the config from the\n",
499 ' existing fwsnort installation ([y]/n)? ';
501 return 1 if $ans eq "\n";
510 sub preserve_config() {
511 my $file = 'fwsnort.conf';
512 open C, "< $file" or die "[*] Could not open $file: $!";
516 open CO, "< $config{'CONF_DIR'}/$file" or die "[*] Could not open ",
517 "$config{'CONF_DIR'}/$file: $!";
518 my @orig_lines = <CO>;
521 print "[+] Preserving existing config: $config{'CONF_DIR'}/$file\n";
522 ### write to a tmp file and then move.
523 my $printed_intf_warning = 0;
524 open CONF, "> $config{'CONF_DIR'}/${file}.new" or die "[*] Could not open ",
525 "$config{'CONF_DIR'}/${file}.new: $!";
526 for my $new_line (@new_lines) {
527 if ($new_line =~ /^\s*#/) {
528 print CONF $new_line;
529 } elsif ($new_line =~ /^\s*(\S+)/) {
532 for my $orig_line (@orig_lines) {
533 if ($orig_line =~ /^\s*\S+INTF\s/) {
534 ### interfaces are no longer used!
535 unless ($printed_intf_warning) {
536 print " NOTE: Interfaces are no longer used as of the ",
537 "0.8.0 release;\n removing $var\n";
538 $printed_intf_warning = 1;
541 if ($orig_line =~ /^\s*$var\s/
542 and $orig_line !~ /INTF/) {
543 print CONF $orig_line;
549 print CONF $new_line;
552 print CONF $new_line;
556 move "$config{'CONF_DIR'}/${file}.new", "$config{'CONF_DIR'}/$file";
563 install.pl: [options]
564 -f, --force-mod-install - Install all perl modules regardless
565 of whether they already installed on
567 -F, --Force-mod-regex <regex> - Install perl module that matches a
568 specific regular expression.
569 -E, --Exclude-mod-regex <re> - Exclude a perl module that matches this
571 -S, --Skip-mod-install - Skip installation of modules.
572 -r, --rules-url <url> - Specify the URL to use for updating the
573 Emerging Threats rule set - the default is:
575 -u, --uninstall - uninstall fwsnort
576 -h, --help - print help and exit