From 8d453da283162a32f0296b49068c247b2d41b315 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Wed, 20 Apr 2022 14:26:57 -0700 Subject: [PATCH] Use new Windows signature APIs from `Microsoft.Security.Extensions` package (#17159) Co-authored-by: Anam Navied Co-authored-by: Travis Plunk --- .../security/CertificateProvider.cs | 186 ++++++++-------- .../System.Management.Automation.csproj | 2 + .../security/Authenticode.cs | 201 ++++++++---------- .../security/MshSignature.cs | 2 +- .../security/SecuritySupport.cs | 2 +- .../security/nativeMethods.cs | 56 ----- .../engine/Security/FileSignature.Tests.ps1 | 22 ++ tools/cgmanifest.json | 10 + 8 files changed, 212 insertions(+), 269 deletions(-) create mode 100644 test/powershell/engine/Security/FileSignature.Tests.ps1 diff --git a/src/Microsoft.PowerShell.Security/security/CertificateProvider.cs b/src/Microsoft.PowerShell.Security/security/CertificateProvider.cs index 7c7627fd52..d6154a8885 100644 --- a/src/Microsoft.PowerShell.Security/security/CertificateProvider.cs +++ b/src/Microsoft.PowerShell.Security/security/CertificateProvider.cs @@ -26,7 +26,7 @@ using System.Xml.XPath; using Dbg = System.Management.Automation; using DWORD = System.UInt32; using Runspaces = System.Management.Automation.Runspaces; -using Security = System.Management.Automation.Security; +using SMASecurity = System.Management.Automation.Security; namespace Microsoft.PowerShell.Commands { @@ -275,7 +275,7 @@ namespace Microsoft.PowerShell.Commands if (handle != IntPtr.Zero) { - fResult = Security.NativeMethods.CertCloseStore(handle, 0); + fResult = SMASecurity.NativeMethods.CertCloseStore(handle, 0); handle = IntPtr.Zero; } @@ -318,25 +318,25 @@ namespace Microsoft.PowerShell.Commands _valid = false; _open = false; - Security.NativeMethods.CertOpenStoreFlags StoreFlags = - Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_SHARE_STORE_FLAG | - Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_SHARE_CONTEXT_FLAG | - Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_OPEN_EXISTING_FLAG | - Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_MAXIMUM_ALLOWED_FLAG; + SMASecurity.NativeMethods.CertOpenStoreFlags StoreFlags = + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_SHARE_STORE_FLAG | + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_SHARE_CONTEXT_FLAG | + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_OPEN_EXISTING_FLAG | + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_MAXIMUM_ALLOWED_FLAG; if (includeArchivedCerts) { - StoreFlags |= Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_ENUM_ARCHIVED_FLAG; + StoreFlags |= SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_ENUM_ARCHIVED_FLAG; } switch (_storeLocation.Location) { case StoreLocation.LocalMachine: - StoreFlags |= Security.NativeMethods.CertOpenStoreFlags.CERT_SYSTEM_STORE_LOCAL_MACHINE; + StoreFlags |= SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_SYSTEM_STORE_LOCAL_MACHINE; break; case StoreLocation.CurrentUser: - StoreFlags |= Security.NativeMethods.CertOpenStoreFlags.CERT_SYSTEM_STORE_CURRENT_USER; + StoreFlags |= SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_SYSTEM_STORE_CURRENT_USER; break; default: @@ -344,9 +344,9 @@ namespace Microsoft.PowerShell.Commands break; } - IntPtr hCertStore = Security.NativeMethods.CertOpenStore( - Security.NativeMethods.CertOpenStoreProvider.CERT_STORE_PROV_SYSTEM, - Security.NativeMethods.CertOpenStoreEncodingType.X509_ASN_ENCODING, + IntPtr hCertStore = SMASecurity.NativeMethods.CertOpenStore( + SMASecurity.NativeMethods.CertOpenStoreProvider.CERT_STORE_PROV_SYSTEM, + SMASecurity.NativeMethods.CertOpenStoreEncodingType.X509_ASN_ENCODING, IntPtr.Zero, // hCryptProv StoreFlags, _storeName); @@ -364,10 +364,10 @@ namespace Microsoft.PowerShell.Commands "UserDS", StringComparison.OrdinalIgnoreCase)) { - if (!Security.NativeMethods.CertControlStore( + if (!SMASecurity.NativeMethods.CertControlStore( _storeHandle.Handle, 0, - Security.NativeMethods.CertControlStoreType.CERT_STORE_CTRL_AUTO_RESYNC, + SMASecurity.NativeMethods.CertControlStoreType.CERT_STORE_CTRL_AUTO_RESYNC, IntPtr.Zero)) { _storeHandle = null; @@ -391,12 +391,12 @@ namespace Microsoft.PowerShell.Commands if (!_open) { throw Marshal.GetExceptionForHR( - Security.NativeMethods.CRYPT_E_NOT_FOUND); + SMASecurity.NativeMethods.CRYPT_E_NOT_FOUND); } if (Valid) { - certContext = Security.NativeMethods.CertEnumCertificatesInStore( + certContext = SMASecurity.NativeMethods.CertEnumCertificatesInStore( _storeHandle.Handle, certContext); } @@ -415,18 +415,18 @@ namespace Microsoft.PowerShell.Commands if (!_open) { throw Marshal.GetExceptionForHR( - Security.NativeMethods.CRYPT_E_NOT_FOUND); + SMASecurity.NativeMethods.CRYPT_E_NOT_FOUND); } if (Valid) { if (DownLevelHelper.HashLookupSupported()) { - certContext = Security.NativeMethods.CertFindCertificateInStore( + certContext = SMASecurity.NativeMethods.CertFindCertificateInStore( _storeHandle.Handle, - Security.NativeMethods.CertOpenStoreEncodingType.X509_ASN_ENCODING, + SMASecurity.NativeMethods.CertOpenStoreEncodingType.X509_ASN_ENCODING, 0, // dwFindFlags - Security.NativeMethods.CertFindType.CERT_FIND_HASH_STR, + SMASecurity.NativeMethods.CertFindType.CERT_FIND_HASH_STR, Name, IntPtr.Zero); // pPrevCertContext } @@ -464,7 +464,7 @@ namespace Microsoft.PowerShell.Commands public void FreeCert(IntPtr certContext) { - Security.NativeMethods.CertFreeCertificateContext(certContext); + SMASecurity.NativeMethods.CertFreeCertificateContext(certContext); } /// @@ -974,15 +974,15 @@ namespace Microsoft.PowerShell.Commands ThrowInvalidOperation(errorId, message); } - const Security.NativeMethods.CertOpenStoreFlags StoreFlags = - Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_CREATE_NEW_FLAG | - Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_MAXIMUM_ALLOWED_FLAG | - Security.NativeMethods.CertOpenStoreFlags.CERT_SYSTEM_STORE_LOCAL_MACHINE; + const SMASecurity.NativeMethods.CertOpenStoreFlags StoreFlags = + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_CREATE_NEW_FLAG | + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_MAXIMUM_ALLOWED_FLAG | + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_SYSTEM_STORE_LOCAL_MACHINE; // Create new store - IntPtr hCertStore = Security.NativeMethods.CertOpenStore( - Security.NativeMethods.CertOpenStoreProvider.CERT_STORE_PROV_SYSTEM, - Security.NativeMethods.CertOpenStoreEncodingType.X509_ASN_ENCODING, + IntPtr hCertStore = SMASecurity.NativeMethods.CertOpenStore( + SMASecurity.NativeMethods.CertOpenStoreProvider.CERT_STORE_PROV_SYSTEM, + SMASecurity.NativeMethods.CertOpenStoreEncodingType.X509_ASN_ENCODING, IntPtr.Zero, // hCryptProv StoreFlags, pathElements[1]); @@ -993,7 +993,7 @@ namespace Microsoft.PowerShell.Commands else // free native store handle { bool fResult = false; - fResult = Security.NativeMethods.CertCloseStore(hCertStore, 0); + fResult = SMASecurity.NativeMethods.CertCloseStore(hCertStore, 0); } X509Store outStore = new(pathElements[1], StoreLocation.LocalMachine); @@ -1616,8 +1616,8 @@ namespace Microsoft.PowerShell.Commands private void DoDeleteKey(IntPtr pProvInfo) { IntPtr hProv = IntPtr.Zero; - Security.NativeMethods.CRYPT_KEY_PROV_INFO keyProvInfo = - Marshal.PtrToStructure(pProvInfo); + SMASecurity.NativeMethods.CRYPT_KEY_PROV_INFO keyProvInfo = + Marshal.PtrToStructure(pProvInfo); IntPtr hWnd = DetectUIHelper.GetOwnerWindow(Host); @@ -1625,33 +1625,33 @@ namespace Microsoft.PowerShell.Commands { if (hWnd != IntPtr.Zero) { - if (Security.NativeMethods.CryptAcquireContext( + if (SMASecurity.NativeMethods.CryptAcquireContext( ref hProv, keyProvInfo.pwszContainerName, keyProvInfo.pwszProvName, (int)keyProvInfo.dwProvType, - (uint)Security.NativeMethods.ProviderFlagsEnum.CRYPT_VERIFYCONTEXT)) + (uint)SMASecurity.NativeMethods.ProviderFlagsEnum.CRYPT_VERIFYCONTEXT)) { unsafe { void* pWnd = hWnd.ToPointer(); - Security.NativeMethods.CryptSetProvParam( + SMASecurity.NativeMethods.CryptSetProvParam( hProv, - Security.NativeMethods.ProviderParam.PP_CLIENT_HWND, + SMASecurity.NativeMethods.ProviderParam.PP_CLIENT_HWND, &pWnd, 0); - Security.NativeMethods.CryptReleaseContext(hProv, 0); + SMASecurity.NativeMethods.CryptReleaseContext(hProv, 0); } } } - if (!Security.NativeMethods.CryptAcquireContext( + if (!SMASecurity.NativeMethods.CryptAcquireContext( ref hProv, keyProvInfo.pwszContainerName, keyProvInfo.pwszProvName, (int)keyProvInfo.dwProvType, - keyProvInfo.dwFlags | (uint)Security.NativeMethods.ProviderFlagsEnum.CRYPT_DELETEKEYSET | - (hWnd == IntPtr.Zero ? (uint)Security.NativeMethods.ProviderFlagsEnum.CRYPT_SILENT : 0))) + keyProvInfo.dwFlags | (uint)SMASecurity.NativeMethods.ProviderFlagsEnum.CRYPT_DELETEKEYSET | + (hWnd == IntPtr.Zero ? (uint)SMASecurity.NativeMethods.ProviderFlagsEnum.CRYPT_SILENT : 0))) { ThrowErrorRemoting(Marshal.GetLastWin32Error()); } @@ -1664,21 +1664,21 @@ namespace Microsoft.PowerShell.Commands IntPtr hCNGProv = IntPtr.Zero; IntPtr hCNGKey = IntPtr.Zero; - if ((keyProvInfo.dwFlags & (uint)Security.NativeMethods.ProviderFlagsEnum.CRYPT_MACHINE_KEYSET) != 0) + if ((keyProvInfo.dwFlags & (uint)SMASecurity.NativeMethods.ProviderFlagsEnum.CRYPT_MACHINE_KEYSET) != 0) { - cngKeyFlag = (uint)Security.NativeMethods.NCryptDeletKeyFlag.NCRYPT_MACHINE_KEY_FLAG; + cngKeyFlag = (uint)SMASecurity.NativeMethods.NCryptDeletKeyFlag.NCRYPT_MACHINE_KEY_FLAG; } if (hWnd == IntPtr.Zero || - (keyProvInfo.dwFlags & (uint)Security.NativeMethods.ProviderFlagsEnum.CRYPT_SILENT) != 0) + (keyProvInfo.dwFlags & (uint)SMASecurity.NativeMethods.ProviderFlagsEnum.CRYPT_SILENT) != 0) { - cngKeyFlag |= (uint)Security.NativeMethods.NCryptDeletKeyFlag.NCRYPT_SILENT_FLAG; + cngKeyFlag |= (uint)SMASecurity.NativeMethods.NCryptDeletKeyFlag.NCRYPT_SILENT_FLAG; } int stat = 0; try { - stat = Security.NativeMethods.NCryptOpenStorageProvider( + stat = SMASecurity.NativeMethods.NCryptOpenStorageProvider( ref hCNGProv, keyProvInfo.pwszProvName, 0); @@ -1687,7 +1687,7 @@ namespace Microsoft.PowerShell.Commands ThrowErrorRemoting(stat); } - stat = Security.NativeMethods.NCryptOpenKey( + stat = SMASecurity.NativeMethods.NCryptOpenKey( hCNGProv, ref hCNGKey, keyProvInfo.pwszContainerName, @@ -1698,21 +1698,21 @@ namespace Microsoft.PowerShell.Commands ThrowErrorRemoting(stat); } - if ((cngKeyFlag & (uint)Security.NativeMethods.NCryptDeletKeyFlag.NCRYPT_SILENT_FLAG) != 0) + if ((cngKeyFlag & (uint)SMASecurity.NativeMethods.NCryptDeletKeyFlag.NCRYPT_SILENT_FLAG) != 0) { unsafe { void* pWnd = hWnd.ToPointer(); - Security.NativeMethods.NCryptSetProperty( + SMASecurity.NativeMethods.NCryptSetProperty( hCNGProv, - Security.NativeMethods.NCRYPT_WINDOW_HANDLE_PROPERTY, + SMASecurity.NativeMethods.NCRYPT_WINDOW_HANDLE_PROPERTY, &pWnd, sizeof(void*), 0); // dwFlags } } - stat = Security.NativeMethods.NCryptDeleteKey(hCNGKey, 0); + stat = SMASecurity.NativeMethods.NCryptDeleteKey(hCNGKey, 0); if (stat != 0) { ThrowErrorRemoting(stat); @@ -1723,10 +1723,10 @@ namespace Microsoft.PowerShell.Commands finally { if (hCNGProv != IntPtr.Zero) - result = Security.NativeMethods.NCryptFreeObject(hCNGProv); + result = SMASecurity.NativeMethods.NCryptFreeObject(hCNGProv); if (hCNGKey != IntPtr.Zero) - result = Security.NativeMethods.NCryptFreeObject(hCNGKey); + result = SMASecurity.NativeMethods.NCryptFreeObject(hCNGKey); } } } @@ -1742,7 +1742,7 @@ namespace Microsoft.PowerShell.Commands private void RemoveCertStore(string storeName, bool fDeleteKey, string sourcePath) { // if recurse is true, remove every cert in the store - IntPtr localName = Security.NativeMethods.CryptFindLocalizedName(storeName); + IntPtr localName = SMASecurity.NativeMethods.CryptFindLocalizedName(storeName); string[] pathElements = GetPathElements(sourcePath); if (localName == IntPtr.Zero)//not find, we can remove { @@ -1767,17 +1767,17 @@ namespace Microsoft.PowerShell.Commands certContext = store.GetNextCert(certContext); } // remove the cert store - const Security.NativeMethods.CertOpenStoreFlags StoreFlags = - Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_READONLY_FLAG | - Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_OPEN_EXISTING_FLAG | - Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG | - Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_DELETE_FLAG | - Security.NativeMethods.CertOpenStoreFlags.CERT_SYSTEM_STORE_LOCAL_MACHINE; + const SMASecurity.NativeMethods.CertOpenStoreFlags StoreFlags = + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_READONLY_FLAG | + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_OPEN_EXISTING_FLAG | + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG | + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_DELETE_FLAG | + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_SYSTEM_STORE_LOCAL_MACHINE; // delete store - IntPtr hCertStore = Security.NativeMethods.CertOpenStore( - Security.NativeMethods.CertOpenStoreProvider.CERT_STORE_PROV_SYSTEM, - Security.NativeMethods.CertOpenStoreEncodingType.X509_ASN_ENCODING, + IntPtr hCertStore = SMASecurity.NativeMethods.CertOpenStore( + SMASecurity.NativeMethods.CertOpenStoreProvider.CERT_STORE_PROV_SYSTEM, + SMASecurity.NativeMethods.CertOpenStoreEncodingType.X509_ASN_ENCODING, IntPtr.Zero, // hCryptProv StoreFlags, storeName); @@ -1849,17 +1849,17 @@ namespace Microsoft.PowerShell.Commands if (fDeleteKey) { // it is fine if below call fails - if (Security.NativeMethods.CertGetCertificateContextProperty( + if (SMASecurity.NativeMethods.CertGetCertificateContextProperty( cert.Handle, - Security.NativeMethods.CertPropertyId.CERT_KEY_PROV_INFO_PROP_ID, + SMASecurity.NativeMethods.CertPropertyId.CERT_KEY_PROV_INFO_PROP_ID, IntPtr.Zero, ref provSize)) { pProvInfo = Marshal.AllocHGlobal((int)provSize); - if (Security.NativeMethods.CertGetCertificateContextProperty( + if (SMASecurity.NativeMethods.CertGetCertificateContextProperty( cert.Handle, - Security.NativeMethods.CertPropertyId.CERT_KEY_PROV_INFO_PROP_ID, + SMASecurity.NativeMethods.CertPropertyId.CERT_KEY_PROV_INFO_PROP_ID, pProvInfo, ref provSize)) { @@ -1879,8 +1879,8 @@ namespace Microsoft.PowerShell.Commands // do remove certificate // should not use the original handle - if (!Security.NativeMethods.CertDeleteCertificateFromStore( - Security.NativeMethods.CertDuplicateCertificateContext(cert.Handle))) + if (!SMASecurity.NativeMethods.CertDeleteCertificateFromStore( + SMASecurity.NativeMethods.CertDuplicateCertificateContext(cert.Handle))) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } @@ -1888,8 +1888,8 @@ namespace Microsoft.PowerShell.Commands // commit the change to physical store if (sourcePath.Contains("UserDS")) { - Security.NativeMethods.CERT_CONTEXT context = - Marshal.PtrToStructure(cert.Handle); + SMASecurity.NativeMethods.CERT_CONTEXT context = + Marshal.PtrToStructure(cert.Handle); CommitUserDS(context.hCertStore); } @@ -1916,10 +1916,10 @@ namespace Microsoft.PowerShell.Commands /// No return. private static void CommitUserDS(IntPtr storeHandle) { - if (!Security.NativeMethods.CertControlStore( + if (!SMASecurity.NativeMethods.CertControlStore( storeHandle, 0, - Security.NativeMethods.CertControlStoreType.CERT_STORE_CTRL_COMMIT, + SMASecurity.NativeMethods.CertControlStoreType.CERT_STORE_CTRL_COMMIT, IntPtr.Zero)) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); @@ -1941,7 +1941,7 @@ namespace Microsoft.PowerShell.Commands IntPtr outCert = IntPtr.Zero; // duplicate cert first - dupCert = Security.NativeMethods.CertDuplicateCertificateContext(cert.Handle); + dupCert = SMASecurity.NativeMethods.CertDuplicateCertificateContext(cert.Handle); if (dupCert == IntPtr.Zero) { @@ -1949,16 +1949,16 @@ namespace Microsoft.PowerShell.Commands } else { - if (!Security.NativeMethods.CertAddCertificateContextToStore( + if (!SMASecurity.NativeMethods.CertAddCertificateContextToStore( store.StoreHandle, cert.Handle, - (uint)Security.NativeMethods.AddCertificateContext.CERT_STORE_ADD_ALWAYS, + (uint)SMASecurity.NativeMethods.AddCertificateContext.CERT_STORE_ADD_ALWAYS, ref outCert)) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } - if (!Security.NativeMethods.CertDeleteCertificateFromStore(dupCert)) + if (!SMASecurity.NativeMethods.CertDeleteCertificateFromStore(dupCert)) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } @@ -1974,7 +1974,7 @@ namespace Microsoft.PowerShell.Commands if (sourcePath.Contains("UserDS")) { - Security.NativeMethods.CERT_CONTEXT context = Marshal.PtrToStructure(cert.Handle); + SMASecurity.NativeMethods.CERT_CONTEXT context = Marshal.PtrToStructure(cert.Handle); CommitUserDS(context.hCertStore); } @@ -3152,9 +3152,9 @@ namespace Microsoft.PowerShell.Commands int propSize = 0; // try to get the property // it is fine if fail for not there - if (Security.NativeMethods.CertGetCertificateContextProperty( + if (SMASecurity.NativeMethods.CertGetCertificateContextProperty( cert.Handle, - Security.NativeMethods.CertPropertyId.CERT_SEND_AS_TRUSTED_ISSUER_PROP_ID, + SMASecurity.NativeMethods.CertPropertyId.CERT_SEND_AS_TRUSTED_ISSUER_PROP_ID, IntPtr.Zero, ref propSize)) { @@ -3165,7 +3165,7 @@ namespace Microsoft.PowerShell.Commands { // if fail int error = Marshal.GetLastWin32Error(); - if (error != Security.NativeMethods.CRYPT_E_NOT_FOUND) + if (error != SMASecurity.NativeMethods.CRYPT_E_NOT_FOUND) { throw new System.ComponentModel.Win32Exception(error); } @@ -3184,7 +3184,7 @@ namespace Microsoft.PowerShell.Commands if (DownLevelHelper.TrustedIssuerSupported()) { IntPtr propertyPtr = IntPtr.Zero; - Security.NativeMethods.CRYPT_DATA_BLOB dataBlob = new(); + SMASecurity.NativeMethods.CRYPT_DATA_BLOB dataBlob = new(); dataBlob.cbData = 0; dataBlob.pbData = IntPtr.Zero; X509Certificate certFromStore = null; @@ -3231,9 +3231,9 @@ namespace Microsoft.PowerShell.Commands } // set property - if (!Security.NativeMethods.CertSetCertificateContextProperty( + if (!SMASecurity.NativeMethods.CertSetCertificateContextProperty( certFromStore != null ? certFromStore.Handle : cert.Handle, - Security.NativeMethods.CertPropertyId.CERT_SEND_AS_TRUSTED_ISSUER_PROP_ID, + SMASecurity.NativeMethods.CertPropertyId.CERT_SEND_AS_TRUSTED_ISSUER_PROP_ID, 0, propertyPtr)) { @@ -3250,7 +3250,7 @@ namespace Microsoft.PowerShell.Commands } else { - Marshal.ThrowExceptionForHR(Security.NativeMethods.NTE_NOT_SUPPORTED); + Marshal.ThrowExceptionForHR(SMASecurity.NativeMethods.NTE_NOT_SUPPORTED); } } @@ -3487,12 +3487,12 @@ namespace Microsoft.PowerShell.Commands if (hWnd == IntPtr.Zero) { - hWnd = Security.NativeMethods.GetConsoleWindow(); + hWnd = SMASecurity.NativeMethods.GetConsoleWindow(); } if (hWnd == IntPtr.Zero) { - hWnd = Security.NativeMethods.GetDesktopWindow(); + hWnd = SMASecurity.NativeMethods.GetDesktopWindow(); } } } @@ -3507,7 +3507,7 @@ namespace Microsoft.PowerShell.Commands uint SessionId; uint ProcessId = (uint)System.Diagnostics.Process.GetCurrentProcess().Id; - if (!Security.NativeMethods.ProcessIdToSessionId(ProcessId, out SessionId)) + if (!SMASecurity.NativeMethods.ProcessIdToSessionId(ProcessId, out SessionId)) return false; if (SessionId == 0) @@ -3553,17 +3553,17 @@ namespace Microsoft.PowerShell.Commands [ArchitectureSensitive] internal static List GetStoreNamesAtLocation(StoreLocation location) { - Security.NativeMethods.CertStoreFlags locationFlag = - Security.NativeMethods.CertStoreFlags.CERT_SYSTEM_STORE_CURRENT_USER; + SMASecurity.NativeMethods.CertStoreFlags locationFlag = + SMASecurity.NativeMethods.CertStoreFlags.CERT_SYSTEM_STORE_CURRENT_USER; switch (location) { case StoreLocation.CurrentUser: - locationFlag = Security.NativeMethods.CertStoreFlags.CERT_SYSTEM_STORE_CURRENT_USER; + locationFlag = SMASecurity.NativeMethods.CertStoreFlags.CERT_SYSTEM_STORE_CURRENT_USER; break; case StoreLocation.LocalMachine: - locationFlag = Security.NativeMethods.CertStoreFlags.CERT_SYSTEM_STORE_LOCAL_MACHINE; + locationFlag = SMASecurity.NativeMethods.CertStoreFlags.CERT_SYSTEM_STORE_LOCAL_MACHINE; break; default: @@ -3571,7 +3571,7 @@ namespace Microsoft.PowerShell.Commands break; } - Security.NativeMethods.CertEnumSystemStoreCallBackProto callBack = new(CertEnumSystemStoreCallBack); + SMASecurity.NativeMethods.CertEnumSystemStoreCallBackProto callBack = new(CertEnumSystemStoreCallBack); // Return a new list to avoid synchronization issues. @@ -3580,7 +3580,7 @@ namespace Microsoft.PowerShell.Commands { storeNames.Clear(); - Security.NativeMethods.CertEnumSystemStore(locationFlag, IntPtr.Zero, + SMASecurity.NativeMethods.CertEnumSystemStore(locationFlag, IntPtr.Zero, IntPtr.Zero, callBack); foreach (string name in storeNames) { diff --git a/src/System.Management.Automation/System.Management.Automation.csproj b/src/System.Management.Automation/System.Management.Automation.csproj index e798d80ef7..56a1e8adeb 100644 --- a/src/System.Management.Automation/System.Management.Automation.csproj +++ b/src/System.Management.Automation/System.Management.Automation.csproj @@ -29,6 +29,8 @@ + + diff --git a/src/System.Management.Automation/security/Authenticode.cs b/src/System.Management.Automation/security/Authenticode.cs index 70b6778c37..a2e7c07ab9 100644 --- a/src/System.Management.Automation/security/Authenticode.cs +++ b/src/System.Management.Automation/security/Authenticode.cs @@ -4,12 +4,16 @@ #pragma warning disable 1634, 1691 #pragma warning disable 56523 -using Dbg = System.Management.Automation; +#if !UNIX +using Microsoft.Security.Extensions; +#endif using System.IO; using System.Management.Automation.Internal; using System.Management.Automation.Security; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; + +using Dbg = System.Management.Automation; using DWORD = System.UInt32; namespace System.Management.Automation @@ -275,12 +279,12 @@ namespace System.Management.Automation if (fileContent == null) { - // First, try to get the signature from the catalog signature APIs. - signature = GetSignatureFromCatalog(fileName); + // First, try to get the signature from the latest dotNet signing API. + signature = GetSignatureFromMSSecurityExtensions(fileName); } // If there is no signature or it is invalid, go by the file content - // with the older WinVerifyTrust APIs + // with the older WinVerifyTrust APIs. if ((signature == null) || (signature.Status != SignatureStatus.Valid)) { signature = GetSignatureFromWinVerifyTrust(fileName, fileContent); @@ -289,147 +293,108 @@ namespace System.Management.Automation return signature; } + /// + /// Gets the file signature using the dotNet Microsoft.Security.Extensions package. + /// This supports both Windows catalog file signatures and embedded file signatures. + /// But it is not supported on all Windows platforms/skus, noteably Win7 and nanoserver. + /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")] - private static Signature GetSignatureFromCatalog(string filename) + private static Signature GetSignatureFromMSSecurityExtensions(string filename) { +#if UNIX + return null; +#else if (Signature.CatalogApiAvailable.HasValue && !Signature.CatalogApiAvailable.Value) { - // Signature.CatalogApiAvailable would be set to false the first time it is detected that - // WTGetSignatureInfo API does not exist on the platform, or if the API is not functional on the target platform. - // Just return from the function instead of revalidating. return null; } - Signature signature = null; - Utils.CheckArgForNullOrEmpty(filename, "fileName"); SecuritySupport.CheckIfFileExists(filename); - try + Signature signature = null; + FileSignatureInfo fileSigInfo; + using (FileStream fileStream = File.OpenRead(filename)) { - using (FileStream stream = File.OpenRead(filename)) + try { - NativeMethods.SIGNATURE_INFO sigInfo = new NativeMethods.SIGNATURE_INFO(); - sigInfo.cbSize = (uint)Marshal.SizeOf(sigInfo); - - IntPtr ppCertContext = IntPtr.Zero; - IntPtr phStateData = IntPtr.Zero; - - try - { - int hresult = NativeMethods.WTGetSignatureInfo(filename, stream.SafeFileHandle.DangerousGetHandle(), - NativeMethods.SIGNATURE_INFO_FLAGS.SIF_CATALOG_SIGNED | - NativeMethods.SIGNATURE_INFO_FLAGS.SIF_CATALOG_FIRST | - NativeMethods.SIGNATURE_INFO_FLAGS.SIF_AUTHENTICODE_SIGNED | - NativeMethods.SIGNATURE_INFO_FLAGS.SIF_BASE_VERIFICATION | - NativeMethods.SIGNATURE_INFO_FLAGS.SIF_CHECK_OS_BINARY, - ref sigInfo, ref ppCertContext, ref phStateData); - - if (Utils.Succeeded(hresult)) - { - DWORD error = GetErrorFromSignatureState(sigInfo.nSignatureState); - - X509Certificate2 cert = null; - - if (ppCertContext != IntPtr.Zero) - { - cert = new X509Certificate2(ppCertContext); - - // Get the time stamper certificate if available - TryGetProviderSigner(phStateData, out IntPtr pProvSigner, out X509Certificate2 timestamperCert); - if (timestamperCert != null) - { - signature = new Signature(filename, error, cert, timestamperCert); - } - else - { - signature = new Signature(filename, error, cert); - } - - switch (sigInfo.nSignatureType) - { - case NativeMethods.SIGNATURE_INFO_TYPE.SIT_AUTHENTICODE: signature.SignatureType = SignatureType.Authenticode; break; - case NativeMethods.SIGNATURE_INFO_TYPE.SIT_CATALOG: signature.SignatureType = SignatureType.Catalog; break; - } - - if (sigInfo.fOSBinary == 1) - { - signature.IsOSBinary = true; - } - } - else - { - signature = new Signature(filename, error); - } - - if (!Signature.CatalogApiAvailable.HasValue) - { - string productFile = Path.Combine(Utils.DefaultPowerShellAppBase, "Modules\\PSDiagnostics\\PSDiagnostics.psm1"); - if (signature.Status != SignatureStatus.Valid) - { - if (string.Equals(filename, productFile, StringComparison.OrdinalIgnoreCase)) - { - Signature.CatalogApiAvailable = false; - } - else - { - // ProductFile has to be Catalog signed. Hence validating - // to see if the Catalog API is functional using the ProductFile. - Signature productFileSignature = GetSignatureFromCatalog(productFile); - Signature.CatalogApiAvailable = (productFileSignature != null && productFileSignature.Status == SignatureStatus.Valid); - } - } - } - } - else - { - // If calling NativeMethods.WTGetSignatureInfo failed (returned a non-zero value), we still want to set Signature.CatalogApiAvailable to false. - Signature.CatalogApiAvailable = false; - } - } - finally - { - if (phStateData != IntPtr.Zero) - { - NativeMethods.FreeWVTStateData(phStateData); - } - - if (ppCertContext != IntPtr.Zero) - { - NativeMethods.CertFreeCertificateContext(ppCertContext); - } - } + fileSigInfo = FileSignatureInfo.GetFromFileStream(fileStream); + System.Diagnostics.Debug.Assert(fileSigInfo is not null, "Returned FileSignatureInfo should never be null."); + } + catch (Exception) + { + // For any API error, enable fallback to WinVerifyTrust APIs. + Signature.CatalogApiAvailable = false; + return null; } } - catch (TypeLoadException) + + DWORD error = GetErrorFromSignatureState(fileSigInfo.State); + + if (fileSigInfo.SigningCertificate is null) { - // If we don't have WTGetSignatureInfo, don't return a Signature. - Signature.CatalogApiAvailable = false; - return null; + signature = new Signature(filename, error); + } + else + { + signature = fileSigInfo.TimestampCertificate is null ? + new Signature(filename, error, fileSigInfo.SigningCertificate) : + new Signature(filename, error, fileSigInfo.SigningCertificate, fileSigInfo.TimestampCertificate); + } + + switch (fileSigInfo.Kind) + { + case SignatureKind.None: + signature.SignatureType = SignatureType.None; + break; + + case SignatureKind.Embedded: + signature.SignatureType = SignatureType.Authenticode; + break; + + case SignatureKind.Catalog: + signature.SignatureType = SignatureType.Catalog; + break; + + default: + System.Diagnostics.Debug.Fail("Signature type can only be None, Authenticode or Catalog."); + break; + } + + signature.IsOSBinary = fileSigInfo.IsOSBinary; + + if (signature.SignatureType == SignatureType.Catalog && !Signature.CatalogApiAvailable.HasValue) + { + Signature.CatalogApiAvailable = fileSigInfo.State != SignatureState.Invalid; } return signature; +#endif } - private static DWORD GetErrorFromSignatureState(NativeMethods.SIGNATURE_STATE state) +#if !UNIX + private static DWORD GetErrorFromSignatureState(SignatureState signatureState) { - switch (state) + switch (signatureState) { - case NativeMethods.SIGNATURE_STATE.SIGNATURE_STATE_UNSIGNED_MISSING: return Win32Errors.TRUST_E_NOSIGNATURE; - case NativeMethods.SIGNATURE_STATE.SIGNATURE_STATE_UNSIGNED_UNSUPPORTED: return Win32Errors.TRUST_E_NOSIGNATURE; - case NativeMethods.SIGNATURE_STATE.SIGNATURE_STATE_UNSIGNED_POLICY: return Win32Errors.TRUST_E_NOSIGNATURE; - case NativeMethods.SIGNATURE_STATE.SIGNATURE_STATE_INVALID_CORRUPT: return Win32Errors.TRUST_E_BAD_DIGEST; - case NativeMethods.SIGNATURE_STATE.SIGNATURE_STATE_INVALID_POLICY: return Win32Errors.CRYPT_E_BAD_MSG; - case NativeMethods.SIGNATURE_STATE.SIGNATURE_STATE_VALID: return Win32Errors.NO_ERROR; - case NativeMethods.SIGNATURE_STATE.SIGNATURE_STATE_TRUSTED: return Win32Errors.NO_ERROR; - case NativeMethods.SIGNATURE_STATE.SIGNATURE_STATE_UNTRUSTED: return Win32Errors.TRUST_E_EXPLICIT_DISTRUST; + case SignatureState.Unsigned: + return Win32Errors.TRUST_E_NOSIGNATURE; + + case SignatureState.SignedAndTrusted: + return Win32Errors.NO_ERROR; + + case SignatureState.SignedAndNotTrusted: + return Win32Errors.TRUST_E_EXPLICIT_DISTRUST; + + case SignatureState.Invalid: + return Win32Errors.TRUST_E_BAD_DIGEST; - // Should not happen default: - System.Diagnostics.Debug.Fail("Should not get here - could not map SIGNATURE_STATE"); + System.Diagnostics.Debug.Fail("Should not get here - could not map FileSignatureInfo.State"); return Win32Errors.TRUST_E_NOSIGNATURE; } } +#endif private static Signature GetSignatureFromWinVerifyTrust(string fileName, string fileContent) { diff --git a/src/System.Management.Automation/security/MshSignature.cs b/src/System.Management.Automation/security/MshSignature.cs index 96ff8a9638..fd8dd4f67e 100644 --- a/src/System.Management.Automation/security/MshSignature.cs +++ b/src/System.Management.Automation/security/MshSignature.cs @@ -110,7 +110,7 @@ namespace System.Management.Automation // Three states: // - True: we can rely on the catalog API to check catalog signature. - // - False: we cannot rely on the catalog API, either because it doesn't exist in the OS (win7), + // - False: we cannot rely on the catalog API, either because it doesn't exist in the OS (win7, nano), // or it's not working properly (OneCore SKUs or dev environment where powershell might // be updated/refreshed). // - Null: it's not determined yet whether catalog API can be relied on or not. diff --git a/src/System.Management.Automation/security/SecuritySupport.cs b/src/System.Management.Automation/security/SecuritySupport.cs index 6321d301ce..03cf24d80f 100644 --- a/src/System.Management.Automation/security/SecuritySupport.cs +++ b/src/System.Management.Automation/security/SecuritySupport.cs @@ -412,7 +412,7 @@ namespace System.Management.Automation.Internal return true; } - // WTGetSignatureInfo is used to verify catalog signature. + // WTGetSignatureInfo, via Microsoft.Security.Extensions, is used to verify catalog signature. // On Win7, catalog API is not available. // On OneCore SKUs like NanoServer/IoT, the API has a bug that makes it not able to find the // corresponding catalog file for a given product file, so it doesn't work properly. diff --git a/src/System.Management.Automation/security/nativeMethods.cs b/src/System.Management.Automation/security/nativeMethods.cs index 165c406300..69ed655963 100644 --- a/src/System.Management.Automation/security/nativeMethods.cs +++ b/src/System.Management.Automation/security/nativeMethods.cs @@ -1145,62 +1145,6 @@ namespace System.Management.Automation.Security DWORD idxCert ); - /// Return Type: HRESULT->LONG->int - ///pszFile: PCWSTR->WCHAR* - ///hFile: HANDLE->void* - ///sigInfoFlags: SIGNATURE_INFO_FLAGS->Anonymous_5157c654_2076_48e7_9241_84ac648615e9 - ///psiginfo: SIGNATURE_INFO* - ///ppCertContext: void** - ///phWVTStateData: HANDLE* - [DllImportAttribute("wintrust.dll", EntryPoint = "WTGetSignatureInfo", CallingConvention = CallingConvention.StdCall)] - internal static extern int WTGetSignatureInfo([InAttribute()][MarshalAsAttribute(UnmanagedType.LPWStr)] string pszFile, [InAttribute()] System.IntPtr hFile, SIGNATURE_INFO_FLAGS sigInfoFlags, ref SIGNATURE_INFO psiginfo, ref System.IntPtr ppCertContext, ref System.IntPtr phWVTStateData); - - internal static void FreeWVTStateData(System.IntPtr phWVTStateData) - { - WINTRUST_DATA wtd = new WINTRUST_DATA(); - DWORD dwResult = Win32Errors.E_FAIL; - IntPtr WINTRUST_ACTION_GENERIC_VERIFY_V2 = IntPtr.Zero; - IntPtr wtdBuffer = IntPtr.Zero; - - Guid actionVerify = - new Guid("00AAC56B-CD44-11d0-8CC2-00C04FC295EE"); - - try - { - WINTRUST_ACTION_GENERIC_VERIFY_V2 = - Marshal.AllocCoTaskMem(Marshal.SizeOf(actionVerify)); - Marshal.StructureToPtr(actionVerify, - WINTRUST_ACTION_GENERIC_VERIFY_V2, - false); - - wtd.cbStruct = (DWORD)Marshal.SizeOf(wtd); - wtd.dwUIChoice = (DWORD)WintrustUIChoice.WTD_UI_NONE; - wtd.fdwRevocationChecks = 0; - wtd.dwUnionChoice = (DWORD)WintrustUnionChoice.WTD_CHOICE_BLOB; - wtd.dwStateAction = (DWORD)WintrustAction.WTD_STATEACTION_CLOSE; - wtd.hWVTStateData = phWVTStateData; - - wtdBuffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(wtd)); - Marshal.StructureToPtr(wtd, wtdBuffer, false); - - // The GetLastWin32Error of this is checked, but PreSharp doesn't seem to be - // able to see that. -#pragma warning disable 56523 - dwResult = WinVerifyTrust( - IntPtr.Zero, - WINTRUST_ACTION_GENERIC_VERIFY_V2, - wtdBuffer); -#pragma warning restore 56523 - } - finally - { - Marshal.DestroyStructure(wtdBuffer); - Marshal.FreeCoTaskMem(wtdBuffer); - Marshal.DestroyStructure(WINTRUST_ACTION_GENERIC_VERIFY_V2); - Marshal.FreeCoTaskMem(WINTRUST_ACTION_GENERIC_VERIFY_V2); - } - } - // // stuff required for getting cert extensions // diff --git a/test/powershell/engine/Security/FileSignature.Tests.ps1 b/test/powershell/engine/Security/FileSignature.Tests.ps1 new file mode 100644 index 0000000000..892aa0e40e --- /dev/null +++ b/test/powershell/engine/Security/FileSignature.Tests.ps1 @@ -0,0 +1,22 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +Describe "Windows platform file signatures" -Tags 'Feature' { + + It "Verifies Get-AuthenticodeSignature returns correct signature for catalog signed file" -Skip:(!$IsWindows) { + + if ($null -eq $env:windir) { + throw "Expected Windows platform environment path variable '%windir%' not available." + } + + $filePath = Join-Path -Path $env:windir -ChildPath 'System32\ntdll.dll' + if (! (Test-Path -Path $filePath)) { + throw "Expected Windows PowerShell platform module path '$filePath' not found." + } + + $signature = Get-AuthenticodeSignature -FilePath $filePath + $signature | Should -Not -BeNullOrEmpty + $signature.Status | Should -BeExactly 'Valid' + $signature.SignatureType | Should -BeExactly 'Catalog' + } +} diff --git a/tools/cgmanifest.json b/tools/cgmanifest.json index 21e6016472..8a3f50c771 100644 --- a/tools/cgmanifest.json +++ b/tools/cgmanifest.json @@ -190,6 +190,16 @@ }, "DevelopmentDependency": false }, + { + "Component": { + "Type": "nuget", + "Nuget": { + "Name": "Microsoft.Security.Extensions", + "Version": "1.2.0" + } + }, + "DevelopmentDependency": false + }, { "Component": { "Type": "nuget",