Skip to content

Commit 1c0bd3d

Browse files
szetszwoMikaelSmith
authored andcommitted
CDPD-90798: HADOOP-19306. Support user defined auth Callback in SaslRpcServer. (apache#7140)
(cherry picked from commit 317db31) (cherry picked from commit eaaa443b1284ca4c5dabf885c25ce15673cff2b3) Change-Id: I00e2060977fd3cf7794ba67200524b2beb1f184f
1 parent 4e6c395 commit 1c0bd3d

File tree

15 files changed

+289
-112
lines changed

15 files changed

+289
-112
lines changed

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/CommonConfigurationKeysPublic.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,12 @@ public class CommonConfigurationKeysPublic {
736736
*/
737737
public static final String HADOOP_RPC_PROTECTION =
738738
"hadoop.rpc.protection";
739+
public static final String HADOOP_SECURITY_SASL_MECHANISM_KEY
740+
= "hadoop.security.sasl.mechanism";
741+
public static final String HADOOP_SECURITY_SASL_MECHANISM_DEFAULT
742+
= "DIGEST-MD5";
743+
public static final String HADOOP_SECURITY_SASL_CUSTOMIZEDCALLBACKHANDLER_CLASS_KEY
744+
= "hadoop.security.sasl.CustomizedCallbackHandler.class";
739745
/** Class to override Sasl Properties for a connection */
740746
public static final String HADOOP_SECURITY_SASL_PROPS_RESOLVER_CLASS =
741747
"hadoop.security.saslproperties.resolver.class";

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/Server.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@
106106
import org.apache.hadoop.ipc.protobuf.RpcHeaderProtos.RPCTraceInfoProto;
107107
import org.apache.hadoop.net.NetUtils;
108108
import org.apache.hadoop.security.AccessControlException;
109-
import org.apache.hadoop.security.SaslConstants;
109+
import org.apache.hadoop.security.SaslMechanismFactory;
110110
import org.apache.hadoop.security.SaslPropertiesResolver;
111111
import org.apache.hadoop.security.SaslRpcServer;
112112
import org.apache.hadoop.security.SaslRpcServer.AuthMethod;
@@ -2144,6 +2144,10 @@ public Server getServer() {
21442144
return Server.this;
21452145
}
21462146

2147+
public Configuration getConf() {
2148+
return Server.this.getConf();
2149+
}
2150+
21472151
/* Return true if the connection has no outstanding rpc */
21482152
private boolean isIdle() {
21492153
return rpcCount.get() == 0;
@@ -2607,7 +2611,7 @@ private RpcSaslProto buildSaslNegotiateResponse()
26072611
// accelerate token negotiation by sending initial challenge
26082612
// in the negotiation response
26092613
if (enabledAuthMethods.contains(AuthMethod.TOKEN)
2610-
&& SaslConstants.SASL_MECHANISM_DEFAULT.equals(AuthMethod.TOKEN.getMechanismName())) {
2614+
&& SaslMechanismFactory.isDefaultMechanism(AuthMethod.TOKEN.getMechanismName())) {
26112615
saslServer = createSaslServer(AuthMethod.TOKEN);
26122616
byte[] challenge = saslServer.evaluateResponse(new byte[0]);
26132617
RpcSaslProto.Builder negotiateBuilder =
Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,80 @@
1515
* See the License for the specific language governing permissions and
1616
* limitations under the License.
1717
*/
18-
package org.apache.hadoop.hdfs.protocol.datatransfer.sasl;
18+
package org.apache.hadoop.security;
19+
20+
import org.apache.hadoop.conf.Configuration;
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
1923

2024
import javax.security.auth.callback.Callback;
2125
import javax.security.auth.callback.UnsupportedCallbackException;
2226
import java.io.IOException;
2327
import java.lang.reflect.InvocationTargetException;
2428
import java.lang.reflect.Method;
29+
import java.util.HashMap;
2530
import java.util.List;
31+
import java.util.Map;
2632

2733
/** For handling customized {@link Callback}. */
2834
public interface CustomizedCallbackHandler {
29-
class DefaultHandler implements CustomizedCallbackHandler{
35+
Logger LOG = LoggerFactory.getLogger(CustomizedCallbackHandler.class);
36+
37+
class Cache {
38+
private static final Map<String, CustomizedCallbackHandler> MAP = new HashMap<>();
39+
40+
private static synchronized CustomizedCallbackHandler getSynchronously(
41+
String key, Configuration conf) {
42+
//check again synchronously
43+
final CustomizedCallbackHandler cached = MAP.get(key);
44+
if (cached != null) {
45+
return cached; //cache hit
46+
}
47+
48+
//cache miss
49+
final Class<?> clazz = conf.getClass(key, DefaultHandler.class);
50+
LOG.info("{} = {}", key, clazz);
51+
if (clazz == DefaultHandler.class) {
52+
return DefaultHandler.INSTANCE;
53+
}
54+
55+
final Object created;
56+
try {
57+
created = clazz.newInstance();
58+
} catch (Exception e) {
59+
LOG.warn("Failed to create a new instance of {}, fallback to {}",
60+
clazz, DefaultHandler.class, e);
61+
return DefaultHandler.INSTANCE;
62+
}
63+
64+
final CustomizedCallbackHandler handler = created instanceof CustomizedCallbackHandler ?
65+
(CustomizedCallbackHandler) created : CustomizedCallbackHandler.delegate(created);
66+
MAP.put(key, handler);
67+
return handler;
68+
}
69+
70+
private static CustomizedCallbackHandler get(String key, Configuration conf) {
71+
final CustomizedCallbackHandler cached = MAP.get(key);
72+
return cached != null ? cached : getSynchronously(key, conf);
73+
}
74+
75+
public static synchronized void clear() {
76+
MAP.clear();
77+
}
78+
79+
private Cache() { }
80+
}
81+
82+
class DefaultHandler implements CustomizedCallbackHandler {
83+
private static final DefaultHandler INSTANCE = new DefaultHandler();
84+
3085
@Override
3186
public void handleCallbacks(List<Callback> callbacks, String username, char[] password)
3287
throws UnsupportedCallbackException {
3388
if (!callbacks.isEmpty()) {
34-
throw new UnsupportedCallbackException(callbacks.get(0));
89+
final Callback cb = callbacks.get(0);
90+
throw new UnsupportedCallbackException(callbacks.get(0),
91+
"Unsupported callback: " + (cb == null ? null : cb.getClass()));
3592
}
3693
}
3794
}
@@ -55,6 +112,10 @@ static CustomizedCallbackHandler delegate(Object delegated) {
55112
};
56113
}
57114

115+
static CustomizedCallbackHandler get(String key, Configuration conf) {
116+
return Cache.get(key, conf);
117+
}
118+
58119
void handleCallbacks(List<Callback> callbacks, String name, char[] password)
59120
throws UnsupportedCallbackException, IOException;
60121
}

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/SaslConstants.java

Lines changed: 0 additions & 45 deletions
This file was deleted.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.security;
19+
20+
import org.apache.hadoop.HadoopIllegalArgumentException;
21+
import org.apache.hadoop.classification.InterfaceAudience;
22+
import org.apache.hadoop.classification.InterfaceStability;
23+
import org.apache.hadoop.conf.Configuration;
24+
import org.slf4j.Logger;
25+
import org.slf4j.LoggerFactory;
26+
27+
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_SASL_MECHANISM_DEFAULT;
28+
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_SASL_MECHANISM_KEY;
29+
30+
/**
31+
* SASL related constants.
32+
*/
33+
@InterfaceAudience.LimitedPrivate({"HDFS", "MapReduce", "YARN", "HBase"})
34+
@InterfaceStability.Evolving
35+
public final class SaslMechanismFactory {
36+
static final Logger LOG = LoggerFactory.getLogger(SaslMechanismFactory.class);
37+
38+
private static final String SASL_MECHANISM_ENV = "HADOOP_SASL_MECHANISM";
39+
private static final String SASL_MECHANISM;
40+
41+
static {
42+
// env
43+
final String envValue = System.getenv(SASL_MECHANISM_ENV);
44+
LOG.debug("{} = {} (env)", SASL_MECHANISM_ENV, envValue);
45+
46+
// conf
47+
final Configuration conf = new Configuration(false);
48+
final String confValue = conf.get(HADOOP_SECURITY_SASL_MECHANISM_KEY,
49+
HADOOP_SECURITY_SASL_MECHANISM_DEFAULT);
50+
LOG.debug("{} = {} (conf)", HADOOP_SECURITY_SASL_MECHANISM_KEY, confValue);
51+
52+
if (envValue != null && confValue != null) {
53+
if (!envValue.equals(confValue)) {
54+
throw new HadoopIllegalArgumentException("SASL Mechanism mismatched: env "
55+
+ SASL_MECHANISM_ENV + " is " + envValue + " but conf "
56+
+ HADOOP_SECURITY_SASL_MECHANISM_KEY + " is " + confValue);
57+
}
58+
}
59+
60+
SASL_MECHANISM = envValue != null ? envValue
61+
: confValue != null ? confValue
62+
: HADOOP_SECURITY_SASL_MECHANISM_DEFAULT;
63+
LOG.debug("SASL_MECHANISM = {} (effective)", SASL_MECHANISM);
64+
}
65+
66+
public static String getMechanism() {
67+
return SASL_MECHANISM;
68+
}
69+
70+
public static boolean isDefaultMechanism(String mechanism) {
71+
return HADOOP_SECURITY_SASL_MECHANISM_DEFAULT.equals(mechanism);
72+
}
73+
74+
private SaslMechanismFactory() {}
75+
}

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/SaslRpcServer.java

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,16 @@
4343
import org.apache.hadoop.classification.InterfaceAudience;
4444
import org.apache.hadoop.classification.InterfaceStability;
4545
import org.apache.hadoop.conf.Configuration;
46-
import org.apache.hadoop.ipc.RetriableException;
4746
import org.apache.hadoop.ipc.Server;
4847
import org.apache.hadoop.ipc.Server.Connection;
49-
import org.apache.hadoop.ipc.StandbyException;
5048
import org.apache.hadoop.security.token.SecretManager;
5149
import org.apache.hadoop.security.token.SecretManager.InvalidToken;
5250
import org.apache.hadoop.security.token.TokenIdentifier;
5351
import org.slf4j.Logger;
5452
import org.slf4j.LoggerFactory;
5553

54+
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_SASL_CUSTOMIZEDCALLBACKHANDLER_CLASS_KEY;
55+
5656
/**
5757
* A utility class for dealing with SASL on RPC server
5858
*/
@@ -223,8 +223,8 @@ public enum AuthMethod {
223223
SIMPLE((byte) 80, ""),
224224
KERBEROS((byte) 81, "GSSAPI"),
225225
@Deprecated
226-
DIGEST((byte) 82, SaslConstants.SASL_MECHANISM),
227-
TOKEN((byte) 82, SaslConstants.SASL_MECHANISM),
226+
DIGEST((byte) 82, SaslMechanismFactory.getMechanism()),
227+
TOKEN((byte) 82, SaslMechanismFactory.getMechanism()),
228228
PLAIN((byte) 83, "PLAIN");
229229

230230
/** The code for this method. */
@@ -234,6 +234,8 @@ public enum AuthMethod {
234234
private AuthMethod(byte code, String mechanismName) {
235235
this.code = code;
236236
this.mechanismName = mechanismName;
237+
LOG.info("{} {}: code={}, mechanism=\"{}\"",
238+
getClass().getSimpleName(), name(), code, mechanismName);
237239
}
238240

239241
private static final int FIRST_CODE = values()[0].code;
@@ -276,28 +278,44 @@ public void write(DataOutput out) throws IOException {
276278
/** CallbackHandler for SASL mechanism. */
277279
@InterfaceStability.Evolving
278280
public static class SaslDigestCallbackHandler implements CallbackHandler {
281+
private final CustomizedCallbackHandler customizedCallbackHandler;
279282
private SecretManager<TokenIdentifier> secretManager;
280283
private Server.Connection connection;
281284

282285
public SaslDigestCallbackHandler(
283286
SecretManager<TokenIdentifier> secretManager,
284287
Server.Connection connection) {
288+
this(secretManager, connection, connection.getConf());
289+
}
290+
291+
public SaslDigestCallbackHandler(
292+
SecretManager<TokenIdentifier> secretManager,
293+
Server.Connection connection,
294+
Configuration conf) {
285295
this.secretManager = secretManager;
286296
this.connection = connection;
297+
this.customizedCallbackHandler = CustomizedCallbackHandler.get(
298+
HADOOP_SECURITY_SASL_CUSTOMIZEDCALLBACKHANDLER_CLASS_KEY, conf);
287299
}
288300

289-
private char[] getPassword(TokenIdentifier tokenid) throws InvalidToken,
290-
StandbyException, RetriableException, IOException {
301+
private char[] getPassword(TokenIdentifier tokenid) throws IOException {
291302
return encodePassword(secretManager.retriableRetrievePassword(tokenid));
292303
}
293304

305+
private char[] getPassword(String name) throws IOException {
306+
final TokenIdentifier tokenIdentifier = getIdentifier(name, secretManager);
307+
final UserGroupInformation user = tokenIdentifier.getUser();
308+
connection.attemptingUser = user;
309+
LOG.debug("SASL server callback: setting password for client: {}", user);
310+
return getPassword(tokenIdentifier);
311+
}
312+
294313
@Override
295-
public void handle(Callback[] callbacks) throws InvalidToken,
296-
UnsupportedCallbackException, StandbyException, RetriableException,
297-
IOException {
314+
public void handle(Callback[] callbacks) throws UnsupportedCallbackException, IOException {
298315
NameCallback nc = null;
299316
PasswordCallback pc = null;
300317
AuthorizeCallback ac = null;
318+
List<Callback> unknownCallbacks = null;
301319
for (Callback callback : callbacks) {
302320
if (callback instanceof AuthorizeCallback) {
303321
ac = (AuthorizeCallback) callback;
@@ -308,20 +326,14 @@ public void handle(Callback[] callbacks) throws InvalidToken,
308326
} else if (callback instanceof RealmCallback) {
309327
continue; // realm is ignored
310328
} else {
311-
throw new UnsupportedCallbackException(callback,
312-
"Unrecognized SASL Callback");
329+
if (unknownCallbacks == null) {
330+
unknownCallbacks = new ArrayList<>();
331+
}
332+
unknownCallbacks.add(callback);
313333
}
314334
}
315335
if (pc != null) {
316-
TokenIdentifier tokenIdentifier = getIdentifier(nc.getDefaultName(),
317-
secretManager);
318-
char[] password = getPassword(tokenIdentifier);
319-
UserGroupInformation user = null;
320-
user = tokenIdentifier.getUser(); // may throw exception
321-
connection.attemptingUser = user;
322-
323-
LOG.debug("SASL server callback: setting password for client: {}", user);
324-
pc.setPassword(password);
336+
pc.setPassword(getPassword(nc.getDefaultName()));
325337
}
326338
if (ac != null) {
327339
String authid = ac.getAuthenticationID();
@@ -341,6 +353,11 @@ public void handle(Callback[] callbacks) throws InvalidToken,
341353
ac.setAuthorizedID(authzid);
342354
}
343355
}
356+
if (unknownCallbacks != null) {
357+
final String name = nc != null ? nc.getDefaultName() : null;
358+
final char[] password = name != null ? getPassword(name) : null;
359+
customizedCallbackHandler.handleCallbacks(unknownCallbacks, name, password);
360+
}
344361
}
345362
}
346363

0 commit comments

Comments
 (0)