Author: toad
Date: 2007-03-17 20:31:38 +0000 (Sat, 17 Mar 2007)
New Revision: 12198
Added:
trunk/freenet/src/freenet/support/io/FileExistsException.java
Modified:
trunk/freenet/src/freenet/client/ArchiveManager.java
trunk/freenet/src/freenet/clients/http/QueueToadlet.java
trunk/freenet/src/freenet/node/TextModeClientInterface.java
trunk/freenet/src/freenet/node/fcp/ClientGet.java
trunk/freenet/src/freenet/node/fcp/ClientPut.java
trunk/freenet/src/freenet/node/fcp/ClientPutDir.java
trunk/freenet/src/freenet/node/fcp/ClientPutDiskDirMessage.java
trunk/freenet/src/freenet/node/fcp/ClientPutMessage.java
trunk/freenet/src/freenet/node/fcp/DiskDirPutFile.java
trunk/freenet/src/freenet/support/io/FileBucket.java
trunk/freenet/src/freenet/support/io/FileBucketFactory.java
trunk/freenet/src/freenet/support/io/PersistentTempBucketFactory.java
trunk/freenet/src/freenet/support/io/SerializableToFieldSetBucketUtil.java
trunk/freenet/src/freenet/support/io/TempFileBucket.java
Log:
Prevent symlink attacks when using DDA.
Modified: trunk/freenet/src/freenet/client/ArchiveManager.java
===================================================================
--- trunk/freenet/src/freenet/client/ArchiveManager.java 2007-03-17
19:14:14 UTC (rev 12197)
+++ trunk/freenet/src/freenet/client/ArchiveManager.java 2007-03-17
20:31:38 UTC (rev 12198)
@@ -406,7 +406,7 @@
*/
private TempStoreElement makeTempStoreBucket(long size) {
File myFile = filenameGenerator.makeRandomFilename();
- FileBucket fb = new FileBucket(myFile, false, true, true, true);
+ FileBucket fb = new FileBucket(myFile, false, false, true,
true, true);
byte[] cipherKey = new byte[32];
random.nextBytes(cipherKey);
Modified: trunk/freenet/src/freenet/clients/http/QueueToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/QueueToadlet.java 2007-03-17
19:14:14 UTC (rev 12197)
+++ trunk/freenet/src/freenet/clients/http/QueueToadlet.java 2007-03-17
20:31:38 UTC (rev 12198)
@@ -225,7 +225,7 @@
String identifier = file.getName() + "-fred-" +
System.currentTimeMillis();
String contentType =
DefaultMIMETypes.guessMIMEType(filename, false);
try {
- ClientPut clientPut = new
ClientPut(fcp.getGlobalClient(), new FreenetURI("CHK@"), identifier,
Integer.MAX_VALUE, RequestStarter.BULK_SPLITFILE_PRIORITY_CLASS,
ClientRequest.PERSIST_FOREVER, null, false, false, -1,
ClientPutMessage.UPLOAD_FROM_DISK, file, contentType, new FileBucket(file,
true, false, false, false), null, file.getName(), false);
+ ClientPut clientPut = new
ClientPut(fcp.getGlobalClient(), new FreenetURI("CHK@"), identifier,
Integer.MAX_VALUE, RequestStarter.BULK_SPLITFILE_PRIORITY_CLASS,
ClientRequest.PERSIST_FOREVER, null, false, false, -1,
ClientPutMessage.UPLOAD_FROM_DISK, file, contentType, new FileBucket(file,
true, false, false, false, false), null, file.getName(), false);
if(logMINOR) Logger.minor(this,
"Started global request to insert "+file+" to CHK@ as "+identifier);
clientPut.start();
fcp.forceStorePersistentRequests();
Modified: trunk/freenet/src/freenet/node/TextModeClientInterface.java
===================================================================
--- trunk/freenet/src/freenet/node/TextModeClientInterface.java 2007-03-17
19:14:14 UTC (rev 12197)
+++ trunk/freenet/src/freenet/node/TextModeClientInterface.java 2007-03-17
20:31:38 UTC (rev 12198)
@@ -559,7 +559,7 @@
if(mimeType.equals(DefaultMIMETypes.DEFAULT_MIME_TYPE))
mimeType = ""; // don't need to override it
- FileBucket fb = new FileBucket(f, true, false, false, false);
+ FileBucket fb = new FileBucket(f, true, false, false, false,
false);
InsertBlock block = new InsertBlock(fb, new
ClientMetadata(mimeType), FreenetURI.EMPTY_CHK_URI);
startTime = System.currentTimeMillis();
@@ -899,7 +899,7 @@
if (filelist[i].isFile()) {
File f = filelist[i];
- FileBucket bucket = new FileBucket(f, true, false,
false, false);
+ FileBucket bucket = new FileBucket(f, true, false,
false, false, false);
ret.put(f.getName(), bucket);
} else if(filelist[i].isDirectory()) {
Modified: trunk/freenet/src/freenet/node/fcp/ClientGet.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ClientGet.java 2007-03-17 19:14:14 UTC
(rev 12197)
+++ trunk/freenet/src/freenet/node/fcp/ClientGet.java 2007-03-17 20:31:38 UTC
(rev 12198)
@@ -90,7 +90,7 @@
if(returnType == ClientGetMessage.RETURN_TYPE_DISK) {
this.targetFile = returnFilename;
this.tempFile = returnTempFilename;
- ret = new FileBucket(returnTempFilename, false, false,
false, false);
+ ret = new FileBucket(returnTempFilename, false, true,
false, false, false);
} else if(returnType == ClientGetMessage.RETURN_TYPE_NONE) {
targetFile = null;
tempFile = null;
@@ -147,7 +147,7 @@
if(returnType == ClientGetMessage.RETURN_TYPE_DISK) {
this.targetFile = message.diskFile;
this.tempFile = message.tempFile;
- ret = new FileBucket(message.tempFile, false, false,
false, false);
+ ret = new FileBucket(message.tempFile, false, true,
false, false, false);
} else if(returnType == ClientGetMessage.RETURN_TYPE_NONE) {
targetFile = null;
tempFile = null;
@@ -229,9 +229,9 @@
Bucket ret = null;
if(returnType == ClientGetMessage.RETURN_TYPE_DISK) {
if (succeeded) {
- ret = new FileBucket(targetFile, false, false,
false, false);
+ ret = new FileBucket(targetFile, false, true,
false, false, false);
} else {
- ret = new FileBucket(tempFile, false, false,
false, false);
+ ret = new FileBucket(tempFile, false, true,
false, false, false);
}
} else if(returnType == ClientGetMessage.RETURN_TYPE_NONE) {
ret = new NullBucket();
@@ -343,7 +343,7 @@
postFetchProtocolErrorMessage =
new ProtocolErrorMessage(ProtocolErrorMessage.COULD_NOT_RENAME_FILE, false,
null, identifier, global);
// Don't delete temp file, user
might want it.
}
- returnBucket = new
FileBucket(targetFile, false, false, false, false);
+ returnBucket = new
FileBucket(targetFile, false, true, false, false, false);
} catch (FileNotFoundException e) {
postFetchProtocolErrorMessage = new
ProtocolErrorMessage(ProtocolErrorMessage.COULD_NOT_WRITE_FILE, false, null,
identifier, global);
} catch (IOException e) {
@@ -643,9 +643,9 @@
synchronized(this) {
if(targetFile != null) {
if(succeeded || tempFile == null)
- return new FileBucket(targetFile,
false, false, false, false);
+ return new FileBucket(targetFile,
false, true, false, false, false);
else
- return new FileBucket(tempFile, false,
false, false, false);
+ return new FileBucket(tempFile, false,
true, false, false, false);
} else return returnBucket;
}
}
Modified: trunk/freenet/src/freenet/node/fcp/ClientPut.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ClientPut.java 2007-03-17 19:14:14 UTC
(rev 12197)
+++ trunk/freenet/src/freenet/node/fcp/ClientPut.java 2007-03-17 20:31:38 UTC
(rev 12198)
@@ -229,7 +229,7 @@
origFilename = new File(fs.get("Filename"));
if(logMINOR)
Logger.minor(this, "Uploading from disk:
"+origFilename+" for "+this);
- data = new FileBucket(origFilename, true, false, false,
false);
+ data = new FileBucket(origFilename, true, false, false,
false, false);
targetURI = null;
} else if(uploadFrom == ClientPutMessage.UPLOAD_FROM_DIRECT) {
origFilename = null;
Modified: trunk/freenet/src/freenet/node/fcp/ClientPutDir.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ClientPutDir.java 2007-03-17
19:14:14 UTC (rev 12197)
+++ trunk/freenet/src/freenet/node/fcp/ClientPutDir.java 2007-03-17
20:31:38 UTC (rev 12198)
@@ -119,7 +119,7 @@
Logger.error(this, "File no longer
exists, cancelling upload: "+ff);
throw new IOException("File no longer
exists, cancelling upload: "+ff);
}
- data = new FileBucket(ff, true, false, false,
false);
+ data = new FileBucket(ff, true, false, false,
false, false);
me = new ManifestElement(name, data,
contentTypeOverride, sz);
fileCount++;
} else if(uploadFrom.equalsIgnoreCase("redirect")) {
Modified: trunk/freenet/src/freenet/node/fcp/ClientPutDiskDirMessage.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ClientPutDiskDirMessage.java
2007-03-17 19:14:14 UTC (rev 12197)
+++ trunk/freenet/src/freenet/node/fcp/ClientPutDiskDirMessage.java
2007-03-17 20:31:38 UTC (rev 12198)
@@ -77,7 +77,7 @@
if (filelist[i].isFile()) {
File f = filelist[i];
- FileBucket bucket = new FileBucket(f, true,
false, false, false);
+ FileBucket bucket = new FileBucket(f, true,
false, false, false, false);
ret.put(f.getName(), new
ManifestElement(f.getName(), prefix + f.getName(), bucket,
DefaultMIMETypes.guessMIMEType(f.getName(), true), f.length()));
} else if(filelist[i].isDirectory()) {
Modified: trunk/freenet/src/freenet/node/fcp/ClientPutMessage.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ClientPutMessage.java 2007-03-17
19:14:14 UTC (rev 12197)
+++ trunk/freenet/src/freenet/node/fcp/ClientPutMessage.java 2007-03-17
20:31:38 UTC (rev 12198)
@@ -140,7 +140,7 @@
if(!(f.exists() && f.isFile() && f.canRead()))
throw new
MessageInvalidException(ProtocolErrorMessage.FILE_NOT_FOUND, null, identifier,
global);
dataLength = f.length();
- FileBucket fileBucket = new FileBucket(f, true, false,
false, false);
+ FileBucket fileBucket = new FileBucket(f, true, false,
false, false, false);
this.bucket = fileBucket;
this.origFilename = f;
redirectTarget = null;
Modified: trunk/freenet/src/freenet/node/fcp/DiskDirPutFile.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/DiskDirPutFile.java 2007-03-17
19:14:14 UTC (rev 12197)
+++ trunk/freenet/src/freenet/node/fcp/DiskDirPutFile.java 2007-03-17
20:31:38 UTC (rev 12198)
@@ -31,7 +31,7 @@
}
public Bucket getData() {
- return new FileBucket(file, true, false, false, false);
+ return new FileBucket(file, true, false, false, false, false);
}
}
Modified: trunk/freenet/src/freenet/support/io/FileBucket.java
===================================================================
--- trunk/freenet/src/freenet/support/io/FileBucket.java 2007-03-17
19:14:14 UTC (rev 12197)
+++ trunk/freenet/src/freenet/support/io/FileBucket.java 2007-03-17
20:31:38 UTC (rev 12198)
@@ -27,6 +27,8 @@
protected boolean readOnly;
protected boolean deleteOnFinalize;
protected boolean deleteOnFree;
+ protected final boolean deleteOnExit;
+ protected final boolean createFileOnly;
protected long length;
// JVM caches File.size() and there is no way to flush the cache, so we
// need to track it ourselves
@@ -38,17 +40,26 @@
* Creates a new FileBucket.
*
* @param file The File to read and write to.
+ * @param createFileOnly If true, create the file if it doesn't exist,
but if it does exist,
+ * throw a FileExistsException on any write operation. This is safe
against symlink attacks
+ * because we write to a temp file and then rename. It is technically
possible that the rename
+ * will clobber an existing file if there is a race condition, but
since it will not write over
+ * a symlink this is probably not dangerous. User-supplied filenames
should in any case be
+ * restricted by higher levels.
* @param readOnly If true, any attempt to write to the bucket will
result in an IOException.
* Can be set later. Irreversible. @see isReadOnly(), setReadOnly()
* @param deleteOnFinalize If true, delete the file on finalization.
Reversible.
* @param deleteOnExit If true, delete the file on a clean exit of the
JVM. Irreversible - use with care!
*/
- public FileBucket(File file, boolean readOnly, boolean
deleteOnFinalize, boolean deleteOnExit, boolean deleteOnFree) {
+ public FileBucket(File file, boolean readOnly, boolean createFileOnly,
boolean deleteOnFinalize, boolean deleteOnExit, boolean deleteOnFree) {
if(file == null) throw new NullPointerException();
+ file = file.getAbsoluteFile();
this.readOnly = readOnly;
+ this.createFileOnly = createFileOnly;
this.file = file;
this.deleteOnFinalize = deleteOnFinalize;
this.deleteOnFree = deleteOnFree;
+ this.deleteOnExit = deleteOnExit;
if(deleteOnExit) {
try {
file.deleteOnExit();
@@ -74,28 +85,28 @@
/**
* Creates a new FileBucket in a random temporary file in the temporary
* directory.
+ * @throws IOException
*/
- public FileBucket(RandomSource random) {
+ public FileBucket(RandomSource random) throws IOException {
// **FIXME**/TODO: locking on tempDir needs to be checked by a
Java guru for consistency
- file =
- new File(
- tempDir,
- 't'
- + Integer.toHexString(
- Math.abs(random.nextInt())));
+ file = File.createTempFile(tempDir, ".freenet.tmp");
+ createFileOnly = true;
// Useful for finding temp file leaks.
//System.err.println("-- FileBucket.ctr(1) -- " +
// file.getAbsolutePath());
//(new Exception("get stack")).printStackTrace();
deleteOnFinalize = true;
+ deleteOnExit = true;
length = 0;
file.deleteOnExit();
}
public FileBucket(SimpleFieldSet fs, PersistentFileTracker f) throws
CannotCreateFromFieldSetException {
+ createFileOnly = true;
+ deleteOnExit = false;
String tmp = fs.get("Filename");
if(tmp == null) throw new CannotCreateFromFieldSetException("No
filename");
- this.file = new File(tmp);
+ this.file = new File(tmp).getAbsoluteFile();
tmp = fs.get("Length");
if(tmp == null) throw new CannotCreateFromFieldSetException("No
length");
try {
@@ -112,19 +123,31 @@
synchronized (this) {
if(readOnly)
throw new IOException("Bucket is read-only");
-
+
+ if(createFileOnly && file.exists())
+ throw new FileExistsException(file);
+
// FIXME: behaviour depends on UNIX semantics, to
totally abstract
// it out we would have to kill the old write streams
here
// FIXME: what about existing streams? Will ones on
append append
// to the new truncated file? Do we want them to? What
about
// truncated ones? We should kill old streams here,
right?
- return newFileBucketOutputStream(file.getPath(),
++fileRestartCounter);
+ return newFileBucketOutputStream(createFileOnly ?
getTempfile() : file, file.getPath(), ++fileRestartCounter);
}
}
+ /**
+ * Create a temporary file in the same directory as this file.
+ */
+ protected File getTempfile() throws IOException {
+ File f = File.createTempFile(file.getName(), ".freenet-tmp",
file.getParentFile());
+ if(deleteOnExit) f.deleteOnExit();
+ return f;
+ }
+
protected FileBucketOutputStream newFileBucketOutputStream(
- String s, long streamNumber) throws IOException {
- return new FileBucketOutputStream(s, streamNumber);
+ File tempfile, String s, long streamNumber) throws IOException {
+ return new FileBucketOutputStream(tempfile, s, streamNumber);
}
protected synchronized void resetLength() {
@@ -134,11 +157,13 @@
class FileBucketOutputStream extends FileOutputStream {
private long restartCount;
+ private File tempfile;
protected FileBucketOutputStream(
- String s, long restartCount)
+ File tempfile, String s, long restartCount)
throws FileNotFoundException {
- super(s, false);
+ super(tempfile, false);
+ this.tempfile = tempfile;
resetLength();
this.restartCount = restartCount;
}
@@ -173,6 +198,26 @@
length++;
}
}
+
+ public void close() throws IOException {
+ try {
+ super.close();
+ } catch (IOException e) {
+ if(createFileOnly) tempfile.delete();
+ throw e;
+ }
+ if(createFileOnly) {
+ if(file.exists()) {
+ tempfile.delete();
+ throw new FileExistsException(file);
+ }
+ if(!tempfile.renameTo(file)) {
+ if(file.exists()) throw new
FileExistsException(file);
+ tempfile.delete();
+ throw new IOException("Cannot rename
file");
+ }
+ }
+ }
}
class FileBucketInputStream extends FileInputStream {
Modified: trunk/freenet/src/freenet/support/io/FileBucketFactory.java
===================================================================
--- trunk/freenet/src/freenet/support/io/FileBucketFactory.java 2007-03-17
19:14:14 UTC (rev 12197)
+++ trunk/freenet/src/freenet/support/io/FileBucketFactory.java 2007-03-17
20:31:38 UTC (rev 12198)
@@ -10,37 +10,18 @@
public class FileBucketFactory implements BucketFactory {
- private int enumm = 0;
private Vector files = new Vector();
// Must have trailing "/"
- public String rootDir = "";
+ public final File rootDir;
- public FileBucketFactory() {
-
+ public FileBucketFactory(File rootDir) {
+ this.rootDir = rootDir;
}
- public FileBucketFactory(String rootDir) {
- this.rootDir = (rootDir.endsWith(File.separator)
- ? rootDir
- : (rootDir + File.separator));
- }
-
- public FileBucketFactory(File dir) {
- this(dir.toString());
- }
-
- public Bucket makeBucket(long size) {
- File f;
- do {
- f = new File(rootDir + "bffile_" + ++enumm);
- // REDFLAG: remove hoaky debugging code
- // System.err.println("----------------------------------------");
- // Exception e = new Exception("created: " + f.getName());
- // e.printStackTrace();
- // System.err.println("----------------------------------------");
- } while (f.exists());
- Bucket b = new FileBucket(f, false, true, false, true);
+ public Bucket makeBucket(long size) throws IOException {
+ File f = File.createTempFile("bf_", ".freenet-tmp", rootDir);
+ Bucket b = new FileBucket(f, false, true, true, false, true);
files.addElement(f);
return b;
}
Added: trunk/freenet/src/freenet/support/io/FileExistsException.java
===================================================================
--- trunk/freenet/src/freenet/support/io/FileExistsException.java
(rev 0)
+++ trunk/freenet/src/freenet/support/io/FileExistsException.java
2007-03-17 20:31:38 UTC (rev 12198)
@@ -0,0 +1,19 @@
+package freenet.support.io;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Thrown when a FileBucket is required to create a new file but not overwrite
an existing file,
+ * and the file exists.
+ */
+public class FileExistsException extends IOException {
+
+ public final File file;
+
+ public FileExistsException(File f) {
+ super("File exists: "+f);
+ this.file = f;
+ }
+
+}
Modified: trunk/freenet/src/freenet/support/io/PersistentTempBucketFactory.java
===================================================================
--- trunk/freenet/src/freenet/support/io/PersistentTempBucketFactory.java
2007-03-17 19:14:14 UTC (rev 12197)
+++ trunk/freenet/src/freenet/support/io/PersistentTempBucketFactory.java
2007-03-17 20:31:38 UTC (rev 12198)
@@ -71,22 +71,6 @@
bucketsToFree = new LinkedList();
}
- /**
- * Called by a client to fetch the bucket denoted by a specific
filename,
- * and to register this fact so that it is not deleted on startup
completion.
- * @throws IOException
- */
- public Bucket register(String filename, boolean mustExist) throws
IOException {
- File f = new File(dir, filename);
- if(mustExist && !f.exists())
- throw new IOException("File does not exist (deleted?):
"+f);
- Bucket b = new FileBucket(f, false, false, false, true);
- synchronized(this) {
- originalFiles.remove(f);
- }
- return b;
- }
-
public void register(File file) {
synchronized(this) {
originalFiles.remove(file);
@@ -106,7 +90,7 @@
}
private Bucket makeRawBucket(long size) throws IOException {
- return new FileBucket(fg.makeRandomFilename(), false, false,
false, true);
+ return new FileBucket(fg.makeRandomFilename(), false, true,
false, false, true);
}
public Bucket makeBucket(long size) throws IOException {
Modified:
trunk/freenet/src/freenet/support/io/SerializableToFieldSetBucketUtil.java
===================================================================
--- trunk/freenet/src/freenet/support/io/SerializableToFieldSetBucketUtil.java
2007-03-17 19:14:14 UTC (rev 12197)
+++ trunk/freenet/src/freenet/support/io/SerializableToFieldSetBucketUtil.java
2007-03-17 20:31:38 UTC (rev 12198)
@@ -44,7 +44,7 @@
if(persistent.exists()) fnam =
persistent;
}
f.register(fnam);
- FileBucket fb = new FileBucket(fnam, false,
false, false, true);
+ FileBucket fb = new FileBucket(fnam, false,
true, false, false, true);
try {
PaddedEphemerallyEncryptedBucket eb =
new
PaddedEphemerallyEncryptedBucket(fb, 1024, len, decryptKey, random, true);
Modified: trunk/freenet/src/freenet/support/io/TempFileBucket.java
===================================================================
--- trunk/freenet/src/freenet/support/io/TempFileBucket.java 2007-03-17
19:14:14 UTC (rev 12197)
+++ trunk/freenet/src/freenet/support/io/TempFileBucket.java 2007-03-17
20:31:38 UTC (rev 12198)
@@ -38,7 +38,7 @@
long minAlloc,
float factor)
throws IOException {
- super(f, false, true, true, true);
+ super(f, false, false, true, true, true);
synchronized(this) {
logDebug = Logger.shouldLog(Logger.DEBUG, this);
}
@@ -274,7 +274,7 @@
if (hook != null)
return new HookedFileBucketOutputStream(s,
restartCount);
else
- return super.newFileBucketOutputStream(s, restartCount);
+ return super.newFileBucketOutputStream(new File(s), s,
restartCount);
}
protected synchronized void deleteFile() {
@@ -353,7 +353,7 @@
String s,
long restartCount)
throws IOException {
- super(s, restartCount);
+ super(new File(s), s, restartCount);
streams.addElement(this);
if (Logger.shouldLog(Logger.DEBUG, this))
Logger.debug(