You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by om...@apache.org on 2011/03/04 04:52:24 UTC

svn commit: r1077212 - in /hadoop/common/branches/branch-0.20-security-patches/src: core/org/apache/hadoop/ipc/ core/org/apache/hadoop/security/ test/ test/org/apache/hadoop/ipc/

Author: omalley
Date: Fri Mar  4 03:52:23 2011
New Revision: 1077212

URL: http://svn.apache.org/viewvc?rev=1077212&view=rev
Log:
commit da7108cfaa231f5d2c5ea14129d58793d65d4d04
Author: Devaraj Das <dd...@yahoo-inc.com>
Date:   Tue Feb 23 17:45:16 2010 -0800

    HADOOP:6543 from https://issues.apache.org/jira/secure/attachment/12436797/6543-bp20.0.patch.
    
    +++ b/YAHOO-CHANGES.txt
    +    HADOOP-6543. Allows secure clients to talk to unsecure clusters.
    +    (Kan Zhang via ddas)
    +

Modified:
    hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/ipc/Client.java
    hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/ipc/ConnectionHeader.java
    hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/ipc/Server.java
    hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/security/SaslRpcClient.java
    hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/security/SaslRpcServer.java
    hadoop/common/branches/branch-0.20-security-patches/src/test/findbugsExcludeFile.xml
    hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/ipc/TestSaslRPC.java

Modified: hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/ipc/Client.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/ipc/Client.java?rev=1077212&r1=1077211&r2=1077212&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/ipc/Client.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/ipc/Client.java Fri Mar  4 03:52:23 2011
@@ -188,8 +188,8 @@ public class Client {
     private String serverPrincipal;  // server's krb5 principal name
     private ConnectionHeader header;              // connection header
     private final ConnectionId remoteId;                // connection id
-    private final AuthMethod authMethod; // authentication method
-    private final boolean useSasl;
+    private AuthMethod authMethod; // authentication method
+    private boolean useSasl;
     private Token<? extends TokenIdentifier> token;
     private SaslRpcClient saslRpcClient;
     
@@ -343,13 +343,13 @@ public class Client {
       }
     }
     
-    private synchronized void setupSaslConnection(final InputStream in2, 
+    private synchronized boolean setupSaslConnection(final InputStream in2, 
         final OutputStream out2) 
-    throws javax.security.sasl.SaslException,IOException,InterruptedException {
+        throws IOException {
       try {
         saslRpcClient = new SaslRpcClient(authMethod, token,
             serverPrincipal);
-        saslRpcClient.saslConnect(in2, out2);
+        return saslRpcClient.saslConnect(in2, out2);
       } catch (javax.security.sasl.SaslException je) {
         if (authMethod == AuthMethod.KERBEROS && 
             UserGroupInformation.isLoginKeytabBased()) {
@@ -357,9 +357,10 @@ public class Client {
           UserGroupInformation.getCurrentUser().reloginFromKeytab();
           //try setting up the connection again
           try {
+            disposeSasl();
             saslRpcClient = new SaslRpcClient(authMethod, token,
                 serverPrincipal);
-            saslRpcClient.saslConnect(in2, out2);
+            return saslRpcClient.saslConnect(in2, out2);
           } catch (javax.security.sasl.SaslException jee) {
             UserGroupInformation.
             setLastUnsuccessfulAuthenticationAttemptTime
@@ -416,15 +417,22 @@ public class Client {
               ticket = ticket.getRealUser();
             }
           }
-          ticket.doAs(new PrivilegedExceptionAction<Object>() {
+          if (ticket.doAs(new PrivilegedExceptionAction<Boolean>() {
             @Override
-            public Object run() throws IOException, InterruptedException {
-              setupSaslConnection(in2, out2);
-              return null;
+            public Boolean run() throws IOException {
+              return setupSaslConnection(in2, out2);
             }
-          });
-          inStream = saslRpcClient.getInputStream(inStream);
-          outStream = saslRpcClient.getOutputStream(outStream);
+          })) {
+            // Sasl connect is successful. Let's set up Sasl i/o streams.
+            inStream = saslRpcClient.getInputStream(inStream);
+            outStream = saslRpcClient.getOutputStream(outStream);
+          } else {
+            // fall back to simple auth because server told us so.
+            authMethod = AuthMethod.SIMPLE;
+            header = new ConnectionHeader(header.getProtocol(),
+                header.getUgi(), authMethod);
+            useSasl = false;
+          }
         }
         this.in = new DataInputStream(new BufferedInputStream
             (new PingInputStream(inStream)));

Modified: hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/ipc/ConnectionHeader.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/ipc/ConnectionHeader.java?rev=1077212&r1=1077211&r2=1077212&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/ipc/ConnectionHeader.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/ipc/ConnectionHeader.java Fri Mar  4 03:52:23 2011
@@ -20,7 +20,6 @@ package org.apache.hadoop.ipc;
 import java.io.DataInput;
 import java.io.DataOutput;
 import java.io.IOException;
-import java.util.Collection;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -28,8 +27,6 @@ import org.apache.hadoop.io.Text;
 import org.apache.hadoop.io.Writable;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.security.SaslRpcServer.AuthMethod;
-import org.apache.hadoop.security.token.Token;
-import org.apache.hadoop.security.token.TokenIdentifier;
 
 /**
  * The IPC connection header sent by the client to the server
@@ -86,16 +83,14 @@ class ConnectionHeader implements Writab
   public void write(DataOutput out) throws IOException {
     Text.writeString(out, (protocol == null) ? "" : protocol);
     if (ugi != null) {
-      if (UserGroupInformation.isSecurityEnabled()) {
-        if (authMethod == AuthMethod.KERBEROS) {
-          //Send effective user for Kerberos auth
-          out.writeBoolean(true);
-          out.writeUTF(ugi.getUserName());
-          out.writeBoolean(false);
-        } else {
-          //Don't send user for token auth
-          out.writeBoolean(false);
-        }
+      if (authMethod == AuthMethod.KERBEROS) {
+        // Send effective user for Kerberos auth
+        out.writeBoolean(true);
+        out.writeUTF(ugi.getUserName());
+        out.writeBoolean(false);
+      } else if (authMethod == AuthMethod.DIGEST) {
+        // Don't send user for token auth
+        out.writeBoolean(false);
       } else {
         //Send both effective user and real user for simple auth
         out.writeBoolean(true);

Modified: hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/ipc/Server.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/ipc/Server.java?rev=1077212&r1=1077211&r2=1077212&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/ipc/Server.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/ipc/Server.java Fri Mar  4 03:52:23 2011
@@ -84,6 +84,7 @@ import org.apache.hadoop.util.StringUtil
  */
 public abstract class Server {
   private final boolean authorize;
+  private boolean isSecurityEnabled;
   
   /**
    * The first four bytes of Hadoop RPC connections
@@ -745,6 +746,7 @@ public abstract class Server {
     SaslServer saslServer;
     private AuthMethod authMethod;
     private boolean saslContextEstablished;
+    private boolean skipInitialSaslHandshake;
     private ByteBuffer rpcHeaderBuffer;
     private ByteBuffer unwrappedData;
     private ByteBuffer unwrappedDataLengthBuffer;
@@ -928,6 +930,15 @@ public abstract class Server {
       }
     }
     
+    private void askClientToUseSimpleAuth() throws IOException {
+      saslCall.connection = this;
+      saslResponse.reset();
+      DataOutputStream out = new DataOutputStream(saslResponse);
+      out.writeInt(SaslRpcServer.SWITCH_TO_SIMPLE_AUTH);
+      saslCall.setResponse(ByteBuffer.wrap(saslResponse.toByteArray()));
+      responder.doRespond(saslCall);
+    }
+    
     public int readAndProcess() throws IOException, InterruptedException {
       while (true) {
         /* Read at most one RPC. If the header is not read completely yet
@@ -956,13 +967,16 @@ public abstract class Server {
           if (authMethod == null) {
             throw new IOException("Unable to read authentication method");
           }
-          if (UserGroupInformation.isSecurityEnabled()
-              && authMethod == AuthMethod.SIMPLE) {
+          if (isSecurityEnabled && authMethod == AuthMethod.SIMPLE) {
             throw new IOException("Authentication is required");
-          } 
-          if (!UserGroupInformation.isSecurityEnabled()
-              && authMethod != AuthMethod.SIMPLE) {
-            throw new IOException("Authentication is not supported");
+          }
+          if (!isSecurityEnabled && authMethod != AuthMethod.SIMPLE) {
+            askClientToUseSimpleAuth();
+            authMethod = AuthMethod.SIMPLE;
+            // client has already sent the initial Sasl message and we
+            // should ignore it. Both client and server should fall back
+            // to simple auth from now on.
+            skipInitialSaslHandshake = true;
           }
           if (authMethod != AuthMethod.SIMPLE) {
             useSasl = true;
@@ -999,6 +1013,11 @@ public abstract class Server {
         if (data.remaining() == 0) {
           dataLengthBuffer.clear();
           data.flip();
+          if (skipInitialSaslHandshake) {
+            data = null;
+            skipInitialSaslHandshake = false;
+            continue;
+          }
           boolean isHeaderRead = headerRead;
           if (useSasl) {
             saslReadAndProcess(data.array());
@@ -1276,6 +1295,7 @@ public abstract class Server {
     this.authorize = 
       conf.getBoolean(ServiceAuthorizationManager.SERVICE_AUTHORIZATION_CONFIG, 
                       false);
+    this.isSecurityEnabled = UserGroupInformation.isSecurityEnabled();
     
     // Start the listener here and let it bind to the port
     listener = new Listener();
@@ -1353,6 +1373,11 @@ public abstract class Server {
     return conf;
   }
   
+  /** for unit testing only, should be called before server is started */ 
+  void disableSecurity() {
+    this.isSecurityEnabled = false;
+  }
+  
   /** Sets the socket buffer size used for responding to RPCs */
   public void setSocketSendBufSize(int size) { this.socketSendBufferSize = size; }
 

Modified: hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/security/SaslRpcClient.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/security/SaslRpcClient.java?rev=1077212&r1=1077211&r2=1077212&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/security/SaslRpcClient.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/security/SaslRpcClient.java Fri Mar  4 03:52:23 2011
@@ -107,9 +107,11 @@ public class SaslRpcClient {
    *          InputStream to use
    * @param outS
    *          OutputStream to use
+   * @return true if connection is set up, or false if needs to switch 
+   *             to simple Auth.
    * @throws IOException
    */
-  public void saslConnect(InputStream inS, OutputStream outS)
+  public boolean saslConnect(InputStream inS, OutputStream outS)
       throws IOException {
     DataInputStream inStream = new DataInputStream(new BufferedInputStream(inS));
     DataOutputStream outStream = new DataOutputStream(new BufferedOutputStream(
@@ -128,7 +130,14 @@ public class SaslRpcClient {
               + " from initSASLContext.");
       }
       if (!saslClient.isComplete()) {
-        saslToken = new byte[inStream.readInt()];
+        int len = inStream.readInt();
+        if (len == SaslRpcServer.SWITCH_TO_SIMPLE_AUTH) {
+          if (LOG.isDebugEnabled())
+            LOG.debug("Server asks us to fall back to simple auth.");
+          saslClient.dispose();
+          return false;
+        }
+        saslToken = new byte[len];
         if (LOG.isDebugEnabled())
           LOG.debug("Will read input token of size " + saslToken.length
               + " for processing by initSASLContext");
@@ -157,8 +166,13 @@ public class SaslRpcClient {
         LOG.debug("SASL client context established. Negotiated QoP: "
             + saslClient.getNegotiatedProperty(Sasl.QOP));
       }
+      return true;
     } catch (IOException e) {
-      saslClient.dispose();
+      try {
+        saslClient.dispose();
+      } catch (SaslException ignored) {
+        // ignore further exceptions during cleanup
+      }
       throw e;
     }
   }

Modified: hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/security/SaslRpcServer.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/security/SaslRpcServer.java?rev=1077212&r1=1077211&r2=1077212&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/security/SaslRpcServer.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/core/org/apache/hadoop/security/SaslRpcServer.java Fri Mar  4 03:52:23 2011
@@ -55,6 +55,7 @@ public class SaslRpcServer {
     // Request mutual authentication
     SASL_PROPS.put(Sasl.SERVER_AUTH, "true");
   }
+  public static final int SWITCH_TO_SIMPLE_AUTH = -88;
 
   static String encodeIdentifier(byte[] identifier) {
     return new String(Base64.encodeBase64(identifier));

Modified: hadoop/common/branches/branch-0.20-security-patches/src/test/findbugsExcludeFile.xml
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/test/findbugsExcludeFile.xml?rev=1077212&r1=1077211&r2=1077212&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/test/findbugsExcludeFile.xml (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/test/findbugsExcludeFile.xml Fri Mar  4 03:52:23 2011
@@ -89,6 +89,14 @@
        <Class name="org.apache.hadoop.mapred.FileOutputCommitter" />
        <Bug pattern="NM_WRONG_PACKAGE_INTENTIONAL" />
      </Match>
+     <!--
+       Further SaslException should be ignored during cleanup and
+       original exception should be re-thrown.
+     -->
+     <Match>
+       <Class name="org.apache.hadoop.security.SaslRpcClient" />
+       <Bug pattern="DE_MIGHT_IGNORE" />
+     </Match>
     <!--
        JobTracker's static variables should be ignored
      -->

Modified: hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/ipc/TestSaslRPC.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/ipc/TestSaslRPC.java?rev=1077212&r1=1077211&r2=1077212&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/ipc/TestSaslRPC.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/ipc/TestSaslRPC.java Fri Mar  4 03:52:23 2011
@@ -75,20 +75,14 @@ public class TestSaslRPC {
     final static Text KIND_NAME = new Text("test.token");
     
     public TestTokenIdentifier() {
-      this.tokenid = new Text();
-      this.realUser = new Text();
+      this(new Text(), new Text());
     }
     public TestTokenIdentifier(Text tokenid) {
-      this.tokenid = tokenid;
-      this.realUser = new Text();
+      this(tokenid, new Text());
     }
     public TestTokenIdentifier(Text tokenid, Text realUser) {
-      this.tokenid = tokenid;
-      if (realUser == null) {
-        this.realUser = new Text();
-      } else {
-        this.realUser = realUser;
-      }
+      this.tokenid = tokenid == null ? new Text() : tokenid;
+      this.realUser = realUser == null ? new Text() : realUser;
     }
     @Override
     public Text getKind() {
@@ -96,7 +90,7 @@ public class TestSaslRPC {
     }
     @Override
     public UserGroupInformation getUser() {
-      if ((realUser == null) || ("".equals(realUser.toString()))) {
+      if ("".equals(realUser.toString())) {
         return UserGroupInformation.createRemoteUser(tokenid.toString());
       } else {
         UserGroupInformation realUgi = UserGroupInformation
@@ -114,9 +108,7 @@ public class TestSaslRPC {
     @Override
     public void write(DataOutput out) throws IOException {
       tokenid.write(out);
-      if (realUser != null) {
-        realUser.write(out);
-      }
+      realUser.write(out);
     }
   }
   
@@ -170,6 +162,20 @@ public class TestSaslRPC {
     final Server server = RPC.getServer(
         new TestSaslImpl(), ADDRESS, 0, 5, true, conf, sm);
 
+    doDigestRpc(server, sm);
+  }
+  
+  @Test
+  public void testSecureToInsecureRpc() throws Exception {
+    Server server = RPC.getServer(TestSaslProtocol.class,
+        new TestSaslImpl(), ADDRESS, 0, 5, true, conf, null);
+    server.disableSecurity();
+    TestTokenSecretManager sm = new TestTokenSecretManager();
+    doDigestRpc(server, sm);
+  }
+  
+  private void doDigestRpc(Server server, TestTokenSecretManager sm)
+      throws Exception {
     server.start();
 
     final UserGroupInformation current = UserGroupInformation.getCurrentUser();