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

org.xnio.sasl.SaslUtils Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source
 *
 * Copyright 2011 Red Hat, Inc. and/or its affiliates, and individual
 * contributors as indicated by the @author tags.
 *
 * Licensed 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.xnio.sasl;

import static java.security.AccessController.doPrivileged;
import static org.xnio._private.Messages.msg;

import java.nio.ByteBuffer;
import java.security.PrivilegedAction;
import java.security.Provider;
import java.security.Security;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;

import org.xnio.Buffers;
import org.xnio.Option;
import org.xnio.OptionMap;
import org.xnio.Options;
import org.xnio.Property;
import org.xnio.Sequence;

import javax.security.sasl.Sasl;
import javax.security.sasl.SaslClient;
import javax.security.sasl.SaslClientFactory;
import javax.security.sasl.SaslServer;
import javax.security.sasl.SaslException;
import javax.security.sasl.SaslServerFactory;

/**
 * Utility methods for handling SASL authentication using NIO-style programming methods.
 *
 * @author David M. Lloyd
 */
public final class SaslUtils {

    private SaslUtils() {
    }

    /**
     * A zero-length byte array, useful for sending and receiving empty SASL messages.
     */
    public static final byte[] EMPTY_BYTES = new byte[0];

    /**
     * Returns an iterator of all of the registered {@code SaslServerFactory}s where the order is based on the
     * order of the Provider registration and/or class path order.  Class path providers are listed before
     * global providers; in the event of a name conflict, the class path provider is preferred.
     *
     * @param classLoader the class loader to use
     * @param includeGlobal {@code true} to include globally registered providers, {@code false} to exclude them
     * @return the {@code Iterator} of {@code SaslServerFactory}s
     */
    public static Iterator getSaslServerFactories(ClassLoader classLoader, boolean includeGlobal) {
        return getFactories(SaslServerFactory.class, classLoader, includeGlobal);
    }

    /**
     * Returns an iterator of all of the registered {@code SaslServerFactory}s where the order is based on the
     * order of the Provider registration and/or class path order.
     *
     * @return the {@code Iterator} of {@code SaslServerFactory}s
     */
    public static Iterator getSaslServerFactories() {
        return getFactories(SaslServerFactory.class, null, true);
    }

    /**
     * Returns an iterator of all of the registered {@code SaslClientFactory}s where the order is based on the
     * order of the Provider registration and/or class path order.  Class path providers are listed before
     * global providers; in the event of a name conflict, the class path provider is preferred.
     *
     * @param classLoader the class loader to use
     * @param includeGlobal {@code true} to include globally registered providers, {@code false} to exclude them
     * @return the {@code Iterator} of {@code SaslClientFactory}s
     */
    public static Iterator getSaslClientFactories(ClassLoader classLoader, boolean includeGlobal) {
        return getFactories(SaslClientFactory.class, classLoader, includeGlobal);
    }

    /**
     * Returns an iterator of all of the registered {@code SaslClientFactory}s where the order is based on the
     * order of the Provider registration and/or class path order.
     *
     * @return the {@code Iterator} of {@code SaslClientFactory}s
     */
    public static Iterator getSaslClientFactories() {
        return getFactories(SaslClientFactory.class, null, true);
    }

    private static  Iterator getFactories(Class type, ClassLoader classLoader, boolean includeGlobal) {
        Set factories = new LinkedHashSet();
        final ServiceLoader loader = ServiceLoader.load(type, classLoader);
        for (T factory : loader) {
            factories.add(factory);
        }
        if (includeGlobal) {
            Set loadedClasses = new HashSet();
            final String filter = type.getSimpleName() + ".";

            Provider[] providers = Security.getProviders();
            final SecurityManager sm = System.getSecurityManager();
            for (final Provider currentProvider : providers) {
                final ClassLoader cl = sm == null ? currentProvider.getClass().getClassLoader() : doPrivileged(new PrivilegedAction() {
                    public ClassLoader run() {
                        return currentProvider.getClass().getClassLoader();
                    }
                });
                for (Object currentKey : currentProvider.keySet()) {
                    if (currentKey instanceof String &&
                            ((String) currentKey).startsWith(filter) &&
                            ((String) currentKey).indexOf(' ') < 0) {
                        String className = currentProvider.getProperty((String) currentKey);
                        if (className != null && loadedClasses.add(className)) {
                            try {
                                factories.add(Class.forName(className, true, cl).asSubclass(type).newInstance());
                            } catch (ClassNotFoundException e) {
                            } catch (ClassCastException e) {
                            } catch (InstantiationException e) {
                            } catch (IllegalAccessException e) {
                            }
                        }
                    }
                }
            }
        }
        return factories.iterator();
    }

