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}