Add rebinding protection
This commit is contained in:
@ -43,7 +43,7 @@
|
||||
|
||||
<target name="war" depends="compile" >
|
||||
<war destfile="build/jsonrpc.war" webxml="web.xml" >
|
||||
<classes dir="./build/obj" excludes="net/i2p/i2pcontrol/I2PControlController.class" />
|
||||
<classes dir="./build/obj" excludes="net/i2p/i2pcontrol/I2PControlController.class net/i2p/i2pcontrol/HostCheckHandler.class" />
|
||||
</war>
|
||||
</target>
|
||||
|
||||
|
127
src/java/net/i2p/i2pcontrol/HostCheckHandler.java
Normal file
127
src/java/net/i2p/i2pcontrol/HostCheckHandler.java
Normal file
@ -0,0 +1,127 @@
|
||||
package net.i2p.i2pcontrol;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import org.apache.http.conn.util.InetAddressUtils;
|
||||
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
|
||||
/**
|
||||
* Block certain Host headers to prevent DNS rebinding attacks.
|
||||
*
|
||||
* This Handler wraps the ContextHandlerCollection, which handles
|
||||
* all the webapps (not just routerconsole).
|
||||
* Therefore, this protects all the webapps.
|
||||
*
|
||||
* @since 0.12 copied from routerconsole
|
||||
*/
|
||||
public class HostCheckHandler extends HandlerWrapper
|
||||
{
|
||||
private final I2PAppContext _context;
|
||||
private final Set<String> _listenHosts;
|
||||
|
||||
/**
|
||||
* MUST call setListenHosts() afterwards.
|
||||
*/
|
||||
public HostCheckHandler(I2PAppContext ctx) {
|
||||
super();
|
||||
_context = ctx;
|
||||
_listenHosts = new HashSet<String>(8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the legal hosts.
|
||||
* Not synched. Call this BEFORE starting.
|
||||
* If empty, all are allowed.
|
||||
*
|
||||
* @param hosts contains hostnames or IPs. But we allow all IPs anyway.
|
||||
*/
|
||||
public void setListenHosts(Set<String> hosts) {
|
||||
_listenHosts.clear();
|
||||
_listenHosts.addAll(hosts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Block by Host header, pass everything else to the delegate.
|
||||
*/
|
||||
public void handle(String pathInContext,
|
||||
Request baseRequest,
|
||||
HttpServletRequest httpRequest,
|
||||
HttpServletResponse httpResponse)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
|
||||
String host = httpRequest.getHeader("Host");
|
||||
if (!allowHost(host)) {
|
||||
Log log = _context.logManager().getLog(HostCheckHandler.class);
|
||||
host = DataHelper.stripHTML(getHost(host));
|
||||
String s = "Console request denied.\n" +
|
||||
" To allow access using the hostname \"" + host + "\", add the line \"" +
|
||||
I2PControlController.PROP_ALLOWED_HOSTS + '=' + host +
|
||||
"\" to I2PControl.conf and restart.";
|
||||
log.logAlways(Log.WARN, s);
|
||||
httpResponse.sendError(403, s);
|
||||
return;
|
||||
}
|
||||
|
||||
super.handle(pathInContext, baseRequest, httpRequest, httpResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should we allow a request with this Host header?
|
||||
*
|
||||
* ref: https://en.wikipedia.org/wiki/DNS_rebinding
|
||||
*
|
||||
* @param host the HTTP Host header, null ok
|
||||
* @return true if OK
|
||||
*/
|
||||
private boolean allowHost(String host) {
|
||||
if (host == null)
|
||||
return true;
|
||||
// common cases
|
||||
if (host.equals("127.0.0.1:7650") ||
|
||||
host.equals("localhost:7650"))
|
||||
return true;
|
||||
// all allowed?
|
||||
if (_listenHosts.isEmpty())
|
||||
return true;
|
||||
host = getHost(host);
|
||||
if (_listenHosts.contains(host))
|
||||
return true;
|
||||
// allow all IP addresses
|
||||
if (InetAddressUtils.isIPv4Address(host) || InetAddressUtils.isIPv6Address(host))
|
||||
return true;
|
||||
//System.out.println(host + " not found in " + s);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip [] and port from a host header
|
||||
*
|
||||
* @param host the HTTP Host header non-null
|
||||
*/
|
||||
private static String getHost(String host) {
|
||||
if (host.startsWith("[")) {
|
||||
host = host.substring(1);
|
||||
int brack = host.indexOf(']');
|
||||
if (brack >= 0)
|
||||
host = host.substring(0, brack);
|
||||
} else {
|
||||
int colon = host.indexOf(':');
|
||||
if (colon >= 0)
|
||||
host = host.substring(0, colon);
|
||||
}
|
||||
return host;
|
||||
}
|
||||
}
|
@ -45,6 +45,9 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.KeyStore;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
|
||||
/**
|
||||
@ -73,6 +76,7 @@ public class I2PControlController implements RouterApp {
|
||||
private ClientAppState _state = UNINITIALIZED;
|
||||
// only for main()
|
||||
private static I2PControlController _instance;
|
||||
static final String PROP_ALLOWED_HOSTS = "i2pcontrol.allowedHosts";
|
||||
|
||||
public I2PControlController(RouterContext ctx, ClientAppManager mgr, String args[]) {
|
||||
_appContext = _context = ctx;
|
||||
@ -225,7 +229,29 @@ public class I2PControlController implements RouterApp {
|
||||
|
||||
ServletHandler sh = new ServletHandler();
|
||||
sh.addServletWithMapping(new ServletHolder(new JSONRPC2Servlet(_context, _secMan)), "/");
|
||||
server.getServer().setHandler(sh);
|
||||
HostCheckHandler hch = new HostCheckHandler(_appContext);
|
||||
Set<String> listenHosts = new HashSet<String>(8);
|
||||
// fix up the allowed hosts set (see HostCheckHandler)
|
||||
// empty set says all are valid
|
||||
String address = _conf.getConf("i2pcontrol.listen.address", "127.0.0.1");
|
||||
if (!(address.equals("0.0.0.0") ||
|
||||
address.equals("::") ||
|
||||
address.equals("0:0:0:0:0:0:0:0"))) {
|
||||
listenHosts.add("localhost");
|
||||
listenHosts.add("127.0.0.1");
|
||||
listenHosts.add("::1");
|
||||
listenHosts.add("0:0:0:0:0:0:0:1");
|
||||
String allowed = _conf.getConf(PROP_ALLOWED_HOSTS, "**unset**");
|
||||
if (!allowed.equals("**unset**")) {
|
||||
StringTokenizer tok = new StringTokenizer(allowed, " ,");
|
||||
while (tok.hasMoreTokens()) {
|
||||
listenHosts.add(tok.nextToken());
|
||||
}
|
||||
}
|
||||
}
|
||||
hch.setListenHosts(listenHosts);
|
||||
hch.setHandler(sh);
|
||||
server.getServer().setHandler(hch);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
Reference in New Issue
Block a user