与OS X钥匙串关联的不可见文件

Tac*_*tex 10 macos locking keychain security-framework flock

似乎钥匙串文件(带扩展名.keychain)通常会有一个与之关联的不可见文件,位于同一目录中.

这个不可见的文件总是具有以下属性:

  • 它是空的(零字节).
  • 其权限是0444(对所有用户都是只读的).
  • 它的名称.fl后跟8个十六进制字符.例如:

    .fl043D1EDD
    .fl947E1BDB
    .fl9FAF0136
    .fl12663120
    .fl8E6EFC6C
    .flCF600F4B
    .fl1D8ECE61
    .fl0D1D1BA9
    .fl79E88CD1
    .fl62323D2F
    .fl75262C83
    .fl652F188E
    
    Run Code Online (Sandbox Code Playgroud)

可以删除不可见的文件,但是当下次修改钥匙串的内容时,将使用相同的名称重新创建不可见的文件.

以下是使用Keychain Access实用程序演示的一些步骤:

  1. 通过选择" 文件">"新建钥匙串"并选择要在其中创建的钥匙串来创建新的钥匙串.将在与新钥匙串相同的目录中创建不可见文件.
  2. 删除不可见文件(使用Finder或终端).
  3. 修改钥匙串的内容.例如,通过选择" 文件">"新安全注释项",向钥匙串添加安全注释.将使用相同的名称重新创建不可见文件.
  4. 通过选择" 文件">"删除钥匙串">"删除引用和文件"来删除钥匙串.隐藏文件也将被删除.

我已经证实这发生在OS X 10.810.9中.


更新

使用security终端中的Apple 工具操作钥匙串时会创建相同的不可见文件:

  1. 创建一个新的钥匙串.还会创建一个不可见的文件.

    $ cd ~/Desktop/
    $ ls -1a
    .
    ..
    $ /usr/bin/security create-keychain ~/Desktop/Test.keychain
    $ ls -1a
    .
    ..
    .fl1BCE4B9A
    Test.keychain
    
    Run Code Online (Sandbox Code Playgroud)
  2. 删除不可见的文件.

    $ rm .fl1BCE4B9A
    $ ls -1a
    .
    ..
    Test.keychain
    
    Run Code Online (Sandbox Code Playgroud)
  3. 修改钥匙串的内容(例如:通过添加互联网密码).使用相同的名称重新创建不可见文件.

    $ /usr/bin/security add-internet-password -a account -s google.com -w password ~/Desktop/Test.keychain
    $ ls -1a
    .
    ..
    .fl1BCE4B9A
    Test.keychain
    
    Run Code Online (Sandbox Code Playgroud)
  4. 删除钥匙串.隐藏文件也会被删除.

    $ /usr/bin/security delete-keychain ~/Desktop/Test.keychain
    $ ls -1a
    .
    ..
    
    Run Code Online (Sandbox Code Playgroud)

问题

  1. 为什么要创建这些不可见的文件?他们的目的是什么?
  2. fl文件名中的含义是什么?
  3. 文件名中的8个十六进制字符是什么?识别钥匙串的某种唯一ID或哈希?
  4. 有没有办法防止在创建或修改钥匙串时创建这些文件?

Tac*_*tex 8

经过大量的调查,我设法回答了我的大部分问题:

  1. 隐形文件实现了钥匙串数据库的写锁定.
  2. .flAtomicFile安全框架中的类创建的锁文件的文件名前缀.
  3. 文件名中的8个十六进制字符是钥匙串文件名的SHA-1哈希的开头.例如,如果调用了keychain文件Test.keychain,则其文件名的SHA-1哈希开头,1BCE4B9A...因此将调用锁定文件.fl1BCE4B9A.
  4. 我没有发现一种方法来防止在创建或修改钥匙串时创建锁文件.我认为这可能是不可能的,但如果有人能找到办法,我会非常感兴趣.

以下是我调查的详细信息:

钥匙串的锁定/解锁状态

我注意到,看不见的文件不会受到钥匙扣的锁定/解锁状态.如果看不到文件已被删除,然后锁定/解锁钥匙串并没有重新无形的文件.

系统调用

我使用Apple的Instruments工具中的File Activity模板进行了一些调查.

这些系统调用负责操作不可见文件:

  • 创建新钥匙串时创建不可见文件:
    • Security::AtomicFile::create(unsigned short)
    • Security::RefPointer<Security::AtomicLockedFile>::release_internal()
    • 调用树的相关部分
  • 修改钥匙串的内容时重新创建不可见文件:
    • Security::AtomicFile::write()
    • Security::RefPointer<Security::AtomicLockedFile>::release_internal()
    • 调用树的相关部分
  • 删除钥匙串时删除不可见文件:

C++文件

这些是相关的文件和类(源代码可从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

源代码中的评论

这些文件中的评论提供了一些线索:

  • AtomicFile::AtomicFile()
    • "计算此文件的锁定文件的名称"
  • AtomicFile::create()
    • "锁定文件以进行写入并返回新创建的AtomicTempFile."
    • "现在我们已经创建了锁,新的db文件创建了一个临时文件对象."
  • LocalFileLocker::lock()
    • "如果锁文件不存在,请创建它"
    • "尝试获得对文件的独占访问权限"
    • "检查并查看我们有权访问的文件是否仍然存在.如果没有,另一个文件由于哈希冲突而共享我们的文件锁,并且已经把我们锁定了 - 或者用户将锁文件自行吹走了."
  • DbModifier::modifyDatabase()
    • "现在我们持有写锁"
  • AtomicFile::write()
    • "锁定数据库文件以进行写入并返回新创建的AtomicTempFile."
  • AtomicFile::performDelete()
    • "获取写锁并删除文件."
    • "取消链接我们的锁定文件"

生成锁定文件的名称

我在AtomicFile构造函数中找到了这段代码:

char buffer[256];
sprintf(buffer, "%08X", hash);
mLockFilePath = mDir + ".fl" + buffer;
Run Code Online (Sandbox Code Playgroud)

其中hash是keychain文件名的SHA-1哈希的前4个字节.

注意:只使用散列的4个字节(32位),有一个合理的机会发生哈希冲突,这在评论中提到LocalFileLocker::lock().

锁的操作

flock()系统调用用于操作上的锁文件锁.

当钥匙串的数据库被锁定以进行写入时,这是调用树:

DbModifier::createDatabase() or ::modifyDatabase() or ::deleteDatabase()
  AtomicFile::create() or ::write() or ::performDelete()
    AtomicLockedFile::lock()
      LocalFileLocker::lock()
        flock(mLockFile, LOCK_EX)  // exclusive lock
Run Code Online (Sandbox Code Playgroud)

当它写完后解锁时:

DbModifier::commit()
  AtomicTempFile::commit()
    RefPointer<AtomicLockedFile>::setPointer(AtomicLockedFile*)
      RefPointer<AtomicLockedFile>::release_internal()
        AtomicLockedFile::~AtomicLockedFile()  // destructor
          AtomicLockedFile::unlock()
            LocalFileLocker::unlock()
              flock(mLockFile, LOCK_UN)  // unlock
Run Code Online (Sandbox Code Playgroud)