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,


Reply via email to