Skip to content

Commit db3120f

Browse files
committed
Fix #263 & #262: insecure keyfile permissions.
* Added conditional to keyfile fix code that excludes windows. * Cleaned up old keyfile permissions fix. * Added umask (not conditional against Windows, because I don't think that is necessary).
1 parent 14bf354 commit db3120f

2 files changed

Lines changed: 72 additions & 27 deletions

File tree

src/helper_startup.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ 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)
7981

src/shared.py

Lines changed: 70 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,10 @@ def isAddressInMyAddressBookSubscriptionsListOrWhitelist(address):
169169
return False
170170

171171
def safeConfigGetBoolean(section,field):
172-
try:
173-
return config.getboolean(section,field)
174-
except:
175-
return False
172+
try:
173+
return config.getboolean(section,field)
174+
except:
175+
return False
176176

177177
def decodeWalletImportFormat(WIFstring):
178178
fullString = arithmetic.changebase(WIFstring,58,256)
@@ -196,22 +196,37 @@ def reloadMyAddressHashes():
196196
myECCryptorObjects.clear()
197197
myAddressesByHash.clear()
198198
#myPrivateKeys.clear()
199+
200+
keyfileSecure = checkSensitiveFilePermissions(appdata + 'keys.dat')
199201
configSections = config.sections()
200-
hasExistingKeys = False
202+
hasEnabledKeys = False
201203
for addressInKeysFile in configSections:
202204
if addressInKeysFile <> 'bitmessagesettings':
203-
hasExistingKeys = True
205+
hasEnabledKeys = True
204206
isEnabled = config.getboolean(addressInKeysFile, 'enabled')
205207
if isEnabled:
206-
status,addressVersionNumber,streamNumber,hash = decodeAddress(addressInKeysFile)
207-
if addressVersionNumber == 2 or addressVersionNumber == 3:
208-
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
209-
if len(privEncryptionKey) == 64:#It is 32 bytes encoded as 64 hex characters
210-
myECCryptorObjects[hash] = highlevelcrypto.makeCryptor(privEncryptionKey)
211-
myAddressesByHash[hash] = addressInKeysFile
208+
if keyfileSecure:
209+
status,addressVersionNumber,streamNumber,hash = decodeAddress(addressInKeysFile)
210+
if addressVersionNumber == 2 or addressVersionNumber == 3:
211+
# Returns a simple 32 bytes of information encoded in 64 Hex characters, or null if there was an error.
212+
privEncryptionKey = decodeWalletImportFormat(
213+
config.get(addressInKeysFile, 'privencryptionkey')).encode('hex')
214+
215+
if len(privEncryptionKey) == 64:#It is 32 bytes encoded as 64 hex characters
216+
myECCryptorObjects[hash] = highlevelcrypto.makeCryptor(privEncryptionKey)
217+
myAddressesByHash[hash] = addressInKeysFile
218+
else:
219+
sys.stderr.write('Error in reloadMyAddressHashes: Can\'t handle address versions other than 2 or 3.\n')
212220
else:
213-
sys.stderr.write('Error in reloadMyAddressHashes: Can\'t handle address versions other than 2 or 3.\n')
214-
fixKeyfilePermissions(appdata + 'keys.dat', hasExistingKeys)
221+
# Insecure keyfile permissions. Disable key.
222+
config.set(addressInKeysFile, 'enabled', 'false')
223+
try:
224+
if not keyfileSecure:
225+
with open(appdata + 'keys.dat', 'wb') as keyfile:
226+
config.write(keyfile)
227+
except:
228+
print 'Failed to disable vulnerable keyfiles.'
229+
raise
215230

216231
def reloadBroadcastSendersForWhichImWatching():
217232
printLock.acquire()
@@ -303,16 +318,14 @@ def fixPotentiallyInvalidUTF8Data(text):
303318
output = 'Part of the message is corrupt. The message cannot be displayed the normal way.\n\n' + repr(text)
304319
return output
305320

306-
# Fix keyfile permissions due to inappropriate umask during keys.dat creation.
307-
def fixKeyfilePermissions(keyfile, hasExistingKeys):
308-
present_keyfile_permissions = os.stat(keyfile)[0]
309-
keyfile_disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
310-
if (present_keyfile_permissions & keyfile_disallowed_permissions) != 0:
311-
allowed_keyfile_permissions = ((1<<32)-1) ^ keyfile_disallowed_permissions
312-
new_keyfile_permissions = (
313-
allowed_keyfile_permissions & present_keyfile_permissions)
314-
os.chmod(keyfile, new_keyfile_permissions)
315-
if hasExistingKeys:
321+
def checkSensitiveFilePermissions(filename):
322+
if sys.platform == 'win32':
323+
# TODO: This might deserve extra checks by someone familiar with
324+
# Windows systems.
325+
fileSecure = True
326+
else:
327+
fileSecure = secureFilePermissions(filename)
328+
if not fileSecure:
316329
print
317330
print '******************************************************************'
318331
print '** !! WARNING !! **'
@@ -321,7 +334,37 @@ def fixKeyfilePermissions(keyfile, hasExistingKeys):
321334
print '** Your keyfiles were vulnerable to being read by other users **'
322335
print '** (including some untrusted daemons). You may wish to consider **'
323336
print '** generating new keys and discontinuing use of your old ones. **'
324-
print '** The problem has been automatically fixed. **'
325-
print '******************************************************************'
326-
print
337+
print '** Your private keys have been disabled for your security, but **'
338+
print '** you may re-enable them using the "Your Identities" tab in **'
339+
print '** the default GUI or by modifying keys.dat such that your keys **'
340+
print '** show "enabled = true". **'
341+
try:
342+
fixSensitiveFilePermissions(filename)
343+
print '** The problem has been automatically fixed. **'
344+
print '******************************************************************'
345+
print
346+
except Exception, e:
347+
print '** Could NOT automatically fix permissions. **'
348+
print '******************************************************************'
349+
print
350+
raise
351+
return fileSecure
352+
353+
354+
# Checks sensitive file permissions for inappropriate umask during keys.dat creation.
355+
# (Or unwise subsequent chmod.)
356+
# Returns true iff file appears to have appropriate permissions.
357+
def secureFilePermissions(filename):
358+
present_permissions = os.stat(filename)[0]
359+
disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
360+
return present_permissions & disallowed_permissions == 0
361+
362+
# Fixes permissions on a sensitive file.
363+
def fixSensitiveFilePermissions(filename):
364+
present_permissions = os.stat(filename)[0]
365+
disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
366+
allowed_permissions = ((1<<32)-1) ^ disallowed_permissions
367+
new_permissions = (
368+
allowed_permissions & present_permissions)
369+
os.chmod(filename, new_permissions)
327370

0 commit comments

Comments
 (0)