Beispiel #1
0
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
Beispiel #2
0
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
Beispiel #3
0
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)
Beispiel #4
0
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
Beispiel #5
0
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
Beispiel #6
0
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
Beispiel #7
0
      
    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:
Beispiel #8
0
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)
Beispiel #9
0
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
Beispiel #10
0
        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: