def validate_user_cert(user_cert, signing_user_id, check_admin=False): """ Given a deserialized user certificate, check its validity: * make sure user identified by signing_user_id exists * make sure the admin identified in user_cert exists, if check_admin is set * verify that the user identified by signing_user_id has signed the cert Return the signing user and the admin user on success Raise an exception on error """ from common.api import verify_data signing_user_fut = SyndicateUser.Read(signing_user_id, async=True) admin_user_fut = None if user_cert.admin_id != signing_user_id: admin_user_fut = SyndicateUser.Read(user_cert.admin_id, async=True) storagetypes.wait_futures([signing_user_fut, admin_user_fut]) else: storagetypes.wait_futures([signing_user_fut]) signing_user = signing_user_fut.get_result() if admin_user_fut is not None: admin_user = admin_user_fut.get_result() else: # signing user and admin are the same admin_user = signing_user if check_admin: if admin_user is None: raise Exception( "The admin (%s) who signed this user cert does not exist" % user_cert.admin_id) if not admin_user.is_admin: raise Exception("User '%s' is not an admin" % (admin_user.email)) if signing_user is None: raise Exception("No such user '%s'" % signing_user_id) user_pubkey = signing_user.public_key user_sig = user_cert.signature user_cert.signature = "" user_cert_nosigs = user_cert.SerializeToString() rc = verify_data(user_pubkey, user_cert_nosigs, base64.b64decode(user_sig)) user_cert.signature = user_sig if not rc: raise Exception("User '%s' did not sign cert" % (signing_user.email)) return signing_user, admin_user
def validate_user_cert( user_cert, signing_user_id, check_admin=False ): """ Given a deserialized user certificate, check its validity: * make sure user identified by signing_user_id exists * make sure the admin identified in user_cert exists, if check_admin is set * verify that the user identified by signing_user_id has signed the cert Return the signing user and the admin user on success Raise an exception on error """ from common.api import verify_data signing_user_fut = SyndicateUser.Read( signing_user_id, async=True ) admin_user_fut = None if user_cert.admin_id != signing_user_id: admin_user_fut = SyndicateUser.Read( user_cert.admin_id, async=True ) storagetypes.wait_futures( [signing_user_fut, admin_user_fut] ) else: storagetypes.wait_futures( [signing_user_fut] ) signing_user = signing_user_fut.get_result() if admin_user_fut is not None: admin_user = admin_user_fut.get_result() else: # signing user and admin are the same admin_user = signing_user if check_admin: if admin_user is None: raise Exception("The admin (%s) who signed this user cert does not exist" % user_cert.admin_id ) if not admin_user.is_admin: raise Exception("User '%s' is not an admin" % (admin_user.email)) if signing_user is None: raise Exception("No such user '%s'" % signing_user_id ) user_pubkey = signing_user.public_key user_sig = user_cert.signature user_cert.signature = "" user_cert_nosigs = user_cert.SerializeToString() rc = verify_data( user_pubkey, user_cert_nosigs, base64.b64decode( user_sig ) ) user_cert.signature = user_sig if not rc: raise Exception("User '%s' did not sign cert" % (signing_user.email)) return signing_user, admin_user
def validate_gateway_cert( gateway_cert, signing_user_id ): """ Given a deserialized gateway certificate, check its validity: * make sure the user and volume it points to exist * make sure the user in the cert signed it, or verify that the user identified by signing_user_id did (e.g. signing_user_id might correspond to a volume owner or admin) Return the user, volume, and signing user (if found) on success Raise an exception on error """ from common.api import verify_data # user and volume must both exist user, volume = _read_user_and_volume( gateway_cert.owner_id, gateway_cert.volume_id ) signing_user = None if user is None: raise Exception("No such user '%s'" % gateway_cert.owner_id) if (volume is None or volume.deleted): raise Exception("No such volume '%s'" % gateway_cert.volume_id) pubkey = None signer_email = None signing_user = None if signing_user_id is not None: signing_user = SyndicateUser.Read( signing_user_id ) if signing_user is None: raise Exception("No such signing user '%s'" % signing_user_id) pubkey = signing_user.public_key signer_email = signing_user.email else: pubkey = user.public_key signer_email = user.email # verify that the user (or signing user) has signed the cert owner_sig = gateway_cert.signature gateway_cert.signature = "" gateway_cert_nosigs = gateway_cert.SerializeToString() rc = verify_data( pubkey, gateway_cert_nosigs, base64.b64decode( owner_sig ) ) gateway_cert.signature = owner_sig if not rc: raise Exception("Gateway certificate not signed by user '%s'" % signer_email ) return (user, volume)
def validate_volume_cert(volume_cert, signing_user_id=None): """ Given a deserialized volume certificate, check its validity: * make sure the user exists * make sure the user in the cert signed it, or verify that the user identified by signing_user_id did (e.g. signing_user_id might correspond to a volume owner or admin) Return the (user, volume) on success (volume can be None if we haven't created it yet) Raise an exception on error """ from common.api import verify_data # user and volume must both exist user, volume = _read_user_and_volume(volume_cert.owner_id, volume_cert.volume_id) if user is None: raise Exception("No such user '%s'" % volume_cert.owner_id) if signing_user_id is not None and user.owner_id != signing_user_id: raise Exception( "User '%s' did not sign this volume cert (got '%s' instead)" % (signing_user_id, user.owner_id)) # verify that the user has signed the cert owner_pubkey = user.public_key owner_sig = volume_cert.signature volume_root = None if volume_cert.HasField("root"): volume_root = volume_cert.root volume_cert.ClearField("root") volume_cert.signature = "" volume_cert_nosigs = volume_cert.SerializeToString() if volume_root is not None: volume_cert.root.MergeFrom(volume_root) volume_cert.signature = owner_sig rc = verify_data(owner_pubkey, volume_cert_nosigs, base64.b64decode(owner_sig)) if not rc: raise Exception("Volume certificate not signed by user '%s'" % volume_cert.owner_id) return user, volume
def validate_volume_cert( volume_cert, signing_user_id=None ): """ Given a deserialized volume certificate, check its validity: * make sure the user exists * make sure the user in the cert signed it, or verify that the user identified by signing_user_id did (e.g. signing_user_id might correspond to a volume owner or admin) Return the (user, volume) on success (volume can be None if we haven't created it yet) Raise an exception on error """ from common.api import verify_data # user and volume must both exist user, volume = _read_user_and_volume( volume_cert.owner_id, volume_cert.volume_id ) if user is None: raise Exception("No such user '%s'" % volume_cert.owner_id) if signing_user_id is not None and user.owner_id != signing_user_id: raise Exception("User '%s' did not sign this volume cert (got '%s' instead)" % (signing_user_id, user.owner_id)) # verify that the user has signed the cert owner_pubkey = user.public_key owner_sig = volume_cert.signature volume_root = None if volume_cert.HasField("root"): volume_root = volume_cert.root volume_cert.ClearField("root") volume_cert.signature = "" volume_cert_nosigs = volume_cert.SerializeToString() if volume_root is not None: volume_cert.root.MergeFrom( volume_root ) volume_cert.signature = owner_sig rc = verify_data( owner_pubkey, volume_cert_nosigs, base64.b64decode( owner_sig ) ) if not rc: raise Exception("Volume certificate not signed by user '%s'" % volume_cert.owner_id ) return user, volume
def validate_cert_bundle( cert_bundle, user, volume_id, volume_version, new_gateway_cert=None ): """ Given a deserialized cert bundle, check its validity: * make sure the user it points to exists * make sure it has the right volume ID (load from the datastore by default, or use volume_id or volume kw if given) * make sure the user signed it, or verify that the user identified by signing_user_id did. * make sure its volume version is equal to or exceeds the given volume_version * make sure the cert bundle's timestamp exceeds the previous cert bundle's timestamp * make sure each of its gateway versions are equal to or exceeds the local copies of the gateway certs' versions --- if new_gateway_cert is given, it will be checked in place of an on-file gateway cert. This is useful when updating a gateway--we need to check the gateway cert that will be written, not the one on file. Return True if so Raise an exception on error. """ from common.api import verify_data if user.owner_id != cert_bundle.owner_id: raise Exception("Invalid cert bundle: invalid owner ID") # verify the user signed the cert owner_pubkey = user.public_key owner_sig = cert_bundle.signature cert_bundle.signature = "" cert_bundle_nosigs = cert_bundle.SerializeToString() rc = verify_data( owner_pubkey, cert_bundle_nosigs, base64.b64decode( owner_sig ) ) cert_bundle.signature = owner_sig if not rc: raise Exception("Cert bundle not signed by user '%s'" % signing_user_id) # volume ID must match if volume_id != cert_bundle.volume_id: raise Exception("Invalid volume ID: %s != %s" % (volume_id, cert_bundle.volume_id)) # volume version must increase or stay the same # NOTE: Manifest.file_version encodes the volume version, when being used as a cert bundle if volume_version > cert_bundle.file_version: raise Exception("Stale volume version for %s: expected > %s, got %s" % (volume_id, volume_version, cert_bundle.file_version)) # get the previous cert bundle prev_cert_bundle_rec = VolumeCertBundle.Get( volume_id ) if prev_cert_bundle_rec is not None: # this cert bundle's timestamp must be newer prev_cert_bundle = VolumeCertBundle.Load( prev_cert_bundle_rec ) if prev_cert_bundle is not None: if prev_cert_bundle.mtime_sec > cert_bundle.mtime_sec or (prev_cert_bundle.mtime_sec == cert_bundle.mtime_sec and prev_cert_bundle.mtime_nsec > cert_bundle.mtime_nsec): # stale raise Exception("Stale cert bundle for %s: expected > %s.%s, got %s.%s" % (volume_id, prev_cert_bundle.mtime_sec, prev_cert_bundle.mtime_nsec, cert_bundle.mtime_sec, cert_bundle.mtime_nsec)) gateway_cert_and_fut = {} # map gateway ID to (gateway cert block, gateway future) all_futs = [] # cert is valid. # validate volume cert--it must match information in the cert manifest if len(cert_bundle.blocks) == 0: raise Exception("Missing volume cert in cert bundle") volume_cert_block = cert_bundle.blocks[0] if volume_cert_block.block_id != volume_id: raise Exception("Cert block for volume does not match volume ID (expected %s, got %s)" % (volume_id, volume_cert_block.block_id)) if volume_cert_block.block_version != cert_bundle.file_version: raise Exception("Cert block for volume does not match volume version (expected %s, got %s)" % (cert_bundle.file_version, volume_cert_block.block_version)) if volume_cert_block.owner_id != user.owner_id: raise Exception("Cert block for volume does not match owner ID (expected %s, got %s)" % (user.owner_id, volume_cert_block.owner_id)) # go validate the affected gateways # NOTE: blocks[0] contains the volume cert (used by gateways) for i in xrange( 1, len(cert_bundle.blocks) ): block = cert_bundle.blocks[i] gateway_id = block.block_id if new_gateway_cert is not None and new_gateway_cert.gateway_id == gateway_id: # skip this one--we'll check below continue gateway_fut = Gateway.Read( gateway_id, async=True ) gateway_cert_and_fut[ gateway_id ] = (block, gateway_fut) all_futs.append( gateway_fut ) storagetypes.wait_futures( all_futs ) for (gateway_id, (block, gateway_fut)) in gateway_cert_and_fut.items(): # from the cert bundle given_gateway_version = None given_gateway_caps = None # on file gateway_version = None gateway_caps = None if new_gateway_cert is not None and new_gateway_cert.gateway_id == gateway_id: # make sure the new gateway cert has an appropriate version given_gateway_version = new_gateway_cert.gateway_version given_gateway_caps = new_gateway_cert.caps # don't worry about the caps--we'll be updating them shortly gateway_version = gateway.version gateway_caps = new_gateway_cert.caps else: # use the gateway on file gateway = gateway_fut.get_result() if gateway is None: # requested a gateway that didn't exist raise Exception("Cert bundle identifies non-existant gateway") if gateway.g_id != gateway_id: # this is a bug--the gateway is keyed by id raise Exception("BUG: requested %s, got %s" % (gateway_id, gateway.g_id)) given_gateway_caps = block.caps given_gateway_version = gateway.cert_version # can only check new gateway certs # check against on-file cert gateway_version = gateway.cert_version gateway_caps = gateway.caps # gateway version must increase or stay the same if gateway_version > given_gateway_version: # stale version raise Exception("Stale version for %s: expected >= %s, got %s" % (gateway_id, gateway_version, given_gateway_version)) if (gateway_caps | given_gateway_caps) != given_gateway_caps: # invalid caps (only works when checked against on-file gateways) raise Exception("Invalid caps for %s: expected %s, got %s" % (gateway_id, gateway.caps, given_gateway_caps)) # valid! return True
if num_volumes > user.get_volume_quota(): raise Exception("User '%s' has exceeded Volume quota of %s" % (user.email, user.get_volume_quota()) ) # verify the root inode root_inode = volume_cert.root root_inode_sigb64 = root_inode.signature root_inode_sig = base64.b64decode( root_inode_sigb64 ) root_inode.signature = "" root_inode_nosig = root_inode.SerializeToString() root_inode.signature = root_inode_sigb64 rc = verify_data( caller_user.public_key, root_inode_nosig, root_inode_sig ) if not rc: raise Exception("Root inode not signed by user '%s'" % (user.email)) # finally, verify that the name is *not* numeric tmp = None try: tmp = int( volume_cert.name ) except: pass if tmp is not None: raise Exception("Invalid volume name '%s'" % volume_cert.name ) new_volume_key = Volume.Create( user, volume_cert ) if new_volume_key is not None:
def validate_gateway_cert(gateway_cert, signing_user_id): """ Given a deserialized gateway certificate, check its validity: * make sure the user and volume it points to exist * make sure the user in the cert signed it, or verify that the user identified by signing_user_id did (e.g. signing_user_id might correspond to a volume owner or admin) Return the user, volume, and signing user (if found) on success Raise an exception on error """ from common.api import verify_data # user and volume must both exist user, volume = _read_user_and_volume(gateway_cert.owner_id, gateway_cert.volume_id) signing_user = None if user is None and gateway_cert.owner_id != USER_ID_ANON: raise Exception("No such user '%s'" % gateway_cert.owner_id) if (volume is None or volume.deleted): raise Exception("No such volume '%s'" % gateway_cert.volume_id) pubkey = None signer_email = None signing_user = None if signing_user_id is not None: signing_user = SyndicateUser.Read(signing_user_id) if signing_user is None: raise Exception("No such signing user '%s'" % signing_user_id) pubkey = signing_user.public_key signer_email = signing_user.email elif gateway_cert.owner_id == USER_ID_ANON: signing_user = SyndicateUser.Read(volume.owner_id) if signing_user is None: raise Exception("No such signing user '%s'" % volume.owner_id) pubkey = signing_user.public_key signer_email = signing_user.email user = signing_user else: pubkey = user.public_key signer_email = user.email # verify that the user (or signing user) has signed the cert owner_sig = gateway_cert.signature gateway_cert.signature = "" gateway_cert_nosigs = gateway_cert.SerializeToString() rc = verify_data(pubkey, gateway_cert_nosigs, base64.b64decode(owner_sig)) gateway_cert.signature = owner_sig if not rc: raise Exception("Gateway certificate not signed by user '%s'" % signer_email) return (user, volume)
def validate_cert_bundle(cert_bundle, user, volume_id, volume_version, new_gateway_cert=None): """ Given a deserialized cert bundle, check its validity: * make sure the user it points to exists * make sure it has the right volume ID (load from the datastore by default, or use volume_id or volume kw if given) * make sure the user signed it, or verify that the user identified by signing_user_id did. * make sure its volume version is equal to or exceeds the given volume_version * make sure the cert bundle's timestamp exceeds the previous cert bundle's timestamp * make sure each of its gateway versions are equal to or exceeds the local copies of the gateway certs' versions --- if new_gateway_cert is given, it will be checked in place of an on-file gateway cert. This is useful when updating a gateway--we need to check the gateway cert that will be written, not the one on file. Return True if so Raise an exception on error. """ from common.api import verify_data if user.owner_id != cert_bundle.owner_id: raise Exception("Invalid cert bundle: invalid owner ID") # verify the user signed the cert owner_pubkey = user.public_key owner_sig = cert_bundle.signature cert_bundle.signature = "" cert_bundle_nosigs = cert_bundle.SerializeToString() rc = verify_data(owner_pubkey, cert_bundle_nosigs, base64.b64decode(owner_sig)) cert_bundle.signature = owner_sig if not rc: raise Exception("Cert bundle not signed by user '%s'" % signing_user_id) # volume ID must match if volume_id != cert_bundle.volume_id: raise Exception("Invalid volume ID: %s != %s" % (volume_id, cert_bundle.volume_id)) # volume version must increase or stay the same # NOTE: Manifest.file_version encodes the volume version, when being used as a cert bundle if volume_version > cert_bundle.file_version: raise Exception("Stale volume version for %s: expected > %s, got %s" % (volume_id, volume_version, cert_bundle.file_version)) # get the previous cert bundle prev_cert_bundle_rec = VolumeCertBundle.Get(volume_id) if prev_cert_bundle_rec is not None: # this cert bundle's timestamp must be newer prev_cert_bundle = VolumeCertBundle.Load(prev_cert_bundle_rec) if prev_cert_bundle is not None: if prev_cert_bundle.mtime_sec > cert_bundle.mtime_sec or ( prev_cert_bundle.mtime_sec == cert_bundle.mtime_sec and prev_cert_bundle.mtime_nsec > cert_bundle.mtime_nsec): # stale raise Exception( "Stale cert bundle for %s: expected > %s.%s, got %s.%s" % (volume_id, prev_cert_bundle.mtime_sec, prev_cert_bundle.mtime_nsec, cert_bundle.mtime_sec, cert_bundle.mtime_nsec)) gateway_cert_and_fut = { } # map gateway ID to (gateway cert block, gateway future) all_futs = [] # cert is valid. # validate volume cert--it must match information in the cert manifest if len(cert_bundle.blocks) == 0: raise Exception("Missing volume cert in cert bundle") volume_cert_block = cert_bundle.blocks[0] if volume_cert_block.block_id != volume_id: raise Exception( "Cert block for volume does not match volume ID (expected %s, got %s)" % (volume_id, volume_cert_block.block_id)) if volume_cert_block.block_version != cert_bundle.file_version: raise Exception( "Cert block for volume does not match volume version (expected %s, got %s)" % (cert_bundle.file_version, volume_cert_block.block_version)) if volume_cert_block.owner_id != user.owner_id: raise Exception( "Cert block for volume does not match owner ID (expected %s, got %s)" % (user.owner_id, volume_cert_block.owner_id)) # go validate the affected gateways # NOTE: blocks[0] contains the volume cert (used by gateways) for i in xrange(1, len(cert_bundle.blocks)): block = cert_bundle.blocks[i] gateway_id = block.block_id if new_gateway_cert is not None and new_gateway_cert.gateway_id == gateway_id: # skip this one--we'll check below continue gateway_fut = Gateway.Read(gateway_id, async=True) gateway_cert_and_fut[gateway_id] = (block, gateway_fut) all_futs.append(gateway_fut) storagetypes.wait_futures(all_futs) for (gateway_id, (block, gateway_fut)) in gateway_cert_and_fut.items(): # from the cert bundle given_gateway_version = None given_gateway_caps = None # on file gateway_version = None gateway_caps = None if new_gateway_cert is not None and new_gateway_cert.gateway_id == gateway_id: # make sure the new gateway cert has an appropriate version given_gateway_version = new_gateway_cert.gateway_version given_gateway_caps = new_gateway_cert.caps # don't worry about the caps--we'll be updating them shortly gateway_version = gateway.version gateway_caps = new_gateway_cert.caps else: # use the gateway on file gateway = gateway_fut.get_result() if gateway is None: # requested a gateway that didn't exist raise Exception( "Cert bundle identifies non-existant gateway %s" % gateway_id) if gateway.g_id != gateway_id: # this is a bug--the gateway is keyed by id raise Exception("BUG: requested %s, got %s" % (gateway_id, gateway.g_id)) given_gateway_caps = block.caps given_gateway_version = gateway.cert_version # can only check new gateway certs # check against on-file cert gateway_version = gateway.cert_version gateway_caps = gateway.caps # gateway version must increase or stay the same if gateway_version > given_gateway_version: # stale version raise Exception( "Stale version for %s: expected >= %s, got %s" % (gateway_id, gateway_version, given_gateway_version)) if (gateway_caps | given_gateway_caps) != given_gateway_caps: # invalid caps (only works when checked against on-file gateways) raise Exception("Invalid caps for %s: expected %s, got %s" % (gateway_id, gateway.caps, given_gateway_caps)) # valid! return True
if num_volumes > user.get_volume_quota(): raise Exception("User '%s' has exceeded Volume quota of %s" % (user.email, user.get_volume_quota())) # verify the root inode root_inode = volume_cert.root root_inode_sigb64 = root_inode.signature root_inode_sig = base64.b64decode(root_inode_sigb64) root_inode.signature = "" root_inode_nosig = root_inode.SerializeToString() root_inode.signature = root_inode_sigb64 rc = verify_data(user.public_key, root_inode_nosig, root_inode_sig) if not rc: raise Exception("Root inode not signed by user '%s'" % (user.email)) # finally, verify that the name is *not* numeric tmp = None try: tmp = int(volume_cert.name) except: pass if tmp is not None: raise Exception("Invalid volume name '%s'" % volume_cert.name) new_volume_key = Volume.Create(user, volume_cert) if new_volume_key is not None: