HADOOP-6580. UGI should contain authentication method.

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@933810 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Boris Shkolnik 2010-04-13 23:01:43 +00:00
parent 87c788d2e1
commit b802476d9d
7 changed files with 184 additions and 19 deletions

View File

@ -68,6 +68,8 @@ Trunk (unreleased changes)
HADOOP-6586. Log authentication and authorization failures and successes HADOOP-6586. Log authentication and authorization failures and successes
for RPC (boryas) for RPC (boryas)
HADOOP-6580. UGI should contain authentication method. (jnp via boryas)
IMPROVEMENTS IMPROVEMENTS
HADOOP-6283. Improve the exception messages thrown by HADOOP-6283. Improve the exception messages thrown by

View File

@ -72,6 +72,7 @@
import org.apache.hadoop.security.SaslRpcServer.SaslStatus; import org.apache.hadoop.security.SaslRpcServer.SaslStatus;
import org.apache.hadoop.security.SaslRpcServer.SaslDigestCallbackHandler; import org.apache.hadoop.security.SaslRpcServer.SaslDigestCallbackHandler;
import org.apache.hadoop.security.SaslRpcServer.SaslGssCallbackHandler; import org.apache.hadoop.security.SaslRpcServer.SaslGssCallbackHandler;
import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.authorize.ProxyUsers; import org.apache.hadoop.security.authorize.ProxyUsers;
import org.apache.hadoop.security.authorize.AuthorizationException; import org.apache.hadoop.security.authorize.AuthorizationException;
@ -1084,18 +1085,32 @@ private void processHeader(byte[] buf) throws IOException {
UserGroupInformation protocolUser = header.getUgi(); UserGroupInformation protocolUser = header.getUgi();
if (!useSasl) { if (!useSasl) {
user = protocolUser; user = protocolUser;
} else if ((protocolUser != null) if (user != null) {
user.setAuthenticationMethod(AuthMethod.SIMPLE.authenticationMethod);
}
} else {
// user is authenticated
user.setAuthenticationMethod(authMethod.authenticationMethod);
//Now we check if this is a proxy user case. If the protocol user is
//different from the 'user', it is a proxy user scenario. However,
//this is not allowed if user authenticated with DIGEST.
if ((protocolUser != null)
&& (!protocolUser.getUserName().equals(user.getUserName()))) { && (!protocolUser.getUserName().equals(user.getUserName()))) {
if (authMethod == AuthMethod.DIGEST) { if (authMethod == AuthMethod.DIGEST) {
// Not allowed to doAs if token authentication is used // Not allowed to doAs if token authentication is used
throw new AccessControlException("Authenticated user (" + user throw new AccessControlException("Authenticated user (" + user
+ ") doesn't match what the client claims to be (" + protocolUser + ") doesn't match what the client claims to be ("
+ ")"); + protocolUser + ")");
} else { } else {
// Effective user can be different from authenticated user // Effective user can be different from authenticated user
// for simple auth or kerberos auth // for simple auth or kerberos auth
// The user is the real user. Now we create a proxy user
UserGroupInformation realUser = user;
user = UserGroupInformation.createProxyUser(protocolUser user = UserGroupInformation.createProxyUser(protocolUser
.getUserName(), user); .getUserName(), realUser);
// Now the user is a proxy user, set Authentication method Proxy.
user.setAuthenticationMethod(AuthenticationMethod.PROXY);
}
} }
} }
} }

View File

@ -41,6 +41,7 @@
import org.apache.hadoop.ipc.Server; import org.apache.hadoop.ipc.Server;
import org.apache.hadoop.security.token.SecretManager; import org.apache.hadoop.security.token.SecretManager;
import org.apache.hadoop.security.token.TokenIdentifier; import org.apache.hadoop.security.token.TokenIdentifier;
import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
import org.apache.hadoop.security.token.SecretManager.InvalidToken; import org.apache.hadoop.security.token.SecretManager.InvalidToken;
/** /**
@ -102,17 +103,20 @@ private SaslStatus(int state) {
/** Authentication method */ /** Authentication method */
public static enum AuthMethod { public static enum AuthMethod {
SIMPLE((byte) 80, ""), // no authentication SIMPLE((byte) 80, "", AuthenticationMethod.SIMPLE),
KERBEROS((byte) 81, "GSSAPI"), // SASL Kerberos authentication KERBEROS((byte) 81, "GSSAPI", AuthenticationMethod.KERBEROS),
DIGEST((byte) 82, "DIGEST-MD5"); // SASL DIGEST-MD5 authentication DIGEST((byte) 82, "DIGEST-MD5", AuthenticationMethod.TOKEN);
/** The code for this method. */ /** The code for this method. */
public final byte code; public final byte code;
public final String mechanismName; public final String mechanismName;
public final AuthenticationMethod authenticationMethod;
private AuthMethod(byte code, String mechanismName) { private AuthMethod(byte code, String mechanismName,
AuthenticationMethod authMethod) {
this.code = code; this.code = code;
this.mechanismName = mechanismName; this.mechanismName = mechanismName;
this.authenticationMethod = authMethod;
} }
private static final int FIRST_CODE = values()[0].code; private static final int FIRST_CODE = values()[0].code;

View File

@ -19,6 +19,8 @@
import java.security.Principal; import java.security.Principal;
import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
/** /**
* Save the full and short name of the user as a principal. This allows us to * Save the full and short name of the user as a principal. This allows us to
* have a single type that we always look for when picking up user names. * have a single type that we always look for when picking up user names.
@ -26,8 +28,13 @@
class User implements Principal { class User implements Principal {
private final String fullName; private final String fullName;
private final String shortName; private final String shortName;
private AuthenticationMethod authMethod = null;
public User(String name) { public User(String name) {
this(name, null);
}
public User(String name, AuthenticationMethod authMethod) {
fullName = name; fullName = name;
int atIdx = name.indexOf('@'); int atIdx = name.indexOf('@');
if (atIdx == -1) { if (atIdx == -1) {
@ -40,6 +47,7 @@ public User(String name) {
shortName = name.substring(0, slashIdx); shortName = name.substring(0, slashIdx);
} }
} }
this.authMethod = authMethod;
} }
/** /**
@ -65,7 +73,7 @@ public boolean equals(Object o) {
} else if (o == null || getClass() != o.getClass()) { } else if (o == null || getClass() != o.getClass()) {
return false; return false;
} else { } else {
return fullName.equals(((User) o).fullName); return ((fullName.equals(((User) o).fullName)) && (authMethod == ((User) o).authMethod));
} }
} }
@ -78,4 +86,12 @@ public int hashCode() {
public String toString() { public String toString() {
return fullName; return fullName;
} }
public void setAuthenticationMethod(AuthenticationMethod authMethod) {
this.authMethod = authMethod;
}
public AuthenticationMethod getAuthenticationMethod() {
return authMethod;
}
} }

View File

@ -51,6 +51,7 @@
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.SaslRpcServer.AuthMethod;
import org.apache.hadoop.security.token.Token; import org.apache.hadoop.security.token.Token;
import org.apache.hadoop.security.token.TokenIdentifier; import org.apache.hadoop.security.token.TokenIdentifier;
@ -470,6 +471,15 @@ public static UserGroupInformation createRemoteUser(String user) {
return new UserGroupInformation(subject); return new UserGroupInformation(subject);
} }
public static enum AuthenticationMethod {
SIMPLE,
KERBEROS,
TOKEN,
CERTIFICATE,
KERBEROS_SSL,
PROXY;
}
/* Create a proxy user using username of the effective user and the ugi of the /* Create a proxy user using username of the effective user and the ugi of the
* real user. * real user.
* *
@ -649,6 +659,30 @@ public String toString() {
} }
} }
/**
* Sets the authentication method in the subject
*
* @param authMethod
*/
public synchronized
void setAuthenticationMethod(AuthenticationMethod authMethod) {
for (User p : subject.getPrincipals(User.class)) {
p.setAuthenticationMethod(authMethod);
}
}
/**
* Get the authentication method from the subject
*
* @return AuthenticationMethod in the subject, null if not present.
*/
public synchronized AuthenticationMethod getAuthenticationMethod() {
for (User p: subject.getPrincipals(User.class)) {
return p.getAuthenticationMethod();
}
return null;
}
/** /**
* Compare the subjects to see if they are equal to each other. * Compare the subjects to see if they are equal to each other.
*/ */

View File

@ -25,8 +25,11 @@
import java.io.DataOutput; import java.io.DataOutput;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.security.PrivilegedExceptionAction;
import java.util.Collection; import java.util.Collection;
import junit.framework.Assert;
import org.apache.commons.logging.*; import org.apache.commons.logging.*;
import org.apache.commons.logging.impl.Log4JLogger; import org.apache.commons.logging.impl.Log4JLogger;
@ -44,6 +47,7 @@
import org.apache.hadoop.security.SaslRpcClient; import org.apache.hadoop.security.SaslRpcClient;
import org.apache.hadoop.security.SaslRpcServer; import org.apache.hadoop.security.SaslRpcServer;
import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
import org.apache.log4j.Level; import org.apache.log4j.Level;
import org.junit.Test; import org.junit.Test;
@ -161,10 +165,14 @@ public Token<TestTokenIdentifier> selectToken(Text service,
@KerberosInfo(SERVER_PRINCIPAL_KEY) @KerberosInfo(SERVER_PRINCIPAL_KEY)
@TokenInfo(TestTokenSelector.class) @TokenInfo(TestTokenSelector.class)
public interface TestSaslProtocol extends TestRPC.TestProtocol { public interface TestSaslProtocol extends TestRPC.TestProtocol {
public AuthenticationMethod getAuthMethod() throws IOException;
} }
public static class TestSaslImpl extends TestRPC.TestImpl implements public static class TestSaslImpl extends TestRPC.TestImpl implements
TestSaslProtocol { TestSaslProtocol {
public AuthenticationMethod getAuthMethod() throws IOException {
return UserGroupInformation.getCurrentUser().getAuthenticationMethod();
}
} }
@Test @Test
@ -258,6 +266,43 @@ static void testKerberosRpc(String principal, String keytab) throws Exception {
} }
} }
@Test
public void testDigestAuthMethod() throws Exception {
TestTokenSecretManager sm = new TestTokenSecretManager();
Server server = RPC.getServer(TestSaslProtocol.class,
new TestSaslImpl(), ADDRESS, 0, 5, true, conf, sm);
server.start();
final UserGroupInformation current = UserGroupInformation.getCurrentUser();
final InetSocketAddress addr = NetUtils.getConnectAddress(server);
TestTokenIdentifier tokenId = new TestTokenIdentifier(new Text(current
.getUserName()));
Token<TestTokenIdentifier> token = new Token<TestTokenIdentifier>(tokenId,
sm);
Text host = new Text(addr.getAddress().getHostAddress() + ":"
+ addr.getPort());
token.setService(host);
LOG.info("Service IP address for token is " + host);
current.addToken(token);
current.doAs(new PrivilegedExceptionAction<Object>() {
public Object run() throws IOException {
TestSaslProtocol proxy = null;
try {
proxy = (TestSaslProtocol) RPC.getProxy(TestSaslProtocol.class,
TestSaslProtocol.versionID, addr, conf);
Assert.assertEquals(AuthenticationMethod.TOKEN, proxy.getAuthMethod());
} finally {
if (proxy != null) {
RPC.stopProxy(proxy);
}
}
return null;
}
});
server.stop();
}
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
System.out.println("Testing Kerberos authentication over RPC"); System.out.println("Testing Kerberos authentication over RPC");
if (args.length != 2) { if (args.length != 2) {

View File

@ -27,13 +27,15 @@
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction; import java.security.PrivilegedExceptionAction;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import org.apache.hadoop.conf.Configuration; import junit.framework.Assert;
import org.apache.hadoop.security.SaslRpcServer.AuthMethod;
import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
import org.apache.hadoop.security.token.Token; import org.apache.hadoop.security.token.Token;
import org.apache.hadoop.security.token.TokenIdentifier; import org.apache.hadoop.security.token.TokenIdentifier;
import org.junit.Test; import org.junit.Test;
@ -212,4 +214,51 @@ public Collection<Token<?>> run() throws IOException {
assertTrue(otherSet.contains(t1)); assertTrue(otherSet.contains(t1));
assertTrue(otherSet.contains(t2)); assertTrue(otherSet.contains(t2));
} }
@Test
public void testUGIAuthMethod() throws Exception {
final UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
final AuthenticationMethod am = AuthenticationMethod.KERBEROS;
ugi.setAuthenticationMethod(am);
Assert.assertEquals(am, ugi.getAuthenticationMethod());
ugi.doAs(new PrivilegedExceptionAction<Object>() {
public Object run() throws IOException {
Assert.assertEquals(am, UserGroupInformation.getCurrentUser()
.getAuthenticationMethod());
return null;
}
});
}
@Test
public void testUGIAuthMethodInRealUser() throws Exception {
final UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
UserGroupInformation proxyUgi = UserGroupInformation.createProxyUser(
"proxy", ugi);
final AuthenticationMethod am = AuthenticationMethod.KERBEROS;
ugi.setAuthenticationMethod(am);
Assert.assertEquals(am, ugi.getAuthenticationMethod());
Assert.assertEquals(null, proxyUgi.getAuthenticationMethod());
proxyUgi.setAuthenticationMethod(AuthenticationMethod.PROXY);
proxyUgi.doAs(new PrivilegedExceptionAction<Object>() {
public Object run() throws IOException {
Assert.assertEquals(AuthenticationMethod.PROXY, UserGroupInformation
.getCurrentUser().getAuthenticationMethod());
Assert.assertEquals(am, UserGroupInformation.getCurrentUser()
.getRealUser().getAuthenticationMethod());
return null;
}
});
UserGroupInformation proxyUgi2 = UserGroupInformation.createProxyUser(
"proxy", ugi);
proxyUgi2.setAuthenticationMethod(AuthenticationMethod.PROXY);
Assert.assertEquals(proxyUgi, proxyUgi2);
// Equality should work if authMethod is null
UserGroupInformation realugi = UserGroupInformation.getCurrentUser();
UserGroupInformation proxyUgi3 = UserGroupInformation.createProxyUser(
"proxyAnother", realugi);
UserGroupInformation proxyUgi4 = UserGroupInformation.createProxyUser(
"proxyAnother", realugi);
Assert.assertEquals(proxyUgi3, proxyUgi4);
}
} }