Commit d7d3800a authored by Leigh Stoller's avatar Leigh Stoller

Add password block decryption and expansion in the instructions panel.

Given a password element in the rspec:

	<emulab:password name='foo'></password>

which the portal has converted to an encrypted secret, when that experiment
is later shown (the status page), ask the server to decrypt the block, and
then replace the string "{password-foo}" in the instructions with the
actual password.

Need to generalize this a bit more, for arbitrary encryption blocks, when
we have those.
parent e8f1f15c
......@@ -151,6 +151,7 @@ class Instance
function virtnode_count() { return $this->field('virtnode_count'); }
function servername() { return $this->field('servername'); }
function aggregate_urn(){ return $this->field('aggregate_urn'); }
function private_key() { return $this->field('privkey'); }
function IsAPT() {
return preg_match('/aptlab/', $this->servername());
}
......
......@@ -1199,6 +1199,7 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal,
$('#instructions_text').html(marked(text));
// Make the div visible.
$('#instructions_panel').removeClass("hidden");
});
});
}
......@@ -1386,11 +1387,9 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal,
// Clear the list view table before adding nodes. Not needed?
$('#listview_table > tbody').html("");
// Add the instructions from only one manifest.
var gottour = 0;
// Save off some templatizing data as we process each manifest.
var uridata = {};
// Save off the last manifest xml blob so we quick process the
// possibly templatized instructions quickly, without reparsing the
// manifest again needlessly.
......@@ -1404,8 +1403,10 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal,
ProcessNodes(aggregate_urn, xml);
});
if (xml != null)
if (xml != null) {
UpdateInstructions(xml,uridata);
FindEncryptionBlocks(xml);
}
/*
* If a single node, show the clone button and maybe the
......@@ -1480,6 +1481,64 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal,
});
}
function FindEncryptionBlocks(xml)
{
var blocks = {};
var passwords = xml[0].getElementsByTagNameNS(EMULAB_NS, 'password');
// Search the instructions for the pattern.
var regex = /\{password-.*\}/gi;
var needed = $('#instructions_text').html().match(regex);
//console.log(needed);
if (!needed || !needed.length)
return;
// Look for all the encryption blocks in the manifest ...
_.each(passwords, function (password) {
var name = $(password).attr('name');
var stuff = $(password).text();
var key = 'password-' + name;
// ... and see if we referenced it in the instructions.
_.each(needed, function(match) {
var token = match.slice(1,-1);
if (token == key) {
blocks[key] = stuff;
}
});
});
// These are blocks that are referenced in the instructions
// and need the server to decrypt. At some point we might
// want to do that here in javascript, but maybe later.
//console.log(blocks);
var callback = function(json) {
//console.log(json);
if (json.code) {
sup.SpitOops("oops", "Could not decrypt secrets: " +
json.value);
return;
}
var itext = $('#instructions_text').html();
_.each(json.value, function(plaintext, key) {
key = "{" + key + "}";
// replace in the instructions text.
itext = itext.replace(key, plaintext);
});
// Write the instructions back after replacing patterns
$('#instructions_text').html(itext);
};
var xmlthing = sup.CallServerMethod(ajaxurl,
"status",
"DecryptBlocks",
{"uuid" : uuid,
"blocks" : blocks});
xmlthing.done(callback);
}
function ShowProgressModal()
{
ShowImagingModal(function()
......
......@@ -114,6 +114,8 @@ $routing = array("myprofiles" =>
"Do_Reload",
"Refresh" =>
"Do_Refresh",
"DecryptBlocks" =>
"Do_DecryptBlocks",
"Lockout" =>
"Do_Lockout")),
"approveuser" =>
......
......@@ -82,6 +82,32 @@ function StatusSetupAjax($needmodify)
return 0;
}
#
# So we can capture stderr. Sheesh.
#
function myexec($cmd)
{
ignore_user_abort(1);
$myexec_output_array = array();
$myexec_output = "";
$myexec_retval = 0;
exec("$cmd 2>&1", $myexec_output_array, $myexec_retval);
if ($myexec_retval) {
for ($i = 0; $i < count($myexec_output_array); $i++) {
$myexec_output .= "$myexec_output_array[$i]\n";
}
$foo = "Shell Program Error. Exit status: $myexec_retval\n";
$foo .= " '$cmd'\n";
$foo .= "\n";
$foo .= $myexec_output;
TBERROR($foo, 0);
return 1;
}
return 0;
}
#
# Status/
#
......@@ -1006,6 +1032,64 @@ function Do_Reload()
Do_RebootOrReload("reload");
}
#
# Decrypt the blocks using the per-instance private key.
#
function Do_DecryptBlocks()
{
global $this_user, $instance, $suexec_output;
global $ajax_args;
if (StatusSetupAjax(1)) {
return;
}
if (!isset($ajax_args["blocks"])) {
SPITAJAX_ERROR(1, "Missing blocks argument");
return;
}
$this_idx = $this_user->uid_idx();
$uuid = $ajax_args["uuid"];
$result = array();
#
# Grab the instance private key and write to a file for smime.
#
$infname = tempnam("/tmp", "decryptin");
$outfname = tempnam("/tmp", "decryptout");
$pkeyname = tempnam("/tmp", "decryptkey");
$fp = fopen($pkeyname, "w");
fwrite($fp, $instance->private_key());
fclose($fp);
#
# Use smime for each block and store.
#
foreach ($ajax_args["blocks"] as $key => $block) {
$fp = fopen($infname, "w");
fwrite($fp, $block);
fclose($fp);
# Truncate the output file.
$fp = fopen($outfname, "w");
fclose($fp);
$retval =
myexec("/usr/bin/openssl smime -decrypt -inform PEM -inkey ".
"$pkeyname -in $infname -out $outfname");
if ($retval) {
SPITAJAX_ERROR(-1, "Internal decryption error");
return;
}
$decrypted = file_get_contents($outfname);
$result[$key] = $decrypted;
}
unlink($infname);
unlink($outfname);
unlink($pkeyname);
SPITAJAX_RESPONSE($result);
}
#
# Set or clear the lockout flag
#
......
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