All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.cassandra.security.PlainSaslServer Maven / Gradle / Ivy

There is a newer version: 3.11.12.3
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.cassandra.security;

import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.Provider;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Map;

import javax.management.remote.JMXAuthenticator;
import javax.security.auth.callback.CallbackHandler;
import javax.security.sasl.SaslException;
import javax.security.sasl.SaslServer;
import javax.security.sasl.SaslServerFactory;


/**
 * Sun JDK only provides a PLAIN client and no server. This class implements the Plain SASL server
 * conforming to RFC #4616 (http://www.ietf.org/rfc/rfc4616.txt).
 */
public class PlainSaslServer implements SaslServer 
{

  public static final String PLAIN_METHOD = "PLAIN";
  public static final String SASL_JMX_AUTHENTICATOR = "jmxmp.sasl.authenticator";
  
  private String user;
  private final CallbackHandler handler;
  private final Map  props;
  
  PlainSaslServer(CallbackHandler handler, String authMethodStr, Map  props) throws SaslException 
  {
    this.handler = handler;
    this.props = props;
  }

  @Override
  public String getMechanismName() 
  {
    return PLAIN_METHOD;
  }

  @Override
  public byte[] evaluateResponse(byte[] response) throws SaslException 
  {
    try {
      // parse the response
      // message   = [authzid] UTF8NUL authcid UTF8NUL passwd'

      Deque tokenList = new ArrayDeque();
      StringBuilder messageToken = new StringBuilder();
      for (byte b : response) 
      {
        if (b == 0) 
        {
          tokenList.addLast(messageToken.toString());
          messageToken = new StringBuilder();
        } else {
          messageToken.append((char) b);
        }
      }
      tokenList.addLast(messageToken.toString());

      // validate response
      if (tokenList.size() < 2 || tokenList.size() > 3) {
        throw new SaslException("Invalid message format");
      }
      String passwd = tokenList.removeLast();
      user = tokenList.removeLast();
      
      if (user == null || user.isEmpty()) 
        throw new SaslException("No user name provided");

      if (passwd == null || passwd.isEmpty())
        throw new SaslException("No password name provided");
      
      String[] credentials = new String[] { user, passwd };
      JMXAuthenticator authenticator = (JMXAuthenticator) props.get(SASL_JMX_AUTHENTICATOR);
      if (authenticator == null)
    	  throw new IllegalStateException("No SASL JMX authenticator");
      
      authenticator.authenticate(credentials);
    } 
    catch (IllegalStateException eL)
    {
      throw new SaslException("Invalid message format", eL);
    } 
    catch (IOException eI) 
    {
      throw new SaslException("Error validating the login", eI);
    }
    return null;
  }

  @Override
  public boolean isComplete() 
  {
    return user != null;
  }

  @Override
  public String getAuthorizationID() 
  {
    return user;
  }

  @Override
  public byte[] unwrap(byte[] incoming, int offset, int len) 
  {
    throw new UnsupportedOperationException();
  }

  @Override
  public byte[] wrap(byte[] outgoing, int offset, int len) 
  {
    throw new UnsupportedOperationException();
  }

  @Override
  public Object getNegotiatedProperty(String propName) 
  {
    return null;
  }

  @Override
  public void dispose() {}

  public static class SaslPlainServerFactory implements SaslServerFactory 
  {

    @Override
    public SaslServer createSaslServer(String mechanism, String protocol, String serverName, Map props, CallbackHandler cbh) 
    {
      if (PLAIN_METHOD.equals(mechanism)) 
      {
        try 
        {
          return new PlainSaslServer(cbh, protocol, props);
        } 
        catch (SaslException e) 
        {
          /* This is to fulfill the contract of the interface which states that an exception shall
             be thrown when a SaslServer cannot be created due to an error but null should be
             returned when a Server can't be created due to the parameters supplied. And the only
             thing PlainSaslServer can fail on is a non-supported authentication mechanism.
             That's why we return null instead of throwing the Exception */
          return null;
        }
      }
      return null;
    }

    @Override
    public String[] getMechanismNames(Map props) 
    {
      return new String[] {PLAIN_METHOD};
    }
  }

  public static class SaslPlainProvider extends Provider 
  {

    public SaslPlainProvider() 
    {
      super("CassandraSaslPlain", 1.0, "Cassandra Plain SASL provider");
      AccessController.doPrivileged(new PrivilegedAction() 
      {
          public Object run() 
          {
              put("SaslServerFactory.PLAIN", SaslPlainServerFactory.class.getName());
              return null;
          }
      });
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy