Delegation is now per-privilege, instead of one bit per credential
authorJosh Karlin <jkarlin@bbn.com>
Wed, 7 Apr 2010 21:17:52 +0000 (21:17 +0000)
committerJosh Karlin <jkarlin@bbn.com>
Wed, 7 Apr 2010 21:17:52 +0000 (21:17 +0000)
sfa/client/sfi.py
sfa/managers/registry_manager_pl.py
sfa/trust/credential.py
sfa/trust/hierarchy.py
sfa/trust/rights.py
tests/testCred.py

index deb565e..90e1f87 100755 (executable)
@@ -477,7 +477,7 @@ class Sfi:
         dcred.set_gid_caller(delegee_gid)
         dcred.set_gid_object(object_gid)
         dcred.set_privileges(user_cred.get_privileges())
-        dcred.set_delegate(True)
+        dcred.get_privileges().delegate_all_privileges(True)
         
 
         # Save the issuer's gid to a file
@@ -623,7 +623,7 @@ class Sfi:
        object_gid = object_cred.get_gid_object()
        object_hrn = object_gid.get_hrn()
     
-       if not object_cred.get_delegate():
+       if not object_cred.get_privileges().get_all_delegate():
            print "Error: Object credential", object_hrn, "does not have delegate bit set"
            return
     
@@ -645,8 +645,9 @@ class Sfi:
        dcred = Credential(subject=object_hrn + " delegated to " + delegee_hrn)
        dcred.set_gid_caller(delegee_gid)
        dcred.set_gid_object(object_gid)
+       privs = object_cred.get_privileges()
        dcred.set_privileges(object_cred.get_privileges())
-       dcred.set_delegate(True)
+       dcred.get_privileges().delegate_all_privileges(True)
        dcred.set_pubkey(object_gid.get_pubkey())
        dcred.set_issuer(user_key, user_hrn)
        dcred.set_parent(object_cred)
index d2ed29b..2108c15 100644 (file)
@@ -58,7 +58,7 @@ def get_credential(api, xrn, type, is_self=False):
     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)
+    new_cred.get_privileges().delegate_all_privileges(True)
     auth_kind = "authority,ma,sa"
     # Parent not necessary, verify with certs
     #new_cred.set_parent(api.auth.hierarchy.get_auth_cred(auth_hrn, kind=auth_kind))
index 8609c3b..d533b95 100644 (file)
@@ -1,8 +1,7 @@
 ##
 # Implements SFA Credentials
 #
-# Credentials are layered on top of certificates, and are essentially a
-# certificate that stores a tuple of parameters.
+# Credentials are signed XML files that assign a subject gid privileges to an object gid
 ##
 
 ### $Id$
@@ -13,7 +12,6 @@ import random
 import os
 import datetime
 
-#import xml.dom.minidom
 from xml.dom.minidom import Document, parseString
 from sfa.trust.credential_legacy import CredentialLegacy
 from sfa.trust.certificate import Certificate
@@ -24,16 +22,12 @@ from sfa.util.sfalogging import logger
 
 
 # TODO:
-# . SFA is using set_parent to do chaining, but that's no longer necessary
-#   with the new credentials. fix.
-#    . This probably requires setting up some sort of CA hierarchy.
-# . Make sure that the creds pass xml verification (probably need some reordering)
 # . Need to implement full verification (parent signatures etc).
 # . 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
