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