2014-09-03 18:24:28 +00:00
|
|
|
"""
|
|
|
|
Tor Browser Launcher
|
|
|
|
https://github.com/micahflee/torbrowser-launcher/
|
|
|
|
|
2017-01-27 04:25:49 +00:00
|
|
|
Copyright (c) 2013-2017 Micah Lee <micah@micahflee.com>
|
2014-09-03 18:24:28 +00:00
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person
|
|
|
|
obtaining a copy of this software and associated documentation
|
|
|
|
files (the "Software"), to deal in the Software without
|
|
|
|
restriction, including without limitation the rights to use,
|
|
|
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
copies of the Software, and to permit persons to whom the
|
|
|
|
Software is furnished to do so, subject to the following
|
|
|
|
conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
|
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
|
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
|
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
|
|
OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
"""
|
|
|
|
|
2017-03-22 12:52:45 +00:00
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import platform
|
|
|
|
import subprocess
|
|
|
|
import locale
|
|
|
|
import pickle
|
|
|
|
import json
|
|
|
|
import re
|
2018-03-23 20:36:37 +00:00
|
|
|
import gettext
|
|
|
|
import gpg
|
2014-09-03 18:24:28 +00:00
|
|
|
|
2020-10-06 22:59:15 +00:00
|
|
|
SHARE = os.getenv("TBL_SHARE", sys.prefix + "/share") + "/torbrowser-launcher"
|
2014-09-03 18:24:28 +00:00
|
|
|
|
2020-10-06 22:59:15 +00:00
|
|
|
gettext.install("torbrowser-launcher")
|
2014-09-03 18:24:28 +00:00
|
|
|
|
2017-03-22 16:57:10 +00:00
|
|
|
# We're looking for output which:
|
|
|
|
#
|
|
|
|
# 1. The first portion must be `[GNUPG:] IMPORT_OK`
|
|
|
|
# 2. The second must be an integer between [0, 15], inclusive
|
|
|
|
# 3. The third must be an uppercased hex-encoded 160-bit fingerprint
|
|
|
|
gnupg_import_ok_pattern = re.compile(
|
2020-10-06 22:59:15 +00:00
|
|
|
b"(\[GNUPG\:\]) (IMPORT_OK) ([0-9]|[1]?[0-5]) ([A-F0-9]{40})"
|
|
|
|
)
|
2017-03-22 16:57:10 +00:00
|
|
|
|
|
|
|
|
2018-03-19 16:29:12 +00:00
|
|
|
class Common(object):
|
2014-09-03 18:24:28 +00:00
|
|
|
def __init__(self, tbl_version):
|
|
|
|
self.tbl_version = tbl_version
|
|
|
|
|
|
|
|
# initialize the app
|
2020-10-06 22:59:15 +00:00
|
|
|
self.default_mirror = "https://dist.torproject.org/"
|
2014-09-03 18:24:28 +00:00
|
|
|
self.discover_arch_lang()
|
|
|
|
self.build_paths()
|
2020-10-06 22:59:15 +00:00
|
|
|
for d in self.paths["dirs"]:
|
|
|
|
self.mkdir(self.paths["dirs"][d])
|
2014-09-03 18:24:28 +00:00
|
|
|
self.load_mirrors()
|
|
|
|
self.load_settings()
|
2020-10-06 22:59:15 +00:00
|
|
|
self.mkdir(self.paths["download_dir"])
|
|
|
|
self.mkdir(self.paths["tbb"]["dir"])
|
2014-09-03 18:24:28 +00:00
|
|
|
self.init_gnupg()
|
|
|
|
|
|
|
|
# discover the architecture and language
|
|
|
|
def discover_arch_lang(self):
|
|
|
|
# figure out the architecture
|
2020-10-06 22:59:15 +00:00
|
|
|
self.architecture = "x86_64" if "64" in platform.architecture()[0] else "i686"
|
2014-09-03 18:24:28 +00:00
|
|
|
|
|
|
|
# figure out the language
|
2020-10-06 22:59:15 +00:00
|
|
|
available_languages = [
|
|
|
|
"ar",
|
|
|
|
"ca",
|
|
|
|
"da",
|
|
|
|
"de",
|
|
|
|
"en-US",
|
|
|
|
"es-ES",
|
|
|
|
"fa",
|
|
|
|
"fr",
|
|
|
|
"ga-IE",
|
|
|
|
"he",
|
|
|
|
"id",
|
|
|
|
"is",
|
|
|
|
"it",
|
|
|
|
"ja",
|
|
|
|
"ko",
|
|
|
|
"nb-NO",
|
|
|
|
"nl",
|
|
|
|
"pl",
|
|
|
|
"pt-BR",
|
|
|
|
"ru",
|
|
|
|
"sv-SE",
|
|
|
|
"tr",
|
|
|
|
"vi",
|
|
|
|
"zh-CN",
|
|
|
|
"zh-TW",
|
|
|
|
]
|
2018-09-14 22:00:41 +00:00
|
|
|
default_locale = locale.getlocale()[0]
|
2014-09-03 18:24:28 +00:00
|
|
|
if default_locale is None:
|
2020-10-06 22:59:15 +00:00
|
|
|
self.language = "en-US"
|
2014-09-03 18:24:28 +00:00
|
|
|
else:
|
2020-10-06 22:59:15 +00:00
|
|
|
self.language = default_locale.replace("_", "-")
|
2014-09-03 18:24:28 +00:00
|
|
|
if self.language not in available_languages:
|
2020-10-06 22:59:15 +00:00
|
|
|
self.language = self.language.split("-")[0]
|
2014-09-03 18:24:28 +00:00
|
|
|
if self.language not in available_languages:
|
|
|
|
for l in available_languages:
|
|
|
|
if l[0:2] == self.language:
|
|
|
|
self.language = l
|
|
|
|
# if language isn't available, default to english
|
|
|
|
if self.language not in available_languages:
|
2020-10-06 22:59:15 +00:00
|
|
|
self.language = "en-US"
|
2014-09-03 18:24:28 +00:00
|
|
|
|
2019-09-03 10:25:30 +00:00
|
|
|
# get value of environment variable, if it is not set return the default value
|
|
|
|
@staticmethod
|
|
|
|
def get_env(var_name, default_value):
|
|
|
|
value = os.getenv(var_name)
|
|
|
|
if not value:
|
|
|
|
value = default_value
|
|
|
|
return value
|
|
|
|
|
2014-09-03 18:24:28 +00:00
|
|
|
# build all relevant paths
|
|
|
|
def build_paths(self, tbb_version=None):
|
2020-10-06 22:59:15 +00:00
|
|
|
homedir = os.getenv("HOME")
|
2014-09-03 18:24:28 +00:00
|
|
|
if not homedir:
|
2020-10-06 22:59:15 +00:00
|
|
|
homedir = "/tmp/.torbrowser-" + os.getenv("USER")
|
2014-09-03 18:24:28 +00:00
|
|
|
if not os.path.exists(homedir):
|
|
|
|
try:
|
2016-11-09 01:57:52 +00:00
|
|
|
os.mkdir(homedir, 0o700)
|
2014-09-03 18:24:28 +00:00
|
|
|
except:
|
2020-10-06 22:59:15 +00:00
|
|
|
self.set_gui(
|
|
|
|
"error", _("Error creating {0}").format(homedir), [], False
|
|
|
|
)
|
2014-09-03 18:24:28 +00:00
|
|
|
if not os.access(homedir, os.W_OK):
|
2020-10-06 22:59:15 +00:00
|
|
|
self.set_gui("error", _("{0} is not writable").format(homedir), [], False)
|
2014-09-03 18:24:28 +00:00
|
|
|
|
2019-09-03 10:25:30 +00:00
|
|
|
tbb_config = '{0}/torbrowser'.format(self.get_env('XDG_CONFIG_HOME', '{0}/.config'.format(homedir)))
|
|
|
|
tbb_cache = '{0}/torbrowser'.format(self.get_env('XDG_CACHE_HOME', '{0}/.cache'.format(homedir)))
|
|
|
|
tbb_local = '{0}/torbrowser'.format(self.get_env('XDG_DATA_HOME', '{0}/.local/share'.format(homedir)))
|
2014-09-03 18:24:28 +00:00
|
|
|
old_tbb_data = '{0}/.torbrowser'.format(homedir)
|
|
|
|
|
|
|
|
if tbb_version:
|
|
|
|
# tarball filename
|
2020-10-06 22:59:15 +00:00
|
|
|
if self.architecture == "x86_64":
|
|
|
|
arch = "linux64"
|
2014-09-03 18:24:28 +00:00
|
|
|
else:
|
2020-10-06 22:59:15 +00:00
|
|
|
arch = "linux32"
|
2016-07-08 21:37:31 +00:00
|
|
|
|
2020-10-06 22:59:15 +00:00
|
|
|
if hasattr(self, "settings") and self.settings["force_en-US"]:
|
|
|
|
language = "en-US"
|
2016-07-08 21:37:31 +00:00
|
|
|
else:
|
|
|
|
language = self.language
|
2020-10-06 22:59:15 +00:00
|
|
|
tarball_filename = (
|
|
|
|
"tor-browser-" + arch + "-" + tbb_version + "_" + language + ".tar.xz"
|
|
|
|
)
|
2014-09-03 18:24:28 +00:00
|
|
|
|
|
|
|
# tarball
|
2020-10-06 22:59:15 +00:00
|
|
|
self.paths["tarball_url"] = (
|
|
|
|
"{0}torbrowser/" + tbb_version + "/" + tarball_filename
|
|
|
|
)
|
|
|
|
self.paths["tarball_file"] = tbb_cache + "/download/" + tarball_filename
|
|
|
|
self.paths["tarball_filename"] = tarball_filename
|
2014-09-03 18:24:28 +00:00
|
|
|
|
|
|
|
# sig
|
2020-10-06 22:59:15 +00:00
|
|
|
self.paths["sig_url"] = (
|
|
|
|
"{0}torbrowser/" + tbb_version + "/" + tarball_filename + ".asc"
|
|
|
|
)
|
|
|
|
self.paths["sig_file"] = (
|
|
|
|
tbb_cache + "/download/" + tarball_filename + ".asc"
|
|
|
|
)
|
|
|
|
self.paths["sig_filename"] = tarball_filename + ".asc"
|
2014-09-03 18:24:28 +00:00
|
|
|
else:
|
|
|
|
self.paths = {
|
2020-10-06 22:59:15 +00:00
|
|
|
"dirs": {"config": tbb_config, "cache": tbb_cache, "local": tbb_local,},
|
|
|
|
"old_data_dir": old_tbb_data,
|
|
|
|
"tbl_bin": sys.argv[0],
|
|
|
|
"icon_file": os.path.join(
|
|
|
|
os.path.dirname(SHARE), "pixmaps/torbrowser.png"
|
|
|
|
),
|
|
|
|
"torproject_pem": os.path.join(SHARE, "torproject.pem"),
|
|
|
|
"signing_keys": {
|
|
|
|
"tor_browser_developers": os.path.join(
|
|
|
|
SHARE, "tor-browser-developers.asc"
|
|
|
|
)
|
2014-09-03 18:24:28 +00:00
|
|
|
},
|
2020-10-06 22:59:15 +00:00
|
|
|
"mirrors_txt": [
|
|
|
|
os.path.join(SHARE, "mirrors.txt"),
|
|
|
|
tbb_config + "/mirrors.txt",
|
|
|
|
],
|
|
|
|
"download_dir": tbb_cache + "/download",
|
|
|
|
"gnupg_homedir": tbb_local + "/gnupg_homedir",
|
|
|
|
"settings_file": tbb_config + "/settings.json",
|
|
|
|
"settings_file_pickle": tbb_config + "/settings",
|
|
|
|
"version_check_url": "https://aus1.torproject.org/torbrowser/update_3/release/Linux_x86_64-gcc3/x/en-US",
|
|
|
|
"version_check_file": tbb_cache + "/download/release.xml",
|
|
|
|
"tbb": {
|
|
|
|
"changelog": tbb_local
|
|
|
|
+ "/tbb/"
|
|
|
|
+ self.architecture
|
|
|
|
+ "/tor-browser_"
|
|
|
|
+ self.language
|
|
|
|
+ "/Browser/TorBrowser/Docs/ChangeLog.txt",
|
|
|
|
"dir": tbb_local + "/tbb/" + self.architecture,
|
|
|
|
"dir_tbb": tbb_local
|
|
|
|
+ "/tbb/"
|
|
|
|
+ self.architecture
|
|
|
|
+ "/tor-browser_"
|
|
|
|
+ self.language,
|
|
|
|
"start": tbb_local
|
|
|
|
+ "/tbb/"
|
|
|
|
+ self.architecture
|
|
|
|
+ "/tor-browser_"
|
|
|
|
+ self.language
|
|
|
|
+ "/start-tor-browser.desktop",
|
2014-09-03 18:24:28 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
Check that GnuPG key import was successful.
Rather than checking the GnuPG process exit code, a more robust way to
handle determining whether or not a GnuPG process behaved as was
intended is to check GnuPG's status-fd output. [0]
In the case of key import, the particular status-fd flag we're looking
for is `IMPORT_OK` followed by a "reason", then the expected
fingerprint. [1] Because the "reason"s are integers which may be ORed,
and we are never expecting private keys to be within the file, we can
assume the reason to be `[0, 15]` inclusive.
While it's not strictly necessary to hardcode Erinn's key fingerprint
within the code because the keyfiles are safely distributed along with
the source code, doing so adds a simple defense-in-depth mechanism for
the unlikely case that a user's torbrowser-launcher package/source
download was compromised. As such, and because it was a trivial
addition which will also assist with checking that a signature was made
by the key with the expected fingerprint [2], I've gone ahead and added
a `common.fingerprints` dictionary whose keys match the names of the
`common.paths` keyfile for their respective key (i.e. the fingerprint
for `common.paths['erinn_key']` is stored at
`common.fingerprints['erinn_key']`) in order to facilitate extensibility
in the event that torbrowser-launcher should add new keyfiles in the
future. This may be removed, if undesirable.
* ADD `common.gnupg_import_ok_pattern`, a compiled regex for
determining if a key import was successful.
* ADD new class attribute, `common.Common.fingerprints` for storing
fingerprints.
* ADD new method, `common.Common.import_key_and_check_status()`, which
imports a GnuPG key, and then checks that the key was successfully
imported.
* CHANGE `common.Common.import_keys()` method to make adding
new/additional keys easier.
* FIXES https://github.com/micahflee/torbrowser-launcher/issues/137
[0]: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;hb=HEAD#l323
[1]: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;hb=HEAD#l713
[2]: https://github.com/micahflee/torbrowser-launcher/issues/147
2014-10-27 21:16:51 +00:00
|
|
|
# Add the expected fingerprint for imported keys:
|
2016-07-08 23:33:57 +00:00
|
|
|
self.fingerprints = {
|
2020-10-06 22:59:15 +00:00
|
|
|
"tor_browser_developers": "EF6E286DDA85EA2A4BA7DE684E2C6E8793298290"
|
2016-07-08 23:33:57 +00:00
|
|
|
}
|
Check that GnuPG key import was successful.
Rather than checking the GnuPG process exit code, a more robust way to
handle determining whether or not a GnuPG process behaved as was
intended is to check GnuPG's status-fd output. [0]
In the case of key import, the particular status-fd flag we're looking
for is `IMPORT_OK` followed by a "reason", then the expected
fingerprint. [1] Because the "reason"s are integers which may be ORed,
and we are never expecting private keys to be within the file, we can
assume the reason to be `[0, 15]` inclusive.
While it's not strictly necessary to hardcode Erinn's key fingerprint
within the code because the keyfiles are safely distributed along with
the source code, doing so adds a simple defense-in-depth mechanism for
the unlikely case that a user's torbrowser-launcher package/source
download was compromised. As such, and because it was a trivial
addition which will also assist with checking that a signature was made
by the key with the expected fingerprint [2], I've gone ahead and added
a `common.fingerprints` dictionary whose keys match the names of the
`common.paths` keyfile for their respective key (i.e. the fingerprint
for `common.paths['erinn_key']` is stored at
`common.fingerprints['erinn_key']`) in order to facilitate extensibility
in the event that torbrowser-launcher should add new keyfiles in the
future. This may be removed, if undesirable.
* ADD `common.gnupg_import_ok_pattern`, a compiled regex for
determining if a key import was successful.
* ADD new class attribute, `common.Common.fingerprints` for storing
fingerprints.
* ADD new method, `common.Common.import_key_and_check_status()`, which
imports a GnuPG key, and then checks that the key was successfully
imported.
* CHANGE `common.Common.import_keys()` method to make adding
new/additional keys easier.
* FIXES https://github.com/micahflee/torbrowser-launcher/issues/137
[0]: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;hb=HEAD#l323
[1]: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;hb=HEAD#l713
[2]: https://github.com/micahflee/torbrowser-launcher/issues/147
2014-10-27 21:16:51 +00:00
|
|
|
|
2014-09-03 18:24:28 +00:00
|
|
|
# create a directory
|
|
|
|
@staticmethod
|
|
|
|
def mkdir(path):
|
|
|
|
try:
|
|
|
|
if not os.path.exists(path):
|
2016-11-09 01:57:52 +00:00
|
|
|
os.makedirs(path, 0o700)
|
2014-09-03 18:24:28 +00:00
|
|
|
return True
|
|
|
|
except:
|
2016-11-09 01:54:37 +00:00
|
|
|
print(_("Cannot create directory {0}").format(path))
|
2014-09-03 18:24:28 +00:00
|
|
|
return False
|
|
|
|
if not os.access(path, os.W_OK):
|
2016-11-09 01:54:37 +00:00
|
|
|
print(_("{0} is not writable").format(path))
|
2014-09-03 18:24:28 +00:00
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
# if gnupg_homedir isn't set up, set it up
|
|
|
|
def init_gnupg(self):
|
2020-10-06 22:59:15 +00:00
|
|
|
if not os.path.exists(self.paths["gnupg_homedir"]):
|
|
|
|
print(_("Creating GnuPG homedir"), self.paths["gnupg_homedir"])
|
|
|
|
self.mkdir(self.paths["gnupg_homedir"])
|
2014-09-03 18:24:28 +00:00
|
|
|
self.import_keys()
|
|
|
|
|
2017-04-08 19:05:51 +00:00
|
|
|
def refresh_keyring(self, fingerprint=None):
|
2017-04-23 23:41:24 +00:00
|
|
|
if fingerprint is not None:
|
2020-10-06 22:59:15 +00:00
|
|
|
print("Refreshing local keyring... Missing key: " + fingerprint)
|
2017-04-23 23:41:24 +00:00
|
|
|
else:
|
2020-10-06 22:59:15 +00:00
|
|
|
print("Refreshing local keyring...")
|
2017-04-23 23:41:24 +00:00
|
|
|
|
2020-10-06 22:58:22 +00:00
|
|
|
# Fetch key from wkd, as per https://support.torproject.org/tbb/how-to-verify-signature/
|
2020-10-06 22:59:15 +00:00
|
|
|
p = subprocess.Popen(
|
|
|
|
[
|
|
|
|
"/usr/bin/gpg2",
|
|
|
|
"--status-fd",
|
|
|
|
"2",
|
|
|
|
"--homedir",
|
|
|
|
self.paths["gnupg_homedir"],
|
|
|
|
"--auto-key-locate",
|
|
|
|
"nodefault,wkd",
|
|
|
|
"--locate-keys",
|
|
|
|
"torbrowser@torproject.org",
|
|
|
|
],
|
|
|
|
stderr=subprocess.PIPE,
|
|
|
|
)
|
2017-04-22 15:17:33 +00:00
|
|
|
p.wait()
|
2017-04-08 19:05:51 +00:00
|
|
|
|
2017-04-23 23:41:24 +00:00
|
|
|
for output in p.stderr.readlines():
|
|
|
|
match = gnupg_import_ok_pattern.match(output)
|
2020-10-06 22:59:15 +00:00
|
|
|
if match and match.group(2) == "IMPORT_OK":
|
2017-04-23 23:41:24 +00:00
|
|
|
fingerprint = str(match.group(4))
|
2020-10-06 22:59:15 +00:00
|
|
|
if match.group(3) == "0":
|
|
|
|
print("Keyring refreshed successfully...")
|
|
|
|
print(" No key updates for key: " + fingerprint)
|
|
|
|
elif match.group(3) == "4":
|
|
|
|
print("Keyring refreshed successfully...")
|
|
|
|
print(" New signatures for key: " + fingerprint)
|
2017-04-23 23:41:24 +00:00
|
|
|
else:
|
2020-10-06 22:59:15 +00:00
|
|
|
print("Keyring refreshed successfully...")
|
2017-04-08 19:05:51 +00:00
|
|
|
|
Check that GnuPG key import was successful.
Rather than checking the GnuPG process exit code, a more robust way to
handle determining whether or not a GnuPG process behaved as was
intended is to check GnuPG's status-fd output. [0]
In the case of key import, the particular status-fd flag we're looking
for is `IMPORT_OK` followed by a "reason", then the expected
fingerprint. [1] Because the "reason"s are integers which may be ORed,
and we are never expecting private keys to be within the file, we can
assume the reason to be `[0, 15]` inclusive.
While it's not strictly necessary to hardcode Erinn's key fingerprint
within the code because the keyfiles are safely distributed along with
the source code, doing so adds a simple defense-in-depth mechanism for
the unlikely case that a user's torbrowser-launcher package/source
download was compromised. As such, and because it was a trivial
addition which will also assist with checking that a signature was made
by the key with the expected fingerprint [2], I've gone ahead and added
a `common.fingerprints` dictionary whose keys match the names of the
`common.paths` keyfile for their respective key (i.e. the fingerprint
for `common.paths['erinn_key']` is stored at
`common.fingerprints['erinn_key']`) in order to facilitate extensibility
in the event that torbrowser-launcher should add new keyfiles in the
future. This may be removed, if undesirable.
* ADD `common.gnupg_import_ok_pattern`, a compiled regex for
determining if a key import was successful.
* ADD new class attribute, `common.Common.fingerprints` for storing
fingerprints.
* ADD new method, `common.Common.import_key_and_check_status()`, which
imports a GnuPG key, and then checks that the key was successfully
imported.
* CHANGE `common.Common.import_keys()` method to make adding
new/additional keys easier.
* FIXES https://github.com/micahflee/torbrowser-launcher/issues/137
[0]: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;hb=HEAD#l323
[1]: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;hb=HEAD#l713
[2]: https://github.com/micahflee/torbrowser-launcher/issues/147
2014-10-27 21:16:51 +00:00
|
|
|
def import_key_and_check_status(self, key):
|
|
|
|
"""Import a GnuPG key and check that the operation was successful.
|
|
|
|
:param str key: A string specifying the key's filepath from
|
2017-03-03 14:01:52 +00:00
|
|
|
``Common.paths``
|
Check that GnuPG key import was successful.
Rather than checking the GnuPG process exit code, a more robust way to
handle determining whether or not a GnuPG process behaved as was
intended is to check GnuPG's status-fd output. [0]
In the case of key import, the particular status-fd flag we're looking
for is `IMPORT_OK` followed by a "reason", then the expected
fingerprint. [1] Because the "reason"s are integers which may be ORed,
and we are never expecting private keys to be within the file, we can
assume the reason to be `[0, 15]` inclusive.
While it's not strictly necessary to hardcode Erinn's key fingerprint
within the code because the keyfiles are safely distributed along with
the source code, doing so adds a simple defense-in-depth mechanism for
the unlikely case that a user's torbrowser-launcher package/source
download was compromised. As such, and because it was a trivial
addition which will also assist with checking that a signature was made
by the key with the expected fingerprint [2], I've gone ahead and added
a `common.fingerprints` dictionary whose keys match the names of the
`common.paths` keyfile for their respective key (i.e. the fingerprint
for `common.paths['erinn_key']` is stored at
`common.fingerprints['erinn_key']`) in order to facilitate extensibility
in the event that torbrowser-launcher should add new keyfiles in the
future. This may be removed, if undesirable.
* ADD `common.gnupg_import_ok_pattern`, a compiled regex for
determining if a key import was successful.
* ADD new class attribute, `common.Common.fingerprints` for storing
fingerprints.
* ADD new method, `common.Common.import_key_and_check_status()`, which
imports a GnuPG key, and then checks that the key was successfully
imported.
* CHANGE `common.Common.import_keys()` method to make adding
new/additional keys easier.
* FIXES https://github.com/micahflee/torbrowser-launcher/issues/137
[0]: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;hb=HEAD#l323
[1]: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;hb=HEAD#l713
[2]: https://github.com/micahflee/torbrowser-launcher/issues/147
2014-10-27 21:16:51 +00:00
|
|
|
:rtype: bool
|
|
|
|
:returns: ``True`` if the key is now within the keyring (or was
|
|
|
|
previously and hasn't changed). ``False`` otherwise.
|
|
|
|
"""
|
2018-03-23 20:36:37 +00:00
|
|
|
with gpg.Context() as c:
|
2020-10-06 22:59:15 +00:00
|
|
|
c.set_engine_info(
|
|
|
|
gpg.constants.protocol.OpenPGP, home_dir=self.paths["gnupg_homedir"]
|
|
|
|
)
|
2018-03-23 20:36:37 +00:00
|
|
|
|
2020-10-06 22:59:15 +00:00
|
|
|
impkey = self.paths["signing_keys"][key]
|
2018-03-23 20:36:37 +00:00
|
|
|
try:
|
|
|
|
c.op_import(gpg.Data(file=impkey))
|
|
|
|
except:
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
result = c.op_import_result()
|
|
|
|
if result and self.fingerprints[key] in result.imports[0].fpr:
|
|
|
|
return True
|
2017-03-22 16:57:10 +00:00
|
|
|
else:
|
2018-03-23 20:36:37 +00:00
|
|
|
return False
|
Check that GnuPG key import was successful.
Rather than checking the GnuPG process exit code, a more robust way to
handle determining whether or not a GnuPG process behaved as was
intended is to check GnuPG's status-fd output. [0]
In the case of key import, the particular status-fd flag we're looking
for is `IMPORT_OK` followed by a "reason", then the expected
fingerprint. [1] Because the "reason"s are integers which may be ORed,
and we are never expecting private keys to be within the file, we can
assume the reason to be `[0, 15]` inclusive.
While it's not strictly necessary to hardcode Erinn's key fingerprint
within the code because the keyfiles are safely distributed along with
the source code, doing so adds a simple defense-in-depth mechanism for
the unlikely case that a user's torbrowser-launcher package/source
download was compromised. As such, and because it was a trivial
addition which will also assist with checking that a signature was made
by the key with the expected fingerprint [2], I've gone ahead and added
a `common.fingerprints` dictionary whose keys match the names of the
`common.paths` keyfile for their respective key (i.e. the fingerprint
for `common.paths['erinn_key']` is stored at
`common.fingerprints['erinn_key']`) in order to facilitate extensibility
in the event that torbrowser-launcher should add new keyfiles in the
future. This may be removed, if undesirable.
* ADD `common.gnupg_import_ok_pattern`, a compiled regex for
determining if a key import was successful.
* ADD new class attribute, `common.Common.fingerprints` for storing
fingerprints.
* ADD new method, `common.Common.import_key_and_check_status()`, which
imports a GnuPG key, and then checks that the key was successfully
imported.
* CHANGE `common.Common.import_keys()` method to make adding
new/additional keys easier.
* FIXES https://github.com/micahflee/torbrowser-launcher/issues/137
[0]: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;hb=HEAD#l323
[1]: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;hb=HEAD#l713
[2]: https://github.com/micahflee/torbrowser-launcher/issues/147
2014-10-27 21:16:51 +00:00
|
|
|
|
2014-09-03 18:24:28 +00:00
|
|
|
# import gpg keys
|
|
|
|
def import_keys(self):
|
Check that GnuPG key import was successful.
Rather than checking the GnuPG process exit code, a more robust way to
handle determining whether or not a GnuPG process behaved as was
intended is to check GnuPG's status-fd output. [0]
In the case of key import, the particular status-fd flag we're looking
for is `IMPORT_OK` followed by a "reason", then the expected
fingerprint. [1] Because the "reason"s are integers which may be ORed,
and we are never expecting private keys to be within the file, we can
assume the reason to be `[0, 15]` inclusive.
While it's not strictly necessary to hardcode Erinn's key fingerprint
within the code because the keyfiles are safely distributed along with
the source code, doing so adds a simple defense-in-depth mechanism for
the unlikely case that a user's torbrowser-launcher package/source
download was compromised. As such, and because it was a trivial
addition which will also assist with checking that a signature was made
by the key with the expected fingerprint [2], I've gone ahead and added
a `common.fingerprints` dictionary whose keys match the names of the
`common.paths` keyfile for their respective key (i.e. the fingerprint
for `common.paths['erinn_key']` is stored at
`common.fingerprints['erinn_key']`) in order to facilitate extensibility
in the event that torbrowser-launcher should add new keyfiles in the
future. This may be removed, if undesirable.
* ADD `common.gnupg_import_ok_pattern`, a compiled regex for
determining if a key import was successful.
* ADD new class attribute, `common.Common.fingerprints` for storing
fingerprints.
* ADD new method, `common.Common.import_key_and_check_status()`, which
imports a GnuPG key, and then checks that the key was successfully
imported.
* CHANGE `common.Common.import_keys()` method to make adding
new/additional keys easier.
* FIXES https://github.com/micahflee/torbrowser-launcher/issues/137
[0]: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;hb=HEAD#l323
[1]: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;hb=HEAD#l713
[2]: https://github.com/micahflee/torbrowser-launcher/issues/147
2014-10-27 21:16:51 +00:00
|
|
|
"""Import all GnuPG keys.
|
|
|
|
:rtype: bool
|
|
|
|
:returns: ``True`` if all keys were successfully imported; ``False``
|
|
|
|
otherwise.
|
|
|
|
"""
|
2020-10-06 22:59:15 +00:00
|
|
|
keys = [
|
|
|
|
"tor_browser_developers",
|
|
|
|
]
|
Check that GnuPG key import was successful.
Rather than checking the GnuPG process exit code, a more robust way to
handle determining whether or not a GnuPG process behaved as was
intended is to check GnuPG's status-fd output. [0]
In the case of key import, the particular status-fd flag we're looking
for is `IMPORT_OK` followed by a "reason", then the expected
fingerprint. [1] Because the "reason"s are integers which may be ORed,
and we are never expecting private keys to be within the file, we can
assume the reason to be `[0, 15]` inclusive.
While it's not strictly necessary to hardcode Erinn's key fingerprint
within the code because the keyfiles are safely distributed along with
the source code, doing so adds a simple defense-in-depth mechanism for
the unlikely case that a user's torbrowser-launcher package/source
download was compromised. As such, and because it was a trivial
addition which will also assist with checking that a signature was made
by the key with the expected fingerprint [2], I've gone ahead and added
a `common.fingerprints` dictionary whose keys match the names of the
`common.paths` keyfile for their respective key (i.e. the fingerprint
for `common.paths['erinn_key']` is stored at
`common.fingerprints['erinn_key']`) in order to facilitate extensibility
in the event that torbrowser-launcher should add new keyfiles in the
future. This may be removed, if undesirable.
* ADD `common.gnupg_import_ok_pattern`, a compiled regex for
determining if a key import was successful.
* ADD new class attribute, `common.Common.fingerprints` for storing
fingerprints.
* ADD new method, `common.Common.import_key_and_check_status()`, which
imports a GnuPG key, and then checks that the key was successfully
imported.
* CHANGE `common.Common.import_keys()` method to make adding
new/additional keys easier.
* FIXES https://github.com/micahflee/torbrowser-launcher/issues/137
[0]: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;hb=HEAD#l323
[1]: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;hb=HEAD#l713
[2]: https://github.com/micahflee/torbrowser-launcher/issues/147
2014-10-27 21:16:51 +00:00
|
|
|
all_imports_succeeded = True
|
|
|
|
|
|
|
|
for key in keys:
|
|
|
|
imported = self.import_key_and_check_status(key)
|
|
|
|
if not imported:
|
2020-10-06 22:59:15 +00:00
|
|
|
print(
|
|
|
|
_(
|
|
|
|
"Could not import key with fingerprint: %s."
|
|
|
|
% self.fingerprints[key]
|
|
|
|
)
|
|
|
|
)
|
Check that GnuPG key import was successful.
Rather than checking the GnuPG process exit code, a more robust way to
handle determining whether or not a GnuPG process behaved as was
intended is to check GnuPG's status-fd output. [0]
In the case of key import, the particular status-fd flag we're looking
for is `IMPORT_OK` followed by a "reason", then the expected
fingerprint. [1] Because the "reason"s are integers which may be ORed,
and we are never expecting private keys to be within the file, we can
assume the reason to be `[0, 15]` inclusive.
While it's not strictly necessary to hardcode Erinn's key fingerprint
within the code because the keyfiles are safely distributed along with
the source code, doing so adds a simple defense-in-depth mechanism for
the unlikely case that a user's torbrowser-launcher package/source
download was compromised. As such, and because it was a trivial
addition which will also assist with checking that a signature was made
by the key with the expected fingerprint [2], I've gone ahead and added
a `common.fingerprints` dictionary whose keys match the names of the
`common.paths` keyfile for their respective key (i.e. the fingerprint
for `common.paths['erinn_key']` is stored at
`common.fingerprints['erinn_key']`) in order to facilitate extensibility
in the event that torbrowser-launcher should add new keyfiles in the
future. This may be removed, if undesirable.
* ADD `common.gnupg_import_ok_pattern`, a compiled regex for
determining if a key import was successful.
* ADD new class attribute, `common.Common.fingerprints` for storing
fingerprints.
* ADD new method, `common.Common.import_key_and_check_status()`, which
imports a GnuPG key, and then checks that the key was successfully
imported.
* CHANGE `common.Common.import_keys()` method to make adding
new/additional keys easier.
* FIXES https://github.com/micahflee/torbrowser-launcher/issues/137
[0]: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;hb=HEAD#l323
[1]: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;hb=HEAD#l713
[2]: https://github.com/micahflee/torbrowser-launcher/issues/147
2014-10-27 21:16:51 +00:00
|
|
|
all_imports_succeeded = False
|
|
|
|
|
2016-07-08 23:44:40 +00:00
|
|
|
if not all_imports_succeeded:
|
2020-10-06 22:59:15 +00:00
|
|
|
print(_("Not all keys were imported successfully!"))
|
Check that GnuPG key import was successful.
Rather than checking the GnuPG process exit code, a more robust way to
handle determining whether or not a GnuPG process behaved as was
intended is to check GnuPG's status-fd output. [0]
In the case of key import, the particular status-fd flag we're looking
for is `IMPORT_OK` followed by a "reason", then the expected
fingerprint. [1] Because the "reason"s are integers which may be ORed,
and we are never expecting private keys to be within the file, we can
assume the reason to be `[0, 15]` inclusive.
While it's not strictly necessary to hardcode Erinn's key fingerprint
within the code because the keyfiles are safely distributed along with
the source code, doing so adds a simple defense-in-depth mechanism for
the unlikely case that a user's torbrowser-launcher package/source
download was compromised. As such, and because it was a trivial
addition which will also assist with checking that a signature was made
by the key with the expected fingerprint [2], I've gone ahead and added
a `common.fingerprints` dictionary whose keys match the names of the
`common.paths` keyfile for their respective key (i.e. the fingerprint
for `common.paths['erinn_key']` is stored at
`common.fingerprints['erinn_key']`) in order to facilitate extensibility
in the event that torbrowser-launcher should add new keyfiles in the
future. This may be removed, if undesirable.
* ADD `common.gnupg_import_ok_pattern`, a compiled regex for
determining if a key import was successful.
* ADD new class attribute, `common.Common.fingerprints` for storing
fingerprints.
* ADD new method, `common.Common.import_key_and_check_status()`, which
imports a GnuPG key, and then checks that the key was successfully
imported.
* CHANGE `common.Common.import_keys()` method to make adding
new/additional keys easier.
* FIXES https://github.com/micahflee/torbrowser-launcher/issues/137
[0]: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;hb=HEAD#l323
[1]: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;hb=HEAD#l713
[2]: https://github.com/micahflee/torbrowser-launcher/issues/147
2014-10-27 21:16:51 +00:00
|
|
|
|
|
|
|
return all_imports_succeeded
|
2014-09-03 18:24:28 +00:00
|
|
|
|
|
|
|
# load mirrors
|
|
|
|
def load_mirrors(self):
|
|
|
|
self.mirrors = []
|
2020-10-06 22:59:15 +00:00
|
|
|
for srcfile in self.paths["mirrors_txt"]:
|
2015-01-08 01:46:52 +00:00
|
|
|
if not os.path.exists(srcfile):
|
2014-09-03 18:24:28 +00:00
|
|
|
continue
|
2020-10-06 22:59:15 +00:00
|
|
|
for mirror in open(srcfile, "r").readlines():
|
2014-09-03 18:24:28 +00:00
|
|
|
if mirror.strip() not in self.mirrors:
|
|
|
|
self.mirrors.append(mirror.strip())
|
|
|
|
|
|
|
|
# load settings
|
|
|
|
def load_settings(self):
|
|
|
|
default_settings = {
|
2020-10-06 22:59:15 +00:00
|
|
|
"tbl_version": self.tbl_version,
|
|
|
|
"installed": False,
|
|
|
|
"download_over_tor": False,
|
|
|
|
"tor_socks_address": "127.0.0.1:9050",
|
|
|
|
"mirror": self.default_mirror,
|
|
|
|
"force_en-US": False,
|
2014-09-03 18:24:28 +00:00
|
|
|
}
|
|
|
|
|
2020-10-06 22:59:15 +00:00
|
|
|
if os.path.isfile(self.paths["settings_file"]):
|
|
|
|
settings = json.load(open(self.paths["settings_file"]))
|
2014-09-03 18:24:28 +00:00
|
|
|
resave = False
|
|
|
|
|
2015-11-09 03:44:18 +00:00
|
|
|
# detect installed
|
2020-10-06 22:59:15 +00:00
|
|
|
settings["installed"] = os.path.isfile(self.paths["tbb"]["start"])
|
2015-11-09 03:44:18 +00:00
|
|
|
|
2014-09-03 18:24:28 +00:00
|
|
|
# make sure settings file is up-to-date
|
|
|
|
for setting in default_settings:
|
|
|
|
if setting not in settings:
|
|
|
|
settings[setting] = default_settings[setting]
|
|
|
|
resave = True
|
|
|
|
|
2018-03-23 19:49:52 +00:00
|
|
|
# make sure tor_socks_address doesn't start with 'tcp:'
|
2020-10-06 22:59:15 +00:00
|
|
|
if settings["tor_socks_address"].startswith("tcp:"):
|
|
|
|
settings["tor_socks_address"] = settings["tor_socks_address"][4:]
|
2018-03-23 19:49:52 +00:00
|
|
|
resave = True
|
|
|
|
|
2014-09-03 18:24:28 +00:00
|
|
|
# make sure the version is current
|
2020-10-06 22:59:15 +00:00
|
|
|
if settings["tbl_version"] != self.tbl_version:
|
|
|
|
settings["tbl_version"] = self.tbl_version
|
2014-09-03 18:24:28 +00:00
|
|
|
resave = True
|
|
|
|
|
|
|
|
self.settings = settings
|
|
|
|
if resave:
|
|
|
|
self.save_settings()
|
|
|
|
|
2015-11-06 02:13:57 +00:00
|
|
|
# if settings file is still using old pickle format, convert to json
|
2020-10-06 22:59:15 +00:00
|
|
|
elif os.path.isfile(self.paths["settings_file_pickle"]):
|
|
|
|
self.settings = pickle.load(open(self.paths["settings_file_pickle"]))
|
2015-11-06 02:13:57 +00:00
|
|
|
self.save_settings()
|
2020-10-06 22:59:15 +00:00
|
|
|
os.remove(self.paths["settings_file_pickle"])
|
2015-11-06 02:13:57 +00:00
|
|
|
self.load_settings()
|
|
|
|
|
2014-09-03 18:24:28 +00:00
|
|
|
else:
|
|
|
|
self.settings = default_settings
|
|
|
|
self.save_settings()
|
|
|
|
|
|
|
|
# save settings
|
|
|
|
def save_settings(self):
|
2020-10-06 22:59:15 +00:00
|
|
|
json.dump(self.settings, open(self.paths["settings_file"], "w"))
|
2014-09-03 18:24:28 +00:00
|
|
|
return True
|