/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Portions Copyright 2007 Sun Microsystems, Inc. */ package org.opends.server.authorization.dseecompat; import static org.opends.server.messages.AciMessages.*; import static org.opends.server.authorization.dseecompat.Aci.*; import static org.opends.server.messages.MessageHandler.getMessage; import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.HashSet; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * This class represents a ip bind rule keyword. */ public class IpCriteria implements KeywordBindRule { private EnumBindRuleType type=null; // private token to express that any address is accepted private final static String ANY_ADDRESSES = "ALL"; private boolean matchAnyAddress = false; private IpBitsNetworkCriteria[] ipBitsCriteria = null; private IpMaskNetworkCriteria[] ipMaskCriteria = null; private static final String valueRegex = "([^," + ZERO_OR_MORE_WHITESPACE + "]+)"; private static final String valuesRegex = valueRegex + ZERO_OR_MORE_WHITESPACE + "(," + ZERO_OR_MORE_WHITESPACE + valueRegex + ")*"; /* * TODO Verifiy IpCriteria constructor adheres to DS 5.2 ip keyword * syntax. * * Based on the contents of the constructor, it doesn't appear that the * IpCriteria class uses the same set of allowed values as DS 5.2 does * with the "ip" keyword (as documented at * http://docs.sun.com/source/817-7613/aci.html#wp20242). Was that the * intention? It looks like it doesn't allow asterisks as wild-ccards or * plus signs to specify netmasks (although it appears that it expects a * slash to be used if you want a netmask). While I don't mind allowing * alternate formats (e.g., CIDR-style addresses), we can't drop support * for the existing ones if we're trying to maintain compatibility. */ /** * Constructor that creates an IpCriteria from an array of values and * an enumeration bind rule type. * @param values An array of address values. * @param type An enumeration of the bind rule type. * @throws UnknownHostException If the host address cannot be resolved to * a hostname. * @throws AciException If a part of the address is invalid. * @throws IndexOutOfBoundsException If an index is incremented past an * array bounds when copying or evaluating an address. */ public IpCriteria(String[] values, EnumBindRuleType type) throws UnknownHostException, IndexOutOfBoundsException, AciException { IpBitsNetworkCriteria[] ipBitsCriteria_2 = null; IpMaskNetworkCriteria[] ipMaskCriteria_2 = null; try { for (String value : values) { if (value.equalsIgnoreCase(ANY_ADDRESSES)) { matchAnyAddress = true; continue; } // determine what format it is to instantiate // the right criteria object int slash = value.indexOf("/"); if (slash == -1) { // simple raw IP address IpBitsNetworkCriteria newInstance; if (InetAddress.getByName(value) instanceof Inet6Address) { newInstance = new IpBitsNetworkCriteria(value, 128); } else { newInstance = new IpBitsNetworkCriteria(value, 32); } if (ipBitsCriteria_2 == null) { ipBitsCriteria_2 = new IpBitsNetworkCriteria[1]; } else { IpBitsNetworkCriteria[] newIpBitsCriteria = new IpBitsNetworkCriteria[ipBitsCriteria_2.length + 1]; System.arraycopy(ipBitsCriteria_2, 0, newIpBitsCriteria, 0, ipBitsCriteria_2.length); ipBitsCriteria_2 = newIpBitsCriteria; } ipBitsCriteria_2[ipBitsCriteria_2.length - 1] = newInstance; } else { // Extract data following the / and figure out whether it // is a bit number or a mask try { int bits = Integer.parseInt(value.substring(slash + 1)); // Well, no exception, so this is a bit // Let's instantiate the corresponding criterion if (ipBitsCriteria_2 == null) { ipBitsCriteria_2 = new IpBitsNetworkCriteria[1]; } else { IpBitsNetworkCriteria[] newIpBitsCriteria = new IpBitsNetworkCriteria[ipBitsCriteria_2.length + 1]; System.arraycopy(ipBitsCriteria_2, 0, newIpBitsCriteria, 0, ipBitsCriteria_2.length); ipBitsCriteria_2 = newIpBitsCriteria; } ipBitsCriteria_2[ipBitsCriteria_2.length - 1] = new IpBitsNetworkCriteria(value. substring(0, slash), bits); } catch (IndexOutOfBoundsException e1) { throw e1; } catch (Exception e2) { // Looks like this is a network mask. if (ipMaskCriteria_2 == null) { ipMaskCriteria_2 = new IpMaskNetworkCriteria[1]; } else { IpMaskNetworkCriteria[] newIpMaskCriteria = new IpMaskNetworkCriteria[ipMaskCriteria_2.length + 1]; System.arraycopy(ipMaskCriteria_2, 0, newIpMaskCriteria, 0, ipMaskCriteria_2.length); ipMaskCriteria_2 = newIpMaskCriteria; } try { ipMaskCriteria_2[ipMaskCriteria_2.length - 1] = new IpMaskNetworkCriteria(value. substring(0, slash), value.substring(slash + 1)); } catch (IndexOutOfBoundsException e3) { throw e3; } } } } } catch (UnknownHostException ue) { throw ue; } ipBitsCriteria = ipBitsCriteria_2; ipMaskCriteria = ipMaskCriteria_2; this.type=type; } /** * Return the ipBitsNetworkCriteria of this IpCriteria. * @return Returns the ipBitsNetworkCriteria. */ public IpBitsNetworkCriteria[] getIpBitsNetworkCriteria() { return ipBitsCriteria; } /** * Return the ipMaskNetworkCriteria of this IpCriteria. * @return Returns the ipMaskNetworkCriteria. */ public IpMaskNetworkCriteria[] getIpMaskNetworkCriteria() { return ipMaskCriteria; } /** * Compare an IP address with the network rule. * * @param theSourceAddress IP source address of the client. * @return true if client matches the network rule or * false if they may not. */ public boolean match (InetAddress theSourceAddress) { if (matchAnyAddress){ return true; } if (ipMaskCriteria != null) { for (IpMaskNetworkCriteria anIpMaskCriteria : ipMaskCriteria) { if (anIpMaskCriteria.match(theSourceAddress)) { return true; } } } if (ipBitsCriteria != null) { for (IpBitsNetworkCriteria anIpBitsCriteria : ipBitsCriteria) { if (anIpBitsCriteria.match(theSourceAddress)) { return true; } } } return (ipBitsCriteria == null) && (ipMaskCriteria == null); } /** * Decode an expression string representing a ip keyword bind rule * expression. * @param expr A string representing the expression. * @param type An enumeration representing the bind rule type. * @return An keyword bind rule that can be used to evaluate the * expression. * @throws AciException If the expression string is invalid. */ public static KeywordBindRule decode(String expr, EnumBindRuleType type) throws AciException { if (!Pattern.matches(valuesRegex, expr)) { int msgID = MSGID_ACI_SYNTAX_INVALID_IP_EXPRESSION; String message = getMessage(msgID, expr); throw new AciException(msgID, message); } int valuePos = 1; Pattern valuePattern = Pattern.compile(valueRegex); Matcher valueMatcher = valuePattern.matcher(expr); HashSet values = new HashSet(); while (valueMatcher.find()) { String value = valueMatcher.group(valuePos); values.add(value); } IpCriteria ipCriteria; String[] strValues = null; if (!values.isEmpty()) { strValues = values.toArray(new String[values.size()]); } try { ipCriteria = new IpCriteria(strValues, type); } catch (Exception e) { int msgID = MSGID_ACI_SYNTAX_INVALID_IP_CRITERIA_DECODE; String message = getMessage(msgID, e.getMessage()); throw new AciException(msgID, message); } return ipCriteria; } /** * Evaluate the evaluation context against this ip criteria. * @param evalCtx An evaluation context to use. * @return An enumeration evaluation result. */ public EnumEvalResult evaluate(AciEvalContext evalCtx) { EnumEvalResult matched=EnumEvalResult.FALSE; if(match(evalCtx.getRemoteAddress())) matched=EnumEvalResult.TRUE; return matched.getRet(type, false); } }