runtime: macOS: Cannot add to X509Store [regression from 3.1]

I cannot seem to write to an X509Store in macOS. Code to reproduce:

using System.Security.Cryptography.X509Certificates;

namespace scratch {
    class Program {
        static void Main(string[] args) {
            using X509Certificate2 cert = new X509Certificate2("cert.pfx", "test", X509KeyStorageFlags.Exportable);
            using X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
            store.Open(OpenFlags.ReadWrite);
            store.Add(cert);
        }
    }
}

This seems to always fail on store.Add(cert). Exception:

Unhandled exception. Interop+AppleCrypto+AppleCommonCryptoCryptographicException: UNIX[Undefined error: 0]
   at Interop.AppleCrypto.X509StoreAddCertificate(SafeKeychainItemHandle certOrIdentity, SafeKeychainHandle keychain)
   at Internal.Cryptography.Pal.StorePal.AppleKeychainStore.Add(ICertificatePal cert)
   at System.Security.Cryptography.X509Certificates.X509Store.Add(X509Certificate2 certificate)
   at scratch.Program.Main(String[] args) in /Users/kjones/Projects/scratch/Program.cs:line 9

The unit tests for this appear to be in X509StoreMutableTests_OSX. When I attempt to run them, all of them are skipped because the “can I write to the store?” conditional fails:

Writing precondition failed with kPOSIXErrorBase, skipping tests.

This is not an SSH session, or a VM, or anything out of the ordinary, it’s a login zsh shell.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 45 (45 by maintainers)

Most upvoted comments

As a test, if that code is changed to:

SafeKeychainHandle keychain = persist
    ? Interop.AppleCrypto.SecKeychainCopyDefault()
    : Interop.AppleCrypto.CreateTemporaryKeychain();

System.GC.SuppressFinalize(keychain);
return ImportPkcs12(rawData, password, exportable, keychain);

Then the problem goes away, too.

@wfurt is going to take a look into this this week.

Thanks a ton. Sorry I’ve just been underwater with other things lately.

Since this fails with debug codegen it’s also unlikely to be a jit GC tracking issue.

GC info leading up to the call to ImportPkcs12 looks reasonable:

000122 E829FBFFFF           call     AppleCrypto:CreateTemporaryKeychain():System.Security.Cryptography.Apple.SafeTemporaryKeychainHandle
000127 48894590             mov      gword ptr [rbp-70H], rax
00012B EB09                 jmp      SHORT G_M61524_IG08
						;; bbWeight=1    PerfScore 13.50
G_M61524_IG07:
00012D E8CEFAFFFF           call     AppleCrypto:SecKeychainCopyDefault():System.Security.Cryptography.Apple.SafeKeychainHandle
000132 48894590             mov      gword ptr [rbp-70H], rax
						;; bbWeight=1    PerfScore 2.00
G_M61524_IG08:
000136 488B7D90             mov      rdi, gword ptr [rbp-70H]
00013A 48897DC0             mov      gword ptr [rbp-40H], rdi
00013E 488B7DC0             mov      rdi, gword ptr [rbp-40H]
000142 48897DB8             mov      gword ptr [rbp-48H], rdi
						;; bbWeight=1    PerfScore 4.00
