Commit ebd44a43 authored by Leigh B. Stoller's avatar Leigh B. Stoller

Checkpoint some initial work before I head out of town; want to be

able to access it from the road.
parent 446a0cc8
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2008 University of Utah and the Flux Group.
# All rights reserved.
#
SRCDIR = @srcdir@
TESTBED_SRCDIR = @top_srcdir@
OBJDIR = ..
SUBDIR = protogeni
include $(OBJDIR)/Makeconf
SUBDIRS = security
all: all-subdirs
include $(TESTBED_SRCDIR)/GNUmakerules
install:
@$(MAKE) -C security install
control-install:
clean: clean-subdirs
distclean: subdir-distclean
subdir-distclean:
@$(MAKE) -C imagezip distclean
# How to recursively descend into subdirectories to make general
# targets such as `all'.
%.MAKE:
@$(MAKE) -C $(dir $@) $(basename $(notdir $@))
%-subdirs: $(addsuffix /%.MAKE,$(SUBDIRS)) ;
.PHONY: $(SUBDIRS)
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2008 University of Utah and the Flux Group.
# All rights reserved.
#
SRCDIR = @srcdir@
TESTBED_SRCDIR = @top_srcdir@
OBJDIR = ../..
SUBDIR = protogeni/security
include $(OBJDIR)/Makeconf
SBIN_STUFF = signgenicred verifygenicred
LIBEXEC_STUFF =
CTRLSBIN_STUFF =
LIBFILES = credential.xsd sig.xsd xml.xsd
# These scripts installed setuid, with sudo.
SETUID_BIN_SCRIPTS =
SETUID_SBIN_SCRIPTS =
SETUID_LIBX_SCRIPTS =
#
# Force dependencies on the scripts so that they will be rerun through
# configure if the .in file is changed.
#
all: $(SBIN_STUFF) $(LIBEXEC_STUFF) $(CTRLSBIN_STUFF) $(LIBFILES)
include $(TESTBED_SRCDIR)/GNUmakerules
install: all script-install
@echo "Don't forget to do a post-install as root"
script-install: $(addprefix $(INSTALL_SBINDIR)/, $(SBIN_STUFF)) \
$(addprefix $(INSTALL_LIBEXECDIR)/, $(LIBEXEC_STUFF)) \
$(addprefix $(INSTALL_LIBDIR)/protogeni/security/, $(LIBFILES))
post-install:
chmod 775 $(INSTALL_BINDIR)
chmod 775 $(INSTALL_SBINDIR)
chmod 775 $(INSTALL_LIBDIR)
chmod 775 $(INSTALL_LIBEXECDIR)
control-install:
# This rule says what web* script depends on which installed binary directory.
$(LIBEXEC_STUFF): $(INSTALL_SBINDIR)
# Just in case the dirs are not yet created,
$(INSTALL_SBINDIR):
clean:
rm -f *.o core
$(INSTALL_DIR)/opsdir/sbin/%: %
@echo "Installing $<"
-mkdir -p $(INSTALL_DIR)/opsdir/sbin
$(INSTALL) $< $@
$(INSTALL_LIBDIR)/protogeni/security/%: %
@echo "Installing $<"
-mkdir -p $(INSTALL_LIBDIR)/protogeni/security
$(INSTALL) $< $@
#
# EMULAB-COPYRIGHT
# Copyright (c) 2008 University of Utah and the Flux Group.
# All rights reserved.
#
# ProtoGENI credential and capability specification. The key points:
#
# * A credential is a set of capabilities or a Ticket, each with a flag
# to indicate delegation is permitted.
# * A credential is signed and the signature included in the body of the
# document.
# * To support delegation, a credential will include its parent, and that
# blob will be signed. So, there will be multiple signatures in the
# document, each with a reference to the credential it signs.
#
namespace sig = "http://www.w3.org/2000/09/xmldsig#"
datatypes xs = "http://www.w3.org/2001/XMLSchema-datatypes"
anyelementbody = (attribute * {text} | text | element * {anyelementbody} )*
## Representation of a single capability.
CapabilitySpec = element capability {
# Name of the capability.
element capability_name { xsd:string { minLength = "1" }},
# Flag indicating this capability can be delegated
element can_delegate { "0" | "1" }
}
## A set of capabilities.
CapabilitiesSpec = element capabilities {
CapabilitySpec*
}
## Define a stub for future ticket.
TicketSpec = element ticket {
## Can the ticket be delegated?
element can_delegate { "0" | "1" },
anyelementbody
}
## A list of signatures.
signatures = element signatures {
element sig:Signature { anyelementbody }+
}
## A credential granting capabilities or a ticket.
credentials = element credential {
## The ID for signature referencing.
attribute xml:id {xs:ID},
## The type of this credential. Currently a Capability set or a Ticket.
element type { "capability" | "ticket" },
## UUID of the owner of this credential.
element owner_uuid { xsd:string },
## UUID of this credential
element this_uuid { xsd:string },
## Capabilities or a ticket
(CapabilitiesSpec | TicketSpec),
## Parent that delegated to us
element parent { credentials }?
}
start = element signed-credential {
credentials,
signatures
}
<?xml version="1.0" encoding="UTF-8"?>
<!--
EMULAB-COPYRIGHT
Copyright (c) 2008 University of Utah and the Flux Group.
All rights reserved.
-->
<!--
ProtoGENI credential and capability specification. The key points:
* A credential is a set of capabilities or a Ticket, each with a flag
to indicate delegation is permitted.
* A credential is signed and the signature included in the body of the
document.
* To support delegation, a credential will include its parent, and that
blob will be signed. So, there will be multiple signatures in the
document, each with a reference to the credential it signs.
-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" xmlns:sig="http://www.w3.org/2000/09/xmldsig#">
<xs:import namespace="http://www.w3.org/2000/09/xmldsig#" schemaLocation="sig.xsd"/>
<xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="xml.xsd"/>
<xs:group name="anyelementbody">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>
</xs:sequence>
</xs:group>
<xs:attributeGroup name="anyelementbody">
<xs:anyAttribute processContents="skip"/>
</xs:attributeGroup>
<xs:element name="capability">
<xs:complexType>
<xs:sequence>
<xs:element ref="capability_name"/>
<xs:element name="can_delegate">
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="0"/>
<xs:enumeration value="1"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="capability_name">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="capabilities">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="capability"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="ticket">
<xs:complexType mixed="true">
<xs:sequence>
<xs:element name="can_delegate">
<xs:annotation>
<xs:documentation>Can the ticket be delegated?</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="0"/>
<xs:enumeration value="1"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:group ref="anyelementbody"/>
</xs:sequence>
<xs:attributeGroup ref="anyelementbody"/>
</xs:complexType>
</xs:element>
<xs:element name="signatures">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" ref="sig:Signature"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="credentials">
<xs:annotation>
<xs:documentation>A credential granting capabilities or a ticket.</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element ref="credential"/>
</xs:sequence>
</xs:complexType>
<xs:element name="credential">
<xs:complexType>
<xs:sequence>
<xs:element ref="type"/>
<xs:element ref="owner_uuid"/>
<xs:element ref="this_uuid"/>
<xs:choice>
<xs:annotation>
<xs:documentation>Capabilities or a ticket</xs:documentation>
</xs:annotation>
<xs:element ref="capabilities"/>
<xs:element ref="ticket"/>
</xs:choice>
<xs:element minOccurs="0" ref="parent"/>
</xs:sequence>
<xs:attribute ref="xml:id" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="type">
<xs:annotation>
<xs:documentation>The type of this credential. Currently a Capability set or a Ticket.</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="capability"/>
<xs:enumeration value="ticket"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="owner_uuid" type="xs:string">
<xs:annotation>
<xs:documentation>UUID of the owner of this credential. </xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="this_uuid" type="xs:string">
<xs:annotation>
<xs:documentation>UUID of this credential</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="parent" type="credentials">
<xs:annotation>
<xs:documentation>Parent that delegated to us</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="signed-credential">
<xs:complexType>
<xs:complexContent>
<xs:extension base="credentials">
<xs:sequence>
<xs:element ref="signatures"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
</xs:schema>
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://www.w3.org/2000/09/xmldsig#" xmlns:sig="http://www.w3.org/2000/09/xmldsig#">
<xs:import schemaLocation="credential.xsd"/>
<xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="xml.xsd"/>
<xs:element name="Signature">
<xs:complexType mixed="true">
<xs:group ref="anyelementbody"/>
<xs:attributeGroup ref="anyelementbody"/>
</xs:complexType>
</xs:element>
</xs:schema>
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2008 University of Utah and the Flux Group.
# All rights reserved.
#
use strict;
use English;
use Getopt::Std;
use XML::LibXML;
use Data::Dumper;
#
# Sign a GENI credential
#
sub usage()
{
print("Usage: signgenicred [-d] [-o file] <capfile> [<user>]\n");
exit(-1);
}
my $optlist = "do:";
my $debug = 1;
my $outfile;
my $capfile;
#
# Configure variables
#
my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
my $TBLOGS = "@TBLOGSEMAIL@";
my $OURDOMAIN = "@OURDOMAIN@";
my $CONTROL = "@USERNODE@";
my $BOSSNODE = "@BOSSNODE@";
#
# Load the Testbed support stuff.
#
use lib "@prefix@/lib";
use libaudit;
use libdb;
use libtestbed;
use User;
#
# Turn off line buffering on output
#
$| = 1;
# Locals
my $USERDIR = USERROOT();
my $SSLDIR = "$TB/lib/ssl";
my $EMULAB_CERT = "$TB/etc/emulab.pem";
my $EMULAB_KEY = "$TB/etc/emulab.key";
my $SCHEMA = "$TB/lib/protogeni/security/credential.xsd";
my $OPENSSL = "/usr/bin/openssl";
my $XMLLINT = "/usr/local/bin/xmllint";
my $XMLSEC1 = "/usr/local/bin/xmlsec1";
my $SAVEUID = $UID;
my $tmpfile = "/tmp/signcred$$";
my $certfile;
my $keyfile;
my $sigid;
my $deletefiles = 0;
#
# This is a template for the xmlsec library.
#
my $sigtmpl =
"<Signature xml:id=\"%s\" >\n".
" <SignedInfo>\n".
" <CanonicalizationMethod ".
" Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315\"/>\n".
" <SignatureMethod ".
" Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/>\n".
" <Reference URI=\"%s\">\n".
" <Transforms>\n".
" <Transform ".
" Algorithm=\"http://www.w3.org/2000/09/xmldsig#".
"enveloped-signature\" />\n".
" </Transforms>\n".
" <DigestMethod ".
" Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/>\n".
" <DigestValue></DigestValue>\n".
" </Reference>\n".
" </SignedInfo>\n".
" <SignatureValue />\n".
" <KeyInfo>\n".
" <X509Data >\n".
" <X509SubjectName/>\n".
" <X509IssuerSerial/>\n".
" <X509Certificate/>\n".
" </X509Data>\n".
" <KeyValue />\n".
" </KeyInfo>\n".
"</Signature>\n";
#
# We don't want to run this script unless its the real version.
#
#if ($EUID != 0) {
# die("*** $0:\n".
# " Must be setuid! Maybe its a development version?\n");
#}
#
# This script is setuid, so please do not run it as root. Hard to track
# what has happened.
#
if ($UID == 0) {
die("*** $0:\n".
" Please do not run this as root!\n");
}
#
# Untaint the path
#
$ENV{'PATH'} = "$TB/bin:$TB/sbin:/bin:/usr/bin:/usr/bin:/usr/sbin";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
#
# Function prototypes
#
sub fatal($);
sub cleanup();
#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
my %options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (defined($options{"d"})) {
$debug = 1;
}
if (defined($options{"o"})) {
$outfile = $options{"o"};
if ($outfile =~ /^([-\w\.\/]+)$/) {
$outfile = $1;
}
else {
fatal("Tainted filename: $outfile");
}
}
usage()
if (!@ARGV);
$capfile = shift(@ARGV);
if ($capfile =~ /^([-\w\.\/]+)$/) {
$capfile = $1;
}
else {
fatal("Tainted filename: $capfile");
}
# Map target user to object.
my $target_user;
if (@ARGV) {
$target_user = User->Lookup($ARGV[0]);
if (! defined($target_user)) {
fatal($ARGV[0] . " does not exist!");
}
}
# Map invoking user to object.
my $this_user = User->LookupByUnixId($UID);
if (! defined($this_user)) {
fatal("You ($UID) do not exist!");
}
#
# This script is always audited. Mail is sent automatically upon exit.
#
if (0 && AuditStart(0)) {
#
# Parent exits normally
#
exit(0);
}
#
# Verify that the credential conforms to the schema. The wrinkle is that it
# might already be a signed credential and we just need to add another
# signature to it, but either way it should still pass the schema check.
#
if (system("$XMLLINT -noout -schema $SCHEMA $capfile")) {
fatal("$capfile does not conform to schema $SCHEMA");
}
#
# Bring in the credential file so we can mess with it.
#
my $parser = XML::LibXML->new;
my $doc = $parser->parse_file($capfile);
my $root = $doc->documentElement();
#
# The certficate used to sign the credential is either the Emulab certificate
# or that of the user delegating the credential. For now lets just use the
# Emulab certificate. I will add user signing later.
#
$keyfile = $EMULAB_KEY;
$certfile = $EMULAB_CERT;
#
# Check the root.
#
if ($root->nodeName eq "credential") {
#
# If its a credential then this is the first signing. We have to
# create a new document that wraps the credential up and adds the
# signing template (see above). When done, it will look like;
#
# <signed-credential>
# <original capfile>
# <signatures>
# <signature></signature>
# </signatures>
# </signed-credential>
#
my $newroot = XML::LibXML::Element->new('signed-credential');
my $signatures_node = XML::LibXML::Element->new('signatures');
$newroot->addChild($root);
$newroot->addChild($signatures_node);
#
# We need the id of the toplevel credential so that we can stick
# it into the sig template above, in the References section. We
# also need to generate an id for the signature node so that we
# can find it and tell xmlsec about it.
#
my ($xmlid_attribute) = $root->attributes();
my $xmlid = $xmlid_attribute->getValue();
$sigid = "Sig_${xmlid}";
my $template = sprintf($sigtmpl, $sigid, $xmlid);
#
# Convert the template above into a document tree and add it to the
# signatures list.
#
my $tmpparser = XML::LibXML->new;
my $newdoc = $tmpparser->parse_string($template);
my $sigroot = $newdoc->documentElement();
$sigroot->setNamespace("http://www.w3.org/2000/09/xmldsig#");
$signatures_node->addChild($sigroot);
$root = $newroot;
$doc->setDocumentElement($root);
print $root->toString(1) . "\n";
}
else {
;
}
#
# So now we have a valid document that needs to be signed at the $sigid
# reference point. Must write this to a tmp file for xmlsec, and then
# use xmlsec to add the signature.
#
open(TMP, ">$tmpfile") or
fatal("Could not open $tmpfile");
print TMP $doc->toString(1) . "\n";
close(TMP);
# Fire up xmlsec and read back the results.
open(SEC, "$XMLSEC1 --sign --node-id $sigid ".
" --privkey-pem $keyfile,$certfile $tmpfile |")
or fatal("Could not start $XMLSEC1 on $tmpfile");
while (<SEC>) {
print $_;
}
close(SEC);
cleanup();
exit(0);
sub cleanup()
{
unlink($tmpfile)
if (-e $tmpfile && !$debug);
}
sub fatal($)
{
my($mesg) = $_[0];
cleanup();
die("*** $0:\n".
" $mesg\n");
}
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2008 University of Utah and the Flux Group.
# All rights reserved.
#
use strict;
use English;
use Getopt::Std;
use XML::LibXML;
use Data::Dumper;
#
# Verify a GENI capability.
#
sub usage()
{
print("Usage: verifygenicap [-d] [-o file] <capfile>\n");
exit(-1);
}
my $optlist = "do:";
my $debug = 0;
my $outfile;
my $xmlfile;
#