root/psad/tags/psad-2.1.2/install.pl

Revision 2136, 71.7 kB (checked in by mbr, 10 months ago)

- Added a new feature whereby psad can acquire iptables log data just by
parsing an existing file (/var/log/messages by default) that is written
to by syslog. By default, psad acquires iptables log data from the
/var/log/psad/fwdata file which is written to by kmsgsd, but on some
systems, having syslog communicate log data to kmsgsd can be problematic
since syslog configs and external factors such as Apparmor and SELinux
can play a role here. This new feature is controled by two new
configuration variables "ENABLE_SYSLOG_FILE" (to enable/disable the
feature) and "IPT_SYSLOG_FILE" to specifiy the path to the file to
parse.
- Better installation support for various Linux distributions including
Fedora 8 and Ubuntu. The current runlevel is now acquired via the
"runlevel" command instead of attempting to read /etc/inittab (which
does not even exist on Ubuntu 7.10), and there are new command line
arguments --init-dir, --init-name, and --runlevel to allow the init
directory, init script name, and the runlevel to be manually specified
on the install.pl command line.
- Updated psad to automatically handle situations where the either the
/var/log/psad/fwdata file or the /var/log/messages file (whichever
syslog is writing iptables log messages to) gets rotated. The
filehandle is closed and reopened if the file shrinks or if the inode
changes. This strategy is borrowed from how the fwknop project deals
with the filesystem packet capture file.
- Updated install.pl to set the LC_ALL environmental variable to "C"
This should address some issues with installing psad on non-English
locale systems.
- Updated install.pl to be compatible with the rsyslog daemon, which is
commonly installed on Fedora 8 systems.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1 #!/usr/bin/perl -w
2 #
3 #########################################################################
4 #
5 # File: install.pl
6 #
7 # Purpose:  install.pl is the installation script for psad.  It is safe
8 #           to execute install.pl even if psad has already been installed
9 #           on a system since install.pl will preserve the existing
10 #           config section within the new script.
11 #
12 # Credits:  (see the CREDITS file)
13 #
14 # Copyright (C) 1999-2008 Michael Rash (mbr@cipherdyne.org)
15 #
16 # License (GNU Public License):
17 #
18 #    This program is distributed in the hope that it will be useful,
19 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
20 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 #    GNU General Public License for more details.
22 #
23 #    You should have received a copy of the GNU General Public License
24 #    along with this program; if not, write to the Free Software
25 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
26 #    USA
27 #
28 # TODO:
29 #
30 #########################################################################
31 #
32 # $Id$
33 #
34
35 use Cwd;
36 use File::Path;
37 use File::Copy;
38 use Sys::Hostname;
39 use IO::Socket;
40 use Getopt::Long;
41 use strict;
42
43 #============== config ===============
44 my $USRSBIN_DIR  = '/usr/sbin'### consistent with FHS (Filesystem
45                                  ### Hierarchy Standard)
46 my $USRBIN_DIR   = '/usr/bin';   ### consistent with FHS
47
48 my $psad_conf_file  = 'psad.conf';
49
50 ### system binaries ###
51 my $chkconfigCmd = '/sbin/chkconfig';
52 my $rcupdateCmd  = '/sbin/rc-update'### Gentoo
53 my $updatercdCmd = '/usr/sbin/update-rc.d'### Ubuntu
54 my $makeCmd      = '/usr/bin/make';
55 my $perlCmd      = '/usr/bin/perl';
56 my $wgetCmd      = '/usr/bin/wget';
57 my $runlevelCmd  = '/sbin/runlevel';
58 #============ end config ============
59
60 my %file_vars = (
61     'signatures'    => 'SIGS_FILE',
62     'auto_dl'       => 'AUTO_DL_FILE',
63     'icmp_types'    => 'ICMP_TYPES_FILE',
64     'posf'          => 'POSF_FILE',
65     'pf.os'         => 'P0F_FILE',
66     'snort_rule_dl' => 'SNORT_RULE_DL_FILE',
67     'ip_options'    => 'IP_OPTS_FILE'
68 );
69
70 my %exclude_cmds = (
71     'wget'         => '',
72     'mail'         => '',
73     'sendmail'     => '',
74     'uname'        => '',
75     'df'           => '',
76     'psadwatchd'   => '',
77     'kmsgsd'       => '',
78     'psad'         => '',
79     'whois'        => '',
80     'fwcheck_psad' => ''
81 );
82
83 ### map perl modules to versions
84 my %required_perl_modules = (
85     'Unix::Syslog' => {
86         'force-install' => 0,
87         'mod-dir' => 'Unix-Syslog'
88     },
89     'Bit::Vector' => {
90         'force-install' => 0,
91         'mod-dir' => 'Bit-Vector'
92     },
93     'Storable', => {
94         'force-install' => 0,
95         'mod-dir' => 'Storable'
96     },
97     'Date::Calc', => {
98         'force-install' => 0,
99         'mod-dir' => 'Date-Calc'
100     },
101     'Net::IPv4Addr' => {
102         'force-install' => 0,
103         'mod-dir' => 'Net-IPv4Addr'
104     },
105     'IPTables::Parse' => {
106         'force-install' => 1,
107         'mod-dir' => 'IPTables-Parse'
108     },
109     'IPTables::ChainMgr' => {
110         'force-install' => 1,
111         'mod-dir' => 'IPTables-ChainMgr'
112     },
113 );
114
115 my %config = ();
116 my %cmds   = ();
117
118 my @cmd_search_paths = qw(
119     /bin
120     /sbin
121     /usr/bin
122     /usr/sbin
123     /usr/local/bin
124     /usr/local/sbin
125 );
126
127 ### IP regex
128 my $ip_re = qr|(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}|;
129
130 ### get the hostname of the system
131 my $HOSTNAME = hostname();
132
133 my $src_dir = getcwd() or die "[*] Could not get current working directory.";
134
135 ### for user answers
136 my $ACCEPT_YES_DEFAULT = 1;
137 my $ACCEPT_NO_DEFAULT  = 2;
138 my $NO_ANS_DEFAULT     = 0;
139
140 ### set the default execution flags and command line args
141 my $noarchive   = 0;
142 my $uninstall   = 0;
143 my $help        = 0;
144 my $archived_old = 0;
145 my $skip_syslog_test = 0;
146 my $skip_module_install   = 0;
147 my $cmdline_force_install = 0;
148 my $force_path_update = 0;
149 my $force_mod_re = '';
150 my $exclude_mod_re = '';
151 my $no_rm_old_lib_dir = 0;
152 my $syslog_conf = '';
153 my $locale = 'C'### default LC_ALL env variable
154 my $no_locale = 0;
155 my $init_dir = '/etc/init.d';
156 my $init_name = 'psad';
157 my $runlevel = -1;
158
159 ### make Getopts case sensitive
160 Getopt::Long::Configure('no_ignore_case');
161
162 &usage(1) unless (GetOptions(
163     'config=s'          => \$psad_conf_file,  ### specify path to psad.conf
164     'force-mod-install' => \$cmdline_force_install,  ### force install of all modules
165     'Force-mod-regex=s' => \$force_mod_re,  ### force specific mod install with regex
166     'Exclude-mod-regex=s' => \$exclude_mod_re, ### exclude a particular perl module
167     'path-update'       => \$force_path_update, ### update command paths
168     'Skip-mod-install'  => \$skip_module_install,
169     'no-rm-lib-dir'     => \$no_rm_old_lib_dir, ### remove any old /usr/lib/psad dir
170     'no-preserve'       => \$noarchive,   ### Don't preserve existing configs.
171     'syslog-conf=s'     => \$syslog_conf, ### specify path to syslog config file.
172     'no-syslog-test'    => \$skip_syslog_test,
173     'uninstall'         => \$uninstall,   ### Uninstall psad.
174     'init-dir=s'        => \$init_dir,
175     'init-name=s'       => \$init_name,
176     'runlevel=i'        => \$runlevel,
177     'LC_ALL=s'          => \$locale,
178     'no-LC_ALL'         => \$no_locale,
179     'help'              => \$help         ### Display help.
180 ));
181 &usage(0) if $help;
182
183 ### set LC_ALL env variable
184 $ENV{'LC_ALL'} = $locale unless $no_locale;
185
186 ### import paths from default psad.conf
187 &import_config();
188
189 my @LOGR_FILES   = (*STDOUT, $config{'INSTALL_LOG_FILE'});
190
191 $force_mod_re = qr|$force_mod_re| if $force_mod_re;
192 $exclude_mod_re = qr|$exclude_mod_re| if $exclude_mod_re;
193
194 $cmds{'make'}     = $makeCmd;
195 $cmds{'perl'}     = $perlCmd;
196 $cmds{'runlevel'} = $runlevelCmd;
197
198 if (-e $config{'INSTALL_LOG_FILE'}) {
199     open INSTALL, "> $config{'INSTALL_LOG_FILE'}" or
200         die "[*] Could not open $config{'INSTALL_LOG_FILE'}: $!";
201     close INSTALL;
202 }
203
204 my $distro = &get_distro();
205
206 if ($distro eq 'redhat' or $distro eq 'fedora') {
207     ### add chkconfig only if we are runing on a redhat distro
208     $cmds{'chkconfig'} = $chkconfigCmd;
209 } elsif ($distro eq 'gentoo') {
210     ### add rc-update if we are running on a gentoo distro
211     $cmds{'rc-update'} = $rcupdateCmd;
212 } elsif ($distro eq 'ubuntu') {
213     ### add update-rc.d if we are running on an ubuntu distro
214     $cmds{'update-rc.d'} = $updatercdCmd;
215 }
216
217 unless (-d $init_dir) {
218     if (-d '/etc/rc.d/init.d') {
219         $init_dir = '/etc/rc.d/init.d';
220     } elsif (-d '/etc/rc.d') {  ### for Slackware
221         $init_dir = '/etc/rc.d';
222     } else {
223         die "[*] Cannot find the init script directory, use ",
224             "--init-dir <path>\n";
225     }
226 }
227
228 ### need to make sure this exists before attempting to
229 ### write anything to the install log.
230 mkdir $config{'PSAD_DIR'}, 0500 unless -d $config{'PSAD_DIR'};
231
232 ### make sure the system binaries are where we expect
233 ### them to be.
234 &check_commands();
235
236 ### check to make sure we are running as root
237 $< == 0 && $> == 0 or die "You need to be root (or equivalent UID 0",
238     " account) to install/uninstall psad!\n";
239
240 ### occasionally things from old psad installations need to be
241 ### dealt with separately.
242 &check_old_psad_installation();
243
244 if ($uninstall) {
245     &uninstall();
246 } else {
247     &install();
248 }
249 exit 0;
250 #================= end main =================
251
252 sub install() {
253
254     ### make sure install.pl is being called from the source directory
255     unless (-e 'psad' and -d 'IPTables-ChainMgr') {
256         die "[*] install.pl can only be executed from the directory\n",
257             "    that contains the psad sources!  Exiting.";
258     }
259     &logr('[+] ' . localtime() . " Installing psad on hostname: $HOSTNAME\n");
260
261     ### make sure another psad process is not running
262     if (&ask_to_stop_psad()) {
263         &stop_psad();
264     }
265
266     unless (-d $config{'PSAD_RUN_DIR'}) {
267         &logr("[+] Creating $config{'PSAD_RUN_DIR'}\n");
268         mkdir $config{'PSAD_RUN_DIR'}, 0500;
269     }
270     unless (-d $config{'PSAD_FIFO_DIR'}) {
271         &logr("[+] Creating $config{'PSAD_FIFO_DIR'}\n");
272         mkdir $config{'PSAD_FIFO_DIR'}, 0500;
273     }
274
275     ### change any existing psad module directory to allow anyone to import
276     my $dir_tmp = $config{'PSAD_LIBS_DIR'};
277     $dir_tmp =~ s|lib/|lib64/|;
278     for my $dir ($config{'PSAD_LIBS_DIR'}, $dir_tmp) {
279         if (-d $dir) {
280             chmod 0755, $dir;
281             unless ($no_rm_old_lib_dir) {
282                 &logr("[+] Removing $dir/ directory from previous " .
283                     "psad installation.\n");
284                 rmtree $dir;
285             }
286         }
287     }
288     unless (-d $config{'PSAD_CONF_DIR'}) {
289         &logr("[+] Creating $config{'PSAD_CONF_DIR'}\n");
290         mkdir $config{'PSAD_CONF_DIR'}, 0500;
291     }
292     unless (-d $config{'CONF_ARCHIVE_DIR'}) {
293         &logr("[+] Creating $config{'CONF_ARCHIVE_DIR'}\n");
294         mkdir $config{'CONF_ARCHIVE_DIR'}, 0500;
295     }
296     unless (-e $config{'PSAD_FIFO_FILE'}) {
297         &logr("[+] Creating named pipe $config{'PSAD_FIFO_FILE'}\n");
298         unless (((system "$cmds{'mknod'} -m 600 " .
299                 "$config{'PSAD_FIFO_FILE'} p")>>8) == 0) {
300             &logr("[*] Could not create the named pipe " .
301                 "\"$config{'PSAD_FIFO_FILE'}\"!\n" .
302                 "[*] psad requires this file to exist!  Aborting install.\n");
303             die;
304         }
305         unless (-p $config{'PSAD_FIFO_FILE'}) {
306             &logr("[*] Could not create the named pipe " .
307                 "\"$config{'PSAD_FIFO_FILE'}\"!\n" .
308                 "[*] psad requires this file to exist!  Aborting " .
309                 "install.\n");
310             die;
311         }
312     }
313
314     unless (-d $config{'PSAD_DIR'}) {
315         &logr("[+] Creating $config{'PSAD_DIR'}\n");
316         mkdir $config{'PSAD_DIR'}, 0500;
317     }
318     unless (-e $config{'FW_DATA_FILE'}) {
319         &logr("[+] Creating $config{'FW_DATA_FILE'} file\n");
320         open F, "> $config{'FW_DATA_FILE'}" or die "[*] Could not open ",
321             "$config{'FW_DATA_FILE'}: $!";
322         close F;
323         chmod 0600, "$config{'FW_DATA_FILE'}";
324         &perms_ownership("$config{'FW_DATA_FILE'}", 0600);
325     }
326
327     unless (-d $USRSBIN_DIR) {
328         &logr("[+] Creating $USRSBIN_DIR\n");
329         mkdir $USRSBIN_DIR,0755;
330     }
331     if (-d 'whois') {
332         &logr("[+] Compiling Marco d'Itri's whois client\n");
333         system "$cmds{'make'} -C whois";
334         if (-e 'whois/whois') {
335             ### if an old whois process is still around ("text file
336             ### busy" error), then it is ok to not be able to copy
337             ### the new whois binary into place; the old one should
338             ### work fine.
339             &logr("[+] Copying whois binary to $cmds{'whois'}\n");
340             copy 'whois/whois', $cmds{'whois'};
341             die "[*] Could not copy whois/whois -> $cmds{'whois'}: $!"
342                 unless -e $cmds{'whois'};
343         } else {
344             die "[*] Could not compile whois";
345         }
346     }
347     &perms_ownership($cmds{'whois'}, 0755);
348     print "\n\n";
349
350     ### install perl modules
351     unless ($skip_module_install) {
352         for my $module (keys %required_perl_modules) {
353             &install_perl_module($module);
354         }
355     }
356
357     &logr("[+] Installing Snort-2.3.3 signatures in " .
358         "$config{'SNORT_RULES_DIR'}\n");
359     unless (-d $config{'SNORT_RULES_DIR'}) {
360         mkdir $config{'SNORT_RULES_DIR'}, 0500
361             or die "[*] Could not create $config{'SNORT_RULES_DIR'}: $!";
362     }
363     opendir D, 'snort_rules' or die "[*] Could not open ",
364         "the snort_rules directory: $!";
365     my @files = readdir D;
366     closedir D;
367
368     for my $file (@files) {
369         next unless $file =~ /\.rules$/ or $file =~ /\.config$/;
370         &logr("[+] Installing snort_rules/${file}\n");
371         copy "snort_rules/${file}", "$config{'SNORT_RULES_DIR'}/${file}" or
372             die "[*] Could not copy snort_rules/${file} -> ",
373                 "$config{'SNORT_RULES_DIR'}/${file}: $!";
374         &perms_ownership("$config{'SNORT_RULES_DIR'}/${file}", 0600);
375     }
376     print "\n\n";
377
378     &logr("[+] Compiling kmsgsd, and psadwatchd:\n");
379
380     ### remove any previously compiled kmsgsd
381     unlink 'kmsgsd' if -e 'kmsgsd';
382
383     ### remove any previously compiled psadwatchd
384     unlink 'psadwatchd' if -e 'psadwatchd';
385
386     ### compile the C psad daemons
387     system $cmds{'make'};
388     &logr("[-] Could not compile kmsgsd.c.\n") unless (-e 'kmsgsd');
389     &logr("[-] Could not compile psadwatchd.c.\n") unless (-e 'psadwatchd');
390
391     ### install fwcheck_psad.pl
392     print "\n\n";
393     &logr("[+] Verifying compilation of fwcheck_psad.pl script:\n");
394     unless (((system "$cmds{'perl'} -c fwcheck_psad.pl")>>8) == 0) {
395         die "[*] fwcheck_psad.pl does not compile with \"perl -c\".  Download ",
396             "the latest sources from:\n\nhttp://www.cipherdyne.org/\n";
397     }
398
399     ### make sure the psad (perl) daemon compiles.  The other three
400     ### daemons have all been re-written in C.
401     &logr("[+] Verifying compilation of psad perl daemon:\n");
402     unless (((system "$cmds{'perl'} -c psad")>>8) == 0) {
403         die "[*] psad does not compile with \"perl -c\".  Download the",
404             " latest sources from:\n\nhttp://www.cipherdyne.org/\n";
405     }
406
407     ### install nf2csv
408     &logr("[+] Verifying compilation of nf2csv script:\n");
409     unless (((system "$cmds{'perl'} -c nf2csv")>>8) == 0) {
410         die "[*] nf2csv does not compile with \"perl -c\".  Download ",
411             "the latest sources from:\n\nhttp://www.cipherdyne.org/\n";
412     }
413
414     ### put the nf2csv script in place
415     unlink '/usr/sbin/nf2csv' if -e '/usr/sbin/nf2csv'### old path
416     &logr("[+] Copying nf2csv -> ${USRBIN_DIR}/nf2csv\n");
417     unlink "${USRBIN_DIR}/nf2csv" if -e "${USRBIN_DIR}/nf2csv";
418     copy 'nf2csv', "${USRBIN_DIR}/nf2csv" or die "[*] Could ",
419         "not copy nf2csv -> ${USRBIN_DIR}/nf2csv: $!";
420     &perms_ownership("${USRBIN_DIR}/nf2csv", 0755);
421
422     ### put the fwcheck_psad.pl script in place
423     &logr("[+] Copying fwcheck_psad.pl -> $cmds{'fwcheck_psad'}\n");
424     unlink $cmds{'fwcheck_psad'} if -e $cmds{'fwcheck_psad'};
425     copy 'fwcheck_psad.pl', $cmds{'fwcheck_psad'} or die "[*] Could ",
426         "not copy fwcheck_psad.pl -> $cmds{'fwcheck_psad'}: $!";
427     &perms_ownership($cmds{'fwcheck_psad'}, 0500);
428
429     ### put the psad daemons in place
430     &logr("[+] Copying psad -> ${USRSBIN_DIR}/psad\n");
431     unlink "${USRSBIN_DIR}/psad" if -e "${USRSBIN_DIR}/psad";
432     copy 'psad', "${USRSBIN_DIR}/psad" or die "[*] Could not copy ",
433         "psad -> ${USRSBIN_DIR}/psad: $!";
434     &perms_ownership("${USRSBIN_DIR}/psad", 0500);
435
436     &logr("[+] Copying psadwatchd -> ${USRSBIN_DIR}/psadwatchd\n");
437     unlink "${USRSBIN_DIR}/psadwatchd" if -e "${USRSBIN_DIR}/psadwatchd";
438     copy 'psadwatchd', "${USRSBIN_DIR}/psadwatchd" or die "[*] Could not ",
439         "copy psadwatchd -> ${USRSBIN_DIR}/psadwatchd: $!";
440     &perms_ownership("${USRSBIN_DIR}/psadwatchd", 0500);
441
442     &logr("[+] Copying kmsgsd -> ${USRSBIN_DIR}/kmsgsd\n");
443     unlink "${USRSBIN_DIR}/kmsgsd" if -e "${USRSBIN_DIR}/kmsgsd";
444     copy 'kmsgsd', "${USRSBIN_DIR}/kmsgsd" or die "[*] Could not copy ",
445         "kmsgsd -> ${USRSBIN_DIR}/kmsgsd: $!";
446     &perms_ownership("${USRSBIN_DIR}/kmsgsd", 0500);
447
448     unless (-d $config{'PSAD_CONF_DIR'}) {
449         &logr("[+] Creating $config{'PSAD_CONF_DIR'}\n");
450         mkdir $config{'PSAD_CONF_DIR'},0500;
451     }
452
453     ### get syslog daemon (e.g. syslog, rsyslog syslog-ng, or metalog)
454     my $syslog_str = &query_syslog();
455
456     my $preserve_rv = 0;
457     if (-e "$config{'PSAD_CONF_DIR'}/psad.conf") {
458         $preserve_rv = &query_preserve_config();
459     }
460
461     ### the order of the config files is important (legacy FW_MSG_SEARCH
462     ### vars in psad.conf).
463     my $prod_file = "$config{'PSAD_CONF_DIR'}/psad.conf";
464     if (-e $prod_file) {
465         &archive($prod_file) unless $noarchive;
466         if ($preserve_rv) {
467             &preserve_config('psad.conf', $prod_file);
468         } else {
469             &logr("[+] Copying psad.conf -> $prod_file\n");
470             copy 'psad.conf', $prod_file or die "[*] Could not ",
471                 "copy psad.conf -> $prod_file: $!";
472         }
473     } else {
474         &logr("[+] Copying psad.conf -> $prod_file\n");
475         copy 'psad.conf', $prod_file or die "[*] Could not copy ",
476             "psad.conf -> $prod_file: $!";
477     }
478
479     if ($force_path_update or not $preserve_rv) {
480         &update_command_paths($prod_file);
481     }
482
483     &perms_ownership($prod_file, 0600);
484
485     ### install auto_dl, signatures, icmp_types, posf, and pf.os files
486     for my $filename qw(signatures icmp_types
487             posf auto_dl snort_rule_dl pf.os ip_options) {
488         my $file = $config{$file_vars{$filename}};
489         if (-e $file) {
490             &archive($file) unless $noarchive;
491             ### FIXME, need a real config preservation routine for these files.
492             unless (&query_preserve_sigs_autodl($file)) {
493                 &logr("[+] Copying $filename -> $file\n");
494                 copy $filename, $file or die "[*] Could not ",
495                     "copy $filename -> $file: $!";
496                 &perms_ownership($file, 0600);
497             }
498         } else {
499             &logr("[+] Copying $filename -> $file\n");
500             copy $filename, $file or die "[*] Could not ",
501                 "copy $filename -> $file: $!";
502             &perms_ownership($file, 0600);
503         }
504     }
505
506     ### archive and remove legacy config files
507     for my $filename qw(kmsgsd.conf psadwatchd.conf alert.conf
508             fw_search.conf) {
509         my $path = "$config{'PSAD_CONF_DIR'}/$filename";
510         if (-e $path) {
511             &archive($path);
512             unlink $path;
513         }
514     }
515     &logr("\n");
516
517     unless ($preserve_rv) {  ### we want to preserve the existing config
518
519         ### get email address(es)
520         my $email_str = &query_email();
521         if ($email_str) {
522             &put_string('EMAIL_ADDRESSES', $email_str,
523                 "$config{'PSAD_CONF_DIR'}/psad.conf");
524         }
525
526         ### Give the admin the opportunity to set the strings that are
527         ### parsed out of iptables messages.  This is useful since the
528         ### admin may have configured the firewall to use a logging prefix
529         ### of "Audit" or something else other than the default string
530         ### "DROP".
531         my $fw_search_aref = &get_fw_search_strings();
532         if ($fw_search_aref) {
533             open F, "< $config{'PSAD_CONF_DIR'}/psad.conf"
534                 or die "[*] Could not open ",
535                     "$config{'PSAD_CONF_DIR'}/psad.conf: $!";
536             my @lines = <F>;
537             close F;
538             open T, "> $config{'PSAD_CONF_DIR'}/psad.conf.tmp"
539                 or die "[*] Could not open ",
540                     "$config{'PSAD_CONF_DIR'}/psad.conf.tmp: $!";
541             for my $line (@lines) {
542                 if ($line =~ /^\s*FW_MSG_SEARCH\s/) {
543                     for my $fw_str (@$fw_search_aref) {
544                         &logr(qq{[+] Setting FW_MSG_SEARCH to "$fw_str" } .
545                             "in $config{'PSAD_CONF_DIR'}/psad.conf\n");
546                         printf T "%-28s%s;\n", 'FW_MSG_SEARCH', $fw_str;
547                     }
548                 } else {
549                     print T $line unless $line =~ /^\s*FW_MSG_SEARCH\s/;
550                 }
551             }
552             close T;
553             move "$config{'PSAD_CONF_DIR'}/psad.conf.tmp",
554                 "$config{'PSAD_CONF_DIR'}/psad.conf"
555                 or die "[*] Could not move ",
556                 "$config{'PSAD_CONF_DIR'}/psad.conf.tmp -> ",
557                 "$config{'PSAD_CONF_DIR'}/psad.conf: $!";
558         }
559         ### Give the admin the opportunity to set the HOME_NET variable.
560         &set_home_net("$config{'PSAD_CONF_DIR'}/psad.conf");
561
562         ### see if the admin would like to have psad send info to
563         ### DShield
564         if (&query_dshield()) {
565             &put_string('ENABLE_DSHIELD_ALERTS', 'Y',
566                 "$config{'PSAD_CONF_DIR'}/psad.conf");
567         }
568
569         ### Set the hostname
570         my $file = "$config{'PSAD_CONF_DIR'}/psad.conf";
571         &logr("[+] Setting hostname to \"$HOSTNAME\" in $file\n");
572         &set_hostname($file);
573     }
574
575     &put_string('SYSLOG_DAEMON', $syslog_str,
576         "$config{'PSAD_CONF_DIR'}/psad.conf");
577
578     if ($syslog_str ne 'ulogd') {
579         my $restarted_syslog = 0;
580         if ($syslog_str eq 'syslogd') {
581             if (-e $syslog_conf) {
582                 &append_fifo_syslog($syslog_conf);
583                 if (((system "$cmds{'killall'} -HUP syslogd 2> /dev/null")>>8) == 0) {
584                     &logr("[+] HUP signal sent to syslogd.\n");
585                     $restarted_syslog = 1;
586                 }
587             }
588         } elsif ($syslog_str eq 'rsyslogd') {
589             if (-e $syslog_conf) {
590                 &append_fifo_syslog($syslog_conf);
591                 if (((system "$cmds{'killall'} -HUP rsyslogd 2> /dev/null")>>8) == 0) {
592                     &logr("[+] HUP signal sent to rsyslogd.\n");
593                     $restarted_syslog = 1;
594                 }
595             }
596
597         } elsif ($syslog_str eq 'syslog-ng') {
598             if (-e $syslog_conf) {
599                 &append_fifo_syslog_ng($syslog_conf);
600                 if (((system "$cmds{'killall'} -HUP syslog-ng 2> /dev/null")>>8) == 0) {
601                     &logr("[+] HUP signal sent to syslog-ng.\n");
602                     $restarted_syslog = 1;
603                 }
604             }
605         } elsif ($syslog_str eq 'metalog') {
606             if (-e $syslog_conf) {
607                 &config_metalog($syslog_conf);
608                 &logr("[-] Metalog support is shaky in psad.  " .
609                     "Use at your own risk.\n");
610                 ### don't send warning about not restarting metalog daemon
611                 $restarted_syslog = 1;
612             }
613         }
614
615         unless ($restarted_syslog) {
616             &logr("[-] Could not restart any syslog daemons.\n");
617         }
618     }
619
620     if (-x $cmds{'iptables'} and not $skip_syslog_test) {
621         &logr("[+] Found iptables. Testing syslog configuration:\n");
622         ### make sure we actually see packets being logged by
623         ### the firewall.
624         if ($syslog_str ne 'ulogd') {
625             if (&test_syslog_config($syslog_str)) {
626                 &logr("[+] Successful $syslog_str reconfiguration.\n\n");
627             } else {
628                 if (&query_init_script_restart_syslog()) {
629
630                     my $restarted = 0;
631                     if ($syslog_str eq 'syslog-ng') {
632                         if (-e "$init_dir/syslog-ng") {
633                             system "$init_dir/syslog-ng restart";
634                             $restarted = 1;
635                         }
636                     } elsif ($syslog_str eq 'rsyslogd') {
637                         if (-e "$init_dir/sysklogd") {
638                             system "$init_dir/sysklogd restart";
639                             $restarted = 1;
640                         } elsif (-e "$init_dir/syslog") {
641                             system "$init_dir/syslog restart";
642                             $restarted = 1;
643                         }
644                     } else {
645                         if (-e "$init_dir/rsyslog") {
646                             system "$init_dir/rsyslog restart";
647                             $restarted = 1;
648                         }
649                     }
650                     ### test syslog config again now that we
651                     ### have restarted syslog via the init script
652                     ### instead of relying on a HUP signal to
653                     ### syslog
654                     if ($restarted) {
655                         if (&test_syslog_config($syslog_str)) {
656                             &logr("[+] Successful $syslog_str reconfiguration.\n\n");
657                         } else {
658                             &logr("[-] Unsuccessful $syslog_str reconfiguration.\n");
659                             &logr("    Consult the psad man page for the basic " .
660                                 "$syslog_str requirement to get psad to work.\n\n");
661                         }
662                     }
663                 } else {
664                     &logr("[-] Ok, hoping that psad can get packet data anyway.\n");
665                 }
666             }
667         }
668     }
669
670     ### download signatures?
671     &download_signatures() if &query_signatures();
672
673     &install_manpage('psad.8');
674     &install_manpage('psadwatchd.8');
675     &install_manpage('kmsgsd.8');
676     &install_manpage('nf2csv.1');
677
678     my $init_file = '';
679     if ($distro eq 'redhat') {
680         $init_file = 'init-scripts/psad-init.redhat';
681     } elsif ($distro eq 'fedora') {
682         $init_file = 'init-scripts/psad-init.fedora';
683     } elsif ($distro eq 'gentoo') {
684         $init_file = 'init-scripts/psad-init.gentoo';
685     } else {
686         $init_file = 'init-scripts/psad-init.generic';
687     }
688
689     if ($init_dir) {
690         &logr("[+] Copying $init_file -> ${init_dir}/$init_name\n");
691         copy $init_file, "${init_dir}/$init_name" or die "[*] Could not copy ",
692             "$init_file -> ${init_dir}/$init_name: $!";
693         &perms_ownership("${init_dir}/$init_name", 0744);
694         &enable_psad_at_boot($distro);
695     }
696
697     &logr("\n========================================================\n");
698     if ($archived_old) {
699         &logr("[+] Copies of your original configs have been made " .
700             "in: $config{'CONF_ARCHIVE_DIR'}\n");
701     }
702     if ($preserve_rv) {
703         &logr("\n[+] psad has been installed (with your original config merged).\n");
704     } else {
705         &logr("\n[+] psad has been installed.\n");
706     }
707     if ($init_dir) {
708         &logr("\n[+] To start psad, run \"${init_dir}/psad start\"\n");
709     } else {
710         &logr("\n[+] To start psad, run ${USRSBIN_DIR}/psad\"\n");
711     }
712     return;
713 }
714
715 sub import_config() {
716     open C, "< $psad_conf_file"
717         or die "[*] Could not open $psad_conf_file: $!";
718     while (<C>) {
719         next if /^\s*#/;
720         if (/^\s*(\S+)\s+(.*?)\;/) {
721             my $varname = $1;
722             my $val     = $2;
723             if ($val =~ m|/.+| and $varname =~ /^\s*(\S+)Cmd$/) {
724                 ### found a command
725                 $cmds{$1} = $val;
726             } else {
727                 $config{$varname} = $val;
728             }
729         }
730     }
731     close C;
732
733     ### resolve internal vars within variable values
734     &expand_vars();
735
736     &required_vars();
737
738     return;
739 }
740
741 sub expand_vars() {
742
743     my $has_sub_var = 1;
744