XmlRpc.java does not provide adequate support to implement HTTP Basic Authentication. WebServer.java implements it incorrectly. This patch fixes both problems.
If an HTTP request requires authentication, the server MUST return a 401 Unauthorized: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2 Without the 401, the client doesn't know which type of authentication to use. It can't assume Basic, because that would mean sending passwords in the clear even to Digest-capable servers (thereby rendering digest auth useless). It can't assume Digest, because the 401 contains the nonce (which is required to perform digest authentication). Patch appended. This also fixes a bug whereby WebServer would return an HTTP/1.1 response without a Content-Length or chunked encoding, which is not allowed. - a ------------------------------------------------------------------------------ Index: src/java/org/apache/xmlrpc/WebServer.java =================================================================== RCS file: /home/cvspublic/xml-rpc/src/java/org/apache/xmlrpc/WebServer.java,v retrieving revision 1.13 diff -u -r1.13 WebServer.java --- src/java/org/apache/xmlrpc/WebServer.java 13 Aug 2002 22:27:16 -0000 1.13 +++ src/java/org/apache/xmlrpc/WebServer.java 13 Aug 2002 23:53:40 -0000 @@ -94,6 +94,7 @@ protected static final byte[] conclose = "Connection: close\r\n".getBytes(); protected static final byte[] ok = " 200 OK\r\n".getBytes(); protected static final byte[] server = "Server: Apache XML-RPC 1.0\r\n".getBytes(); + protected static final byte[] www_authenticate = "WWW-Authenticate: Basic +realm=XMLRPC\r\n".getBytes(); private static final String HTTP_11 = "HTTP/1.1"; private static final String STAR = "*"; @@ -584,29 +585,40 @@ { ServerInputStream sin = new ServerInputStream(input, contentLength); - byte result[] = xmlrpc.execute(sin, user, password); - output.write(httpversion.getBytes()); - output.write(ok); - output.write(server); - if (keepalive) - { - output.write(conkeep); - } - else - { - output.write (conclose); + try { + byte result[] = xmlrpc.execute(sin, user, password); + output.write(httpversion.getBytes()); + output.write(ok); + output.write(server); + if (keepalive) + { + output.write(conkeep); + } + else + { + output.write (conclose); + } + output.write(ctype); + output.write(clength); + output.write(Integer.toString(result.length) + .getBytes()); + output.write(doubleNewline); + output.write(result); + } catch (XmlRpcServer.AuthenticationRequiredException are) { + output.write("HTTP/1.0".getBytes()); + output.write(" 401 Unauthorized\r\n".getBytes()); + output.write(server); + output.write(www_authenticate); + output.write("\r\n".getBytes()); + output.write(("Method " + method + + " requires a username and +password").getBytes()); + keepalive = false; } - output.write(ctype); - output.write(clength); - output.write(Integer.toString(result.length) - .getBytes()); - output.write(doubleNewline); - output.write(result); output.flush(); } else { - output.write(httpversion.getBytes()); + output.write("HTTP/1.0".getBytes()); output.write(" 400 Bad Request\r\n".getBytes()); output.write(server); output.write("\r\n".getBytes()); Index: src/java/org/apache/xmlrpc/XmlRpcServer.java =================================================================== RCS file: /home/cvspublic/xml-rpc/src/java/org/apache/xmlrpc/XmlRpcServer.java,v retrieving revision 1.28 diff -u -r1.28 XmlRpcServer.java --- src/java/org/apache/xmlrpc/XmlRpcServer.java 9 Aug 2002 09:14:24 -0000 1.28 +++ src/java/org/apache/xmlrpc/XmlRpcServer.java 13 Aug 2002 23:53:40 -0000 @@ -136,6 +136,7 @@ * since this is all packed into the response. */ public byte[] execute(InputStream is) + throws AuthenticationRequiredException { return execute(is, null, null); } @@ -146,6 +147,7 @@ * use the credentials to authenticate the user. */ public byte[] execute(InputStream is, String user, String password) + throws AuthenticationRequiredException { Worker worker = getWorker(); byte[] retval = worker.execute(is, user, password); @@ -202,6 +204,7 @@ * Given a request for the server, generates a response. */ public byte[] execute(InputStream is, String user, String password) + throws AuthenticationRequiredException { try { @@ -224,7 +227,7 @@ * @return */ private byte[] executeInternal(InputStream is, String user, - String password) + String password) throws AuthenticationRequiredException { byte[] result; long now = 0; @@ -284,6 +287,7 @@ Object outParam; if (handler instanceof AuthenticatedXmlRpcHandler) { + if (user == null) throw new +XmlRpcServer.AuthenticationRequiredException(); outParam =((AuthenticatedXmlRpcHandler) handler) .execute(methodName, inParams, user, password); } @@ -303,6 +307,9 @@ } catch(Exception x) { + if (x instanceof XmlRpcServer.AuthenticationRequiredException) + throw (XmlRpcServer.AuthenticationRequiredException)x; + if (XmlRpc.debug) { x.printStackTrace(); @@ -427,6 +434,11 @@ writer.endElement("methodResponse"); } } // end of inner class Worker + + public static class AuthenticationRequiredException extends IOException { + AuthenticationRequiredException() { } + } + } // XmlRpcServer /** @@ -555,4 +567,6 @@ } return returnValue; } + } +