forked from I2P_Developers/i2p.i2p
Add authorization
New PasswordManager methods for use by SAM
This commit is contained in:
@ -85,6 +85,9 @@ public class SAMBridge implements Runnable, ClientApp {
|
|||||||
private static final String PROP_SAM_SSL = "sam.useSSL";
|
private static final String PROP_SAM_SSL = "sam.useSSL";
|
||||||
public static final String PROP_TCP_HOST = "sam.tcp.host";
|
public static final String PROP_TCP_HOST = "sam.tcp.host";
|
||||||
public static final String PROP_TCP_PORT = "sam.tcp.port";
|
public static final String PROP_TCP_PORT = "sam.tcp.port";
|
||||||
|
public static final String PROP_AUTH = "sam.auth";
|
||||||
|
public static final String PROP_PW_PREFIX = "sam.auth.";
|
||||||
|
public static final String PROP_PW_SUFFIX = ".shash";
|
||||||
protected static final String DEFAULT_TCP_HOST = "127.0.0.1";
|
protected static final String DEFAULT_TCP_HOST = "127.0.0.1";
|
||||||
protected static final String DEFAULT_TCP_PORT = "7656";
|
protected static final String DEFAULT_TCP_PORT = "7656";
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import java.util.StringTokenizer;
|
|||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
import net.i2p.util.PasswordManager;
|
||||||
import net.i2p.util.VersionComparator;
|
import net.i2p.util.VersionComparator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,6 +94,20 @@ class SAMHandlerFactory {
|
|||||||
SAMHandler.writeString("HELLO REPLY RESULT=NOVERSION\n", s);
|
SAMHandler.writeString("HELLO REPLY RESULT=NOVERSION\n", s);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Boolean.valueOf(i2cpProps.getProperty(SAMBridge.PROP_AUTH))) {
|
||||||
|
String user = props.getProperty("USER");
|
||||||
|
String pw = props.getProperty("PASSWORD");
|
||||||
|
if (user == null || pw == null)
|
||||||
|
throw new SAMException("USER and PASSWORD required");
|
||||||
|
String savedPW = i2cpProps.getProperty(SAMBridge.PROP_PW_PREFIX + user + SAMBridge.PROP_PW_SUFFIX);
|
||||||
|
if (savedPW == null)
|
||||||
|
throw new SAMException("Authorization failed");
|
||||||
|
PasswordManager pm = new PasswordManager(I2PAppContext.getGlobalContext());
|
||||||
|
if (!pm.checkHash(savedPW, pw))
|
||||||
|
throw new SAMException("Authorization failed");
|
||||||
|
}
|
||||||
|
|
||||||
// Let's answer positively
|
// Let's answer positively
|
||||||
if (!SAMHandler.writeString("HELLO REPLY RESULT=OK VERSION=" + ver + "\n", s))
|
if (!SAMHandler.writeString("HELLO REPLY RESULT=OK VERSION=" + ver + "\n", s))
|
||||||
throw new SAMException("Error writing to socket");
|
throw new SAMException("Error writing to socket");
|
||||||
|
@ -99,6 +99,18 @@ public class PasswordManager {
|
|||||||
String shash = _context.getProperty(pfx + PROP_SHASH);
|
String shash = _context.getProperty(pfx + PROP_SHASH);
|
||||||
if (shash == null)
|
if (shash == null)
|
||||||
return false;
|
return false;
|
||||||
|
return checkHash(shash, pw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check pw against b64 salt+hash, as generated by createHash()
|
||||||
|
*
|
||||||
|
* @param shash b64 string
|
||||||
|
* @param pw plain text non-null, already trimmed
|
||||||
|
* @return if pw verified
|
||||||
|
* @since 0.9.22
|
||||||
|
*/
|
||||||
|
public boolean checkHash(String shash, String pw) {
|
||||||
byte[] shashBytes = Base64.decode(shash);
|
byte[] shashBytes = Base64.decode(shash);
|
||||||
if (shashBytes == null || shashBytes.length != SHASH_LENGTH)
|
if (shashBytes == null || shashBytes.length != SHASH_LENGTH)
|
||||||
return false;
|
return false;
|
||||||
@ -110,6 +122,23 @@ public class PasswordManager {
|
|||||||
return DataHelper.eq(hash, pwHash);
|
return DataHelper.eq(hash, pwHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a salt+hash, to be saved and verified later by verifyHash().
|
||||||
|
*
|
||||||
|
* @param pw plain text non-null, already trimmed
|
||||||
|
* @return salted+hash b64 string
|
||||||
|
* @since 0.9.22
|
||||||
|
*/
|
||||||
|
public String createHash(String pw) {
|
||||||
|
byte[] salt = new byte[SALT_LENGTH];
|
||||||
|
_context.random().nextBytes(salt);
|
||||||
|
byte[] pwHash = _context.keyGenerator().generateSessionKey(salt, DataHelper.getUTF8(pw)).getData();
|
||||||
|
byte[] shashBytes = new byte[SHASH_LENGTH];
|
||||||
|
System.arraycopy(salt, 0, shashBytes, 0, SALT_LENGTH);
|
||||||
|
System.arraycopy(pwHash, 0, shashBytes, SALT_LENGTH, SessionKey.KEYSIZE_BYTES);
|
||||||
|
return Base64.encode(shashBytes);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Either plain or b64
|
* Either plain or b64
|
||||||
*
|
*
|
||||||
|
@ -158,13 +158,7 @@ public class RouterPasswordManager extends PasswordManager {
|
|||||||
String pfx = realm;
|
String pfx = realm;
|
||||||
if (user != null && user.length() > 0)
|
if (user != null && user.length() > 0)
|
||||||
pfx += '.' + user;
|
pfx += '.' + user;
|
||||||
byte[] salt = new byte[SALT_LENGTH];
|
String shash = createHash(pw);
|
||||||
_context.random().nextBytes(salt);
|
|
||||||
byte[] pwHash = _context.keyGenerator().generateSessionKey(salt, DataHelper.getUTF8(pw)).getData();
|
|
||||||
byte[] shashBytes = new byte[SHASH_LENGTH];
|
|
||||||
System.arraycopy(salt, 0, shashBytes, 0, SALT_LENGTH);
|
|
||||||
System.arraycopy(pwHash, 0, shashBytes, SALT_LENGTH, SessionKey.KEYSIZE_BYTES);
|
|
||||||
String shash = Base64.encode(shashBytes);
|
|
||||||
Map<String, String> toAdd = Collections.singletonMap(pfx + PROP_SHASH, shash);
|
Map<String, String> toAdd = Collections.singletonMap(pfx + PROP_SHASH, shash);
|
||||||
List<String> toDel = new ArrayList<String>(4);
|
List<String> toDel = new ArrayList<String>(4);
|
||||||
toDel.add(pfx + PROP_PW);
|
toDel.add(pfx + PROP_PW);
|
||||||
|
Reference in New Issue
Block a user