📄 install.pl
字号:
# permit use of existing keys to keep keys across upgrades if (-e "${key_dest}/DSA") { print "DSA keys already exist, skipping key generation...\n" } else { # generate keys (client also, for testing, to share randomSeed) cd("$key_dest"); $cmd = "${binary_dest}/makekeys $elgamal_key_len $dsa_key_len 0"; if ($branding_string) { print "Supplying branding string: $branding_string\n"; run("echo \"$branding_string\" | $cmd"); } else { run("$cmd"); } } # write uninstall info #prependUninstallCmd("removing keys", "rm -rf ${key_dest}/DSA ${key_dest}/ElGamal"); #prependUninstallCmd("removing cached host keys", "rm -rf ${key_dest}/keys"); # we don't want to delete keys, esp. server keys, since we want people to use # the same server keys across an upgrade.. if someone truly wants to delete # the keys, they'll have to do it themselves endSection("generated keys");}if (startSection()) { # create a text file for users to view the public key cd("$key_dest"); run("${binary_dest}/viewkey DSA/public.key > " . "${key_dest}/DSA/public.key.txt"); # don't bother with uninstall info because the above # deletion of ${key_dest}/DSA will get public.key.txt endSection("created public.key.txt");}if (startSection()) { # set permissions for key files and directories run("chmod $key_dir_perm $key_dest", "chmod $seed_perm ${key_dest}/randomSeed", "chmod $key_subdir_perm ${key_dest}/DSA", "chmod $privkey_perm ${key_dest}/DSA/private.key", "chmod $pubkey_perm ${key_dest}/DSA/public.key", "chmod $pubkeytxt_perm ${key_dest}/DSA/public.key.txt", "chmod 700 ${key_dest}/ElGamal", "chmod 600 ${key_dest}/ElGamal/*", "chown -R $daemon_user $key_dest", "chgrp -R $daemon_group $key_dest"); # no explicit uninstall necessary because these files are deleted # during other stages' uninstalls endSection("set the permissions on the keys");}# there used to be a "partial" test here, but I decided it wasn't# worth the hassle of making it work reliably, and its diagnostic# value was not very muchif (startSection()) { # backup, read beginModifying($etc_services); # append a block of section break comments @file = (@file, "#\n", "# added $comment\n", "#\n"); # put a safetp line if ($sftpd_port == 21) { # modify existing ftp line my $numSubsts = 0; # count number of times we substitute foreach $line (@file) { # in this regexp, $1 is the required part (ftp...21/tcp) and # $2 is the rest of the line (other service names and possibly # a comment) $numSubsts += $line =~ s[^(ftp\s+21/tcp)(.*)] [$1\t\tsafetp$2\t\t# safetp added $comment]; } if ($numSubsts != 1) { # too many, or too few, substitutions print("error: While computing the new $fn, I made $numSubsts\n", " changes, instead of the expected 1 change. Thus, I\n", " screwed up, most likely.\n\n"); if (open(TMP1, ">$tmp1")) { print TMP1 @file; close(TMP1); print(" I wrote the botched $fn to $tmp1,\n", " so you can inspect it.\n\n"); } else { print(" I tried to write the botched $fn to $tmp1,\n", " but failed to open $tmp1...?\n\n"); } print(" See if you can figure out what went wrong, and modify\n", " $fn to try to fix it, then re-run $0.\n"); exit(4); } } else { # add a new line for safetp @file = (@file, "safetp\t\t${sftpd_port}/tcp\t\t# added $comment\n"); } # add the raw-ftp line @file = (@file, "raw-ftp\t\t${raw_ftp_port}/tcp\t\t# added $comment\n"); # write computed contents to /etc/services finishModifying(); endSection("modified /etc/services");}if (startSection()) { # backup, read beginModifying($etc_inetd_conf); # grab the current ftp line @ftp_lines = grep /^ftp\s/, @file; if (@ftp_lines > 1) { # this shouldn't happen unless /etc/inetd.conf has mulitple entries for ftp! print("error: I am confused as to which of these is the ftp line:\n"); printIndented(@ftp_lines); print("If you (temporarily) comment-out all but one, I will use that one.\n"); exit(2); } # check that there is an ftp line if (@ftp_lines == 0) { print("error: I don't see any ftp lines in $fn. Please add a line for\n", " the ftp service so I can move it around.\n"); exit(2); } # turn off existing ftp if it would conflict with sftpd if ($sftpd_port == 21) { # here, comment is put in front, to emphasize that it must be removed # completely for this line to be re-activated my $numSubsts = 0; foreach $line (@file) { $numSubsts += $line =~ s[(^ftp\s.*)] [# (removed ${comment}) $1]; } if ($numSubsts != 1) { die "I goofed modifying $fn"; } } # going to append, so insert a section break # this is the only comment that appears for these lines, because inetd.conf # doesn't allow comments on the same line as a non-comment line # append a block of section break comments @file = (@file, "#\n", "# added $comment\n", "#\n"); # modify existing ftp line (currently in $ftp_lines[0]) @file = (@file, "raw-${ftp_lines[0]}"); # add the safetp line @file = (@file, "safetp\tstream\ttcp\tnowait\t$daemon_user\t" . "${binary_dest}/sftpd sftpd ${sftpd_options}\n"); # write to /etc/inetd.conf finishModifying(); endSection("modified /etc/inetd.conf");}if (startSection()) { # HUP it; this causes it to re-read /etc/inetd.conf hupInetdOrAskUser(); # write uninstall info; note that, for this, we *append* rather # than prepend, because hup'ing inetd should wait until everything # else is back in order appendUninstall("hupInetdOrAskUser();"); endSection("sent HUP signal to inetd");}if (startSection()) { if ($do_full_test == 1) { # temporarily switch to $daemon_user dropRoot($daemon_uid, $daemon_gid); # run sftpc cd("/tmp"); # run from /tmp to avoid cwd=cwd problem askQuestionNotSaved( "Instructions: I'm about to start sftpc so you can test it.\n" . "You need to give four responses:\n" . " username: any valid user name on this system\n" . " password: the corresponding password\n" . " sftpc> test (at first sftpc prompt)\n" . " sftpc> quit (at second sftpc prompt)\n" . "When ready, hit Enter: ", ""); my $sftpc_cmd = "${binary_dest}/sftpc ${sftpc_full_extras} " . "localhost ${sftpd_port}"; if (!runDontDie($sftpc_cmd)) { print("\n", "error: The full test failed. Since the protocol test\n", " above already succeeded, the error here is most\n", " likely something wrong with inetd.\n", "\n", " sftpc command line used:\n", " env SAFETP_CONFIG=" . $ENV{SAFETP_CONFIG} . "\n", " ${sftpc_cmd}\n", "\n", " See http://safetp.cs.berkeley.edu/trouble.html\n", " for some ideas on how to fix this.\n"); exit(2); } # restore uid/gid resumeRoot(); print "full test: SUCCESS!\n"; } endSection("did full test");}if (startSection()) { # link to the unix client makeSymlink("${binary_dest}/sftpc", "${user_binary_dest}/sftpc"); # links to keys and entropy utilities; I make the symlink name # include "sftpc-" to avoid executable namespace pollution makeSymlink("${binary_dest}/makekeys", "${user_binary_dest}/sftpc-makekeys"); makeSymlink("${binary_dest}/addent", "${user_binary_dest}/sftpc-addent"); # uninstall info prependUninstallCmd("removing client binary symlinks", "rm -f ${user_binary_dest}/sftpc " . "${user_binary_dest}/sftpc-makekeys " . "${user_binary_dest}/sftpc-addent"); endSection("created symlinks");}if (startSection()) { if ($do_edit_motd) { # backup, read beginModifying($etc_motd); # append banner, including pointer to public key text file # (syntax: "foo" x 3 yields "foofoofoo") @file = (@file, "\n", " $datestamp SafeTP installed. See http://safetp.cs.berkeley.edu\n", " " . " " x length($datestamp) . " Public key: ${key_dest}/DSA/public.key.txt\n"); # write changed file to disk finishModifying(); } endSection("edited /etc/motd");}if (startSection()) { print <<EOF; -------------------------------------------------- -- The SafeTP install has finished! Excellent! -- -------------------------------------------------- Summary of changes: - binaries were copied to $binary_dest - symlinks were created in $user_binary_dest - keys were created in $key_dest - $etc_services: - added 'safetp' as port $sftpd_port - added 'raw-ftp' as port $raw_ftp_port - $etc_inetd_conf: - added 'safetp' -> 'sftpd' - removed 'ftp' -> 'ftpd' (if safetp port is 21) - added 'raw-ftp' -> 'ftpd' - $etc_motd: optionally added a blurb - put backups and diffs of modified files in $work_dir - created $uninstall_file Executing '$0 -u' will un-install SafeTP.EOF endSection("printed final success message");}else { # only executed if we skipped final section, which only happens # if someone re-runs install script after it's finished print("This is the end of the install script. If you need to\n", "re-run this install script, remove the file ${state_file}.\n");}# end of install script!exit(0);# ----------------- helper subroutines ----------------------# interpret -u to mean uninstall, die on any othersub processCommandLineOptions { my $op; while ($op = shift @ARGV) { if ($op eq "-u") { doUninstall(); exit(); } elsif ($op eq "-hostTest") { print("Here is my guess at a complete process list:\n"); printIndented(getAllProcesses()); hupInetdOrAskUser(); print("I think the host type is: $hostType\n"); printf("I think the fqdn is %s\n", getFQDN()); exit(); } elsif ($op eq "-debug") { $debug_mode = 1; diagnostic("debug mode is on"); } elsif ($op eq "-test") { # setup things for dry run $dry_run = 1; $work_dir = "$drydir/safetp"; $state_file = "$work_dir/safetp_install_state"; $uninstall_file = "$work_dir/uninstall.cmds"; mkdirIfNotAlready($drydir); mkdirIfNotAlready("$drydir/links"); $etc_services = dryRunFile($etc_services, "$drydir/services"); $etc_inetd_conf = dryRunFile($etc_inetd_conf, "$drydir/inetd.conf"); if ($has_etc_motd) { $etc_motd = dryRunFile($etc_motd, "$drydir/motd"); } } elsif ($op eq "-noninter") { $noninteractive = 1; } else { print("Supported options:\n", " (no option) - run the installer\n", " -u - run the uninstaller\n", " -hostTest - test some system-specific stuff\n", " -debug - turn on debugging diagnostics\n", " -test - dry-run test install as non-root\n", " -noninter - use all the defaults, don't do final test\n", ); exit(1); } }}# read in state from a prior run of this script, so we can resume# where we left off; also, arrange to keep appending new statesub readSavedAnswers { if (!open(STATE, "<$state_file")) { # this is the first run @savedAnswers = (); diagnostic("this is the first run"); } else { # grab what we have @savedAnswers = <STATE>; # strip newlines from all the answers for ($i = 0; $i < @savedAnswers; $i++) { chomp($savedAnswers[$i]); } diagnostic("read $i saved answers"); close(STATE) or die; }}# write a single line of state to the state filesub appendState { my ($line) = @_; appendToFile($line, "$state_file");}# remove last saved answersub forgetLastAnswer { diagnostic("forgetting last answer"); @answers = readFile($state_file); pop(@answers); writeFile($state_file, @answers);}# return the first line of a (possibly) multi-line value,# throwing away the newline as wellsub firstLine { my ($line) = @_; # grab first line, with \n $line =~ s/\n//; # throw away \n return $line;}# print the value of a variable, given a string containing its namesub pval { my ($name) = @_; print "$name = " . $$name . "\n"; # $$name means, map 'name' to its scalar value (a string), then map # that string to *its* scalar value}# run a command (or list of commands), and if it fails, return false# either way, set global $lastCmdTried to command triedsub runDontDie { my $cmd; while ($cmd = shift @_) { $lastCmdTried = $cmd; print("executing: $cmd\n"); if (system($cmd) != 0) { # error return 0; } } return 1;}# run a command (or list of commands), and if it fails, print a message and exitsub run { if (!runDontDie(@_)) { complainAndQuit($lastCmdTried); }}# print a message about what went wrong, and print the call stack toosub complainAndQuit { my ($context) = @_; print("error: "); printStackTrace(1); print("this failed:\n", " $context\n", "Try to correct the problem, and re-run $0\n"); exit(4);}# print the stack trace (except the last n frames)sub printStackTrace { my ($n) = @_; my @info; my $outputLine = 1; while (@info = caller($n++)) { if ($outputLine++ == 1) { print("At "); } else { print(", called from\n "); } print("line $info[2] of $info[1]"); } print(":\n");}# change to a directory; require that it succeedsub cd { my ($dir) = @_; chdir("$dir") or complainAndQuit("chdir $dir");}# begin modifying $fn; sets these globals:# $fn - file name of current file being modified# $oldfn - name of file before modifications# @file - array of file's contents, one line per array entrysub beginModifying { ($fn) = @_; # fn is global
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -