Skip to content

Commit

Permalink
Add hashed control password protection for tor
Browse files Browse the repository at this point in the history
  • Loading branch information
cipres authored and cipres committed Dec 4, 2020
1 parent e5bff47 commit a76f86f
Show file tree
Hide file tree
Showing 21 changed files with 280 additions and 168 deletions.
22 changes: 15 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,34 @@ the changes in the CHANGELOG formatting.

## [Unreleased]

## [0.4.41] - 2020-12-02
## [0.4.41] - 2020-12-04
### Added
- Tor support
- Automatic use of Tor when bootstrapping succeeds
- Tor proxying for all ipfs-search and cyber requests
- Tor proxying on all platforms (enabled manually from the
status bar for now, very soon there'll be finer control
of the relays via [stem](https://stem.torproject.org/))
- Proxying of ipfs-search and cyber requests via Tor

- Add a new *anonymous* web profile
- Automatically fetch favicons when hashmarking an http(s) website
- Handle SSL certificate errors

- New python dependencies
- [aiohttp-socks](https://pypi.org/project/aiohttp-socks/) >=0.5.5
- validators >= 0.18.1

### Changed
- Bookmarking of any type of URLs
- Browser UI
- Browser tab UI
- Use a block-style cursor for the address bar
- Typing an .eth domain name automatically loads it through *ens://*
- Run IPFS searches or searches with popular engines (duckduckgo, ..)
from the address bar
- Nicer history lookup interface
- Change the history lookup interface

- The @Earth workspace is now the default workspace in the stack
- The @Earth workspace is now the first/default workspace in the WS stack
- Workspace APIs
- Changed wsRegisterTab(): accept a *position* argument to insert tabs
at the end of the tabs list or after the current tab

### Fixed
- Bookmarking of clearnet URLs
Expand Down
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ for more information).
of files in the MFS)
- File sharing
- BitTorrent to IPFS bridge
- Tor support
- Search content with the ipfs-search_ search engine as well as with cyber_
- Built-in blog with Atom feeds
- Webcam to IPFS capture (image and videos)
Expand Down
5 changes: 4 additions & 1 deletion galacteek/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,8 @@ def initMisc(self):
self.tempDirWeb = self.tempDirCreate(
self.tempDir.path(), 'webdownloads')

self.tor = TorLauncher(self._torConfigLocation)
self.tor = TorLauncher(self._torConfigLocation,
self._torDataDirLocation)
self._goIpfsBinPath = self.suitableGoIpfsBinary()

def tempDirCreate(self, basedir, name=None):
Expand Down Expand Up @@ -1008,6 +1009,7 @@ def setupPaths(self):
self._mHashDbLocation = os.path.join(self.dataLocation, 'mhashmetadb')
self._sqliteDbLocation = os.path.join(self.dataLocation, 'db.sqlite')
self._torConfigLocation = os.path.join(self.dataLocation, 'torrc')
self._torDataDirLocation = os.path.join(self.dataLocation, 'tor-data')
self._pLockLocation = os.path.join(self.dataLocation, 'profile.lock')
self._mainDbLocation = os.path.join(
self.dataLocation, 'db_main.sqlite3')
Expand Down Expand Up @@ -1041,6 +1043,7 @@ def setupPaths(self):
for dir in [self._mHashDbLocation,
self._logsLocation,
self.ipfsBinLocation,
self._torDataDirLocation,
self.marksDataLocation,
self.cryptoDataLocation,
self.eccDataLocation,
Expand Down
3 changes: 3 additions & 0 deletions galacteek/core/glogger.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ class LogRecordStyler:
'basecolor': 'darkred',
'red': -0.1
},
'galacteek.core.tor': {
'basecolor': 'darkred'
},

# crypto modules
'galacteek.crypto': {
Expand Down
90 changes: 71 additions & 19 deletions galacteek/core/tor.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,56 @@
import asyncio
import re
import signal
import psutil
import platform
import subprocess
import tempfile
import secrets
from os import urandom
from binascii import b2a_hex
from hashlib import sha1

from galacteek import log
from galacteek import ensure
from galacteek import AsyncSignal
from galacteek.core.asynclib import asyncWriteFile
from galacteek.core import unusedTcpPort


def getTorHashedPassword(secret):
'''
https://gist.github.com/jamesacampbell/2f170fc17a328a638322078f42e04cbc
'''
# static 'count' value later referenced as "c"
indicator = chr(96)
# generate salt and append indicator value so that it
salt = "%s%s" % (urandom(8), indicator)
c = ord(salt[8])
# generate an even number that can be divided in subsequent sections.
# (Thanks Roman)
EXPBIAS = 6
count = (16 + (c & 15)) << ((c >> 4) + EXPBIAS)
d = sha1()
# take the salt and append the password
tmp = salt[:8] + secret
# hash the salty password
slen = len(tmp)
while count:
if count > slen:
d.update(tmp.encode())
count -= slen
else:
d.update(tmp[:count].encode())
count = 0
hashed = d.digest()

# Put it all together into the proprietary Tor format.
salt = b2a_hex(salt[:8].encode()).decode().upper()
ind = b2a_hex(indicator.encode()).decode()
h = b2a_hex(hashed).decode().upper()

return '16:{salt}{i}{h}'.format(
salt=salt,
i=ind,
h=h
)


class TorProtocol(asyncio.SubprocessProtocol):
Expand Down Expand Up @@ -65,16 +105,18 @@ def process_exited(self):
AutomapHostsOnResolve 1
AutomapHostsSuffixes .exit,.onion
DataDirectory {dataDir}
HashedControlPassword {hashedControlPass}
'''


class TorConfigBuilder:
def __init__(self):
def __init__(self, dataDir):
self._socksPort = None
self._controlPort = None
self._dnsPort = None
self._hostname = '127.0.0.1'
self._dataDir = tempfile.mkdtemp(prefix='gtor')
self._dataDir = dataDir
self.__controlPass = secrets.token_hex(8)

@property
def socksPort(self):
Expand Down Expand Up @@ -103,12 +145,14 @@ def __str__(self):
socksPort=self.socksPort,
controlPort=self.controlPort,
dnsPort=self.dnsPort,
dataDir=self._dataDir
dataDir=self._dataDir,
hashedControlPass=getTorHashedPassword(self.__controlPass)
)


class TorLauncher:
def __init__(self, configPath, torPath='tor', debug=True, loop=None):
def __init__(self, configPath, dataDirPath,
torPath='tor', debug=True, loop=None):

self.loop = loop if loop else asyncio.get_event_loop()
self.exitFuture = asyncio.Future(loop=self.loop)
Expand All @@ -118,9 +162,10 @@ def __init__(self, configPath, torPath='tor', debug=True, loop=None):
self._process = None
self.torPath = torPath
self.configPath = configPath
self.dataDirPath = dataDirPath
self.debug = debug
self.transport, self.proto = None, None
self.torCfg = TorConfigBuilder()
self.torCfg = TorConfigBuilder(self.dataDirPath)
self.torProto = TorProtocol(self.loop, self.exitFuture,
self.startedFuture,
debug=self.debug)
Expand Down Expand Up @@ -158,9 +203,9 @@ async def start(self):
startupInfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupInfo.wShowWindow = subprocess.SW_HIDE

# for socksPort in range(9052, 9080):
for x in range(0, 12):
socksPort = unusedTcpPort()
# for x in range(0, 12):
for socksPort in range(9050, 9080):
# socksPort = unusedTcpPort()

self.torCfg.socksPort = socksPort
await asyncWriteFile(self.configPath, str(self.torCfg), 'w+t')
Expand All @@ -184,8 +229,20 @@ async def start(self):

self._procPid = self.transport.get_pid()
self.process = psutil.Process(self._procPid)
except Exception:
log.debug(f'Starting TOR failed on port {socksPort}')

# Wait a bit, if there are port binding issues
# tor will exit immediately
await asyncio.sleep(3)

status = self.process.status()
assert status in [
psutil.STATUS_RUNNING,
psutil.STATUS_SLEEPING
]
except Exception as err:
log.debug(f'Starting TOR failed on port {socksPort} : '
f'error {err}')
self.transport.close()
continue
else:
log.debug(f'Starting TOR OK on port {socksPort}')
Expand All @@ -197,12 +254,7 @@ def stop(self):
if not self.process:
raise Exception('Process not found')

if platform.system() == 'Windows':
self.process.kill()
else:
self.process.send_signal(signal.SIGINT)
self.process.send_signal(signal.SIGHUP)

self.transport.terminate()
self._procPid = None
return True
except Exception as err:
Expand Down
1 change: 1 addition & 0 deletions galacteek/core/webprofiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def setSettings(self):
True)
self.webSettings.setAttribute(QWebEngineSettings.LocalStorageEnabled,
True)
self.setHttpCacheType(QWebEngineProfile.NoCache)

def installHandler(self, scheme, handler):
sch = scheme if isinstance(scheme, bytes) else scheme.encode()
Expand Down
2 changes: 1 addition & 1 deletion galacteek/docs/manual/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__manual_en_version__ = '20201022'
__manual_en_version__ = '20201204'
8 changes: 4 additions & 4 deletions galacteek/docs/manual/en/browsing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@ will hide the results.
You can also use specific syntax to search with certain
search engines:

- Use the **d** command to search with the
[DuckDuckGo](https://duckduckgo.com/) web search engine.
- Use the **d** prefix to search with the DuckDuckGo_ web search engine.
Example: **d distributed web**
- Use the **i** or **ip** command to run a search on the IPFS
- Use the **i** or **ip** prefix to run a search on the IPFS
network. Example: **i distributed web**

CID status icon
Expand Down Expand Up @@ -170,7 +169,7 @@ and the result is cached.
Web profiles
------------

There are 3 distinct web profiles that can be used when accessing a
There are 4 distinct web profiles that can be used when accessing a
webpage. The current profile can be changed from a browser tab by
opening the IPFS menu and selecting a profile from the *Web profile*
submenu.
Expand Down Expand Up @@ -208,3 +207,4 @@ a *Web3* instance (from the *web3.js* JS library) available as
*window.web3* in the main Javascript world

.. _ENS: https://ens.domains/
.. _DuckDuckGo: https://duckduckgo.com
Loading

0 comments on commit a76f86f

Please sign in to comment.