Commit 0eba3e76 authored by Leigh B. Stoller's avatar Leigh B. Stoller

Convert install-rpm/install-tarfile to use the web server instead of

tmcd (which is bad, since tying up the tmcd threads blocks all nodes
in the testbed). The old functionality is left in tmcd for now.

On the server side, a new web page (www/spewrpmtar.php3) receives a
request for a file, along with the nodeid (pcXXX) making the request,
and the secret key that is generated for each new experiment and
transfered to the node via tmcd. If the key matches, the operation is
handed off to tbsetup/spewrpmtar.in which verifies that the file is in
the list of rpm/tar files for that node, and then spits it out to
stdout. The web page uses fpassthru() to send the file out to the
client. The client is using wget, and is required to use https (the
web page checks).

At present, the external script is run as the creator of the
experiment, and gid of the experiment. Perhaps this is not a good
idea. In any event, the file must be in the list of rpm/tarfiles,
either owned by the experiment creator or with a group of the
experiment, and the file must reside in either /proj or /groups.
I use the realpath() function to make sure there are no symlink tricks
pointing to outside those filesystems. I use the standard NFS read goo to
prevent transient mount problems that we all know and love.
parent 5c4b45b6
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2003 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
use Getopt::Std;
#
# Spew a tar/rpm file to stdout.
#
# The script is setuid and run from the webserver.
#
sub usage()
{
print STDERR "Usage: spewtarfile [-v] <nodeid> <file>\n".
"Spew a tar/rpm for an experiment.\n";
exit(-1);
}
my $optlist = "v";
my $debug = 1;
my $doverify = 0;
#
# Configure variables
#
my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
my $TBLOGS = "@TBLOGSEMAIL@";
#
# Load the Testbed support stuff.
#
use lib "@prefix@/lib";
use libdb;
use libtestbed;
# un-taint path
$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
# Turn off line buffering on output
$| = 1;
#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
%options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (defined($options{"v"})) {
$doverify = 1;
}
if (@ARGV != 2) {
usage();
}
my $nodeid = $ARGV[0];
my $file = $ARGV[1];
my $pid;
my $eid;
#
# Untaint the arguments.
#
if ($nodeid =~ /^([-\@\w]+)$/) {
$nodeid = $1;
}
else {
die("*** Bad data in nodeid: $nodeid\n");
}
# Note different taint check (allow /).
if ($file =~ /^([-\@\w\.\/]+)$/) {
$file = $1;
}
else {
die("*** Bad data in argument: $file");
}
#
# We need the pid,eid,gid,creator.
#
if (!NodeidToExp($nodeid, \$pid, \$eid)) {
die("*** $0:\n".
" $nodeid is not allocated to an experiment!\n");
}
my $gid = ExpGroup($pid, $eid);
my $creator = ExpLeader($pid, $eid);
my $bytelen = 0;
exit(1)
if (!VerifyFile());
exit(0)
if ($doverify);
exit(2)
if (!SpewFile());
exit(0);
#
# Spew out a file.
#
sub SpewFile()
{
open(FD, "< $file")
or fatal("Could not open $file!\n");
#
# Deal with NFS read failures
#
my $foffset = 0;
my $retries = 5;
while ($bytelen) {
my $rlen = sysread(FD, $buf, 8192);
if (! defined($rlen)) {
#
# Retry a few times on error to avoid the
# changing-exports-file server problem.
#
if ($retries > 0 && sysseek(FD, $foffset, 0)) {
$retries--;
sleep(1);
next;
}
fatal("Error reading $file: $!");
}
if ($rlen == 0) {
last;
}
if (! syswrite(STDOUT, $buf, $rlen)) {
fatal("Error writing file to stdout: $!");
}
$foffset += $rlen;
$bytelen -= $rlen;
$retries = 5;
}
if ($bytelen) {
fatal("Did not get the entire file! $bytelen bytes left.");
}
close(FD);
}
#
# Verify that we can return this file, return 0 if not allowed.
#
sub VerifyFile()
{
#
# First make sure the file is in the rpm or tar list for the node,
# and that it exists and we can read it.
#
if (!VerifyTar() && !VerifyRPM()) {
if ($debug) {
print STDERR "VerifyFile: Could not verify $file!\n";
}
return 0;
}
#
# Now a few other checks.
#
# Use realpath to remove any symlinks to make sure we are not going
# to hand out a file outside the appropriate files systems.
#
my $translated = `realpath $file`;
if ($translated =~ /^([-\@\w.\/]+)$/) {
$translated = $1;
}
else {
fatal("Bad data returned by realpath: $translated");
}
#
# The file must reside in /proj/$pid/$eid or /groups/$pid/$eid.
# allow anything from /users!
#
if (! ($translated =~ /^\/proj\/$pid\//) &&
! ($translated =~ /^\/groups\/$pid\/$eid\//)) {
if ($debug) {
print STDERR "$translated is not in /proj or /groups!\n";
}
return 0;
}
#
# Stat the file to confirm that its either owned by the experiment
# creator, or in the gid of the experiment.
#
(undef,undef,undef,undef,$stat_uid,$stat_gid,undef,$bytelen)
= stat($translated);
my ($unix_uid, $unix_gid, $unix_gname);
TBGroupUnixInfo($pid, $gid, \$unix_gid, \$unix_gname) or
fatal("No such group $pid/$gid\n");
(undef,undef,$unix_uid) = getpwnam($creator) or
fatal("No such user $creator\n");
if ($stat_gid != $unix_gid &&
$stat_uid != $unix_uid) {
if ($debug) {
print STDERR "$translated has wrong uid/gid!\n";
}
return 0;
}
$file = $translated;
return 1;
}
#
# Check the DB to make sure this is a valid TAR/RPM file for the node.
# Must pass a number of other checks too.
#
sub VerifyTar()
{
#
# Get the tarball list from the DB. The requested path must be
# on the list of tarballs for this node.
#
my $query_result =
DBQueryFatal("select tarballs from nodes where node_id='$nodeid'");
# No rpms/tarballs for the node in question.
return 0
if (!$query_result->numrows);
#
# The format is a colon separated list of "dir filename". We must find
# the filename in the list.
#
my ($tarballs) = $query_result->fetchrow_array();
foreach my $tarspec (split(":", $tarballs)) {
my ($dir, $tar) = split(" ", $tarspec);
return 1
if ($tar eq $file && -r $tar);
}
return 0;
}
sub VerifyRPM()
{
my $query_result =
DBQueryFatal("select rpms from nodes where node_id='$nodeid'");
# No rpms/tarballs for the node in question.
return 0
if (!$query_result->numrows);
#
# The format is a colon separated list of filenames. We must find
# the filename in the list.
#
my ($rpms) = $query_result->fetchrow_array();
foreach my $rpm (split(":", $rpms)) {
return 1
if ($rpm eq $file && -r $rpm);
}
return 0;
}
sub fatal {
my $msg = $_[0];
SENDMAIL($TBOPS, "spewrpmtar:$file", $msg);
die("*** $0:\n".
" $msg\n");
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment