async def async_render_GET(self, request: Request):
        # make sure that there is a valid mapping session, to stop people dictionary-
        # scanning for accounts
        session_id = request.getCookie(SESSION_COOKIE_NAME)
        if not session_id:
            _return_json({"error": "missing session_id"}, request)
            return

        session_id = session_id.decode("ascii", errors="replace")
        session = get_mapping_session(session_id)
        if not session:
            logger.info("Couldn't find session id %s", session_id)
            _return_json({"error": "unknown session"}, request)
            return

        if b"username" not in request.args:
            _return_json({"error": "missing username"}, request)
            return
        localpart = request.args[b"username"][0].decode("utf-8",
                                                        errors="replace")
        logger.info("Checking for availability of username %s", localpart)
        try:
            user_id = self._module_api.get_qualified_user_id(localpart)
            registered_id = await self._module_api.check_user_exists(user_id)
            available = registered_id is None
        except Exception as e:
            logger.warning("Error checking for availability of %s: %s %s" %
                           (localpart, type(e), e))
            available = False
        response = {"available": available}
        _return_json(response, request)
Beispiel #2
0
    def render_GET(self, request: Request):  # $ requestHandler
        # see https://twistedmatrix.com/documents/21.2.0/api/twisted.web.server.Request.html
        ensure_tainted(
            request,  # $ tainted
            request.uri,  # $ tainted
            request.path,  # $ tainted
            request.prepath,  # $ tainted
            request.postpath,  # $ tainted

            # file-like
            request.content,  # $ tainted
            request.content.read(),  # $ MISSING: tainted

            # Dict[bytes, List[bytes]] (for query args)
            request.args,  # $ tainted
            request.args[b"key"],  # $ tainted
            request.args[b"key"][0],  # $ tainted
            request.args.get(b"key"),  # $ tainted
            request.args.get(b"key")[0],  # $ tainted
            request.received_cookies,  # $ tainted
            request.received_cookies["key"],  # $ tainted
            request.received_cookies.get("key"),  # $ tainted
            request.getCookie(b"key"),  # $ tainted

            # twisted.web.http_headers.Headers
            # see https://twistedmatrix.com/documents/21.2.0/api/twisted.web.http_headers.Headers.html
            request.requestHeaders,  # $ tainted
            request.requestHeaders.getRawHeaders("key"),  # $ MISSING: tainted
            request.requestHeaders.getRawHeaders("key")
            [0],  # $ MISSING: tainted
            request.requestHeaders.getAllRawHeaders(),  # $ MISSING: tainted
            list(request.requestHeaders.getAllRawHeaders()
                 ),  # $ MISSING: tainted
            request.getHeader("key"),  # $ tainted
            request.getAllHeaders(),  # $ tainted
            request.getAllHeaders()["key"],  # $ tainted
            request.user,  # $ tainted
            request.getUser(),  # $ tainted
            request.password,  # $ tainted
            request.getPassword(),  # $ tainted
            request.host,  # $ tainted
            request.getHost(),  # $ tainted
            request.getRequestHostname(),  # $ tainted
        )

        # technically user-controlled, but unlikely to lead to vulnerabilities.
        ensure_not_tainted(request.method, )

        # not tainted at all
        ensure_not_tainted(
            # outgoing things
            request.cookies,
            request.responseHeaders,
        )
    async def async_render_POST(self, request: Request):
        # make sure that there is a valid mapping session, to stop people dictionary-
        # scanning for accounts
        session_id = request.getCookie(SESSION_COOKIE_NAME)
        if not session_id:
            _return_json({"error": "missing session_id"}, request)
            return

        session_id = session_id.decode("ascii", errors="replace")
        session = get_mapping_session(session_id)
        if not session:
            logger.info("Couldn't find session id %s", session_id)
            _return_json({"error": "unknown session"}, request)
            return

        if b"username" not in request.args:
            _return_json({"error": "missing username"}, request)
            return
        localpart = request.args[b"username"][0].decode("utf-8",
                                                        errors="replace")

        if b"password" not in request.args:
            _return_json({"error": "missing password"}, request)
            return
        password = request.args[b"password"][0].decode("utf-8",
                                                       errors="replace")

        if localpart.startswith("@"):
            uid = localpart
        else:
            uid = UserID(localpart, self._module_api._hs.hostname).to_string()

        success = False
        try:
            passwd_response = await self._module_api._auth_handler._check_local_password(
                uid, password)
            if passwd_response == uid:
                success = True
        except Exception as e:
            logger.warning("Error checking credentials of %s: %s %s" %
                           (localpart, type(e), e))

        response = {"success": success}
        _return_json(response, request)
    async def async_render_POST(self, request: Request):
        session_id = request.getCookie(SESSION_COOKIE_NAME)
        if not session_id:
            _return_html_error(400, "missing session_id", request)
            return

        session_id = session_id.decode("ascii", errors="replace")
        session = get_mapping_session(session_id)
        if not session:
            logger.info("Session ID %s not found", session_id)
            _return_html_error(403, "Unknown session", request)
            return

        # we don't clear the session from the dict until the ID is successfully
        # registered, so the user can go round and have another go if need be.
        #
        # this means there's theoretically a race where a single user can register
        # two accounts. I'm going to assume that's not a dealbreaker.

        if b"username" not in request.args:
            _return_html_error(400, "missing username", request)
            return
        localpart = request.args[b"username"][0].decode("utf-8",
                                                        errors="replace")

        if b"password" not in request.args:
            logger.info("Registering username %s", localpart)
            try:
                registered_user_id = await self._module_api.register_user(
                    localpart=localpart, displayname=localpart)
            except SynapseError as e:
                logger.warning("Error during registration: %s", e)
                _return_html_error(e.code, e.msg, request)
                return
        else:
            password = request.args[b"password"][0].decode("utf-8",
                                                           errors="replace")
            if localpart.startswith("@"):
                registered_user_id = localpart
            else:
                registered_user_id = UserID(
                    localpart, self._module_api._hs.hostname).to_string()

            success = False
            try:
                passwd_response = await self._module_api._auth_handler._check_local_password(
                    registered_user_id, password)
                if passwd_response != registered_user_id:
                    raise LoginError(403,
                                     "Invalid password",
                                     errcode=Codes.FORBIDDEN)
            except Exception as e:
                logger.warning("Error checking credentials of %s: %s %s" %
                               (localpart, type(e), e))
                _return_html_error(e.code, e.msg, request)
                return

        await self._module_api.record_user_external_id("saml",
                                                       session.remote_user_id,
                                                       registered_user_id)

        del username_mapping_sessions[session_id]

        # delete the cookie
        request.addCookie(
            SESSION_COOKIE_NAME,
            b"",
            expires=b"Thu, 01 Jan 1970 00:00:00 GMT",
            path=b"/",
        )

        await self._module_api.complete_sso_login_async(
            registered_user_id,
            request,
            session.client_redirect_url,
        )
