Author: toad
Date: 2008-09-03 12:31:58 +0000 (Wed, 03 Sep 2008)
New Revision: 22363
Modified:
trunk/freenet/src/freenet/clients/http/ConfigToadlet.java
trunk/freenet/src/freenet/clients/http/PageMaker.java
trunk/freenet/src/freenet/l10n/freenet.l10n.en.properties
trunk/freenet/src/freenet/node/SecurityLevels.java
Log:
Changing the network security level works, and warns the user and gets
confirmation when they're going to do something stupid.
Modified: trunk/freenet/src/freenet/clients/http/ConfigToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/ConfigToadlet.java 2008-09-02
22:43:59 UTC (rev 22362)
+++ trunk/freenet/src/freenet/clients/http/ConfigToadlet.java 2008-09-03
12:31:58 UTC (rev 22363)
@@ -20,6 +20,7 @@
import freenet.l10n.L10n;
import freenet.node.Node;
import freenet.node.NodeClientCore;
+import freenet.node.SecurityLevels;
import freenet.node.SecurityLevels.NETWORK_THREAT_LEVEL;
import freenet.node.useralerts.AbstractUserAlert;
import freenet.node.useralerts.UserAlert;
@@ -105,9 +106,6 @@
@Override
public void handlePost(URI uri, HTTPRequest request, ToadletContext ctx)
throws ToadletContextClosedException, IOException {
- StringBuffer errbuf = new StringBuffer();
- SubConfig[] sc = config.getConfigs();
-
String pass = request.getPartAsString("formPassword", 32);
if((pass == null) || !pass.equals(core.formPassword)) {
MultiValueTable headers = new MultiValueTable();
@@ -116,6 +114,64 @@
return;
}
+ if(request.isPartSet("seclevels")) {
+ // Handle the security level changes.
+
+ HTMLNode pageNode = null;
+ HTMLNode content = null;
+ HTMLNode ul = null;
+ HTMLNode formNode = null;
+ boolean addedWarning = false;
+ String configName =
"security-levels.networkThreatLevel";
+ String confirm =
"security-levels.networkThreatLevel.confirm";
+ String networkThreatLevel =
request.getPartAsString(configName, 128);
+ NETWORK_THREAT_LEVEL newThreatLevel =
SecurityLevels.parseNetworkThreatLevel(networkThreatLevel);
+ if(newThreatLevel != null) {
+ if(newThreatLevel !=
node.securityLevels.getNetworkThreatLevel()) {
+ if(!request.isPartSet(confirm)) {
+ HTMLNode warning =
node.securityLevels.getConfirmWarning(newThreatLevel, confirm);
+ if(warning != null) {
+ if(pageNode == null) {
+ pageNode =
ctx.getPageMaker().getPageNode(L10n.getString("ConfigToadlet.fullTitle", new
String[] { "name" }, new String[] { node.getMyName() }), ctx);
+ content =
ctx.getPageMaker().getContentNode(pageNode);
+ formNode =
ctx.addFormChild(content, ".", "configFormSecLevels");
+ ul =
formNode.addChild("ul", "class", "config");
+ }
+ HTMLNode seclevelGroup
= ul.addChild("li");
+
+
seclevelGroup.addChild("input", new String[] { "type", "name", "value" }, new
String[] { "hidden", configName, networkThreatLevel });
+ HTMLNode infobox =
seclevelGroup.addChild("div", "class", "infobox infobox-information");
+ infobox.addChild("div",
"class", "infobox-header", l10nSec("networkThreatLevelConfirmTitle", "mode",
SecurityLevels.localisedName(newThreatLevel)));
+ HTMLNode infoboxContent
= infobox.addChild("div", "class", "infobox-content");
+
infoboxContent.addChild(warning);
+ addedWarning = true;
+ } else {
+ // Apply immediately,
no confirm needed.
+
node.securityLevels.setThreatLevel(newThreatLevel);
+ }
+ } else {
+ // Apply immediately, user
confirmed it.
+
node.securityLevels.setThreatLevel(newThreatLevel);
+ }
+ }
+ }
+ if(pageNode != null) {
+ formNode.addChild("input", new String[] {
"type", "name", "value" }, new String[] { "hidden", "seclevels", "on" });
+ formNode.addChild("input", new String[] {
"type", "value" }, new String[] { "submit", l10n("apply")});
+ formNode.addChild("input", new String[] {
"type", "value" }, new String[] { "reset", l10n("reset")});
+ writeHTMLReply(ctx, 200, "OK",
pageNode.generate());
+ return;
+ } else {
+ MultiValueTable headers = new MultiValueTable();
+ headers.put("Location",
"/config/?mode="+MODE_SECURITY_LEVELS);
+ ctx.sendReplyHeaders(302, "Found", headers,
null, 0);
+ return;
+ }
+ }
+
+ SubConfig[] sc = config.getConfigs();
+ StringBuffer errbuf = new StringBuffer();
+
if(!ctx.isAllowedFullAccess()) {
super.sendErrorPage(ctx, 403,
L10n.getString("Toadlet.unauthorizedTitle"),
L10n.getString("Toadlet.unauthorized"));
return;
@@ -352,9 +408,9 @@
for(NETWORK_THREAT_LEVEL level : NETWORK_THREAT_LEVEL.values())
{
HTMLNode input;
if(level == networkLevel) {
- input =
seclevelGroup.addChild("p").addChild("input", new String[] { "type", "checked",
"name" }, new String[] { "radio", "on", controlName });
+ input =
seclevelGroup.addChild("p").addChild("input", new String[] { "type", "checked",
"name", "value" }, new String[] { "radio", "on", controlName, level.name() });
} else {
- input =
seclevelGroup.addChild("p").addChild("input", new String[] { "type", "name" },
new String[] { "radio", controlName });
+ input =
seclevelGroup.addChild("p").addChild("input", new String[] { "type", "name",
"value" }, new String[] { "radio", controlName, level.name() });
}
input.addChild("b",
l10nSec("networkThreatLevel.name."+level));
input.addChild("#", ": ");
@@ -362,6 +418,7 @@
}
// FIXME implement the rest, it should be very similar to the
above.
+ formNode.addChild("input", new String[] { "type", "name",
"value" }, new String[] { "hidden", "seclevels", "on" });
formNode.addChild("input", new String[] { "type", "value" },
new String[] { "submit", l10n("apply")});
formNode.addChild("input", new String[] { "type", "value" },
new String[] { "reset", l10n("reset")});
}
@@ -370,8 +427,11 @@
private String l10nSec(String key) {
return L10n.getString("SecurityLevels."+key);
}
+
+ private String l10nSec(String key, String pattern, String value) {
+ return L10n.getString("SecurityLevels."+key, pattern, value);
+ }
-
@Override
public String supportedMethods() {
return "GET, POST";
Modified: trunk/freenet/src/freenet/clients/http/PageMaker.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/PageMaker.java 2008-09-02
22:43:59 UTC (rev 22362)
+++ trunk/freenet/src/freenet/clients/http/PageMaker.java 2008-09-03
12:31:58 UTC (rev 22363)
@@ -272,6 +272,14 @@
HTMLNode row = table.addChild("tr");
HTMLNode cell = row.addChild("td");
+ if(alternateMode > -1) {
+ if(mode != alternateMode)
+ cell.addChild("a", new String[] { "href",
"title" }, new String[] { "?mode="+alternateMode,
L10n.getString(alternateModeTooltipKey) },
L10n.getString(alternateModeTitleKey));
+ else
+ cell.addChild("b", "title",
L10n.getString(alternateModeTooltipKey), L10n.getString(alternateModeTitleKey));
+ cell = row.addChild("td");
+ }
+
if(mode != MODE_SIMPLE)
cell.addChild("a", new String[] { "href", "title" },
new String[] { "?mode=1", l10n("modeSimpleTooltip") }, l10n("modeSimple"));
else
@@ -281,14 +289,6 @@
cell.addChild("a", new String[] { "href", "title" },
new String[] { "?mode=2", l10n("modeAdvancedTooltip") }, l10n("modeAdvanced"));
else
cell.addChild("b", "title",
l10n("modeAdvancedTooltip"), l10n("modeAdvanced"));
- if(alternateMode > -1) {
- cell = row.addChild("td");
- if(mode != alternateMode)
- cell.addChild("a", new String[] { "href",
"title" }, new String[] { "?mode="+alternateMode,
L10n.getString(alternateModeTooltipKey) },
L10n.getString(alternateModeTitleKey));
- else
- cell.addChild("b", "title",
L10n.getString(alternateModeTooltipKey), L10n.getString(alternateModeTitleKey));
- }
-
return mode;
}
Modified: trunk/freenet/src/freenet/l10n/freenet.l10n.en.properties
===================================================================
--- trunk/freenet/src/freenet/l10n/freenet.l10n.en.properties 2008-09-02
22:43:59 UTC (rev 22362)
+++ trunk/freenet/src/freenet/l10n/freenet.l10n.en.properties 2008-09-03
12:31:58 UTC (rev 22363)
@@ -1049,6 +1049,15 @@
SecurityLevels.networkThreatLevel.desc.HIGH=I intend to access information
that could get me arrested, imprisoned, or worse. I am worried about my
government or ISP blocking Freenet. I understand that Freenet is experimental
and ${bold}cannot${/bold} ensure security against certain known attacks, but I
accept the risks compared to the alternatives. Freenet will not connect to
unknown nodes, so ${bold}I must have friends already using Freenet to select
this mode${/bold}.
SecurityLevels.networkThreatLevel.desc.NORMAL=I live in a relatively free
country, but I would like to make it more difficult for others to monitor my
communications. Freenet will be reasonably careful to protect your anonymity,
at some performance cost. Freenet will automatically connect to unknown nodes.
We recommend that you add friends running Freenet and upgrade to HIGH.
SecurityLevels.networkThreatLevel.desc.LOW=I do not care about monitoring and
want maximum performance. It may be quite easy for others to discover my
identity.
+SecurityLevels.noFriendsWarning=You have not added any Friends. If you set the
network security level to high, you will be unable to connect to Freenet until
you have at least one connected Friend! Note that for there to be any
meaningful security benefit, these must be people you actually know and at
least marginally trust, and for good performance you will need at least 5-10 of
them connected at any time. Are you sure?
+SecurityLevels.noFriendsCheckbox=I am sure, enable high network security
level, I will add some Friends ASAP.
+SecurityLevels.noConnectedFriendsWarning=You do not have any connected
Friends, although you have added ${added} friends. If you set the network
security level to high, you will only be able to connect to Freenet when your
friends are online, which they are not at the moment. You should add additional
Friends, only use Freenet when your friends are connected, or not upgrade to
high security. Note that for there to be any meaningful security benefit, your
Friends must be people you actually know and at least marginally trust, and for
good performance you will need at least 5-10 of them connected at any time. Are
you sure?
+SecurityLevels.noConnectedFriendsCheckbox=I am sure, I will add more Friends
and/or I accept that Freenet will only be online when my existing Friends are
online.
+SecurityLevels.fewConnectedFriendsWarning=You only have ${connected} connected
Friends right now, and you have added ${added} friends in total. If you set the
network security level to high, Freenet will only connect to your Friends, so
your performance may be significantly reduced, and if all your Friends are
offline then it will not be able to connect at all. Note that Friends must be
people you know and at least marginally trust for there to be any real security
benefit, and you will need at least 5-10 connected Friends for good
performance. Are you sure?
+SecurityLevels.fewConnectedFriendsCheckbox=I am sure, I will add more Friends
and/or I accept the performance cost and the fact that Freenet may not connect
when my Friends are offline.
+SecurityLevels.networkThreatLevelLowWarning=You are about to downgrade your
node's network security level to low. This means that it will be easy for
strangers to attack your anonymity over the Internet. Are you sure?
+SecurityLevels.networkThreatLevelLowCheckbox=I am sure, I want more speed and
I don't care who can tell what I'm doing with Freenet!
+SecurityLevels.networkThreatLevelConfirmTitle=WARNING: Setting network
security level to ${mode}
ShortOption.parseError=Cannot parse value as a string array: ${error}
ShortOption.parseError=The value specified can't be parsed as a 16-bit integer
: ${val}
SimpleToadletServer.advancedMode=Enable Advanced Mode?
Modified: trunk/freenet/src/freenet/node/SecurityLevels.java
===================================================================
--- trunk/freenet/src/freenet/node/SecurityLevels.java 2008-09-02 22:43:59 UTC
(rev 22362)
+++ trunk/freenet/src/freenet/node/SecurityLevels.java 2008-09-03 12:31:58 UTC
(rev 22363)
@@ -11,6 +11,8 @@
import freenet.config.NodeNeedRestartException;
import freenet.config.PersistentConfig;
import freenet.config.SubConfig;
+import freenet.l10n.L10n;
+import freenet.support.HTMLNode;
import freenet.support.Logger;
import freenet.support.api.StringCallback;
@@ -27,6 +29,8 @@
*/
public class SecurityLevels {
+ private final Node node;
+
public enum NETWORK_THREAT_LEVEL {
HIGH, // paranoid, darknet only
NORMAL, // normal setting, darknet/opennet hybrid
@@ -53,13 +57,16 @@
private MyCallback<PHYSICAL_THREAT_LEVEL> physicalThreatLevelCallback;
public SecurityLevels(Node node, PersistentConfig config) {
+ this.node = node;
SubConfig myConfig = new SubConfig("security-levels", config);
int sortOrder = 0;
networkThreatLevelCallback = new
MyCallback<NETWORK_THREAT_LEVEL>() {
@Override
public String get() {
- return networkThreatLevel.name();
+ synchronized(SecurityLevels.this) {
+ return networkThreatLevel.name();
+ }
}
public String[] getPossibleValues() {
@@ -77,9 +84,12 @@
@Override
protected void setValue(String val) throws
InvalidConfigValueException {
- NETWORK_THREAT_LEVEL newValue =
NETWORK_THREAT_LEVEL.valueOf(val);
+ NETWORK_THREAT_LEVEL newValue =
parseNetworkThreatLevel(val);
if(newValue != null)
throw new
InvalidConfigValueException("Invalid value for network threat level: "+val);
+ synchronized(SecurityLevels.this) {
+ networkThreatLevel = newValue;
+ }
}
};
@@ -89,7 +99,9 @@
@Override
public String get() {
- return friendsThreatLevel.name();
+ synchronized(SecurityLevels.this) {
+ return friendsThreatLevel.name();
+ }
}
public String[] getPossibleValues() {
@@ -110,6 +122,9 @@
FRIENDS_THREAT_LEVEL newValue =
FRIENDS_THREAT_LEVEL.valueOf(val);
if(newValue != null)
throw new
InvalidConfigValueException("Invalid value for friends threat level: "+val);
+ synchronized(SecurityLevels.this) {
+ friendsThreatLevel = newValue;
+ }
}
};
@@ -119,7 +134,9 @@
@Override
public String get() {
- return physicalThreatLevel.name();
+ synchronized(SecurityLevels.this) {
+ return physicalThreatLevel.name();
+ }
}
public String[] getPossibleValues() {
@@ -140,22 +157,26 @@
PHYSICAL_THREAT_LEVEL newValue =
PHYSICAL_THREAT_LEVEL.valueOf(val);
if(newValue != null)
throw new
InvalidConfigValueException("Invalid value for physical threat level: "+val);
+ synchronized(SecurityLevels.this) {
+ physicalThreatLevel = newValue;
+ }
}
};
myConfig.register("physicalThreatLevel", "NORMAL", sortOrder++,
false, true, "SecurityLevels.physicalThreatLevelShort",
"SecurityLevels.physicalThreatLevel", physicalThreatLevelCallback);
physicalThreatLevel =
PHYSICAL_THREAT_LEVEL.valueOf(myConfig.getString("physicalThreatLevel"));
+ myConfig.finishedInitialization();
}
- public void
addNetworkThreatLevelListener(SecurityLevelListener<NETWORK_THREAT_LEVEL>
listener) {
+ public synchronized void
addNetworkThreatLevelListener(SecurityLevelListener<NETWORK_THREAT_LEVEL>
listener) {
networkThreatLevelCallback.addListener(listener);
}
- public void
addFriendsThreatLevelListener(SecurityLevelListener<FRIENDS_THREAT_LEVEL>
listener) {
+ public synchronized void
addFriendsThreatLevelListener(SecurityLevelListener<FRIENDS_THREAT_LEVEL>
listener) {
friendsThreatLevelCallback.addListener(listener);
}
- public void
addPhysicalThreatLevelListener(SecurityLevelListener<PHYSICAL_THREAT_LEVEL>
listener) {
+ public synchronized void
addPhysicalThreatLevelListener(SecurityLevelListener<PHYSICAL_THREAT_LEVEL>
listener) {
physicalThreatLevelCallback.addListener(listener);
}
@@ -194,5 +215,67 @@
public NETWORK_THREAT_LEVEL getNetworkThreatLevel() {
return networkThreatLevel;
}
+
+ public static NETWORK_THREAT_LEVEL parseNetworkThreatLevel(String arg) {
+ try {
+ return NETWORK_THREAT_LEVEL.valueOf(arg);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ /**
+ * If changing to the new threat level is a potential problem, warn the
user,
+ * and include a checkbox for confirmation.
+ * @param newThreatLevel
+ * @return
+ */
+ public HTMLNode getConfirmWarning(NETWORK_THREAT_LEVEL newThreatLevel,
String checkboxName) {
+ if(newThreatLevel == networkThreatLevel)
+ return null; // Not going to be changed.
+ HTMLNode parent = new HTMLNode("div");
+ if(newThreatLevel == NETWORK_THREAT_LEVEL.HIGH) {
+ if(node.peers.getDarknetPeers().length == 0) {
+ parent.addChild("p", l10n("noFriendsWarning"));
+ parent.addChild("input", new String[] { "type",
"name", "value" }, new String[] { "checkbox", checkboxName, "off" },
l10n("noFriendsCheckbox"));
+ } else if(node.peers.countConnectedDarknetPeers() == 0)
{
+ parent.addChild("p",
l10n("noConnectedFriendsWarning", "added",
Integer.toString(node.peers.getDarknetPeers().length)));
+ parent.addChild("input", new String[] { "type",
"name", "value" }, new String[] { "checkbox", checkboxName, "off" },
l10n("noConnectedFriendsCheckbox"));
+ } else if(node.peers.countConnectedDarknetPeers() < 10)
{
+ parent.addChild("p",
l10n("fewConnectedFriendsWarning", new String[] { "connected", "added" }, new
String[] { Integer.toString(node.peers.countConnectedDarknetPeers()),
Integer.toString(node.peers.getDarknetPeers().length)}));
+ parent.addChild("input", new String[] { "type",
"name", "value" }, new String[] { "checkbox", checkboxName, "off" },
l10n("fewConnectedFriendsCheckbox"));
+ } else return null;
+ return parent;
+ } else if(newThreatLevel == NETWORK_THREAT_LEVEL.LOW) {
+ parent.addChild("p",
l10n("networkThreatLevelLowWarning"));
+ parent.addChild("input", new String[] { "type", "name",
"value" }, new String[] { "checkbox", checkboxName, "off" },
l10n("networkThreatLevelLowCheckbox"));
+ return parent;
+ } // Don't warn on switching to NORMAL.
+ return null;
+ }
+
+ private String l10n(String string) {
+ return L10n.getString("SecurityLevels."+string);
+ }
+
+ private String l10n(String string, String pattern, String value) {
+ return L10n.getString("SecurityLevels."+string, pattern, value);
+ }
+
+ private String l10n(String string, String[] patterns, String[] values) {
+ return L10n.getString("SecurityLevels."+string, patterns,
values);
+ }
+
+ public void setThreatLevel(NETWORK_THREAT_LEVEL newThreatLevel) {
+ if(newThreatLevel == null) throw new NullPointerException();
+ synchronized(this) {
+ networkThreatLevel = newThreatLevel;
+ }
+ node.config.store();
+ }
+
+ public static String localisedName(NETWORK_THREAT_LEVEL newThreatLevel)
{
+ return
L10n.getString("SecurityLevels.networkThreatLevel.name."+newThreatLevel.name());
+ }
}