    /**
     * Evaluate a sasl challenge.  If the result is {@code false} then the negotiation is not yet complete and the data
     * written into the destination buffer needs to be sent to the server as a response.  If the result is {@code true}
     * then negotiation was successful and no response needs to be sent to the server.
     * 

* The {@code source} buffer should have its position and remaining length set to encompass exactly one SASL * message. The SASL message itself does not encode any length information so it is up to the protocol implementer * to ensure that the message is properly framed. * * @param client the SASL client to use to evaluate the challenge message * @param destination the destination buffer into which the response message should be written, if any * @param source the source buffer from which the challenge message should be read * @return {@code true} if negotiation is complete and successful, {@code false} otherwise * @throws SaslException if negotiation failed or another error occurred */ public static boolean evaluateChallenge(SaslClient client, ByteBuffer destination, ByteBuffer source) throws SaslException { final byte[] result; result = evaluateChallenge(client, source); if (result != null) { if (destination == null) { throw msg.extraChallenge(); } destination.put(result); return false; } else { return true; } } /** * Evaluate a sasl challenge. If the result is non-{@code null} then the negotiation is not yet complete and the data * returned needs to be sent to the server as a response. If the result is {@code null} * then negotiation was successful and no response needs to be sent to the server. *

* The {@code source} buffer should have its position and remaining length set to encompass exactly one SASL * message. The SASL message itself does not encode any length information so it is up to the protocol implementer * to ensure that the message is properly framed. * * @param client the SASL client to use to evaluate the challenge message * @param source the source buffer from which the challenge message should be read * @return {@code null} if negotiation is complete and successful, or the response otherwise * @throws SaslException if negotiation failed or another error occurred */ public static byte[] evaluateChallenge(SaslClient client, ByteBuffer source) throws SaslException { return client.evaluateChallenge(Buffers.take(source)); } /** * Evaluate a sasl response. If the result is {@code false} then the negotiation is not yet complete and the data * written into the destination buffer needs to be sent to the server as a response. If the result is {@code true} * then negotiation was successful and no response needs to be sent to the client (other than a successful completion * message, depending on the protocol). *

* The {@code source} buffer should have its position and remaining length set to encompass exactly one SASL * message. The SASL message itself does not encode any length information so it is up to the protocol implementer * to ensure that the message is properly framed. * * @param server the SASL server to use to evaluate the response message * @param destination the destination buffer into which the response message should be written, if any * @param source the source buffer from which the response message should be read * @return {@code true} if negotiation is complete and successful, {@code false} otherwise * @throws SaslException if negotiation failed or another error occurred */ public static boolean evaluateResponse(SaslServer server, ByteBuffer destination, ByteBuffer source) throws SaslException { final byte[] result; result = evaluateResponse(server, source); if (result != null) { if (destination == null) { throw msg.extraResponse(); } destination.put(result); return server.isComplete(); } else { return true; } } /** * Evaluate a sasl response. If the result is non-{@code null} then the negotiation is not yet complete and the data * returned needs to be sent to the server as a response. If the result is {@code null} * then negotiation was successful and no response needs to be sent to the client (other than a successful completion * message, depending on the protocol). *

* The {@code source} buffer should have its position and remaining length set to encompass exactly one SASL * message. The SASL message itself does not encode any length information so it is up to the protocol implementer * to ensure that the message is properly framed. * * @param server the SASL server to use to evaluate the response message * @param source the source buffer from which the response message should be read * @return {@code true} if negotiation is complete and successful, {@code false} otherwise * @throws SaslException if negotiation failed or another error occurred */ public static byte[] evaluateResponse(final SaslServer server, final ByteBuffer source) throws SaslException { return server.evaluateResponse(source.hasRemaining() ? Buffers.take(source) : EMPTY_BYTES); } /** * Wrap a message. Wrapping occurs from the source buffer to the destination idea. *

* The {@code source} buffer should have its position and remaining length set to encompass exactly one SASL * message (without the length field). The SASL message itself does not encode any length information so it is up * to the protocol implementer to ensure that the message is properly framed. * * @param client the SASL client to wrap with * @param destination the buffer into which bytes should be written * @param source the buffers from which bytes should be read * @throws SaslException if a SASL error occurs * @see SaslClient#wrap(byte[], int, int) */ public static void wrap(SaslClient client, ByteBuffer destination, ByteBuffer source) throws SaslException { destination.put(wrap(client, source)); } /** * Wrap a message. Wrapping occurs from the source buffer to the destination idea. *

* The {@code source} buffer should have its position and remaining length set to encompass exactly one SASL * message (without the length field). The SASL message itself does not encode any length information so it is up * to the protocol implementer to ensure that the message is properly framed. * * @param client the SASL client to wrap with * @param source the buffers from which bytes should be read * @return the wrap result * @throws SaslException if a SASL error occurs * @see SaslClient#wrap(byte[], int, int) */ public static byte[] wrap(final SaslClient client, final ByteBuffer source) throws SaslException { final byte[] result; final int len = source.remaining(); if (len == 0) { result = client.wrap(EMPTY_BYTES, 0, len); } else if (source.hasArray()) { final byte[] array = source.array(); final int offs = source.arrayOffset(); source.position(source.position() + len); result = client.wrap(array, offs, len); } else { result = client.wrap(Buffers.take(source, len), 0, len); } return result; } /** * Wrap a message. Wrapping occurs from the source buffer to the destination idea. *

* The {@code source} buffer should have its position and remaining length set to encompass exactly one SASL * message (without the length field). The SASL message itself does not encode any length information so it is up * to the protocol implementer to ensure that the message is properly framed. * * @param server the SASL server to wrap with * @param destination the buffer into which bytes should be written * @param source the buffers from which bytes should be read * @throws SaslException if a SASL error occurs * @see SaslServer#wrap(byte[], int, int) */ public static void wrap(SaslServer server, ByteBuffer destination, ByteBuffer source) throws SaslException { destination.put(wrap(server, source)); } /** * Wrap a message. Wrapping occurs from the source buffer to the destination idea. *

* The {@code source} buffer should have its position and remaining length set to encompass exactly one SASL * message (without the length field). The SASL message itself does not encode any length information so it is up * to the protocol implementer to ensure that the message is properly framed. * * @param server the SASL server to wrap with * @param source the buffers from which bytes should be read * @return the wrap result * @throws SaslException if a SASL error occurs * @see SaslServer#wrap(byte[], int, int) */ public static byte[] wrap(final SaslServer server, final ByteBuffer source) throws SaslException { final byte[] result; final int len = source.remaining(); if (len == 0) { result = server.wrap(EMPTY_BYTES, 0, len); } else if (source.hasArray()) { final byte[] array = source.array(); final int offs = source.arrayOffset(); source.position(source.position() + len); result = server.wrap(array, offs, len); } else { result = server.wrap(Buffers.take(source, len), 0, len); } return result; } /** * Unwrap a message. Unwrapping occurs from the source buffer to the destination idea. *

* The {@code source} buffer should have its position and remaining length set to encompass exactly one SASL * message (without the length field). The SASL message itself does not encode any length information so it is up * to the protocol implementer to ensure that the message is properly framed. * * @param client the SASL client to unwrap with * @param destination the buffer into which bytes should be written * @param source the buffers from which bytes should be read * @throws SaslException if a SASL error occurs * @see SaslClient#unwrap(byte[], int, int) */ public static void unwrap(SaslClient client, ByteBuffer destination, ByteBuffer source) throws SaslException { destination.put(unwrap(client, source)); } /** * Unwrap a message. Unwrapping occurs from the source buffer to the destination idea. *

* The {@code source} buffer should have its position and remaining length set to encompass exactly one SASL * message (without the length field). The SASL message itself does not encode any length information so it is up * to the protocol implementer to ensure that the message is properly framed. * * @param client the SASL client to unwrap with * @param source the buffers from which bytes should be read * @return the wrap result * @throws SaslException if a SASL error occurs * @see SaslClient#unwrap(byte[], int, int) */ public static byte[] unwrap(final SaslClient client, final ByteBuffer source) throws SaslException { final byte[] result; final int len = source.remaining(); if (len == 0) { result = client.unwrap(EMPTY_BYTES, 0, len); } else if (source.hasArray()) { final byte[] array = source.array(); final int offs = source.arrayOffset(); source.position(source.position() + len); result = client.unwrap(array, offs, len); } else { result = client.unwrap(Buffers.take(source, len), 0, len); } return result; } /** * Unwrap a message. Unwrapping occurs from the source buffer to the destination idea. *

* The {@code source} buffer should have its position and remaining length set to encompass exactly one SASL * message (without the length field). The SASL message itself does not encode any length information so it is up * to the protocol implementer to ensure that the message is properly framed. * * @param server the SASL server to unwrap with * @param destination the buffer into which bytes should be written * @param source the buffers from which bytes should be read * @throws SaslException if a SASL error occurs * @see SaslServer#unwrap(byte[], int, int) */ public static void unwrap(SaslServer server, ByteBuffer destination, ByteBuffer source) throws SaslException { destination.put(unwrap(server, source)); } /** * Unwrap a message. Unwrapping occurs from the source buffer to the destination idea. *

* The {@code source} buffer should have its position and remaining length set to encompass exactly one SASL * message (without the length field). The SASL message itself does not encode any length information so it is up * to the protocol implementer to ensure that the message is properly framed. * * @param server the SASL server to unwrap with * @param source the buffers from which bytes should be read * @return the wrap result * @throws SaslException if a SASL error occurs * @see SaslServer#unwrap(byte[], int, int) */ public static byte[] unwrap(final SaslServer server, final ByteBuffer source) throws SaslException { final byte[] result; final int len = source.remaining(); if (len == 0) { result = server.unwrap(EMPTY_BYTES, 0, len); } else if (source.hasArray()) { final byte[] array = source.array(); final int offs = source.arrayOffset(); source.position(source.position() + len); result = server.unwrap(array, offs, len); } else { result = server.unwrap(Buffers.take(source, len), 0, len); } return result; } /** * Create a SASL property map from an XNIO option map. * * @param optionMap the option map * @param secure {@code true} if the channel is secure, {@code false} otherwise * @return the property map */ public static Map createPropertyMap(final OptionMap optionMap, final boolean secure) { final Map propertyMap = new HashMap(); add(optionMap, Options.SASL_POLICY_FORWARD_SECRECY, propertyMap, Sasl.POLICY_FORWARD_SECRECY, null); add(optionMap, Options.SASL_POLICY_NOACTIVE, propertyMap, Sasl.POLICY_NOACTIVE, null); add(optionMap, Options.SASL_POLICY_NOANONYMOUS, propertyMap, Sasl.POLICY_NOANONYMOUS, Boolean.TRUE); add(optionMap, Options.SASL_POLICY_NODICTIONARY, propertyMap, Sasl.POLICY_NODICTIONARY, null); add(optionMap, Options.SASL_POLICY_NOPLAINTEXT, propertyMap, Sasl.POLICY_NOPLAINTEXT, Boolean.valueOf(! secure)); add(optionMap, Options.SASL_POLICY_PASS_CREDENTIALS, propertyMap, Sasl.POLICY_PASS_CREDENTIALS, null); add(optionMap, Options.SASL_REUSE, propertyMap, Sasl.REUSE, null); add(optionMap, Options.SASL_SERVER_AUTH, propertyMap, Sasl.SERVER_AUTH, null); addQopList(optionMap, Options.SASL_QOP, propertyMap, Sasl.QOP); add(optionMap, Options.SASL_STRENGTH, propertyMap, Sasl.STRENGTH, null); addSaslProperties(optionMap, Options.SASL_PROPERTIES, propertyMap); return propertyMap; } private static void add(OptionMap optionMap, Option option, Map map, String propName, T defaultVal) { final Object value = optionMap.get(option, defaultVal); if (value != null) map.put(propName, value.toString().toLowerCase(Locale.US)); } private static void addQopList(OptionMap optionMap, Option> option, Map map, String propName) { final Sequence value = optionMap.get(option); if (value == null) return; final Sequence seq = value; final StringBuilder builder = new StringBuilder(); final Iterator iterator = seq.iterator(); if (!iterator.hasNext()) { return; } else do { builder.append(iterator.next().getString()); if (iterator.hasNext()) { builder.append(','); } } while (iterator.hasNext()); map.put(propName, builder.toString()); } private static void addSaslProperties(OptionMap optionMap, Option> option, Map map) { final Sequence value = optionMap.get(option); if (value == null) return; for (Property current : value) { map.put(current.getKey(), current.getValue()); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy