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 *
2225import highlevelcrypto
2326import shared
2427import helper_startup
2528
2629
30+
2731config = ConfigParser .SafeConfigParser ()
2832myECCryptorObjects = {}
2933MyECSubscriptionCryptorObjects = {}
@@ -118,8 +122,11 @@ def lookupAppdataFolder():
118122 if "HOME" in environ :
119123 dataFolder = path .join (os .environ ["HOME" ], "Library/Application Support/" , APPNAME ) + '/'
120124 else :
121- logger .critical ('Could not find home folder, please report this message and your '
122- 'OS X version to the BitMessage Github.' )
125+ stringToLog = 'Could not find home folder, please report this message and your OS X version to the BitMessage Github.'
126+ if 'logger' in globals ():
127+ logger .critical (stringToLog )
128+ else :
129+ print stringToLog
123130 sys .exit ()
124131
125132 elif 'win32' in sys .platform or 'win64' in sys .platform :
@@ -133,9 +140,14 @@ def lookupAppdataFolder():
133140
134141 # Migrate existing data to the proper location if this is an existing install
135142 try :
136- logger .info ("Moving data folder to %s" % (dataFolder ))
137143 move (path .join (environ ["HOME" ], ".%s" % APPNAME ), dataFolder )
144+ stringToLog = "Moving data folder to %s" % (dataFolder )
145+ if 'logger' in globals ():
146+ logger .info (stringToLog )
147+ else :
148+ print stringToLog
138149 except IOError :
150+ # Old directory may not exist.
139151 pass
140152 dataFolder = dataFolder + '/'
141153 return dataFolder
@@ -181,23 +193,26 @@ def isAddressInMyAddressBookSubscriptionsListOrWhitelist(address):
181193 return False
182194
183195def safeConfigGetBoolean (section ,field ):
184- try :
185- return config .getboolean (section ,field )
186- except :
187- return False
196+ try :
197+ return config .getboolean (section ,field )
198+ except :
199+ return False
188200
189201def decodeWalletImportFormat (WIFstring ):
190202 fullString = arithmetic .changebase (WIFstring ,58 ,256 )
191203 privkey = fullString [:- 4 ]
192204 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 ))
205+ logger .error ('Major problem! When trying to decode one of your private keys, the checksum '
206+ 'failed. Here is the PRIVATE key: %s\n ' % str (WIFstring ))
194207 return ""
195208 else :
196209 #checksum passed
197210 if privkey [0 ] == '\x80 ' :
198211 return privkey [1 :]
199212 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 ))
213+ logger .error ('Major problem! When trying to decode one of your private keys, the '
214+ 'checksum passed but the key doesn\' t begin with hex 80. Here is the '
215+ 'PRIVATE key: %s\n ' % str (WIFstring ))
201216 return ""
202217
203218
@@ -206,19 +221,32 @@ def reloadMyAddressHashes():
206221 myECCryptorObjects .clear ()
207222 myAddressesByHash .clear ()
208223 #myPrivateKeys.clear()
224+
225+ keyfileSecure = checkSensitiveFilePermissions (appdata + 'keys.dat' )
209226 configSections = config .sections ()
227+ hasEnabledKeys = False
210228 for addressInKeysFile in configSections :
211229 if addressInKeysFile <> 'bitmessagesettings' :
212230 isEnabled = config .getboolean (addressInKeysFile , 'enabled' )
213231 if isEnabled :
232+ hasEnabledKeys = True
214233 status ,addressVersionNumber ,streamNumber ,hash = decodeAddress (addressInKeysFile )
215234 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
235+ # Returns a simple 32 bytes of information encoded in 64 Hex characters,
236+ # or null if there was an error.
237+ privEncryptionKey = decodeWalletImportFormat (
238+ config .get (addressInKeysFile , 'privencryptionkey' )).encode ('hex' )
239+
217240 if len (privEncryptionKey ) == 64 :#It is 32 bytes encoded as 64 hex characters
218241 myECCryptorObjects [hash ] = highlevelcrypto .makeCryptor (privEncryptionKey )
219242 myAddressesByHash [hash ] = addressInKeysFile
243+
220244 else :
221- sys .stderr .write ('Error in reloadMyAddressHashes: Can\' t handle address versions other than 2 or 3.\n ' )
245+ logger .error ('Error in reloadMyAddressHashes: Can\' t handle address '
246+ 'versions other than 2 or 3.\n ' )
247+
248+ if not keyfileSecure :
249+ fixSensitiveFilePermissions (appdata + 'keys.dat' , hasEnabledKeys )
222250
223251def reloadBroadcastSendersForWhichImWatching ():
224252 logger .debug ('reloading subscriptions...' )
@@ -269,6 +297,7 @@ def doCleanShutdown():
269297 sqlSubmitQueue .put ('exit' )
270298 sqlLock .release ()
271299 logger .info ('Finished flushing inventory.' )
300+
272301 # Wait long enough to guarantee that any running proof of work worker threads will check the
273302 # shutdown variable and exit. If the main thread closes before they do then they won't stop.
274303 time .sleep (.25 )
@@ -306,5 +335,40 @@ def fixPotentiallyInvalidUTF8Data(text):
306335 output = 'Part of the message is corrupt. The message cannot be displayed the normal way.\n \n ' + repr (text )
307336 return output
308337
338+ # Checks sensitive file permissions for inappropriate umask during keys.dat creation.
339+ # (Or unwise subsequent chmod.)
340+ #
341+ # Returns true iff file appears to have appropriate permissions.
342+ def checkSensitiveFilePermissions (filename ):
343+ if sys .platform == 'win32' :
344+ # TODO: This might deserve extra checks by someone familiar with
345+ # Windows systems.
346+ return True
347+ else :
348+ present_permissions = os .stat (filename )[0 ]
349+ disallowed_permissions = stat .S_IRWXG | stat .S_IRWXO
350+ return present_permissions & disallowed_permissions == 0
351+
352+ # Fixes permissions on a sensitive file.
353+ def fixSensitiveFilePermissions (filename , hasEnabledKeys ):
354+ if hasEnabledKeys :
355+ logger .warning ('Keyfile had insecure permissions, and there were enabled keys. '
356+ 'The truly paranoid should stop using them immediately.' )
357+ else :
358+ logger .warning ('Keyfile had insecure permissions, but there were no enabled keys.' )
359+ try :
360+ present_permissions = os .stat (filename )[0 ]
361+ disallowed_permissions = stat .S_IRWXG | stat .S_IRWXO
362+ allowed_permissions = ((1 << 32 )- 1 ) ^ disallowed_permissions
363+ new_permissions = (
364+ allowed_permissions & present_permissions )
365+ os .chmod (filename , new_permissions )
366+
367+ logger .info ('Keyfile permissions automatically fixed.' )
368+
369+ except Exception , e :
370+ logger .exception ('Keyfile permissions could not be fixed.' )
371+ raise
372+
309373helper_startup .loadConfig ()
310374from debug import logger
0 commit comments