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