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