---------- Forwarded message ---------- From: Greg Smethells <gsmethe...@medstrat.com> To: xmlrpc-a...@ws.apache.org Date: Thu, 18 Mar 2010 10:22:21 -0500 Subject: Connection re-use bug in XML-RPC 3.1.3
XML-RPC Developers, There is a security hole in the re-use of server connections when basic authentication is involved. Context: We have an XML-RPC client that connects to a server over HTTPS and requires basic authentication. Initially, the app starts up using a 1st username/password and our java.net.Authenticator does a setDefault() to our own WebServerAuthenticator class. It's getPasswordAuthentication() is called by HttpURLConnection and the connection succeeds on the server. I cannot tell if it is ever used, but the XmlRpcClientConfigImpl also has its setBasicUserName and setBasicPassword set on the xmlrpcClient instance. Everything looks good at that point. Then, we have an administration dialog we use to configure the server side, which is where the issue starts to come up. The username/password at this point is different than when the app first signed into the server. A new WebServerAuthenticator class is setDefault()-ed with this admin (2nd) username/password and a new XmlRpcClientConfigImpl is instantiated and set to the admin's (2nd) user/pass and a new xmlrpcClient is also instantiated and set to the new client config. On the server side, I can see the client try to log in as the admin (2nd); however, if I provide a bogus password, I can see the basic auth fail on the server side AND THEN the original (1st) username/password (not the admin (2nd) credentials) are used to sign in and the RPC returns successfully (no exception thrown for password mismatch!). Unbeknownst to the app the xmlrpcClient has used credentials that were not asked to be used! Source Code: This is called during construction of the client-side server proxy ... // Assign an Authenticator to pass username and password data during HTTPS requests // Required for getting past Apache authentication (.htaccess) Authenticator.setDefault(new WebServerAuthenticator(username, password)); // Create the XML-RPC client XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl(); // Set-up the URL to the XML-RPC server config.setServerURL(new URL("https://" + ipAddress + "/" + cgiScript)); // Set-up the log-in credentials that provide our actual security config.setBasicUserName(username); config.setBasicPassword(password); config.setEnabledForExtensions(true); // WARNING: must be as high enough to allow all timeouts given to call() config.setConnectionTimeout(30 * 1000); // Timeout for connecting config.setReplyTimeout(MAX_TIMEOUT * 1000); // Timeout for XML-RPC replies xmlrpcClient = new XmlRpcClient(); xmlrpcClient.setConfig(config); // Timeout after "timeout" seconds ("timeout" seconds x 1000 milliseconds) TimingOutCallback callback = new TimingOutCallback(15 * 1000); // Fault tolerant connect try { Object[] result; Object[] params = new Object[] {}; if( testRPC.length() > 0 ) { // Asynchronous remote procedure call xmlrpcClient.executeAsync(testRPC, getParams(params), callback); result = (Object[]) callback.waitForResponse(); // If we did not throw an exception and got here, then we are connected state = CONNECTED; // Finish any internal state set-up setupState(result); Console.print("Connection opened to " + toString()); } else { // Assume we are "connected" state = CONNECTED; Console.print("Proxy created for " + toString()); } } catch(TimeoutException e) { setState(e.getMessage(), ADDRESS_UNREACHABLE); } catch(XmlRpcException xrex) { parseState(xrex.getMessage()); } catch(Exception ex) { parseState(ex.getMessage()); } catch(Throwable t) { parseState(t.toString()); } } else { state = CONNECTION_NOT_ALLOWED; } Thanks, Greg Software Architect Medstrat, Inc. -- Germanys national anthem is the most boring in the world - how telling!