def RenewSlice(self, slice_urn, expire_str): self.logger.info("Called RenewSlice(%s, %s)", slice_urn, expire_str) if not self.slices.has_key(slice_urn): self.logger.warning('Slice %s was not found', slice_urn) return False try: in_expiration = dateutil.parser.parse(expire_str) except: self.logger.warning('Unable to parse date "%s"', expire_str) return False # Is requested expiration valid? It must be in the future, # but not too far into the future. now = datetime.datetime.utcnow() now = now.replace(tzinfo=in_expiration.tzinfo) if in_expiration < now: self.logger.warning('Expiration "%s" is in the past.', expire_str) return False duration = in_expiration - now max_duration = datetime.timedelta(seconds=SLICE_MAX_LIFE_SECS) if duration > max_duration: self.logger.warning('Expiration %s is too far in the future.', expire_str) return False # Everything checks out, so create a new slice cred and tuck it away. user_gid = gid.GID(string=self._server.pem_cert) slice_cred = self.slices[slice_urn] slice_gid = slice_cred.get_gid_object() duration_secs = duration.seconds + duration.days * 24 * 3600 slice_cred = self.create_slice_credential(user_gid, slice_gid, duration_secs) self.logger.info("Slice %s renewed to %s", slice_urn, expire_str) return True
def verify_from_strings(self, gid_string, cred_strings, target_urn, privileges): '''Create Credential and GID objects from the given strings, and then verify the GID has the right privileges according to the given credentials on the given target.''' def make_cred(cred_string): return cred.Credential(string=cred_string) return self.verify(gid.GID(string=gid_string), map(make_cred, cred_strings), target_urn, privileges)
def CreateUserCredential(self, user_gid): '''Return string representation of a user credential issued by this CH with caller/object this user_gid (string) with user privileges''' # FIXME: Validate arg - non empty, my user user_gid = gid.GID(string=user_gid) self.logger.info("Called CreateUserCredential for GID %s" % user_gid.get_hrn()) expiration = datetime.datetime.utcnow() + datetime.timedelta(seconds=USER_CRED_LIFE) try: ucred = cred_util.create_credential(user_gid, user_gid, expiration, 'user', self.keyfile, self.certfile, self.trusted_root_files) except Exception, exc: self.logger.error("Failed to create user credential for %s: %s", user_gid.get_hrn(), traceback.format_exc()) raise Exception("Failed to create user credential for %s" % user_gid.get_hrn(), exc)
def CreateSlice(user_cert, urn_req=None): # Is this user allowed to create a slice? # first get the user with this cert username = get_username_from_cert(user_cert) try: User.objects.get(username=username) except User.DoesNotExist: raise Exception("Unknown user %s." % username) if urn_req: # check the requested URN urn = URN(urn=urn_req) # make sure that we would generate the same urn if using the # same name (i.e. authority is the same...) urn_gen = get_slice_urn(urn.getName()) if urn_gen != urn_req: raise BadURNException( "The requested URN is not one that would be generated" " by this clearinghouse. Requested was %s, but generated" " is %s" % (urn_req, urn_gen) ) else: # Generate a unique URN for the slice urn_req = create_slice_urn() try: slice_gid = create_x509_cert(urn_req)[0] except Exception as exc: logger.error("Could not create slice. Error\n %s" % traceback.format_exc()) raise Exception("Failed to create slice %s." % urn_req) # Now get the user GID which will have permissions on this slice. # It doesnt have the chain but should be signed # by this CHs cert, which should also be a trusted # root at any federated AM. So everyone can verify it as is. # Note that if a user from a different CH (installed # as trusted by this CH for some reason) called this method, # that user would be used here - and can still get a valid slice try: user_gid = gid.GID(string=user_cert) except Exception, exc: logger.error("CreateSlice failed to create user_gid from SSL client cert: %s", traceback.format_exc()) raise Exception("Failed to create slice %s. Cant get user GID from SSL client certificate." % urn_req, exc)
def CreateUserCredential(user_gid): '''Return string representation of a user credential issued by this CH with caller/object this user_gid (string) with user privileges''' username = get_username_from_cert(user_gid) try: User.objects.get(username=username) except User.DoesNotExist: raise Exception("Unknown user %s." % username) user_gid = gid.GID(string=user_gid) logger.info("Called CreateUserCredential for GID %s" % user_gid.get_hrn()) try: ucred = create_user_credential(user_gid) except Exception, exc: logger.error("Failed to create user credential for %s: %s", user_gid.get_hrn(), traceback.format_exc()) raise Exception("Failed to create user credential for %s" % user_gid.get_hrn(), exc)
def RenewSlice(self, slice_urn, expire_str): self.logger.info("Called RenewSlice(%s, %s)", slice_urn, expire_str) if not self.slices.has_key(slice_urn): self.logger.warning('Slice %s was not found', slice_urn) return False try: in_expiration = dateutil.parser.parse(expire_str) in_expiration = cred_util.naiveUTC(in_expiration) except: self.logger.warning('Unable to parse date "%s"', expire_str) return False # Is requested expiration valid? It must be in the future, # but not too far into the future. now = datetime.datetime.utcnow() if in_expiration < now: self.logger.warning('Expiration "%s" is in the past.', expire_str) return False duration = in_expiration - now max_duration = datetime.timedelta(seconds=SLICE_MAX_LIFE_SECS) if duration > max_duration: self.logger.warning('Expiration %s is too far in the future.', expire_str) return False # Everything checks out, so create a new slice cred and tuck it away. user_gid = gid.GID(string=self._server.pem_cert) slice_cred = self.slices[slice_urn] slice_gid = slice_cred.get_gid_object() # if original slice' privileges were all delegatable, # make all the privs here delegatable # Of course, the correct thing would be to do it priv by priv... dgatable = False if slice_cred.get_privileges().get_all_delegate(): dgatable = True slice_cred = self.create_slice_credential(user_gid, slice_gid, in_expiration, delegatable=dgatable) self.logger.info("Slice %s renewed to %s", slice_urn, expire_str) self.slices[slice_urn] = slice_cred return True
def create_credential(caller_gid, object_gid, life_secs, typename, issuer_keyfile, issuer_certfile, trusted_roots): '''Create and Return a Credential object issued by given key/cert for the given caller and object GID objects, given life in seconds, and given type. Privileges are determined by type per sfa/trust/rights.py''' # FIXME: Validate args: my gids, >0 life, # type of cred one I can issue # and readable key and cert files if caller_gid is None: raise ValueError("Missing Caller GID") if object_gid is None: raise ValueError("Missing Object GID") if life_secs is None or life_secs < 1: raise ValueError("Credential life in seconds was 0") if trusted_roots is None: raise ValueError("Missing list of trusted roots") if typename is None or typename.strip() == '': raise ValueError("Missing credential type") typename = typename.strip().lower() if typename not in ("user", "sa", "ma", "authority", "slice", "component"): raise ValueError("Unknown credential type %s" % typename) if not os.path.isfile(issuer_keyfile): raise ValueError("Cant read issuer key file %s" % issuer_keyfile) if not os.path.isfile(issuer_certfile): raise ValueError("Cant read issuer cert file %s" % issuer_certfile) issuer_gid = gid.GID(filename=issuer_certfile) if not (object_gid.get_urn() == issuer_gid.get_urn() or (issuer_gid.get_type() == 'authority' and object_gid.get_urn().split('+')[1].startswith( issuer_gid.get_urn().split('+')[1]))): raise ValueError( "Issuer not authorized to issue credential: Issuer=%s Target=%s" % (issuer_gid.get_urn(), object_gid.get_urn())) ucred = cred.Credential() # FIXME: Validate the caller_gid and object_gid # are my user and slice # Do get_issuer and compare to the issuer cert? # Or do gid.is_signed_by_cert(issuer_certfile)? ucred.set_gid_caller(caller_gid) ucred.set_gid_object(object_gid) ucred.set_lifetime(life_secs) # Use sfa/trust/rights.py to figure out what privileges # the credential should have. # user means refresh, resolve, info # per the privilege_table that lets users do # remove, update, resolve, list, getcredential, # listslices, listnodes, getpolicy # Note that it does not allow manipulating slivers privileges = rights.determine_rights(typename, None) ucred.set_privileges(privileges) ucred.encode() ucred.set_issuer_keys(issuer_keyfile, issuer_certfile) ucred.sign() try: ucred.verify(trusted_roots) except Exception, exc: raise Exception( "Create Credential failed to verify new credential from trusted roots: %s" % exc)
class Clearinghouse(object): def __init__(self): self.logger = cred_util.logging.getLogger('gcf-ch') self.slices = {} self.aggs = [] def load_aggregates(self): """Loads aggregates from the clearinghouse section of the config file. In the config section there are keys for each am, am_1, am_2, ..., am_n The value for each key is the urn and url of the aggregate separated by a comma Returns True if aggregates were loaded, False otherwise. """ for (key, val) in self.config['clearinghouse'].items(): if not key.startswith('am_'): continue (urn, url) = val.split(',') urn = urn.strip() url = url.strip() if not urn: self.logger.warn('Empty URN for aggregate %s in gcf_config' % key) continue if not url: self.logger.warn('Empty URL for aggregate %s in gcf_config' % key) continue if urn in [x for (x, _) in self.aggs]: self.logger.warn('Duplicate URN %s in gcf_config' % key) continue self.logger.info("Registering AM %s at %s", urn, url) self.aggs.append((urn, url)) def runserver(self, addr, keyfile=None, certfile=None, ca_certs=None, authority=None, user_len=None, slice_len=None, config=None): """Run the clearinghouse server.""" # ca_certs is a dir of several certificates for peering # If not supplied just use the certfile as the only trusted root self.keyfile = keyfile self.certfile = certfile self.config = config # Error check the keyfile, certfile all exist if keyfile is None or not os.path.isfile( os.path.expanduser(keyfile)) or os.path.getsize( os.path.expanduser(keyfile)) < 1: raise Exception("Missing CH key file %s" % keyfile) if certfile is None or not os.path.isfile( os.path.expanduser(certfile)) or os.path.getsize( os.path.expanduser(certfile)) < 1: raise Exception("Missing CH cert file %s" % certfile) if ca_certs is None: ca_certs = certfile self.logger.info("Using only my CH cert as a trusted root cert") self.trusted_root_files = cred_util.CredentialVerifier( ca_certs).root_cert_files if not os.path.exists(os.path.expanduser(ca_certs)): raise Exception("Missing CA cert(s): %s" % ca_certs) global SLICE_AUTHORITY, USER_CRED_LIFE, SLICE_CRED_LIFE SLICE_AUTHORITY = authority USER_CRED_LIFE = int(user_len) SLICE_CRED_LIFE = int(slice_len) # Load up the aggregates self.load_aggregates() # This is the arg to _make_server ca_certs_onefname = cred_util.CredentialVerifier.getCAsFileFromDir( ca_certs) # This is used below by CreateSlice self.ca_cert_fnames = [] if os.path.isfile(os.path.expanduser(ca_certs)): self.ca_cert_fnames = [os.path.expanduser(ca_certs)] elif os.path.isdir(os.path.expanduser(ca_certs)): self.ca_cert_fnames = [ os.path.join(os.path.expanduser(ca_certs), name) for name in os.listdir(os.path.expanduser(ca_certs)) if name != cred_util.CredentialVerifier.CATEDCERTSFNAME ] # Create the xmlrpc server, load the rootkeys and do the ssl thing. self._server = self._make_server(addr, keyfile, certfile, ca_certs_onefname) self._server.register_instance(SampleClearinghouseServer(self)) self.logger.info('GENI CH Listening on port %d...' % (addr[1])) self._server.serve_forever() def _make_server(self, addr, keyfile=None, certfile=None, ca_certs=None): """Creates the XML RPC server.""" # ca_certs is a file of concatenated certs # make 2nd arg logRequests=True if --debug debug = False if self.config.has_key('debug'): debug = self.config['debug'] return SecureXMLRPCServer(addr, logRequests=debug, keyfile=keyfile, certfile=certfile, ca_certs=ca_certs) def _naiveUTC(self, dt): """Converts dt to a naive datetime in UTC. if 'dt' has a timezone then convert to UTC strip off timezone (make it "naive" in Python parlance) """ if dt.tzinfo: tz_utc = dateutil.tz.tzutc() dt = dt.astimezone(tz_utc) dt = dt.replace(tzinfo=None) return dt def GetVersion(self): self.logger.info("Called GetVersion") version = dict() version['gcf-ch_api'] = 1 return version # FIXME: Change that URN to be a name and non-optional # Currently gcf-test.py doesnt supply it, and # Omni takes a name and constructs a URN to supply def CreateSlice(self, urn_req=None): self.logger.info("Called CreateSlice URN REQ %r" % urn_req) slice_gid = None if urn_req and self.slices.has_key(urn_req): # If the Slice has expired, treat this as # a request to renew slice_cred = self.slices[urn_req] slice_exp = self._naiveUTC(slice_cred.expiration) if slice_exp <= datetime.datetime.utcnow(): # Need to renew this slice self.logger.info( "CreateSlice on %r found existing cred that expired at %r - will renew", urn_req, slice_exp) slice_gid = slice_cred.get_gid_object() else: self.logger.debug( "Slice cred is still valid at %r until %r - return it", datetime.datetime.utcnow(), slice_exp) return slice_cred.save_to_string() # Create a random uuid for the slice slice_uuid = uuid.uuid4() # First ensure we have a slice_urn if urn_req: # Validate urn_req has the right form # to be issued by this CH if not urn_util.is_valid_urn(urn_req): # FIXME: make sure it isnt empty, etc... urn = urn_util.publicid_to_urn(urn_req) else: urn = urn_req # Validate the urn meets name restrictions if not urn_util.is_valid_urn_bytype(urn, 'slice', self.logger): raise Exception( "Cannot create slice with urn %s: URN is invalid" % urn) else: # Generate a unique URN for the slice # based on this CH location and a UUID # Where was the slice created? (ipaddr, port) = self._server.socket._sock.getsockname() # FIXME: Get public_id start from a properties file # Create a unique name for the slice based on uuid slice_name = slice_uuid.__str__()[4:12] public_id = 'IDN %s slice %s//%s:%d' % (SLICE_AUTHORITY, slice_name, ipaddr, port) # this func adds the urn:publicid: # and converts spaces to +'s, and // to : urn = urn_util.publicid_to_urn(public_id) # Now create a GID for the slice (signed credential) if slice_gid is None: # FIXME: For APIv3 compliance, we need # - slice email address # - unique cert serial number try: slice_gid = cert_util.create_cert(urn, self.keyfile, self.certfile, uuidarg=slice_uuid)[0] except Exception, exc: self.logger.error("Cant create slice gid for slice urn %s: %s", urn, traceback.format_exc()) raise Exception( "Failed to create slice %s. Cant create slice gid" % urn, exc) # Now get the user GID which will have permissions on this slice. # Get client x509 cert from the SSL connection # It doesnt have the chain but should be signed # by this CHs cert, which should also be a trusted # root at any federated AM. So everyone can verify it as is. # Note that if a user from a different CH (installed # as trusted by this CH for some reason) called this method, # that user would be used here - and can still get a valid slice try: user_gid = gid.GID(string=self._server.pem_cert) except Exception, exc: self.logger.error( "CreateSlice failed to create user_gid from SSL client cert: %s", traceback.format_exc()) raise Exception( "Failed to create slice %s. Cant get user GID from SSL client certificate." % urn, exc)
def create_credential(caller_gid, object_gid, expiration, typename, issuer_keyfile, issuer_certfile, trusted_roots, delegatable=False): '''Create and Return a Credential object issued by given key/cert for the given caller and object GID objects, given life in seconds, and given type. Privileges are determined by type per sfa/trust/rights.py Privileges are delegatable if requested.''' # FIXME: Validate args: my gids, >0 life, # type of cred one I can issue # and readable key and cert files if caller_gid is None: raise ValueError("Missing Caller GID") if object_gid is None: raise ValueError("Missing Object GID") if expiration is None: raise ValueError("Missing expiration") naive_expiration = naiveUTC(expiration) duration = naive_expiration - datetime.datetime.utcnow() life_secs = duration.seconds + duration.days * 24 * 3600 if life_secs < 1: raise ValueError("Credential expiration is in the past") if trusted_roots is None: raise ValueError("Missing list of trusted roots") if typename is None or typename.strip() == '': raise ValueError("Missing credential type") typename = typename.strip().lower() if typename not in ("user", "sa", "ma", "authority", "slice", "component"): raise ValueError("Unknown credential type %s" % typename) if not os.path.isfile(issuer_keyfile): raise ValueError("Cant read issuer key file %s" % issuer_keyfile) if not os.path.isfile(issuer_certfile): raise ValueError("Cant read issuer cert file %s" % issuer_certfile) issuer_gid = gid.GID(filename=issuer_certfile) if not (object_gid.get_urn() == issuer_gid.get_urn() or (issuer_gid.get_type().find('authority') == 0 and hrn_authfor_hrn(issuer_gid.get_hrn(), object_gid.get_hrn()))): raise ValueError( "Issuer not authorized to issue credential: Issuer=%s Target=%s" % (issuer_gid.get_urn(), object_gid.get_urn())) ucred = cred.Credential() # FIXME: Validate the caller_gid and object_gid # are my user and slice # Do get_issuer and compare to the issuer cert? # Or do gid.is_signed_by_cert(issuer_certfile)? ucred.set_gid_caller(caller_gid) ucred.set_gid_object(object_gid) ucred.set_expiration(expiration) # Use sfa/trust/rights.py to figure out what privileges # the credential should have. # user means refresh, resolve, info # per the privilege_table that lets users do # remove, update, resolve, list, getcredential, # listslices, listnodes, getpolicy # Note that it does not allow manipulating slivers # And every right is delegatable if any are delegatable (default False) privileges = rights.determine_rights(typename, None) privileges.delegate_all_privileges(delegatable) ucred.set_privileges(privileges) ucred.encode() ucred.set_issuer_keys(issuer_keyfile, issuer_certfile) ucred.sign() try: ucred.verify(trusted_roots) except Exception, exc: raise Exception( "Create Credential failed to verify new credential from trusted roots: %s" % exc)