Beispiel #5
0
    def render(self, request: server.Request) -> bytes:
        # Deny by default.
        request.setResponseCode(401)

        # Get session cookie value if any.
        sessionid = request.getCookie(self.cookie)
        if sessionid is not None:
            if sessionid in self.sessions:
                request.setResponseCode(200)
                self.log.info("Session: Validation succeeded")
                return b""
            else:
                self.log.info("Session: Invalid session id")

        # Token is passed as a query parameter in the original URL.
        origurl = http.urlparse(request.getHeader(self.header))
        query = http.parse_qs(origurl.query)
        args = query.get(self.param, [])
        if len(args) != 1:
            self.log.error("Request: Token {param} missing", param=self.param)
            return b""

        try:
            token = jwt.JWT(key=self.key, jwt=args[0].decode())
        except (jwt.JWTExpired, jwt.JWTNotYetValid, jwt.JWTMissingClaim,
                jwt.JWTInvalidClaimValue, jwt.JWTInvalidClaimFormat,
                jwt.JWTMissingKeyID, jwt.JWTMissingKey) as error:
            self.log.error("JWT token: {error}", error=error)
            return b""
        except Exception:
            self.log.failure("JWT token: Unknown exception")
            return b""

        try:
            claims = json.loads(token.claims)
        except json.JSONDecodeError as error:
            self.log.failure("JWT token: Claims {error}", error=error)
            return b""

        # Collect session parameters from claims.
        sessparams = claims.get("session", {})
        kwargs = {
            "expires": sessparams.get("expires", None),
            "domain": sessparams.get("domain", None),
            "path": sessparams.get("path", None),
            "secure": sessparams.get("secure", None),
            "httpOnly": sessparams.get("httpOnly", None),
            "sameSite": sessparams.get("sameSite", None),
        }

        # Use maxAge for session ttl if it is present, convert it into a str
        # type as required by the addCookie call.
        if "maxAge" in sessparams:
            kwargs["max_age"] = str(sessparams["maxAge"])
            sessttl = int(sessparams["maxAge"])
        else:
            sessttl = self.sessttl

        # Generate a new session id and remember it. Also clean it up after
        # ttl seconds.
        sessionid = secrets.token_urlsafe(nbytes=16).encode()
        self.sessions.add(sessionid)
        reactor.callLater(sessttl, self._session_remove, sessionid)
        self.log.info("Session: Created, num sessions: {sessions}",
                      sessions=len(self.sessions))

        # Set cookie in the browser.
        request.addCookie(self.cookie, sessionid, **kwargs)

        request.setResponseCode(200)
        self.log.info("JWT token: Validation succeeded")
        return b""