It seems that a keychain file (with extension .keychain
) will usually have an invisible file associated with it, located in the same directory.
This invisible file always has these properties:
0444
(read-only for all users).Its name consists of .fl
followed by 8 hex characters. For example:
.fl043D1EDD
.fl947E1BDB
.fl9FAF0136
.fl12663120
.fl8E6EFC6C
.flCF600F4B
.fl1D8ECE61
.fl0D1D1BA9
.fl79E88CD1
.fl62323D2F
.fl75262C83
.fl652F188E
The invisible file can be deleted, but when the keychain's contents are next modified, the invisible file will be recreated with the same name.
Here are some steps to demonstrate, using the Keychain Access utility:
I've verified that this occurs in OS X 10.8 and 10.9.
The same invisible files are created when manipulating keychains using Apple's security
tool in the Terminal:
Create a new keychain. An invisible file is also created.
$ cd ~/Desktop/
$ ls -1a
.
..
$ /usr/bin/security create-keychain ~/Desktop/Test.keychain
$ ls -1a
.
..
.fl1BCE4B9A
Test.keychain
Delete the invisible file.
$ rm .fl1BCE4B9A
$ ls -1a
.
..
Test.keychain
Modify the keychain's contents (eg: by adding an internet password). The invisible file is recreated with the same name.
$ /usr/bin/security add-internet-password -a account -s google.com -w password ~/Desktop/Test.keychain
$ ls -1a
.
..
.fl1BCE4B9A
Test.keychain
Delete the keychain. The invisible file is deleted too.
$ /usr/bin/security delete-keychain ~/Desktop/Test.keychain
$ ls -1a
.
..
fl
mean in the filename?After a lot of investigating, I've managed to answer most of my questions:
.fl
is the filename prefix for lock files created by the AtomicFile
class in the Security framework.Test.keychain
, then the SHA-1 hash of its filename begins with 1BCE4B9A...
and so the lock file will be called .fl1BCE4B9A
.Here are the details of my investigation:
I noticed that the invisible file is not affected by the keychain's locked/unlocked status. If the invisible file has been deleted, then locking/unlocking the keychain does not recreate the invisible file.
I did some investigating using the File Activity template in Apple's Instruments tool.
These system calls are responsible for manipulating the invisible file:
Security::AtomicFile::create(unsigned short)
Security::RefPointer<Security::AtomicLockedFile>::release_internal()
Security::AtomicFile::write()
Security::RefPointer<Security::AtomicLockedFile>::release_internal()
Security::AtomicFile::performDelete()
These are the relevant files and classes (source code available from Apple Open Source for OS X 10.9.2):
AtomicFile.cpp
Security::AtomicFile
Security::AtomicLockedFile
Security::AtomicTempFile
Security::LocalFileLocker
AppleDatabase.cpp
Security::AppleDatabase
Security::DbModifier
The comments in those files provided some clues:
AtomicFile::AtomicFile()
AtomicFile::create()
LocalFileLocker::lock()
DbModifier::modifyDatabase()
AtomicFile::write()
AtomicFile::performDelete()
I found this code in the AtomicFile
constructor:
char buffer[256];
sprintf(buffer, "%08X", hash);
mLockFilePath = mDir + ".fl" + buffer;
where hash
is the first 4 bytes of the SHA-1 hash of the keychain's filename.
Note: using only 4 bytes (32 bits) of the hash, there's a reasonable chance of a hash collision, which is mentioned in the comment in LocalFileLocker::lock()
.
The flock()
system call is used to manipulate the lock on the lock file.
Here's the call tree when the keychain's database is being locked for writing:
DbModifier::createDatabase() or ::modifyDatabase() or ::deleteDatabase()
AtomicFile::create() or ::write() or ::performDelete()
AtomicLockedFile::lock()
LocalFileLocker::lock()
flock(mLockFile, LOCK_EX) // exclusive lock
and when it's being unlocked after writing:
DbModifier::commit()
AtomicTempFile::commit()
RefPointer<AtomicLockedFile>::setPointer(AtomicLockedFile*)
RefPointer<AtomicLockedFile>::release_internal()
AtomicLockedFile::~AtomicLockedFile() // destructor
AtomicLockedFile::unlock()
LocalFileLocker::unlock()
flock(mLockFile, LOCK_UN) // unlock
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With