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).
This commit is contained in:
Gregor Robinson 2013-06-27 10:02:52 +00:00
parent 14bf35421b
commit db3120f655
2 changed files with 72 additions and 27 deletions

View File

@ -74,6 +74,8 @@ def loadConfig():
print 'Creating new config files in', shared.appdata print 'Creating new config files in', shared.appdata
if not os.path.exists(shared.appdata): if not os.path.exists(shared.appdata):
os.makedirs(shared.appdata) os.makedirs(shared.appdata)
if not sys.platform.startswith('win'):
os.umask(0o077)
with open(shared.appdata + 'keys.dat', 'wb') as configfile: with open(shared.appdata + 'keys.dat', 'wb') as configfile:
shared.config.write(configfile) shared.config.write(configfile)

View File

@ -169,10 +169,10 @@ def isAddressInMyAddressBookSubscriptionsListOrWhitelist(address):
return False return False
def safeConfigGetBoolean(section,field): def safeConfigGetBoolean(section,field):
try: try:
return config.getboolean(section,field) return config.getboolean(section,field)
except: except:
return False return False
def decodeWalletImportFormat(WIFstring): def decodeWalletImportFormat(WIFstring):
fullString = arithmetic.changebase(WIFstring,58,256) fullString = arithmetic.changebase(WIFstring,58,256)
@ -196,22 +196,37 @@ def reloadMyAddressHashes():
myECCryptorObjects.clear() myECCryptorObjects.clear()
myAddressesByHash.clear() myAddressesByHash.clear()
#myPrivateKeys.clear() #myPrivateKeys.clear()
keyfileSecure = checkSensitiveFilePermissions(appdata + 'keys.dat')
configSections = config.sections() configSections = config.sections()
hasExistingKeys = False hasEnabledKeys = False
for addressInKeysFile in configSections: for addressInKeysFile in configSections:
if addressInKeysFile <> 'bitmessagesettings': if addressInKeysFile <> 'bitmessagesettings':
hasExistingKeys = True hasEnabledKeys = True
isEnabled = config.getboolean(addressInKeysFile, 'enabled') isEnabled = config.getboolean(addressInKeysFile, 'enabled')
if isEnabled: if isEnabled:
status,addressVersionNumber,streamNumber,hash = decodeAddress(addressInKeysFile) if keyfileSecure:
if addressVersionNumber == 2 or addressVersionNumber == 3: status,addressVersionNumber,streamNumber,hash = decodeAddress(addressInKeysFile)
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 if addressVersionNumber == 2 or addressVersionNumber == 3:
if len(privEncryptionKey) == 64:#It is 32 bytes encoded as 64 hex characters # Returns a simple 32 bytes of information encoded in 64 Hex characters, or null if there was an error.
myECCryptorObjects[hash] = highlevelcrypto.makeCryptor(privEncryptionKey) privEncryptionKey = decodeWalletImportFormat(
myAddressesByHash[hash] = addressInKeysFile config.get(addressInKeysFile, 'privencryptionkey')).encode('hex')
if len(privEncryptionKey) == 64:#It is 32 bytes encoded as 64 hex characters
myECCryptorObjects[hash] = highlevelcrypto.makeCryptor(privEncryptionKey)
myAddressesByHash[hash] = addressInKeysFile
else:
sys.stderr.write('Error in reloadMyAddressHashes: Can\'t handle address versions other than 2 or 3.\n')
else: else:
sys.stderr.write('Error in reloadMyAddressHashes: Can\'t handle address versions other than 2 or 3.\n') # Insecure keyfile permissions. Disable key.
fixKeyfilePermissions(appdata + 'keys.dat', hasExistingKeys) config.set(addressInKeysFile, 'enabled', 'false')
try:
if not keyfileSecure:
with open(appdata + 'keys.dat', 'wb') as keyfile:
config.write(keyfile)
except:
print 'Failed to disable vulnerable keyfiles.'
raise
def reloadBroadcastSendersForWhichImWatching(): def reloadBroadcastSendersForWhichImWatching():
printLock.acquire() printLock.acquire()
@ -303,16 +318,14 @@ def fixPotentiallyInvalidUTF8Data(text):
output = 'Part of the message is corrupt. The message cannot be displayed the normal way.\n\n' + repr(text) output = 'Part of the message is corrupt. The message cannot be displayed the normal way.\n\n' + repr(text)
return output return output
# Fix keyfile permissions due to inappropriate umask during keys.dat creation. def checkSensitiveFilePermissions(filename):
def fixKeyfilePermissions(keyfile, hasExistingKeys): if sys.platform == 'win32':
present_keyfile_permissions = os.stat(keyfile)[0] # TODO: This might deserve extra checks by someone familiar with
keyfile_disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO # Windows systems.
if (present_keyfile_permissions & keyfile_disallowed_permissions) != 0: fileSecure = True
allowed_keyfile_permissions = ((1<<32)-1) ^ keyfile_disallowed_permissions else:
new_keyfile_permissions = ( fileSecure = secureFilePermissions(filename)
allowed_keyfile_permissions & present_keyfile_permissions) if not fileSecure:
os.chmod(keyfile, new_keyfile_permissions)
if hasExistingKeys:
print print
print '******************************************************************' print '******************************************************************'
print '** !! WARNING !! **' print '** !! WARNING !! **'
@ -321,7 +334,37 @@ def fixKeyfilePermissions(keyfile, hasExistingKeys):
print '** Your keyfiles were vulnerable to being read by other users **' print '** Your keyfiles were vulnerable to being read by other users **'
print '** (including some untrusted daemons). You may wish to consider **' print '** (including some untrusted daemons). You may wish to consider **'
print '** generating new keys and discontinuing use of your old ones. **' print '** generating new keys and discontinuing use of your old ones. **'
print '** The problem has been automatically fixed. **' print '** Your private keys have been disabled for your security, but **'
print '******************************************************************' print '** you may re-enable them using the "Your Identities" tab in **'
print print '** the default GUI or by modifying keys.dat such that your keys **'
print '** show "enabled = true". **'
try:
fixSensitiveFilePermissions(filename)
print '** The problem has been automatically fixed. **'
print '******************************************************************'
print
except Exception, e:
print '** Could NOT automatically fix permissions. **'
print '******************************************************************'
print
raise
return fileSecure
# Checks sensitive file permissions for inappropriate umask during keys.dat creation.
# (Or unwise subsequent chmod.)
# Returns true iff file appears to have appropriate permissions.
def secureFilePermissions(filename):
present_permissions = os.stat(filename)[0]
disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
return present_permissions & disallowed_permissions == 0
# Fixes permissions on a sensitive file.
def fixSensitiveFilePermissions(filename):
present_permissions = os.stat(filename)[0]
disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
allowed_permissions = ((1<<32)-1) ^ disallowed_permissions
new_permissions = (
allowed_permissions & present_permissions)
os.chmod(filename, new_permissions)