Skip to content

Commit

Permalink
win_acl/Certificate: Minor refactoring, correctly free NCrypt handles…
Browse files Browse the repository at this point in the history
… with NCryptFreeObject
  • Loading branch information
jpitlor committed Sep 7, 2023
1 parent 07a2d03 commit 0f8b790
Showing 1 changed file with 62 additions and 42 deletions.
104 changes: 62 additions & 42 deletions plugins/module_utils/_CertACLHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ namespace ansible_collections.ansible.windows.plugins.module_utils._CertACLHelpe
{
internal class SafeCryptHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private enum SecurityStatus : int
{
ERROR_SUCCESS = 0
}

public bool ShouldFree { get; set; }
public bool NCrypt { get; set; }

public SafeCryptHandle()
: base(true)
{
Expand All @@ -26,9 +34,19 @@ public SafeCryptHandle()
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CryptReleaseContext(IntPtr safeProvHandle, uint dwFlags);

[DllImport("ncrypt.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int NCryptFreeObject(IntPtr safeProvHandle);

protected override bool ReleaseHandle()
{
return CryptReleaseContext(this.handle, 0);
if (!ShouldFree)
{
return true;
}

return NCrypt
? NCryptFreeObject(this.handle) == (int)SecurityStatus.ERROR_SUCCESS
: CryptReleaseContext(this.handle, 0);
}
}

Expand Down Expand Up @@ -64,6 +82,11 @@ protected override bool ReleaseHandle()
}
}

internal class KeyStorageProperty
{
public const string NCRYPT_SECURITY_DESCR_PROPERTY = "Security Descr";
}

[Flags]
public enum CertAccessRights : int
{
Expand All @@ -77,6 +100,7 @@ public class CertAclHelper
private enum SecurityInformationFlags : uint
{
DACL_SECURITY_INFORMATION = 0x00000004,
NCRYPT_SILENT_FLAG = 0x00000040,
}

[Flags]
Expand All @@ -101,6 +125,11 @@ private enum KeySpec : uint
CERT_NCRYPT_KEY_SPEC = 0xFFFFFFFF
}

private enum CryptProvParam : uint
{
PP_KEYSET_SEC_DESCR = 8
}

[DllImport("crypt32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool CryptAcquireCertificatePrivateKey(IntPtr pCert, uint dwFlags, IntPtr pvParameters, out SafeCryptHandle phCryptProvOrNCryptKey, out KeySpec pdwKeySpec, out bool pfCallerFreeProvOrNCryptKey);

Expand All @@ -110,11 +139,6 @@ private enum KeySpec : uint
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int NCryptSetProperty(SafeCryptHandle hObject, [MarshalAs(UnmanagedType.LPWStr)] string pszProperty, [MarshalAs(UnmanagedType.LPArray)] byte[] pbInput, uint cbInput, SecurityInformationFlags dwFlags);

private enum CryptProvParam : uint
{
PP_KEYSET_SEC_DESCR = 8
}

[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CryptGetProvParam(SafeCryptHandle safeProvHandle, CryptProvParam dwParam, SafeSecurityDescriptorPtr pbData, ref uint dwDataLen, SecurityInformationFlags dwFlags);
Expand All @@ -124,85 +148,81 @@ private enum CryptProvParam : uint
private static extern bool CryptSetProvParam(SafeCryptHandle safeProvHandle, CryptProvParam dwParam, [MarshalAs(UnmanagedType.LPArray)] byte[] pbData, SecurityInformationFlags dwFlags);

private SafeCryptHandle handle;
private bool ncrypt = false;

public CertAclHelper(X509Certificate2 certificate)
{
SafeCryptHandle certPkeyHandle;
KeySpec keySpec;
bool ownHandle;
if (CryptAcquireCertificatePrivateKey(
bool shouldFreeKey;
if (!CryptAcquireCertificatePrivateKey(
certificate.Handle,
(uint)CryptAcquireKeyFlags.CRYPT_ACQUIRE_SILENT_FLAG | (uint)CryptAcquireKeyFlagControl.CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG,
IntPtr.Zero,
out certPkeyHandle,
out handle,
out keySpec,
out ownHandle))
{
if (!ownHandle)
{
throw new NotSupportedException("Could not take ownership of certificate private key handle");
}

if (keySpec == KeySpec.CERT_NCRYPT_KEY_SPEC)
{
ncrypt = true;
}

handle = certPkeyHandle;
}
else
out shouldFreeKey))
{
throw new Win32Exception();
}

handle.ShouldFree = shouldFreeKey;
handle.NCrypt = keySpec == KeySpec.CERT_NCRYPT_KEY_SPEC;
}

public FileSecurity Acl
{
get
{
SafeSecurityDescriptorPtr securityDescriptorBuffer;
uint securityDescriptorSize = 0;
if (ncrypt)
var securityDescriptorSize = 0U;
if (handle.NCrypt)
{
// We first have to find out how large of a buffer to reserve, so the docs say that
// we should pass NULL for the buffer address, then the penultimate parameter will
// get assigned the required size.
var securityDescriptorResult = NCryptGetProperty(
handle,
"Security Descr",
new SafeSecurityDescriptorPtr(),
KeyStorageProperty.NCRYPT_SECURITY_DESCR_PROPERTY,
null,
0,
ref securityDescriptorSize,
SecurityInformationFlags.DACL_SECURITY_INFORMATION);
SecurityInformationFlags.DACL_SECURITY_INFORMATION | SecurityInformationFlags.NCRYPT_SILENT_FLAG);
if (securityDescriptorResult != 0)
{
throw new Win32Exception(securityDescriptorResult);
}
securityDescriptorBuffer = new SafeSecurityDescriptorPtr(securityDescriptorSize);

// Now that we know the required size, we can allocate a buffer and actually ask NCrypt
// to copy the security description into it.
securityDescriptorBuffer = new SafeSecurityDescriptorPtr(securityDescriptorSize);
securityDescriptorResult = NCryptGetProperty(
handle,
"Security Descr",
KeyStorageProperty.NCRYPT_SECURITY_DESCR_PROPERTY,
securityDescriptorBuffer,
securityDescriptorSize,
ref securityDescriptorSize,
SecurityInformationFlags.DACL_SECURITY_INFORMATION);
SecurityInformationFlags.DACL_SECURITY_INFORMATION | SecurityInformationFlags.NCRYPT_SILENT_FLAG);
if (securityDescriptorResult != 0)
{
throw new Win32Exception(securityDescriptorResult);
}

}
else
{
// We first have to find out how large of a buffer to reserve, so the docs say that
// we should pass NULL for the buffer address, then the penultimate parameter will
// get assigned the required size.
if (!CryptGetProvParam(
handle,
CryptProvParam.PP_KEYSET_SEC_DESCR,
new SafeSecurityDescriptorPtr(),
null,
ref securityDescriptorSize,
SecurityInformationFlags.DACL_SECURITY_INFORMATION))
{
throw new Win32Exception();
}

// Now that we know the required size, we can allocate a buffer and actually ask NCrypt
// to copy the security description into it.
securityDescriptorBuffer = new SafeSecurityDescriptorPtr(securityDescriptorSize);
if (!CryptGetProvParam(
handle,
Expand All @@ -224,15 +244,15 @@ public FileSecurity Acl
}
set
{
if (ncrypt)
if (handle.NCrypt)
{
var sd = value.GetSecurityDescriptorBinaryForm();
var securityDescriptor = value.GetSecurityDescriptorBinaryForm();
var setPropertyResult = NCryptSetProperty(
handle,
"Security Descr",
sd,
(uint)sd.Length,
SecurityInformationFlags.DACL_SECURITY_INFORMATION);
KeyStorageProperty.NCRYPT_SECURITY_DESCR_PROPERTY,
securityDescriptor,
(uint)securityDescriptor.Length,
SecurityInformationFlags.DACL_SECURITY_INFORMATION | SecurityInformationFlags.NCRYPT_SILENT_FLAG);
if (setPropertyResult != 0)
{
throw new Win32Exception(setPropertyResult);
Expand Down

0 comments on commit 0f8b790

Please sign in to comment.