[tor-commits] [sandboxed-tor-browser/master] Bug 22932: Add experimental Amensiac Profile Directory support.

yawning at torproject.org yawning at torproject.org
Sun Jul 16 08:32:47 UTC 2017


commit c8e63575340e5541ae50051acbe03b69b14e577c
Author: Yawning Angel <yawning at schwanenlied.me>
Date:   Sun Jul 16 04:13:50 2017 +0000

    Bug 22932: Add experimental Amensiac Profile Directory support.
    
    This is currently an advanced config option as it is still experimental
    and needs more testing.  When enabled it will copy the Firefox profile
    directory into a tmpfs mount when the container is created, that is
    discarded when the container is torn down.
    
    The following directories are read-only bind mounted as usual:
     * profile.default/preferences
     * profile.default/extensions
---
 ChangeLog                                          |  1 +
 data/ui/gtkui.ui                                   | 42 +++++++++++++-
 .../internal/sandbox/application.go                | 18 +++---
 .../internal/sandbox/hugbox.go                     | 66 ++++++++++++++++++++++
 .../internal/ui/config/config.go                   | 12 ++++
 .../internal/ui/gtk/config.go                      | 33 +++++++----
 6 files changed, 152 insertions(+), 20 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 2d7ee1a..29a479b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,6 @@
 Changes in version 0.0.11 - UNRELEASED:
  * Bug 22910: Deprecate the volatile extension dir option.
+ * Bug 22932: Add experimental Amensiac Profile Directory support.
 
 Changes in version 0.0.10 - 2017-07-12:
  * Bug 22829: Remove default obfs4 bridge riemann.
diff --git a/data/ui/gtkui.ui b/data/ui/gtkui.ui
index 728fccb..dffacb9 100644
--- a/data/ui/gtkui.ui
+++ b/data/ui/gtkui.ui
@@ -628,6 +628,42 @@
                   </packing>
                 </child>
                 <child>
+                  <object class="GtkBox" id="amnesiacProfileBox">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="margin_bottom">6</property>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="halign">start</property>
+                        <property name="label" translatable="yes">Amnesiac Profile Directory (Experimental)</property>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkSwitch" id="amnesiacProfileSwitch">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">3</property>
+                  </packing>
+                </child>
+                <child>
                   <object class="GtkBox" id="downloadsDirBox">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
@@ -662,7 +698,7 @@
                   <packing>
                     <property name="expand">False</property>
                     <property name="fill">True</property>
-                    <property name="position">3</property>
+                    <property name="position">4</property>
                   </packing>
                 </child>
                 <child>
@@ -700,7 +736,7 @@
                   <packing>
                     <property name="expand">False</property>
                     <property name="fill">True</property>
-                    <property name="position">4</property>
+                    <property name="position">5</property>
                   </packing>
                 </child>
                 <child>
@@ -737,7 +773,7 @@
                   <packing>
                     <property name="expand">False</property>
                     <property name="fill">True</property>
-                    <property name="position">5</property>
+                    <property name="position">6</property>
                   </packing>
                 </child>
               </object>
