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