From 98d2ab5fb2edc5a74c528eba89e85e63f338084c Mon Sep 17 00:00:00 2001 From: "Leigh B. Stoller" <stoller@flux.utah.edu> Date: Tue, 21 Dec 2004 18:17:34 +0000 Subject: [PATCH] Rework old XMLRPC code that I stuck into defs.php3 a long time ago, but never made use of. Moved to its own file (www/xmlrpc.php3.in) and made to be more like the perl library I did a couple of months ago, that presents an interface to an sslxmlrpc server, via the sslxmlrpc client program operating in "raw" mode (takes raw xml on stdin, and returns raw xml on stdout). Added ELABINELAB code to nodetipacl.php3 so that you can click on console icon on an inner emulab web page, and it will ask the outer emulab sslxmlrpc server for the stuff it needs, and return that to the user. --- www/GNUmakefile.in | 2 +- www/defs.php3.in | 89 --------------------------- www/nodetipacl.php3 | 72 ++++++++++++++-------- www/xmlrpc.php3.in | 147 ++++++++++++++++++++++++++++++++++++++++++++ xmlrpc/webxmlrpc.in | 7 +-- 5 files changed, 198 insertions(+), 119 deletions(-) create mode 100644 www/xmlrpc.php3.in diff --git a/www/GNUmakefile.in b/www/GNUmakefile.in index d14c1f8405..c9de76282e 100644 --- a/www/GNUmakefile.in +++ b/www/GNUmakefile.in @@ -20,7 +20,7 @@ include $(OBJDIR)/Makeconf # Force dependencies to make sure configure regenerates if the .in file # is changed. # -all: defs.php3 dbdefs.php3 swish.conf websearch +all: defs.php3 dbdefs.php3 swish.conf websearch xmlrpc.php3 include $(TESTBED_SRCDIR)/GNUmakerules diff --git a/www/defs.php3.in b/www/defs.php3.in index 2056754d95..758831a382 100644 --- a/www/defs.php3.in +++ b/www/defs.php3.in @@ -286,95 +286,6 @@ function SUEXEC($uid, $gid, $cmdandargs, $action) { return $suexec_retval; } -# -# Invoke the XMLRPC backend. We invoke the xmlrpc server, writing the request -# to its stdin. We then wait for the reply to came back on its stdout. We -# decode that reply, and return an error status and output to the caller. -# Note that we run the backend as the uid/gid, much like we do for plain -# scripts. This might change in the future. -# -function XMLRPC($uid, $gid, $method, $arghash) -{ - global $TBSUEXEC_PATH; - - $xmlcode = xmlrpc_encode_request($method, array(0.1, $arghash)); - - $descriptorspec = array(0 => array("pipe", "r"), - 1 => array("pipe", "w")); - - $process = proc_open("$TBSUEXEC_PATH $uid $gid webxmlrpc", - $descriptorspec, $pipes); - - if (! is_resource($process)) { - TBERROR("Could not invoke XMLRPC backend!\n". - "$uid $gid $method\n". - print_r($arghash, true), 1); - } - # $pipes now looks like this: - # 0 => writeable handle connected to child stdin - # 1 => readable handle connected to child stdout - - # - # Write the request to the process, and then close the pipe so that - # the other side sees the EOF. The sshxmlrpc protocol looks sorta - # like a POST request, so we have to do that. - # - fwrite($pipes[0], "content-length: " . strlen($xmlcode) . "\r\n"); - fwrite($pipes[0], "\r\n"); - fwrite($pipes[0], "$xmlcode"); - fflush($pipes[0]); - fclose($pipes[0]); - - # - # Now read back the results into a string. We then convert the string - # back into a PHP datatype. This datatype is defined outside this code - # though. - # - # Skip the headers, but we need the content-length. - # - $content_length = 0; - while (!feof($pipes[1]) && ($foo = trim(fgets($pipes[1], 4096)))) { - if (preg_match("/^content-length: (\d*)/", $foo, $matches)) { - $content_length = $matches[1]; - } - } - - $output = ""; - while(!feof($pipes[1])) { - $output .= fgets($pipes[1], 1024); - } - fclose($pipes[1]); - - # It is important that you close any pipes before calling - # proc_close in order to avoid a deadlock. - $return_value = proc_close($process); - - if (!$return_value && $content_length) { - $decoded = xmlrpc_decode_request(substr($output, 0, $content_length), - $meth); - } - - # On command error or on a "Fault" reply, send email and terminate the - # script; something went really wrong. - if ($return_value || !$content_length || - (count($decoded) == 2 && - array_key_exists("faultCode", $decoded) && - array_key_exists("faultString", $decoded)) || - !(array_key_exists("code", $decoded) && - array_key_exists("value", $decoded) && - array_key_exists("output", $decoded))) { - TBERROR("XMLRPC backend failure!\n". - "$uid $gid $method returned $return_value\n". - "Arg Hash:\n" . - print_r($arghash, true) . "\n\n" . - "XML:\n" . - "$xmlcode\n\n" . - "Output:\n" . - "$output\n", 1); - } - return $decoded; -} - function ADDPUBKEY($uid, $cmdandargs) { global $TBSUEXEC_PATH; diff --git a/www/nodetipacl.php3 b/www/nodetipacl.php3 index 0db649b2eb..08d48ed1bf 100644 --- a/www/nodetipacl.php3 +++ b/www/nodetipacl.php3 @@ -1,10 +1,11 @@ <?php # # EMULAB-COPYRIGHT -# Copyright (c) 2000-2002 University of Utah and the Flux Group. +# Copyright (c) 2000-2002, 2004 University of Utah and the Flux Group. # All rights reserved. # include("defs.php3"); +include("xmlrpc.php3"); # # This script generates an "acl" file. @@ -36,28 +37,57 @@ if (! $isadmin) { } } -$query_result = DBQueryFatal("SELECT server, portnum, keylen, keydata " . - "FROM tiplines WHERE node_id='$node_id'" ); - -if (mysql_num_rows($query_result) == 0) { - USERERROR("The node $node_id does not exist, or seem to have a tipline!", 1); -} - # -# Read in the fingerprint of capture's certificate +# Ask outer emulab for the stuff we need. It does it own perm checks # -$capfile = "$TBETC_DIR/capture.fingerprint"; -$lines = file($capfile,"r"); -if (!$lines) { - TBERROR("Unable to open $capfile!",1); -} +if ($ELABINELAB) { + $arghash = array(); + $arghash["node"] = $node_id; -$fingerline = rtrim($lines[0]); -if (!preg_match("/Fingerprint=([\w:]+)$/",$fingerline,$matches)) { - TBERROR("Unable to find fingerprint in string $fingerline!",1); + $results = XMLRPC($uid, "nobody", "elabinelab.console", $arghash); + + if (!$results || + ! (isset($results{'server'}) && isset($results{'portnum'}) && + isset($results{'keydata'}) && isset($results{'certsha'}))) { + TBERROR("Did not get everything we needed from RPC call", 1); + } + + $server = $results['server']; + $portnum = $results['portnum']; + $keydata = $results['keydata']; + $keylen = strlen($keydata); + $certhash= strtolower($results{'certsha'}); } +else { -$certhash = str_replace(":","",strtolower($matches[1])); + $query_result = DBQueryFatal("SELECT server, portnum, keylen, keydata " . + "FROM tiplines WHERE node_id='$node_id'" ); + + if (mysql_num_rows($query_result) == 0) { + USERERROR("The node $node_id does not exist, ". + "or does not have a tipline!", 1); + } + $row = mysql_fetch_array($query_result); + $server = $row[server]; + $portnum = $row[portnum]; + $keylen = $row[keylen]; + $keydata = $row[keydata]; + + # + # Read in the fingerprint of the capture certificate + # + $capfile = "$TBETC_DIR/capture.fingerprint"; + $lines = file($capfile,"r"); + if (!$lines) { + TBERROR("Unable to open $capfile!",1); + } + + $fingerline = rtrim($lines[0]); + if (!preg_match("/Fingerprint=([\w:]+)$/",$fingerline,$matches)) { + TBERROR("Unable to find fingerprint in string $fingerline!",1); + } + $certhash = str_replace(":","",strtolower($matches[1])); +} $filename = $node_id . ".tbacl"; @@ -68,12 +98,6 @@ header("Content-Description: ACL key file for a testbed node serial port"); # XXX, should handle multiple tip lines gracefully somehow, # but not important for now. -$row = mysql_fetch_array($query_result); -$server = $row[server]; -$portnum = $row[portnum]; -$keylen = $row[keylen]; -$keydata = $row[keydata]; - echo "host: $server\n"; echo "port: $portnum\n"; echo "keylen: $keylen\n"; diff --git a/www/xmlrpc.php3.in b/www/xmlrpc.php3.in new file mode 100644 index 0000000000..d3874fa3dc --- /dev/null +++ b/www/xmlrpc.php3.in @@ -0,0 +1,147 @@ +<?php +# +# EMULAB-COPYRIGHT +# Copyright (c) 2004 University of Utah and the Flux Group. +# All rights reserved. +# +# This is an included file. No headers or footers. +# +# Stuff to use the xmlrpc client/server. This is functionally equivalent +# to the perl stuff I wrote in xmlrpc/libxmlrpc.pm.in. +# +$RPCSERVER = "@OUTERBOSS_NODENAME@"; +$RPCPORT = "@OUTERBOSS_XMLRPCPORT@"; +$RPCCERT = "@OUTERBOSS_SSLCERTNAME@"; + +# +# Emulab XMLRPC defs. +# +# WARNING: If you change this stuff, also change defs in xmlrpc directory. +# +define("XMLRPC_RESPONSE_SUCCESS", 0); +define("XMLRPC_RESPONSE_BADARGS", 1); +define("XMLRPC_RESPONSE_ERROR", 2); +define("XMLRPC_RESPONSE_FORBIDDEN", 3); +define("XMLRPC_RESPONSE_BADVERSION", 4); +define("XMLRPC_RESPONSE_SERVERERROR", 5); +define("XMLRPC_RESPONSE_TOOBIG", 6); +define("XMLRPC_RESPONSE_REFUSED", 7); +define("XMLRPC_RESPONSE_TIMEDOUT", 8); + +## +# The package version number +# +define("XMLRPC_PACKAGE_VERSION", 0.1); + +# +# This is the "structure" returned by the RPC server. It gets converted into +# a php hash by the unmarshaller, and we return that directly to the caller +# (as a reference). +# +# class EmulabResponse: +# def __init__(self, code, value=0, output=""): +# self.code = code # A RESPONSE code +# self.value = value # A return value; any valid XML type. +# self.output = output # Pithy output to print +# return +# +function ParseResponse($xmlgoo) +{ + # The method is ignored. + $decoded = xmlrpc_decode_request($xmlgoo, $meth); + $rval = array(); + + if (array_key_exists("faultCode", $decoded) && + array_key_exists("faultString", $decoded)) { + $code = $decoded{"faultCode"}; + $value = $code; + $output = $decoded{"faultString"}; + } + elseif (!(array_key_exists("code", $decoded) && + array_key_exists("value", $decoded) && + array_key_exists("output", $decoded))) { + # + # Malformed response; let caller do something reasonable. + # + return NULL; + } + else { + $code = $decoded{"code"}; + $value = $decoded{"value"}; + $output = $decoded{"output"}; + } + $rval{'code'} = $code; + $rval{'value'} = $value; + $rval{'output'} = $output; + + return $rval; +} + +# +# Invoke the ssl xmlrpc client in raw mode, passing it an encoded XMLRPC +# string, reading back an XMLRPC encoded response, which is converted to +# a PHP datatype with the ParseResponse() function above. In other words, +# we invoke a method on a remote xmlrpc server, and get back a response. +# Invoked as the current user, but the actual uid of the caller is contained +# in the ssl certificate we use, which for now is the elabinelab certificate +# of the creator (since that is the only place this code is being used). +# +function XMLRPC($uid, $gid, $method, $arghash) +{ + global $TBSUEXEC_PATH, $TBADMINGROUP; + global $RPCSERVER, $RPCPORT, $RPCCERT; + + $xmlcode = xmlrpc_encode_request($method, + array(XMLRPC_PACKAGE_VERSION, $arghash)); + + $descriptorspec = array(0 => array("pipe", "r"), + 1 => array("pipe", "w")); + + $process = proc_open("$TBSUEXEC_PATH $uid $TBADMINGROUP webxmlrpc -r ". + "-s $RPCSERVER -p $RPCPORT --cert=$RPCCERT ", + $descriptorspec, $pipes); + + if (! is_resource($process)) { + TBERROR("Could not invoke XMLRPC backend!\n". + "$uid $gid $method\n". + print_r($arghash, true), 1); + } + # $pipes now looks like this: + # 0 => writeable handle connected to child stdin + # 1 => readable handle connected to child stdout + + # + # Write the request to the process, and then close the pipe so that + # the other side sees the EOF. + # + fwrite($pipes[0], "$xmlcode"); + fflush($pipes[0]); + fclose($pipes[0]); + + # + # Now read back the results into a string. + $output = ""; + while(!feof($pipes[1])) { + $output .= fgets($pipes[1], 1024); + } + fclose($pipes[1]); + + # It is important that you close any pipes before calling + # proc_close in order to avoid a deadlock. + $return_value = proc_close($process); + + if ($return_value || $output == "" || + (($decoded = ParseResponse($output)) == NULL) || $decoded->{"code"}) { + TBERROR("XMLRPC backend failure!\n". + "$uid $gid $method returned $return_value\n". + "Arg Hash:\n" . + print_r($arghash, true) . "\n\n" . + "XML:\n" . + "$xmlcode\n\n" . + "Output:\n" . + "$output\n", 1); + } +# TBERROR(print_r($decoded, true), 0); + return $decoded{'value'}; +} + diff --git a/xmlrpc/webxmlrpc.in b/xmlrpc/webxmlrpc.in index f7506e59ac..0b18310d63 100644 --- a/xmlrpc/webxmlrpc.in +++ b/xmlrpc/webxmlrpc.in @@ -17,12 +17,9 @@ use English; # my $TB = "@prefix@"; -# We have to do this to fake out the server. Harmless. -$ENV{'SSH_CONNECTION'} = "localhost 0 localhost 0"; - # # Run the real thing, and never return. # -exec "$TB/sbin/sshxmlrpc_server.py", @ARGV; +exec "$TB/bin/sslxmlrpc_client.py", @ARGV; -die("webxmlrpc: Could not exec sshxmlrpc_server.py: $!"); +die("webxmlrpc: Could not exec sslxmlrpc_client.py: $!"); -- GitLab