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.io.ByteArrayInputStream;
021import java.io.DataInputStream;
022import java.io.IOException;
023import java.util.Map;
024import javax.security.sasl.Sasl;
025import javax.security.sasl.SaslException;
026import javax.security.sasl.SaslServer;
027import org.apache.hadoop.hbase.io.crypto.aes.CryptoAES;
028import org.apache.hadoop.hbase.security.provider.AttemptingUserProvidingSaslServer;
029import org.apache.hadoop.hbase.security.provider.SaslServerAuthenticationProvider;
030import org.apache.hadoop.security.token.SecretManager;
031import org.apache.hadoop.security.token.SecretManager.InvalidToken;
032import org.apache.hadoop.security.token.TokenIdentifier;
033import org.apache.yetus.audience.InterfaceAudience;
034
035/**
036 * A utility class that encapsulates SASL logic for RPC server. Copied from
037 * <code>org.apache.hadoop.security</code>
038 */
039@InterfaceAudience.Private
040public class HBaseSaslRpcServer {
041
042  private final AttemptingUserProvidingSaslServer serverWithProvider;
043  private final SaslServer saslServer;
044  private CryptoAES cryptoAES;
045  private final Map<String, String> saslProps;
046
047  public HBaseSaslRpcServer(SaslServerAuthenticationProvider provider,
048    Map<String, String> saslProps, SecretManager<TokenIdentifier> secretManager)
049    throws IOException {
050    serverWithProvider = provider.createServer(secretManager, saslProps);
051    saslServer = serverWithProvider.getServer();
052    this.saslProps = saslProps;
053  }
054
055  public boolean isComplete() {
056    return saslServer.isComplete();
057  }
058
059  public byte[] evaluateResponse(byte[] response) throws SaslException {
060    return saslServer.evaluateResponse(response);
061  }
062
063  /** Release resources used by wrapped saslServer */
064  public void dispose() {
065    SaslUtil.safeDispose(saslServer);
066  }
067
068  public void switchToCryptoAES(CryptoAES cryptoAES) {
069    this.cryptoAES = cryptoAES;
070  }
071
072  public String getAttemptingUser() {
073    return serverWithProvider.getAttemptingUser().map(Object::toString).orElse("Unknown");
074  }
075
076  public byte[] wrap(byte[] buf, int off, int len) throws SaslException {
077    if (cryptoAES != null) {
078      return cryptoAES.wrap(buf, off, len);
079    } else {
080      return saslServer.wrap(buf, off, len);
081    }
082  }
083
084  public byte[] unwrap(byte[] buf, int off, int len) throws SaslException {
085    if (cryptoAES != null) {
086      return cryptoAES.unwrap(buf, off, len);
087    } else {
088      return saslServer.unwrap(buf, off, len);
089    }
090  }
091
092  public String getNegotiatedQop() {
093    return (String) saslServer.getNegotiatedProperty(Sasl.QOP);
094  }
095
096  public String getRequestedQop() {
097    return (String) saslProps.get(Sasl.QOP);
098  }
099
100  public String getAuthorizationID() {
101    return saslServer.getAuthorizationID();
102  }
103
104  public static <T extends TokenIdentifier> T getIdentifier(String id,
105    SecretManager<T> secretManager) throws InvalidToken {
106    byte[] tokenId = SaslUtil.decodeIdentifier(id);
107    T tokenIdentifier = secretManager.createIdentifier();
108    try {
109      tokenIdentifier.readFields(new DataInputStream(new ByteArrayInputStream(tokenId)));
110    } catch (IOException e) {
111      throw (InvalidToken) new InvalidToken("Can't de-serialize tokenIdentifier").initCause(e);
112    }
113    return tokenIdentifier;
114  }
115
116  /**
117   * Unwrap InvalidToken exception, otherwise return the one passed in.
118   */
119  public static Throwable unwrap(Throwable e) {
120    Throwable cause = e;
121    while (cause != null) {
122      if (cause instanceof InvalidToken) {
123        return cause;
124      }
125      cause = cause.getCause();
126    }
127    return e;
128  }
129}