about to overhaul signatures and signed-creds
authorJosh Karlin <jkarlin@bbn.com>
Wed, 7 Apr 2010 18:37:11 +0000 (18:37 +0000)
committerJosh Karlin <jkarlin@bbn.com>
Wed, 7 Apr 2010 18:37:11 +0000 (18:37 +0000)
sfa/managers/registry_manager_pl.py
sfa/server/modpythonapi/AuthenticatedApi.py
sfa/trust/auth.py
sfa/trust/credential.py
sfa/trust/hierarchy.py
sfa/trust/trustedroot.py

index 68690dd..d2ed29b 100644 (file)
@@ -60,7 +60,8 @@ def get_credential(api, xrn, type, is_self=False):
     new_cred.set_privileges(rights)
     new_cred.set_delegate(True)
     auth_kind = "authority,ma,sa"
-    new_cred.set_parent(api.auth.hierarchy.get_auth_cred(auth_hrn, kind=auth_kind))
+    # Parent not necessary, verify with certs
+    #new_cred.set_parent(api.auth.hierarchy.get_auth_cred(auth_hrn, kind=auth_kind))
     new_cred.encode()
     new_cred.sign()
 
index c909346..f87d4bb 100755 (executable)
@@ -18,6 +18,7 @@ class AuthenticatedApi(BaseApi):
         BaseApi.__init__(self, encoding)
         if trustedRootsDir:
             self.trusted_cert_list = TrustedRootList(trustedRootsDir).get_list()
+            self.trusted_cert_file_list = TrustedRootList(trustedRootsDir).get_file_list()
         else:
             self.trusted_cert_list = None
 
@@ -40,7 +41,7 @@ class AuthenticatedApi(BaseApi):
 
     def validateCred(self, cred):
         if self.trusted_cert_list:
-            cred.verify_chain(self.trusted_cert_list)
+            cred.verify(self.trusted_cert_file_list)
             caller_gid = cred.get_gid_caller()
             object_gid = cred.get_gid_object()
             if caller_gid:
index 3bcc097..51b7edc 100644 (file)
@@ -28,6 +28,7 @@ class Auth:
         if not config:
             self.config = Config()
         self.trusted_cert_list = TrustedRootList(self.config.get_trustedroots_dir()).get_list()
+        self.trusted_cert_file_list = TrustedRootList(self.config.get_trustedroots_dir()).get_file_list()
 
 
     def check(self, cred, operation):
@@ -56,7 +57,7 @@ class Auth:
                 raise InsufficientRights(operation)
 
         if self.trusted_cert_list:
-            self.client_cred.verify_chain(self.trusted_cert_list)
+            self.client_cred.verify(self.trusted_cert_file_list)
             if self.client_gid:
                 self.client_gid.verify_chain(self.trusted_cert_list)
             if self.object_gid:
@@ -98,7 +99,7 @@ class Auth:
 
     def validateCred(self, cred):
         if self.trusted_cert_list:
-            cred.verify_chain(self.trusted_cert_list)
+            cred.verify(self.trusted_cert_file_list)
             caller_gid = cred.get_gid_caller()
             object_gid = cred.get_gid_object()
             if caller_gid:
index 608acfa..e6245db 100644 (file)
@@ -32,6 +32,7 @@ from sfa.util.sfalogging import logger
 # . remove verify_chain
 # . make delegation per privilege instead of global
 # . make privs match between PG and PL
+# . what about tickets?  do they need to be redone to be like credentials?
 # . Need to test
 
 signature_template = \
@@ -62,6 +63,12 @@ signature_template = \
 
 
 
+
+#class Signature(object):
+
+#class Signed_Credential(object):
+    
+
 ##
 # Credential is a tuple:
 #    (GIDCaller, GIDObject, Expiration (in UTC time), Privileges, DelegateBit)
@@ -153,7 +160,6 @@ class Credential(object):
         if not cred.xml:
             cred.encode()
 
-        logger.info("Setting parent for %s to %s" % (self.gidCaller.get_urn(), cred.gidCaller.get_urn()))
         doc = parseString(cred.xml)
         signed = doc.getElementsByTagName("signed-credential")[0]
         cred = signed.getElementsByTagName("credential")[0]
@@ -338,7 +344,7 @@ class Credential(object):
 
         # Get the finished product
         self.xml = doc.toxml()
-        #print doc.toprettyxml()
+        #print doc.toxml()
         #self.sign()
 
 
@@ -378,25 +384,15 @@ class Credential(object):
             return []
         
         refs = []
-        
-        next_xml = self.parent_xml
-
-        logger.info("instance--")
 
-        while next_xml:
-            doc = parseString(next_xml)
-            cred = doc.getElementsByTagName("credential")[0]
-            refid = cred.getAttribute("xml:id")
-            logger.info("Found refid %s" % refid)
-            assert(refid not in refs)
-            refs.append(refid)
-
-            parent = doc.getElementsByTagName("parent")
-            if len(parent) > 0:
-                next_xml = parent[0].getElementsByTagName("credential")[0].toxml()
+        next_cred = Credential(string=self.parent_xml)
+        while next_cred:
+            refs.append(next_cred.get_refid())
+            if next_cred.parent_xml:
+                next_cred = Credential(string=next_cred.parent_xml)
             else:
-                next_xml = None
-
+                next_cred = None
+        
         # Find a unique refid for this credential
         rid = self.get_refid()
         while rid in refs:
@@ -454,13 +450,21 @@ class Credential(object):
 
     def decode(self):
         doc = parseString(self.xml)
-        signed_cred = doc.getElementsByTagName("signed-credential")[0]
-        cred = signed_cred.getElementsByTagName("credential")[0]
-        signatures = signed_cred.getElementsByTagName("signatures")[0]
-        sigs = signatures.getElementsByTagName("Signature")
+        sigs = None
+        signed_cred = doc.getElementsByTagName("signed-credential")
+
+        # Is this a signed-cred or just a cred?
+        if len(signed_cred) > 0:
+            cred = signed_cred[0].getElementsByTagName("credential")[0]
+            signatures = signed_cred[0].getElementsByTagName("signatures")
+            if len(signatures) > 0:
+                sigs = signatures[0].getElementsByTagName("Signature")
+        else:
+            cred = doc.getElementsByTagName("credential")[0]
+        
 
 
-        self.max_refid = int(cred.getAttribute("xml:id")[3:])
+        self.set_refid(cred.getAttribute("xml:id"))
         sz_expires = self.getTextNode(cred, "expires")
         if sz_expires != '':
             self.expiration = datetime.datetime.strptime(sz_expires, '%Y-%m-%dT%H:%M:%S')            
@@ -496,30 +500,101 @@ class Credential(object):
             self.signatures.append(sig.toxml())
             
     ##
-    # For a simple credential (no delegation) verify..
-    # . That the signature is valid for the credential's xml:id
-    # . That the signer's pub key matches the pub key of the target (object)
-    # . That the target/owner urns match those in the gids
+    # Verify that:
+    # . All of the signatures are valid and that the issuers trace back
+    #   to trusted roots (performed by xmlsec1)
+    # . That the issuer of the credential is the authority in the target's urn
+    #    . In the case of a delegated credential, this must be true of the root
+    #
+    # -- For Delegates (credentials with parents)
+    # . The privileges must be a subset of the parent credentials
+    # . The privileges must have "can_delegate" set for each delegated privilege
+    # . The target gid must be the same between child and parents
+    # . The expiry time on the child must be no later than the parent
+    # . The signer of the child must be the owner of the parent
+    #
+    # -- Verify does *NOT*
+    # . ensure that an xmlrpc client's gid matches a credential gid, that
+    #   must be done elsewhere
     #
     # @param trusted_certs: The certificates of trusted CA certificates
     
     def verify(self, trusted_certs):
+        logger.info("verifying cert")
         if not self.xml:
             self.decode()        
 
