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.token;
20 
21 import java.io.IOException;
22 
23 import com.google.protobuf.RpcCallback;
24 import com.google.protobuf.RpcController;
25 import com.google.protobuf.Service;
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.apache.hadoop.hbase.classification.InterfaceAudience;
29 import org.apache.hadoop.hbase.Coprocessor;
30 import org.apache.hadoop.hbase.CoprocessorEnvironment;
31 import org.apache.hadoop.hbase.coprocessor.CoprocessorService;
32 import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
33 import org.apache.hadoop.hbase.ipc.RpcServer;
34 import org.apache.hadoop.hbase.ipc.RpcServerInterface;
35 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
36 import org.apache.hadoop.hbase.protobuf.ResponseConverter;
37 import org.apache.hadoop.hbase.protobuf.generated.AuthenticationProtos;
38 import org.apache.hadoop.hbase.security.AccessDeniedException;
39 import org.apache.hadoop.hbase.security.User;
40 import org.apache.hadoop.security.UserGroupInformation;
41 import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
42 import org.apache.hadoop.security.token.SecretManager;
43 import org.apache.hadoop.security.token.Token;
44 
45 /**
46  * Provides a service for obtaining authentication tokens via the
47  * {@link AuthenticationProtos} AuthenticationService coprocessor service.
48  */
49 @InterfaceAudience.Private
50 public class TokenProvider implements AuthenticationProtos.AuthenticationService.Interface,
51     Coprocessor, CoprocessorService {
52 
53   private static final Log LOG = LogFactory.getLog(TokenProvider.class);
54 
55   private AuthenticationTokenSecretManager secretManager;
56 
57 
58   @Override
start(CoprocessorEnvironment env)59   public void start(CoprocessorEnvironment env) {
60     // if running at region
61     if (env instanceof RegionCoprocessorEnvironment) {
62       RegionCoprocessorEnvironment regionEnv =
63           (RegionCoprocessorEnvironment)env;
64       RpcServerInterface server = regionEnv.getRegionServerServices().getRpcServer();
65       SecretManager<?> mgr = ((RpcServer)server).getSecretManager();
66       if (mgr instanceof AuthenticationTokenSecretManager) {
67         secretManager = (AuthenticationTokenSecretManager)mgr;
68       }
69     }
70   }
71 
72   @Override
stop(CoprocessorEnvironment env)73   public void stop(CoprocessorEnvironment env) throws IOException {
74   }
75 
76   /**
77    * @param ugi A user group information.
78    * @return true if delegation token operation is allowed
79    */
isAllowedDelegationTokenOp(UserGroupInformation ugi)80   private boolean isAllowedDelegationTokenOp(UserGroupInformation ugi) throws IOException {
81     AuthenticationMethod authMethod = ugi.getAuthenticationMethod();
82     if (authMethod == AuthenticationMethod.PROXY) {
83       authMethod = ugi.getRealUser().getAuthenticationMethod();
84     }
85     if (authMethod != AuthenticationMethod.KERBEROS
86         && authMethod != AuthenticationMethod.KERBEROS_SSL
87         && authMethod != AuthenticationMethod.CERTIFICATE) {
88       return false;
89     }
90     return true;
91   }
92 
93   // AuthenticationService implementation
94 
95   @Override
getService()96   public Service getService() {
97     return AuthenticationProtos.AuthenticationService.newReflectiveService(this);
98   }
99 
100   @Override
getAuthenticationToken(RpcController controller, AuthenticationProtos.GetAuthenticationTokenRequest request, RpcCallback<AuthenticationProtos.GetAuthenticationTokenResponse> done)101   public void getAuthenticationToken(RpcController controller,
102                                      AuthenticationProtos.GetAuthenticationTokenRequest request,
103                                      RpcCallback<AuthenticationProtos.GetAuthenticationTokenResponse> done) {
104     AuthenticationProtos.GetAuthenticationTokenResponse.Builder response =
105         AuthenticationProtos.GetAuthenticationTokenResponse.newBuilder();
106 
107     try {
108       if (secretManager == null) {
109         throw new IOException(
110             "No secret manager configured for token authentication");
111       }
112 
113       User currentUser = RpcServer.getRequestUser();
114       UserGroupInformation ugi = null;
115       if (currentUser != null) {
116         ugi = currentUser.getUGI();
117       }
118       if (currentUser == null) {
119         throw new AccessDeniedException("No authenticated user for request!");
120       } else if (!isAllowedDelegationTokenOp(ugi)) {
121         LOG.warn("Token generation denied for user="+currentUser.getName()
122             +", authMethod="+ugi.getAuthenticationMethod());
123         throw new AccessDeniedException(
124             "Token generation only allowed for Kerberos authenticated clients");
125       }
126 
127       Token<AuthenticationTokenIdentifier> token =
128           secretManager.generateToken(currentUser.getName());
129       response.setToken(ProtobufUtil.toToken(token)).build();
130     } catch (IOException ioe) {
131       ResponseConverter.setControllerException(controller, ioe);
132     }
133     done.run(response.build());
134   }
135 
136   @Override
whoAmI(RpcController controller, AuthenticationProtos.WhoAmIRequest request, RpcCallback<AuthenticationProtos.WhoAmIResponse> done)137   public void whoAmI(RpcController controller, AuthenticationProtos.WhoAmIRequest request,
138                      RpcCallback<AuthenticationProtos.WhoAmIResponse> done) {
139     User requestUser = RpcServer.getRequestUser();
140     AuthenticationProtos.WhoAmIResponse.Builder response =
141         AuthenticationProtos.WhoAmIResponse.newBuilder();
142     if (requestUser != null) {
143       response.setUsername(requestUser.getShortName());
144       AuthenticationMethod method = requestUser.getUGI().getAuthenticationMethod();
145       if (method != null) {
146         response.setAuthMethod(method.name());
147       }
148     }
149     done.run(response.build());
150   }
151 }
152