1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.security;
19
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assert.fail;
24 import static org.mockito.Matchers.any;
25 import static org.mockito.Matchers.anyString;
26 import static org.mockito.Mockito.mock;
27 import static org.mockito.Mockito.verify;
28 import static org.mockito.Mockito.when;
29
30 import com.google.common.base.Strings;
31
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.io.OutputStream;
35
36 import javax.security.auth.callback.Callback;
37 import javax.security.auth.callback.CallbackHandler;
38 import javax.security.auth.callback.NameCallback;
39 import javax.security.auth.callback.PasswordCallback;
40 import javax.security.auth.callback.TextOutputCallback;
41 import javax.security.auth.callback.UnsupportedCallbackException;
42 import javax.security.sasl.RealmCallback;
43 import javax.security.sasl.RealmChoiceCallback;
44 import javax.security.sasl.Sasl;
45 import javax.security.sasl.SaslClient;
46
47 import org.apache.hadoop.hbase.security.AbstractHBaseSaslRpcClient.SaslClientCallbackHandler;
48 import org.apache.hadoop.hbase.testclassification.SecurityTests;
49 import org.apache.hadoop.hbase.testclassification.SmallTests;
50 import org.apache.hadoop.hbase.util.Bytes;
51 import org.apache.hadoop.io.DataInputBuffer;
52 import org.apache.hadoop.io.DataOutputBuffer;
53 import org.apache.hadoop.security.token.Token;
54 import org.apache.hadoop.security.token.TokenIdentifier;
55 import org.apache.log4j.Level;
56 import org.apache.log4j.Logger;
57 import org.junit.BeforeClass;
58 import org.junit.Rule;
59 import org.junit.Test;
60 import org.junit.experimental.categories.Category;
61 import org.junit.rules.ExpectedException;
62 import org.mockito.Mockito;
63
64 @Category({SecurityTests.class, SmallTests.class})
65 public class TestHBaseSaslRpcClient {
66
67 static {
68 System.setProperty("java.security.krb5.realm", "DOMAIN.COM");
69 System.setProperty("java.security.krb5.kdc", "DOMAIN.COM");
70 }
71
72 static final String DEFAULT_USER_NAME = "principal";
73 static final String DEFAULT_USER_PASSWORD = "password";
74
75 private static final Logger LOG = Logger.getLogger(TestHBaseSaslRpcClient.class);
76
77
78 @Rule
79 public ExpectedException exception = ExpectedException.none();
80
81 @BeforeClass
82 public static void before() {
83 Logger.getRootLogger().setLevel(Level.DEBUG);
84 }
85
86 @Test
87 public void testSaslClientUsesGivenRpcProtection() throws Exception {
88 Token<? extends TokenIdentifier> token = createTokenMockWithCredentials(DEFAULT_USER_NAME,
89 DEFAULT_USER_PASSWORD);
90 for (SaslUtil.QualityOfProtection qop : SaslUtil.QualityOfProtection.values()) {
91 String negotiatedQop = new HBaseSaslRpcClient(AuthMethod.DIGEST, token,
92 "principal/host@DOMAIN.COM", false, qop.name()) {
93 public String getQop() {
94 return saslProps.get(Sasl.QOP);
95 }
96 }.getQop();
97 assertEquals(negotiatedQop, qop.getSaslQop());
98 }
99 }
100
101 @Test
102 public void testSaslClientCallbackHandler() throws UnsupportedCallbackException {
103 final Token<? extends TokenIdentifier> token = createTokenMock();
104 when(token.getIdentifier()).thenReturn(Bytes.toBytes(DEFAULT_USER_NAME));
105 when(token.getPassword()).thenReturn(Bytes.toBytes(DEFAULT_USER_PASSWORD));
106
107 final NameCallback nameCallback = mock(NameCallback.class);
108 final PasswordCallback passwordCallback = mock(PasswordCallback.class);
109 final RealmCallback realmCallback = mock(RealmCallback.class);
110 final RealmChoiceCallback realmChoiceCallback = mock(RealmChoiceCallback.class);
111
112 Callback[] callbackArray = {nameCallback, passwordCallback, realmCallback, realmChoiceCallback};
113 final SaslClientCallbackHandler saslClCallbackHandler = new SaslClientCallbackHandler(token);
114 saslClCallbackHandler.handle(callbackArray);
115 verify(nameCallback).setName(anyString());
116 verify(realmCallback).setText(anyString());
117 verify(passwordCallback).setPassword(any(char[].class));
118 }
119
120 @Test
121 public void testSaslClientCallbackHandlerWithException() {
122 final Token<? extends TokenIdentifier> token = createTokenMock();
123 when(token.getIdentifier()).thenReturn(Bytes.toBytes(DEFAULT_USER_NAME));
124 when(token.getPassword()).thenReturn(Bytes.toBytes(DEFAULT_USER_PASSWORD));
125 final SaslClientCallbackHandler saslClCallbackHandler = new SaslClientCallbackHandler(token);
126 try {
127 saslClCallbackHandler.handle(new Callback[] { mock(TextOutputCallback.class) });
128 } catch (UnsupportedCallbackException expEx) {
129
130 } catch (Exception ex) {
131 fail("testSaslClientCallbackHandlerWithException error : " + ex.getMessage());
132 }
133 }
134
135 @Test
136 public void testHBaseSaslRpcClientCreation() throws Exception {
137
138 assertFalse(assertSuccessCreationKerberosPrincipal(null));
139 assertFalse(assertSuccessCreationKerberosPrincipal("DOMAIN.COM"));
140 assertFalse(assertSuccessCreationKerberosPrincipal("principal/DOMAIN.COM"));
141 if (!assertSuccessCreationKerberosPrincipal("principal/localhost@DOMAIN.COM")) {
142
143
144 LOG.warn("Could not create a SASL client with valid Kerberos credential");
145 }
146
147
148 assertFalse(assertSuccessCreationDigestPrincipal(null, null));
149 assertFalse(assertSuccessCreationDigestPrincipal("", ""));
150 assertFalse(assertSuccessCreationDigestPrincipal("", null));
151 assertFalse(assertSuccessCreationDigestPrincipal(null, ""));
152 assertTrue(assertSuccessCreationDigestPrincipal(DEFAULT_USER_NAME, DEFAULT_USER_PASSWORD));
153
154
155 assertFalse(assertSuccessCreationSimplePrincipal("", ""));
156 assertFalse(assertSuccessCreationSimplePrincipal(null, null));
157 assertFalse(assertSuccessCreationSimplePrincipal(DEFAULT_USER_NAME, DEFAULT_USER_PASSWORD));
158
159
160 assertTrue(assertIOExceptionThenSaslClientIsNull(DEFAULT_USER_NAME, DEFAULT_USER_PASSWORD));
161 assertTrue(assertIOExceptionWhenGetStreamsBeforeConnectCall(
162 DEFAULT_USER_NAME, DEFAULT_USER_PASSWORD));
163 }
164
165 @Test
166 public void testAuthMethodReadWrite() throws IOException {
167 DataInputBuffer in = new DataInputBuffer();
168 DataOutputBuffer out = new DataOutputBuffer();
169
170 assertAuthMethodRead(in, AuthMethod.SIMPLE);
171 assertAuthMethodRead(in, AuthMethod.KERBEROS);
172 assertAuthMethodRead(in, AuthMethod.DIGEST);
173
174 assertAuthMethodWrite(out, AuthMethod.SIMPLE);
175 assertAuthMethodWrite(out, AuthMethod.KERBEROS);
176 assertAuthMethodWrite(out, AuthMethod.DIGEST);
177 }
178
179 private void assertAuthMethodRead(DataInputBuffer in, AuthMethod authMethod)
180 throws IOException {
181 in.reset(new byte[] {authMethod.code}, 1);
182 assertEquals(authMethod, AuthMethod.read(in));
183 }
184
185 private void assertAuthMethodWrite(DataOutputBuffer out, AuthMethod authMethod)
186 throws IOException {
187 authMethod.write(out);
188 assertEquals(authMethod.code, out.getData()[0]);
189 out.reset();
190 }
191
192 private boolean assertIOExceptionWhenGetStreamsBeforeConnectCall(String principal,
193 String password) throws IOException {
194 boolean inState = false;
195 boolean outState = false;
196
197 HBaseSaslRpcClient rpcClient = new HBaseSaslRpcClient(AuthMethod.DIGEST,
198 createTokenMockWithCredentials(principal, password), principal, false) {
199 @Override
200 public SaslClient createDigestSaslClient(String[] mechanismNames,
201 String saslDefaultRealm, CallbackHandler saslClientCallbackHandler)
202 throws IOException {
203 return Mockito.mock(SaslClient.class);
204 }
205
206 @Override
207 public SaslClient createKerberosSaslClient(String[] mechanismNames,
208 String userFirstPart, String userSecondPart) throws IOException {
209 return Mockito.mock(SaslClient.class);
210 }
211 };
212
213 try {
214 rpcClient.getInputStream(Mockito.mock(InputStream.class));
215 } catch(IOException ex) {
216
217 inState = true;
218 }
219
220 try {
221 rpcClient.getOutputStream(Mockito.mock(OutputStream.class));
222 } catch(IOException ex) {
223
224 outState = true;
225 }
226
227 return inState && outState;
228 }
229
230 private boolean assertIOExceptionThenSaslClientIsNull(String principal, String password) {
231 try {
232 new HBaseSaslRpcClient(AuthMethod.DIGEST,
233 createTokenMockWithCredentials(principal, password), principal, false) {
234 @Override
235 public SaslClient createDigestSaslClient(String[] mechanismNames,
236 String saslDefaultRealm, CallbackHandler saslClientCallbackHandler)
237 throws IOException {
238 return null;
239 }
240
241 @Override
242 public SaslClient createKerberosSaslClient(String[] mechanismNames,
243 String userFirstPart, String userSecondPart) throws IOException {
244 return null;
245 }
246 };
247 return false;
248 } catch (IOException ex) {
249 return true;
250 }
251 }
252
253 private boolean assertSuccessCreationKerberosPrincipal(String principal) {
254 HBaseSaslRpcClient rpcClient = null;
255 try {
256 rpcClient = createSaslRpcClientForKerberos(principal);
257 } catch(Exception ex) {
258 LOG.error(ex.getMessage(), ex);
259 }
260 return rpcClient != null;
261 }
262
263 private boolean assertSuccessCreationDigestPrincipal(String principal, String password) {
264 HBaseSaslRpcClient rpcClient = null;
265 try {
266 rpcClient = new HBaseSaslRpcClient(AuthMethod.DIGEST,
267 createTokenMockWithCredentials(principal, password), principal, false);
268 } catch(Exception ex) {
269 LOG.error(ex.getMessage(), ex);
270 }
271 return rpcClient != null;
272 }
273
274 private boolean assertSuccessCreationSimplePrincipal(String principal, String password) {
275 HBaseSaslRpcClient rpcClient = null;
276 try {
277 rpcClient = createSaslRpcClientSimple(principal, password);
278 } catch(Exception ex) {
279 LOG.error(ex.getMessage(), ex);
280 }
281 return rpcClient != null;
282 }
283
284 private HBaseSaslRpcClient createSaslRpcClientForKerberos(String principal)
285 throws IOException {
286 return new HBaseSaslRpcClient(AuthMethod.KERBEROS, createTokenMock(), principal, false);
287 }
288
289 private Token<? extends TokenIdentifier> createTokenMockWithCredentials(
290 String principal, String password)
291 throws IOException {
292 Token<? extends TokenIdentifier> token = createTokenMock();
293 if (!Strings.isNullOrEmpty(principal) && !Strings.isNullOrEmpty(password)) {
294 when(token.getIdentifier()).thenReturn(Bytes.toBytes(DEFAULT_USER_NAME));
295 when(token.getPassword()).thenReturn(Bytes.toBytes(DEFAULT_USER_PASSWORD));
296 }
297 return token;
298 }
299
300 private HBaseSaslRpcClient createSaslRpcClientSimple(String principal, String password)
301 throws IOException {
302 return new HBaseSaslRpcClient(AuthMethod.SIMPLE, createTokenMock(), principal, false);
303 }
304
305 @SuppressWarnings("unchecked")
306 private Token<? extends TokenIdentifier> createTokenMock() {
307 return mock(Token.class);
308 }
309 }