Esempio n. 1
0
    def render(self, req):
        if req.method != 'POST':
            raise server.UnsupportedMethod(('POST',))
        if req.args.get('token', False):
            raise WebError("Do not pass 'token' as URL argument", http.BAD_REQUEST)
        # not using get_arg() here because we *don't* want the token
        # argument to work if you passed it as a GET-style argument
        token = None
        if req.fields and 'token' in req.fields:
            token = req.fields['token'].value.strip()
        if not token:
            raise WebError("Missing token", http.UNAUTHORIZED)
        if not timing_safe_compare(token, self.client.get_auth_token()):
            raise WebError("Invalid token", http.UNAUTHORIZED)

        t = get_arg(req, "t", "").strip()
        if not t:
            raise WebError("Must provide 't=' argument")
        if t == u'json':
            try:
                return self.post_json(req)
            except Exception:
                message, code = humanize_failure(Failure())
                req.setResponseCode(code)
                return simplejson.dumps({"error": message})
        else:
            raise WebError("'%s' invalid type for 't' arg" % (t,), http.BAD_REQUEST)
Esempio n. 2
0
def authorize(request, get_auth_token):
    if "token" in request.args:
        error(
            request,
            http.BAD_REQUEST,
            "Do not pass 'token' as URL argument",
        )
        return False

    t = request.content.tell()
    request.content.seek(0)
    fields = cgi.FieldStorage(
        request.content,
        {k: vs[0]
         for (k, vs) in request.requestHeaders.getAllRawHeaders()},
        environ={'REQUEST_METHOD': 'POST'},
    )
    request.content.seek(t)

    # not using get_arg() here because we *don't* want the token
    # argument to work if you passed it as a GET-style argument
    token = None
    if fields and 'token' in fields:
        token = fields['token'].value.strip()
    if not token:
        error(request, http.UNAUTHORIZED, "Missing token")
        return False
    if not timing_safe_compare(token, get_auth_token()):
        error(request, http.UNAUTHORIZED, "Invalid token")
        return False

    return True
Esempio n. 3
0
def _is_authorized(request, get_auth_token):
    """
    Check the authorization carried by the given request.

    :param IRequest request: The request object being considered.  If it has
        an *Authorization* header carrying a *Bearer token* matching the token
        returned by ``get_auth_token`` the request is considered authorized,
        otherwise it is not.

    :param (IO bytes) get_auth_token: A callable which returns the
        authorization token value which is required for requests to be
        authorized.

    :return bool: True if and only if the given request contains the required
        authorization materials.
    """
    authorization = request.requestHeaders.getRawHeaders(u"authorization")
    if authorization is None or len(authorization) == 0:
        return False
    if len(authorization) > 1:
        return False
    auth_token = get_auth_token()
    expected = u"Bearer {}".format(auth_token).encode("ascii")
    return timing_safe_compare(
        authorization[0].encode("ascii"),
        expected,
    )
Esempio n. 4
0
    def cancel_lease(self, cancel_secret):
        """Remove a lease with the given cancel_secret. If the last lease is
        cancelled, the file will be removed. Return the number of bytes that
        were freed (by truncating the list of leases, and possibly by
        deleting the file. Raise IndexError if there was no lease with the
        given cancel_secret.
        """

        leases = list(self.get_leases())
        num_leases_removed = 0
        for i, lease in enumerate(leases):
            if timing_safe_compare(lease.cancel_secret, cancel_secret):
                leases[i] = None
                num_leases_removed += 1
        if not num_leases_removed:
            raise IndexError("unable to find matching lease to cancel")
        if num_leases_removed:
            # pack and write out the remaining leases. We write these out in
            # the same order as they were added, so that if we crash while
            # doing this, we won't lose any non-cancelled leases.
            leases = [l for l in leases if l]  # remove the cancelled leases
            with open(self.home, 'rb+') as f:
                for i, lease in enumerate(leases):
                    self._write_lease_record(f, i, lease)
                self._write_num_leases(f, len(leases))
                self._truncate_leases(f, len(leases))
        space_freed = self.LEASE_SIZE * num_leases_removed
        if not len(leases):
            space_freed += os.stat(self.home)[stat.ST_SIZE]
            self.unlink()
        return space_freed
