X-Git-Url: http://andersk.mit.edu/gitweb/gssapi-openssh.git/blobdiff_plain/70d921fc27cbd701def679bcec9c4a06fce6a8bd..34e7131dc115018ad0decd69dda3b8a5b7ef0c4a:/setup/setup-openssh.pl diff --git a/setup/setup-openssh.pl b/setup/setup-openssh.pl index bc518bb..a2c65fc 100644 --- a/setup/setup-openssh.pl +++ b/setup/setup-openssh.pl @@ -24,7 +24,7 @@ $gptpath = $ENV{GPT_LOCATION}; $gpath = $ENV{GLOBUS_LOCATION}; if (!defined($gpath)) { - die "GLOBUS_LOCATION needs to be set before running this script" + exitDie("ERROR: GLOBUS_LOCATION needs to be set before running this script!\n"); } # @@ -97,82 +97,58 @@ my $keyfiles = { # to verify correct args by using anon subs in various places. # -my($interactive, $force, $verbose); +my($prompt, $force, $verbose); + +$prompt = 1; +$verbose = 0; GetOptions( - 'interactive!' => \$interactive, + 'prompt!' => \$prompt, 'force' => \$force, 'verbose' => \$verbose, ) or pod2usage(2); # -# main execution. This should find its way into a subroutine at some future -# point. +# miscellaneous initialization functions # -print "$myname: Configuring package 'gsi_openssh'...\n"; -print "---------------------------------------------------------------------\n"; -print "Hi, I'm the setup script for the gsi_openssh package! I will create\n"; -print "a number of configuration files based on your local system setup. I\n"; -print "will also attempt to copy or create a number of SSH key pairs for\n"; -print "this machine.\n"; -print "\n"; -print "(Loosely, if I find a pair of host keys in /etc/ssh, I will copy them\n"; -print "into \$GLOBUS_LOCATION/etc/ssh. Otherwise, I will generate them for\n"; -print "you.)\n"; -print "\n"; - -if ( isForced() ) -{ - print "WARNING:\n"; - print "\n"; - print " Using the '-force' flag will cause all gsi_openssh_setup files to\n"; - print " be removed and replaced by new versions! Backup any critical\n"; - print " SSH configuration files before you choose to continue!\n"; - print "\n"; -} - -$response = query_boolean("Do you wish to continue with the setup package?","y"); -if ($response eq "n") -{ - print "\n"; - print "Exiting gsi_openssh setup.\n"; +setPrivilegeSeparation(0); - exit 0; -} +# +# main execution. This should find its way into a subroutine at some future +# point. +# -print "\n"; +debug0("Configuring gsi_openssh\n"); +debug0("------------------------------------------------------------\n"); +debug0("Executing...\n"); makeConfDir(); +copyPRNGFile(); $keyhash = determineKeys(); runKeyGen($keyhash->{gen}); -copyKeyFiles($keyhash->{copy}); +linkKeyFiles($keyhash->{link}); copyConfigFiles(); -copyPRNGFile(); my $metadata = new Grid::GPT::Setup(package_name => "gsi_openssh_setup"); $metadata->finish(); -print "\n"; -print "Additional Notes:\n"; -print "\n"; -print " o I see that you have your GLOBUS_LOCATION environmental variable\n"; -print " set to:\n"; -print "\n"; -print " \t\"$gpath\"\n"; -print "\n"; -print " Remember to keep this variable set (correctly) when you want to\n"; -print " use the executables that came with this package.\n"; -print "\n"; -print " After that you may run, e.g.:\n"; -print "\n"; -print " \t\$ . \$GLOBUS_LOCATION/etc/globus-user-env.sh\n"; -print "\n"; -print " to prepare your environment for running the gsi_openssh\n"; -print " executables.\n"; -print "---------------------------------------------------------------------\n"; -print "$myname: Finished configuring package 'gsi_openssh'.\n"; +debug0("\n"); +debug0("Notes:\n\n"); + +if ( getPrivilegeSeparation() ) +{ + debug0(" o Privilege separation is on.\n"); +} +elsif ( !getPrivilegeSeparation() ) +{ + debug0(" o Privilege separation is off.\n"); +} + +debug0(" o GSI-OpenSSH website is .\n"); +debug0("------------------------------------------------------------\n"); +debug0("Finished configuring gsi_openssh.\n"); exit; @@ -208,7 +184,7 @@ sub initPRNGHash( ) addPRNGCommand("\@PROG_IPCS\@", "ipcs"); addPRNGCommand("\@PROG_TAIL\@", "tail"); - print "Determining paths for PRNG commands...\n"; + debug1("Determining paths for PRNG commands...\n"); $paths = determinePRNGPaths(); @@ -271,15 +247,15 @@ sub copyPRNGFile my($mode, $uid, $gid); my($data); - if ( isPresent("/dev/random") && !isForced() ) + if ( isPresent("$sysconfdir/ssh_prng_cmds") && !isForced() ) { - printf("/dev/random found and not forced. Not installing ssh_prng_cmds...\n"); + debug1("ssh_prng_cmds found and not forced. Not installing ssh_prng_cmds...\n"); return; } initPRNGHash(); - print "Fixing paths in ssh_prng_cmds...\n"; + debug1("Fixing paths in ssh_prng_cmds...\n"); $fileInput = "$setupdir/ssh_prng_cmds.in"; $fileOutput = "$sysconfdir/ssh_prng_cmds"; @@ -290,7 +266,7 @@ sub copyPRNGFile if ( !isReadable($fileInput) ) { - printf("Cannot read $fileInput... skipping.\n"); + debug1("Cannot read $fileInput... skipping.\n"); return; } @@ -450,22 +426,22 @@ sub findExecutable return "undef"; } -### copyKeyFiles( $copylist ) +### linkKeyFiles( $linklist ) # -# given an array of keys to copy, copy both the key and its public variant into +# given an array of keys to link, link both the key and its public variant into # the gsi-openssh configuration directory. # -sub copyKeyFiles +sub linkKeyFiles { - my($copylist) = @_; + my($linklist) = @_; my($regex, $basename); - if (@$copylist) + if (@$linklist) { - print "Copying ssh host keys...\n"; + debug1("Linking ssh host keys...\n"); - for my $f (@$copylist) + for my $f (@$linklist) { $f =~ s:/+:/:g; @@ -474,8 +450,8 @@ sub copyKeyFiles $keyfile = "$f"; $pubkeyfile = "$f.pub"; - copyFile("$localsshdir/$keyfile", "$sysconfdir/$keyfile"); - copyFile("$localsshdir/$pubkeyfile", "$sysconfdir/$pubkeyfile"); + linkFile("$localsshdir/$keyfile", "$sysconfdir/$keyfile"); + linkFile("$localsshdir/$pubkeyfile", "$sysconfdir/$pubkeyfile"); } } } @@ -590,10 +566,11 @@ sub makeConfDir return; } - die("${sysconfdir} already exists and is not a directory!\n"); + debug1("${sysconfdir} already exists and is not a directory!\n"); + exit; } - print "Could not find ${sysconfdir} directory... creating.\n"; + debug1("Could not find ${sysconfdir} directory... creating.\n"); action("mkdir -p $sysconfdir"); return; @@ -619,10 +596,10 @@ sub determineKeys $keyhash = {}; $keyhash->{gen} = []; # a list of keytypes to generate - $keyhash->{copy} = []; # a list of files to copy from the + $keyhash->{link} = []; # a list of files to link $genlist = $keyhash->{gen}; - $copylist = $keyhash->{copy}; + $linklist = $keyhash->{link}; # # loop over our keytypes and determine what we need to do for each of them @@ -656,7 +633,7 @@ sub determineKeys } # - # if we can find a copy of the keys in /etc/ssh, we'll copy them to the user's + # if we can find a copy of the keys in /etc/ssh, we'll link them to the user's # globus location # @@ -665,7 +642,7 @@ sub determineKeys if ( isReadable($mainkeyfile) && isReadable($mainpubkeyfile) ) { - push(@$copylist, $basekeyfile); + push(@$linklist, $basekeyfile); $count++; next; } @@ -694,7 +671,7 @@ sub runKeyGen if (@$gen_keys && -x $keygen) { - print "Generating ssh host keys...\n"; + debug1("Generating ssh host keys...\n"); for my $k (@$gen_keys) { @@ -721,8 +698,9 @@ sub copySSHDConfigFile my($fileInput, $fileOutput); my($mode, $uid, $gid); my($line, $newline); + my($privsep_enabled); - print "Fixing paths in sshd_config...\n"; + debug1("Fixing paths in sshd_config...\n"); $fileInput = "$setupdir/sshd_config.in"; $fileOutput = "$sysconfdir/sshd_config"; @@ -733,7 +711,7 @@ sub copySSHDConfigFile if ( !isReadable($fileInput) ) { - printf("Cannot read $fileInput... skipping.\n"); + debug1("Cannot read $fileInput... skipping.\n"); return; } @@ -746,6 +724,28 @@ sub copySSHDConfigFile return; } + # + # check to see whether we should enable privilege separation + # + + if ( userExists("sshd") && ( -d "/var/empty" ) && ( getOwnerID("/var/empty") eq 0 ) ) + { + setPrivilegeSeparation(1); + } + else + { + setPrivilegeSeparation(0); + } + + if ( getPrivilegeSeparation() ) + { + $privsep_enabled = "yes"; + } + else + { + $privsep_enabled = "no"; + } + # # Grab the current mode/uid/gid for use later # @@ -758,36 +758,34 @@ sub copySSHDConfigFile # Open the files for reading and writing, and loop over the input's contents # - open(IN, "<$fileInput") || die ("$0: input file $fileInput missing!\n"); - open(OUT, ">$fileOutput") || die ("$0: unable to open output file $fileOutput!\n"); + $data = readFile($fileInput); - while () - { - # - # sorry for the whacky regex, but i need to verify a whole line - # + # # + # # alter the PidFile config + # # + # + # $text = "PidFile\t$gpath/var/sshd.pid"; + # $data =~ s:^[\s|#]*PidFile.*$:$text:gm; - $line = $_; - if ( $line =~ /^\s*Subsystem\s+sftp\s+\S+\s*$/ ) - { - $line = "Subsystem\tsftp\t$gpath/libexec/sftp-server\n"; - $line =~ s:/+:/:g; - } - elsif ( $line =~ /^\s*PidFile.*$/ ) - { - $line = "PidFile\t$gpath/var/sshd.pid\n"; - $line =~ s:/+:/:g; - } - else - { - # do nothing - } + # + # set the sftp directive + # - print OUT "$line"; - } # while + $text = "Subsystem\tsftp\t$gpath/libexec/sftp-server"; + $data =~ s:^[\s|#]*Subsystem\s+sftp\s+.*$:$text:gm; - close(OUT); - close(IN); + # + # set the privilege separation directive + # + + $text = "UsePrivilegeSeparation\t${privsep_enabled}"; + $data =~ s:^[\s|#]*UsePrivilegeSeparation.*$:$text:gm; + + # + # dump the modified output to the config file + # + + writeFile($fileOutput, $data); # # An attempt to revert the new file back to the original file's @@ -800,6 +798,28 @@ sub copySSHDConfigFile return 0; } +### setPrivilegeSeparation( $value ) +# +# set the privilege separation variable to $value +# + +sub setPrivilegeSeparation +{ + my($value) = @_; + + $privsep = $value; +} + +### getPrivilegeSeparation( ) +# +# return the value of the privilege separation variable +# + +sub getPrivilegeSeparation +{ + return $privsep; +} + ### prepareFileWrite( $file ) # # test $file to prepare for writing to it. @@ -811,25 +831,25 @@ sub prepareFileWrite if ( isPresent($file) ) { - printf("$file already exists... "); + debug1("$file already exists... "); if ( isForced() ) { if ( isWritable($file) ) { - printf("removing.\n"); + debug1("removing.\n"); action("rm $file"); return 1; } else { - printf("not writable -- skipping.\n"); + debug1("not writable -- skipping.\n"); return 0; } } else { - printf("skipping.\n"); + debug1("skipping.\n"); return 0; } } @@ -856,7 +876,7 @@ sub copyConfigFiles # do straight copies of the ssh_config and moduli files. # - printf("Copying ssh_config and moduli to their proper location...\n"); + debug1("Copying ssh_config and moduli to their proper location...\n"); copyFile("$setupdir/ssh_config", "$sysconfdir/ssh_config"); copyFile("$setupdir/moduli", "$sysconfdir/moduli"); @@ -868,6 +888,29 @@ sub copyConfigFiles copySXXScript("$setupdir/SXXsshd.in", "$sbindir/SXXsshd"); } +### linkFile( $src, $dest ) +# +# create a symbolic link from $src to $dest. +# + +sub linkFile +{ + my($src, $dest) = @_; + + if ( !isReadable($src) ) + { + debug1("$src is not readable... not creating $dest.\n"); + return; + } + + if ( !prepareFileWrite($dest) ) + { + return; + } + + action("ln -s $src $dest"); +} + ### copyFile( $src, $dest ) # # copy the file pointed to by $src to the location specified by $dest. in the @@ -880,7 +923,7 @@ sub copyFile if ( !isReadable($src) ) { - printf("$src is not readable... not creating $dest.\n"); + debug1("$src is not readable... not creating $dest.\n"); return; } @@ -901,10 +944,11 @@ sub copyFile sub copySXXScript { my($in, $out) = @_; + my($tmpgpath); if ( !isReadable($in) ) { - printf("$in is not readable... not creating $out.\n"); + debug1("$in is not readable... not creating $out.\n"); return; } @@ -913,8 +957,20 @@ sub copySXXScript return; } + # + # clean up any junk in the globus path variable + # + + $tmpgpath = $gpath; + $tmpgpath =~ s:/+:/:g; + $tmpgpath =~ s:([^/]+)/$:\1:g; + + # + # read in the script, substitute globus location, then write it back out + # + $data = readFile($in); - $data =~ s|\@GLOBUS_LOCATION\@|$gpath|g; + $data =~ s|\@GLOBUS_LOCATION\@|$tmpgpath|g; writeFile($out, $data); action("chmod 755 $out"); } @@ -929,7 +985,7 @@ sub readFile my($filename) = @_; my($data); - open(IN, "$filename") || die "Can't open '$filename': $!"; + open(IN, "$filename") || exitDie("ERROR: Can't open '$filename': $!\n"); $/ = undef; $data = ; $/ = "\n"; @@ -954,7 +1010,7 @@ sub writeFile if ( !defined($filename) || (length($filename) lt 1) ) { - die "Filename is undefined"; + exitDie("ERROR: Filename is undefined!\n"); } # @@ -975,6 +1031,47 @@ sub writeFile close(OUT); } +### debug1( $arg1, $arg2 ) +# +# Print out a debugging message at level 1. +# + +sub debug1 +{ + debug(string => \@_, level => 1); +} + +### debug0( $arg1, $arg2 ) +# +# Print out a debugging message at level 0. +# + +sub debug0 +{ + debug(string => \@_, level => 0); +} + +### debug( string => $string, level => $level ) +# +# Print out debugging messages at various levels. Feel free to use debugN() directly +# which in turn calls this subroutine. +# + +sub debug +{ + my %args = @_; + + if (!defined($args{'level'})) + { + $args{'level'} = 0; + } + + if ($verbose >= $args{'level'}) + { + printf(@{$args{'string'}}); + } +} + ### action( $command ) # # run $command within a proper system() command. @@ -984,16 +1081,30 @@ sub action { my($command) = @_; - printf "$command\n"; + debug1("$command\n"); - my $result = system("LD_LIBRARY_PATH=\"$gpath/lib:\$LD_LIBRARY_PATH\"; $command 2>&1"); + my $result = system("LD_LIBRARY_PATH=\"$gpath/lib:\$LD_LIBRARY_PATH\"; $command >/dev/null 2>&1"); if (($result or $?) and $command !~ m!patch!) { - die "ERROR: Unable to execute command: $!\n"; + exitDie("ERROR: Unable to execute command: $!\n"); } } +### exitDie( $error ) +# +# a horribly named method meant to look like die but only exit, thereby not causing +# gpt-postinstall to croak. +# + +sub exitDie +{ + my($error) = @_; + + print $error; + exit; +} + ### query_boolean( $query_text, $default ) # # query the user with a string, and expect a response. If the user hits @@ -1005,6 +1116,12 @@ sub query_boolean my($query_text, $default) = @_; my($nondefault, $foo, $bar); + if ( !$prompt ) + { + print "Prompt suppressed. Continuing...\n"; + return "y"; + } + # # Set $nondefault to the boolean opposite of $default. # @@ -1030,6 +1147,10 @@ sub query_boolean $bar = $default; } + elsif ($bar eq '') + { + $bar = $default; + } elsif ($bar ne $default) { # everything else means 'nondefault'. @@ -1062,3 +1183,82 @@ sub absolutePath $file = abs_path($file); return $file; } + +### getOwnerID( $file ) +# +# return the uid containing the owner ID of the given file. +# + +sub getOwnerID +{ + my($file) = @_; + my($uid); + + # + # call stat() to get the mode of the file + # + + $uid = (stat($file))[4]; + + return $uid; +} + +### getMode( $file ) +# +# return a string containing the mode of the given file. +# + +sub getMode +{ + my($file) = @_; + my($tempmode, $mode); + + # + # call stat() to get the mode of the file + # + + $tempmode = (stat($file))[2]; + if (length($tempmode) < 1) + { + return ""; + } + + # + # call sprintf to format the mode into a UNIX-like string + # + + $mode = sprintf("%04o", $tempmode & 07777); + + return $mode; +} + +### userExists( $username ) +# +# given a username, return true if the user exists on the system. return false +# otherwise. +# + +sub userExists +{ + my($username) = @_; + my($uid); + + # + # retrieve the userid of the user with the given username + # + + $uid = getpwnam($username); + + # + # return true if $uid is defined and has a length greater than 0 + # + + if ( defined($uid) and (length($uid) > 0) ) + { + return 1; + } + else + { + return 0; + } +}