Skip to content

Commit

Permalink
Hold temporary keychain refs for certificates imported with X509Certi…
Browse files Browse the repository at this point in the history
…ficate2Collection.Import

This corrects the reference counting for temporary keychains when using X509Certificate2Collection.Import,
both releasing a ref that is no longer needed, and adding a ref to the new/target keychain to prevent that
keychain from being deleted too soon.
  • Loading branch information
filipnavara authored Feb 16, 2023
1 parent d4c46d5 commit 28f958d
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ internal static SafeSecIdentityHandle X509CopyWithPrivateKey(
out identityHandle,
out osStatus);

SafeTemporaryKeychainHandle.TrackItem(identityHandle);

if (result == 1)
{
Debug.Assert(!identityHandle.IsInvalid);
Expand All @@ -288,13 +290,25 @@ internal static SafeSecIdentityHandle X509CopyWithPrivateKey(
{
SafeSecIdentityHandle identityHandle;
int osStatus;
int result;

int result = AppleCryptoNative_X509MoveToKeychain(
cert,
targetKeychain,
privateKey ?? SafeSecKeyRefHandle.InvalidHandle,
out identityHandle,
out osStatus);
// Ensure the keychain is not disposed, if there is any associated one
using (SafeKeychainHandle keychain = Interop.AppleCrypto.SecKeychainItemCopyKeychain(cert))
{
// AppleCryptoNative_X509MoveToKeychain can change the keychain of the input
// certificate, so we need to reflect that change.
SafeTemporaryKeychainHandle.UntrackItem(cert.DangerousGetHandle());

result = AppleCryptoNative_X509MoveToKeychain(
cert,
targetKeychain,
privateKey ?? SafeSecKeyRefHandle.InvalidHandle,
out identityHandle,
out osStatus);

SafeTemporaryKeychainHandle.TrackItem(cert);
SafeTemporaryKeychainHandle.TrackItem(identityHandle);
}

if (result == 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,5 @@ public static ICertificatePal FromBlob(

return result ?? FromDerBlob(rawData, GetDerCertContentType(rawData), password, keyStorageFlags);
}

// No temporary keychain on iOS
partial void DisposeTempKeychain();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ namespace System.Security.Cryptography.X509Certificates
{
internal sealed partial class AppleCertificatePal : ICertificatePal
{
private SafeKeychainHandle? _tempKeychain;

public static ICertificatePal FromBlob(
ReadOnlySpan<byte> rawData,
SafePasswordHandle password,
Expand Down Expand Up @@ -53,20 +51,7 @@ public static ICertificatePal FromBlob(

using (keychain)
{
AppleCertificatePal ret = ImportPkcs12(rawData, password, exportable, keychain);
if (!persist)
{
// If we used temporary keychain we need to prevent deletion.
// on 10.15+ if keychain is unlinked, certain certificate operations may fail.
bool success = false;
keychain.DangerousAddRef(ref success);
if (success)
{
ret._tempKeychain = keychain;
}
}

return ret;
return ImportPkcs12(rawData, password, exportable, keychain);
}
}

Expand All @@ -92,11 +77,6 @@ public static ICertificatePal FromBlob(
throw new CryptographicException();
}

public void DisposeTempKeychain()
{
Interlocked.Exchange(ref _tempKeychain, null)?.Dispose();
}

internal unsafe byte[] ExportPkcs8(ReadOnlySpan<char> password)
{
Debug.Assert(_identityHandle != null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,6 @@ public void Dispose()

_certHandle = null!;
_identityHandle = null;

DisposeTempKeychain();
}

internal SafeSecCertificateHandle CertificateHandle => _certHandle;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public void MoveTo(X509Certificate2Collection collection)

using (safeSecKeyRefHandle)
{
ICertificatePal newPal;
AppleCertificatePal newPal;

// SecItemImport doesn't seem to respect non-exportable import for PKCS#8,
// only PKCS#12.
Expand All @@ -119,6 +119,11 @@ public void MoveTo(X509Certificate2Collection collection)

X509Certificate2 cert = new X509Certificate2(newPal);
collection.Add(cert);

if (newPal != pal)
{
pal.Dispose();
}
}
}
}
Expand Down

0 comments on commit 28f958d

Please sign in to comment.