Skip to content

Commit e82a8ae

Browse files
author
Jonathan Warren
committed
manual merge
2 parents b500a01 + fa53eb3 commit e82a8ae

File tree

3 files changed

+78
-18
lines changed

3 files changed

+78
-18
lines changed

src/helper_bootstrap.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def dns():
3333
print 'Adding', item[4][0], 'to knownNodes based on DNS boostrap method'
3434
shared.knownNodes[1][item[4][0]] = (8080, int(time.time()))
3535
except:
36-
print 'bootstrap8080.bitmessage.org DNS bootstraping failed.'
36+
print 'bootstrap8080.bitmessage.org DNS bootstrapping failed.'
3737
try:
3838
for item in socket.getaddrinfo('bootstrap8444.bitmessage.org', 80):
3939
print 'Adding', item[4][0], 'to knownNodes based on DNS boostrap method'

src/helper_startup.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,7 @@ def loadConfig():
7474
print 'Creating new config files in', shared.appdata
7575
if not os.path.exists(shared.appdata):
7676
os.makedirs(shared.appdata)
77+
if not sys.platform.startswith('win'):
78+
os.umask(0o077)
7779
with open(shared.appdata + 'keys.dat', 'wb') as configfile:
7880
shared.config.write(configfile)

src/shared.py

Lines changed: 75 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,27 @@
88
useVeryEasyProofOfWorkForTesting = False # If you set this to True while on the normal network, you won't be able to send or sometimes receive messages.
99

1010

11-
import threading
12-
import sys
13-
from addresses import *
14-
import highlevelcrypto
15-
import Queue
16-
import pickle
17-
import os
18-
import time
11+
# Libraries.
1912
import ConfigParser
20-
import socket
13+
import os
14+
import pickle
15+
import Queue
2116
import random
17+
import socket
18+
import sys
19+
import stat
20+
import threading
21+
import time
22+
23+
# Project imports.
24+
from addresses import *
25+
from debug import logger
2226
import highlevelcrypto
2327
import shared
2428
import helper_startup
2529

2630

31+
2732
config = ConfigParser.SafeConfigParser()
2833
myECCryptorObjects = {}
2934
MyECSubscriptionCryptorObjects = {}
@@ -136,6 +141,7 @@ def lookupAppdataFolder():
136141
logger.info("Moving data folder to %s" % (dataFolder))
137142
move(path.join(environ["HOME"], ".%s" % APPNAME), dataFolder)
138143
except IOError:
144+
# Old directory may not exist.
139145
pass
140146
dataFolder = dataFolder + '/'
141147
return dataFolder
@@ -181,23 +187,26 @@ def isAddressInMyAddressBookSubscriptionsListOrWhitelist(address):
181187
return False
182188

183189
def safeConfigGetBoolean(section,field):
184-
try:
185-
return config.getboolean(section,field)
186-
except:
187-
return False
190+
try:
191+
return config.getboolean(section,field)
192+
except:
193+
return False
188194

189195
def decodeWalletImportFormat(WIFstring):
190196
fullString = arithmetic.changebase(WIFstring,58,256)
191197
privkey = fullString[:-4]
192198
if fullString[-4:] != hashlib.sha256(hashlib.sha256(privkey).digest()).digest()[:4]:
193-
sys.stderr.write('Major problem! When trying to decode one of your private keys, the checksum failed. Here is the PRIVATE key: %s\n' % str(WIFstring))
199+
logger.error('Major problem! When trying to decode one of your private keys, the checksum '
200+
'failed. Here is the PRIVATE key: %s\n' % str(WIFstring))
194201
return ""
195202
else:
196203
#checksum passed
197204
if privkey[0] == '\x80':
198205
return privkey[1:]
199206
else:
200-
sys.stderr.write('Major problem! When trying to decode one of your private keys, the checksum passed but the key doesn\'t begin with hex 80. Here is the PRIVATE key: %s\n' % str(WIFstring))
207+
logger.error('Major problem! When trying to decode one of your private keys, the '
208+
'checksum passed but the key doesn\'t begin with hex 80. Here is the '
209+
'PRIVATE key: %s\n' % str(WIFstring))
201210
return ""
202211

203212

