001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.security.provider; 019 020import java.io.IOException; 021import java.net.InetAddress; 022import java.util.Map; 023import javax.security.sasl.Sasl; 024import javax.security.sasl.SaslClient; 025import org.apache.hadoop.conf.Configuration; 026import org.apache.hadoop.hbase.security.SaslUtil; 027import org.apache.hadoop.hbase.security.SecurityConstants; 028import org.apache.hadoop.hbase.security.SecurityInfo; 029import org.apache.hadoop.hbase.security.User; 030import org.apache.hadoop.security.SecurityUtil; 031import org.apache.hadoop.security.UserGroupInformation; 032import org.apache.hadoop.security.token.Token; 033import org.apache.hadoop.security.token.TokenIdentifier; 034import org.apache.yetus.audience.InterfaceAudience; 035import org.slf4j.Logger; 036import org.slf4j.LoggerFactory; 037 038import org.apache.hadoop.hbase.shaded.protobuf.generated.RPCProtos.UserInformation; 039 040@InterfaceAudience.Private 041public class GssSaslClientAuthenticationProvider extends GssSaslAuthenticationProvider 042 implements SaslClientAuthenticationProvider { 043 private static final Logger LOG = 044 LoggerFactory.getLogger(GssSaslClientAuthenticationProvider.class); 045 046 private static boolean useCanonicalHostname(Configuration conf) { 047 return !conf.getBoolean( 048 SecurityConstants.UNSAFE_HBASE_CLIENT_KERBEROS_HOSTNAME_DISABLE_REVERSEDNS, 049 SecurityConstants.DEFAULT_UNSAFE_HBASE_CLIENT_KERBEROS_HOSTNAME_DISABLE_REVERSEDNS); 050 } 051 052 public static String getHostnameForServerPrincipal(Configuration conf, InetAddress addr) { 053 final String hostname; 054 055 if (useCanonicalHostname(conf)) { 056 hostname = addr.getCanonicalHostName(); 057 if (hostname.equals(addr.getHostAddress())) { 058 LOG.warn("Canonical hostname for SASL principal is the same with IP address: " + hostname 059 + ", " + addr.getHostName() + ". Check DNS configuration or consider " 060 + SecurityConstants.UNSAFE_HBASE_CLIENT_KERBEROS_HOSTNAME_DISABLE_REVERSEDNS + "=true"); 061 } 062 } else { 063 hostname = addr.getHostName(); 064 } 065 066 return hostname.toLowerCase(); 067 } 068 069 String getServerPrincipal(Configuration conf, SecurityInfo securityInfo, InetAddress server) 070 throws IOException { 071 String hostname = getHostnameForServerPrincipal(conf, server); 072 073 String serverKey = securityInfo.getServerPrincipal(); 074 if (serverKey == null) { 075 throw new IllegalArgumentException( 076 "Can't obtain server Kerberos config key from SecurityInfo"); 077 } 078 return SecurityUtil.getServerPrincipal(conf.get(serverKey), hostname); 079 } 080 081 @Override 082 public SaslClient createClient(Configuration conf, InetAddress serverAddr, 083 SecurityInfo securityInfo, Token<? extends TokenIdentifier> token, boolean fallbackAllowed, 084 Map<String, String> saslProps) throws IOException { 085 String serverPrincipal = getServerPrincipal(conf, securityInfo, serverAddr); 086 LOG.debug("Setting up Kerberos RPC to server={}", serverPrincipal); 087 String[] names = SaslUtil.splitKerberosName(serverPrincipal); 088 if (names.length != 3) { 089 throw new IOException( 090 "Kerberos principal '" + serverPrincipal + "' does not have the expected format"); 091 } 092 return Sasl.createSaslClient(new String[] { getSaslAuthMethod().getSaslMechanism() }, null, 093 names[0], names[1], saslProps, null); 094 } 095 096 @Override 097 public UserInformation getUserInfo(User user) { 098 UserInformation.Builder userInfoPB = UserInformation.newBuilder(); 099 // Send effective user for Kerberos auth 100 userInfoPB.setEffectiveUser(user.getUGI().getUserName()); 101 return userInfoPB.build(); 102 } 103 104 @Override 105 public boolean canRetry() { 106 return true; 107 } 108 109 @Override 110 public void relogin() throws IOException { 111 // Check if UGI thinks we need to do another login 112 if (UserGroupInformation.isLoginKeytabBased()) { 113 UserGroupInformation.getLoginUser().reloginFromKeytab(); 114 } else { 115 UserGroupInformation.getLoginUser().reloginFromTicketCache(); 116 } 117 } 118 119 @Override 120 public UserGroupInformation getRealUser(User user) { 121 final UserGroupInformation ugi = user.getUGI(); 122 // Unwrap the UGI with the real user when we're using Kerberos auth 123 if (ugi != null && ugi.getRealUser() != null) { 124 return ugi.getRealUser(); 125 } 126 127 // Otherwise, use the UGI we were given 128 return ugi; 129 } 130}