aboutsummaryrefslogtreecommitdiff
path: root/infra/libkookie/nixpkgs/pkgs/applications/networking/browsers/tor-browser-bundle-bin/default.nix
{ stdenv
, fetchurl
, makeDesktopItem

# Common run-time dependencies
, zlib

# libxul run-time dependencies
, atk
, cairo
, dbus
, dbus-glib
, fontconfig
, freetype
, gdk-pixbuf
, glib
, gtk3
, libxcb
, libX11
, libXext
, libXrender
, libXt
, pango

, audioSupport ? mediaSupport
, pulseaudioSupport ? false
, libpulseaudio
, apulse

# Media support (implies audio support)
, mediaSupport ? true
, ffmpeg_3

, gmp

# Pluggable transport dependencies
, python27

# Wrapper runtime
, coreutils
, glibcLocales
, gnome3
, runtimeShell
, shared-mime-info
, gsettings-desktop-schemas

# Hardening
, graphene-hardened-malloc
# crashes with intel driver
, useHardenedMalloc ? false

# Whether to disable multiprocess support to work around crashing tabs
# TODO: fix the underlying problem instead of this terrible work-around
, disableContentSandbox ? true

# Extra preferences
, extraPrefs ? ""
}:

with stdenv.lib;

let
  libPath = makeLibraryPath libPkgs;

  libPkgs = [
    atk
    cairo
    dbus
    dbus-glib
    fontconfig
    freetype
    gdk-pixbuf
    glib
    gtk3
    libxcb
    libX11
    libXext
    libXrender
    libXt
    pango
    stdenv.cc.cc
    stdenv.cc.libc
    zlib
  ]
  ++ optionals pulseaudioSupport [ libpulseaudio ]
  ++ optionals mediaSupport [
    ffmpeg_3
  ];

  # Library search path for the fte transport
  fteLibPath = makeLibraryPath [ stdenv.cc.cc gmp ];

  # Upstream source
  version = "10.0.5";

  lang = "en-US";

  srcs = {
    x86_64-linux = fetchurl {
      url = "https://dist.torproject.org/torbrowser/${version}/tor-browser-linux64-${version}_${lang}.tar.xz";
      sha256 = "1cxh39x69m4lgqin5k5p67gs9g26w7cnlbdpjqi8dw47y0bpr9xw";
    };

    i686-linux = fetchurl {
      url = "https://dist.torproject.org/torbrowser/${version}/tor-browser-linux32-${version}_${lang}.tar.xz";
      sha256 = "1cyg5ic7mrj6x1gxw5w609933d9ripa5b5gxyqnvnxfa23dkh608";
    };
  };