-        # Verify the sigatures
+        # Verify the signatures
         filename = self.save_to_random_tmp_file()
         cert_args = " ".join(['--trusted-pem %s' % x for x in trusted_certs])
 
-        ref = "Sig_%s" % self.get_refid()
-        verified = os.popen('/usr/bin/xmlsec1 --verify --node-id "%s" %s %s 2>&1' \
-                            % (ref, cert_args, filename)).read()
+        
+        refs = []
+        refs.append("Sig_%s" % self.get_refid())
 
-        if not verified.strip().startswith("OK"):
-            raise CredentialNotVerifiable("xmlsec1 error: " + verified)
+        parentRefs = self.updateRefID()
+        for ref in parentRefs:
+            refs.append("Sig_%s" % ref)
+
+        for ref in refs:
+            logger.info('/usr/bin/xmlsec1 --verify --node-id "%s" %s %s 2>&1' \
+                            % (ref, cert_args, filename))
+            verified = os.popen('/usr/bin/xmlsec1 --verify --node-id "%s" %s %s 2>&1' \
+                            % (ref, cert_args, filename)).read()
+            if not verified.strip().startswith("OK"):
+                raise CredentialNotVerifiable("xmlsec1 error: " + verified)
 
         os.remove(filename)
 
+        # Verify the parents (delegation)
+        #if self.parent_xml:
+        #    self.verify_parent(Credential(string=self.parent_xml))
+
+        # Make sure the issuer is the target's authority
+        self.verify_issuer()
+
+
+
+    ##
+    # Make sure the issuer of this credential is the target's authority
+    def verify_issuer(self):
+        target_authority = get_authority(self.get_gid_object().get_hrn())
+
+        # Find the root credential's refid
+        cur_cred = self
+        root_refid = None
+        while cur_cred:            
+            if cur_cred.parent_xml:
+                cur_cred = Credential(string=cur_cred.parent_xml)
+            else:
+                root_refid = "Sig_%s" % cur_cred.get_refid()                
+                cur_cred = None
+
+        # Find the signature for the root credential
+        root_issuer = None
+        for sig in self.signatures:
+            doc = parseString(sig)
+            esig = doc.getElementsByTagName("Signature")[0]
+            ref = esig.getAttribute("xml:id")
+            if ref.lower() == root_refid.lower():
+                # Found the right signature, look for the issuer
+                keyinfo = esig.getElementsByTagName("X509Data")[0]
+                root_issuer = self.getTextNode(keyinfo, "X509SubjectName")
+                root_issuer = root_issuer.strip('CN=')
+                
+        # Ensure that the signer of the root credential is the target_authority
+        root_issuer = hrn_to_urn(root_issuer, 'authority')
+        target_authority = hrn_to_urn(target_authority, 'authority')
+
+        if root_issuer != target_authority:
+            raise CredentialNotVerifiable("issuer (%s) != authority of target (%s)" \
+                                          % (root_issuer, target_authority))
+                                          
+        
+    def verify_parent(self, parent_cred):
+        if parent_cred.parent_xml:
+            parent_cred.verify_parent(Credential(string=parent_cred.parent_xml))
+
     ##
     # Verify that a chain of credentials is valid (see cert.py:verify). In
     # addition to the checks for ordinary certificates, verification also
index c4b9054..609a48a 100644 (file)
@@ -314,6 +314,8 @@ class Hierarchy:
             # 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_keys(parent_auth_info.get_privkey_filename(), parent_auth_info.get_gid_filename())
+
+            
             cred.set_parent(self.get_auth_cred(parent_hrn, kind))
 
         cred.encode()
index 90dfe46..2d4b89e 100644 (file)
@@ -34,3 +34,13 @@ class TrustedRootList:
 
         return gid_list
 
+    def get_file_list(self):
+        gid_file_list = []
+        
+        file_list = os.listdir(self.basedir)
+        for gid_file in file_list:
+            fn = os.path.join(self.basedir, gid_file)
+            if os.path.isfile(fn):
+                gid_file_list.append(fn)
+
+        return gid_file_list