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

Reply via email to