rpc-dev  

patch to correct improper handling of HTTP Basic authentication

Adam Megacz
Tue, 13 Aug 2002 16:33:37 -0700


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;
     }
+
 }
+