Esempio n. 5
0
    def cancel_lease(self, cancel_secret):
        """Remove a lease with the given cancel_secret. If the last lease is
        cancelled, the file will be removed. Return the number of bytes that
        were freed (by truncating the list of leases, and possibly by
        deleting the file. Raise IndexError if there was no lease with the
        given cancel_secret.
        """

        leases = list(self.get_leases())
        num_leases_removed = 0
        for i,lease in enumerate(leases):
            if timing_safe_compare(lease.cancel_secret, cancel_secret):
                leases[i] = None
                num_leases_removed += 1
        if not num_leases_removed:
            raise IndexError("unable to find matching lease to cancel")
        if num_leases_removed:
            # pack and write out the remaining leases. We write these out in
            # the same order as they were added, so that if we crash while
            # doing this, we won't lose any non-cancelled leases.
            leases = [l for l in leases if l] # remove the cancelled leases
            f = open(self.home, 'rb+')
            for i,lease in enumerate(leases):
                self._write_lease_record(f, i, lease)
            self._write_num_leases(f, len(leases))
            self._truncate_leases(f, len(leases))
            f.close()
        space_freed = self.LEASE_SIZE * num_leases_removed
        if not len(leases):
            space_freed += os.stat(self.home)[stat.ST_SIZE]
            self.unlink()
        return space_freed
Esempio n. 6
0
    def render(self, req):
        if req.method != 'POST':
            raise server.UnsupportedMethod(('POST', ))
        if req.args.get('token', False):
            raise WebError("Do not pass 'token' as URL argument",
                           http.BAD_REQUEST)
        # not using get_arg() here because we *don't* want the token
        # argument to work if you passed it as a GET-style argument
        token = None
        if req.fields and 'token' in req.fields:
            token = req.fields['token'].value.strip()
        if not token:
            raise WebError("Missing token", http.UNAUTHORIZED)
        if not timing_safe_compare(token, self.client.get_auth_token()):
            raise WebError("Invalid token", http.UNAUTHORIZED)

        t = get_arg(req, "t", "").strip()
        if not t:
            raise WebError("Must provide 't=' argument")
        if t == u'json':
            try:
                return self.post_json(req)
            except Exception:
                message, code = humanize_failure(Failure())
                req.setResponseCode(code)
                return simplejson.dumps({"error": message})
        else:
            raise WebError("'%s' invalid type for 't' arg" % (t, ),
                           http.BAD_REQUEST)
Esempio n. 7
0
    def is_cancel_secret(self, candidate_secret):
        # type: (bytes) -> bool
        """
        Check a string to see if it is the correct cancel secret.

        :return: ``True`` if it is the correct cancel secret, ``False``
            otherwise.
        """
        return timing_safe_compare(self.cancel_secret, candidate_secret)
Esempio n. 8
0
 def renew_lease(self, renew_secret, new_expire_time):
     for i, lease in enumerate(self.get_leases()):
         if timing_safe_compare(lease.renew_secret, renew_secret):
             # yup. See if we need to update the owner time.
             if new_expire_time > lease.expiration_time:
                 # yes
                 lease.expiration_time = new_expire_time
                 with open(self.home, 'rb+') as f:
                     self._write_lease_record(f, i, lease)
             return
     raise IndexError("unable to renew non-existent lease")
Esempio n. 9
0
 def renew_lease(self, renew_secret, new_expire_time):
     for i,lease in enumerate(self.get_leases()):
         if timing_safe_compare(lease.renew_secret, renew_secret):
             # yup. See if we need to update the owner time.
             if new_expire_time > lease.expiration_time:
                 # yes
                 lease.expiration_time = new_expire_time
                 f = open(self.home, 'rb+')
                 self._write_lease_record(f, i, lease)
                 f.close()
             return
     raise IndexError("unable to renew non-existent lease")
