001/** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019package org.apache.hadoop.hbase.security; 020 021import java.nio.charset.StandardCharsets; 022import java.util.Map; 023import java.util.TreeMap; 024 025import javax.security.sasl.Sasl; 026import javax.security.sasl.SaslClient; 027import javax.security.sasl.SaslException; 028import javax.security.sasl.SaslServer; 029 030import org.apache.commons.codec.binary.Base64; 031import org.apache.hadoop.hbase.util.Bytes; 032import org.apache.yetus.audience.InterfaceAudience; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036@InterfaceAudience.Private 037public class SaslUtil { 038 private static final Logger LOG = LoggerFactory.getLogger(SaslUtil.class); 039 public static final String SASL_DEFAULT_REALM = "default"; 040 public static final int SWITCH_TO_SIMPLE_AUTH = -88; 041 042 public enum QualityOfProtection { 043 AUTHENTICATION("auth"), 044 INTEGRITY("auth-int"), 045 PRIVACY("auth-conf"); 046 047 private final String saslQop; 048 049 QualityOfProtection(String saslQop) { 050 this.saslQop = saslQop; 051 } 052 053 public String getSaslQop() { 054 return saslQop; 055 } 056 057 public boolean matches(String stringQop) { 058 if (saslQop.equals(stringQop)) { 059 LOG.warn("Use authentication/integrity/privacy as value for rpc protection " 060 + "configurations instead of auth/auth-int/auth-conf."); 061 return true; 062 } 063 return name().equalsIgnoreCase(stringQop); 064 } 065 } 066 067 /** Splitting fully qualified Kerberos name into parts */ 068 public static String[] splitKerberosName(String fullName) { 069 return fullName.split("[/@]"); 070 } 071 072 static String encodeIdentifier(byte[] identifier) { 073 return new String(Base64.encodeBase64(identifier), StandardCharsets.UTF_8); 074 } 075 076 static byte[] decodeIdentifier(String identifier) { 077 return Base64.decodeBase64(Bytes.toBytes(identifier)); 078 } 079 080 static char[] encodePassword(byte[] password) { 081 return new String(Base64.encodeBase64(password), StandardCharsets.UTF_8).toCharArray(); 082 } 083 084 /** 085 * Returns {@link org.apache.hadoop.hbase.security.SaslUtil.QualityOfProtection} 086 * corresponding to the given {@code stringQop} value. 087 * @throws IllegalArgumentException If stringQop doesn't match any QOP. 088 */ 089 public static QualityOfProtection getQop(String stringQop) { 090 for (QualityOfProtection qop : QualityOfProtection.values()) { 091 if (qop.matches(stringQop)) { 092 return qop; 093 } 094 } 095 throw new IllegalArgumentException("Invalid qop: " + stringQop 096 + ". It must be one of 'authentication', 'integrity', 'privacy'."); 097 } 098 099 /** 100 * @param rpcProtection Value of 'hbase.rpc.protection' configuration. 101 * @return Map with values for SASL properties. 102 */ 103 public static Map<String, String> initSaslProperties(String rpcProtection) { 104 String saslQop; 105 if (rpcProtection.isEmpty()) { 106 saslQop = QualityOfProtection.AUTHENTICATION.getSaslQop(); 107 } else { 108 String[] qops = rpcProtection.split(","); 109 StringBuilder saslQopBuilder = new StringBuilder(); 110 for (int i = 0; i < qops.length; ++i) { 111 QualityOfProtection qop = getQop(qops[i]); 112 saslQopBuilder.append(",").append(qop.getSaslQop()); 113 } 114 saslQop = saslQopBuilder.substring(1); // remove first ',' 115 } 116 Map<String, String> saslProps = new TreeMap<>(); 117 saslProps.put(Sasl.QOP, saslQop); 118 saslProps.put(Sasl.SERVER_AUTH, "true"); 119 return saslProps; 120 } 121 122 static void safeDispose(SaslClient saslClient) { 123 try { 124 saslClient.dispose(); 125 } catch (SaslException e) { 126 LOG.error("Error disposing of SASL client", e); 127 } 128 } 129 130 static void safeDispose(SaslServer saslServer) { 131 try { 132 saslServer.dispose(); 133 } catch (SaslException e) { 134 LOG.error("Error disposing of SASL server", e); 135 } 136 } 137}