001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2008 Sun Microsystems, Inc. 015 * Portions Copyright 2013-2015 ForgeRock AS. 016 */ 017package org.opends.server.authorization.dseecompat; 018 019import static org.opends.messages.AccessControlMessages.*; 020import static org.opends.server.authorization.dseecompat.Aci.*; 021import static org.opends.server.util.StaticUtils.*; 022 023import java.net.InetAddress; 024import java.util.LinkedList; 025import java.util.List; 026import java.util.regex.Matcher; 027import java.util.regex.Pattern; 028 029import org.forgerock.i18n.LocalizableMessage; 030import org.forgerock.i18n.slf4j.LocalizedLogger; 031 032/** 033 * This class implements the dns bind rule keyword. 034 */ 035public class DNS implements KeywordBindRule { 036 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 037 038 /** List of patterns to match against. */ 039 private List<String> patterns; 040 041 /** The enumeration representing the bind rule type of the DNS rule. */ 042 private EnumBindRuleType type; 043 044 /** Regular expression group used to match a dns rule. */ 045 private static final String valueRegex = "([a-zA-Z0-9\\.\\-\\*]+)"; 046 047 /** Regular expression group used to match one or more DNS values. */ 048 private static final String valuesRegExGroup = 049 valueRegex + ZERO_OR_MORE_WHITESPACE + 050 "(," + ZERO_OR_MORE_WHITESPACE + valueRegex + ")*"; 051 052 /** 053 * Create a class representing a dns bind rule keyword. 054 * @param patterns List of dns patterns to match against. 055 * @param type An enumeration representing the bind rule type. 056 */ 057 DNS(List<String> patterns, EnumBindRuleType type) { 058 this.patterns=patterns; 059 this.type=type; 060 } 061 062 /** 063 * Decode an string representing a dns bind rule. 064 * @param expr A string representation of the bind rule. 065 * @param type An enumeration representing the bind rule type. 066 * @return A keyword bind rule class that can be used to evaluate 067 * this bind rule. 068 * @throws AciException If the expression string is invalid. 069 */ 070 public static DNS decode(String expr, EnumBindRuleType type) 071 throws AciException 072 { 073 if (!Pattern.matches(valuesRegExGroup, expr)) { 074 LocalizableMessage message = WARN_ACI_SYNTAX_INVALID_DNS_EXPRESSION.get(expr); 075 throw new AciException(message); 076 } 077 List<String> dns = new LinkedList<>(); 078 int valuePos = 1; 079 Pattern valuePattern = Pattern.compile(valueRegex); 080 Matcher valueMatcher = valuePattern.matcher(expr); 081 while (valueMatcher.find()) { 082 String hn=valueMatcher.group(valuePos); 083 String[] hnArray=hn.split("\\.", -1); 084 for(int i=1, n=hnArray.length; i < n; i++) { 085 if(hnArray[i].equals("*")) { 086 LocalizableMessage message = 087 WARN_ACI_SYNTAX_INVALID_DNS_WILDCARD.get(expr); 088 throw new AciException(message); 089 } 090 } 091 092 // If the provided hostname does not contain any wildcard 093 // characters, then it must be the canonical hostname for the 094 // associated IP address. If it is not, then it will not match the 095 // intended target, and we should generate a warning message to let 096 // the administrator know about it. If the provided value does not 097 // match the canonical name for the associated IP address, and the 098 // given hostname is "localhost", then we should treat it specially 099 // and also match the canonical hostname. This is necessary because 100 // "localhost" is likely to be very commonly used in these kinds of 101 // rules and on some systems the canonical representation is 102 // configured to be "localhost.localdomain" which may not be known 103 // to the administrator. 104 if (!hn.contains("*")) 105 { 106 try 107 { 108 for (InetAddress addr : InetAddress.getAllByName(hn)) 109 { 110 String canonicalName = addr.getCanonicalHostName(); 111 if (! hn.equalsIgnoreCase(canonicalName)) 112 { 113 if (hn.equalsIgnoreCase("localhost") 114 && !dns.contains(canonicalName)) 115 { 116 dns.add(canonicalName); 117 118 logger.warn(WARN_ACI_LOCALHOST_DOESNT_MATCH_CANONICAL_VALUE, expr, hn, canonicalName); 119 } 120 else 121 { 122 logger.warn(WARN_ACI_HOSTNAME_DOESNT_MATCH_CANONICAL_VALUE, expr, hn, addr.getHostAddress(), 123 addr.getCanonicalHostName()); 124 } 125 } 126 } 127 } 128 catch (Exception e) 129 { 130 logger.traceException(e); 131 132 logger.warn(WARN_ACI_ERROR_CHECKING_CANONICAL_HOSTNAME, hn, expr, getExceptionMessage(e)); 133 } 134 } 135 136 dns.add(hn); 137 } 138 return new DNS(dns, type); 139 } 140 141 /** 142 * Performs evaluation of dns keyword bind rule using the provided 143 * evaluation context. 144 * @param evalCtx An evaluation context to use in the evaluation. 145 * @return An enumeration evaluation result. 146 */ 147 @Override 148 public EnumEvalResult evaluate(AciEvalContext evalCtx) { 149 EnumEvalResult matched=EnumEvalResult.FALSE; 150 String[] remoteHost = evalCtx.getHostName().split("\\.", -1); 151 for(String p : patterns) { 152 String[] pat = p.split("\\.", -1); 153 if(evalHostName(remoteHost, pat)) { 154 matched=EnumEvalResult.TRUE; 155 break; 156 } 157 } 158 return matched.getRet(type, false); 159 } 160 161 /** 162 * Checks an array containing the remote client's hostname against 163 * patterns specified in the bind rule expression. Wild-cards are 164 * only permitted in the leftmost field and the rest of the domain 165 * name array components must match. A single wild-card matches any 166 * hostname. 167 * @param remoteHostName Array containing components of the remote clients 168 * hostname (split on "."). 169 * @param pat An array containing the pattern specified in 170 * the bind rule expression. The first array slot may be a wild-card "*". 171 * @return True if the remote hostname matches the pattern. 172 */ 173 boolean evalHostName(String[] remoteHostName, String[] pat) { 174 boolean wildCard=pat[0].equals("*"); 175 //Check if there is a single wild-card. 176 if(pat.length == 1 && wildCard) { 177 return true; 178 } 179 int remoteHnIndex=remoteHostName.length-pat.length; 180 if(remoteHnIndex < 0) { 181 return false; 182 } 183 int patternIndex=0; 184 if(!wildCard) { 185 remoteHnIndex=0; 186 } else { 187 patternIndex=1; 188 remoteHnIndex++; 189 } 190 for(int i=remoteHnIndex ;i<remoteHostName.length;i++) { 191 if(!pat[patternIndex++].equalsIgnoreCase(remoteHostName[i])) { 192 return false; 193 } 194 } 195 return true; 196 } 197 198 /** {@inheritDoc} */ 199 @Override 200 public String toString() { 201 final StringBuilder sb = new StringBuilder(); 202 toString(sb); 203 return sb.toString(); 204 } 205 206 /** {@inheritDoc} */ 207 @Override 208 public final void toString(StringBuilder buffer) { 209 buffer.append(super.toString()); 210 } 211 212}