@@ -206,19 +215,32 @@ def reloadMyAddressHashes():
206215
myECCryptorObjects.clear()
207216
myAddressesByHash.clear()
208217
#myPrivateKeys.clear()
218+
219+
keyfileSecure = checkSensitiveFilePermissions(appdata + 'keys.dat')
209220
configSections = config.sections()
221+
hasEnabledKeys = False
210222
for addressInKeysFile in configSections:
211223
if addressInKeysFile <> 'bitmessagesettings':
212224
isEnabled = config.getboolean(addressInKeysFile, 'enabled')
213225
if isEnabled:
226+
hasEnabledKeys = True
214227
status,addressVersionNumber,streamNumber,hash = decodeAddress(addressInKeysFile)
215228
if addressVersionNumber == 2 or addressVersionNumber == 3:
216-
privEncryptionKey = decodeWalletImportFormat(config.get(addressInKeysFile, 'privencryptionkey')).encode('hex') #returns a simple 32 bytes of information encoded in 64 Hex characters, or null if there was an error
229+
# Returns a simple 32 bytes of information encoded in 64 Hex characters,
230+
# or null if there was an error.
231+
privEncryptionKey = decodeWalletImportFormat(
232+
config.get(addressInKeysFile, 'privencryptionkey')).encode('hex')
233+
217234
if len(privEncryptionKey) == 64:#It is 32 bytes encoded as 64 hex characters
218235
myECCryptorObjects[hash] = highlevelcrypto.makeCryptor(privEncryptionKey)
219236
myAddressesByHash[hash] = addressInKeysFile
237+
220238
else:
221-
sys.stderr.write('Error in reloadMyAddressHashes: Can\'t handle address versions other than 2 or 3.\n')
239+
logger.error('Error in reloadMyAddressHashes: Can\'t handle address '
240+
'versions other than 2 or 3.\n')
241+
242+
if not keyfileSecure:
243+
fixSensitiveFilePermissions(appdata + 'keys.dat', hasEnabledKeys)
222244

223245
def reloadBroadcastSendersForWhichImWatching():
224246
logger.debug('reloading subscriptions...')
@@ -269,6 +291,7 @@ def doCleanShutdown():
269291
sqlSubmitQueue.put('exit')
270292
sqlLock.release()
271293
logger.info('Finished flushing inventory.')
294+
272295
# Wait long enough to guarantee that any running proof of work worker threads will check the
273296
# shutdown variable and exit. If the main thread closes before they do then they won't stop.
274297
time.sleep(.25)
@@ -306,5 +329,40 @@ def fixPotentiallyInvalidUTF8Data(text):
306329
output = 'Part of the message is corrupt. The message cannot be displayed the normal way.\n\n' + repr(text)
307330
return output
308331

332+
# Checks sensitive file permissions for inappropriate umask during keys.dat creation.
333+
# (Or unwise subsequent chmod.)
334+
#
335+
# Returns true iff file appears to have appropriate permissions.
336+
def checkSensitiveFilePermissions(filename):
337+
if sys.platform == 'win32':
338+
# TODO: This might deserve extra checks by someone familiar with
339+
# Windows systems.
340+
return True
341+
else:
342+
present_permissions = os.stat(filename)[0]
343+
disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
344+
return present_permissions & disallowed_permissions == 0
345+
346+
# Fixes permissions on a sensitive file.
347+
def fixSensitiveFilePermissions(filename, hasEnabledKeys):
348+
if hasEnabledKeys:
349+
logger.warning('Keyfile had insecure permissions, and there were enabled keys. '
350+
'The truly paranoid should stop using them immediately.')
351+
else:
352+
logger.warning('Keyfile had insecure permissions, but there were no enabled keys.')
353+
try:
354+
present_permissions = os.stat(filename)[0]
355+
disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
356+
allowed_permissions = ((1<<32)-1) ^ disallowed_permissions
357+
new_permissions = (
358+
allowed_permissions & present_permissions)
359+
os.chmod(filename, new_permissions)
360+
361+
logger.info('Keyfile permissions automatically fixed.')
362+
363+
except Exception, e:
364+
logger.exception('Keyfile permissions could not be fixed.')
365+
raise
366+
309367
helper_startup.loadConfig()
310368
from debug import logger

0 commit comments

Comments
 (0)