- Don't reread RI if netdb date is recent
    - Prevent LS/RI overwrites
    - Disallow hash mismatches in RI files
    - Reseed won't fetch our own RI
    - Reseed won't overwrite recent RIs
This commit is contained in:
zzz
2012-03-20 18:38:28 +00:00
parent 3da6ccfbbe
commit e3da181cea
5 changed files with 74 additions and 19 deletions

View File

@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */
public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION;
public final static long BUILD = 18;
public final static long BUILD = 19;
/** for example "-test" */
public final static String EXTRA = "";

View File

@ -684,7 +684,9 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
// if it hasn't changed, no need to do anything
return rv;
}
} catch (ClassCastException cce) {}
} catch (ClassCastException cce) {
throw new IllegalArgumentException("Attempt to replace RI with " + leaseSet);
}
String err = validate(key, leaseSet);
if (err != null)
@ -811,7 +813,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
return store(key, routerInfo, true);
}
public RouterInfo store(Hash key, RouterInfo routerInfo, boolean persist) throws IllegalArgumentException {
RouterInfo store(Hash key, RouterInfo routerInfo, boolean persist) throws IllegalArgumentException {
if (!_initialized) return null;
RouterInfo rv = null;
@ -821,7 +823,9 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
// no need to validate
return rv;
}
} catch (ClassCastException cce) {}
} catch (ClassCastException cce) {
throw new IllegalArgumentException("Attempt to replace LS with " + routerInfo);
}
String err = validate(key, routerInfo);
if (err != null)

View File