Esempio n. 10
0
 def check_write_enabler(self, write_enabler, si_s):
     with open(self.home, 'rb+') as f:
         (real_write_enabler, write_enabler_nodeid) = \
                              self._read_write_enabler_and_nodeid(f)
     # avoid a timing attack
     #if write_enabler != real_write_enabler:
     if not timing_safe_compare(write_enabler, real_write_enabler):
         # accomodate share migration by reporting the nodeid used for the
         # old write enabler.
         self.log(format="bad write enabler on SI %(si)s,"
                  " recorded by nodeid %(nodeid)s",
                  facility="tahoe.storage",
                  level=log.WEIRD, umid="cE1eBQ",
                  si=si_s, nodeid=idlib.nodeid_b2a(write_enabler_nodeid))
         msg = "The write enabler was recorded by nodeid '%s'." % \
               (idlib.nodeid_b2a(write_enabler_nodeid),)
         raise BadWriteEnablerError(msg)
Esempio n. 11
0
 def check_write_enabler(self, write_enabler, si_s):
     f = open(self.home, 'rb+')
     (real_write_enabler, write_enabler_nodeid) = \
                          self._read_write_enabler_and_nodeid(f)
     f.close()
     # avoid a timing attack
     #if write_enabler != real_write_enabler:
     if not timing_safe_compare(write_enabler, real_write_enabler):
         # accomodate share migration by reporting the nodeid used for the
         # old write enabler.
         self.log(format="bad write enabler on SI %(si)s,"
                  " recorded by nodeid %(nodeid)s",
                  facility="tahoe.storage",
                  level=log.WEIRD, umid="cE1eBQ",
                  si=si_s, nodeid=idlib.nodeid_b2a(write_enabler_nodeid))
         msg = "The write enabler was recorded by nodeid '%s'." % \
               (idlib.nodeid_b2a(write_enabler_nodeid),)
         raise BadWriteEnablerError(msg)
Esempio n. 12
0
    def renderHTTP(self, ctx):
        req = IRequest(ctx)
        if req.method != 'POST':
            raise server.UnsupportedMethod(('POST',))

        token = get_arg(req, "token", None)
        if not token:
            raise WebError("Missing token", http.UNAUTHORIZED)
        if not timing_safe_compare(token, self.client.get_auth_token()):
            raise WebError("Invalid token", http.UNAUTHORIZED)

        t = get_arg(req, "t", "").strip()
        if not t:
            raise WebError("Must provide 't=' argument")
        if t == u'json':
            return self.post_json(req)
        else:
            raise WebError("'%s' invalid type for 't' arg" % (t,), http.BAD_REQUEST)
Esempio n. 13
0
 def renew_lease(self, renew_secret, new_expire_time):
     accepting_nodeids = set()
     with open(self.home, 'rb+') as f:
         for (leasenum, lease) in self._enumerate_leases(f):
             if timing_safe_compare(lease.renew_secret, renew_secret):
                 # yup. See if we need to update the owner time.
                 if new_expire_time > lease.expiration_time:
                     # yes
                     lease.expiration_time = new_expire_time
                     self._write_lease_record(f, leasenum, lease)
                 return
             accepting_nodeids.add(lease.nodeid)
     # Return the accepting_nodeids set, to give the client a chance to
     # update the leases on a share which has been migrated from its
     # original server to a new one.
     msg = ("Unable to renew non-existent lease. I have leases accepted by"
            " nodeids: ")
     msg += ",".join([("'%s'" % idlib.nodeid_b2a(anid))
                      for anid in accepting_nodeids])
     msg += " ."
     raise IndexError(msg)
Esempio n. 14
0
 def renew_lease(self, renew_secret, new_expire_time):
     accepting_nodeids = set()
     f = open(self.home, 'rb+')
     for (leasenum,lease) in self._enumerate_leases(f):
         if timing_safe_compare(lease.renew_secret, renew_secret):
             # yup. See if we need to update the owner time.
             if new_expire_time > lease.expiration_time:
                 # yes
                 lease.expiration_time = new_expire_time
                 self._write_lease_record(f, leasenum, lease)
             f.close()
             return
         accepting_nodeids.add(lease.nodeid)
     f.close()
     # Return the accepting_nodeids set, to give the client a chance to
     # update the leases on a share which has been migrated from its
     # original server to a new one.
     msg = ("Unable to renew non-existent lease. I have leases accepted by"
            " nodeids: ")
     msg += ",".join([("'%s'" % idlib.nodeid_b2a(anid))
                      for anid in accepting_nodeids])
     msg += " ."
     raise IndexError(msg)
