88useVeryEasyProofOfWorkForTesting = 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.
1912import ConfigParser
20- import socket
13+ import os
14+ import pickle
15+ import Queue
2116import 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
2226import highlevelcrypto
2327import shared
2428import helper_startup
2529
2630
31+
2732config = ConfigParser .SafeConfigParser ()
2833myECCryptorObjects = {}
2934MyECSubscriptionCryptorObjects = {}
@@ -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
183189def 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
189195def 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
223245def 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+
309367helper_startup .loadConfig ()
310368from debug import logger
0 commit comments