From e940f51599f7b363b6d2e8c331037b147e05dc17 Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Fri, 19 Nov 2010 00:40:33 +0000 Subject: [PATCH] * SecureFile: New class, catch places that were missed, add i2p.insecureFiles option to disable (default false) --- .../src/net/i2p/addressbook/ConfigParser.java | 3 +- .../src/org/klomp/snark/I2PSnarkUtil.java | 3 +- .../java/src/org/klomp/snark/Storage.java | 5 +- core/java/src/net/i2p/I2PAppContext.java | 18 +++++ .../src/net/i2p/util/SecureDirectory.java | 9 +-- core/java/src/net/i2p/util/SecureFile.java | 81 +++++++++++++++++++ .../net/i2p/util/SecureFileOutputStream.java | 16 +++- 7 files changed, 124 insertions(+), 11 deletions(-) create mode 100644 core/java/src/net/i2p/util/SecureFile.java diff --git a/apps/addressbook/java/src/net/i2p/addressbook/ConfigParser.java b/apps/addressbook/java/src/net/i2p/addressbook/ConfigParser.java index c963ecce54..44bbbe7813 100644 --- a/apps/addressbook/java/src/net/i2p/addressbook/ConfigParser.java +++ b/apps/addressbook/java/src/net/i2p/addressbook/ConfigParser.java @@ -35,6 +35,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import net.i2p.util.SecureFile; import net.i2p.util.SecureFileOutputStream; /** @@ -280,7 +281,7 @@ public class ConfigParser { * if file cannot be written to. */ public static void write(Map map, File file) throws IOException { - File tmp = File.createTempFile("hoststxt-", ".tmp", file.getAbsoluteFile().getParentFile()); + File tmp = SecureFile.createTempFile("hoststxt-", ".tmp", file.getAbsoluteFile().getParentFile()); ConfigParser .write(map, new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(tmp), "UTF-8"))); boolean success = tmp.renameTo(file); diff --git a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java index 8edc92fdf3..001928d479 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java +++ b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java @@ -27,6 +27,7 @@ import net.i2p.util.EepGet; import net.i2p.util.FileUtil; import net.i2p.util.Log; import net.i2p.util.SecureDirectory; +import net.i2p.util.SecureFile; import net.i2p.util.SimpleScheduler; import net.i2p.util.SimpleTimer; import net.i2p.util.Translate; @@ -244,7 +245,7 @@ public class I2PSnarkUtil { File out = null; try { // we could use the system tmp dir but deleteOnExit() doesn't seem to work on all platforms... - out = File.createTempFile("i2psnark", null, _tmpDir); + out = SecureFile.createTempFile("i2psnark", null, _tmpDir); } catch (IOException ioe) { ioe.printStackTrace(); if (out != null) diff --git a/apps/i2psnark/java/src/org/klomp/snark/Storage.java b/apps/i2psnark/java/src/org/klomp/snark/Storage.java index 3e2a4ad631..87aa0d3572 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Storage.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Storage.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.StringTokenizer; import net.i2p.crypto.SHA1; +import net.i2p.util.SecureFile; /** * Maintains pieces on disk. Can be used to store and retrieve pieces. @@ -462,7 +463,7 @@ public class Storage /** use a saved bitfield and timestamp from a config file */ public void check(String rootDir, long savedTime, BitField savedBitField) throws IOException { - File base = new File(rootDir, filterName(metainfo.getName())); + File base = new SecureFile(rootDir, filterName(metainfo.getName())); boolean useSavedBitField = savedTime > 0 && savedBitField != null; List files = metainfo.getFiles(); @@ -623,7 +624,7 @@ public class Storage else { // The final element (file) in the hierarchy. - f = new File(base, name); + f = new SecureFile(base, name); if (!f.createNewFile() && !f.exists()) throw new IOException("Could not create file " + f); } diff --git a/core/java/src/net/i2p/I2PAppContext.java b/core/java/src/net/i2p/I2PAppContext.java index 98e7d6af9a..b497796408 100644 --- a/core/java/src/net/i2p/I2PAppContext.java +++ b/core/java/src/net/i2p/I2PAppContext.java @@ -114,6 +114,11 @@ public class I2PAppContext { * */ public static I2PAppContext getGlobalContext() { + // skip the global lock + I2PAppContext rv = _globalAppContext; + if (rv != null) + return rv; + synchronized (I2PAppContext.class) { if (_globalAppContext == null) { _globalAppContext = new I2PAppContext(false, null); @@ -122,6 +127,18 @@ public class I2PAppContext { return _globalAppContext; } + /** + * Pull the default context, WITHOUT creating a new one. + * Use this in static methods used early in router initialization, + * where creating a context messes things up. + * + * @return context or null + * @since 0.8.2 + */ + public static I2PAppContext getCurrentContext() { + return _globalAppContext; + } + /** * Lets root a brand new context * @@ -257,6 +274,7 @@ public class I2PAppContext { _appDir = _routerDir; } /****** + (new Exception("Initialized by")).printStackTrace(); System.err.println("Base directory: " + _baseDir.getAbsolutePath()); System.err.println("Config directory: " + _configDir.getAbsolutePath()); System.err.println("Router directory: " + _routerDir.getAbsolutePath()); diff --git a/core/java/src/net/i2p/util/SecureDirectory.java b/core/java/src/net/i2p/util/SecureDirectory.java index 30d609e96f..d238cc694d 100644 --- a/core/java/src/net/i2p/util/SecureDirectory.java +++ b/core/java/src/net/i2p/util/SecureDirectory.java @@ -5,15 +5,14 @@ import java.io.File; /** * Same as File but sets the file mode after mkdir() so it can * be read and written by the owner only (i.e. 700 on linux) + * As of 0.8.2, just use SecureFile instead of this. * * @since 0.8.1 * @author zzz */ public class SecureDirectory extends File { - private static final boolean canSetPerms = - (new VersionComparator()).compare(System.getProperty("java.version"), "1.6") >= 0; - private static final boolean isNotWindows = !System.getProperty("os.name").startsWith("Win"); + protected static final boolean isNotWindows = !System.getProperty("os.name").startsWith("Win"); public SecureDirectory(String pathname) { super(pathname); @@ -54,8 +53,8 @@ public class SecureDirectory extends File { * Tries to set the permissions to 700, * ignores errors */ - private void setPerms() { - if (!canSetPerms) + protected void setPerms() { + if (!SecureFileOutputStream.canSetPerms()) return; try { setReadable(false, false); diff --git a/core/java/src/net/i2p/util/SecureFile.java b/core/java/src/net/i2p/util/SecureFile.java new file mode 100644 index 0000000000..a931abb556 --- /dev/null +++ b/core/java/src/net/i2p/util/SecureFile.java @@ -0,0 +1,81 @@ +package net.i2p.util; + +import java.io.File; +import java.io.IOException; + +/** + * Same as SecureDirectory but sets the file mode after createNewFile() + * and createTempFile() also. So just use this instead. + * Probably should have just made this class in the beginning and not had two. + * + * @since 0.8.2 + * @author zzz + */ +public class SecureFile extends SecureDirectory { + + public SecureFile(String pathname) { + super(pathname); + } + + public SecureFile(String parent, String child) { + super(parent, child); + } + + public SecureFile(File parent, String child) { + super(parent, child); + } + + /** + * Sets file to mode 600 if the file is created + */ + @Override + public boolean createNewFile() throws IOException { + boolean rv = super.createNewFile(); + if (rv) + setPerms(); + return rv; + } + + /** + * Sets file to mode 600 when the file is created + */ + public static File createTempFile(String prefix, String suffix) throws IOException { + File rv = File.createTempFile(prefix, suffix); + // same thing as below but static + SecureFileOutputStream.setPerms(rv); + return rv; + } + + /** + * Sets file to mode 600 when the file is created + */ + public static File createTempFile(String prefix, String suffix, File directory) throws IOException { + File rv = File.createTempFile(prefix, suffix, directory); + // same thing as below but static + SecureFileOutputStream.setPerms(rv); + return rv; + } + + /** + * Tries to set the permissions to 600, + * ignores errors + */ + @Override + protected void setPerms() { + if (!SecureFileOutputStream.canSetPerms()) + return; + try { + setReadable(false, false); + setReadable(true, true); + setWritable(false, false); + setWritable(true, true); + if (isNotWindows && isDirectory()) { + setExecutable(false, false); + setExecutable(true, true); + } + } catch (Throwable t) { + // NoSuchMethodException or NoSuchMethodError if we somehow got the + // version detection wrong or the JVM doesn't support it + } + } +} diff --git a/core/java/src/net/i2p/util/SecureFileOutputStream.java b/core/java/src/net/i2p/util/SecureFileOutputStream.java index 01c6eba718..9494827eb6 100644 --- a/core/java/src/net/i2p/util/SecureFileOutputStream.java +++ b/core/java/src/net/i2p/util/SecureFileOutputStream.java @@ -4,6 +4,8 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import net.i2p.I2PAppContext; + /** * Same as FileOutputStream but sets the file mode so it can only * be read and written by the owner only (i.e. 600 on linux) @@ -13,7 +15,7 @@ import java.io.FileOutputStream; */ public class SecureFileOutputStream extends FileOutputStream { - private static final boolean canSetPerms = + private static final boolean oneDotSix = (new VersionComparator()).compare(System.getProperty("java.version"), "1.6") >= 0; /** @@ -51,12 +53,22 @@ public class SecureFileOutputStream extends FileOutputStream { setPerms(file); } + /** @since 0.8.2 */ + static boolean canSetPerms() { + if (!oneDotSix) + return false; + I2PAppContext ctx = I2PAppContext.getCurrentContext(); + if (ctx == null) + return true; + return !ctx.getBooleanProperty("i2p.insecureFiles"); + } + /** * Tries to set the permissions to 600, * ignores errors */ public static void setPerms(File f) { - if (!canSetPerms) + if (!canSetPerms()) return; try { f.setReadable(false, false); -- GitLab