Esempio n. 15
0
    def renderHTTP(self, ctx):
        req = IRequest(ctx)
        if req.method != "POST":
            raise server.UnsupportedMethod(("POST",))
        if req.args.get("token", False):
            raise WebError("Do not pass 'token' as URL argument", http.BAD_REQUEST)
        # not using get_arg() here because we *don't* want the token
        # argument to work if you passed it as a GET-style argument
        token = None
        if req.fields and "token" in req.fields:
            token = req.fields["token"].value[0]
        if not token:
            raise WebError("Missing token", http.UNAUTHORIZED)
        if not timing_safe_compare(token, self.client.get_auth_token()):
            raise WebError("Invalid token", http.UNAUTHORIZED)

        t = get_arg(req, "t", "").strip()
        if not t:
            raise WebError("Must provide 't=' argument")
        if t == u"json":
            return self.post_json(req)
        else:
            raise WebError("'%s' invalid type for 't' arg" % (t,), http.BAD_REQUEST)
Esempio n. 16
0
    def cancel_lease(self, cancel_secret):
        """Remove any leases with the given cancel_secret. If the last lease
        is cancelled, the file will be removed. Return the number of bytes
        that were freed (by truncating the list of leases, and possibly by
        deleting the file. Raise IndexError if there was no lease with the
        given cancel_secret."""

        accepting_nodeids = set()
        modified = 0
        remaining = 0
        blank_lease = LeaseInfo(owner_num=0,
                                renew_secret="\x00"*32,
                                cancel_secret="\x00"*32,
                                expiration_time=0,
                                nodeid="\x00"*20)
        f = open(self.home, 'rb+')
        for (leasenum,lease) in self._enumerate_leases(f):
            accepting_nodeids.add(lease.nodeid)
            if timing_safe_compare(lease.cancel_secret, cancel_secret):
                self._write_lease_record(f, leasenum, blank_lease)
                modified += 1
            else:
                remaining += 1
        if modified:
            freed_space = self._pack_leases(f)
            f.close()
            if not remaining:
                freed_space += os.stat(self.home)[stat.ST_SIZE]
                self.unlink()
            return freed_space

        msg = ("Unable to cancel non-existent lease. I have leases "
               "accepted by nodeids: ")
        msg += ",".join([("'%s'" % idlib.nodeid_b2a(anid))
                         for anid in accepting_nodeids])
        msg += " ."
        raise IndexError(msg)
Esempio n. 17
0
    def cancel_lease(self, cancel_secret):
        """Remove any leases with the given cancel_secret. If the last lease
        is cancelled, the file will be removed. Return the number of bytes
        that were freed (by truncating the list of leases, and possibly by
        deleting the file. Raise IndexError if there was no lease with the
        given cancel_secret."""

        accepting_nodeids = set()
        modified = 0
        remaining = 0
        blank_lease = LeaseInfo(owner_num=0,
                                renew_secret="\x00" * 32,
                                cancel_secret="\x00" * 32,
                                expiration_time=0,
                                nodeid="\x00" * 20)
        with open(self.home, 'rb+') as f:
            for (leasenum, lease) in self._enumerate_leases(f):
                accepting_nodeids.add(lease.nodeid)
                if timing_safe_compare(lease.cancel_secret, cancel_secret):
                    self._write_lease_record(f, leasenum, blank_lease)
                    modified += 1
                else:
                    remaining += 1
            if modified:
                freed_space = self._pack_leases(f)
                f.close()
                if not remaining:
                    freed_space += os.stat(self.home)[stat.ST_SIZE]
                    self.unlink()
                return freed_space

        msg = ("Unable to cancel non-existent lease. I have leases "
               "accepted by nodeids: ")
        msg += ",".join([("'%s'" % idlib.nodeid_b2a(anid))
                         for anid in accepting_nodeids])
        msg += " ."
        raise IndexError(msg)
Esempio n. 18
0
 def test_timing_safe_compare(self):
     self.failUnless(hashutil.timing_safe_compare(b"a", b"a"))
     self.failUnless(hashutil.timing_safe_compare(b"ab", b"ab"))
     self.failIf(hashutil.timing_safe_compare(b"a", b"b"))
     self.failIf(hashutil.timing_safe_compare(b"a", b"aa"))