Author: toad
Date: 2007-03-20 21:14:55 +0000 (Tue, 20 Mar 2007)
New Revision: 12238
Modified:
trunk/freenet/src/freenet/clients/http/QueueToadlet.java
trunk/freenet/src/freenet/node/NodeClientCore.java
trunk/freenet/src/freenet/node/fcp/ClientGet.java
trunk/freenet/src/freenet/node/fcp/ClientPut.java
trunk/freenet/src/freenet/node/fcp/ClientPutComplexDirMessage.java
trunk/freenet/src/freenet/node/fcp/ClientPutDiskDirMessage.java
trunk/freenet/src/freenet/node/fcp/DiskDirPutFile.java
trunk/freenet/src/freenet/node/fcp/FCPConnectionHandler.java
trunk/freenet/src/freenet/node/fcp/FCPServer.java
Log:
Add downloadAllowedDir/uploadAllowedDir options (defaults to downloads and all
respectively). These control where downloads are allowed to be made to, and
where uploads are allowed to be made from.
Previously we relied on the node not overwriting existing files; it turns out
that this protection is illusory.
Modified: trunk/freenet/src/freenet/clients/http/QueueToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/QueueToadlet.java 2007-03-20
20:57:00 UTC (rev 12237)
+++ trunk/freenet/src/freenet/clients/http/QueueToadlet.java 2007-03-20
21:14:55 UTC (rev 12238)
@@ -27,6 +27,7 @@
import freenet.node.fcp.FCPServer;
import freenet.node.fcp.IdentifierCollisionException;
import freenet.node.fcp.MessageInvalidException;
+import freenet.node.fcp.NotAllowedException;
import freenet.support.HTMLNode;
import freenet.support.Logger;
import freenet.support.MultiValueTable;
@@ -161,7 +162,12 @@
}
String persistence =
request.getPartAsString("persistence", 32);
String returnType =
request.getPartAsString("return-type", 32);
- fcp.makePersistentGlobalRequest(fetchURI,
expectedMIMEType, persistence, returnType);
+ try {
+
fcp.makePersistentGlobalRequest(fetchURI, expectedMIMEType, persistence,
returnType);
+ } catch (NotAllowedException e) {
+ this.writeError("Cannot download to
disk", "The node's current configuration does not allow you to download files
to the downloads directory.", ctx);
+ return;
+ }
writePermanentRedirect(ctx, "Done", "/queue/");
return;
} else if (request.isPartSet("change_priority")) {
@@ -215,6 +221,9 @@
fcp.forceStorePersistentRequests();
} catch (IdentifierCollisionException e) {
e.printStackTrace();
+ } catch (NotAllowedException e) {
+ this.writeError("Not allowed to upload
from "+file, "The current configuration of the node prohibits you from
uploading the file "+file+".", ctx);
+ return;
}
writePermanentRedirect(ctx, "Done", "/queue/");
return;
@@ -231,6 +240,9 @@
fcp.forceStorePersistentRequests();
} catch (IdentifierCollisionException e) {
e.printStackTrace();
+ } catch (NotAllowedException e) {
+ this.writeError("Not allowed to upload
from "+file, "The current configuration of the node prohibits you from
uploading the file "+file+".", ctx);
+ return;
}
writePermanentRedirect(ctx, "Done", "/queue/");
return;
Modified: trunk/freenet/src/freenet/node/NodeClientCore.java
===================================================================
--- trunk/freenet/src/freenet/node/NodeClientCore.java 2007-03-20 20:57:00 UTC
(rev 12237)
+++ trunk/freenet/src/freenet/node/NodeClientCore.java 2007-03-20 21:14:55 UTC
(rev 12238)
@@ -46,6 +46,7 @@
import freenet.support.SimpleFieldSet;
import freenet.support.api.BooleanCallback;
import freenet.support.api.BucketFactory;
+import freenet.support.api.StringArrCallback;
import freenet.support.api.StringCallback;
import freenet.support.io.FilenameGenerator;
import freenet.support.io.PaddedEphemerallyEncryptedBucketFactory;
@@ -67,6 +68,11 @@
public final String formPassword;
File downloadDir;
+ private File[] downloadAllowedDirs;
+ private boolean includeDownloadDir;
+ private boolean downloadAllowedEverywhere;
+ private File[] uploadAllowedDirs;
+ private boolean uploadAllowedEverywhere;
final FilenameGenerator tempFilenameGenerator;
public final BucketFactory tempBucketFactory;
final Node node;
@@ -190,7 +196,54 @@
throw new
NodeInitException(Node.EXIT_BAD_DOWNLOADS_DIR, "Could not find or create
default downloads directory");
}
+ // Downloads allowed, uploads allowed
+
+ nodeConfig.register("downloadAllowedDirs", new String[]
{"downloads"}, sortOrder++, true, true, "Directories downloading is allowed
to",
+ "Semicolon separated list of directories to
which downloads are allowed. \"downloads\" means downloadsDir, empty means no
downloads to disk allowed, \"all\" means downloads allowed from anywhere. "+
+ "WARNING! If this is set to \"all\" any user
can download any file to anywhere on your computer!",
+ new StringArrCallback() {
+ public String[] get() {
+
synchronized(NodeClientCore.this) {
+
if(downloadAllowedEverywhere) return new String[] { "all" };
+ String[] dirs = new
String[downloadAllowedDirs.length + (includeDownloadDir ? 1 : 0) ];
+ for(int
i=0;i<downloadAllowedDirs.length;i++)
+ dirs[i] =
downloadAllowedDirs[i].getPath();
+ if(includeDownloadDir)
+
dirs[downloadAllowedDirs.length] = "downloads";
+ return dirs;
+ }
+ }
+
+ public void set(String[] val) throws
InvalidConfigValueException {
+ setDownloadAllowedDirs(val);
+ }
+
+ });
+
setDownloadAllowedDirs(nodeConfig.getStringArr("downloadAllowedDirs"));
+
+ nodeConfig.register("uploadAllowedDirs", new String[] {"all"},
sortOrder++, true, true, "Directories uploading is allowed from",
+ "Semicolon separated list of directories from
which uploads are allowed. Empty means no uploads from disk allowed, \"all\"
means uploads allowed from anywhere (including system files etc!)."+
+ "WARNING! If this is set to \"all\" any file on
your computer can be uploaded by any user.",
+ new StringArrCallback() {
+
+ public String[] get() {
+
synchronized(NodeClientCore.this) {
+
if(uploadAllowedEverywhere) return new String[] { "all" };
+ String[] dirs = new
String[uploadAllowedDirs.length];
+ for(int
i=0;i<uploadAllowedDirs.length;i++)
+ dirs[i] =
uploadAllowedDirs[i].getPath();
+ return dirs;
+ }
+ }
+
+ public void set(String[] val) throws
InvalidConfigValueException {
+ setUploadAllowedDirs(val);
+ }
+
+ });
+
setUploadAllowedDirs(nodeConfig.getStringArr("uploadAllowedDirs"));
+
archiveManager = new ArchiveManager(MAX_ARCHIVE_HANDLERS,
MAX_CACHED_ARCHIVE_DATA, MAX_ARCHIVE_SIZE, MAX_ARCHIVED_FILE_SIZE,
MAX_CACHED_ELEMENTS, random, tempFilenameGenerator);
Logger.normal(this, "Initializing USK Manager");
System.out.println("Initializing USK Manager");
@@ -238,6 +291,47 @@
}
+ protected synchronized void setDownloadAllowedDirs(String[] val) {
+ int x = 0;
+ downloadAllowedEverywhere = false;
+ includeDownloadDir = false;
+ int i = 0;
+ downloadAllowedDirs = new File[val.length];
+ for(i=0;i<downloadAllowedDirs.length;i++) {
+ String s = val[i];
+ if(s.equals("downloads"))
+ includeDownloadDir = true;
+ else if(s.equals("all"))
+ downloadAllowedEverywhere = true;
+ else
+ downloadAllowedDirs[x++] = new File(val[i]);
+ }
+ if(x != i) {
+ File[] newDirs = new File[x];
+ System.arraycopy(downloadAllowedDirs, 0, newDirs, 0, x);
+ downloadAllowedDirs = newDirs;
+ }
+ }
+
+ protected synchronized void setUploadAllowedDirs(String[] val) {
+ int x = 0;
+ int i = 0;
+ uploadAllowedEverywhere = false;
+ uploadAllowedDirs = new File[val.length];
+ for(i=0;i<uploadAllowedDirs.length;i++) {
+ String s = val[i];
+ if(s.equals("all"))
+ uploadAllowedEverywhere = true;
+ else
+ uploadAllowedDirs[x++] = new File(val[i]);
+ }
+ if(x != i) {
+ File[] newDirs = new File[x];
+ System.arraycopy(uploadAllowedDirs, 0, newDirs, 0, x);
+ uploadAllowedDirs = newDirs;
+ }
+ }
+
public void start(Config config) throws NodeInitException {
// TMCI
@@ -826,4 +920,68 @@
public boolean lazyResume() {
return lazyResume;
}
+
+ public synchronized File[] getAllowedDownloadDirs() {
+ if(!includeDownloadDir)
+ return downloadAllowedDirs;
+ else {
+ File[] realDownloadAllowedDirs = new
File[downloadAllowedDirs.length+1];
+ realDownloadAllowedDirs[downloadAllowedDirs.length] =
downloadDir;
+ return realDownloadAllowedDirs;
+ }
+ }
+
+ public File[] getAllowedUploadDirs() {
+ return uploadAllowedDirs;
+ }
+
+ public boolean allowDownloadTo(File filename) {
+ if(downloadAllowedEverywhere) return true;
+ if(includeDownloadDir) {
+ if(isParent(downloadDir, filename)) return true;
+ }
+ for(int i=0;i<downloadAllowedDirs.length;i++) {
+ if(isParent(downloadAllowedDirs[i], filename)) return
true;
+ }
+ return false;
+ }
+
+ public boolean allowUploadFrom(File filename) {
+ if(uploadAllowedEverywhere) return true;
+ for(int i=0;i<uploadAllowedDirs.length;i++) {
+ if(isParent(uploadAllowedDirs[i], filename)) return
true;
+ }
+ return false;
+ }
+
+ /** Is possParent a parent of filename?
+ * FIXME Move somewhere generic.
+ * Why doesn't java provide this? :( */
+ private boolean isParent(File possParent, File filename) {
+ File canonParent;
+ File canonFile;
+ try {
+ canonParent = possParent.getCanonicalFile();
+ } catch (IOException e) {
+ canonParent = possParent.getAbsoluteFile();
+ }
+ try {
+ canonFile = filename.getCanonicalFile();
+ } catch (IOException e) {
+ canonFile = filename.getAbsoluteFile();
+ }
+ if(isParentInner(possParent, filename)) return true;
+ if(isParentInner(possParent, canonFile)) return true;
+ if(isParentInner(canonParent, filename)) return true;
+ if(isParentInner(canonParent, canonFile)) return true;
+ return false;
+ }
+
+ private boolean isParentInner(File possParent, File filename) {
+ while(true) {
+ if(filename.equals(possParent)) return true;
+ filename = filename.getParentFile();
+ if(filename == null) return false;
+ }
+ }
}
Modified: trunk/freenet/src/freenet/node/fcp/ClientGet.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ClientGet.java 2007-03-20 20:57:00 UTC
(rev 12237)
+++ trunk/freenet/src/freenet/node/fcp/ClientGet.java 2007-03-20 21:14:55 UTC
(rev 12238)
@@ -68,11 +68,12 @@
/**
* Create one for a global-queued request not made by FCP.
* @throws IdentifierCollisionException
+ * @throws NotAllowedException
*/
public ClientGet(FCPClient globalClient, FreenetURI uri, boolean
dsOnly, boolean ignoreDS,
int maxSplitfileRetries, int maxNonSplitfileRetries,
long maxOutputLength,
short returnType, boolean persistRebootOnly, String
identifier, int verbosity, short prioClass,
- File returnFilename, File returnTempFilename) throws
IdentifierCollisionException {
+ File returnFilename, File returnTempFilename) throws
IdentifierCollisionException, NotAllowedException {
super(uri, identifier, verbosity, null, globalClient, prioClass,
(persistRebootOnly ?
ClientRequest.PERSIST_REBOOT : ClientRequest.PERSIST_FOREVER),
null, true);
@@ -90,6 +91,8 @@
if(returnType == ClientGetMessage.RETURN_TYPE_DISK) {
this.targetFile = returnFilename;
this.tempFile = returnTempFilename;
+ if(!(client.core.allowDownloadTo(returnTempFilename) &&
client.core.allowDownloadTo(returnFilename)))
+ throw new NotAllowedException();
ret = new FileBucket(returnTempFilename, false, true,
false, false, false);
} else if(returnType == ClientGetMessage.RETURN_TYPE_NONE) {
targetFile = null;
@@ -126,7 +129,7 @@
}
- public ClientGet(FCPConnectionHandler handler, ClientGetMessage
message) throws IdentifierCollisionException {
+ public ClientGet(FCPConnectionHandler handler, ClientGetMessage
message) throws IdentifierCollisionException, MessageInvalidException {
super(message.uri, message.identifier, message.verbosity,
handler, message.priorityClass,
message.persistenceType, message.clientToken,
message.global);
// Create a Fetcher directly in order to get more fine-grained
control,
@@ -147,6 +150,8 @@
if(returnType == ClientGetMessage.RETURN_TYPE_DISK) {
this.targetFile = message.diskFile;
this.tempFile = message.tempFile;
+ if(!(client.core.allowDownloadTo(tempFile) &&
client.core.allowDownloadTo(targetFile)))
+ throw new
MessageInvalidException(ProtocolErrorMessage.ACCESS_DENIED, "Not allowed to
download to "+tempFile+" or "+targetFile, identifier, global);
ret = new FileBucket(message.tempFile, false, true,
false, false, false);
} else if(returnType == ClientGetMessage.RETURN_TYPE_NONE) {
targetFile = null;
Modified: trunk/freenet/src/freenet/node/fcp/ClientPut.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ClientPut.java 2007-03-20 20:57:00 UTC
(rev 12237)
+++ trunk/freenet/src/freenet/node/fcp/ClientPut.java 2007-03-20 21:14:55 UTC
(rev 12238)
@@ -79,12 +79,17 @@
* The URI to redirect to (if <code>uploadFromType</code> is
* UPLOAD_FROM_REDIRECT)
* @throws IdentifierCollisionException
+ * @throws NotAllowedException
*/
public ClientPut(FCPClient globalClient, FreenetURI uri, String
identifier, int verbosity,
short priorityClass, short persistenceType, String
clientToken, boolean getCHKOnly,
boolean dontCompress, int maxRetries, short
uploadFromType, File origFilename, String contentType,
- Bucket data, FreenetURI redirectTarget, String
targetFilename, boolean earlyEncode) throws IdentifierCollisionException {
+ Bucket data, FreenetURI redirectTarget, String
targetFilename, boolean earlyEncode) throws IdentifierCollisionException,
NotAllowedException {
super(uri, identifier, verbosity, null, globalClient,
priorityClass, persistenceType, null, true, getCHKOnly, dontCompress,
maxRetries, earlyEncode);
+ if(uploadFromType == ClientPutMessage.UPLOAD_FROM_DISK) {
+ if(!globalClient.core.allowUploadFrom(origFilename))
+ throw new NotAllowedException();
+ }
logMINOR = Logger.shouldLog(Logger.MINOR, this);
this.targetFilename = targetFilename;
this.uploadFrom = uploadFromType;
@@ -131,10 +136,14 @@
}
}
- public ClientPut(FCPConnectionHandler handler, ClientPutMessage
message) throws IdentifierCollisionException {
+ public ClientPut(FCPConnectionHandler handler, ClientPutMessage
message) throws IdentifierCollisionException, MessageInvalidException {
super(message.uri, message.identifier, message.verbosity,
handler,
message.priorityClass, message.persistenceType,
message.clientToken, message.global,
message.getCHKOnly, message.dontCompress,
message.maxRetries, message.earlyEncode);
+ if(message.uploadFromType == ClientPutMessage.UPLOAD_FROM_DISK)
{
+
if(!handler.server.core.allowUploadFrom(message.origFilename))
+ throw new
MessageInvalidException(ProtocolErrorMessage.ACCESS_DENIED, "Not allowed to
upload from "+message.origFilename, identifier, global);
+ }
this.targetFilename = message.targetFilename;
logMINOR = Logger.shouldLog(Logger.MINOR, this);
this.uploadFrom = message.uploadFromType;
Modified: trunk/freenet/src/freenet/node/fcp/ClientPutComplexDirMessage.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ClientPutComplexDirMessage.java
2007-03-20 20:57:00 UTC (rev 12237)
+++ trunk/freenet/src/freenet/node/fcp/ClientPutComplexDirMessage.java
2007-03-20 21:14:55 UTC (rev 12238)
@@ -142,7 +142,7 @@
// of ManifestElement's.
// Then simply create the ClientPutDir.
HashMap manifestElements = new HashMap();
- convertFilesByNameToManifestElements(filesByName,
manifestElements);
+ convertFilesByNameToManifestElements(filesByName,
manifestElements, node);
handler.startClientPutDir(this, manifestElements);
}
@@ -150,7 +150,7 @@
* Convert a hierarchy of HashMap's containing DirPutFile's into a
hierarchy of
* HashMap's containing ManifestElement's.
*/
- private void convertFilesByNameToManifestElements(HashMap filesByName,
HashMap manifestElements) {
+ private void convertFilesByNameToManifestElements(HashMap filesByName,
HashMap manifestElements, Node node) throws MessageInvalidException {
Iterator i = filesByName.keySet().iterator();
while(i.hasNext()) {
String tempName = (String) (i.next());
@@ -159,9 +159,11 @@
HashMap h = (HashMap) val;
HashMap manifests = new HashMap();
manifestElements.put(tempName, manifests);
- convertFilesByNameToManifestElements(h,
manifests);
+ convertFilesByNameToManifestElements(h,
manifests, node);
} else {
DirPutFile f = (DirPutFile) val;
+ if(f instanceof DiskDirPutFile &&
!node.clientCore.allowUploadFrom(((DiskDirPutFile)f).getFile()))
+ throw new
MessageInvalidException(ProtocolErrorMessage.ACCESS_DENIED, "Not allowed to
upload "+((DiskDirPutFile) f).getFile(), identifier, global);
ManifestElement e = f.getElement();
manifestElements.put(tempName, e);
}
Modified: trunk/freenet/src/freenet/node/fcp/ClientPutDiskDirMessage.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ClientPutDiskDirMessage.java
2007-03-20 20:57:00 UTC (rev 12237)
+++ trunk/freenet/src/freenet/node/fcp/ClientPutDiskDirMessage.java
2007-03-20 21:14:55 UTC (rev 12238)
@@ -49,6 +49,8 @@
public void run(FCPConnectionHandler handler, Node node)
throws MessageInvalidException {
+ if(!handler.server.core.allowUploadFrom(dirname))
+ throw new
MessageInvalidException(ProtocolErrorMessage.ACCESS_DENIED, "Not allowed to
upload from "+dirname, identifier, global);
// Create a directory listing of Buckets of data, mapped to
ManifestElement's.
// Directories are sub-HashMap's.
HashMap buckets = makeBucketsByName(dirname, "");
Modified: trunk/freenet/src/freenet/node/fcp/DiskDirPutFile.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/DiskDirPutFile.java 2007-03-20
20:57:00 UTC (rev 12237)
+++ trunk/freenet/src/freenet/node/fcp/DiskDirPutFile.java 2007-03-20
21:14:55 UTC (rev 12238)
@@ -34,4 +34,8 @@
return new FileBucket(file, true, false, false, false, false);
}
+ public File getFile() {
+ return file;
+ }
+
}
Modified: trunk/freenet/src/freenet/node/fcp/FCPConnectionHandler.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/FCPConnectionHandler.java
2007-03-20 20:57:00 UTC (rev 12237)
+++ trunk/freenet/src/freenet/node/fcp/FCPConnectionHandler.java
2007-03-20 21:14:55 UTC (rev 12238)
@@ -119,6 +119,9 @@
requestsByIdentifier.put(id,
cg);
} catch (IdentifierCollisionException e) {
success = false;
+ } catch (MessageInvalidException e) {
+ outputHandler.queue(new
ProtocolErrorMessage(e.protocolCode, false, e.getMessage(), e.ident, false));
+ return;
}
}
}
@@ -157,6 +160,9 @@
cp = new ClientPut(this, message);
} catch (IdentifierCollisionException e) {
success = false;
+ } catch (MessageInvalidException e) {
+ outputHandler.queue(new
ProtocolErrorMessage(e.protocolCode, false, e.getMessage(), e.ident, false));
+ return;
}
if(!persistent)
requestsByIdentifier.put(id, cp);
Modified: trunk/freenet/src/freenet/node/fcp/FCPServer.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/FCPServer.java 2007-03-20 20:57:00 UTC
(rev 12237)
+++ trunk/freenet/src/freenet/node/fcp/FCPServer.java 2007-03-20 21:14:55 UTC
(rev 12238)
@@ -653,8 +653,9 @@
* @param fetchURI The file to fetch.
* @param persistence The persistence type.
* @param returnType The return type.
+ * @throws NotAllowedException
*/
- public void makePersistentGlobalRequest(FreenetURI fetchURI, String
expectedMimeType, String persistenceTypeString, String returnTypeString) {
+ public void makePersistentGlobalRequest(FreenetURI fetchURI, String
expectedMimeType, String persistenceTypeString, String returnTypeString) throws
NotAllowedException {
boolean persistence =
persistenceTypeString.equalsIgnoreCase("reboot");
short returnType =
ClientGetMessage.parseReturnType(returnTypeString);
File returnFilename = null, returnTempFilename = null;
@@ -732,7 +733,7 @@
}
private void innerMakePersistentGlobalRequest(FreenetURI fetchURI,
boolean persistRebootOnly, short returnType, String id, File returnFilename,
- File returnTempFilename) throws
IdentifierCollisionException {
+ File returnTempFilename) throws
IdentifierCollisionException, NotAllowedException {
ClientGet cg =
new ClientGet(globalClient, fetchURI,
defaultFetchContext.localRequestOnly,
defaultFetchContext.ignoreStore,
QUEUE_MAX_RETRIES, QUEUE_MAX_RETRIES,