+# . Need to test delegation, xml verification
 
 signature_template = \
 '''
@@ -127,7 +121,7 @@ class Signature(object):
 
 ##
 # Credential is a tuple:
-#    (GIDCaller, GIDObject, Expiration (in UTC time), Privileges, DelegateBit)
+#    (GIDCaller, GIDObject, Expiration (in UTC time), Privileges)
 #
 # These fields are encoded in one of two ways.  The legacy style places
 # it in the subjectAltName of an X509 certificate.  The new credentials
@@ -139,7 +133,6 @@ class Credential(object):
     gidObject = None
     expiration = None
     privileges = None
-    delegate = False
     issuer_privkey = None
     issuer_gid = None
     issuer_pubkey = None
@@ -187,7 +180,7 @@ class Credential(object):
             self.set_lifetime(int(lifetime))
         self.lifeTime = legacy.get_lifetime()
         self.set_privileges(legacy.get_privileges())
-        self.delegate = legacy.get_delegate()
+        self.get_privileges().delegate_all_privileges(legacy.get_delegate())
 
     ##
     # Need the issuer's private key and name
@@ -286,22 +279,7 @@ class Credential(object):
             self.decode()
         return self.expiration
 
-    ##
-    # set the delegate bit
-    #
-    # @param delegate boolean (True or False)
-
-    def set_delegate(self, delegate):
-        self.delegate = delegate
-
-    ##
-    # get the delegate bit
-
-    def get_delegate(self):
-        if not self.delegate:
-            self.decode()
-        return self.delegate
-
     ##
     # set the privileges
     #
@@ -312,6 +290,7 @@ class Credential(object):
             self.privileges = RightList(string = privs)
         else:
             self.privileges = privs
+        
 
     ##
     # return the privileges as a RightList object
@@ -371,11 +350,11 @@ class Credential(object):
         cred.appendChild(privileges)
 
         if self.privileges:
-            rights = self.privileges.save_to_string().split(",")
-            for right in rights:
+            rights = self.get_privileges()
+            for right in rights.rights:
                 priv = doc.createElement("privilege")
-                self.append_sub(doc, priv, "name", right.strip())
-                self.append_sub(doc, priv, "can_delegate", str(self.delegate))
+                self.append_sub(doc, priv, "name", right.kind)
+                self.append_sub(doc, priv, "can_delegate", str(right.delegate))
                 privileges.appendChild(priv)
 
         # Add the parent credential if it exists
@@ -637,9 +616,23 @@ class Credential(object):
         if root_issuer != target_authority:
             raise CredentialNotVerifiable("issuer (%s) != authority of target (%s)" \
                                           % (root_issuer, target_authority))
-                                          
+
+    ##
+    # -- For Delegates (credentials with parents) verify that:
+    # . 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
         
     def verify_parent(self, parent_cred):
+        # make sure the rights given to the child are a subset of the
+        # parents rights (and check delegate bits)
+        if not parent_cred.get_privileges().is_superset(self.get_privileges()):
+            raise ChildRightsNotSubsetOfParent(
+                self.parent.get_privileges().save_to_string() + " " +
+                self.get_privileges().save_to_string())
+        
         if parent_cred.parent_xml:
             parent_cred.verify_parent(Credential(string=parent_cred.parent_xml))
 
@@ -651,9 +644,6 @@ class Credential(object):
     #
     # Each credential must be a subset of the rights of the parent.
 
-    def verify_chain(self, trusted_certs):
-        return
-
  ##    def verify_chain(self, trusted_certs = None):
 ##         # do the normal certificate verification stuff
 ##         Certificate.verify_chain(self, trusted_certs)
index 609a48a..e73e5bd 100644 (file)
@@ -302,7 +302,7 @@ class Hierarchy:
         cred.set_gid_caller(gid)
         cred.set_gid_object(gid)
         cred.set_privileges(kind)
-        cred.set_delegate(True)
+        cred.get_privileges().delegate_all_privileges(True)
         #cred.set_pubkey(auth_info.get_gid_object().get_pubkey())
 
         parent_hrn = get_authority(hrn)
index 16ef185..1c08a1e 100644 (file)
@@ -78,9 +78,10 @@ class Right:
    #
    # @param kind is a string naming the right. For example "control"
 
-   def __init__(self, kind):
+   def __init__(self, kind, delegate=False):
       self.kind = kind
-
+      self.delegate = delegate
+      
    ##
    # Test to see if this right object is allowed to perform an operation.
    # Returns True if the operation is allowed, False otherwise.
@@ -109,6 +110,9 @@ class Right:
       my_allowed_ops = privilege_table.get(self.kind.lower(), None)
       child_allowed_ops = privilege_table.get(child.kind.lower(), None)
 
+      if not self.delegate:
+          return False
+
       if "*" in my_allowed_ops:
           return True
 
@@ -140,9 +144,9 @@ class RightList:
     #
     # @param right is either a Right object or a string describing the right
 
-    def add(self, right):
+    def add(self, right, delegate=False):
         if isinstance(right, str):
-            right = Right(kind = right)
+            right = Right(kind = right, delegate=delegate)
         self.rights.append(right)
 
     ##
@@ -157,16 +161,23 @@ class RightList:
 
         parts = string.split(",")
         for part in parts:
-            self.rights.append(Right(part))
+            if ':' in part:
+                spl = part.split(':')
+                kind = spl[0]
+                delegate = int(spl[1])
+            else:
+                kind = part
+                delegate = 0
+            self.rights.append(Right(kind, bool(delegate)))
 
     ##
     # Save the rightlist object to a string. It is saved in the format of a
     # comma-separated list.
 
-    def save_to_string(self):
+    def save_to_string(self):        
         right_names = []
         for right in self.rights:
-            right_names.append(right.kind)
+            right_names.append('%s:%d' % (right.kind, right.delegate))
 
         return ",".join(right_names)
 
@@ -203,7 +214,29 @@ class RightList:
 
 
     ##
-    # Determine tje rights that an object should have. The rights are entirely
+    # set the delegate bit to 'delegate' on
+    # all privileges
+    #
+    # @param delegate boolean (True or False)
+
+    def delegate_all_privileges(self, delegate):
+        for right in self.rights:
+            right.delegate = delegate
+
+    ##
+    # true if all privileges have delegate bit set true
+    # false otherwise
+
+    def get_all_delegate(self):
+        for right in self.rights:
+            if not right.delegate:
+                return False
+        return True
+
+
+
+    ##
+    # Determine the rights that an object should have. The rights are entirely
     # dependent on the type of the object. For example, users automatically
     # get "refresh", "resolve", and "info".
     #
index 68c9b74..155543c 100755 (executable)
@@ -23,7 +23,7 @@ class TestCred(unittest.TestCase):
       gidObject = GID(subject="object", uuid=create_uuid(), hrn="foo.object")
       lifeTime = 12345
       delegate = True
-      rights = "embed,bind"
+      rights = "embed:1,bind:1"
 
       cred.set_gid_caller(gidCaller)
       self.assertEqual(cred.get_gid_caller().get_subject(), gidCaller.get_subject())
@@ -34,9 +34,6 @@ class TestCred(unittest.TestCase):
       cred.set_lifetime(lifeTime)
       self.assertEqual(cred.get_lifetime(), lifeTime)
 
-      cred.set_delegate(delegate)
-      self.assertEqual(cred.get_delegate(), delegate)
-
       cred.set_privileges(rights)
       self.assertEqual(cred.get_privileges().save_to_string(), rights)