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 }