xmlrpcbag.in 4.66 KB
Newer Older
1
#!/usr/bin/perl -wT
Leigh Stoller's avatar
Leigh Stoller committed
2
#
3
# Copyright (c) 2000-2004 University of Utah and the Flux Group.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# 
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
Leigh Stoller's avatar
Leigh Stoller committed
23
#
24 25
use Sys::Syslog;
use BSD::Resource;
Leigh Stoller's avatar
Leigh Stoller committed
26

27 28 29 30 31
# xmlrpcbag - This is a rework of security/paperbag.in, which was written
# by Robert Ricci <ricci@cs.utah.edu> on November 17, 2000. This version
# is tailored to invoking the xmlrpc server, and is quite a bit simpler
# since we do not have to worry about the command line, interactive mode,
# directory changing, etc. 
32

33 34 35 36
#
# Configure variables
#
my $TB		= "@prefix@";
37
my $USERNODE    = "@USERNODE@";
38 39 40 41 42 43
my $XMLRPC      = "$TB/sbin/sshxmlrpc_server.py";
my $TBLOG	= "@TBLOGFACIL@";

# Locals.
my $debug       = 0;
my $module;
44

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
# Turn off line buffering.
$| = 1;

#
# Debugging goes to syslog. There is generally no reason to invoke this
# interactively, except during debugging, and in that case you can go
# look at the log file.
#
sub debug($) {
    my ($mesg) = @_;

    syslog("debug", $mesg)
	if ($debug);
}

#
# List of allowed RPC modules. The current usage is that the bag is
# invoked as /xmlrpc/module. We split out the module name and pass it
# to the server as the first and only argument. In reality, we could
# skip this check, and just pass the single token to the server and let
# it validate it. But, might as well be careful.
#
my %modules = ( "experiment" => "experiment",
	        "node"       => "node",
		"imageid"    => "imageid",
		"osid"	     => "osid",
		"fs"	     => "fs",
		"user"	     => "user",
		"emulab"     => "emulab",
		# Wrapper class.
		"server"     => "EmulabServer",
	      );
77

78 79 80 81 82 83 84 85 86 87 88 89 90
#
# Scrub the environment - delete all but a few variables we consider to be
# safe.
#
my %SAFE_ENV_VARS = (LOGNAME => 1, TERM => 1, SHELL => 1, HOME => 1, USER => 1,
    SSH_CLIENT => 1, SSH_CONNECTION => 1, SSH_AUTH_SOCK => 1, SSH_TTY => 1);

foreach my $var (keys %ENV) {
    if (!$SAFE_ENV_VARS{$var}) {
	delete $ENV{$var};
    }
}

91 92 93 94 95
#
# Provide a simple path. Note though that the server will not call anything
# without giving it an absolute path.
#
$ENV{PATH} = "/bin:/usr/bin:/usr/local/bin";
96

97 98
Sys::Syslog::setlogsock('unix');
openlog("xmlrpcbag", "pid", $TBLOG);
99

100 101 102 103 104 105 106 107
my $mesg = "Connect:";
if (exists($ENV{"USER"})) {
    $mesg .= " user:" . $ENV{"USER"};
}
if (exists($ENV{"SSH_CONNECTION"})) {
    if ($ENV{"SSH_CONNECTION"} =~ /^([\d.]*) .*$/) {
	$mesg .= " host:" . $1;
    }
108
}
109
syslog("info", $mesg);
110

111 112 113 114 115 116
#
# When invoked by ssh, we get a -c option cause it thinks we are a real shell.
# Otherwise, we want exactly one argument.
#
if (@ARGV == 2 && $ARGV[0] eq "-c") {
    $module = $ARGV[1];
117
}
118 119
elsif (@ARGV == 1) {
    $module = $ARGV[0];
120
}
121 122 123
else {
    syslog("err", "bad args: '@ARGV'");
    exit(-1);
124 125
}

126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
#
# Split the string. We allow xmlrpc, xmlrpc/module or just the module. 
#
if ($module =~ /^xmlrpc\/(\w+)$/) {
    $module = $1;
}
elsif ($module eq "xmlrpc") {
    undef($module);
}
if (defined($module)) {
    if ($module =~ /^([\w]*)$/) {
	$module = $1;
    }
    else {
	syslog("err", "illegal chars in module: '$module'");
	exit(-1);
    }
143 144
}

145 146 147 148 149 150
#
# Okay, module must be in the list unless we want the wrapper class. 
#
if (defined($module) && !exists($modules{$module})) {
    syslog("err", "unknown module: '$module'");
    exit(-1);
151
}
152 153
syslog("info", "module: '$module'")
    if (defined($module));
154

155 156 157 158 159 160 161 162 163 164 165 166 167 168
#
# Invoke the XMLRPC server. Exec ourselves to be SURE that a shell does
# not get called and do something insecure.
#
my $pid = fork();
if ($pid < 0) {
    syslog("err", "Fork failed: errno = $?");
    exit(-1);
}
elsif ($pid) {
    # Parent just waits.
    wait;
    syslog("info", "child exited with status = $?");
    exit($? >> 0);
169
}
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
else {
    #
    # Lets set some limits. 
    #
    setrlimit(RLIMIT_CPU, 1000, 1000);
    setrlimit(RLIMIT_CORE, 0, 0);

    #
    # We could also nice ourselves down, but that would put XMLRPC users
    # at a disadvantage relative to web users.
    # 
    
    # Extra braces avoid warning.
    { exec($XMLRPC, (defined($module) ? ($modules{$module}) : ())) };
    syslog("err", "exec failed: errno = $?");
    exit(-1);
}