G_M61524_IG09:
000146 488B7DF0             mov      rdi, bword ptr [rbp-10H]
00014A 488B75F8             mov      rsi, qword ptr [rbp-08H]
00014E 488B55E8             mov      rdx, gword ptr [rbp-18H]
000152 8B4DCC               mov      ecx, dword ptr [rbp-34H]
000155 4C8B45C0             mov      r8, gword ptr [rbp-40H]
000159 E85AC0FFFF           call     Internal.Cryptography.Pal.AppleCertificatePal:ImportPkcs12(System.ReadOnlySpan`1[Byte],Microsoft.Win32.SafeHandles.SafePasswordHandle,bool,System.Security.Cryptography.Apple.SafeKeychainHandle):Internal.Cryptography.ICertificatePal
00015E 488945B0             mov      gword ptr [rbp-50H], rax

;; stack slots rbp-70, rbp-40, rbp-48 are untracked

Register slot id for reg rax = 17.
Register slot id for reg rsi = 18.
Register slot id for reg rdi = 19.
Register slot id for reg rdx = 20.
Register slot id for reg r8 = 21.
Register slot id for reg rcx = 22.

Set state of slot 17 at instr offset 0x127 to Live.
Set state of slot 17 at instr offset 0x12d to Dead.
Set state of slot 17 at instr offset 0x132 to Live.
Set state of slot 17 at instr offset 0x136 to Dead.
Set state of slot 19 at instr offset 0x13a to Live.
Set state of slot 19 at instr offset 0x146 to Dead.
Set state of slot 16 at instr offset 0x14a to Live.
Set state of slot 20 at instr offset 0x152 to Live.
Set state of slot 21 at instr offset 0x159 to Live.
Set state of slot 17 at instr offset 0x15e to Live.

, I am surprised that this is failing out of scope.

Note that this is all new code in 5.0. ImportPkcs12 call at https://github.com/dotnet/runtime/issues/39603#issuecomment-681047300 did not exist in 3.1. I think it is most likely a bug in the new code, it is unlikely to be a bug in the core runtime.

I don’t think this is a GC issue - I threw in the SuppressFinalize so the GC wouldn’t clean up the handle (which deletes the temp keychain) and force it to leak. That snippet was solely there to at least confirm that is the source of some object-lifetime issue.

I have repro using latest daily 5.0 build:

bash-3.2$ ~/dotnet/dotnet run
Unhandled exception. Interop+AppleCrypto+AppleCommonCryptoCryptographicException: UNIX[Undefined error: 0]
   at Interop.AppleCrypto.X509StoreAddCertificate(SafeKeychainItemHandle certOrIdentity, SafeKeychainHandle keychain)
   at Internal.Cryptography.Pal.StorePal.AppleKeychainStore.Add(ICertificatePal cert)
   at System.Security.Cryptography.X509Certificates.X509Store.Add(X509Certificate2 certificate)
   at scratch.Program.Main(String[] args) in /Users/furt/certs/Program.cs:line 9

I will take a look and share findings.

what OSX API is failing

SecKeychainItemCreateCopy

https://github.com/dotnet/runtime/blob/ec59f65100d741539b00e308294cc53f04fc326b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain.c#L354

and why

The resulting OS Status is kPOSIXErrorBase. That isn’t a very descriptive error code.

Native stack:

debug	15:13:29.064492-0400	0x00007fff3614fa2d Security::CommonError::LogBacktrace() + 181
debug	15:13:29.064522-0400	0x00007fff3614fd51 Security::UnixError::UnixError(int, bool) + 277
debug	15:13:29.064546-0400	0x00007fff3614fdae Security::UnixError::throwMe(int) + 36
debug	15:13:29.064568-0400	0x00007fff35eb7394 Security::AtomicBufferedFile::open() + 696
debug	15:13:29.064593-0400	0x00007fff35eb5e21 Security::DbModifier::getDbVersion(bool) + 331
debug	15:13:29.064619-0400	0x00007fff35eb5ca5 Security::AppleDatabase::dbOpen(Security::DbContext&) + 57
debug	15:13:29.064645-0400	0x00007fff35eb55bf Security::Database::_dbOpen(Security::DatabaseSession&, unsigned int, Security::AccessCredentials const*, void const*) + 113
debug	15:13:29.064670-0400	0x00007fff35eb4932 Security::DatabaseManager::dbOpen(Security::DatabaseSession&, Security::DbName const&, unsigned int, Security::AccessCredentials const*, void const*) + 64
debug	15:13:29.064693-0400	0x00007fff35eb445d Security::DatabaseSession::DbOpen(char const*, cssm_net_address const*, unsigned int, Security::AccessCredentials const*, void const*, long&) + 189
debug	15:13:29.064717-0400	0x00007fff35ec61b3 cssm_DbOpen(long, char const*, cssm_net_address const*, unsigned int, cssm_access_credentials const*, void const*, long*) + 118
debug	15:13:29.064764-0400	0x00007fff35ec5f7c CSSM_DL_DbOpen + 152
debug	15:13:29.064862-0400	0x00007fff35ec5298 Security::CssmClient::DbImpl::open() + 248
debug	15:13:29.064965-0400	0x00007fff35ffb0a9 SSDatabaseImpl::load(Security::DLDbIdentifier const&) + 53
debug	15:13:29.065066-0400	0x00007fff35ec636d SSDLSession::DbOpen(char const*, cssm_net_address const*, unsigned int, Security::AccessCredentials const*, void const*, long&) + 273
debug	15:13:29.065133-0400	0x00007fff35ec61b3 cssm_DbOpen(long, char const*, cssm_net_address const*, unsigned int, cssm_access_credentials const*, void const*, long*) + 118
debug	15:13:29.065244-0400	0x00007fff35ec5f7c CSSM_DL_DbOpen + 152
debug	15:13:29.065339-0400	0x00007fff35ec5298 Security::CssmClient::DbImpl::open() + 248
debug	15:13:29.065439-0400	0x00007fff35ec5162 Security::CssmClient::DbImpl::activate() + 80
debug	15:13:29.065521-0400	0x00007fff35ff3e5c Security::CssmClient::DbImpl::handle() + 56
debug	15:13:29.065588-0400	0x00007fff35ed2a17 Security::CssmClient::DbUniqueRecordImpl::get(Security::CssmClient::DbAttributes*, Security::CssmDataContainer*) + 155
debug	15:13:29.065705-0400	0x00007fff360b5263 Security::KeychainCore::KeyItem::copyTo(Security::KeychainCore::Keychain const&, Security::KeychainCore::Access*) + 2611
debug	15:13:29.065787-0400	0x00007fff360f7096 SecKeychainItemCreateCopy + 257

It looks like it is having trouble opening the keychain in the temporary directory.

@jeffhandley

Are you planning to investigate this further?

Yeah, but I haven’t made a ton of progress. I think a good first step would be to see if someone other than me can repro it, and do the unit tests actually run (not just skipped). Maybe it just doesn’t like me.

I can still repro it on Big Sur beta 4, I’m installing 5 right now.