$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");
}
#
# 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();
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 " Jacobim Mugatu says,\n";
-print " \t\"Utopian Prime Minister Bad! GSI-OpenSSH Good!\"\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 <http://grid.ncsa.uiuc.edu/ssh/>.\n");
+debug0("------------------------------------------------------------\n");
+debug0("Finished configuring gsi_openssh.\n");
exit;
addPRNGCommand("\@PROG_IPCS\@", "ipcs");
addPRNGCommand("\@PROG_TAIL\@", "tail");
- print "Determining paths for PRNG commands...\n";
+ debug1("Determining paths for PRNG commands...\n");
$paths = determinePRNGPaths();
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";
if ( !isReadable($fileInput) )
{
- printf("Cannot read $fileInput... skipping.\n");
+ debug1("Cannot read $fileInput... skipping.\n");
return;
}
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;
$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");
}
}
}
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;
$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
}
#
- # 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
#
if ( isReadable($mainkeyfile) && isReadable($mainpubkeyfile) )
{
- push(@$copylist, $basekeyfile);
+ push(@$linklist, $basekeyfile);
$count++;
next;
}
if (@$gen_keys && -x $keygen)
{
- print "Generating ssh host keys...\n";
+ debug1("Generating ssh host keys...\n");
for my $k (@$gen_keys)
{
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";
if ( !isReadable($fileInput) )
{
- printf("Cannot read $fileInput... skipping.\n");
+ debug1("Cannot read $fileInput... skipping.\n");
return;
}
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
#
# 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 (<IN>)
- {
- #
- # 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 <IN>
+ $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
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.
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;
}
}
# 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");
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
if ( !isReadable($src) )
{
- printf("$src is not readable... not creating $dest.\n");
+ debug1("$src is not readable... not creating $dest.\n");
return;
}
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;
}
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");
}
my($filename) = @_;
my($data);
- open(IN, "$filename") || die "Can't open '$filename': $!";
+ open(IN, "$filename") || exitDie("ERROR: Can't open '$filename': $!\n");
$/ = undef;
$data = <IN>;
$/ = "\n";
if ( !defined($filename) || (length($filename) lt 1) )
{
- die "Filename is undefined";
+ exitDie("ERROR: Filename is undefined!\n");
}
#
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.
{
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
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.
#
$bar = $default;
}
+ elsif ($bar eq '')
+ {
+ $bar = $default;
+ }
elsif ($bar ne $default)
{
# everything else means 'nondefault'.
$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;
+ }
+}