Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
Authenticode.cs606 linesDownload Raw Back to security
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4#pragma warning disable 1634, 16915#pragma warning disable 565236 7#if !UNIX8using Microsoft.Security.Extensions;9#endif10using System.ComponentModel;11using System.IO;12using System.Management.Automation.Internal;13using System.Management.Automation.Security;14using System.Management.Automation.Win32Native;15using System.Runtime.InteropServices;16using System.Security.Cryptography.X509Certificates;17 18using Dbg = System.Management.Automation;19 20namespace System.Management.Automation21{22    /// <summary>23    /// Defines the options that control what data is embedded in the24    /// signature blob.25    /// </summary>26    public enum SigningOption27    {28        /// <summary>29        /// Embeds only the signer's certificate.30        /// </summary>31        AddOnlyCertificate,32 33        /// <summary>34        /// Embeds the entire certificate chain.35        /// </summary>36        AddFullCertificateChain,37 38        /// <summary>39        /// Embeds the entire certificate chain, except for the root40        /// certificate.41        /// </summary>42        AddFullCertificateChainExceptRoot,43 44        /// <summary>45        /// Default: Embeds the entire certificate chain, except for the46        /// root certificate.47        /// </summary>48        Default = AddFullCertificateChainExceptRoot49    }50 51    /// <summary>52    /// Helper functions for signature functionality.53    /// </summary>54    internal static class SignatureHelper55    {56        private static Guid WINTRUST_ACTION_GENERIC_VERIFY_V2 = new Guid("00AAC56B-CD44-11d0-8CC2-00C04FC295EE");57 58        /// <summary>59        /// Tracer for SignatureHelper.60        /// </summary>61        [Dbg.TraceSource("SignatureHelper",62                          "tracer for SignatureHelper")]63        private static readonly Dbg.PSTraceSource s_tracer =64            Dbg.PSTraceSource.GetTracer("SignatureHelper",65                          "tracer for SignatureHelper");66 67        /// <summary>68        /// Sign a file.69        /// </summary>70        /// <param name="option">Option that controls what gets embedded in the signature blob.</param>71        /// <param name="fileName">Name of file to sign.</param>72        /// <param name="certificate">Signing cert.</param>73        /// <param name="timeStampServerUrl">URL of time stamping server.</param>74        /// <param name="hashAlgorithm"> The name of the hash75        /// algorithm to use.</param>76        /// <returns>Does not return a value.</returns>77        /// <exception cref="System.ArgumentNullException">78        /// Thrown if argument fileName or certificate is null.79        /// </exception>80        /// <exception cref="System.ArgumentException">81        /// Thrown if82        /// -- argument fileName is empty OR83        /// -- the specified certificate is not suitable for84        ///    signing code85        /// </exception>86        /// <exception cref="System.Security.Cryptography.CryptographicException">87        /// This exception can be thrown if any cryptographic error occurs.88        /// It is not possible to know exactly what went wrong.89        /// This is because of the way CryptographicException is designed.90        /// Possible reasons:91        ///  -- certificate is invalid92        ///  -- certificate has no private key93        ///  -- certificate password mismatch94        ///  -- etc95        /// </exception>96        /// <exception cref="System.IO.FileNotFoundException">97        /// Thrown if the file specified by argument fileName is not found98        /// </exception>99        internal static Signature SignFile(SigningOption option,100                                           string fileName,101                                           X509Certificate2 certificate,102                                           string timeStampServerUrl,103                                           string hashAlgorithm)104        {105            bool result = false;106            Signature signature = null;107            IntPtr pSignInfo = IntPtr.Zero;108            uint error = 0;109            string hashOid = null;110 111            Utils.CheckArgForNullOrEmpty(fileName, "fileName");112            Utils.CheckArgForNull(certificate, "certificate");113 114            // If given, TimeStamp server URLs must begin with http:// or https://115            if (!string.IsNullOrEmpty(timeStampServerUrl))116            {117                if ((timeStampServerUrl.Length <= 7) || (118                    !timeStampServerUrl.StartsWith("http://", StringComparison.OrdinalIgnoreCase) &&119                    !timeStampServerUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase)))120                {121                    throw PSTraceSource.NewArgumentException(122                        nameof(certificate),123                        Authenticode.TimeStampUrlRequired);124                }125            }126 127            // Validate that the hash algorithm is valid128            if (!string.IsNullOrEmpty(hashAlgorithm))129            {130                IntPtr intptrAlgorithm = Marshal.StringToHGlobalUni(hashAlgorithm);131 132                IntPtr oidPtr = NativeMethods.CryptFindOIDInfo(NativeConstants.CRYPT_OID_INFO_NAME_KEY,133                        intptrAlgorithm,134                        0);135 136                // If we couldn't find an OID for the hash137                // algorithm, it was invalid.138                if (oidPtr == IntPtr.Zero)139                {140                    throw PSTraceSource.NewArgumentException(141                        nameof(certificate),142                        Authenticode.InvalidHashAlgorithm);143                }144                else145                {146                    NativeMethods.CRYPT_OID_INFO oidInfo =147                        Marshal.PtrToStructure<NativeMethods.CRYPT_OID_INFO>(oidPtr);148 149                    hashOid = oidInfo.pszOID;150                }151            }152 153            if (!SecuritySupport.CertIsGoodForSigning(certificate))154            {155                throw PSTraceSource.NewArgumentException(156                        nameof(certificate),157                        Authenticode.CertNotGoodForSigning);158            }159 160            SecuritySupport.CheckIfFileExists(fileName);161            // SecurityUtils.CheckIfFileSmallerThan4Bytes(fileName);162 163            try164            {165                // CryptUI is not documented either way, but does not166                // support empty strings for the timestamp server URL.167                // It expects null, only.  Instead, it randomly AVs if you168                // try.169                string timeStampServerUrlForCryptUI = null;170                if (!string.IsNullOrEmpty(timeStampServerUrl))171                {172                    timeStampServerUrlForCryptUI = timeStampServerUrl;173                }174 175                //176                // first initialize the struct to pass to177                // CryptUIWizDigitalSign() function178                //179                NativeMethods.CRYPTUI_WIZ_DIGITAL_SIGN_INFO si = NativeMethods.InitSignInfoStruct(fileName,180                                                              certificate,181                                                              timeStampServerUrlForCryptUI,182                                                              hashOid,183                                                              option);184 185                pSignInfo = Marshal.AllocCoTaskMem(Marshal.SizeOf(si));186                Marshal.StructureToPtr(si, pSignInfo, false);187 188                //189                // sign the file190                //191                // The GetLastWin32Error of this is checked, but PreSharp doesn't seem to be192                // able to see that.193#pragma warning disable 56523194                result = NativeMethods.CryptUIWizDigitalSign(195                    (uint)NativeMethods.CryptUIFlags.CRYPTUI_WIZ_NO_UI,196                    IntPtr.Zero,197                    IntPtr.Zero,198                    pSignInfo,199                    IntPtr.Zero);200#pragma warning restore 56523201 202                if (si.pSignExtInfo != IntPtr.Zero)203                {204                    Marshal.DestroyStructure<NativeMethods.CRYPTUI_WIZ_DIGITAL_SIGN_EXTENDED_INFO>(si.pSignExtInfo);205                    Marshal.FreeCoTaskMem(si.pSignExtInfo);206                }207 208                if (!result)209                {210                    error = GetLastWin32Error();211 212                    //213                    // ISSUE-2004/05/08-kumarp : there seems to be a bug214                    // in CryptUIWizDigitalSign().215                    // It returns 80004005 or 80070001216                    // but it signs the file correctly. Mask this error217                    // till we figure out this odd behavior.218                    //219                    if ((error == 0x80004005) ||220                        (error == 0x80070001) ||221 222                        // CryptUIWizDigitalSign introduced a breaking change in Win8 to return this223                        // error code (ERROR_INTERNET_NAME_NOT_RESOLVED) when you provide an invalid224                        // timestamp server. It used to be 0x80070001.225                        // Also masking this out so that we don't introduce a breaking change ourselves.226                        (error == 0x80072EE7)227                        )228                    {229                        result = true;230                    }231                    else232                    {233                        if (error == Win32Errors.NTE_BAD_ALGID)234                        {235                            throw PSTraceSource.NewArgumentException(236                                nameof(certificate),237                                Authenticode.InvalidHashAlgorithm);238                        }239 240                        s_tracer.TraceError("CryptUIWizDigitalSign: failed: {0:x}",241                                          error);242                    }243                }244 245                if (result)246                {247                    signature = GetSignature(fileName, null);248                }249                else250                {251                    signature = new Signature(fileName, (uint)error);252                }253            }254            finally255            {256                Marshal.DestroyStructure<NativeMethods.CRYPTUI_WIZ_DIGITAL_SIGN_INFO>(pSignInfo);257                Marshal.FreeCoTaskMem(pSignInfo);258            }259 260            return signature;261        }262 263        /// <summary>264        /// Get signature on the specified file.265        /// </summary>266        /// <param name="fileName">Name of file to check.</param>267        /// <param name="fileContent">Content of file to check.</param>268        /// <returns>Signature object.</returns>269        /// <exception cref="System.ArgumentException">270        /// Thrown if argument fileName is empty.271        /// </exception>272        /// <exception cref="System.ArgumentNullException">273        /// Thrown if argument fileName is null274        /// </exception>275        /// <exception cref="System.IO.FileNotFoundException">276        /// Thrown if the file specified by argument fileName is not found.277        /// </exception>278        internal static Signature GetSignature(string fileName, byte[] fileContent)279        {280            Signature signature = null;281 282            if (fileContent == null)283            {284                // First, try to get the signature from the latest dotNet signing API.285                signature = GetSignatureFromMSSecurityExtensions(fileName);286            }287 288            // If there is no signature or it is invalid, go by the file content289            // with the older WinVerifyTrust APIs.290            if ((signature == null) || (signature.Status != SignatureStatus.Valid))291            {292                signature = GetSignatureFromWinVerifyTrust(fileName, fileContent);293            }294 295            return signature;296        }297 298        /// <summary>299        /// Gets the file signature using the dotNet Microsoft.Security.Extensions package.300        /// This supports both Windows catalog file signatures and embedded file signatures.301        /// But it is not supported on all Windows platforms/skus, noteably Win7 and nanoserver.302        /// </summary>303        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")]304        private static Signature GetSignatureFromMSSecurityExtensions(string filename)305        {306#if UNIX307            return null;308#else309            if (Signature.CatalogApiAvailable.HasValue && !Signature.CatalogApiAvailable.Value)310            {311                return null;312            }313 314            Utils.CheckArgForNullOrEmpty(filename, "fileName");315            SecuritySupport.CheckIfFileExists(filename);316 317            Signature signature = null;318            FileSignatureInfo fileSigInfo;319            using (FileStream fileStream = File.OpenRead(filename))320            {321                try322                {323                    fileSigInfo = FileSignatureInfo.GetFromFileStream(fileStream);324                    System.Diagnostics.Debug.Assert(fileSigInfo is not null, "Returned FileSignatureInfo should never be null.");325                }326                catch (Exception)327                {328                    // For any API error, enable fallback to WinVerifyTrust APIs.329                    Signature.CatalogApiAvailable = false;330                    return null;331                }332            }333 334            uint error = GetErrorFromSignatureState(fileSigInfo.State);335 336            if (fileSigInfo.SigningCertificate is null)337            {338                signature = new Signature(filename, error);339            }340            else341            {342                signature = fileSigInfo.TimestampCertificate is null ?343                    new Signature(filename, error, fileSigInfo.SigningCertificate) :344                    new Signature(filename, error, fileSigInfo.SigningCertificate, fileSigInfo.TimestampCertificate);345            }346 347            switch (fileSigInfo.Kind)348            {349                case SignatureKind.None:350                    signature.SignatureType = SignatureType.None;351                    break;352 353                case SignatureKind.Embedded:354                    signature.SignatureType = SignatureType.Authenticode;355                    break;356 357                case SignatureKind.Catalog:358                    signature.SignatureType = SignatureType.Catalog;359                    break;360 361                default:362                    System.Diagnostics.Debug.Fail("Signature type can only be None, Authenticode or Catalog.");363                    break;364            }365 366            signature.IsOSBinary = fileSigInfo.IsOSBinary;367 368            if (signature.SignatureType == SignatureType.Catalog && !Signature.CatalogApiAvailable.HasValue)369            {370                Signature.CatalogApiAvailable = fileSigInfo.State != SignatureState.Invalid;371            }372 373            return signature;374#endif375        }376 377#if !UNIX378        private static uint GetErrorFromSignatureState(SignatureState signatureState)379        {380            switch (signatureState)381            {382                case SignatureState.Unsigned:383                    return Win32Errors.TRUST_E_NOSIGNATURE;384 385                case SignatureState.SignedAndTrusted:386                    return Win32Errors.NO_ERROR;387 388                case SignatureState.SignedAndNotTrusted:389                    return Win32Errors.TRUST_E_EXPLICIT_DISTRUST;390 391                case SignatureState.Invalid:392                    return Win32Errors.TRUST_E_BAD_DIGEST;393 394                default:395                    System.Diagnostics.Debug.Fail("Should not get here - could not map FileSignatureInfo.State");396                    return Win32Errors.TRUST_E_NOSIGNATURE;397            }398        }399#endif400 401        private static Signature GetSignatureFromWinVerifyTrust(string fileName, byte[] fileContent)402        {403            Signature signature = null;404 405            WinTrustMethods.WINTRUST_DATA wtd;406            uint error = Win32Errors.E_FAIL;407 408            if (fileContent == null)409            {410                Utils.CheckArgForNullOrEmpty(fileName, "fileName");411                SecuritySupport.CheckIfFileExists(fileName);412 413                // SecurityUtils.CheckIfFileSmallerThan4Bytes(fileName);414            }415 416            try417            {418                error = GetWinTrustData(fileName, fileContent, out wtd);419 420                if (error != Win32Errors.NO_ERROR)421                {422                    s_tracer.WriteLine("GetWinTrustData failed: {0:x}", error);423                }424 425                signature = GetSignatureFromWintrustData(fileName, error, wtd);426 427                wtd.dwStateAction = WinTrustAction.WTD_STATEACTION_CLOSE;428                error = WinTrustMethods.WinVerifyTrust(429                    IntPtr.Zero,430                    ref WINTRUST_ACTION_GENERIC_VERIFY_V2,431                    ref wtd);432 433                if (error != Win32Errors.NO_ERROR)434                {435                    s_tracer.WriteLine("DestroyWinTrustDataStruct failed: {0:x}", error);436                }437            }438            catch (AccessViolationException)439            {440                signature = new Signature(fileName, Win32Errors.TRUST_E_NOSIGNATURE);441            }442 443            return signature;444        }445 446        private static uint GetWinTrustData(447            string fileName,448            byte[] fileContent,449            out WinTrustMethods.WINTRUST_DATA wtData)450        {451            wtData = new()452            {453                cbStruct = (uint)Marshal.SizeOf<WinTrustMethods.WINTRUST_DATA>(),454                dwUIChoice = WinTrustUIChoice.WTD_UI_NONE,455                dwStateAction = WinTrustAction.WTD_STATEACTION_VERIFY,456            };457 458            unsafe459            {460                fixed (char* fileNamePtr = fileName)461                {462                    if (fileContent == null)463                    {464                        WinTrustMethods.WINTRUST_FILE_INFO wfi = new()465                        {466                            cbStruct = (uint)Marshal.SizeOf<WinTrustMethods.WINTRUST_FILE_INFO>(),467                            pcwszFilePath = fileNamePtr,468                        };469                        wtData.dwUnionChoice = WinTrustUnionChoice.WTD_CHOICE_FILE;470                        wtData.pChoice = &wfi;471 472                        return WinTrustMethods.WinVerifyTrust(473                            IntPtr.Zero,474                            ref WINTRUST_ACTION_GENERIC_VERIFY_V2,475                            ref wtData);476                    }477 478                    fixed (byte* contentPtr = fileContent)479                    {480                        Guid pwshSIP = new("603BCC1F-4B59-4E08-B724-D2C6297EF351");481                        WinTrustMethods.WINTRUST_BLOB_INFO wbi = new()482                        {483                            cbStruct = (uint)Marshal.SizeOf<WinTrustMethods.WINTRUST_BLOB_INFO>(),484                            gSubject = pwshSIP,485                            pcwszDisplayName = fileNamePtr,486                            cbMemObject = (uint)fileContent.Length,487                            pbMemObject = contentPtr,488                        };489                        wtData.dwUnionChoice = WinTrustUnionChoice.WTD_CHOICE_BLOB;490                        wtData.pChoice = &wbi;491 492                        return WinTrustMethods.WinVerifyTrust(493                            IntPtr.Zero,494                            ref WINTRUST_ACTION_GENERIC_VERIFY_V2,495                            ref wtData);496                    }497                }498            }499        }500 501        private static X509Certificate2 GetCertFromChain(IntPtr pSigner)502        {503            try504            {505                IntPtr pCert = WinTrustMethods.WTHelperGetProvCertFromChain(pSigner, 0);506                NativeMethods.CRYPT_PROVIDER_CERT provCert =507                    Marshal.PtrToStructure<NativeMethods.CRYPT_PROVIDER_CERT>(pCert);508                return new X509Certificate2(provCert.pCert);509            }510            catch (Win32Exception)511            {512                // We don't care about the Win32 error code here, so return513                // null on a failure and let the caller handle it.514                return null;515            }516        }517 518        private static Signature GetSignatureFromWintrustData(519            string filePath,520            uint error,521            WinTrustMethods.WINTRUST_DATA wtd)522        {523            s_tracer.WriteLine("GetSignatureFromWintrustData: error: {0}", error);524 525            Signature signature = null;526            if (TryGetProviderSigner(wtd.hWVTStateData, out IntPtr pProvSigner, out X509Certificate2 timestamperCert))527            {528                //529                // get cert of the signer530                //531                X509Certificate2 signerCert = GetCertFromChain(pProvSigner);532 533                if (signerCert != null)534                {535                    if (timestamperCert != null)536                    {537                        signature = new Signature(filePath,538                                                  error,539                                                  signerCert,540                                                  timestamperCert);541                    }542                    else543                    {544                        signature = new Signature(filePath,545                                                  error,546                                                  signerCert);547                    }548 549                    signature.SignatureType = SignatureType.Authenticode;550                }551            }552 553            Diagnostics.Assert(error != 0 || signature != null, "GetSignatureFromWintrustData: general crypto failure");554 555            if ((signature == null) && (error != 0))556            {557                signature = new Signature(filePath, error);558            }559 560            return signature;561        }562 563        private static bool TryGetProviderSigner(IntPtr wvtStateData, out IntPtr pProvSigner, out X509Certificate2 timestamperCert)564        {565            pProvSigner = IntPtr.Zero;566            timestamperCert = null;567 568            try569            {570                IntPtr pProvData = WinTrustMethods.WTHelperProvDataFromStateData(wvtStateData);571 572                pProvSigner = WinTrustMethods.WTHelperGetProvSignerFromChain(573                    pProvData,574                    signerIdx: 0,575                    counterSigner: false,576                    counterSignerIdx: 0);577 578                NativeMethods.CRYPT_PROVIDER_SGNR provSigner =579                    Marshal.PtrToStructure<NativeMethods.CRYPT_PROVIDER_SGNR>(pProvSigner);580                if (provSigner.csCounterSigners == 1)581                {582                    //583                    // time stamper cert available584                    //585                    timestamperCert = GetCertFromChain(provSigner.pasCounterSigners);586                }587 588                return true;589            }590            catch (Win32Exception)591            {592                return false;593            }594        }595 596        private static uint GetLastWin32Error()597        {598            int error = Marshal.GetLastWin32Error();599 600            return SecuritySupport.GetDWORDFromInt(error);601        }602    }603}604 605#pragma warning restore 56523606