@ -392,12 +392,17 @@ class PersistentDataStore extends TransientDataStore {
private class ReadRouterJob extends JobImpl {
private final File _routerFile;
private final Hash _key;
private long _knownDate;
/**
* @param key must match the RI hash in the file
*/
public ReadRouterJob(File routerFile, Hash key) {
super(PersistentDataStore.this._context);
_routerFile = routerFile;
_key = key;
}
public String getName() { return "Read RouterInfo"; }
private boolean shouldRead() {
@ -405,19 +410,21 @@ class PersistentDataStore extends TransientDataStore {
DatabaseEntry data = get(_key, false);
if (data == null) return true;
if (data.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) {
long knownDate = ((RouterInfo)data).getPublished();
_knownDate = ((RouterInfo)data).getPublished();
long fileDate = _routerFile.lastModified();
if (fileDate > knownDate)
return true;
else
return false;
// don't overwrite recent netdb RIs with reseed data
return fileDate > _knownDate + (60*60*1000);
} else {
// wtf
return true;
// wtf - prevent injection from reseeding
_log.error("Prevented LS overwrite by RI " + _key + " from " + _routerFile);
return false;
}
}
public void runJob() {
if (!shouldRead()) return;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Reading " + _routerFile);
try {
InputStream fis = null;
boolean corrupt = false;
@ -432,6 +439,15 @@ class PersistentDataStore extends TransientDataStore {
_log.error("The router "
+ ri.getIdentity().calculateHash().toBase64()
+ " is from a different network");
} else if (!ri.getIdentity().calculateHash().equals(_key)) {
// prevent injection from reseeding
// this is checked in KNDF.validate() but catch it sooner and log as error.
corrupt = true;
_log.error(ri.getIdentity().calculateHash() + " does not match " + _key + " from " + _routerFile);
} else if (ri.getPublished() <= _knownDate) {
// Don't store but don't delete
if (_log.shouldLog(Log.WARN))
_log.warn("Skipping since netdb newer than " + _routerFile);
} else {
try {
// persist = false so we don't write what we just read
@ -441,7 +457,7 @@ class PersistentDataStore extends TransientDataStore {
// so add it here.
getContext().profileManager().heardAbout(ri.getIdentity().getHash(), ri.getPublished());
} catch (IllegalArgumentException iae) {
_log.info("Refused locally loaded routerInfo - deleting");
_log.info("Refused locally loaded routerInfo - deleting", iae);
corrupt = true;
}
}

View File

@ -334,6 +334,8 @@ public class Reseeder {
// This isn't really URLs, but Base64 hashes
// but they may include % encoding
Set<String> urls = new HashSet(1024);
Hash ourHash = _context.routerHash();
String ourB64 = ourHash != null ? ourHash.toBase64() : null;
int cur = 0;
int total = 0;
while (total++ < 1000) {
@ -352,7 +354,13 @@ public class Reseeder {
continue;
}
String name = content.substring(start + ("href=\"" + ROUTERINFO_PREFIX).length(), end);
urls.add(name);
// never load our own RI
if (ourB64 == null || !name.contains(ourB64)) {
urls.add(name);
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Skipping our own RI");
}
cur = end + 1;
}
if (total <= 0) {
@ -372,7 +380,8 @@ public class Reseeder {
System.setProperty(PROP_STATUS,
_("Reseeding: fetching router info from seed URL ({0} successful, {1} errors).", fetched, errors));
fetchSeed(seedURL, iter.next());
if (!fetchSeed(seedURL, iter.next()))
continue;
fetched++;
if (echoStatus) {
System.out.print(".");
@ -408,8 +417,9 @@ public class Reseeder {
* We do NOT validate the received data here - that is done in PersistentDataStore
*
* @param peer The Base64 hash, may include % encoding. It is decoded and validated here.
* @return true on success, false if skipped
*/
private void fetchSeed(String seedURL, String peer) throws IOException, URISyntaxException {
private boolean fetchSeed(String seedURL, String peer) throws IOException, URISyntaxException {
// Use URI to do % decoding of the B64 hash (some servers escape ~ and =)
// Also do basic hash validation. This prevents stuff like
// .. or / in the file name
@ -420,13 +430,16 @@ public class Reseeder {
byte[] hash = Base64.decode(b64);
if (hash == null || hash.length != Hash.HASH_LENGTH)
throw new IOException("bad hash " + peer);
Hash ourHash = _context.routerHash();
if (ourHash != null && DataHelper.eq(hash, ourHash.getData()))
return false;
URL url = new URL(seedURL + (seedURL.endsWith("/") ? "" : "/") + ROUTERINFO_PREFIX + peer + ROUTERINFO_SUFFIX);
byte data[] = readURL(url);
if (data == null || data.length <= 0)
throw new IOException("Failed fetch of " + url);
writeSeed(b64, data);
return writeSeed(b64, data);
}
/** @return null on error */
@ -468,16 +481,24 @@ public class Reseeder {
/**
* @param name valid Base64 hash
* @return true on success, false if skipped
*/
private void writeSeed(String name, byte data[]) throws IOException {
private boolean writeSeed(String name, byte data[]) throws IOException {
String dirName = "netDb"; // _context.getProperty("router.networkDatabase.dbDir", "netDb");
File netDbDir = new SecureDirectory(_context.getRouterDir(), dirName);
if (!netDbDir.exists()) {
boolean ok = netDbDir.mkdirs();
netDbDir.mkdirs();
}
File file = new File(netDbDir, ROUTERINFO_PREFIX + name + ROUTERINFO_SUFFIX);
// don't overwrite recent file
// TODO: even better would be to compare to last-mod date from eepget
if (file.exists() && file.lastModified() > _context.clock().now() - 60*60*1000) {
if (_log.shouldLog(Log.INFO))
_log.info("Skipping RI, ours is recent: " + file);
return false;
}
FileOutputStream fos = null;
try {
File file = new File(netDbDir, ROUTERINFO_PREFIX + name + ROUTERINFO_SUFFIX);
fos = new SecureFileOutputStream(file);
fos.write(data);
if (_log.shouldLog(Log.INFO))
@ -487,6 +508,7 @@ public class Reseeder {
if (fos != null) fos.close();
} catch (IOException ioe) {}
}
return true;
}
}