HADOOP-9698. [RPC v9] Client must honor server's SASL negotiate response (daryn)
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1508086 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
13b526b15f
commit
65be212675
|
@ -352,6 +352,8 @@ Release 2.1.0-beta - 2013-07-02
|
||||||
|
|
||||||
HADOOP-9683. [RPC v9] Wrap IpcConnectionContext in RPC headers (daryn)
|
HADOOP-9683. [RPC v9] Wrap IpcConnectionContext in RPC headers (daryn)
|
||||||
|
|
||||||
|
HADOOP-9698. [RPC v9] Client must honor server's SASL negotiate response (daryn)
|
||||||
|
|
||||||
NEW FEATURES
|
NEW FEATURES
|
||||||
|
|
||||||
HADOOP-9283. Add support for running the Hadoop client on AIX. (atm)
|
HADOOP-9283. Add support for running the Hadoop client on AIX. (atm)
|
||||||
|
|
|
@ -82,11 +82,6 @@ import org.apache.hadoop.security.SaslRpcClient;
|
||||||
import org.apache.hadoop.security.SaslRpcServer.AuthMethod;
|
import org.apache.hadoop.security.SaslRpcServer.AuthMethod;
|
||||||
import org.apache.hadoop.security.SecurityUtil;
|
import org.apache.hadoop.security.SecurityUtil;
|
||||||
import org.apache.hadoop.security.UserGroupInformation;
|
import org.apache.hadoop.security.UserGroupInformation;
|
||||||
import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
|
|
||||||
import org.apache.hadoop.security.token.Token;
|
|
||||||
import org.apache.hadoop.security.token.TokenIdentifier;
|
|
||||||
import org.apache.hadoop.security.token.TokenInfo;
|
|
||||||
import org.apache.hadoop.security.token.TokenSelector;
|
|
||||||
import org.apache.hadoop.util.ProtoUtil;
|
import org.apache.hadoop.util.ProtoUtil;
|
||||||
import org.apache.hadoop.util.ReflectionUtils;
|
import org.apache.hadoop.util.ReflectionUtils;
|
||||||
import org.apache.hadoop.util.StringUtils;
|
import org.apache.hadoop.util.StringUtils;
|
||||||
|
@ -368,10 +363,9 @@ public class Client {
|
||||||
* socket: responses may be delivered out of order. */
|
* socket: responses may be delivered out of order. */
|
||||||
private class Connection extends Thread {
|
private class Connection extends Thread {
|
||||||
private InetSocketAddress server; // server ip:port
|
private InetSocketAddress server; // server ip:port
|
||||||
private String serverPrincipal; // server's krb5 principal name
|
|
||||||
private final ConnectionId remoteId; // connection id
|
private final ConnectionId remoteId; // connection id
|
||||||
private AuthMethod authMethod; // authentication method
|
private AuthMethod authMethod; // authentication method
|
||||||
private Token<? extends TokenIdentifier> token;
|
private AuthProtocol authProtocol;
|
||||||
private int serviceClass;
|
private int serviceClass;
|
||||||
private SaslRpcClient saslRpcClient;
|
private SaslRpcClient saslRpcClient;
|
||||||
|
|
||||||
|
@ -418,45 +412,11 @@ public class Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
UserGroupInformation ticket = remoteId.getTicket();
|
UserGroupInformation ticket = remoteId.getTicket();
|
||||||
Class<?> protocol = remoteId.getProtocol();
|
// try SASL if security is enabled or if the ugi contains tokens.
|
||||||
if (protocol != null) {
|
// this causes a SIMPLE client with tokens to attempt SASL
|
||||||
TokenInfo tokenInfo = SecurityUtil.getTokenInfo(protocol, conf);
|
boolean trySasl = UserGroupInformation.isSecurityEnabled() ||
|
||||||
if (tokenInfo != null) {
|
(ticket != null && !ticket.getTokens().isEmpty());
|
||||||
TokenSelector<? extends TokenIdentifier> tokenSelector = null;
|
this.authProtocol = trySasl ? AuthProtocol.SASL : AuthProtocol.NONE;
|
||||||
try {
|
|
||||||
tokenSelector = tokenInfo.value().newInstance();
|
|
||||||
} catch (InstantiationException e) {
|
|
||||||
throw new IOException(e.toString());
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new IOException(e.toString());
|
|
||||||
}
|
|
||||||
token = tokenSelector.selectToken(
|
|
||||||
SecurityUtil.buildTokenService(server),
|
|
||||||
ticket.getTokens());
|
|
||||||
}
|
|
||||||
KerberosInfo krbInfo = SecurityUtil.getKerberosInfo(protocol, conf);
|
|
||||||
if (krbInfo != null) {
|
|
||||||
serverPrincipal = remoteId.getServerPrincipal();
|
|
||||||
if (LOG.isDebugEnabled()) {
|
|
||||||
LOG.debug("RPC Server's Kerberos principal name for protocol="
|
|
||||||
+ protocol.getCanonicalName() + " is " + serverPrincipal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AuthenticationMethod authentication;
|
|
||||||
if (token != null) {
|
|
||||||
authentication = AuthenticationMethod.TOKEN;
|
|
||||||
} else if (ticket != null) {
|
|
||||||
authentication = ticket.getRealAuthenticationMethod();
|
|
||||||
} else { // this only happens in lazy tests
|
|
||||||
authentication = AuthenticationMethod.SIMPLE;
|
|
||||||
}
|
|
||||||
authMethod = authentication.getAuthMethod();
|
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("Use " + authMethod + " authentication for protocol "
|
|
||||||
+ (protocol == null? null: protocol.getSimpleName()));
|
|
||||||
|
|
||||||
this.setName("IPC Client (" + socketFactory.hashCode() +") connection to " +
|
this.setName("IPC Client (" + socketFactory.hashCode() +") connection to " +
|
||||||
server.toString() +
|
server.toString() +
|
||||||
|
@ -567,11 +527,10 @@ public class Client {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized boolean setupSaslConnection(final InputStream in2,
|
private synchronized AuthMethod setupSaslConnection(final InputStream in2,
|
||||||
final OutputStream out2)
|
final OutputStream out2) throws IOException, InterruptedException {
|
||||||
throws IOException {
|
saslRpcClient = new SaslRpcClient(remoteId.getTicket(),
|
||||||
saslRpcClient = new SaslRpcClient(authMethod, token, serverPrincipal,
|
remoteId.getProtocol(), remoteId.getAddress(), conf);
|
||||||
fallbackAllowed);
|
|
||||||
return saslRpcClient.saslConnect(in2, out2);
|
return saslRpcClient.saslConnect(in2, out2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -609,7 +568,8 @@ public class Client {
|
||||||
* client, to ensure Server matching address of the client connection
|
* client, to ensure Server matching address of the client connection
|
||||||
* to host name in principal passed.
|
* to host name in principal passed.
|
||||||
*/
|
*/
|
||||||
if (UserGroupInformation.isSecurityEnabled()) {
|
UserGroupInformation ticket = remoteId.getTicket();
|
||||||
|
if (ticket != null && ticket.hasKerberosCredentials()) {
|
||||||
KerberosInfo krbInfo =
|
KerberosInfo krbInfo =
|
||||||
remoteId.getProtocol().getAnnotation(KerberosInfo.class);
|
remoteId.getProtocol().getAnnotation(KerberosInfo.class);
|
||||||
if (krbInfo != null && krbInfo.clientPrincipal() != null) {
|
if (krbInfo != null && krbInfo.clientPrincipal() != null) {
|
||||||
|
@ -687,7 +647,7 @@ public class Client {
|
||||||
} else {
|
} else {
|
||||||
String msg = "Couldn't setup connection for "
|
String msg = "Couldn't setup connection for "
|
||||||
+ UserGroupInformation.getLoginUser().getUserName() + " to "
|
+ UserGroupInformation.getLoginUser().getUserName() + " to "
|
||||||
+ serverPrincipal;
|
+ remoteId;
|
||||||
LOG.warn(msg);
|
LOG.warn(msg);
|
||||||
throw (IOException) new IOException(msg).initCause(ex);
|
throw (IOException) new IOException(msg).initCause(ex);
|
||||||
}
|
}
|
||||||
|
@ -723,19 +683,19 @@ public class Client {
|
||||||
InputStream inStream = NetUtils.getInputStream(socket);
|
InputStream inStream = NetUtils.getInputStream(socket);
|
||||||
OutputStream outStream = NetUtils.getOutputStream(socket);
|
OutputStream outStream = NetUtils.getOutputStream(socket);
|
||||||
writeConnectionHeader(outStream);
|
writeConnectionHeader(outStream);
|
||||||
if (authMethod != AuthMethod.SIMPLE) {
|
if (authProtocol == AuthProtocol.SASL) {
|
||||||
final InputStream in2 = inStream;
|
final InputStream in2 = inStream;
|
||||||
final OutputStream out2 = outStream;
|
final OutputStream out2 = outStream;
|
||||||
UserGroupInformation ticket = remoteId.getTicket();
|
UserGroupInformation ticket = remoteId.getTicket();
|
||||||
if (ticket.getRealUser() != null) {
|
if (ticket.getRealUser() != null) {
|
||||||
ticket = ticket.getRealUser();
|
ticket = ticket.getRealUser();
|
||||||
}
|
}
|
||||||
boolean continueSasl = false;
|
|
||||||
try {
|
try {
|
||||||
continueSasl = ticket
|
authMethod = ticket
|
||||||
.doAs(new PrivilegedExceptionAction<Boolean>() {
|
.doAs(new PrivilegedExceptionAction<AuthMethod>() {
|
||||||
@Override
|
@Override
|
||||||
public Boolean run() throws IOException {
|
public AuthMethod run()
|
||||||
|
throws IOException, InterruptedException {
|
||||||
return setupSaslConnection(in2, out2);
|
return setupSaslConnection(in2, out2);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -747,13 +707,15 @@ public class Client {
|
||||||
ticket);
|
ticket);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (continueSasl) {
|
if (authMethod != AuthMethod.SIMPLE) {
|
||||||
// Sasl connect is successful. Let's set up Sasl i/o streams.
|
// Sasl connect is successful. Let's set up Sasl i/o streams.
|
||||||
inStream = saslRpcClient.getInputStream(inStream);
|
inStream = saslRpcClient.getInputStream(inStream);
|
||||||
outStream = saslRpcClient.getOutputStream(outStream);
|
outStream = saslRpcClient.getOutputStream(outStream);
|
||||||
} else {
|
} else if (UserGroupInformation.isSecurityEnabled() &&
|
||||||
// fall back to simple auth because server told us so.
|
!fallbackAllowed) {
|
||||||
authMethod = AuthMethod.SIMPLE;
|
throw new IOException("Server asks us to fall back to SIMPLE " +
|
||||||
|
"auth, but this client is configured to only allow secure " +
|
||||||
|
"connections.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -873,14 +835,6 @@ public class Client {
|
||||||
out.write(RpcConstants.HEADER.array());
|
out.write(RpcConstants.HEADER.array());
|
||||||
out.write(RpcConstants.CURRENT_VERSION);
|
out.write(RpcConstants.CURRENT_VERSION);
|
||||||
out.write(serviceClass);
|
out.write(serviceClass);
|
||||||
final AuthProtocol authProtocol;
|
|
||||||
switch (authMethod) {
|
|
||||||
case SIMPLE:
|
|
||||||
authProtocol = AuthProtocol.NONE;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
authProtocol = AuthProtocol.SASL;
|
|
||||||
}
|
|
||||||
out.write(authProtocol.callId);
|
out.write(authProtocol.callId);
|
||||||
out.flush();
|
out.flush();
|
||||||
}
|
}
|
||||||
|
@ -1493,7 +1447,6 @@ public class Client {
|
||||||
final Class<?> protocol;
|
final Class<?> protocol;
|
||||||
private static final int PRIME = 16777619;
|
private static final int PRIME = 16777619;
|
||||||
private final int rpcTimeout;
|
private final int rpcTimeout;
|
||||||
private final String serverPrincipal;
|
|
||||||
private final int maxIdleTime; //connections will be culled if it was idle for
|
private final int maxIdleTime; //connections will be culled if it was idle for
|
||||||
//maxIdleTime msecs
|
//maxIdleTime msecs
|
||||||
private final RetryPolicy connectionRetryPolicy;
|
private final RetryPolicy connectionRetryPolicy;
|
||||||
|
@ -1504,15 +1457,13 @@ public class Client {
|
||||||
private final int pingInterval; // how often sends ping to the server in msecs
|
private final int pingInterval; // how often sends ping to the server in msecs
|
||||||
|
|
||||||
ConnectionId(InetSocketAddress address, Class<?> protocol,
|
ConnectionId(InetSocketAddress address, Class<?> protocol,
|
||||||
UserGroupInformation ticket, int rpcTimeout,
|
UserGroupInformation ticket, int rpcTimeout, int maxIdleTime,
|
||||||
String serverPrincipal, int maxIdleTime,
|
|
||||||
RetryPolicy connectionRetryPolicy, int maxRetriesOnSocketTimeouts,
|
RetryPolicy connectionRetryPolicy, int maxRetriesOnSocketTimeouts,
|
||||||
boolean tcpNoDelay, boolean doPing, int pingInterval) {
|
boolean tcpNoDelay, boolean doPing, int pingInterval) {
|
||||||
this.protocol = protocol;
|
this.protocol = protocol;
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.ticket = ticket;
|
this.ticket = ticket;
|
||||||
this.rpcTimeout = rpcTimeout;
|
this.rpcTimeout = rpcTimeout;
|
||||||
this.serverPrincipal = serverPrincipal;
|
|
||||||
this.maxIdleTime = maxIdleTime;
|
this.maxIdleTime = maxIdleTime;
|
||||||
this.connectionRetryPolicy = connectionRetryPolicy;
|
this.connectionRetryPolicy = connectionRetryPolicy;
|
||||||
this.maxRetriesOnSocketTimeouts = maxRetriesOnSocketTimeouts;
|
this.maxRetriesOnSocketTimeouts = maxRetriesOnSocketTimeouts;
|
||||||
|
@ -1537,10 +1488,6 @@ public class Client {
|
||||||
return rpcTimeout;
|
return rpcTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getServerPrincipal() {
|
|
||||||
return serverPrincipal;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getMaxIdleTime() {
|
int getMaxIdleTime() {
|
||||||
return maxIdleTime;
|
return maxIdleTime;
|
||||||
}
|
}
|
||||||
|
@ -1590,11 +1537,9 @@ public class Client {
|
||||||
max, 1, TimeUnit.SECONDS);
|
max, 1, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
String remotePrincipal = getRemotePrincipal(conf, addr, protocol);
|
|
||||||
boolean doPing =
|
boolean doPing =
|
||||||
conf.getBoolean(CommonConfigurationKeys.IPC_CLIENT_PING_KEY, true);
|
conf.getBoolean(CommonConfigurationKeys.IPC_CLIENT_PING_KEY, true);
|
||||||
return new ConnectionId(addr, protocol, ticket,
|
return new ConnectionId(addr, protocol, ticket, rpcTimeout,
|
||||||
rpcTimeout, remotePrincipal,
|
|
||||||
conf.getInt(CommonConfigurationKeysPublic.IPC_CLIENT_CONNECTION_MAXIDLETIME_KEY,
|
conf.getInt(CommonConfigurationKeysPublic.IPC_CLIENT_CONNECTION_MAXIDLETIME_KEY,
|
||||||
CommonConfigurationKeysPublic.IPC_CLIENT_CONNECTION_MAXIDLETIME_DEFAULT),
|
CommonConfigurationKeysPublic.IPC_CLIENT_CONNECTION_MAXIDLETIME_DEFAULT),
|
||||||
connectionRetryPolicy,
|
connectionRetryPolicy,
|
||||||
|
@ -1607,25 +1552,6 @@ public class Client {
|
||||||
(doPing ? Client.getPingInterval(conf) : 0));
|
(doPing ? Client.getPingInterval(conf) : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getRemotePrincipal(Configuration conf,
|
|
||||||
InetSocketAddress address, Class<?> protocol) throws IOException {
|
|
||||||
if (!UserGroupInformation.isSecurityEnabled() || protocol == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
KerberosInfo krbInfo = SecurityUtil.getKerberosInfo(protocol, conf);
|
|
||||||
if (krbInfo != null) {
|
|
||||||
String serverKey = krbInfo.serverPrincipal();
|
|
||||||
if (serverKey == null) {
|
|
||||||
throw new IOException(
|
|
||||||
"Can't obtain server Kerberos config key from protocol="
|
|
||||||
+ protocol.getCanonicalName());
|
|
||||||
}
|
|
||||||
return SecurityUtil.getServerPrincipal(conf.get(serverKey), address
|
|
||||||
.getAddress());
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean isEqual(Object a, Object b) {
|
static boolean isEqual(Object a, Object b) {
|
||||||
return a == null ? b == null : a.equals(b);
|
return a == null ? b == null : a.equals(b);
|
||||||
}
|
}
|
||||||
|
@ -1644,7 +1570,6 @@ public class Client {
|
||||||
&& this.pingInterval == that.pingInterval
|
&& this.pingInterval == that.pingInterval
|
||||||
&& isEqual(this.protocol, that.protocol)
|
&& isEqual(this.protocol, that.protocol)
|
||||||
&& this.rpcTimeout == that.rpcTimeout
|
&& this.rpcTimeout == that.rpcTimeout
|
||||||
&& isEqual(this.serverPrincipal, that.serverPrincipal)
|
|
||||||
&& this.tcpNoDelay == that.tcpNoDelay
|
&& this.tcpNoDelay == that.tcpNoDelay
|
||||||
&& isEqual(this.ticket, that.ticket);
|
&& isEqual(this.ticket, that.ticket);
|
||||||
}
|
}
|
||||||
|
@ -1660,8 +1585,6 @@ public class Client {
|
||||||
result = PRIME * result + pingInterval;
|
result = PRIME * result + pingInterval;
|
||||||
result = PRIME * result + ((protocol == null) ? 0 : protocol.hashCode());
|
result = PRIME * result + ((protocol == null) ? 0 : protocol.hashCode());
|
||||||
result = PRIME * result + rpcTimeout;
|
result = PRIME * result + rpcTimeout;
|
||||||
result = PRIME * result
|
|
||||||
+ ((serverPrincipal == null) ? 0 : serverPrincipal.hashCode());
|
|
||||||
result = PRIME * result + (tcpNoDelay ? 1231 : 1237);
|
result = PRIME * result + (tcpNoDelay ? 1231 : 1237);
|
||||||
result = PRIME * result + ((ticket == null) ? 0 : ticket.hashCode());
|
result = PRIME * result + ((ticket == null) ? 0 : ticket.hashCode());
|
||||||
return result;
|
return result;
|
||||||
|
@ -1669,7 +1592,7 @@ public class Client {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return serverPrincipal + "@" + address;
|
return address.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,7 @@ import org.apache.hadoop.ipc.protobuf.RpcHeaderProtos.RpcResponseHeaderProto;
|
||||||
import org.apache.hadoop.ipc.protobuf.RpcHeaderProtos.RpcResponseHeaderProto.RpcErrorCodeProto;
|
import org.apache.hadoop.ipc.protobuf.RpcHeaderProtos.RpcResponseHeaderProto.RpcErrorCodeProto;
|
||||||
import org.apache.hadoop.ipc.protobuf.RpcHeaderProtos.RpcResponseHeaderProto.RpcStatusProto;
|
import org.apache.hadoop.ipc.protobuf.RpcHeaderProtos.RpcResponseHeaderProto.RpcStatusProto;
|
||||||
import org.apache.hadoop.ipc.protobuf.RpcHeaderProtos.RpcSaslProto;
|
import org.apache.hadoop.ipc.protobuf.RpcHeaderProtos.RpcSaslProto;
|
||||||
|
import org.apache.hadoop.ipc.protobuf.RpcHeaderProtos.RpcSaslProto.SaslAuth;
|
||||||
import org.apache.hadoop.ipc.protobuf.RpcHeaderProtos.RpcSaslProto.SaslState;
|
import org.apache.hadoop.ipc.protobuf.RpcHeaderProtos.RpcSaslProto.SaslState;
|
||||||
import org.apache.hadoop.net.NetUtils;
|
import org.apache.hadoop.net.NetUtils;
|
||||||
import org.apache.hadoop.security.AccessControlException;
|
import org.apache.hadoop.security.AccessControlException;
|
||||||
|
@ -795,7 +796,10 @@ public abstract class Server {
|
||||||
LOG.info(getName() + ": readAndProcess caught InterruptedException", ieo);
|
LOG.info(getName() + ": readAndProcess caught InterruptedException", ieo);
|
||||||
throw ieo;
|
throw ieo;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// log stack trace for "interesting" exceptions not sent to client
|
// a WrappedRpcServerException is an exception that has been sent
|
||||||
|
// to the client, so the stacktrace is unnecessary; any other
|
||||||
|
// exceptions are unexpected internal server errors and thus the
|
||||||
|
// stacktrace should be logged
|
||||||
LOG.info(getName() + ": readAndProcess from client " +
|
LOG.info(getName() + ": readAndProcess from client " +
|
||||||
c.getHostAddress() + " threw exception [" + e + "]",
|
c.getHostAddress() + " threw exception [" + e + "]",
|
||||||
(e instanceof WrappedRpcServerException) ? null : e);
|
(e instanceof WrappedRpcServerException) ? null : e);
|
||||||
|
@ -1164,7 +1168,6 @@ public abstract class Server {
|
||||||
private AuthMethod authMethod;
|
private AuthMethod authMethod;
|
||||||
private AuthProtocol authProtocol;
|
private AuthProtocol authProtocol;
|
||||||
private boolean saslContextEstablished;
|
private boolean saslContextEstablished;
|
||||||
private boolean skipInitialSaslHandshake;
|
|
||||||
private ByteBuffer connectionHeaderBuf = null;
|
private ByteBuffer connectionHeaderBuf = null;
|
||||||
private ByteBuffer unwrappedData;
|
private ByteBuffer unwrappedData;
|
||||||
private ByteBuffer unwrappedDataLengthBuffer;
|
private ByteBuffer unwrappedDataLengthBuffer;
|
||||||
|
@ -1339,23 +1342,39 @@ public abstract class Server {
|
||||||
"Client already attempted negotiation");
|
"Client already attempted negotiation");
|
||||||
}
|
}
|
||||||
saslResponse = buildSaslNegotiateResponse();
|
saslResponse = buildSaslNegotiateResponse();
|
||||||
|
// simple-only server negotiate response is success which client
|
||||||
|
// interprets as switch to simple
|
||||||
|
if (saslResponse.getState() == SaslState.SUCCESS) {
|
||||||
|
switchToSimple();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case INITIATE: {
|
case INITIATE: {
|
||||||
if (saslMessage.getAuthsCount() != 1) {
|
if (saslMessage.getAuthsCount() != 1) {
|
||||||
throw new SaslException("Client mechanism is malformed");
|
throw new SaslException("Client mechanism is malformed");
|
||||||
}
|
}
|
||||||
String authMethodName = saslMessage.getAuths(0).getMethod();
|
// verify the client requested an advertised authType
|
||||||
authMethod = createSaslServer(authMethodName);
|
SaslAuth clientSaslAuth = saslMessage.getAuths(0);
|
||||||
if (authMethod == null) { // the auth method is not supported
|
if (!negotiateResponse.getAuthsList().contains(clientSaslAuth)) {
|
||||||
if (sentNegotiate) {
|
if (sentNegotiate) {
|
||||||
throw new AccessControlException(
|
throw new AccessControlException(
|
||||||
authMethodName + " authentication is not enabled."
|
clientSaslAuth.getMethod() + " authentication is not enabled."
|
||||||
+ " Available:" + enabledAuthMethods);
|
+ " Available:" + enabledAuthMethods);
|
||||||
}
|
}
|
||||||
saslResponse = buildSaslNegotiateResponse();
|
saslResponse = buildSaslNegotiateResponse();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
authMethod = AuthMethod.valueOf(clientSaslAuth.getMethod());
|
||||||
|
// abort SASL for SIMPLE auth, server has already ensured that
|
||||||
|
// SIMPLE is a legit option above. we will send no response
|
||||||
|
if (authMethod == AuthMethod.SIMPLE) {
|
||||||
|
switchToSimple();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// sasl server for tokens may already be instantiated
|
||||||
|
if (saslServer == null || authMethod != AuthMethod.TOKEN) {
|
||||||
|
saslServer = createSaslServer(authMethod);
|
||||||
|
}
|
||||||
// fallthru to process sasl token
|
// fallthru to process sasl token
|
||||||
}
|
}
|
||||||
case RESPONSE: {
|
case RESPONSE: {
|
||||||
|
@ -1379,6 +1398,12 @@ public abstract class Server {
|
||||||
return saslResponse;
|
return saslResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void switchToSimple() {
|
||||||
|
// disable SASL and blank out any SASL server
|
||||||
|
authProtocol = AuthProtocol.NONE;
|
||||||
|
saslServer = null;
|
||||||
|
}
|
||||||
|
|
||||||
private RpcSaslProto buildSaslResponse(SaslState state, byte[] replyToken) {
|
private RpcSaslProto buildSaslResponse(SaslState state, byte[] replyToken) {
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("Will send " + state + " token of size "
|
LOG.debug("Will send " + state + " token of size "
|
||||||
|
@ -1434,7 +1459,8 @@ public abstract class Server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int readAndProcess() throws IOException, InterruptedException {
|
public int readAndProcess()
|
||||||
|
throws WrappedRpcServerException, IOException, InterruptedException {
|
||||||
while (true) {
|
while (true) {
|
||||||
/* Read at most one RPC. If the header is not read completely yet
|
/* Read at most one RPC. If the header is not read completely yet
|
||||||
* then iterate until we read first RPC or until there is no data left.
|
* then iterate until we read first RPC or until there is no data left.
|
||||||
|
@ -1537,15 +1563,7 @@ public abstract class Server {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SASL: {
|
default: {
|
||||||
// switch to simple hack, but don't switch if other auths are
|
|
||||||
// supported, ex. tokens
|
|
||||||
if (isSimpleEnabled && enabledAuthMethods.size() == 1) {
|
|
||||||
authProtocol = AuthProtocol.NONE;
|
|
||||||
skipInitialSaslHandshake = true;
|
|
||||||
doSaslReply(buildSaslResponse(SaslState.SUCCESS, null));
|
|
||||||
}
|
|
||||||
// else wait for a negotiate or initiate
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1570,25 +1588,6 @@ public abstract class Server {
|
||||||
return negotiateMessage;
|
return negotiateMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AuthMethod createSaslServer(String authMethodName)
|
|
||||||
throws IOException, InterruptedException {
|
|
||||||
AuthMethod authMethod;
|
|
||||||
try {
|
|
||||||
authMethod = AuthMethod.valueOf(authMethodName);
|
|
||||||
if (!enabledAuthMethods.contains(authMethod)) {
|
|
||||||
authMethod = null;
|
|
||||||
}
|
|
||||||
} catch (IllegalArgumentException iae) {
|
|
||||||
authMethod = null;
|
|
||||||
}
|
|
||||||
if (authMethod != null &&
|
|
||||||
// sasl server for tokens may already be instantiated
|
|
||||||
(saslServer == null || authMethod != AuthMethod.TOKEN)) {
|
|
||||||
saslServer = createSaslServer(authMethod);
|
|
||||||
}
|
|
||||||
return authMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SaslServer createSaslServer(AuthMethod authMethod)
|
private SaslServer createSaslServer(AuthMethod authMethod)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
return new SaslRpcServer(authMethod).create(this, secretManager);
|
return new SaslRpcServer(authMethod).create(this, secretManager);
|
||||||
|
@ -1703,8 +1702,8 @@ public abstract class Server {
|
||||||
* or the request could not be decoded into a Call
|
* or the request could not be decoded into a Call
|
||||||
* @throws InterruptedException
|
* @throws InterruptedException
|
||||||
*/
|
*/
|
||||||
private void processRpcRequestPacket(byte[] buf) throws IOException,
|
private void processRpcRequestPacket(byte[] buf)
|
||||||
InterruptedException {
|
throws WrappedRpcServerException, IOException, InterruptedException {
|
||||||
if (saslContextEstablished && useWrap) {
|
if (saslContextEstablished && useWrap) {
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Have read input token of size " + buf.length
|
LOG.debug("Have read input token of size " + buf.length
|
||||||
|
@ -1717,8 +1716,8 @@ public abstract class Server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void unwrapPacketAndProcessRpcs(byte[] inBuf) throws IOException,
|
private void unwrapPacketAndProcessRpcs(byte[] inBuf)
|
||||||
InterruptedException {
|
throws WrappedRpcServerException, IOException, InterruptedException {
|
||||||
ReadableByteChannel ch = Channels.newChannel(new ByteArrayInputStream(
|
ReadableByteChannel ch = Channels.newChannel(new ByteArrayInputStream(
|
||||||
inBuf));
|
inBuf));
|
||||||
// Read all RPCs contained in the inBuf, even partial ones
|
// Read all RPCs contained in the inBuf, even partial ones
|
||||||
|
@ -1903,13 +1902,9 @@ public abstract class Server {
|
||||||
} else if (callId == AuthProtocol.SASL.callId) {
|
} else if (callId == AuthProtocol.SASL.callId) {
|
||||||
// if client was switched to simple, ignore first SASL message
|
// if client was switched to simple, ignore first SASL message
|
||||||
if (authProtocol != AuthProtocol.SASL) {
|
if (authProtocol != AuthProtocol.SASL) {
|
||||||
if (!skipInitialSaslHandshake) {
|
throw new WrappedRpcServerException(
|
||||||
throw new WrappedRpcServerException(
|
RpcErrorCodeProto.FATAL_INVALID_RPC_HEADER,
|
||||||
RpcErrorCodeProto.FATAL_INVALID_RPC_HEADER,
|
"SASL protocol not requested by client");
|
||||||
"SASL protocol not requested by client");
|
|
||||||
}
|
|
||||||
skipInitialSaslHandshake = false;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
RpcSaslProto response = saslReadAndProcess(dis);
|
RpcSaslProto response = saslReadAndProcess(dis);
|
||||||
// send back response if any, may throw IOException
|
// send back response if any, may throw IOException
|
||||||
|
@ -2220,17 +2215,23 @@ public abstract class Server {
|
||||||
private RpcSaslProto buildNegotiateResponse(List<AuthMethod> authMethods)
|
private RpcSaslProto buildNegotiateResponse(List<AuthMethod> authMethods)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
RpcSaslProto.Builder negotiateBuilder = RpcSaslProto.newBuilder();
|
RpcSaslProto.Builder negotiateBuilder = RpcSaslProto.newBuilder();
|
||||||
negotiateBuilder.setState(SaslState.NEGOTIATE);
|
if (authMethods.contains(AuthMethod.SIMPLE) && authMethods.size() == 1) {
|
||||||
for (AuthMethod authMethod : authMethods) {
|
// SIMPLE-only servers return success in response to negotiate
|
||||||
if (authMethod == AuthMethod.SIMPLE) { // not a SASL method
|
negotiateBuilder.setState(SaslState.SUCCESS);
|
||||||
continue;
|
} else {
|
||||||
|
negotiateBuilder.setState(SaslState.NEGOTIATE);
|
||||||
|
for (AuthMethod authMethod : authMethods) {
|
||||||
|
SaslRpcServer saslRpcServer = new SaslRpcServer(authMethod);
|
||||||
|
SaslAuth.Builder builder = negotiateBuilder.addAuthsBuilder()
|
||||||
|
.setMethod(authMethod.toString())
|
||||||
|
.setMechanism(saslRpcServer.mechanism);
|
||||||
|
if (saslRpcServer.protocol != null) {
|
||||||
|
builder.setProtocol(saslRpcServer.protocol);
|
||||||
|
}
|
||||||
|
if (saslRpcServer.serverId != null) {
|
||||||
|
builder.setServerId(saslRpcServer.serverId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SaslRpcServer saslRpcServer = new SaslRpcServer(authMethod);
|
|
||||||
negotiateBuilder.addAuthsBuilder()
|
|
||||||
.setMethod(authMethod.toString())
|
|
||||||
.setMechanism(saslRpcServer.mechanism)
|
|
||||||
.setProtocol(saslRpcServer.protocol)
|
|
||||||
.setServerId(saslRpcServer.serverId);
|
|
||||||
}
|
}
|
||||||
return negotiateBuilder.build();
|
return negotiateBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,9 @@ import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.security.auth.callback.Callback;
|
import javax.security.auth.callback.Callback;
|
||||||
|
@ -32,6 +35,7 @@ import javax.security.auth.callback.CallbackHandler;
|
||||||
import javax.security.auth.callback.NameCallback;
|
import javax.security.auth.callback.NameCallback;
|
||||||
import javax.security.auth.callback.PasswordCallback;
|
import javax.security.auth.callback.PasswordCallback;
|
||||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||||
|
import javax.security.auth.kerberos.KerberosPrincipal;
|
||||||
import javax.security.sasl.RealmCallback;
|
import javax.security.sasl.RealmCallback;
|
||||||
import javax.security.sasl.RealmChoiceCallback;
|
import javax.security.sasl.RealmChoiceCallback;
|
||||||
import javax.security.sasl.Sasl;
|
import javax.security.sasl.Sasl;
|
||||||
|
@ -42,6 +46,7 @@ import org.apache.commons.logging.Log;
|
||||||
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.classification.InterfaceStability;
|
import org.apache.hadoop.classification.InterfaceStability;
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.ipc.ProtobufRpcEngine.RpcRequestMessageWrapper;
|
import org.apache.hadoop.ipc.ProtobufRpcEngine.RpcRequestMessageWrapper;
|
||||||
import org.apache.hadoop.ipc.ProtobufRpcEngine.RpcResponseMessageWrapper;
|
import org.apache.hadoop.ipc.ProtobufRpcEngine.RpcResponseMessageWrapper;
|
||||||
import org.apache.hadoop.ipc.RPC.RpcKind;
|
import org.apache.hadoop.ipc.RPC.RpcKind;
|
||||||
|
@ -58,6 +63,8 @@ import org.apache.hadoop.security.SaslRpcServer.AuthMethod;
|
||||||
import org.apache.hadoop.security.authentication.util.KerberosName;
|
import org.apache.hadoop.security.authentication.util.KerberosName;
|
||||||
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.apache.hadoop.security.token.TokenInfo;
|
||||||
|
import org.apache.hadoop.security.token.TokenSelector;
|
||||||
import org.apache.hadoop.util.ProtoUtil;
|
import org.apache.hadoop.util.ProtoUtil;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
|
@ -69,9 +76,13 @@ import com.google.protobuf.ByteString;
|
||||||
public class SaslRpcClient {
|
public class SaslRpcClient {
|
||||||
public static final Log LOG = LogFactory.getLog(SaslRpcClient.class);
|
public static final Log LOG = LogFactory.getLog(SaslRpcClient.class);
|
||||||
|
|
||||||
private final AuthMethod authMethod;
|
private final UserGroupInformation ugi;
|
||||||
private final SaslClient saslClient;
|
private final Class<?> protocol;
|
||||||
private final boolean fallbackAllowed;
|
private final InetSocketAddress serverAddr;
|
||||||
|
private final Configuration conf;
|
||||||
|
|
||||||
|
private SaslClient saslClient;
|
||||||
|
|
||||||
private static final RpcRequestHeaderProto saslHeader = ProtoUtil
|
private static final RpcRequestHeaderProto saslHeader = ProtoUtil
|
||||||
.makeRpcRequestHeader(RpcKind.RPC_PROTOCOL_BUFFER,
|
.makeRpcRequestHeader(RpcKind.RPC_PROTOCOL_BUFFER,
|
||||||
OperationProto.RPC_FINAL_PACKET, AuthProtocol.SASL.callId,
|
OperationProto.RPC_FINAL_PACKET, AuthProtocol.SASL.callId,
|
||||||
|
@ -80,44 +91,121 @@ public class SaslRpcClient {
|
||||||
RpcSaslProto.newBuilder().setState(SaslState.NEGOTIATE).build();
|
RpcSaslProto.newBuilder().setState(SaslState.NEGOTIATE).build();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a SaslRpcClient for an authentication method
|
* Create a SaslRpcClient that can be used by a RPC client to negotiate
|
||||||
*
|
* SASL authentication with a RPC server
|
||||||
* @param method
|
* @param ugi - connecting user
|
||||||
* the requested authentication method
|
* @param protocol - RPC protocol
|
||||||
* @param token
|
* @param serverAddr - InetSocketAddress of remote server
|
||||||
* token to use if needed by the authentication method
|
* @param conf - Configuration
|
||||||
*/
|
*/
|
||||||
public SaslRpcClient(AuthMethod method,
|
public SaslRpcClient(UserGroupInformation ugi, Class<?> protocol,
|
||||||
Token<? extends TokenIdentifier> token, String serverPrincipal,
|
InetSocketAddress serverAddr, Configuration conf) {
|
||||||
boolean fallbackAllowed)
|
this.ugi = ugi;
|
||||||
throws IOException {
|
this.protocol = protocol;
|
||||||
this.authMethod = method;
|
this.serverAddr = serverAddr;
|
||||||
this.fallbackAllowed = fallbackAllowed;
|
this.conf = conf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate a sasl client for the first supported auth type in the
|
||||||
|
* given list. The auth type must be defined, enabled, and the user
|
||||||
|
* must possess the required credentials, else the next auth is tried.
|
||||||
|
*
|
||||||
|
* @param authTypes to attempt in the given order
|
||||||
|
* @return SaslAuth of instantiated client
|
||||||
|
* @throws AccessControlException - client doesn't support any of the auths
|
||||||
|
* @throws IOException - misc errors
|
||||||
|
*/
|
||||||
|
private SaslAuth selectSaslClient(List<SaslAuth> authTypes)
|
||||||
|
throws SaslException, AccessControlException, IOException {
|
||||||
|
SaslAuth selectedAuthType = null;
|
||||||
|
boolean switchToSimple = false;
|
||||||
|
for (SaslAuth authType : authTypes) {
|
||||||
|
if (!isValidAuthType(authType)) {
|
||||||
|
continue; // don't know what it is, try next
|
||||||
|
}
|
||||||
|
AuthMethod authMethod = AuthMethod.valueOf(authType.getMethod());
|
||||||
|
if (authMethod == AuthMethod.SIMPLE) {
|
||||||
|
switchToSimple = true;
|
||||||
|
} else {
|
||||||
|
saslClient = createSaslClient(authType);
|
||||||
|
if (saslClient == null) { // client lacks credentials, try next
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectedAuthType = authType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (saslClient == null && !switchToSimple) {
|
||||||
|
List<String> serverAuthMethods = new ArrayList<String>();
|
||||||
|
for (SaslAuth authType : authTypes) {
|
||||||
|
serverAuthMethods.add(authType.getMethod());
|
||||||
|
}
|
||||||
|
throw new AccessControlException(
|
||||||
|
"Client cannot authenticate via:" + serverAuthMethods);
|
||||||
|
}
|
||||||
|
if (LOG.isDebugEnabled()) {
|
||||||
|
LOG.debug("Use " + selectedAuthType.getMethod() +
|
||||||
|
" authentication for protocol " + protocol.getSimpleName());
|
||||||
|
}
|
||||||
|
return selectedAuthType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean isValidAuthType(SaslAuth authType) {
|
||||||
|
AuthMethod authMethod;
|
||||||
|
try {
|
||||||
|
authMethod = AuthMethod.valueOf(authType.getMethod());
|
||||||
|
} catch (IllegalArgumentException iae) { // unknown auth
|
||||||
|
authMethod = null;
|
||||||
|
}
|
||||||
|
// do we know what it is? is it using our mechanism?
|
||||||
|
return authMethod != null &&
|
||||||
|
authMethod.getMechanismName().equals(authType.getMechanism());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to create a SaslClient for an authentication type. May return
|
||||||
|
* null if the type isn't supported or the client lacks the required
|
||||||
|
* credentials.
|
||||||
|
*
|
||||||
|
* @param authType - the requested authentication method
|
||||||
|
* @return SaslClient for the authType or null
|
||||||
|
* @throws SaslException - error instantiating client
|
||||||
|
* @throws IOException - misc errors
|
||||||
|
*/
|
||||||
|
private SaslClient createSaslClient(SaslAuth authType)
|
||||||
|
throws SaslException, IOException {
|
||||||
String saslUser = null;
|
String saslUser = null;
|
||||||
String saslProtocol = null;
|
// SASL requires the client and server to use the same proto and serverId
|
||||||
String saslServerName = null;
|
// if necessary, auth types below will verify they are valid
|
||||||
|
final String saslProtocol = authType.getProtocol();
|
||||||
|
final String saslServerName = authType.getServerId();
|
||||||
Map<String, String> saslProperties = SaslRpcServer.SASL_PROPS;
|
Map<String, String> saslProperties = SaslRpcServer.SASL_PROPS;
|
||||||
CallbackHandler saslCallback = null;
|
CallbackHandler saslCallback = null;
|
||||||
|
|
||||||
|
final AuthMethod method = AuthMethod.valueOf(authType.getMethod());
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case TOKEN: {
|
case TOKEN: {
|
||||||
saslProtocol = "";
|
Token<?> token = getServerToken(authType);
|
||||||
saslServerName = SaslRpcServer.SASL_DEFAULT_REALM;
|
if (token == null) {
|
||||||
|
return null; // tokens aren't supported or user doesn't have one
|
||||||
|
}
|
||||||
saslCallback = new SaslClientCallbackHandler(token);
|
saslCallback = new SaslClientCallbackHandler(token);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case KERBEROS: {
|
case KERBEROS: {
|
||||||
if (serverPrincipal == null || serverPrincipal.isEmpty()) {
|
if (ugi.getRealAuthenticationMethod().getAuthMethod() !=
|
||||||
throw new IOException(
|
AuthMethod.KERBEROS) {
|
||||||
"Failed to specify server's Kerberos principal name");
|
return null; // client isn't using kerberos
|
||||||
}
|
}
|
||||||
KerberosName name = new KerberosName(serverPrincipal);
|
String serverPrincipal = getServerPrincipal(authType);
|
||||||
saslProtocol = name.getServiceName();
|
if (serverPrincipal == null) {
|
||||||
saslServerName = name.getHostName();
|
return null; // protocol doesn't use kerberos
|
||||||
if (saslServerName == null) {
|
}
|
||||||
throw new IOException(
|
if (LOG.isDebugEnabled()) {
|
||||||
"Kerberos principal name does NOT have the expected hostname part: "
|
LOG.debug("RPC Server's Kerberos principal name for protocol="
|
||||||
+ serverPrincipal);
|
+ protocol.getCanonicalName() + " is " + serverPrincipal);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -127,17 +215,86 @@ public class SaslRpcClient {
|
||||||
|
|
||||||
String mechanism = method.getMechanismName();
|
String mechanism = method.getMechanismName();
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("Creating SASL " + mechanism + "(" + authMethod + ") "
|
LOG.debug("Creating SASL " + mechanism + "(" + method + ") "
|
||||||
+ " client to authenticate to service at " + saslServerName);
|
+ " client to authenticate to service at " + saslServerName);
|
||||||
}
|
}
|
||||||
saslClient = Sasl.createSaslClient(
|
return Sasl.createSaslClient(
|
||||||
new String[] { mechanism }, saslUser, saslProtocol, saslServerName,
|
new String[] { mechanism }, saslUser, saslProtocol, saslServerName,
|
||||||
saslProperties, saslCallback);
|
saslProperties, saslCallback);
|
||||||
if (saslClient == null) {
|
|
||||||
throw new IOException("Unable to find SASL client implementation");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to locate the required token for the server.
|
||||||
|
*
|
||||||
|
* @param authType of the SASL client
|
||||||
|
* @return Token<?> for server, or null if no token available
|
||||||
|
* @throws IOException - token selector cannot be instantiated
|
||||||
|
*/
|
||||||
|
private Token<?> getServerToken(SaslAuth authType) throws IOException {
|
||||||
|
TokenInfo tokenInfo = SecurityUtil.getTokenInfo(protocol, conf);
|
||||||
|
LOG.debug("Get token info proto:"+protocol+" info:"+tokenInfo);
|
||||||
|
if (tokenInfo == null) { // protocol has no support for tokens
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
TokenSelector<?> tokenSelector = null;
|
||||||
|
try {
|
||||||
|
tokenSelector = tokenInfo.value().newInstance();
|
||||||
|
} catch (InstantiationException e) {
|
||||||
|
throw new IOException(e.toString());
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new IOException(e.toString());
|
||||||
|
}
|
||||||
|
return tokenSelector.selectToken(
|
||||||
|
SecurityUtil.buildTokenService(serverAddr), ugi.getTokens());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the remote server's principal. The value will be obtained from
|
||||||
|
* the config and cross-checked against the server's advertised principal.
|
||||||
|
*
|
||||||
|
* @param authType of the SASL client
|
||||||
|
* @return String of the server's principal
|
||||||
|
* @throws IOException - error determining configured principal
|
||||||
|
*/
|
||||||
|
|
||||||
|
// try to get the configured principal for the remote server
|
||||||
|
private String getServerPrincipal(SaslAuth authType) throws IOException {
|
||||||
|
KerberosInfo krbInfo = SecurityUtil.getKerberosInfo(protocol, conf);
|
||||||
|
LOG.debug("Get kerberos info proto:"+protocol+" info:"+krbInfo);
|
||||||
|
if (krbInfo == null) { // protocol has no support for kerberos
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String serverKey = krbInfo.serverPrincipal();
|
||||||
|
if (serverKey == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Can't obtain server Kerberos config key from protocol="
|
||||||
|
+ protocol.getCanonicalName());
|
||||||
|
}
|
||||||
|
// construct the expected principal from the config
|
||||||
|
String confPrincipal = SecurityUtil.getServerPrincipal(
|
||||||
|
conf.get(serverKey), serverAddr.getAddress());
|
||||||
|
if (confPrincipal == null || confPrincipal.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Failed to specify server's Kerberos principal name");
|
||||||
|
}
|
||||||
|
// ensure it looks like a host-based service principal
|
||||||
|
KerberosName name = new KerberosName(confPrincipal);
|
||||||
|
if (name.getHostName() == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Kerberos principal name does NOT have the expected hostname part: "
|
||||||
|
+ confPrincipal);
|
||||||
|
}
|
||||||
|
// check that the server advertised principal matches our conf
|
||||||
|
KerberosPrincipal serverPrincipal = new KerberosPrincipal(
|
||||||
|
authType.getProtocol() + "/" + authType.getServerId());
|
||||||
|
if (!serverPrincipal.getName().equals(confPrincipal)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Server has invalid Kerberos principal: " + serverPrincipal);
|
||||||
|
}
|
||||||
|
return confPrincipal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do client side SASL authentication with server via the given InputStream
|
* Do client side SASL authentication with server via the given InputStream
|
||||||
* and OutputStream
|
* and OutputStream
|
||||||
|
@ -146,18 +303,18 @@ public class SaslRpcClient {
|
||||||
* InputStream to use
|
* InputStream to use
|
||||||
* @param outS
|
* @param outS
|
||||||
* OutputStream to use
|
* OutputStream to use
|
||||||
* @return true if connection is set up, or false if needs to switch
|
* @return AuthMethod used to negotiate the connection
|
||||||
* to simple Auth.
|
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public boolean saslConnect(InputStream inS, OutputStream outS)
|
public AuthMethod saslConnect(InputStream inS, OutputStream outS)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
DataInputStream inStream = new DataInputStream(new BufferedInputStream(inS));
|
DataInputStream inStream = new DataInputStream(new BufferedInputStream(inS));
|
||||||
DataOutputStream outStream = new DataOutputStream(new BufferedOutputStream(
|
DataOutputStream outStream = new DataOutputStream(new BufferedOutputStream(
|
||||||
outS));
|
outS));
|
||||||
|
|
||||||
// track if SASL ever started, or server switched us to simple
|
// redefined if/when a SASL negotiation completes
|
||||||
boolean inSasl = false;
|
AuthMethod authMethod = AuthMethod.SIMPLE;
|
||||||
|
|
||||||
sendSaslMessage(outStream, negotiateRequest);
|
sendSaslMessage(outStream, negotiateRequest);
|
||||||
|
|
||||||
// loop until sasl is complete or a rpc error occurs
|
// loop until sasl is complete or a rpc error occurs
|
||||||
|
@ -191,50 +348,48 @@ public class SaslRpcClient {
|
||||||
RpcSaslProto.Builder response = null;
|
RpcSaslProto.Builder response = null;
|
||||||
switch (saslMessage.getState()) {
|
switch (saslMessage.getState()) {
|
||||||
case NEGOTIATE: {
|
case NEGOTIATE: {
|
||||||
inSasl = true;
|
// create a compatible SASL client, throws if no supported auths
|
||||||
// TODO: should instantiate sasl client based on advertisement
|
SaslAuth saslAuthType = selectSaslClient(saslMessage.getAuthsList());
|
||||||
// but just blindly use the pre-instantiated sasl client for now
|
authMethod = AuthMethod.valueOf(saslAuthType.getMethod());
|
||||||
String clientAuthMethod = authMethod.toString();
|
|
||||||
SaslAuth saslAuthType = null;
|
byte[] responseToken = null;
|
||||||
for (SaslAuth authType : saslMessage.getAuthsList()) {
|
if (authMethod == AuthMethod.SIMPLE) { // switching to SIMPLE
|
||||||
if (clientAuthMethod.equals(authType.getMethod())) {
|
done = true; // not going to wait for success ack
|
||||||
saslAuthType = authType;
|
} else {
|
||||||
break;
|
byte[] challengeToken = null;
|
||||||
|
if (saslAuthType.hasChallenge()) {
|
||||||
|
// server provided the first challenge
|
||||||
|
challengeToken = saslAuthType.getChallenge().toByteArray();
|
||||||
|
saslAuthType =
|
||||||
|
SaslAuth.newBuilder(saslAuthType).clearChallenge().build();
|
||||||
|
} else if (saslClient.hasInitialResponse()) {
|
||||||
|
challengeToken = new byte[0];
|
||||||
}
|
}
|
||||||
|
responseToken = (challengeToken != null)
|
||||||
|
? saslClient.evaluateChallenge(challengeToken)
|
||||||
|
: new byte[0];
|
||||||
}
|
}
|
||||||
if (saslAuthType == null) {
|
|
||||||
saslAuthType = SaslAuth.newBuilder()
|
|
||||||
.setMethod(clientAuthMethod)
|
|
||||||
.setMechanism(saslClient.getMechanismName())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] challengeToken = null;
|
|
||||||
if (saslAuthType != null && saslAuthType.hasChallenge()) {
|
|
||||||
// server provided the first challenge
|
|
||||||
challengeToken = saslAuthType.getChallenge().toByteArray();
|
|
||||||
saslAuthType =
|
|
||||||
SaslAuth.newBuilder(saslAuthType).clearChallenge().build();
|
|
||||||
} else if (saslClient.hasInitialResponse()) {
|
|
||||||
challengeToken = new byte[0];
|
|
||||||
}
|
|
||||||
byte[] responseToken = (challengeToken != null)
|
|
||||||
? saslClient.evaluateChallenge(challengeToken)
|
|
||||||
: new byte[0];
|
|
||||||
|
|
||||||
response = createSaslReply(SaslState.INITIATE, responseToken);
|
response = createSaslReply(SaslState.INITIATE, responseToken);
|
||||||
response.addAuths(saslAuthType);
|
response.addAuths(saslAuthType);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CHALLENGE: {
|
case CHALLENGE: {
|
||||||
inSasl = true;
|
if (saslClient == null) {
|
||||||
|
// should probably instantiate a client to allow a server to
|
||||||
|
// demand a specific negotiation
|
||||||
|
throw new SaslException("Server sent unsolicited challenge");
|
||||||
|
}
|
||||||
byte[] responseToken = saslEvaluateToken(saslMessage, false);
|
byte[] responseToken = saslEvaluateToken(saslMessage, false);
|
||||||
response = createSaslReply(SaslState.RESPONSE, responseToken);
|
response = createSaslReply(SaslState.RESPONSE, responseToken);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SUCCESS: {
|
case SUCCESS: {
|
||||||
if (inSasl && saslEvaluateToken(saslMessage, true) != null) {
|
// simple server sends immediate success to a SASL client for
|
||||||
throw new SaslException("SASL client generated spurious token");
|
// switch to simple
|
||||||
|
if (saslClient == null) {
|
||||||
|
authMethod = AuthMethod.SIMPLE;
|
||||||
|
} else {
|
||||||
|
saslEvaluateToken(saslMessage, true);
|
||||||
}
|
}
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
|
@ -248,12 +403,7 @@ public class SaslRpcClient {
|
||||||
sendSaslMessage(outStream, response.build());
|
sendSaslMessage(outStream, response.build());
|
||||||
}
|
}
|
||||||
} while (!done);
|
} while (!done);
|
||||||
if (!inSasl && !fallbackAllowed) {
|
return authMethod;
|
||||||
throw new IOException("Server asks us to fall back to SIMPLE " +
|
|
||||||
"auth, but this client is configured to only allow secure " +
|
|
||||||
"connections.");
|
|
||||||
}
|
|
||||||
return inSasl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendSaslMessage(DataOutputStream out, RpcSaslProto message)
|
private void sendSaslMessage(DataOutputStream out, RpcSaslProto message)
|
||||||
|
@ -268,17 +418,37 @@ public class SaslRpcClient {
|
||||||
out.flush();
|
out.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluate the server provided challenge. The server must send a token
|
||||||
|
* if it's not done. If the server is done, the challenge token is
|
||||||
|
* optional because not all mechanisms send a final token for the client to
|
||||||
|
* update its internal state. The client must also be done after
|
||||||
|
* evaluating the optional token to ensure a malicious server doesn't
|
||||||
|
* prematurely end the negotiation with a phony success.
|
||||||
|
*
|
||||||
|
* @param saslResponse - client response to challenge
|
||||||
|
* @param serverIsDone - server negotiation state
|
||||||
|
* @throws SaslException - any problems with negotiation
|
||||||
|
*/
|
||||||
private byte[] saslEvaluateToken(RpcSaslProto saslResponse,
|
private byte[] saslEvaluateToken(RpcSaslProto saslResponse,
|
||||||
boolean done) throws SaslException {
|
boolean serverIsDone) throws SaslException {
|
||||||
byte[] saslToken = null;
|
byte[] saslToken = null;
|
||||||
if (saslResponse.hasToken()) {
|
if (saslResponse.hasToken()) {
|
||||||
saslToken = saslResponse.getToken().toByteArray();
|
saslToken = saslResponse.getToken().toByteArray();
|
||||||
saslToken = saslClient.evaluateChallenge(saslToken);
|
saslToken = saslClient.evaluateChallenge(saslToken);
|
||||||
} else if (!done) {
|
} else if (!serverIsDone) {
|
||||||
throw new SaslException("Challenge contains no token");
|
// the server may only omit a token when it's done
|
||||||
|
throw new SaslException("Server challenge contains no token");
|
||||||
}
|
}
|
||||||
if (done && !saslClient.isComplete()) {
|
if (serverIsDone) {
|
||||||
throw new SaslException("Client is out of sync with server");
|
// server tried to report success before our client completed
|
||||||
|
if (!saslClient.isComplete()) {
|
||||||
|
throw new SaslException("Client is out of sync with server");
|
||||||
|
}
|
||||||
|
// a client cannot generate a response to a success message
|
||||||
|
if (saslToken != null) {
|
||||||
|
throw new SaslException("Client generated spurious response");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return saslToken;
|
return saslToken;
|
||||||
}
|
}
|
||||||
|
@ -327,7 +497,10 @@ public class SaslRpcClient {
|
||||||
|
|
||||||
/** Release resources used by wrapped saslClient */
|
/** Release resources used by wrapped saslClient */
|
||||||
public void dispose() throws SaslException {
|
public void dispose() throws SaslException {
|
||||||
saslClient.dispose();
|
if (saslClient != null) {
|
||||||
|
saslClient.dispose();
|
||||||
|
saslClient = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SaslClientCallbackHandler implements CallbackHandler {
|
private static class SaslClientCallbackHandler implements CallbackHandler {
|
||||||
|
|
|
@ -18,14 +18,9 @@
|
||||||
|
|
||||||
package org.apache.hadoop.ipc;
|
package org.apache.hadoop.ipc;
|
||||||
|
|
||||||
import static org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod.KERBEROS;
|
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION;
|
||||||
import static org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod.SIMPLE;
|
import static org.apache.hadoop.security.SaslRpcServer.AuthMethod.*;
|
||||||
import static org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod.TOKEN;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
import java.io.DataOutput;
|
import java.io.DataOutput;
|
||||||
|
@ -51,6 +46,7 @@ import javax.security.sasl.SaslServer;
|
||||||
|
|
||||||
import junit.framework.Assert;
|
import junit.framework.Assert;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.apache.commons.logging.impl.Log4JLogger;
|
import org.apache.commons.logging.impl.Log4JLogger;
|
||||||
|
@ -103,6 +99,13 @@ public class TestSaslRPC {
|
||||||
static Boolean forceSecretManager = null;
|
static Boolean forceSecretManager = null;
|
||||||
static Boolean clientFallBackToSimpleAllowed = true;
|
static Boolean clientFallBackToSimpleAllowed = true;
|
||||||
|
|
||||||
|
static enum UseToken {
|
||||||
|
NONE(),
|
||||||
|
VALID(),
|
||||||
|
INVALID(),
|
||||||
|
OTHER();
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void setupKerb() {
|
public static void setupKerb() {
|
||||||
System.setProperty("java.security.krb5.kdc", "");
|
System.setProperty("java.security.krb5.kdc", "");
|
||||||
|
@ -113,9 +116,11 @@ public class TestSaslRPC {
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
conf = new Configuration();
|
conf = new Configuration();
|
||||||
SecurityUtil.setAuthenticationMethod(KERBEROS, conf);
|
conf.set(HADOOP_SECURITY_AUTHENTICATION, KERBEROS.toString());
|
||||||
UserGroupInformation.setConfiguration(conf);
|
UserGroupInformation.setConfiguration(conf);
|
||||||
enableSecretManager = null;
|
enableSecretManager = null;
|
||||||
|
forceSecretManager = null;
|
||||||
|
clientFallBackToSimpleAllowed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
@ -367,28 +372,6 @@ public class TestSaslRPC {
|
||||||
assertEquals(0, remoteId.getPingInterval());
|
assertEquals(0, remoteId.getPingInterval());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetRemotePrincipal() throws Exception {
|
|
||||||
try {
|
|
||||||
Configuration newConf = new Configuration(conf);
|
|
||||||
newConf.set(SERVER_PRINCIPAL_KEY, SERVER_PRINCIPAL_1);
|
|
||||||
ConnectionId remoteId = ConnectionId.getConnectionId(
|
|
||||||
new InetSocketAddress(0), TestSaslProtocol.class, null, 0, newConf);
|
|
||||||
assertEquals(SERVER_PRINCIPAL_1, remoteId.getServerPrincipal());
|
|
||||||
// this following test needs security to be off
|
|
||||||
SecurityUtil.setAuthenticationMethod(SIMPLE, newConf);
|
|
||||||
UserGroupInformation.setConfiguration(newConf);
|
|
||||||
remoteId = ConnectionId.getConnectionId(new InetSocketAddress(0),
|
|
||||||
TestSaslProtocol.class, null, 0, newConf);
|
|
||||||
assertEquals(
|
|
||||||
"serverPrincipal should be null when security is turned off", null,
|
|
||||||
remoteId.getServerPrincipal());
|
|
||||||
} finally {
|
|
||||||
// revert back to security is on
|
|
||||||
UserGroupInformation.setConfiguration(conf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPerConnectionConf() throws Exception {
|
public void testPerConnectionConf() throws Exception {
|
||||||
TestTokenSecretManager sm = new TestTokenSecretManager();
|
TestTokenSecretManager sm = new TestTokenSecretManager();
|
||||||
|
@ -409,12 +392,13 @@ public class TestSaslRPC {
|
||||||
Configuration newConf = new Configuration(conf);
|
Configuration newConf = new Configuration(conf);
|
||||||
newConf.set(CommonConfigurationKeysPublic.
|
newConf.set(CommonConfigurationKeysPublic.
|
||||||
HADOOP_RPC_SOCKET_FACTORY_CLASS_DEFAULT_KEY, "");
|
HADOOP_RPC_SOCKET_FACTORY_CLASS_DEFAULT_KEY, "");
|
||||||
newConf.set(SERVER_PRINCIPAL_KEY, SERVER_PRINCIPAL_1);
|
|
||||||
|
|
||||||
TestSaslProtocol proxy1 = null;
|
TestSaslProtocol proxy1 = null;
|
||||||
TestSaslProtocol proxy2 = null;
|
TestSaslProtocol proxy2 = null;
|
||||||
TestSaslProtocol proxy3 = null;
|
TestSaslProtocol proxy3 = null;
|
||||||
|
int timeouts[] = {111222, 3333333};
|
||||||
try {
|
try {
|
||||||
|
newConf.setInt(CommonConfigurationKeysPublic.IPC_CLIENT_CONNECTION_MAXIDLETIME_KEY, timeouts[0]);
|
||||||
proxy1 = RPC.getProxy(TestSaslProtocol.class,
|
proxy1 = RPC.getProxy(TestSaslProtocol.class,
|
||||||
TestSaslProtocol.versionID, addr, newConf);
|
TestSaslProtocol.versionID, addr, newConf);
|
||||||
proxy1.getAuthMethod();
|
proxy1.getAuthMethod();
|
||||||
|
@ -427,20 +411,21 @@ public class TestSaslRPC {
|
||||||
proxy2.getAuthMethod();
|
proxy2.getAuthMethod();
|
||||||
assertEquals("number of connections in cache is wrong", 1, conns.size());
|
assertEquals("number of connections in cache is wrong", 1, conns.size());
|
||||||
// different conf, new connection should be set up
|
// different conf, new connection should be set up
|
||||||
newConf.set(SERVER_PRINCIPAL_KEY, SERVER_PRINCIPAL_2);
|
newConf.setInt(CommonConfigurationKeysPublic.IPC_CLIENT_CONNECTION_MAXIDLETIME_KEY, timeouts[1]);
|
||||||
proxy3 = RPC.getProxy(TestSaslProtocol.class,
|
proxy3 = RPC.getProxy(TestSaslProtocol.class,
|
||||||
TestSaslProtocol.versionID, addr, newConf);
|
TestSaslProtocol.versionID, addr, newConf);
|
||||||
proxy3.getAuthMethod();
|
proxy3.getAuthMethod();
|
||||||
ConnectionId[] connsArray = conns.toArray(new ConnectionId[0]);
|
assertEquals("number of connections in cache is wrong", 2, conns.size());
|
||||||
assertEquals("number of connections in cache is wrong", 2,
|
// now verify the proxies have the correct connection ids and timeouts
|
||||||
connsArray.length);
|
ConnectionId[] connsArray = {
|
||||||
String p1 = connsArray[0].getServerPrincipal();
|
RPC.getConnectionIdForProxy(proxy1),
|
||||||
String p2 = connsArray[1].getServerPrincipal();
|
RPC.getConnectionIdForProxy(proxy2),
|
||||||
assertFalse("should have different principals", p1.equals(p2));
|
RPC.getConnectionIdForProxy(proxy3)
|
||||||
assertTrue("principal not as expected", p1.equals(SERVER_PRINCIPAL_1)
|
};
|
||||||
|| p1.equals(SERVER_PRINCIPAL_2));
|
assertEquals(connsArray[0], connsArray[1]);
|
||||||
assertTrue("principal not as expected", p2.equals(SERVER_PRINCIPAL_1)
|
assertEquals(connsArray[0].getMaxIdleTime(), timeouts[0]);
|
||||||
|| p2.equals(SERVER_PRINCIPAL_2));
|
assertFalse(connsArray[0].equals(connsArray[2]));
|
||||||
|
assertNotSame(connsArray[2].getMaxIdleTime(), timeouts[1]);
|
||||||
} finally {
|
} finally {
|
||||||
server.stop();
|
server.stop();
|
||||||
RPC.stopProxy(proxy1);
|
RPC.stopProxy(proxy1);
|
||||||
|
@ -599,13 +584,22 @@ public class TestSaslRPC {
|
||||||
private static Pattern KrbFailed =
|
private static Pattern KrbFailed =
|
||||||
Pattern.compile(".*Failed on local exception:.* " +
|
Pattern.compile(".*Failed on local exception:.* " +
|
||||||
"Failed to specify server's Kerberos principal name.*");
|
"Failed to specify server's Kerberos principal name.*");
|
||||||
private static Pattern Denied(AuthenticationMethod method) {
|
private static Pattern Denied(AuthMethod method) {
|
||||||
return Pattern.compile(".*RemoteException.*AccessControlException.*: "
|
return Pattern.compile(".*RemoteException.*AccessControlException.*: "
|
||||||
+method.getAuthMethod() + " authentication is not enabled.*");
|
+ method + " authentication is not enabled.*");
|
||||||
|
}
|
||||||
|
private static Pattern No(AuthMethod ... method) {
|
||||||
|
String methods = StringUtils.join(method, ",\\s*");
|
||||||
|
return Pattern.compile(".*Failed on local exception:.* " +
|
||||||
|
"Client cannot authenticate via:\\[" + methods + "\\].*");
|
||||||
}
|
}
|
||||||
private static Pattern NoTokenAuth =
|
private static Pattern NoTokenAuth =
|
||||||
Pattern.compile(".*IllegalArgumentException: " +
|
Pattern.compile(".*IllegalArgumentException: " +
|
||||||
"TOKEN authentication requires a secret manager");
|
"TOKEN authentication requires a secret manager");
|
||||||
|
private static Pattern NoFallback =
|
||||||
|
Pattern.compile(".*Failed on local exception:.* " +
|
||||||
|
"Server asks us to fall back to SIMPLE auth, " +
|
||||||
|
"but this client is configured to only allow secure connections.*");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* simple server
|
* simple server
|
||||||
|
@ -613,61 +607,95 @@ public class TestSaslRPC {
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleServer() throws Exception {
|
public void testSimpleServer() throws Exception {
|
||||||
assertAuthEquals(SIMPLE, getAuthMethod(SIMPLE, SIMPLE));
|
assertAuthEquals(SIMPLE, getAuthMethod(SIMPLE, SIMPLE));
|
||||||
// SASL methods are reverted to SIMPLE, but test setup fails
|
assertAuthEquals(SIMPLE, getAuthMethod(SIMPLE, SIMPLE, UseToken.OTHER));
|
||||||
assertAuthEquals(KrbFailed, getAuthMethod(KERBEROS, SIMPLE));
|
// SASL methods are normally reverted to SIMPLE
|
||||||
|
assertAuthEquals(SIMPLE, getAuthMethod(KERBEROS, SIMPLE));
|
||||||
|
assertAuthEquals(SIMPLE, getAuthMethod(KERBEROS, SIMPLE, UseToken.OTHER));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleServerWithTokensWithNoClientFallbackToSimple()
|
public void testNoClientFallbackToSimple()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
clientFallBackToSimpleAllowed = false;
|
clientFallBackToSimpleAllowed = false;
|
||||||
|
// tokens are irrelevant w/o secret manager enabled
|
||||||
|
assertAuthEquals(SIMPLE, getAuthMethod(SIMPLE, SIMPLE));
|
||||||
|
assertAuthEquals(SIMPLE, getAuthMethod(SIMPLE, SIMPLE, UseToken.OTHER));
|
||||||
|
assertAuthEquals(SIMPLE, getAuthMethod(SIMPLE, SIMPLE, UseToken.VALID));
|
||||||
|
assertAuthEquals(SIMPLE, getAuthMethod(SIMPLE, SIMPLE, UseToken.INVALID));
|
||||||
|
|
||||||
try{
|
// A secure client must not fallback
|
||||||
// Client has a token even though its configs says simple auth. Server
|
assertAuthEquals(NoFallback, getAuthMethod(KERBEROS, SIMPLE));
|
||||||
// is configured for simple auth, but as client sends the token, and
|
assertAuthEquals(NoFallback, getAuthMethod(KERBEROS, SIMPLE, UseToken.OTHER));
|
||||||
// server asks to switch to simple, this should fail.
|
assertAuthEquals(NoFallback, getAuthMethod(KERBEROS, SIMPLE, UseToken.VALID));
|
||||||
getAuthMethod(SIMPLE, SIMPLE, true);
|
assertAuthEquals(NoFallback, getAuthMethod(KERBEROS, SIMPLE, UseToken.INVALID));
|
||||||
} catch (IOException ioe) {
|
|
||||||
Assert
|
|
||||||
.assertTrue(ioe.getMessage().contains("Failed on local exception: " +
|
|
||||||
"java.io.IOException: java.io.IOException: " +
|
|
||||||
"Server asks us to fall back to SIMPLE auth, " +
|
|
||||||
"but this client is configured to only allow secure connections"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now set server to simple and also force the secret-manager. Now server
|
// Now set server to simple and also force the secret-manager. Now server
|
||||||
// should have both simple and token enabled.
|
// should have both simple and token enabled.
|
||||||
forceSecretManager = true;
|
forceSecretManager = true;
|
||||||
assertAuthEquals(TOKEN, getAuthMethod(SIMPLE, SIMPLE, true));
|
assertAuthEquals(SIMPLE, getAuthMethod(SIMPLE, SIMPLE));
|
||||||
forceSecretManager = false;
|
assertAuthEquals(SIMPLE, getAuthMethod(SIMPLE, SIMPLE, UseToken.OTHER));
|
||||||
clientFallBackToSimpleAllowed = true;
|
assertAuthEquals(TOKEN, getAuthMethod(SIMPLE, SIMPLE, UseToken.VALID));
|
||||||
|
assertAuthEquals(BadToken, getAuthMethod(SIMPLE, SIMPLE, UseToken.INVALID));
|
||||||
|
|
||||||
|
// A secure client must not fallback
|
||||||
|
assertAuthEquals(NoFallback, getAuthMethod(KERBEROS, SIMPLE));
|
||||||
|
assertAuthEquals(NoFallback, getAuthMethod(KERBEROS, SIMPLE, UseToken.OTHER));
|
||||||
|
assertAuthEquals(TOKEN, getAuthMethod(KERBEROS, SIMPLE, UseToken.VALID));
|
||||||
|
assertAuthEquals(BadToken, getAuthMethod(KERBEROS, SIMPLE, UseToken.INVALID));
|
||||||
|
|
||||||
|
// doesn't try SASL
|
||||||
|
assertAuthEquals(Denied(SIMPLE), getAuthMethod(SIMPLE, TOKEN));
|
||||||
|
// does try SASL
|
||||||
|
assertAuthEquals(No(TOKEN), getAuthMethod(SIMPLE, TOKEN, UseToken.OTHER));
|
||||||
|
assertAuthEquals(TOKEN, getAuthMethod(SIMPLE, TOKEN, UseToken.VALID));
|
||||||
|
assertAuthEquals(BadToken, getAuthMethod(SIMPLE, TOKEN, UseToken.INVALID));
|
||||||
|
|
||||||
|
assertAuthEquals(No(TOKEN), getAuthMethod(KERBEROS, TOKEN));
|
||||||
|
assertAuthEquals(No(TOKEN), getAuthMethod(KERBEROS, TOKEN, UseToken.OTHER));
|
||||||
|
assertAuthEquals(TOKEN, getAuthMethod(KERBEROS, TOKEN, UseToken.VALID));
|
||||||
|
assertAuthEquals(BadToken, getAuthMethod(KERBEROS, TOKEN, UseToken.INVALID));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleServerWithTokens() throws Exception {
|
public void testSimpleServerWithTokens() throws Exception {
|
||||||
// Client not using tokens
|
// Client not using tokens
|
||||||
assertAuthEquals(SIMPLE, getAuthMethod(SIMPLE, SIMPLE));
|
assertAuthEquals(SIMPLE, getAuthMethod(SIMPLE, SIMPLE));
|
||||||
// SASL methods are reverted to SIMPLE, but test setup fails
|
// SASL methods are reverted to SIMPLE
|
||||||
assertAuthEquals(KrbFailed, getAuthMethod(KERBEROS, SIMPLE));
|
assertAuthEquals(SIMPLE, getAuthMethod(KERBEROS, SIMPLE));
|
||||||
|
|
||||||
// Use tokens. But tokens are ignored because client is reverted to simple
|
// Use tokens. But tokens are ignored because client is reverted to simple
|
||||||
assertAuthEquals(SIMPLE, getAuthMethod(KERBEROS, SIMPLE, true));
|
// due to server not using tokens
|
||||||
|
assertAuthEquals(SIMPLE, getAuthMethod(KERBEROS, SIMPLE, UseToken.VALID));
|
||||||
|
assertAuthEquals(SIMPLE, getAuthMethod(KERBEROS, SIMPLE, UseToken.OTHER));
|
||||||
|
|
||||||
|
// server isn't really advertising tokens
|
||||||
enableSecretManager = true;
|
enableSecretManager = true;
|
||||||
assertAuthEquals(SIMPLE, getAuthMethod(SIMPLE, SIMPLE, true));
|
assertAuthEquals(SIMPLE, getAuthMethod(SIMPLE, SIMPLE, UseToken.VALID));
|
||||||
assertAuthEquals(SIMPLE, getAuthMethod(KERBEROS, SIMPLE, true));
|
assertAuthEquals(SIMPLE, getAuthMethod(SIMPLE, SIMPLE, UseToken.OTHER));
|
||||||
|
|
||||||
|
assertAuthEquals(SIMPLE, getAuthMethod(KERBEROS, SIMPLE, UseToken.VALID));
|
||||||
|
assertAuthEquals(SIMPLE, getAuthMethod(KERBEROS, SIMPLE, UseToken.OTHER));
|
||||||
|
|
||||||
|
// now the simple server takes tokens
|
||||||
|
forceSecretManager = true;
|
||||||
|
assertAuthEquals(TOKEN, getAuthMethod(SIMPLE, SIMPLE, UseToken.VALID));
|
||||||
|
assertAuthEquals(SIMPLE, getAuthMethod(SIMPLE, SIMPLE, UseToken.OTHER));
|
||||||
|
|
||||||
|
assertAuthEquals(TOKEN, getAuthMethod(KERBEROS, SIMPLE, UseToken.VALID));
|
||||||
|
assertAuthEquals(SIMPLE, getAuthMethod(KERBEROS, SIMPLE, UseToken.OTHER));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleServerWithInvalidTokens() throws Exception {
|
public void testSimpleServerWithInvalidTokens() throws Exception {
|
||||||
// Tokens are ignored because client is reverted to simple
|
// Tokens are ignored because client is reverted to simple
|
||||||
assertAuthEquals(SIMPLE, getAuthMethod(SIMPLE, SIMPLE, false));
|
assertAuthEquals(SIMPLE, getAuthMethod(SIMPLE, SIMPLE, UseToken.INVALID));
|
||||||
assertAuthEquals(SIMPLE, getAuthMethod(KERBEROS, SIMPLE, false));
|
assertAuthEquals(SIMPLE, getAuthMethod(KERBEROS, SIMPLE, UseToken.INVALID));
|
||||||
enableSecretManager = true;
|
enableSecretManager = true;
|
||||||
assertAuthEquals(SIMPLE, getAuthMethod(SIMPLE, SIMPLE, false));
|
assertAuthEquals(SIMPLE, getAuthMethod(SIMPLE, SIMPLE, UseToken.INVALID));
|
||||||
assertAuthEquals(SIMPLE, getAuthMethod(KERBEROS, SIMPLE, false));
|
assertAuthEquals(SIMPLE, getAuthMethod(KERBEROS, SIMPLE, UseToken.INVALID));
|
||||||
|
forceSecretManager = true;
|
||||||
|
assertAuthEquals(BadToken, getAuthMethod(SIMPLE, SIMPLE, UseToken.INVALID));
|
||||||
|
assertAuthEquals(BadToken, getAuthMethod(KERBEROS, SIMPLE, UseToken.INVALID));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -675,26 +703,29 @@ public class TestSaslRPC {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testTokenOnlyServer() throws Exception {
|
public void testTokenOnlyServer() throws Exception {
|
||||||
|
// simple client w/o tokens won't try SASL, so server denies
|
||||||
assertAuthEquals(Denied(SIMPLE), getAuthMethod(SIMPLE, TOKEN));
|
assertAuthEquals(Denied(SIMPLE), getAuthMethod(SIMPLE, TOKEN));
|
||||||
assertAuthEquals(KrbFailed, getAuthMethod(KERBEROS, TOKEN));
|
assertAuthEquals(No(TOKEN), getAuthMethod(SIMPLE, TOKEN, UseToken.OTHER));
|
||||||
|
assertAuthEquals(No(TOKEN), getAuthMethod(KERBEROS, TOKEN));
|
||||||
|
assertAuthEquals(No(TOKEN), getAuthMethod(KERBEROS, TOKEN, UseToken.OTHER));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTokenOnlyServerWithTokens() throws Exception {
|
public void testTokenOnlyServerWithTokens() throws Exception {
|
||||||
assertAuthEquals(TOKEN, getAuthMethod(SIMPLE, TOKEN, true));
|
assertAuthEquals(TOKEN, getAuthMethod(SIMPLE, TOKEN, UseToken.VALID));
|
||||||
assertAuthEquals(TOKEN, getAuthMethod(KERBEROS, TOKEN, true));
|
assertAuthEquals(TOKEN, getAuthMethod(KERBEROS, TOKEN, UseToken.VALID));
|
||||||
enableSecretManager = false;
|
enableSecretManager = false;
|
||||||
assertAuthEquals(NoTokenAuth, getAuthMethod(SIMPLE, TOKEN, true));
|
assertAuthEquals(NoTokenAuth, getAuthMethod(SIMPLE, TOKEN, UseToken.VALID));
|
||||||
assertAuthEquals(NoTokenAuth, getAuthMethod(KERBEROS, TOKEN, true));
|
assertAuthEquals(NoTokenAuth, getAuthMethod(KERBEROS, TOKEN, UseToken.VALID));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTokenOnlyServerWithInvalidTokens() throws Exception {
|
public void testTokenOnlyServerWithInvalidTokens() throws Exception {
|
||||||
assertAuthEquals(BadToken, getAuthMethod(SIMPLE, TOKEN, false));
|
assertAuthEquals(BadToken, getAuthMethod(SIMPLE, TOKEN, UseToken.INVALID));
|
||||||
assertAuthEquals(BadToken, getAuthMethod(KERBEROS, TOKEN, false));
|
assertAuthEquals(BadToken, getAuthMethod(KERBEROS, TOKEN, UseToken.INVALID));
|
||||||
enableSecretManager = false;
|
enableSecretManager = false;
|
||||||
assertAuthEquals(NoTokenAuth, getAuthMethod(SIMPLE, TOKEN, false));
|
assertAuthEquals(NoTokenAuth, getAuthMethod(SIMPLE, TOKEN, UseToken.INVALID));
|
||||||
assertAuthEquals(NoTokenAuth, getAuthMethod(KERBEROS, TOKEN, false));
|
assertAuthEquals(NoTokenAuth, getAuthMethod(KERBEROS, TOKEN, UseToken.INVALID));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -702,38 +733,43 @@ public class TestSaslRPC {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testKerberosServer() throws Exception {
|
public void testKerberosServer() throws Exception {
|
||||||
assertAuthEquals(Denied(SIMPLE), getAuthMethod(SIMPLE, KERBEROS));
|
// doesn't try SASL
|
||||||
assertAuthEquals(KrbFailed, getAuthMethod(KERBEROS, KERBEROS));
|
assertAuthEquals(Denied(SIMPLE), getAuthMethod(SIMPLE, KERBEROS));
|
||||||
|
// does try SASL
|
||||||
|
assertAuthEquals(No(TOKEN,KERBEROS), getAuthMethod(SIMPLE, KERBEROS, UseToken.OTHER));
|
||||||
|
// no tgt
|
||||||
|
assertAuthEquals(KrbFailed, getAuthMethod(KERBEROS, KERBEROS));
|
||||||
|
assertAuthEquals(KrbFailed, getAuthMethod(KERBEROS, KERBEROS, UseToken.OTHER));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKerberosServerWithTokens() throws Exception {
|
public void testKerberosServerWithTokens() throws Exception {
|
||||||
// can use tokens regardless of auth
|
// can use tokens regardless of auth
|
||||||
assertAuthEquals(TOKEN, getAuthMethod(SIMPLE, KERBEROS, true));
|
assertAuthEquals(TOKEN, getAuthMethod(SIMPLE, KERBEROS, UseToken.VALID));
|
||||||
assertAuthEquals(TOKEN, getAuthMethod(KERBEROS, KERBEROS, true));
|
assertAuthEquals(TOKEN, getAuthMethod(KERBEROS, KERBEROS, UseToken.VALID));
|
||||||
// can't fallback to simple when using kerberos w/o tokens
|
|
||||||
enableSecretManager = false;
|
enableSecretManager = false;
|
||||||
assertAuthEquals(Denied(TOKEN), getAuthMethod(SIMPLE, KERBEROS, true));
|
// shouldn't even try token because server didn't tell us to
|
||||||
assertAuthEquals(Denied(TOKEN), getAuthMethod(KERBEROS, KERBEROS, true));
|
assertAuthEquals(No(KERBEROS), getAuthMethod(SIMPLE, KERBEROS, UseToken.VALID));
|
||||||
|
assertAuthEquals(KrbFailed, getAuthMethod(KERBEROS, KERBEROS, UseToken.VALID));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testKerberosServerWithInvalidTokens() throws Exception {
|
public void testKerberosServerWithInvalidTokens() throws Exception {
|
||||||
assertAuthEquals(BadToken, getAuthMethod(SIMPLE, KERBEROS, false));
|
assertAuthEquals(BadToken, getAuthMethod(SIMPLE, KERBEROS, UseToken.INVALID));
|
||||||
assertAuthEquals(BadToken, getAuthMethod(KERBEROS, KERBEROS, false));
|
assertAuthEquals(BadToken, getAuthMethod(KERBEROS, KERBEROS, UseToken.INVALID));
|
||||||
enableSecretManager = false;
|
enableSecretManager = false;
|
||||||
assertAuthEquals(Denied(TOKEN), getAuthMethod(SIMPLE, KERBEROS, false));
|
assertAuthEquals(No(KERBEROS), getAuthMethod(SIMPLE, KERBEROS, UseToken.INVALID));
|
||||||
assertAuthEquals(Denied(TOKEN), getAuthMethod(KERBEROS, KERBEROS, false));
|
assertAuthEquals(KrbFailed, getAuthMethod(KERBEROS, KERBEROS, UseToken.INVALID));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// test helpers
|
// test helpers
|
||||||
|
|
||||||
private String getAuthMethod(
|
private String getAuthMethod(
|
||||||
final AuthenticationMethod clientAuth,
|
final AuthMethod clientAuth,
|
||||||
final AuthenticationMethod serverAuth) throws Exception {
|
final AuthMethod serverAuth) throws Exception {
|
||||||
try {
|
try {
|
||||||
return internalGetAuthMethod(clientAuth, serverAuth, false, false);
|
return internalGetAuthMethod(clientAuth, serverAuth, UseToken.NONE);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.warn("Auth method failure", e);
|
LOG.warn("Auth method failure", e);
|
||||||
return e.toString();
|
return e.toString();
|
||||||
|
@ -741,11 +777,11 @@ public class TestSaslRPC {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getAuthMethod(
|
private String getAuthMethod(
|
||||||
final AuthenticationMethod clientAuth,
|
final AuthMethod clientAuth,
|
||||||
final AuthenticationMethod serverAuth,
|
final AuthMethod serverAuth,
|
||||||
final boolean useValidToken) throws Exception {
|
final UseToken tokenType) throws Exception {
|
||||||
try {
|
try {
|
||||||
return internalGetAuthMethod(clientAuth, serverAuth, true, useValidToken);
|
return internalGetAuthMethod(clientAuth, serverAuth, tokenType);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.warn("Auth method failure", e);
|
LOG.warn("Auth method failure", e);
|
||||||
return e.toString();
|
return e.toString();
|
||||||
|
@ -753,15 +789,14 @@ public class TestSaslRPC {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String internalGetAuthMethod(
|
private String internalGetAuthMethod(
|
||||||
final AuthenticationMethod clientAuth,
|
final AuthMethod clientAuth,
|
||||||
final AuthenticationMethod serverAuth,
|
final AuthMethod serverAuth,
|
||||||
final boolean useToken,
|
final UseToken tokenType) throws Exception {
|
||||||
final boolean useValidToken) throws Exception {
|
|
||||||
|
|
||||||
String currentUser = UserGroupInformation.getCurrentUser().getUserName();
|
String currentUser = UserGroupInformation.getCurrentUser().getUserName();
|
||||||
|
|
||||||
final Configuration serverConf = new Configuration(conf);
|
final Configuration serverConf = new Configuration(conf);
|
||||||
SecurityUtil.setAuthenticationMethod(serverAuth, serverConf);
|
serverConf.set(HADOOP_SECURITY_AUTHENTICATION, serverAuth.toString());
|
||||||
UserGroupInformation.setConfiguration(serverConf);
|
UserGroupInformation.setConfiguration(serverConf);
|
||||||
|
|
||||||
final UserGroupInformation serverUgi =
|
final UserGroupInformation serverUgi =
|
||||||
|
@ -793,7 +828,7 @@ public class TestSaslRPC {
|
||||||
});
|
});
|
||||||
|
|
||||||
final Configuration clientConf = new Configuration(conf);
|
final Configuration clientConf = new Configuration(conf);
|
||||||
SecurityUtil.setAuthenticationMethod(clientAuth, clientConf);
|
clientConf.set(HADOOP_SECURITY_AUTHENTICATION, clientAuth.toString());
|
||||||
clientConf.setBoolean(
|
clientConf.setBoolean(
|
||||||
CommonConfigurationKeys.IPC_CLIENT_FALLBACK_TO_SIMPLE_AUTH_ALLOWED_KEY,
|
CommonConfigurationKeys.IPC_CLIENT_FALLBACK_TO_SIMPLE_AUTH_ALLOWED_KEY,
|
||||||
clientFallBackToSimpleAllowed);
|
clientFallBackToSimpleAllowed);
|
||||||
|
@ -804,16 +839,26 @@ public class TestSaslRPC {
|
||||||
clientUgi.setAuthenticationMethod(clientAuth);
|
clientUgi.setAuthenticationMethod(clientAuth);
|
||||||
|
|
||||||
final InetSocketAddress addr = NetUtils.getConnectAddress(server);
|
final InetSocketAddress addr = NetUtils.getConnectAddress(server);
|
||||||
if (useToken) {
|
if (tokenType != UseToken.NONE) {
|
||||||
TestTokenIdentifier tokenId = new TestTokenIdentifier(
|
TestTokenIdentifier tokenId = new TestTokenIdentifier(
|
||||||
new Text(clientUgi.getUserName()));
|
new Text(clientUgi.getUserName()));
|
||||||
Token<TestTokenIdentifier> token = useValidToken
|
Token<TestTokenIdentifier> token = null;
|
||||||
? new Token<TestTokenIdentifier>(tokenId, sm)
|
switch (tokenType) {
|
||||||
: new Token<TestTokenIdentifier>(
|
case VALID:
|
||||||
|
token = new Token<TestTokenIdentifier>(tokenId, sm);
|
||||||
|
SecurityUtil.setTokenService(token, addr);
|
||||||
|
break;
|
||||||
|
case INVALID:
|
||||||
|
token = new Token<TestTokenIdentifier>(
|
||||||
tokenId.getBytes(), "bad-password!".getBytes(),
|
tokenId.getBytes(), "bad-password!".getBytes(),
|
||||||
tokenId.getKind(), null);
|
tokenId.getKind(), null);
|
||||||
|
SecurityUtil.setTokenService(token, addr);
|
||||||
SecurityUtil.setTokenService(token, addr);
|
break;
|
||||||
|
case OTHER:
|
||||||
|
token = new Token<TestTokenIdentifier>();
|
||||||
|
break;
|
||||||
|
case NONE: // won't get here
|
||||||
|
}
|
||||||
clientUgi.addToken(token);
|
clientUgi.addToken(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -848,7 +893,7 @@ public class TestSaslRPC {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assertAuthEquals(AuthenticationMethod expect,
|
private static void assertAuthEquals(AuthMethod expect,
|
||||||
String actual) {
|
String actual) {
|
||||||
assertEquals(expect.toString(), actual);
|
assertEquals(expect.toString(), actual);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue