Skip to content

Commit c0e99f7

Browse files
codexCyberShadow
authored andcommitted
ae.net.ssl.schannel: Clean up failed credential setup
Allow the identity-import validation probe to acquire accessible CNG-backed certificate keys with CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG. This is an acquisition flag only; server PFX imports retain their machine-keyset scope without adding a CNG-preference import flag. When key validation fails after selecting an imported certificate with a private key, delete its provider-specific persisted container before freeing the certificate context. This preserves the existing persist-then-delete lifecycle even when setup fails before ownership transfers to certContext. Synchronous adapter initialization now releases acquired SSPI credential and context handles if it throws while still allowing the exception to escape createAdapter. Release SChannel output buffers with scope-exit cleanup so an exception from next.send cannot leak a handshake or close-notify buffer. The Linux ae_unittest suite passed. The full Windows SChannel suite passed through a key-authenticated SSH logon, including the PFX echo and curl tests. A disposable forced-CNG run also asserted CERT_NCRYPT_KEY_SPEC and passed all SChannel tests.
1 parent 7477a82 commit c0e99f7

1 file changed

Lines changed: 23 additions & 12 deletions

File tree

net/ssl/schannel.d

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ extern(Windows) nothrow @nogc
143143
// (set by PFXImportCertStore without PKCS12_NO_PERSIST_KEY, readable by LSASS).
144144
enum DWORD CERT_KEY_PROV_INFO_PROP_ID = 2;
145145
enum DWORD CERT_NCRYPT_KEY_SPEC = 0xFFFFFFFF;
146+
enum DWORD CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG = 0x00010000;
146147

147148
// Key provider info structure returned by CertGetCertificateContextProperty for
148149
// CERT_KEY_PROV_INFO_PROP_ID. dwProvType == 0 ⇒ CNG key; != 0 ⇒ legacy CSP.
@@ -330,13 +331,19 @@ class SChannelContext : SSLContext
330331

331332
if (!chosen)
332333
throw new Exception("setIdentityFromPKCS12: PFX contains no certificates");
333-
scope(failure) CertFreeCertificateContext(chosen);
334+
scope(failure)
335+
{
336+
if (chosenHasKey)
337+
deleteKeyContainerSilent(chosen);
338+
CertFreeCertificateContext(chosen);
339+
}
334340

335341
ULONG_PTR hKey;
336342
DWORD keySpec;
337343
BOOL callerFree;
338344
sspiEnforce(CryptAcquireCertificatePrivateKey(
339-
chosen, 0, null, &hKey, &keySpec, &callerFree) != 0,
345+
chosen, CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, null,
346+
&hKey, &keySpec, &callerFree) != 0,
340347
"CryptAcquireCertificatePrivateKey", cast(SECURITY_STATUS) GetLastError());
341348
if (callerFree)
342349
{
@@ -538,7 +545,10 @@ class SChannelAdapter : SSLAdapter
538545
this.context = context;
539546
super(next);
540547
if (next.state == ConnectionState.connected)
548+
{
549+
scope(failure) cleanupContext();
541550
initialize();
551+
}
542552
}
543553

544554
override void onConnect()
@@ -1062,11 +1072,11 @@ protected:
10621072
serverReqFlags(), 0, &hCtxt, &outDesc, &attr, &ts);
10631073
}
10641074

1075+
scope(exit)
1076+
if (outBuf.pvBuffer)
1077+
FreeContextBuffer(outBuf.pvBuffer);
10651078
if (outBuf.cbBuffer && outBuf.pvBuffer)
1066-
{
10671079
next.send(Data((cast(ubyte*) outBuf.pvBuffer)[0 .. outBuf.cbBuffer]));
1068-
FreeContextBuffer(outBuf.pvBuffer);
1069-
}
10701080
}
10711081

10721082
private void cleanupContext()
@@ -1095,16 +1105,17 @@ protected:
10951105

10961106
private void sendAndFreeOutputBuffers(SecBuffer[] bufs)
10971107
{
1108+
scope(exit)
1109+
foreach (ref b; bufs)
1110+
if (b.pvBuffer)
1111+
{
1112+
FreeContextBuffer(b.pvBuffer);
1113+
b.pvBuffer = null;
1114+
b.cbBuffer = 0;
1115+
}
10981116
foreach (ref b; bufs)
1099-
{
11001117
if (b.cbBuffer && b.pvBuffer)
1101-
{
11021118
next.send(Data((cast(ubyte*) b.pvBuffer)[0 .. b.cbBuffer]));
1103-
FreeContextBuffer(b.pvBuffer);
1104-
b.pvBuffer = null;
1105-
b.cbBuffer = 0;
1106-
}
1107-
}
11081119
}
11091120
}
11101121

0 commit comments

Comments
 (0)