View Javadoc

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  
19  package org.apache.hadoop.hbase.security;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.DataInputStream;
23  import java.io.IOException;
24  import java.util.Locale;
25  import java.util.Map;
26
27  import javax.security.auth.callback.Callback;
28  import javax.security.auth.callback.CallbackHandler;
29  import javax.security.auth.callback.NameCallback;
30  import javax.security.auth.callback.PasswordCallback;
31  import javax.security.auth.callback.UnsupportedCallbackException;
32  import javax.security.sasl.AuthorizeCallback;
33  import javax.security.sasl.RealmCallback;
34
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  import org.apache.hadoop.hbase.classification.InterfaceAudience;
38  import org.apache.hadoop.conf.Configuration;
39  import org.apache.hadoop.hbase.ipc.RpcServer;
40  import org.apache.hadoop.hbase.security.SaslUtil.QualityOfProtection;
41  import org.apache.hadoop.security.UserGroupInformation;
42  import org.apache.hadoop.security.token.SecretManager;
43  import org.apache.hadoop.security.token.TokenIdentifier;
44  import org.apache.hadoop.security.token.SecretManager.InvalidToken;
45
46  /**
47   * A utility class for dealing with SASL on RPC server
48   */
49  @InterfaceAudience.Private
50  public class HBaseSaslRpcServer {
51    private static final Log LOG = LogFactory.getLog(HBaseSaslRpcServer.class);
52
53    private static Map<String, String> saslProps = null;
54
55    public static void init(Configuration conf) {
56      saslProps = SaslUtil.initSaslProperties(conf.get("hbase.rpc.protection",
57            QualityOfProtection.AUTHENTICATION.name().toLowerCase(Locale.ROOT)));
58    }
59
60    public static Map<String, String> getSaslProps() {
61      return saslProps;
62    }
63
64    public static <T extends TokenIdentifier> T getIdentifier(String id,
65        SecretManager<T> secretManager) throws InvalidToken {
66      byte[] tokenId = SaslUtil.decodeIdentifier(id);
67      T tokenIdentifier = secretManager.createIdentifier();
68      try {
69        tokenIdentifier.readFields(new DataInputStream(new ByteArrayInputStream(
70            tokenId)));
71      } catch (IOException e) {
72        throw (InvalidToken) new InvalidToken(
73            "Can't de-serialize tokenIdentifier").initCause(e);
74      }
75      return tokenIdentifier;
76    }
77  
78
79    /** CallbackHandler for SASL DIGEST-MD5 mechanism */
80    public static class SaslDigestCallbackHandler implements CallbackHandler {
81      private SecretManager<TokenIdentifier> secretManager;
82      private RpcServer.Connection connection;
83
84      public SaslDigestCallbackHandler(
85          SecretManager<TokenIdentifier> secretManager,
86          RpcServer.Connection connection) {
87        this.secretManager = secretManager;
88        this.connection = connection;
89      }
90
91      private char[] getPassword(TokenIdentifier tokenid) throws InvalidToken {
92        return SaslUtil.encodePassword(secretManager.retrievePassword(tokenid));
93      }
94
95      /** {@inheritDoc} */
96      @Override
97      public void handle(Callback[] callbacks) throws InvalidToken,
98          UnsupportedCallbackException {
99        NameCallback nc = null;
100       PasswordCallback pc = null;
101       AuthorizeCallback ac = null;
102       for (Callback callback : callbacks) {
103         if (callback instanceof AuthorizeCallback) {
104           ac = (AuthorizeCallback) callback;
105         } else if (callback instanceof NameCallback) {
106           nc = (NameCallback) callback;
107         } else if (callback instanceof PasswordCallback) {
108           pc = (PasswordCallback) callback;
109         } else if (callback instanceof RealmCallback) {
110           continue; // realm is ignored
111         } else {
112           throw new UnsupportedCallbackException(callback,
113               "Unrecognized SASL DIGEST-MD5 Callback");
114         }
115       }
116       if (pc != null) {
117         TokenIdentifier tokenIdentifier = getIdentifier(nc.getDefaultName(), secretManager);
118         char[] password = getPassword(tokenIdentifier);
119         UserGroupInformation user = null;
120         user = tokenIdentifier.getUser(); // may throw exception
121         connection.attemptingUser = user;
122         if (LOG.isDebugEnabled()) {
123           LOG.debug("SASL server DIGEST-MD5 callback: setting password "
124               + "for client: " + tokenIdentifier.getUser());
125         }
126         pc.setPassword(password);
127       }
128       if (ac != null) {
129         String authid = ac.getAuthenticationID();
130         String authzid = ac.getAuthorizationID();
131         if (authid.equals(authzid)) {
132           ac.setAuthorized(true);
133         } else {
134           ac.setAuthorized(false);
135         }
136         if (ac.isAuthorized()) {
137           if (LOG.isDebugEnabled()) {
138             String username =
139               getIdentifier(authzid, secretManager).getUser().getUserName();
140             LOG.debug("SASL server DIGEST-MD5 callback: setting "
141                 + "canonicalized client ID: " + username);
142           }
143           ac.setAuthorizedID(authzid);
144         }
145       }
146     }
147   }
148
149   /** CallbackHandler for SASL GSSAPI Kerberos mechanism */
150   public static class SaslGssCallbackHandler implements CallbackHandler {
151
152     /** {@inheritDoc} */
153     @Override
154     public void handle(Callback[] callbacks) throws
155         UnsupportedCallbackException {
156       AuthorizeCallback ac = null;
157       for (Callback callback : callbacks) {
158         if (callback instanceof AuthorizeCallback) {
159           ac = (AuthorizeCallback) callback;
160         } else {
161           throw new UnsupportedCallbackException(callback,
162               "Unrecognized SASL GSSAPI Callback");
163         }
164       }
165       if (ac != null) {
166         String authid = ac.getAuthenticationID();
167         String authzid = ac.getAuthorizationID();
168         if (authid.equals(authzid)) {
169           ac.setAuthorized(true);
170         } else {
171           ac.setAuthorized(false);
172         }
173         if (ac.isAuthorized()) {
174           if (LOG.isDebugEnabled())
175             LOG.debug("SASL server GSSAPI callback: setting "
176                 + "canonicalized client ID: " + authzid);
177           ac.setAuthorizedID(authzid);
178         }
179       }
180     }
181   }
182 }