I2P Address: [http://git.idk.i2p]

Skip to content
Snippets Groups Projects
Commit 6b578dfd authored by zzz's avatar zzz
Browse files

Console: Fix UTF-8 passwords

Partial fix for UTF-8 usernames
Better input checking and help messages
parent 8bb6922e
No related branches found
No related tags found
No related merge requests found
...@@ -463,6 +463,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem ...@@ -463,6 +463,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
" realm=\"" + getRealm() + '"' + " realm=\"" + getRealm() + '"' +
(isDigest ? ", nonce=\"" + getNonce() + "\"," + (isDigest ? ", nonce=\"" + getNonce() + "\"," +
" algorithm=MD5," + " algorithm=MD5," +
" charset=UTF-8," + // RFC 7616/7617
" qop=\"auth\"" + " qop=\"auth\"" +
(isStale ? ", stale=true" : "") (isStale ? ", stale=true" : "")
: "") + : "") +
......
...@@ -5,6 +5,8 @@ import java.util.HashMap; ...@@ -5,6 +5,8 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import net.i2p.data.DataHelper;
/** set the theme */ /** set the theme */
public class ConfigUIHandler extends FormHandler { public class ConfigUIHandler extends FormHandler {
private boolean _shouldSave; private boolean _shouldSave;
...@@ -80,6 +82,16 @@ public class ConfigUIHandler extends FormHandler { ...@@ -80,6 +82,16 @@ public class ConfigUIHandler extends FormHandler {
addFormError(_t("No user name entered")); addFormError(_t("No user name entered"));
return; return;
} }
// XSS filters # and ; but not =
// We store the username as the part of an option key, so we can't handle '='
if (name.contains("=")) {
addFormError("User name may not contain '='");
return;
}
byte[] b1 = DataHelper.getUTF8(name);
byte[] b2 = DataHelper.getASCII(name);
if (!DataHelper.eq(b1, b2))
addFormError(_t("Warning: User names outside the ISO-8859-1 character set are not recommended. Support is not standardized and varies by browser."));
String pw = getJettyString("nofilter_pw"); String pw = getJettyString("nofilter_pw");
if (pw == null || pw.length() <= 0) { if (pw == null || pw.length() <= 0) {
addFormError(_t("No password entered")); addFormError(_t("No password entered"));
...@@ -91,6 +103,8 @@ public class ConfigUIHandler extends FormHandler { ...@@ -91,6 +103,8 @@ public class ConfigUIHandler extends FormHandler {
if (!_context.getBooleanProperty(RouterConsoleRunner.PROP_PW_ENABLE)) if (!_context.getBooleanProperty(RouterConsoleRunner.PROP_PW_ENABLE))
_context.router().saveConfig(RouterConsoleRunner.PROP_PW_ENABLE, "true"); _context.router().saveConfig(RouterConsoleRunner.PROP_PW_ENABLE, "true");
addFormNotice(_t("Added user {0}", name)); addFormNotice(_t("Added user {0}", name));
addFormNotice(_t("To recover from a forgotten or non-working password, stop I2P, edit the file {0}, delete the line {1}, and restart I2P.",
_context.router().getConfigFilename(), RouterConsoleRunner.PROP_PW_ENABLE + "=true"));
addFormError(_t("Restart required to take effect")); addFormError(_t("Restart required to take effect"));
} else { } else {
addFormError(_t("Error saving the configuration (applied but not saved) - please see the error logs.")); addFormError(_t("Error saving the configuration (applied but not saved) - please see the error logs."));
......
...@@ -5,6 +5,7 @@ import java.io.File; ...@@ -5,6 +5,7 @@ import java.io.File;
import java.io.FilenameFilter; import java.io.FilenameFilter;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.Inet4Address; import java.net.Inet4Address;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
...@@ -838,16 +839,51 @@ public class RouterConsoleRunner implements RouterApp { ...@@ -838,16 +839,51 @@ public class RouterConsoleRunner implements RouterApp {
HashLoginService realm = new HashLoginService(JETTY_REALM); HashLoginService realm = new HashLoginService(JETTY_REALM);
sec.setLoginService(realm); sec.setLoginService(realm);
sec.setAuthenticator(authenticator); sec.setAuthenticator(authenticator);
String[] role = new String[] {JETTY_ROLE};
for (Map.Entry<String, String> e : userpw.entrySet()) { for (Map.Entry<String, String> e : userpw.entrySet()) {
String user = e.getKey(); String user = e.getKey();
String pw = e.getValue(); String pw = e.getValue();
realm.putUser(user, Credential.getCredential(MD5.__TYPE + pw), new String[] {JETTY_ROLE}); Credential cred = Credential.getCredential(MD5.__TYPE + pw);
realm.putUser(user, cred, role);
Constraint constraint = new Constraint(user, JETTY_ROLE); Constraint constraint = new Constraint(user, JETTY_ROLE);
constraint.setAuthenticate(true); constraint.setAuthenticate(true);
ConstraintMapping cm = new ConstraintMapping(); ConstraintMapping cm = new ConstraintMapping();
cm.setConstraint(constraint); cm.setConstraint(constraint);
cm.setPathSpec("/"); cm.setPathSpec("/");
constraints.add(cm); constraints.add(cm);
// Jetty does auth checking only with ISO-8859-1,
// so register a 2nd and 3rd user with different encodings if necessary.
// Might work, might not...
// There's no standard and browser behavior varies.
// Chrome sends UTF-8. Firefox doesn't send anything.
// https://bugzilla.mozilla.org/show_bug.cgi?id=41489
// see also RFC 7616/7617 (late 2015) and PasswordManager.md5Hex()
byte[] b1 = DataHelper.getUTF8(user);
byte[] b2 = DataHelper.getASCII(user);
if (!DataHelper.eq(b1, b2)) {
try {
// each char truncated to 8 bytes
String user2 = new String(b2, "ISO-8859-1");
realm.putUser(user2, cred, role);
constraint = new Constraint(user2, JETTY_ROLE);
constraint.setAuthenticate(true);
cm = new ConstraintMapping();
cm.setConstraint(constraint);
cm.setPathSpec("/");
constraints.add(cm);
// each UTF-8 byte as a char
// this is what chrome does
String user3 = new String(b1, "ISO-8859-1");
realm.putUser(user3, cred, role);
constraint = new Constraint(user3, JETTY_ROLE);
constraint.setAuthenticate(true);
cm = new ConstraintMapping();
cm.setConstraint(constraint);
cm.setPathSpec("/");
constraints.add(cm);
} catch (UnsupportedEncodingException uee) {}
}
} }
} }
} }
......
...@@ -1873,7 +1873,9 @@ public class DataHelper { ...@@ -1873,7 +1873,9 @@ public class DataHelper {
* Roughly the same as orig.getBytes("ISO-8859-1") but much faster and * Roughly the same as orig.getBytes("ISO-8859-1") but much faster and
* will not throw an exception. * will not throw an exception.
* *
* @param orig non-null, must be 7-bit chars * Warning - misnamed, converts to ISO-8859-1.
*
* @param orig non-null, truncates to 8-bit chars
* @since 0.9.5 * @since 0.9.5
*/ */
public static byte[] getASCII(String orig) { public static byte[] getASCII(String orig) {
......
...@@ -30,7 +30,7 @@ public class PasswordManager { ...@@ -30,7 +30,7 @@ public class PasswordManager {
protected static final String PROP_PW = ".password"; protected static final String PROP_PW = ".password";
/** stored obfuscated as b64 of the UTF-8 bytes */ /** stored obfuscated as b64 of the UTF-8 bytes */
protected static final String PROP_B64 = ".b64"; protected static final String PROP_B64 = ".b64";
/** stored as the hex of the MD5 hash of the ISO-8859-1 bytes. Compatible with Jetty. */ /** stored as the hex of the MD5 hash of the UTF-8 bytes. Compatible with Jetty. */
protected static final String PROP_MD5 = ".md5"; protected static final String PROP_MD5 = ".md5";
/** stored as a Unix crypt string */ /** stored as a Unix crypt string */
protected static final String PROP_CRYPT = ".crypt"; protected static final String PROP_CRYPT = ".crypt";
...@@ -185,6 +185,10 @@ public class PasswordManager { ...@@ -185,6 +185,10 @@ public class PasswordManager {
* Will return the MD5 sum of "user:subrealm:pw", compatible with Jetty * Will return the MD5 sum of "user:subrealm:pw", compatible with Jetty
* and RFC 2617. * and RFC 2617.
* *
* Updated in 0.9.26 to use UTF-8, as implied in RFC 7616/7617
* See also http://stackoverflow.com/questions/7242316/what-encoding-should-i-use-for-http-basic-authentication
* http://stackoverflow.com/questions/702629/utf-8-characters-mangled-in-http-basic-auth-username
*
* @param subrealm to be used in creating the checksum * @param subrealm to be used in creating the checksum
* @param user non-null, non-empty, already trimmed * @param user non-null, non-empty, already trimmed
* @param pw non-null, plain text, already trimmed * @param pw non-null, plain text, already trimmed
...@@ -200,17 +204,18 @@ public class PasswordManager { ...@@ -200,17 +204,18 @@ public class PasswordManager {
* Will return the MD5 sum of the data, compatible with Jetty * Will return the MD5 sum of the data, compatible with Jetty
* and RFC 2617. * and RFC 2617.
* *
* Updated in 0.9.26 to use UTF-8, as implied in RFC 7616/7617
* See also http://stackoverflow.com/questions/7242316/what-encoding-should-i-use-for-http-basic-authentication
*
* @param fullpw non-null, plain text, already trimmed * @param fullpw non-null, plain text, already trimmed
* @return lower-case hex with leading zeros, 32 chars, or null on error * @return lower-case hex with leading zeros, 32 chars, or null on error
*/ */
public static String md5Hex(String fullpw) { public static String md5Hex(String fullpw) {
try { byte[] data = DataHelper.getUTF8(fullpw);
byte[] data = fullpw.getBytes("ISO-8859-1"); byte[] sum = md5Sum(data);
byte[] sum = md5Sum(data); if (sum != null)
if (sum != null) // adds leading zeros if necessary
// adds leading zeros if necessary return DataHelper.toString(sum);
return DataHelper.toString(sum);
} catch (UnsupportedEncodingException uee) {}
return null; return null;
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment