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-2016 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/** This class implements the dns bind rule keyword. */ 033public class DNS implements KeywordBindRule { 034 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 035 036 /** List of patterns to match against. */ 037 private final List<String> patterns; 038 /** The enumeration representing the bind rule type of the DNS rule. */ 039 private final EnumBindRuleType type; 040 041 /** Regular expression group used to match a dns rule. */ 042 private static final String valueRegex = "([a-zA-Z0-9\\.\\-\\*]+)"; 043 /** Regular expression group used to match one or more DNS values. */ 044 private static final String valuesRegExGroup = 045 valueRegex + ZERO_OR_MORE_WHITESPACE + 046 "(," + ZERO_OR_MORE_WHITESPACE + valueRegex + ")*"; 047 048 /** 049 * Create a class representing a dns bind rule keyword. 050 * @param patterns List of dns patterns to match against. 051 * @param type An enumeration representing the bind rule type. 052 */ 053 DNS(List<String> patterns, EnumBindRuleType type) { 054 this.patterns=patterns; 055 this.type=type; 056 } 057 058 /** 059 * Decode an string representing a dns bind rule. 060 * @param expr A string representation of the bind rule. 061 * @param type An enumeration representing the bind rule type. 062 * @return A keyword bind rule class that can be used to evaluate 063 * this bind rule. 064 * @throws AciException If the expression string is invalid. 065 */ 066 public static DNS decode(String expr, EnumBindRuleType type) 067 throws AciException 068 { 069 if (!Pattern.matches(valuesRegExGroup, expr)) { 070 LocalizableMessage message = WARN_ACI_SYNTAX_INVALID_DNS_EXPRESSION.get(expr); 071 throw new AciException(message); 072 } 073 List<String> dns = new LinkedList<>(); 074 int valuePos = 1; 075 Pattern valuePattern = Pattern.compile(valueRegex); 076 Matcher valueMatcher = valuePattern.matcher(expr); 077 while (valueMatcher.find()) { 078 String hn=valueMatcher.group(valuePos); 079 String[] hnArray=hn.split("\\.", -1); 080 for(int i=1, n=hnArray.length; i < n; i++) { 081 if ("*".equals(hnArray[i])) { 082 LocalizableMessage message = 083 WARN_ACI_SYNTAX_INVALID_DNS_WILDCARD.get(expr); 084 throw new AciException(message); 085 } 086 } 087 088 // If the provided hostname does not contain any wildcard 089 // characters, then it must be the canonical hostname for the 090 // associated IP address. If it is not, then it will not match the 091 // intended target, and we should generate a warning message to let 092 // the administrator know about it. If the provided value does not 093 // match the canonical name for the associated IP address, and the 094 // given hostname is "localhost", then we should treat it specially 095 // and also match the canonical hostname. This is necessary because 096 // "localhost" is likely to be very commonly used in these kinds of 097 // rules and on some systems the canonical representation is 098 // configured to be "localhost.localdomain" which may not be known 099 // to the administrator. 100 if (!hn.contains("*")) 101 { 102 try 103 { 104 for (InetAddress addr : InetAddress.getAllByName(hn)) 105 { 106 String canonicalName = addr.getCanonicalHostName(); 107 if (! hn.equalsIgnoreCase(canonicalName)) 108 { 109 if ("localhost".equalsIgnoreCase(hn) 110 && !dns.contains(canonicalName)) 111 { 112 dns.add(canonicalName); 113 114 logger.warn(WARN_ACI_LOCALHOST_DOESNT_MATCH_CANONICAL_VALUE, expr, hn, canonicalName); 115 } 116 else 117 { 118 logger.warn(WARN_ACI_HOSTNAME_DOESNT_MATCH_CANONICAL_VALUE, expr, hn, addr.getHostAddress(), 119 addr.getCanonicalHostName()); 120 } 121 } 122 } 123 } 124 catch (Exception e) 125 { 126 logger.traceException(e); 127 128 logger.warn(WARN_ACI_ERROR_CHECKING_CANONICAL_HOSTNAME, hn, expr, getExceptionMessage(e)); 129 } 130 } 131 132 dns.add(hn); 133 } 134 return new DNS(dns, type); 135 } 136 137 /** 138 * Performs evaluation of dns keyword bind rule using the provided 139 * evaluation context. 140 * @param evalCtx An evaluation context to use in the evaluation. 141 * @return An enumeration evaluation result. 142 */ 143 @Override 144 public EnumEvalResult evaluate(AciEvalContext evalCtx) { 145 EnumEvalResult matched=EnumEvalResult.FALSE; 146 String[] remoteHost = evalCtx.getHostName().split("\\.", -1); 147 for(String p : patterns) { 148 String[] pat = p.split("\\.", -1); 149 if(evalHostName(remoteHost, pat)) { 150 matched=EnumEvalResult.TRUE; 151 break; 152 } 153 } 154 return matched.getRet(type, false); 155 } 156 157 /** 158 * Checks an array containing the remote client's hostname against 159 * patterns specified in the bind rule expression. Wild-cards are 160 * only permitted in the leftmost field and the rest of the domain 161 * name array components must match. A single wild-card matches any 162 * hostname. 163 * @param remoteHostName Array containing components of the remote clients 164 * hostname (split on "."). 165 * @param pat An array containing the pattern specified in 166 * the bind rule expression. The first array slot may be a wild-card "*". 167 * @return True if the remote hostname matches the pattern. 168 */ 169 boolean evalHostName(String[] remoteHostName, String[] pat) { 170 boolean wildCard = "*".equals(pat[0]); 171 //Check if there is a single wild-card. 172 if(pat.length == 1 && wildCard) { 173 return true; 174 } 175 int remoteHnIndex=remoteHostName.length-pat.length; 176 if(remoteHnIndex < 0) { 177 return false; 178 } 179 int patternIndex=0; 180 if(!wildCard) { 181 remoteHnIndex=0; 182 } else { 183 patternIndex=1; 184 remoteHnIndex++; 185 } 186 for(int i=remoteHnIndex ;i<remoteHostName.length;i++) { 187 if(!pat[patternIndex++].equalsIgnoreCase(remoteHostName[i])) { 188 return false; 189 } 190 } 191 return true; 192 } 193 194 @Override 195 public String toString() { 196 final StringBuilder sb = new StringBuilder(); 197 toString(sb); 198 return sb.toString(); 199 } 200 201 @Override 202 public final void toString(StringBuilder buffer) { 203 buffer.append(super.toString()); 204 } 205}