in
stdenv.mkDerivation rec {
  pname = "tor-browser-bundle-bin";
  inherit version;

  src = srcs.${stdenv.hostPlatform.system} or (throw "unsupported system: ${stdenv.hostPlatform.system}");

  preferLocalBuild = true;
  allowSubstitutes = false;

  desktopItem = makeDesktopItem {
    name = "torbrowser";
    exec = "tor-browser";
    icon = "torbrowser";
    desktopName = "Tor Browser";
    genericName = "Web Browser";
    comment = meta.description;
    categories = "Network;WebBrowser;Security;";
  };

  buildCommand = ''
    # For convenience ...
    TBB_IN_STORE=$out/share/tor-browser
    interp=$(< $NIX_CC/nix-support/dynamic-linker)

    # Unpack & enter
    mkdir -p "$TBB_IN_STORE"
    tar xf "${src}" -C "$TBB_IN_STORE" --strip-components=2
    pushd "$TBB_IN_STORE"

    # Set ELF interpreter
    for exe in firefox.real TorBrowser/Tor/tor ; do
      echo "Setting ELF interpreter on $exe ..." >&2
      patchelf --set-interpreter "$interp" "$exe"
    done

    # firefox is a wrapper that checks for a more recent libstdc++ & appends it to the ld path
    mv firefox.real firefox

    # The final libPath.  Note, we could split this into firefoxLibPath
    # and torLibPath for accuracy, but this is more convenient ...
    libPath=${libPath}:$TBB_IN_STORE:$TBB_IN_STORE/TorBrowser/Tor

    # apulse uses a non-standard library path.  For now special-case it.
    ${optionalString (audioSupport && !pulseaudioSupport) ''
      libPath=${apulse}/lib/apulse:$libPath
    ''}

    # Fixup paths to pluggable transports.
    sed -i TorBrowser/Data/Tor/torrc-defaults \
        -e "s,./TorBrowser,$TBB_IN_STORE/TorBrowser,g"

    # Fixup obfs transport.  Work around patchelf failing to set
    # interpreter for pre-compiled Go binaries by invoking the interpreter
    # directly.
    sed -i TorBrowser/Data/Tor/torrc-defaults \
        -e "s|\(ClientTransportPlugin meek_lite,obfs2,obfs3,obfs4,scramblesuit\) exec|\1 exec $interp|"

    # Similarly fixup snowflake
    sed -i TorBrowser/Data/Tor/torrc-defaults \
        -e "s|\(ClientTransportPlugin snowflake\) exec|\1 exec $interp|"


    # Prepare for autoconfig.
    #
    # See https://developer.mozilla.org/en-US/Firefox/Enterprise_deployment
    cat >defaults/pref/autoconfig.js <<EOF
    //
    pref("general.config.filename", "mozilla.cfg");
    pref("general.config.obscure_value", 0);
    EOF

    # Hard-coded Firefox preferences.
    cat >mozilla.cfg <<EOF
    // First line must be a comment

    // Always update via Nixpkgs
    lockPref("app.update.auto", false);
    lockPref("app.update.enabled", false);
    lockPref("extensions.update.autoUpdateDefault", false);
    lockPref("extensions.update.enabled", false);
    lockPref("extensions.torbutton.versioncheck_enabled", false);

    // User should never change these.  Locking prevents these
    // values from being written to prefs.js, avoiding Store
    // path capture.
    lockPref("extensions.torlauncher.torrc-defaults_path", "$TBB_IN_STORE/TorBrowser/Data/Tor/torrc-defaults");
    lockPref("extensions.torlauncher.tor_path", "$TBB_IN_STORE/TorBrowser/Tor/tor");

    // Reset pref that captures store paths.
    clearPref("extensions.xpiState");

    // Stop obnoxious first-run redirection.
    lockPref("noscript.firstRunRedirection", false);

    // Insist on using IPC for communicating with Tor
    //
    // Defaults to creating \$TBB_HOME/TorBrowser/Data/Tor/{socks,control}.socket
    lockPref("extensions.torlauncher.control_port_use_ipc", true);
    lockPref("extensions.torlauncher.socks_port_use_ipc", true);

    // Optionally disable multiprocess support.  We always set this to ensure that
    // toggling the pref takes effect.
    lockPref("browser.tabs.remote.autostart.2", ${if disableContentSandbox then "false" else "true"});

    // Allow sandbox access to sound devices if using ALSA directly
    ${if (audioSupport && !pulseaudioSupport) then ''
      pref("security.sandbox.content.write_path_whitelist", "/dev/snd/");
    '' else ''
      clearPref("security.sandbox.content.write_path_whitelist");
    ''}

    ${optionalString (extraPrefs != "") ''
      ${extraPrefs}
    ''}
    EOF

    # Hard-code path to TBB fonts; see also FONTCONFIG_FILE in
    # the wrapper below.
    FONTCONFIG_FILE=$TBB_IN_STORE/TorBrowser/Data/fontconfig/fonts.conf
    sed -i "$FONTCONFIG_FILE" \
        -e "s,<dir>fonts</dir>,<dir>$TBB_IN_STORE/fonts</dir>,"

    # Preload extensions by moving into the runtime instead of storing under the
    # user's profile directory.
    # See https://support.mozilla.org/en-US/kb/deploying-firefox-with-extensions
    mkdir -p "$TBB_IN_STORE/distribution/extensions"
    mv "$TBB_IN_STORE/TorBrowser/Data/Browser/profile.default/extensions/"* \
      "$TBB_IN_STORE/distribution/extensions"

    # Hard-code paths to geoip data files.  TBB resolves the geoip files
    # relative to torrc-defaults_path but if we do not hard-code them
    # here, these paths end up being written to the torrc in the user's
    # state dir.
    cat >>TorBrowser/Data/Tor/torrc-defaults <<EOF
    GeoIPFile $TBB_IN_STORE/TorBrowser/Data/Tor/geoip
    GeoIPv6File $TBB_IN_STORE/TorBrowser/Data/Tor/geoip6
    EOF

    WRAPPER_LD_PRELOAD=${optionalString useHardenedMalloc
      "${graphene-hardened-malloc}/lib/libhardened_malloc.so"}

    WRAPPER_XDG_DATA_DIRS=${concatMapStringsSep ":" (x: "${x}/share") [
      gnome3.adwaita-icon-theme
      shared-mime-info
    ]}
    WRAPPER_XDG_DATA_DIRS+=":"${concatMapStringsSep ":" (x: "${x}/share/gsettings-schemas/${x.name}") [
      glib
      gsettings-desktop-schemas
      gtk3
    ]};

    # Generate wrapper
    mkdir -p $out/bin
    cat > "$out/bin/tor-browser" << EOF
    #! ${runtimeShell}
    set -o errexit -o nounset

    PATH=${makeBinPath [ coreutils ]}
    export LC_ALL=C
    export LOCALE_ARCHIVE=${glibcLocales}/lib/locale/locale-archive

    # Enter local state directory.
    REAL_HOME=\$HOME
    TBB_HOME=\''${TBB_HOME:-''${XDG_DATA_HOME:-\$REAL_HOME/.local/share}/tor-browser}
    HOME=\$TBB_HOME

    mkdir -p "\$HOME"
    cd "\$HOME"

    # Initialize empty TBB local state directory hierarchy.  We
    # intentionally mirror the layout that TBB would see if executed from
    # the unpacked bundle dir.
    mkdir -p "\$HOME/TorBrowser" "\$HOME/TorBrowser/Data"

    # Initialize the Tor data directory.
    mkdir -p "\$HOME/TorBrowser/Data/Tor"

    # TBB will fail if ownership is too permissive
    chmod 0700 "\$HOME/TorBrowser/Data/Tor"

    # Initialize the browser profile state.  Note that the only data
    # copied from the Store payload is the initial bookmark file, which is
    # never updated once created.  All other files under user's profile
    # dir are generated by TBB.
    mkdir -p "\$HOME/TorBrowser/Data/Browser/profile.default"
    cp -u --no-preserve=mode,owner "$TBB_IN_STORE/TorBrowser/Data/Browser/profile.default/bookmarks.html" \
      "\$HOME/TorBrowser/Data/Browser/profile.default/bookmarks.html"

    # Clear out some files that tend to capture store references but are
    # easily generated by firefox at startup.
    rm -f "\$HOME/TorBrowser/Data/Browser/profile.default"/{addonStartup.json.lz4,compatibility.ini,extensions.ini,extensions.json}
    rm -f "\$HOME/TorBrowser/Data/Browser/profile.default"/startupCache/*

    # XDG
    : "\''${XDG_RUNTIME_DIR:=/run/user/\$(id -u)}"
    : "\''${XDG_CONFIG_HOME:=\$REAL_HOME/.config}"

    ${optionalString pulseaudioSupport ''
      # Figure out some envvars for pulseaudio
      : "\''${PULSE_SERVER:=\$XDG_RUNTIME_DIR/pulse/native}"
      : "\''${PULSE_COOKIE:=\$XDG_CONFIG_HOME/pulse/cookie}"
    ''}

    # Font cache files capture store paths; clear them out on the off
    # chance that TBB would continue using old font files.
    rm -rf "\$HOME/.cache/fontconfig"

    # Lift-off
    #
    # XAUTHORITY and DISPLAY are required for TBB to work at all.
    #
    # DBUS_SESSION_BUS_ADDRESS is inherited to avoid auto-launch; to
    # prevent that, set it to an empty/invalid value prior to running
    # tor-browser.
    #
    # PULSE_SERVER is necessary for audio playback.
    #
    # Setting FONTCONFIG_FILE is required to make fontconfig read the TBB
    # fonts.conf; upstream uses FONTCONFIG_PATH, but FC_DEBUG=1024
    # indicates the system fonts.conf being used instead.
    #
    # XDG_DATA_DIRS is set to prevent searching system dirs (looking for .desktop & icons)
    exec env -i \
      LD_PRELOAD=$WRAPPER_LD_PRELOAD \
      \
      TZ=":" \
      TZDIR="\''${TZDIR:-}" \
      LOCALE_ARCHIVE="\$LOCALE_ARCHIVE" \
      \
      TMPDIR="\''${TMPDIR:-/tmp}" \
      HOME="\$HOME" \
      XAUTHORITY="\''${XAUTHORITY:-\$HOME/.Xauthority}" \
      DISPLAY="\$DISPLAY" \
      DBUS_SESSION_BUS_ADDRESS="\''${DBUS_SESSION_BUS_ADDRESS:-unix:path=\$XDG_RUNTIME_DIR/bus}" \\
      \
      XDG_DATA_HOME="\$HOME/.local/share" \
      XDG_DATA_DIRS="$WRAPPER_XDG_DATA_DIRS" \
      \
      PULSE_SERVER="\''${PULSE_SERVER:-}" \
      PULSE_COOKIE="\''${PULSE_COOKIE:-}" \
      \
      APULSE_PLAYBACK_DEVICE="\''${APULSE_PLAYBACK_DEVICE:-plug:dmix}" \
      \
      TOR_SKIP_LAUNCH="\''${TOR_SKIP_LAUNCH:-}" \
      TOR_CONTROL_PORT="\''${TOR_CONTROL_PORT:-}" \
      TOR_SOCKS_PORT="\''${TOR_SOCKS_PORT:-}" \
      \
      FONTCONFIG_FILE="$FONTCONFIG_FILE" \
      \
      LD_LIBRARY_PATH="$libPath" \
      \
      "$TBB_IN_STORE/firefox" \
        --class "Tor Browser" \
        -no-remote \
        -profile "\$HOME/TorBrowser/Data/Browser/profile.default" \
        "\''${@}"
    EOF
    chmod +x $out/bin/tor-browser

    # Easier access to docs
    mkdir -p $out/share/doc
    ln -s $TBB_IN_STORE/TorBrowser/Docs $out/share/doc/tor-browser

    # Install .desktop item
    mkdir -p $out/share/applications
    cp $desktopItem/share/applications"/"* $out/share/applications
    sed -i $out/share/applications/torbrowser.desktop \
        -e "s,Exec=.*,Exec=$out/bin/tor-browser," \
        -e "s,Icon=.*,Icon=tor-browser,"
    for i in 16 32 48 64 128; do
      mkdir -p $out/share/icons/hicolor/''${i}x''${i}/apps/
      ln -s $out/share/tor-browser/browser/chrome/icons/default/default$i.png $out/share/icons/hicolor/''${i}x''${i}/apps/tor-browser.png
    done

    # Check installed apps
    echo "Checking bundled Tor ..."
    LD_LIBRARY_PATH=$libPath $TBB_IN_STORE/TorBrowser/Tor/tor --version >/dev/null

    echo "Checking tor-browser wrapper ..."
    DISPLAY="" XAUTHORITY="" DBUS_SESSION_BUS_ADDRESS="" TBB_HOME=$(mktemp -d) \
      $out/bin/tor-browser --version >/dev/null
  '';

  meta = with stdenv.lib; {
    description = "Tor Browser Bundle built by torproject.org";
    longDescription = ''
      Tor Browser Bundle is a bundle of the Tor daemon, Tor Browser (heavily patched version of
      Firefox), several essential extensions for Tor Browser, and some tools that glue those
      together with a convenient UI.

      `tor-browser-bundle-bin` package is the official version built by torproject.org patched with
      `patchelf` to work under nix and with bundled scripts adapted to the read-only nature of
      the `/nix/store`.
    '';
    homepage = "https://www.torproject.org/";
    changelog = "https://gitweb.torproject.org/builders/tor-browser-build.git/plain/projects/tor-browser/Bundle-Data/Docs/ChangeLog.txt?h=maint-${version}";
    platforms = attrNames srcs;
    maintainers = with maintainers; [ offline matejc thoughtpolice joachifm hax404 cap KarlJoad ];
    hydraPlatforms = [];
    # MPL2.0+, GPL+, &c.  While it's not entirely clear whether
    # the compound is "libre" in a strict sense (some components place certain
    # restrictions on redistribution), it's free enough for our purposes.
    license = licenses.free;
  };
}