ac942c
From 2b9692621b0ee27def37c597621ea6a20c757ca8 Mon Sep 17 00:00:00 2001
ac942c
From: Florence Blanc-Renaud <flo@redhat.com>
ac942c
Date: Tue, 21 Nov 2023 09:37:25 +0100
ac942c
Subject: [PATCH] Check the HTTP Referer header on all requests
ac942c
ac942c
The referer was only checked in WSGIExecutioner classes:
ac942c
ac942c
 - jsonserver
ac942c
 - KerberosWSGIExecutioner
ac942c
 - xmlserver
ac942c
 - jsonserver_kerb
ac942c
ac942c
This left /i18n_messages, /session/login_kerberos,
ac942c
/session/login_x509, /session/login_password,
ac942c
/session/change_password and /session/sync_token unprotected
ac942c
against CSRF attacks.
ac942c
ac942c
CVE-2023-5455
ac942c
ac942c
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
ac942c
---
ac942c
 ipaserver/rpcserver.py | 34 +++++++++++++++++++++++++++++++---
ac942c
 1 file changed, 31 insertions(+), 3 deletions(-)
ac942c
ac942c
diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py
ac942c
index 3c831a55888ba723fc52c988593c364fb7883b7b..91bec2133479a9b4bd1311e6be800d982cf855f3 100644
ac942c
--- a/ipaserver/rpcserver.py
ac942c
+++ b/ipaserver/rpcserver.py
ac942c
@@ -136,6 +136,19 @@ _success_template = """<html>
ac942c
 </html>"""
ac942c
 
ac942c
 class HTTP_Status(plugable.Plugin):
ac942c
+    def check_referer(self, environ):
ac942c
+        if "HTTP_REFERER" not in environ:
ac942c
+            logger.error("Rejecting request with missing Referer")
ac942c
+            return False
ac942c
+        if (not environ["HTTP_REFERER"].startswith(
ac942c
+                "https://%s/ipa" % self.api.env.host)
ac942c
+                and not self.env.in_tree):
ac942c
+            logger.error("Rejecting request with bad Referer %s",
ac942c
+                         environ["HTTP_REFERER"])
ac942c
+            return False
ac942c
+        logger.debug("Valid Referer %s", environ["HTTP_REFERER"])
ac942c
+        return True
ac942c
+
ac942c
     def not_found(self, environ, start_response, url, message):
ac942c
         """
ac942c
         Return a 404 Not Found error.
ac942c
@@ -297,9 +310,6 @@ class wsgi_dispatch(Executioner, HTTP_Status):
ac942c
         self.__apps[key] = app
ac942c
 
ac942c
 
ac942c
-
ac942c
-
ac942c
-
ac942c
 class WSGIExecutioner(Executioner):
ac942c
     """
ac942c
     Base class for execution backends with a WSGI application interface.
ac942c
@@ -803,6 +813,9 @@ class jsonserver_session(jsonserver, KerberosSession):
ac942c
 
ac942c
         logger.debug('WSGI jsonserver_session.__call__:')
ac942c
 
ac942c
+        if not self.check_referer(environ):
ac942c
+            return self.bad_request(environ, start_response, 'denied')
ac942c
+
ac942c
         # Redirect to login if no Kerberos credentials
ac942c
         ccache_name = self.get_environ_creds(environ)
ac942c
         if ccache_name is None:
ac942c
@@ -843,6 +856,9 @@ class KerberosLogin(Backend, KerberosSession):
ac942c
     def __call__(self, environ, start_response):
ac942c
         logger.debug('WSGI KerberosLogin.__call__:')
ac942c
 
ac942c
+        if not self.check_referer(environ):
ac942c
+            return self.bad_request(environ, start_response, 'denied')
ac942c
+
ac942c
         # Redirect to login if no Kerberos credentials
ac942c
         user_ccache_name = self.get_environ_creds(environ)
ac942c
         if user_ccache_name is None:
ac942c
@@ -861,6 +877,9 @@ class login_x509(KerberosLogin):
ac942c
     def __call__(self, environ, start_response):
ac942c
         logger.debug('WSGI login_x509.__call__:')
ac942c
 
ac942c
+        if not self.check_referer(environ):
ac942c
+            return self.bad_request(environ, start_response, 'denied')
ac942c
+
ac942c
         if 'KRB5CCNAME' not in environ:
ac942c
             return self.unauthorized(
ac942c
                 environ, start_response, 'KRB5CCNAME not set',
ac942c
@@ -881,6 +900,9 @@ class login_password(Backend, KerberosSession):
ac942c
     def __call__(self, environ, start_response):
ac942c
         logger.debug('WSGI login_password.__call__:')
ac942c
 
ac942c
+        if not self.check_referer(environ):
ac942c
+            return self.bad_request(environ, start_response, 'denied')
ac942c
+
ac942c
         # Get the user and password parameters from the request
ac942c
         content_type = environ.get('CONTENT_TYPE', '').lower()
ac942c
         if not content_type.startswith('application/x-www-form-urlencoded'):
ac942c
@@ -1018,6 +1040,9 @@ class change_password(Backend, HTTP_Status):
ac942c
     def __call__(self, environ, start_response):
ac942c
         logger.info('WSGI change_password.__call__:')
ac942c
 
ac942c
+        if not self.check_referer(environ):
ac942c
+            return self.bad_request(environ, start_response, 'denied')
ac942c
+
ac942c
         # Get the user and password parameters from the request
ac942c
         content_type = environ.get('CONTENT_TYPE', '').lower()
ac942c
         if not content_type.startswith('application/x-www-form-urlencoded'):
ac942c
@@ -1231,6 +1256,9 @@ class xmlserver_session(xmlserver, KerberosSession):
ac942c
 
ac942c
         logger.debug('WSGI xmlserver_session.__call__:')
ac942c
 
ac942c
+        if not self.check_referer(environ):
ac942c
+            return self.bad_request(environ, start_response, 'denied')
ac942c
+
ac942c
         ccache_name = environ.get('KRB5CCNAME')
ac942c
 
ac942c
         # Redirect to /ipa/xml if no Kerberos credentials
ac942c
-- 
ac942c
2.26.3
ac942c