From e831a27ae30e5ffdd9c5ed4eb350e869642ad1a6 Mon Sep 17 00:00:00 2001 From: Kevin Froman Date: Mon, 23 Nov 2020 03:47:50 +0000 Subject: [PATCH] bug fixes in block creation and directory security --- CHANGELOG.md | 4 +++ git-update.sh | 2 +- src/etc/onionrvalues.py | 2 +- src/onionrexceptions.py | 5 ++++ src/onionrproofs/subprocesspow.py | 42 ++++++++++++++++--------------- src/utils/createdirs.py | 12 +++++++++ tests/runtime-result.txt | 2 +- 7 files changed, 46 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b4e6be3..c95a5244 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [7.1.0] - 2020-11-23 + +* Check for ownership of existing dirs in createdirs, this prevents the rare edge case where a user might use a home directory in a location an attacker could write (allowing arbitrary code execution via plugins). This was already partially mitigated by the chmod of the home directory in any case, but this further fixes the issue. + ## [7.0.0] - 2020-11-22 * Removed communicator timers diff --git a/git-update.sh b/git-update.sh index 3449787e..9ae6dbae 100755 --- a/git-update.sh +++ b/git-update.sh @@ -5,5 +5,5 @@ echo "Enter to continue, ctrl-c to stop." read git pull origin master -pip install -r --require-hashes requirements.txt +pip3 install -r --require-hashes requirements.txt ./onionr.sh reset-plugins diff --git a/src/etc/onionrvalues.py b/src/etc/onionrvalues.py index 5c4ce3f0..6c9db40f 100755 --- a/src/etc/onionrvalues.py +++ b/src/etc/onionrvalues.py @@ -23,7 +23,7 @@ import filepaths DENIABLE_PEER_ADDRESS = "OVPCZLOXD6DC5JHX4EQ3PSOGAZ3T24F75HQLIUZSDSMYPEOXCPFA" PASSWORD_LENGTH = 25 ONIONR_TAGLINE = 'Private P2P Communication - GPLv3 - https://Onionr.net' -ONIONR_VERSION = '7.0.0' +ONIONR_VERSION = '7.1.0' ONIONR_VERSION_CODENAME = 'Genesis' ONIONR_VERSION_TUPLE = tuple(ONIONR_VERSION.split('.')) # (MAJOR, MINOR, VERSION) API_VERSION = '2' # increments of 1; only change when something fundamental about how the API works changes. This way other nodes know how to communicate without learning too much information about you. diff --git a/src/onionrexceptions.py b/src/onionrexceptions.py index 0f30c7b1..d606873a 100755 --- a/src/onionrexceptions.py +++ b/src/onionrexceptions.py @@ -95,6 +95,11 @@ class Timeout(Exception): # file exceptions +class InsecureDirectoryUsage(IOError): + """This occurs when a directory used by the daemon is not owned by the user. + This is important to stop code execution attacks""" + pass + class DiskAllocationReached(Exception): pass diff --git a/src/onionrproofs/subprocesspow.py b/src/onionrproofs/subprocesspow.py index 426436b7..750bddec 100755 --- a/src/onionrproofs/subprocesspow.py +++ b/src/onionrproofs/subprocesspow.py @@ -116,24 +116,26 @@ class SubprocessPOW: metadata['n'] = secrets.randbits(16) puzzle = self.puzzle difficulty = self.difficulty - - while True: - # Break if shutdown received - try: - if pipe.poll() and pipe.recv() == 'shutdown': + try: + while True: + # Break if shutdown received + try: + if pipe.poll() and pipe.recv() == 'shutdown': + break + except KeyboardInterrupt: break - except KeyboardInterrupt: - break - # Load nonce into block metadata - metadata['c'] = nonce - # Serialize metadata, combine with block data - payload = json.dumps(metadata).encode() + b'\n' + data - # Check sha3_256 hash of block, compare to puzzle - # Send payload if puzzle finished - token = sha3_hash(payload) - # ensure token is string - token = bytesconverter.bytes_to_str(token) - if puzzle == token[0:difficulty]: - pipe.send(payload) - break - nonce += 1 + # Load nonce into block metadata + metadata['c'] = nonce + # Serialize metadata, combine with block data + payload = json.dumps(metadata).encode() + b'\n' + data + # Check sha3_256 hash of block, compare to puzzle + # Send payload if puzzle finished + token = sha3_hash(payload) + # ensure token is string + token = bytesconverter.bytes_to_str(token) + if puzzle == token[0:difficulty]: + pipe.send(payload) + break + nonce += 1 + except KeyboardInterrupt: + pass diff --git a/src/utils/createdirs.py b/src/utils/createdirs.py index add5f920..d0590bf4 100644 --- a/src/utils/createdirs.py +++ b/src/utils/createdirs.py @@ -4,9 +4,12 @@ Create required Onionr directories """ import os import stat +from pwd import getpwuid +from getpass import getuser from . import identifyhome import filepaths +import onionrexceptions """ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,6 +27,10 @@ import filepaths home = identifyhome.identify_home() +def find_owner(filename): + return getpwuid(os.stat(filename).st_uid).pw_name + + def create_dirs(): """Create onionr data-related directories in order of the hardcoded list below, @@ -33,6 +40,11 @@ def create_dirs(): for path in gen_dirs: if not os.path.exists(path): os.makedirs(path) + else: + if getuser() != find_owner(path): + raise onionrexceptions.InsecureDirectoryUsage( + "Directory " + path + + " already exists and is not owned by the same user") os.chmod(home, stat.S_IRWXU) diff --git a/tests/runtime-result.txt b/tests/runtime-result.txt index f0198367..58067c86 100644 --- a/tests/runtime-result.txt +++ b/tests/runtime-result.txt @@ -1 +1 @@ -1606002757 \ No newline at end of file +1606103134 \ No newline at end of file