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