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