1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.security;
20
21 import java.io.ByteArrayInputStream;
22 import java.io.DataInputStream;
23 import java.io.IOException;
24
25 import javax.security.auth.callback.Callback;
26 import javax.security.auth.callback.CallbackHandler;
27 import javax.security.auth.callback.NameCallback;
28 import javax.security.auth.callback.PasswordCallback;
29 import javax.security.auth.callback.UnsupportedCallbackException;
30 import javax.security.sasl.AuthorizeCallback;
31 import javax.security.sasl.RealmCallback;
32 import javax.security.sasl.Sasl;
33
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36 import org.apache.hadoop.conf.Configuration;
37 import org.apache.hadoop.hbase.ipc.HBaseServer;
38 import org.apache.hadoop.security.UserGroupInformation;
39 import org.apache.hadoop.security.token.SecretManager;
40 import org.apache.hadoop.security.token.TokenIdentifier;
41 import org.apache.hadoop.security.token.SecretManager.InvalidToken;
42
43
44
45
46 public class HBaseSaslRpcServer {
47 public static final Log LOG = LogFactory.getLog(HBaseSaslRpcServer.class);
48
49 public static enum QualityOfProtection {
50 AUTHENTICATION("auth"),
51 INTEGRITY("auth-int"),
52 PRIVACY("auth-conf");
53
54 public final String saslQop;
55
56 private QualityOfProtection(String saslQop) {
57 this.saslQop = saslQop;
58 }
59
60 public String getSaslQop() {
61 return saslQop;
62 }
63 }
64
65 public static void init(Configuration conf) {
66 QualityOfProtection saslQOP = QualityOfProtection.AUTHENTICATION;
67 String rpcProtection = conf.get("hbase.rpc.protection",
68 QualityOfProtection.AUTHENTICATION.name().toLowerCase());
69 if (QualityOfProtection.INTEGRITY.name().toLowerCase()
70 .equals(rpcProtection)) {
71 saslQOP = QualityOfProtection.INTEGRITY;
72 } else if (QualityOfProtection.PRIVACY.name().toLowerCase().equals(
73 rpcProtection)) {
74 saslQOP = QualityOfProtection.PRIVACY;
75 }
76
77 SaslUtil.SASL_PROPS.put(Sasl.QOP, saslQOP.getSaslQop());
78 SaslUtil.SASL_PROPS.put(Sasl.SERVER_AUTH, "true");
79 }
80
81 public static <T extends TokenIdentifier> T getIdentifier(String id,
82 SecretManager<T> secretManager) throws InvalidToken {
83 byte[] tokenId = SaslUtil.decodeIdentifier(id);
84 T tokenIdentifier = secretManager.createIdentifier();
85 try {
86 tokenIdentifier.readFields(new DataInputStream(new ByteArrayInputStream(
87 tokenId)));
88 } catch (IOException e) {
89 throw (InvalidToken) new InvalidToken(
90 "Can't de-serialize tokenIdentifier").initCause(e);
91 }
92 return tokenIdentifier;
93 }
94
95
96
97 public static class SaslDigestCallbackHandler implements CallbackHandler {
98 private SecretManager<TokenIdentifier> secretManager;
99 private HBaseServer.Connection connection;
100
101 public SaslDigestCallbackHandler(
102 SecretManager<TokenIdentifier> secretManager,
103 HBaseServer.Connection connection) {
104 this.secretManager = secretManager;
105 this.connection = connection;
106 }
107
108 private char[] getPassword(TokenIdentifier tokenid) throws InvalidToken {
109 return SaslUtil.encodePassword(secretManager.retrievePassword(tokenid));
110 }
111
112
113 @Override
114 public void handle(Callback[] callbacks) throws InvalidToken,
115 UnsupportedCallbackException {
116 NameCallback nc = null;
117 PasswordCallback pc = null;
118 AuthorizeCallback ac = null;
119 for (Callback callback : callbacks) {
120 if (callback instanceof AuthorizeCallback) {
121 ac = (AuthorizeCallback) callback;
122 } else if (callback instanceof NameCallback) {
123 nc = (NameCallback) callback;
124 } else if (callback instanceof PasswordCallback) {
125 pc = (PasswordCallback) callback;
126 } else if (callback instanceof RealmCallback) {
127 continue;
128 } else {
129 throw new UnsupportedCallbackException(callback,
130 "Unrecognized SASL DIGEST-MD5 Callback");
131 }
132 }
133 if (pc != null) {
134 TokenIdentifier tokenIdentifier = getIdentifier(nc.getDefaultName(), secretManager);
135 char[] password = getPassword(tokenIdentifier);
136 UserGroupInformation user = null;
137 user = tokenIdentifier.getUser();
138 connection.attemptingUser = user;
139 if (LOG.isDebugEnabled()) {
140 LOG.debug("SASL server DIGEST-MD5 callback: setting password "
141 + "for client: " + tokenIdentifier.getUser());
142 }
143 pc.setPassword(password);
144 }
145 if (ac != null) {
146 String authid = ac.getAuthenticationID();
147 String authzid = ac.getAuthorizationID();
148 if (authid.equals(authzid)) {
149 ac.setAuthorized(true);
150 } else {
151 ac.setAuthorized(false);
152 }
153 if (ac.isAuthorized()) {
154 if (LOG.isDebugEnabled()) {
155 String username =
156 getIdentifier(authzid, secretManager).getUser().getUserName();
157 LOG.debug("SASL server DIGEST-MD5 callback: setting "
158 + "canonicalized client ID: " + username);
159 }
160 ac.setAuthorizedID(authzid);
161 }
162 }
163 }
164 }
165
166
167 public static class SaslGssCallbackHandler implements CallbackHandler {
168
169
170 @Override
171 public void handle(Callback[] callbacks) throws
172 UnsupportedCallbackException {
173 AuthorizeCallback ac = null;
174 for (Callback callback : callbacks) {
175 if (callback instanceof AuthorizeCallback) {
176 ac = (AuthorizeCallback) callback;
177 } else {
178 throw new UnsupportedCallbackException(callback,
179 "Unrecognized SASL GSSAPI Callback");
180 }
181 }
182 if (ac != null) {
183 String authid = ac.getAuthenticationID();
184 String authzid = ac.getAuthorizationID();
185 if (authid.equals(authzid)) {
186 ac.setAuthorized(true);
187 } else {
188 ac.setAuthorized(false);
189 }
190 if (ac.isAuthorized()) {
191 if (LOG.isDebugEnabled())
192 LOG.debug("SASL server GSSAPI callback: setting "
193 + "canonicalized client ID: " + authzid);
194 ac.setAuthorizedID(authzid);
195 }
196 }
197 }
198 }
199 }