Improvements to the XML based credential, still no verification
authorJosh Karlin <jkarlin@bbn.com>
Wed, 31 Mar 2010 20:45:20 +0000 (20:45 +0000)
committerJosh Karlin <jkarlin@bbn.com>
Wed, 31 Mar 2010 20:45:20 +0000 (20:45 +0000)
sfa/client/sfi.py
sfa/managers/registry_manager_pl.py
sfa/plc/api.py
sfa/trust/credential.py
sfa/trust/hierarchy.py

index 0e3b812..4187446 100755 (executable)
@@ -8,6 +8,7 @@ import os, os.path
 import tempfile
 import traceback
 import socket
+import random
 from types import StringTypes, ListType
 from optparse import OptionParser
 from sfa.trust.certificate import Keypair, Certificate
@@ -478,7 +479,15 @@ class Sfi:
         dcred.set_privileges(user_cred.get_privileges())
         dcred.set_delegate(True)
         dcred.set_pubkey(object_gid.get_pubkey())
-        dcred.set_issuer(user_key, user_cred.get_gid_caller())
+
+        # Save the issuer's gid to a file
+        fname = self.options.sfi_dir + os.sep + "gid_%d" % random.randint(0,999999999)
+        f = open(fname, "w")
+        f.write(user_cred.get_gid_caller().save_to_string())
+        f.close()
+        dcred.set_issuer_keys(self.get_key_file(), fname)
+        os.remove(fname)
+        
         dcred.set_parent(user_cred)
         dcred.encode()
         dcred.sign()
index 03462ee..e7690a2 100644 (file)
@@ -55,7 +55,7 @@ def get_credential(api, xrn, type, is_self=False):
     new_cred = Credential(subject = object_gid.get_subject())
     new_cred.set_gid_caller(caller_gid)
     new_cred.set_gid_object(object_gid)
-    new_cred.set_issuer_keys(auth_info.get_pkey_object(), auth_info.get_gid_object())
+    new_cred.set_issuer_keys(auth_info.get_privkey_filename(), auth_info.get_gid_filename())
     new_cred.set_pubkey(object_gid.get_pubkey())
     new_cred.set_privileges(rights)
     new_cred.set_delegate(True)
index 4cb05b1..a5aa921 100644 (file)
@@ -153,7 +153,7 @@ class SfaAPI(BaseAPI):
         new_cred = Credential(subject = object_gid.get_subject())
         new_cred.set_gid_caller(object_gid)
         new_cred.set_gid_object(object_gid)
-        new_cred.set_issuer_keys(auth_info.get_pkey_object(), auth_info.get_gid_object())
+        new_cred.set_issuer_keys(auth_info.get_privkey_filename(), auth_info.get_gid_filename())
         new_cred.set_pubkey(object_gid.get_pubkey())
         r1 = determine_rights(type, hrn)
         new_cred.set_privileges(r1)
index 5b27d2d..e8fc703 100644 (file)
@@ -23,35 +23,12 @@ from sfa.util.faults import *
 from sfa.util.sfalogging import *
 
 
-signed_cred_template = \
-'''
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-  %s
-<signed-credential>
-  <signatures>
-  %s
-  </signatures>
-</signed-credential>
-'''
-
-
-credential_template = \
-'''
-  <credential xml:id="%s">
-    <type>privilege</type>
-    <serial>8</serial>
-    <owner_gid>%s</owner_gid>
-    <owner_urn>%s</owner_urn>
-    <target_gid>%s</target_gid>
-    <target_urn>%s</target_urn>
-    <uuid></uuid>
-    <expires>%s</expires>
-    <privileges>
-    %s
-    </privileges>
-  </credential>
-'''
-
+# TODO:
+# . Need to verify credentials
+# . Need to add privileges
+# . Need to fix lifetime
+# . Need to make sure delegation is fully supported
+# . Need to test
 
 signature_template = \
 '''
@@ -92,13 +69,13 @@ signature_template = \
 # are placed in signed XML.
 
 
-class Credential(Certificate):
+class Credential(object):
     gidCaller = None
     gidObject = None
     lifeTime = None
     privileges = None
     delegate = False
-    issuer_key = None
+    issuer_privkey = None
     issuer_gid = None
     issuer_pubkey = None
     parent = None
@@ -116,14 +93,17 @@ class Credential(Certificate):
 
         # Check if this is a legacy credential, translate it if so
         if string or filename:
-            if str:
+            if string:                
                 str = string
             elif filename:
                 str = file(filename).read()
                 
             if str.strip().startswith("-----"):
                 self.translate_legacy(str)
-                
+            else:
+                self.xml = str
+                # Let's not mess around with invalid credentials
+                self.verify_chain()
 
     ##
     # Translate a legacy credential into a new one
@@ -131,13 +111,12 @@ class Credential(Certificate):
     # @param String of the legacy credential
 
     def translate_legacy(self, str):
-        legacy = CredentialLegacy(create, subject, string, filename)
+        legacy = CredentialLegacy(False,string=str)
         self.gidCaller = legacy.get_gid_caller()
         self.gidObject = legacy.get_gid_object()
         self.lifeTime = legacy.get_lifetime()
         self.privileges = legacy.get_privileges()
         self.delegate = legacy.get_delegate()
-        self.encode()
 
     ##
     # Need the issuer's private key and name
@@ -145,7 +124,7 @@ class Credential(Certificate):
     # @param gid GID of the issuing authority
 
     def set_issuer_keys(self, privkey, gid):
-        self.issuer_key = privkey
+        self.issuer_privkey = privkey
         self.issuer_gid = gid
 
     def set_pubkey(self, pubkey):
@@ -252,7 +231,9 @@ class Credential(Certificate):
         return rights.can_perform(op_name)
 
     def append_sub(self, doc, parent, element, text):
-        parent.appendChild(doc.createElement(element).appendChild(doc.createTextNode(text)))
+        ele = doc.createElement(element)
+        ele.appendChild(doc.createTextNode(text))
+        parent.appendChild(ele)
 
     ##
     # Encode the attributes of the credential into an XML string    
@@ -275,17 +256,18 @@ class Credential(Certificate):
         
 
         # Fill in the <credential> bit
+        refid = "ref1"
         cred = doc.createElement("credential")
         cred.setAttribute("xml:id", refid)
         signed_cred.appendChild(cred)
         self.append_sub(doc, cred, "type", "privilege")
         self.append_sub(doc, cred, "serial", "8")
-        self.append_sub(doc, cred, "owner_gid", cur_cred.gidCaller.save_to_string())
-        self.append_sub(doc, cred, "owner_urn", cur_cred.gidCaller.get_urn())
-        self.append_sub(doc, cred, "target_gid", cur_cred.gidObject.save_to_string())
-        self.append_sub(doc, cred, "target_urn", cur_cred.gidObject.get_urn())
+        self.append_sub(doc, cred, "owner_gid", self.gidCaller.save_to_string())
+        self.append_sub(doc, cred, "owner_urn", self.gidCaller.get_urn())
+        self.append_sub(doc, cred, "target_gid", self.gidObject.save_to_string())
+        self.append_sub(doc, cred, "target_urn", self.gidObject.get_urn())
         self.append_sub(doc, cred, "uuid", "")
-        self.append_sub(doc, cred, "expires", self.lifeTime)
+        self.append_sub(doc, cred, "expires", str(self.lifeTime))
         priveleges = doc.createElement("privileges")
         cred.appendChild(priveleges)
 
@@ -307,7 +289,7 @@ class Credential(Certificate):
         sz_sig = signature_template % (refid,refid)
 
         sdoc = xml.dom.minidom.parseString(sz_sig)
-        sig_ele = doc.importNode(sdoc.getElemenetsByTagName("Signature")[0], True)
+        sig_ele = doc.importNode(sdoc.getElementsByTagName("Signature")[0], True)
         signatures.appendChild(sig_ele)
 
 
@@ -316,56 +298,109 @@ class Credential(Certificate):
             for sig in p_sigs:
                 signatures.appendChild(sig)
 
+        signed_cred.appendChild(signatures)
         # Get the finished product
         self.xml = doc.toxml()
+        #print doc.toprettyxml()
+
 
 
-##         # Call out to xmlsec1 to sign it
-##         XMLSEC = '/usr/bin/xmlsec1'
 
-##         filename = "/tmp/cred_%d" % random.random()
-##         f = open(filename, "w")
-##         f.write(whole);
-##         f.close()
-##         signed = os.popen('/usr/bin/xmlsec1 --sign --node-id "%s" --privkey-pem %s,%s %s'
-##                  % ('ref1', self.issuer_privkey, self.issuer_cert, filename)).readlines()
-##         os.rm(filename)
+    def save_to_string(self):
+        if not self.xml:
+            self.encode()
+        return self.xml
 
-##         self.encoded = signed
+    def sign(self):
+        if not self.xml:
+            self.encode()
         
+        # Call out to xmlsec1 to sign it
+        XMLSEC = '/usr/bin/xmlsec1'
+
+        filename = "/tmp/cred_%d" % random.randint(0,999999999)
+        f = open(filename, "w")
+        f.write(self.xml);
+        f.close()
+        signed = os.popen('/usr/bin/xmlsec1 --sign --node-id "%s" --privkey-pem %s,%s %s' \
+                 % ('ref1', self.issuer_privkey, self.issuer_gid, filename)).read()
+        os.remove(filename)
+
+        self.xml = signed
+
+    def getTextNode(self, element, subele):
+        sub = element.getElementsByTagName(subele)[0]
+        return sub.childNodes[0].nodeValue
+
     ##
-    # Retrieve the attributes of the credential from the alt-subject-name field
-    # of the X509 certificate. This is automatically done by the various
-    # get_* methods of this class and should not need to be called explicitly.
+    # Retrieve the attributes of the credential from the XML.
+    # This is automatically caleld by the various get_* methods of
+    # this class and should not need to be called explicitly.
 
     def decode(self):
-        data = self.get_data().lstrip('URI:http://')
+        p_doc = xml.dom.minidom.parseString(self.xml)
+        p_signed_cred = p_doc.getElementsByTagName("signed-credential")[0]
+        p_cred = p_signed_cred.getElementsByTagName("credential")[0]
+        p_signatures = p_signed_cred.getElementsByTagName("signatures")[0]
+        p_sigs = p_signatures.getElementsByTagName("Signature")
+
+        self.lifeTime = self.getTextNode(p_cred, "expires")
+        self.gidCaller = GID(string=self.getTextNode(p_cred, "owner_gid"))
+        self.gidObject = GID(string=self.getTextNode(p_cred, "target_gid"))
         
-        if data:
-            dict = xmlrpclib.loads(data)[0][0]
-        else:
-            dict = {}
+            
+        
+##     ##
+##     # Retrieve the attributes of the credential from the alt-subject-name field
+##     # of the X509 certificate. This is automatically done by the various
+##     # get_* methods of this class and should not need to be called explicitly.
 
-        self.lifeTime = dict.get("lifeTime", None)
-        self.delegate = dict.get("delegate", None)
+##     def decode(self):
+##         data = self.get_data().lstrip('URI:http://')
+        
+##         if data:
+##             dict = xmlrpclib.loads(data)[0][0]
+##         else:
+##             dict = {}
 
-        privStr = dict.get("privileges", None)
-        if privStr:
-            self.privileges = RightList(string = privStr)
-        else:
-            self.privileges = None
+##         self.lifeTime = dict.get("lifeTime", None)
+##         self.delegate = dict.get("delegate", None)
 
-        gidCallerStr = dict.get("gidCaller", None)
-        if gidCallerStr:
-            self.gidCaller = GID(string=gidCallerStr)
-        else:
-            self.gidCaller = None
+##         privStr = dict.get("privileges", None)
+##         if privStr:
+##             self.privileges = RightList(string = privStr)
+##         else:
+##             self.privileges = None
+
+##         gidCallerStr = dict.get("gidCaller", None)
+##         if gidCallerStr:
+##             self.gidCaller = GID(string=gidCallerStr)
+##         else:
+##             self.gidCaller = None
+
+##         gidObjectStr = dict.get("gidObject", None)
+##         if gidObjectStr:
+##             self.gidObject = GID(string=gidObjectStr)
+##         else:
+##             self.gidObject = None
+
+
+    ##
+    # Verify for the initial credential:
+    # 1. That the signature is valid
+    # 2. That the xml signer's certificate matches the object's certificate
+    # 3. That the urns match those in the gids
+    #
+    # Verify for the delegated credentials:
+    # 1. That the signature is valid
+    
+    # 4. 
+    # 3. That the object's certificate stays the s
+    # 2. That the GID of the 
+
+    def verify(self, trusted_certs = None):
+        
 
-        gidObjectStr = dict.get("gidObject", None)
-        if gidObjectStr:
-            self.gidObject = GID(string=gidObjectStr)
-        else:
-            self.gidObject = None
 
     ##
     # Verify that a chain of credentials is valid (see cert.py:verify). In
index d683931..3f0580f 100644 (file)
@@ -60,6 +60,12 @@ class AuthInfo:
         self.gid_filename = fn
         self.gid_object = None
 
+    def get_privkey_filename(self):
+        return self.privkey_filename
+
+    def get_gid_filename(self):
+        return self.gid_filename
+
     ##
     # Get the GID in the form of a GID object
 
@@ -307,7 +313,7 @@ class Hierarchy:
         else:
             # we need the parent's private key in order to sign this GID
             parent_auth_info = self.get_auth_info(parent_hrn)
-            cred.set_issuer(parent_auth_info.get_pkey_object(), parent_auth_info.get_gid_object())
+            cred.set_issuer(parent_auth_info.get_privkey_filename(), parent_auth_info.get_gid_filename())
             cred.set_parent(self.get_auth_cred(parent_hrn, kind))
 
         cred.encode()