diff --git a/src/cmd/sandboxed-tor-browser/internal/sandbox/application.go b/src/cmd/sandboxed-tor-browser/internal/sandbox/application.go
index 4057b21..c4acbad 100644
--- a/src/cmd/sandboxed-tor-browser/internal/sandbox/application.go
+++ b/src/cmd/sandboxed-tor-browser/internal/sandbox/application.go
@@ -99,15 +99,11 @@ func RunTorBrowser(cfg *config.Config, manif *config.Manifest, tor *tor.Tor) (pr
 	browserHome := filepath.Join(h.homeDir, "sandboxed-tor-browser", "tor-browser", "Browser")
 	realBrowserHome := filepath.Join(cfg.BundleInstallDir, "Browser")
 	realProfileDir := filepath.Join(realBrowserHome, profileSubDir)
-	realCachesDir := filepath.Join(realBrowserHome, cachesSubDir)
 	realDesktopDir := filepath.Join(realBrowserHome, "Desktop")
 	realDownloadsDir := filepath.Join(realBrowserHome, "Downloads")
 	realExtensionsDir := filepath.Join(realProfileDir, "extensions")
 
 	// Ensure that the `Caches`, `Downloads` and `Desktop` mount points exist.
-	if err = os.MkdirAll(realCachesDir, DirMode); err != nil {
-		return
-	}
 	if err = os.MkdirAll(realDesktopDir, DirMode); err != nil {
 		return
 	}
@@ -131,11 +127,19 @@ func RunTorBrowser(cfg *config.Config, manif *config.Manifest, tor *tor.Tor) (pr
 
 	// Filesystem stuff.
 	h.roBind(cfg.BundleInstallDir, filepath.Join(h.homeDir, "sandboxed-tor-browser", "tor-browser"), false)
-	h.bind(realProfileDir, profileDir, false)
+	if cfg.Sandbox.EnableAmnesiacProfileDirectory {
+		excludes := []string{
+			filepath.Join(realProfileDir, "preferences"),
+			realExtensionsDir,
+		}
+		h.shadowDir(profileDir, realProfileDir, excludes)
+	} else {
+		h.bind(realProfileDir, profileDir, false)
+	}
+	h.roBind(filepath.Join(realProfileDir, "preferences"), filepath.Join(profileDir, "preferences"), false)
 	h.bind(realDesktopDir, desktopDir, false)
 	h.bind(realDownloadsDir, downloadsDir, false)
-	h.bind(realCachesDir, cachesDir, false) // XXX: Do I need this?
-	h.roBind(filepath.Join(realProfileDir, "preferences"), filepath.Join(profileDir, "preferences"), false)
+	h.tmpfs(cachesDir)
 	h.chdir = browserHome
 
 	// Explicitly bind mount the expected extensions in.
diff --git a/src/cmd/sandboxed-tor-browser/internal/sandbox/hugbox.go b/src/cmd/sandboxed-tor-browser/internal/sandbox/hugbox.go
index 127af77..d477eb6 100644
--- a/src/cmd/sandboxed-tor-browser/internal/sandbox/hugbox.go
+++ b/src/cmd/sandboxed-tor-browser/internal/sandbox/hugbox.go
@@ -21,6 +21,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"io"
+	"io/ioutil"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -161,6 +162,71 @@ func (h *hugbox) tmpfs(dest string) {
 	h.args = append(h.args, "--tmpfs", dest)
 }
 
+func (h *hugbox) shadowDir(dest, src string, exclude []string) {
+	Debugf("sandbox: shadowDir: %s -> %s", src, dest)
+
+	excludeMap := make(map[string]bool)
+	for _, s := range exclude {
+		excludeMap[s] = true
+	}
+
+	shadowWalk := func(path string, info os.FileInfo, err error) error {
+		if path == src {
+			h.tmpfs(dest)
+			return nil
+		}
+
+		isDir := info.IsDir()
+		if excludeMap[path] {
+			Debugf("sandbox: shadowDir: excluding '%s'", path)
+			if isDir {
+				return filepath.SkipDir
+			}
+			return nil
+		}
+
+		// Dealing with this is annoying, and it doesn't happen under
+		// normal usage.
+		const (
+			modeIrregular  = os.ModeSymlink | os.ModeNamedPipe | os.ModeSocket | os.ModeDevice
+			modeExecutable = 0111
+		)
+		mode := info.Mode()
+		if mode&modeIrregular != 0 {
+			Debugf("sandbox: shadowDir: '%s' irregular perm bits: %s", path, mode)
+			return fmt.Errorf("sandbox: shadowDir: '%s' irregular perm bits: %s", path, mode)
+		} else if mode&modeExecutable != 0 && !isDir {
+			// Alas shadowDir has limits, because bwrap doesn't give a easy way
+			// to set this up.
+			Debugf("sandbox: shadowDir: '%s' ignoring executable perm bits: %s", path, mode)
+		}
+
+		relPath := filepath.Clean(strings.TrimPrefix(path, src))
+		destPath := filepath.Join(dest, relPath)
+		if isDir {
+			h.dir(destPath)
+		} else {
+			// XXX: This guzzles memory, and it'll be easier just to open
+			// the source file, but cleanup on errors would be a huge
+			// nightmare, because Go is too cool for destructors.
+			b, err := ioutil.ReadFile(path)
+			if err != nil {
+				return err
+			}
+			h.file(destPath, b)
+		}
+
+		// Debugf("shadow: '%s' -> '%s'", relPath, destPath)
+
+		return nil
+	}
+
+	// Create the directory, and then walk.
+	if err := filepath.Walk(src, shadowWalk); err != nil {
+		panic(err)
+	}
+}
+
 func (h *hugbox) run() (*Process, error) {
 	// Create the command struct for the sandbox.
 	cmd := &exec.Cmd{
diff --git a/src/cmd/sandboxed-tor-browser/internal/ui/config/config.go b/src/cmd/sandboxed-tor-browser/internal/ui/config/config.go
index 6ab7749..407419f 100644
--- a/src/cmd/sandboxed-tor-browser/internal/ui/config/config.go
+++ b/src/cmd/sandboxed-tor-browser/internal/ui/config/config.go
@@ -211,6 +211,9 @@ type Sandbox struct {
 	// EnableCircuitDisplay enables the Tor Browser circuit display.
 	EnableCircuitDisplay bool `json:"enableCircuitDisplay"`
 
+	// EnableAmnesiacProfileDirectory enables read-only profile directories.
+	EnableAmnesiacProfileDirectory bool `json:"enableAmnesiacProfileDirectory"`
+
 	// DesktopDir is the directory to be bind mounted instead of the default
 	// bundle Desktop directory.
 	DesktopDir string `json:"desktopDir,omitEmpty"`
@@ -255,6 +258,15 @@ func (sb *Sandbox) SetEnableCircuitDisplay(b bool) {
 	}
 }
 
+// SetEnableAmnesiacProfileDirectory sets tthe circit display enable and marks
+// the config dirty.
+func (sb *Sandbox) SetEnableAmnesiacProfileDirectory(b bool) {
+	if sb.EnableAmnesiacProfileDirectory != b {
+		sb.EnableAmnesiacProfileDirectory = b
+		sb.cfg.isDirty = true
+	}
+}
+
 // SetDownloadsDir sets the sandbox `~/Downloads` bind mount source and marks
 // the config dirty.
 func (sb *Sandbox) SetDownloadsDir(s string) {
diff --git a/src/cmd/sandboxed-tor-browser/internal/ui/gtk/config.go b/src/cmd/sandboxed-tor-browser/internal/ui/gtk/config.go
index 8a66706..66db109 100644
--- a/src/cmd/sandboxed-tor-browser/internal/ui/gtk/config.go
+++ b/src/cmd/sandboxed-tor-browser/internal/ui/gtk/config.go
@@ -60,15 +60,17 @@ type configDialog struct {
 	torSystemIndicator *gtk3.Box
 
 	// Sandbox config elements.
-	pulseAudioSwitch     *gtk3.Switch
-	avCodecSwitch        *gtk3.Switch
-	circuitDisplaySwitch *gtk3.Switch
-	displayBox           *gtk3.Box
-	displayEntry         *gtk3.Entry
-	downloadsDirBox      *gtk3.Box
-	downloadsDirChooser  *gtk3.FileChooserButton
-	desktopDirBox        *gtk3.Box
-	desktopDirChooser    *gtk3.FileChooserButton
+	pulseAudioSwitch      *gtk3.Switch
+	avCodecSwitch         *gtk3.Switch
+	circuitDisplaySwitch  *gtk3.Switch
+	amnesiacProfileBox    *gtk3.Box
+	amnesiacProfileSwitch *gtk3.Switch
+	displayBox            *gtk3.Box
+	displayEntry          *gtk3.Entry
+	downloadsDirBox       *gtk3.Box
+	downloadsDirChooser   *gtk3.FileChooserButton
+	desktopDirBox         *gtk3.Box
+	desktopDirChooser     *gtk3.FileChooserButton
 }
 
 const proxySOCKS4 = "SOCKS 4"
@@ -111,6 +113,10 @@ func (d *configDialog) loadFromConfig() {
 	d.pulseAudioSwitch.SetActive(d.ui.Cfg.Sandbox.EnablePulseAudio)
 	d.avCodecSwitch.SetActive(d.ui.Cfg.Sandbox.EnableAVCodec)
 	d.circuitDisplaySwitch.SetActive(d.ui.Cfg.Sandbox.EnableCircuitDisplay)
+	d.amnesiacProfileSwitch.SetActive(d.ui.Cfg.Sandbox.EnableAmnesiacProfileDirectory)
+	if d.ui.Cfg.Sandbox.EnableAmnesiacProfileDirectory {
+		forceAdv = true
+	}
 	if d.ui.Cfg.Sandbox.Display != "" {
 		d.displayEntry.SetText(d.ui.Cfg.Sandbox.Display)
 		forceAdv = true
@@ -125,7 +131,7 @@ func (d *configDialog) loadFromConfig() {
 	}
 
 	// Hide certain options from the masses, that are probably confusing.
-	for _, w := range []*gtk3.Box{d.displayBox, d.downloadsDirBox, d.desktopDirBox} {
+	for _, w := range []*gtk3.Box{d.amnesiacProfileBox, d.displayBox, d.downloadsDirBox, d.desktopDirBox} {
 		w.SetVisible(d.ui.AdvancedConfig || forceAdv)
 	}
 	d.loaded = true
@@ -193,6 +199,7 @@ func (d *configDialog) onOk() error {
 	d.ui.Cfg.Sandbox.SetEnablePulseAudio(d.pulseAudioSwitch.GetActive())
 	d.ui.Cfg.Sandbox.SetEnableAVCodec(d.avCodecSwitch.GetActive())
 	d.ui.Cfg.Sandbox.SetEnableCircuitDisplay(d.circuitDisplaySwitch.GetActive())
+	d.ui.Cfg.Sandbox.SetEnableAmnesiacProfileDirectory(d.amnesiacProfileSwitch.GetActive())
 	if s, err := d.displayEntry.GetText(); err != nil {
 		return err
 	} else {
@@ -380,6 +387,12 @@ func (ui *gtkUI) initConfigDialog(b *gtk3.Builder) error {
 	if d.circuitDisplaySwitch, err = getSwitch(b, "circuitDisplaySwitch"); err != nil {
 		return err
 	}
+	if d.amnesiacProfileSwitch, err = getSwitch(b, "amnesiacProfileSwitch"); err != nil {
+		return err
+	}
+	if d.amnesiacProfileBox, err = getBox(b, "amnesiacProfileBox"); err != nil {
+		return err
+	}
 	if d.displayBox, err = getBox(b, "displayBox"); err != nil {
 		return err
 	}



More information about the tor-commits mailing list