1 //
2 // ========================================================================
3 // Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
8 //
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
11 //
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
14 //
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
17 //
18
19 package org.eclipse.jetty.server.handler;
20
21 import java.io.IOException;
22 import java.net.InetSocketAddress;
23 import java.util.Collections;
24 import java.util.List;
25 import java.util.Map;
26
27 import javax.servlet.ServletException;
28 import javax.servlet.http.HttpServletRequest;
29 import javax.servlet.http.HttpServletResponse;
30
31 import org.eclipse.jetty.http.HttpStatus;
32 import org.eclipse.jetty.http.PathMap;
33 import org.eclipse.jetty.io.EndPoint;
34 import org.eclipse.jetty.server.HttpChannel;
35 import org.eclipse.jetty.server.Request;
36 import org.eclipse.jetty.util.IPAddressMap;
37 import org.eclipse.jetty.util.log.Log;
38 import org.eclipse.jetty.util.log.Logger;
39
40
41 /**
42 * IP Access Handler
43 * <p>
44 * Controls access to the wrapped handler by the real remote IP. Control is provided
45 * by white/black lists that include both internet addresses and URIs. This handler
46 * uses the real internet address of the connection, not one reported in the forwarded
47 * for headers, as this cannot be as easily forged.
48 * <p>
49 * Typically, the black/white lists will be used in one of three modes:
50 * <ul>
51 * <li>Blocking a few specific IPs/URLs by specifying several black list entries.
52 * <li>Allowing only some specific IPs/URLs by specifying several white lists entries.
53 * <li>Allowing a general range of IPs/URLs by specifying several general white list
54 * entries, that are then further refined by several specific black list exceptions
55 * </ul>
56 * <p>
57 * An empty white list is treated as match all. If there is at least one entry in
58 * the white list, then a request must match a white list entry. Black list entries
59 * are always applied, so that even if an entry matches the white list, a black list
60 * entry will override it.
61 * <p>
62 * Internet addresses may be specified as absolute address or as a combination of
63 * four octet wildcard specifications (a.b.c.d) that are defined as follows.
64 * </p>
65 * <pre>
66 * nnn - an absolute value (0-255)
67 * mmm-nnn - an inclusive range of absolute values,
68 * with following shorthand notations:
69 * nnn- => nnn-255
70 * -nnn => 0-nnn
71 * - => 0-255
72 * a,b,... - a list of wildcard specifications
73 * </pre>
74 * <p>
75 * Internet address specification is separated from the URI pattern using the "|" (pipe)
76 * character. URI patterns follow the servlet specification for simple * prefix and
77 * suffix wild cards (e.g. /, /foo, /foo/bar, /foo/bar/*, *.baz).
78 * <p>
79 * Earlier versions of the handler used internet address prefix wildcard specification
80 * to define a range of the internet addresses (e.g. 127., 10.10., 172.16.1.).
81 * They also used the first "/" character of the URI pattern to separate it from the
82 * internet address. Both of these features have been deprecated in the current version.
83 * <p>
84 * Examples of the entry specifications are:
85 * <ul>
86 * <li>10.10.1.2 - all requests from IP 10.10.1.2
87 * <li>10.10.1.2|/foo/bar - all requests from IP 10.10.1.2 to URI /foo/bar
88 * <li>10.10.1.2|/foo/* - all requests from IP 10.10.1.2 to URIs starting with /foo/
89 * <li>10.10.1.2|*.html - all requests from IP 10.10.1.2 to URIs ending with .html
90 * <li>10.10.0-255.0-255 - all requests from IPs within 10.10.0.0/16 subnet
91 * <li>10.10.0-.-255|/foo/bar - all requests from IPs within 10.10.0.0/16 subnet to URI /foo/bar
92 * <li>10.10.0-3,1,3,7,15|/foo/* - all requests from IPs addresses with last octet equal
93 * to 1,3,7,15 in subnet 10.10.0.0/22 to URIs starting with /foo/
94 * </ul>
95 * <p>
96 * Earlier versions of the handler used internet address prefix wildcard specification
97 * to define a range of the internet addresses (e.g. 127., 10.10., 172.16.1.).
98 * They also used the first "/" character of the URI pattern to separate it from the
99 * internet address. Both of these features have been deprecated in the current version.
100 */
101 public class IPAccessHandler extends HandlerWrapper
102 {
103 private static final Logger LOG = Log.getLogger(IPAccessHandler.class);
104
105 IPAddressMap<PathMap> _white = new IPAddressMap<PathMap>();
106 IPAddressMap<PathMap> _black = new IPAddressMap<PathMap>();
107
108 /* ------------------------------------------------------------ */
109 /**
110 * Creates new handler object
111 */
112 public IPAccessHandler()
113 {
114 super();
115 }
116
117 /* ------------------------------------------------------------ */
118 /**
119 * Creates new handler object and initializes white- and black-list
120 *
121 * @param white array of whitelist entries
122 * @param black array of blacklist entries
123 */
124 public IPAccessHandler(String[] white, String []black)
125 {
126 super();
127
128 if (white != null && white.length > 0)
129 setWhite(white);
130 if (black != null && black.length > 0)
131 setBlack(black);
132 }
133
134 /* ------------------------------------------------------------ */
135 /**
136 * Add a whitelist entry to an existing handler configuration
137 *
138 * @param entry new whitelist entry
139 */
140 public void addWhite(String entry)
141 {
142 add(entry, _white);
143 }
144
145 /* ------------------------------------------------------------ */
146 /**
147 * Add a blacklist entry to an existing handler configuration
148 *
149 * @param entry new blacklist entry
150 */
151 public void addBlack(String entry)
152 {
153 add(entry, _black);
154 }
155
156 /* ------------------------------------------------------------ */
157 /**
158 * Re-initialize the whitelist of existing handler object
159 *
160 * @param entries array of whitelist entries
161 */
162 public void setWhite(String[] entries)
163 {
164 set(entries, _white);
165 }
166
167 /* ------------------------------------------------------------ */
168 /**
169 * Re-initialize the blacklist of existing handler object
170 *
171 * @param entries array of blacklist entries
172 */
173 public void setBlack(String[] entries)
174 {
175 set(entries, _black);
176 }
177
178 /* ------------------------------------------------------------ */
179 /**
180 * Checks the incoming request against the whitelist and blacklist
181 *
182 * @see org.eclipse.jetty.server.handler.HandlerWrapper#handle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
183 */
184 @Override
185 public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
186 {
187 // Get the real remote IP (not the one set by the forwarded headers (which may be forged))
188 HttpChannel channel = baseRequest.getHttpChannel();
189 if (channel!=null)
190 {
191 EndPoint endp=channel.getEndPoint();
192 if (endp!=null)
193 {
194 InetSocketAddress address = endp.getRemoteAddress();
195 if (address!=null && !isAddrUriAllowed(address.getHostString(),baseRequest.getPathInfo()))
196 {
197 response.sendError(HttpStatus.FORBIDDEN_403);
198 baseRequest.setHandled(true);
199 return;
200 }
201 }
202 }
203
204 getHandler().handle(target,baseRequest, request, response);
205 }
206
207
208 /* ------------------------------------------------------------ */
209 /**
210 * Helper method to parse the new entry and add it to
211 * the specified address pattern map.
212 *
213 * @param entry new entry
214 * @param patternMap target address pattern map
215 */
216 protected void add(String entry, IPAddressMap<PathMap> patternMap)
217 {
218 if (entry != null && entry.length() > 0)
219 {
220 boolean deprecated = false;
221 int idx;
222 if (entry.indexOf('|') > 0 )
223 {
224 idx = entry.indexOf('|');
225 }
226 else
227 {
228 idx = entry.indexOf('/');
229 deprecated = (idx >= 0);
230 }
231
232 String addr = idx > 0 ? entry.substring(0,idx) : entry;
233 String path = idx > 0 ? entry.substring(idx) : "/*";
234
235 if (addr.endsWith("."))
236 deprecated = true;
237 if (path!=null && (path.startsWith("|") || path.startsWith("/*.")))
238 path=path.substring(1);
239
240 PathMap pathMap = patternMap.get(addr);
241 if (pathMap == null)
242 {
243 pathMap = new PathMap(true);
244 patternMap.put(addr,pathMap);
245 }
246 if (path != null && !"".equals(path))
247 pathMap.put(path,path);
248
249 if (deprecated)
250 LOG.debug(toString() +" - deprecated specification syntax: "+entry);
251 }
252 }
253
254 /* ------------------------------------------------------------ */
255 /**
256 * Helper method to process a list of new entries and replace
257 * the content of the specified address pattern map
258 *
259 * @param entries new entries
260 * @param patternMap target address pattern map
261 */
262 protected void set(String[] entries, IPAddressMap<PathMap> patternMap)
263 {
264 patternMap.clear();
265
266 if (entries != null && entries.length > 0)
267 {
268 for (String addrPath:entries)
269 {
270 add(addrPath, patternMap);
271 }
272 }
273 }
274
275 /* ------------------------------------------------------------ */
276 /**
277 * Check if specified request is allowed by current IPAccess rules.
278 *
279 * @param addr internet address
280 * @param path context path
281 * @return true if request is allowed
282 *
283 */
284 protected boolean isAddrUriAllowed(String addr, String path)
285 {
286 if (_white.size()>0)
287 {
288 boolean match = false;
289
290 Object whiteObj = _white.getLazyMatches(addr);
291 if (whiteObj != null)
292 {
293 List whiteList = (whiteObj instanceof List) ? (List)whiteObj : Collections.singletonList(whiteObj);
294
295 for (Object entry: whiteList)
296 {
297 PathMap pathMap = ((Map.Entry<String,PathMap>)entry).getValue();
298 if (match = (pathMap!=null && (pathMap.size()==0 || pathMap.match(path)!=null)))
299 break;
300 }
301 }
302
303 if (!match)
304 return false;
305 }
306
307 if (_black.size() > 0)
308 {
309 Object blackObj = _black.getLazyMatches(addr);
310 if (blackObj != null)
311 {
312 List blackList = (blackObj instanceof List) ? (List)blackObj : Collections.singletonList(blackObj);
313
314 for (Object entry: blackList)
315 {
316 PathMap pathMap = ((Map.Entry<String,PathMap>)entry).getValue();
317 if (pathMap!=null && (pathMap.size()==0 || pathMap.match(path)!=null))
318 return false;
319 }
320 }
321 }
322
323 return true;
324 }
325
326 /* ------------------------------------------------------------ */
327 /**
328 * Dump the handler configuration
329 */
330 public String dump()
331 {
332 StringBuilder buf = new StringBuilder();
333
334 buf.append(toString());
335 buf.append(" WHITELIST:\n");
336 dump(buf, _white);
337 buf.append(toString());
338 buf.append(" BLACKLIST:\n");
339 dump(buf, _black);
340
341 return buf.toString();
342 }
343
344 /* ------------------------------------------------------------ */
345 /**
346 * Dump a pattern map into a StringBuilder buffer
347 *
348 * @param buf buffer
349 * @param patternMap pattern map to dump
350 */
351 protected void dump(StringBuilder buf, IPAddressMap<PathMap> patternMap)
352 {
353 for (String addr: patternMap.keySet())
354 {
355 for (Object path: ((PathMap)patternMap.get(addr)).values())
356 {
357 buf.append("# ");
358 buf.append(addr);
359 buf.append("|");
360 buf.append(path);
361 buf.append("\n");
362 }
363 }
364 }
365 }