📄 install.pl
字号:
#!/usr/bin/perl -w# SafeTP install script# assumptions:# run as root# binaries already available# safetp user already created# version history:# 1.21 made root check not a state, but always performed# removed partial test# added -noninter flag for easier testing (and# *possibly* useful for unattended installs, if the# defaults are *all* correct)# 1.20 switching to interactive prompting instead of forcing# the user to edit the script itself, lots of other # general improvements# 1.11b working on a check for brace expansion functionality# fixed troublshooting reference (trouble.txt)# 1.11 added more support for keeping keys across upgrades# (the intent was never that people would re-run the install# script for an upgrade, just replace binaries.. maybe should# have an upgrade script..?)# fixed some problems with the way the test programs were run,# specifically, eliminating use of 'su' (since I usually run# with no shell for the 'safetp' user)# 1.10 rewritten in perl (was in csh), with (at least) these benefits:# - eliminated need for helper scripts (is self-contained now)# - improved config-file modification by using Perl's internal# pattern matching and string substitution mechanisms, and# ability to do all editing in-memory (eliminating error-prone# tmp1/tmp2 game)# - improved error handling# - easier maintenance, due to language superiority over csh# - other benefits of being able to define subroutines.. :)# removed stuff about -3, since it's unnecessary now# added -9 to the default options list# 1.02 corrected/improved some of the comments below# updated sftpc syntax for 1.20 sftpc, inc. -X# removed now-redundant ^D eat-extra stuff# 1.01 added ability to not simply co-opt ftp's port, for krb5 compat.# 1.00 all previous..use strict 'subs'; # disallow barewordsuse English; # mnemonics for things like EUID# subroutines (all defined & documented at end of file)sub processCommandLineOptions;sub readSavedAnswers;sub appendState;sub forgetLastAnswer;sub firstLine;sub pval;sub runDontDie;sub run;sub complainAndQuit;sub printStackTrace;sub cd;sub beginModifying;sub finishModifying;sub appendUninstall;sub appendToFile;sub prependUninstall;sub hupInetdOrAskUser;sub hupInetd;sub getAllProcesses;sub getAllProcessesInner;sub doUninstall;sub readFile;sub writeFile;sub packageCmd;sub appendUninstallCmd;sub prependUninstallCmd;sub makeSymlink;sub askStringQuestion;sub askQuestionNotSaved;sub askBooleanQuestion;sub startSection;sub endSection;sub dropRoot;sub resumeRoot;sub diagnostic;# ------------------ script-wide globals -------------# directory for storing install state files$work_dir = "/etc/safetp";# file for recording script state$state_file = "$work_dir/safetp_install_state";# name of the uninstall script commands$uninstall_file = "$work_dir/uninstall.cmds";# names of system files to modify$etc_services = "/etc/services";$etc_inetd_conf = "/etc/inetd.conf";$etc_motd = "/etc/motd";# some systems don't have this file$has_etc_motd = (-f "/etc/motd");# assume we will not be doing a dry run$dry_run = 0;# but if we do, we will use this directory to play in$drydir = "/tmp/dry";# and don't start in debug mode$debug_mode = 0;# when true, we avoid anything that would require an# interactive response$noninteractive = 0;# ----------------- preliminaries -----------------------getHostType();# do before acting on $work_dir since we might change itprocessCommandLineOptions();# test for being root (right after cmdline, since it is there# we might set $dry_run)if ($EUID != 0 && !$dry_run) { # $EUID is effective user id, 0 is user id of root die "You must be root to install the SafeTP daemon.\n";}mkdirIfNotAlready($work_dir);readSavedAnswers();# ------------------ question and answer ---------------------# now, we ask the user a bunch of questions. if the user has# already answered some of them, they won't be asked again## first tell the user what's going to happen#$message = <<EOF;Welcome to the SafeTP install script.You should already have done these things: - compiled the binaries; see compile.txt - created a user for sftpd to run as, such as "safetp" - became root to run this scriptIf you have not done these things, stop now, read install.txt,and come back later.First, I will ask you a bunch of questions. For most of them, if notall, the default answer will be fine; just hit Enter. Otherwise, entera different value and press Enter.If you make a mistake, use ^C to quit the script. The answers you havegiven so far, including default answers, are stored in$state_file; delete this file to start over.After the questions I will begin the install itself. If something goeswrong I will stop. When you fix whatever is wrong, simply re-start thescript, and it will resume by trying the last thing that failed.EOF$proceed = askBooleanQuestion("$message\nAre you ready to begin? ", 1);if (!$proceed) { forgetLastAnswer(); exit();}## this is the user on whose behalf the sftpd daemon will run# (and the user that will have ownership of the daemon files)#$daemon_user = askStringQuestion( "What is the name of the user that\nsftpd should run as? ", $dry_run? firstLine(`whoami`) : "safetp");if (!getpwnam($daemon_user)) { print("I don't see user `$daemon_user' in the passwd file; that should\n", "be fixed first. Perhaps you should say 'useradd $daemon_user'.\n"); forgetLastAnswer(); exit();}## grab stuff from passwd file#($_, $_, $daemon_uid, $daemon_gid, $_, $_, $_, $daemon_home) = getpwnam($daemon_user);($daemon_group) = getgrgid($daemon_gid);if (!getgrnam($daemon_group)) { print("I don't see group `$daemon_group' in the group file; that should\n", "be fixed first.\n"); forgetLastAnswer(); exit();}## This string will appear in the DSA public key, and ftp users# will see it upon connection. The name should be something# users will recognize. Comment this out to get the normal# interactive prompt from 'makekeys'.#$hostname = getFQDN();$branding_string = askStringQuestion( "Your DSA public key will include a descriptive name, called its\n" . "\"brand\", that users will see when they connect to your server.\n" . "This string should be something users will recognize. What brand\n" . "would you like? ", "SafeTP at $hostname");## this is where the pre-built binaries are. the needed binaries are:# sftpc - SafeTP unix client# sftpd - SafeTP daemon itself# makekeys - program to generate ElGamal (client) and DSA (server) keys# viewkey - program to base64 a binary key file# addent - entropy-gathering tool#$binary_source = askStringQuestion( "Where are the SafeTP binaries, such as 'sftpd',\nlocated now? ", ".");if (! -f "${binary_source}/sftpd") { print("I don't see 'sftpd' in '$binary_source'.\n"); forgetLastAnswer(); exit();}## this is where the binaries will be placed, where inetd# will find them; ideally, this is on the local machine#$binary_dest = askStringQuestion( "Where should the SafeTP binaries be placed for ongoing use? Since\n" . "many network file system protocols, such as NFS, are insecure, this\n" . "should be on the local machine which will run the SafeTP daemon.\n", $dry_run? $drydir : $daemon_home);## this is where symbolic links to binaries that users will# typically run will be placed#$user_binary_dest = askStringQuestion( "For user convenience, I can put symlinks to the SafeTP binaries in some\n" . "conventional place. Where should I put these symlinks?\n", $dry_run? "$drydir/links" : "/usr/local/bin");## this is where the server keys will be placed (actually, they# will go into a DSA subdirectory of this directory); this# *really must* be on the local machine (otherwise the private# key is vulnerable while being fetched over the network)#$key_dest = askStringQuestion( "Where should I put the DSA server keys? It is imperative that the\n" . "directory specified here be on the local machine, because if the\n" . "server keys are sniffed then SafeTP is compromised. (Note also that\n" . "you need to think carefully about how/whether these keys are part\n" . "of any automatic backup procedures.)\n", $dry_run? $drydir : $daemon_home);## raw (unencrypted) FTP service will be installed on this port#$raw_ftp_port = askStringQuestion( "To which port should I move the existing FTP daemon? Since SafeTP uses\n" . "this daemon, you can't just remove it entirely. ", 351);## this is the port sftpd will listen to# (if it is desired to install sftpd on a port other than 21,# we recommend 353; the Windows client depends on it using 353)#$sftpd_port = askStringQuestion( "Which port should SafeTP listen to? Normally you should make SafeTP\n" . "listen to port 21, the default FTP port. However, if for some reason\n" . "you want it to listen to a different port, 353 is the recommended\n" . "alternative. ", 21);## command-line switches to sftpd. common ones are:# -pN listen for incoming connections on port N# -fN contact ftpd on port N# -s use stdin as control connection (for use with inetd)# -d1 log diagnostic (debugging) messages# -9 disallow RFC 959 (unencrypted) connections# -y<directory> specify working directory (default is cwd)# -o send debug output to log file instead of syslog# -l<filename> specify logging file (use with -o)# -3 disable the 3rd-party transfer optimization# (additional options available: "sftpd -h")# (additional docs: http://safetp.cs.berkeley.edu/sftpd.html)#$sftpd_options = "-f${raw_ftp_port} -s -y${key_dest}";if (!askBooleanQuestion( "Do you want SafeTP to accept unencrypted connections as well as\n" . "encrypted connections? It makes the transition path easier for\n" . "users but also eliminates the forcing function for them to switch\n" . "to using SafeTP. Accept unencrypted? ", 0)) { $sftpd_options .= " -9";}$extra_options = askStringQuestion( "The current argument string to sftpd is:\n" . " sftpd $sftpd_options\n" . "You can enter additional arguments here if you want:\n", "");$sftpd_options .= " " . $extra_options;## amount of entropy to gather; the default (1024) requires# approximately 200 keystrokes; reduce to suit keymashing# tolerance... (setting it to 0 will effectively add only# the current date/time as the entropy, which is insecure)#$bits_of_entropy = 1024;## length of DSA keys (recommended: 1024)#$dsa_key_len = 1024;## controls whether a full test is performed post-install#$do_full_test = askBooleanQuestion( "After installing, do a full (interactive) test? ", $noninteractive? 0 : 1);## Additional parameters to sftpc during the full test (if it# is done at all); the one that makes most sense here, if any,# is "-n", which does protocol negotiation only. This should be# used after the install works right on one machine, with a real# full test, and it is desired to install on other machines# noninteractively.# -X accept new key silently#$sftpc_full_extras = "-X";#$sftpc_full_extras = "-X -n";## if this is 1, we edit /etc/motd to tell users that# a secure FTP server has been installed (some systems# overwrite /etc/motd during boot...)#if ($has_etc_motd) { $do_edit_motd = askBooleanQuestion( "After install, should I add a blurb to /etc/motd telling users\n" . "that SafeTP is installed? ", 1);}else { # don't modify it if it's not there $do_edit_motd = 0;}## some sites like to have admins put their login name in a# comment next to config changes; the following string will# be appended to comment strings inserted#$comment_extra = askStringQuestion( "When I modify system files, I will tag the modifications with the name\n" . "of the admin responsible. What tag should I use?\n", $dry_run? "dry_run" : "");## how to retrieve a list of all processes; only set this if# you have reason to believe that getAllProcesses() (at end# of file) isn't working##$allProcsCmd = "ps aux";# ------------ less-used configuration options ----------------## permissions for various files; see install.txt for summary# of minimum and convenient permissions; the defaults here# are the "convenient" permissions#$binary_dir_perm = "755";$key_dir_perm = "755";$binary_perm = "755";$seed_perm = "600";$key_subdir_perm = "711";$privkey_perm = "600";$pubkey_perm = "644";$pubkeytxt_perm = "644";## length of elgamal keys (used for testing only)#$elgamal_key_len = 1024;## grab a datestamp and timestamp#$datestamp = firstLine(`date +%x`); die "date failed" if $?;$timestamp = firstLine(`date +%Y.%m.%d.%H.%M.%S`); die "date failed" if $?; # ------------- other globals ---------------------## grab two temp file names#$tmp1 = "/tmp/safetp.$$.tmp1";#$tmp2 = "/tmp/safetp.$$.tmp2"; # no longer used..# list of binaries to copy@bins = qw(sftpc sftpd makekeys viewkey addent);# (part of) comment to append to modified lines of config files$comment = "by SafeTP install ${datestamp} ${comment_extra}";# directory where server keys, and client test keys, will be created$ENV{SAFETP_CONFIG} = $key_dest;# A quick note to those who maintain this file:# It's important to keep in mind that restarting the# install script causes different parts to run with# different ${timestamp} values. This is why, for example,# an atomic section (like modifying /etc/services) must# go all the way to the point where the uninstall info is# written; breaking it in the middle could break the# uninstall script.# --------------- installation proper ---------------------if (startSection()) { # start an uninstall script commands file if (-e "$uninstall_file") { # move the old one first run("mv $uninstall_file ${uninstall_file}.${timestamp}"); } appendUninstall("# this is somewhere in $uninstall_file"); # the uninstall script should remove the state file, even if the # install only partially succeeded, on the presumption that a # re-install attempt will follow appendUninstallCmd("removing install state", "rm $state_file"); endSection("started the uninstall file");}if (startSection()) { # copy the required binaries foreach $bin (@bins) { run("cp ${binary_source}/$bin $binary_dest"); # write uninstall info prependUninstallCmd("removing $bin", "rm ${binary_dest}/$bin"); } endSection("copied the binaries to $binary_dest");}if (startSection()) { # set ownership and permissions run("chmod $binary_dir_perm $binary_dest", "chown $daemon_user $binary_dest"); foreach $bin (@bins) { run("chmod $binary_perm ${binary_dest}/$bin", "chown $daemon_user ${binary_dest}/$bin", "chgrp $daemon_group ${binary_dest}/$bin"); } endSection("set the permissions on the binaries");}if (startSection()) { # as a convenience during debugging of this script, permit # an existing randomSeed to be used if (-e "$key_dest/randomSeed") { print("${key_dest}/randomSeed already exists, skipping entropy-gathering...\n"); } elsif ($noninteractive && $dry_run) { # *only* do this for a dry run; a real install must not be # allowed to proceed without entropy! # # makekeys below will generate the randomSeed file, and # it won't prompt for entropy because of 3rd arg print("skipping addent because it's noninteractive dry run\n"); } else { # gather entropy cd("$key_dest"); run("${binary_dest}/addent $bits_of_entropy"); # (no longer have to eat keystrokes, because addent takes care of it) } # write uninstall info #prependUninstallCmd("removing seed", "rm ${key_dest}/randomSeed"); # like the keys, leave the seed endSection("gathered entropy");}if (startSection()) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -