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; 019 020import java.util.Base64; 021import java.util.Map; 022import java.util.TreeMap; 023import javax.security.sasl.Sasl; 024import javax.security.sasl.SaslClient; 025import javax.security.sasl.SaslException; 026import javax.security.sasl.SaslServer; 027import org.apache.hadoop.hbase.util.Bytes; 028import org.apache.yetus.audience.InterfaceAudience; 029import org.slf4j.Logger; 030import org.slf4j.LoggerFactory; 031 032import org.apache.hbase.thirdparty.com.google.common.base.Splitter; 033 034@InterfaceAudience.Private 035public class SaslUtil { 036 private static final Logger LOG = LoggerFactory.getLogger(SaslUtil.class); 037 public static final String SASL_DEFAULT_REALM = "default"; 038 public static final int SWITCH_TO_SIMPLE_AUTH = -88; 039 040 public enum QualityOfProtection { 041 AUTHENTICATION("auth"), 042 INTEGRITY("auth-int"), 043 PRIVACY("auth-conf"); 044 045 private final String saslQop; 046 047 QualityOfProtection(String saslQop) { 048 this.saslQop = saslQop; 049 } 050 051 public String getSaslQop() { 052 return saslQop; 053 } 054 055 public boolean matches(String stringQop) { 056 if (saslQop.equals(stringQop)) { 057 LOG.warn("Use authentication/integrity/privacy as value for rpc protection " 058 + "configurations instead of auth/auth-int/auth-conf."); 059 return true; 060 } 061 return name().equalsIgnoreCase(stringQop); 062 } 063 } 064 065 /** Splitting fully qualified Kerberos name into parts */ 066 public static String[] splitKerberosName(String fullName) { 067 return Splitter.onPattern("[/@]").splitToStream(fullName).toArray(String[]::new); 068 } 069 070 public static String encodeIdentifier(byte[] identifier) { 071 return Base64.getEncoder().encodeToString(identifier); 072 } 073 074 public static byte[] decodeIdentifier(String identifier) { 075 return Base64.getDecoder().decode(Bytes.toBytes(identifier)); 076 } 077 078 public static char[] encodePassword(byte[] password) { 079 return Base64.getEncoder().encodeToString(password).toCharArray(); 080 } 081 082 /** 083 * Returns {@link org.apache.hadoop.hbase.security.SaslUtil.QualityOfProtection} corresponding to 084 * the given {@code stringQop} value. 085 * @throws IllegalArgumentException If stringQop doesn't match any QOP. 086 */ 087 public static QualityOfProtection getQop(String stringQop) { 088 for (QualityOfProtection qop : QualityOfProtection.values()) { 089 if (qop.matches(stringQop)) { 090 return qop; 091 } 092 } 093 throw new IllegalArgumentException("Invalid qop: " + stringQop 094 + ". It must be one of 'authentication', 'integrity', 'privacy'."); 095 } 096 097 /** 098 * Initialize SASL properties for a given RPC protection level. 099 * @param rpcProtection Value of 'hbase.rpc.protection' configuration. 100 * @return Map with values for SASL properties. 101 */ 102 public static Map<String, String> initSaslProperties(String rpcProtection) { 103 String saslQop; 104 if (rpcProtection.isEmpty()) { 105 saslQop = QualityOfProtection.AUTHENTICATION.getSaslQop(); 106 } else { 107 StringBuilder saslQopBuilder = new StringBuilder(); 108 for (String s : Splitter.on(',').split(rpcProtection)) { 109 QualityOfProtection qop = getQop(s); 110 saslQopBuilder.append(",").append(qop.getSaslQop()); 111 } 112 saslQop = saslQopBuilder.substring(1); // remove first ',' 113 } 114 Map<String, String> saslProps = new TreeMap<>(); 115 saslProps.put(Sasl.QOP, saslQop); 116 saslProps.put(Sasl.SERVER_AUTH, "true"); 117 return saslProps; 118 } 119 120 static void safeDispose(SaslClient saslClient) { 121 try { 122 saslClient.dispose(); 123 } catch (SaslException e) { 124 LOG.error("Error disposing of SASL client", e); 125 } 126 } 127 128 static void safeDispose(SaslServer saslServer) { 129 try { 130 saslServer.dispose(); 131 } catch (SaslException e) { 132 LOG.error("Error disposing of SASL server", e); 133 } 134 } 135}