ac7d03
From 0367e6dd66d47a78484e12e76119d9662356bf48 Mon Sep 17 00:00:00 2001
ac7d03
From: Stanislav Laznicka <slaznick@redhat.com>
ac7d03
Date: Fri, 26 May 2017 08:37:36 +0200
ac7d03
Subject: [PATCH] Avoid possible endless recursion in RPC call
ac7d03
ac7d03
This commit removes recursion in RPCClient.forward() which may lack
ac7d03
end condition.
ac7d03
ac7d03
https://pagure.io/freeipa/issue/6796
ac7d03
ac7d03
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
ac7d03
---
ac7d03
 ipalib/rpc.py | 95 +++++++++++++++++++++++++++++++++--------------------------
ac7d03
 1 file changed, 54 insertions(+), 41 deletions(-)
ac7d03
ac7d03
diff --git a/ipalib/rpc.py b/ipalib/rpc.py
ac7d03
index e23ca3d061645b2695a9e0deaa0b7d666f986e0e..297ed80414fae3d8b27558567425fec704f3e862 100644
ac7d03
--- a/ipalib/rpc.py
ac7d03
+++ b/ipalib/rpc.py
ac7d03
@@ -1088,50 +1088,63 @@ class RPCClient(Connectible):
ac7d03
         :param kw: Keyword arguments to pass to remote command.
ac7d03
         """
ac7d03
         server = getattr(context, 'request_url', None)
ac7d03
-        self.log.info("Forwarding '%s' to %s server '%s'",
ac7d03
-                      name, self.protocol, server)
ac7d03
         command = getattr(self.conn, name)
ac7d03
         params = [args, kw]
ac7d03
-        try:
ac7d03
-            return self._call_command(command, params)
ac7d03
-        except Fault as e:
ac7d03
-            e = decode_fault(e)
ac7d03
-            self.debug('Caught fault %d from server %s: %s', e.faultCode,
ac7d03
-                server, e.faultString)
ac7d03
-            if e.faultCode in errors_by_code:
ac7d03
-                error = errors_by_code[e.faultCode]
ac7d03
-                raise error(message=e.faultString)
ac7d03
-            raise UnknownError(
ac7d03
-                code=e.faultCode,
ac7d03
-                error=e.faultString,
ac7d03
-                server=server,
ac7d03
-            )
ac7d03
-        except SSLError as e:
ac7d03
-            raise NetworkError(uri=server, error=str(e))
ac7d03
-        except ProtocolError as e:
ac7d03
-            # By catching a 401 here we can detect the case where we have
ac7d03
-            # a single IPA server and the session is invalid. Otherwise
ac7d03
-            # we always have to do a ping().
ac7d03
-            session_cookie = getattr(context, 'session_cookie', None)
ac7d03
-            if session_cookie and e.errcode == 401:
ac7d03
-                # Unauthorized. Remove the session and try again.
ac7d03
-                delattr(context, 'session_cookie')
ac7d03
-                try:
ac7d03
-                    principal = getattr(context, 'principal', None)
ac7d03
-                    delete_persistent_client_session_data(principal)
ac7d03
-                except Exception as e:
ac7d03
-                    # This shouldn't happen if we have a session but it isn't fatal.
ac7d03
-                    pass
ac7d03
 
ac7d03
-                # Create a new serverproxy with the non-session URI
ac7d03
-                serverproxy = self.create_connection(os.environ.get('KRB5CCNAME'), self.env.verbose, self.env.fallback, self.env.delegate)
ac7d03
-                setattr(context, self.id, Connection(serverproxy, self.disconnect))
ac7d03
-                return self.forward(name, *args, **kw)
ac7d03
-            raise NetworkError(uri=server, error=e.errmsg)
ac7d03
-        except socket.error as e:
ac7d03
-            raise NetworkError(uri=server, error=str(e))
ac7d03
-        except (OverflowError, TypeError) as e:
ac7d03
-            raise XMLRPCMarshallError(error=str(e))
ac7d03
+        # we'll be trying to connect multiple times with a new session cookie
ac7d03
+        # each time should we be getting UNAUTHORIZED error from the server
ac7d03
+        max_tries = 5
ac7d03
+        for try_num in range(0, max_tries):
ac7d03
+            self.log.info("[try %d]: Forwarding '%s' to %s server '%s'",
ac7d03
+                          try_num+1, name, self.protocol, server)
ac7d03
+            try:
ac7d03
+                return self._call_command(command, params)
ac7d03
+            except Fault as e:
ac7d03
+                e = decode_fault(e)
ac7d03
+                self.debug('Caught fault %d from server %s: %s', e.faultCode,
ac7d03
+                           server, e.faultString)
ac7d03
+                if e.faultCode in errors_by_code:
ac7d03
+                    error = errors_by_code[e.faultCode]
ac7d03
+                    raise error(message=e.faultString)
ac7d03
+                raise UnknownError(
ac7d03
+                    code=e.faultCode,
ac7d03
+                    error=e.faultString,
ac7d03
+                    server=server,
ac7d03
+                )
ac7d03
+            except ProtocolError as e:
ac7d03
+                # By catching a 401 here we can detect the case where we have
ac7d03
+                # a single IPA server and the session is invalid. Otherwise
ac7d03
+                # we always have to do a ping().
ac7d03
+                session_cookie = getattr(context, 'session_cookie', None)
ac7d03
+                if session_cookie and e.errcode == 401:
ac7d03
+                    # Unauthorized. Remove the session and try again.
ac7d03
+                    delattr(context, 'session_cookie')
ac7d03
+                    try:
ac7d03
+                        principal = getattr(context, 'principal', None)
ac7d03
+                        delete_persistent_client_session_data(principal)
ac7d03
+                    except Exception as e:
ac7d03
+                        # This shouldn't happen if we have a session
ac7d03
+                        # but it isn't fatal.
ac7d03
+                        self.debug("Error trying to remove persisent session "
ac7d03
+                                   "data: {err}".format(err=e))
ac7d03
+
ac7d03
+                    # Create a new serverproxy with the non-session URI
ac7d03
+                    serverproxy = self.create_connection(
ac7d03
+                        os.environ.get('KRB5CCNAME'), self.env.verbose,
ac7d03
+                        self.env.fallback, self.env.delegate)
ac7d03
+
ac7d03
+                    setattr(context, self.id,
ac7d03
+                            Connection(serverproxy, self.disconnect))
ac7d03
+                    # try to connect again with the new session cookie
ac7d03
+                    continue
ac7d03
+                raise NetworkError(uri=server, error=e.errmsg)
ac7d03
+            except (SSLError, socket.error) as e:
ac7d03
+                raise NetworkError(uri=server, error=str(e))
ac7d03
+            except (OverflowError, TypeError) as e:
ac7d03
+                raise XMLRPCMarshallError(error=str(e))
ac7d03
+        raise NetworkError(
ac7d03
+            uri=server,
ac7d03
+            error=_("Exceeded number of tries to forward a request."))
ac7d03
 
ac7d03
 
ac7d03
 class xmlclient(RPCClient):
ac7d03
-- 
ac7d03
2.9.4
ac7d03