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
046  public HBaseSaslRpcServer(SaslServerAuthenticationProvider provider,
047    Map<String, String> saslProps, SecretManager<TokenIdentifier> secretManager)
048    throws IOException {
049    serverWithProvider = provider.createServer(secretManager, saslProps);
050    saslServer = serverWithProvider.getServer();
051  }
052
053  public boolean isComplete() {
054    return saslServer.isComplete();
055  }
056
057  public byte[] evaluateResponse(byte[] response) throws SaslException {
058    return saslServer.evaluateResponse(response);
059  }
060
061  /** Release resources used by wrapped saslServer */
062  public void dispose() {
063    SaslUtil.safeDispose(saslServer);
064  }
065
066  public void switchToCryptoAES(CryptoAES cryptoAES) {
067    this.cryptoAES = cryptoAES;
068  }
069
070  public String getAttemptingUser() {
071    return serverWithProvider.getAttemptingUser().map(Object::toString).orElse("Unknown");
072  }
073
074  public byte[] wrap(byte[] buf, int off, int len) throws SaslException {
075    if (cryptoAES != null) {
076      return cryptoAES.wrap(buf, off, len);
077    } else {
078      return saslServer.wrap(buf, off, len);
079    }
080  }
081
082  public byte[] unwrap(byte[] buf, int off, int len) throws SaslException {
083    if (cryptoAES != null) {
084      return cryptoAES.unwrap(buf, off, len);
085    } else {
086      return saslServer.unwrap(buf, off, len);
087    }
088  }
089
090  public String getNegotiatedQop() {
091    return (String) saslServer.getNegotiatedProperty(Sasl.QOP);
092  }
093
094  public String getAuthorizationID() {
095    return saslServer.getAuthorizationID();
096  }
097
098  public static <T extends TokenIdentifier> T getIdentifier(String id,
099    SecretManager<T> secretManager) throws InvalidToken {
100    byte[] tokenId = SaslUtil.decodeIdentifier(id);
101    T tokenIdentifier = secretManager.createIdentifier();
102    try {
103      tokenIdentifier.readFields(new DataInputStream(new ByteArrayInputStream(tokenId)));
104    } catch (IOException e) {
105      throw (InvalidToken) new InvalidToken("Can't de-serialize tokenIdentifier").initCause(e);
106    }
107    return tokenIdentifier;
108  }
109
110  /**
111   * Unwrap InvalidToken exception, otherwise return the one passed in.
112   */
113  public static Throwable unwrap(Throwable e) {
114    Throwable cause = e;
115    while (cause != null) {
116      if (cause instanceof InvalidToken) {
117        return cause;
118      }
119      cause = cause.getCause();
120    }
121    return e;
122  }
123}