Exemple #1
0
        internal static Signature SignFile(SigningOption option,
                                           string fileName,
                                           X509Certificate2 certificate,
                                           string timeStampServerUrl,
                                           string hashAlgorithm)
        {
            bool      result    = false;
            Signature signature = null;
            IntPtr    pSignInfo = IntPtr.Zero;
            DWORD     error     = 0;
            string    hashOid   = null;

            Utils.CheckArgForNullOrEmpty(fileName, "fileName");
            Utils.CheckArgForNull(certificate, "certificate");

            // If given, TimeStamp server URLs must begin with http://
            if (!string.IsNullOrEmpty(timeStampServerUrl))
            {
                if ((timeStampServerUrl.Length <= 7) ||
                    (timeStampServerUrl.IndexOf("http://", StringComparison.OrdinalIgnoreCase) != 0))
                {
                    throw PSTraceSource.NewArgumentException(
                              nameof(certificate),
                              Authenticode.TimeStampUrlRequired);
                }
            }

            // Validate that the hash algorithm is valid
            if (!string.IsNullOrEmpty(hashAlgorithm))
            {
                IntPtr intptrAlgorithm = Marshal.StringToHGlobalUni(hashAlgorithm);

                IntPtr oidPtr = NativeMethods.CryptFindOIDInfo(NativeConstants.CRYPT_OID_INFO_NAME_KEY,
                                                               intptrAlgorithm,
                                                               0);

                // If we couldn't find an OID for the hash
                // algorithm, it was invalid.
                if (oidPtr == IntPtr.Zero)
                {
                    throw PSTraceSource.NewArgumentException(
                              nameof(certificate),
                              Authenticode.InvalidHashAlgorithm);
                }
                else
                {
                    NativeMethods.CRYPT_OID_INFO oidInfo =
                        Marshal.PtrToStructure <NativeMethods.CRYPT_OID_INFO>(oidPtr);

                    hashOid = oidInfo.pszOID;
                }
            }

            if (!SecuritySupport.CertIsGoodForSigning(certificate))
            {
                throw PSTraceSource.NewArgumentException(
                          nameof(certificate),
                          Authenticode.CertNotGoodForSigning);
            }

            SecuritySupport.CheckIfFileExists(fileName);
            // SecurityUtils.CheckIfFileSmallerThan4Bytes(fileName);

            try
            {
                // CryptUI is not documented either way, but does not
                // support empty strings for the timestamp server URL.
                // It expects null, only.  Instead, it randomly AVs if you
                // try.
                string timeStampServerUrlForCryptUI = null;
                if (!string.IsNullOrEmpty(timeStampServerUrl))
                {
                    timeStampServerUrlForCryptUI = timeStampServerUrl;
                }

                //
                // first initialize the struct to pass to
                // CryptUIWizDigitalSign() function
                //
                NativeMethods.CRYPTUI_WIZ_DIGITAL_SIGN_INFO si = NativeMethods.InitSignInfoStruct(fileName,
                                                                                                  certificate,
                                                                                                  timeStampServerUrlForCryptUI,
                                                                                                  hashOid,
                                                                                                  option);

                pSignInfo = Marshal.AllocCoTaskMem(Marshal.SizeOf(si));
                Marshal.StructureToPtr(si, pSignInfo, false);

                //
                // sign the file
                //
                // The GetLastWin32Error of this is checked, but PreSharp doesn't seem to be
                // able to see that.
#pragma warning disable 56523
                result = NativeMethods.CryptUIWizDigitalSign(
                    (DWORD)NativeMethods.CryptUIFlags.CRYPTUI_WIZ_NO_UI,
                    IntPtr.Zero,
                    IntPtr.Zero,
                    pSignInfo,
                    IntPtr.Zero);
#pragma warning restore 56523

                if (si.pSignExtInfo != IntPtr.Zero)
                {
                    Marshal.DestroyStructure <NativeMethods.CRYPTUI_WIZ_DIGITAL_SIGN_EXTENDED_INFO>(si.pSignExtInfo);
                    Marshal.FreeCoTaskMem(si.pSignExtInfo);
                }

                if (!result)
                {
                    error = GetLastWin32Error();

                    //
                    // ISSUE-2004/05/08-kumarp : there seems to be a bug
                    // in CryptUIWizDigitalSign().
                    // It returns 80004005 or 80070001
                    // but it signs the file correctly. Mask this error
                    // till we figure out this odd behavior.
                    //
                    if ((error == 0x80004005) ||
                        (error == 0x80070001) ||

                        // CryptUIWizDigitalSign introduced a breaking change in Win8 to return this
                        // error code (ERROR_INTERNET_NAME_NOT_RESOLVED) when you provide an invalid
                        // timestamp server. It used to be 0x80070001.
                        // Also masking this out so that we don't introduce a breaking change ourselves.
                        (error == 0x80072EE7)
                        )
                    {
                        result = true;
                    }
                    else
                    {
                        if (error == Win32Errors.NTE_BAD_ALGID)
                        {
                            throw PSTraceSource.NewArgumentException(
                                      nameof(certificate),
                                      Authenticode.InvalidHashAlgorithm);
                        }

                        s_tracer.TraceError("CryptUIWizDigitalSign: failed: {0:x}",
                                            error);
                    }
                }

                if (result)
                {
                    signature = GetSignature(fileName, null);
                }
                else
                {
                    signature = new Signature(fileName, (DWORD)error);
                }
            }
            finally
            {
                Marshal.DestroyStructure <NativeMethods.CRYPTUI_WIZ_DIGITAL_SIGN_INFO>(pSignInfo);
                Marshal.FreeCoTaskMem(pSignInfo);
            }

            return(signature);
        }
Exemple #2
0
        private static DWORD GetLastWin32Error()
        {
            int error = Marshal.GetLastWin32Error();

            return(SecuritySupport.GetDWORDFromInt(error));
        }
Exemple #3
0
        private static Signature GetSignatureFromMSSecurityExtensions(string filename)
        {
#if UNIX
            return(null);
#else
            if (Signature.CatalogApiAvailable.HasValue && !Signature.CatalogApiAvailable.Value)
            {
                return(null);
            }

            Utils.CheckArgForNullOrEmpty(filename, "fileName");
            SecuritySupport.CheckIfFileExists(filename);

            Signature         signature = null;
            FileSignatureInfo fileSigInfo;
            using (FileStream fileStream = File.OpenRead(filename))
            {
                try
                {
                    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);
                }
            }

            DWORD error = GetErrorFromSignatureState(fileSigInfo.State);

            if (fileSigInfo.SigningCertificate is 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
        }
Exemple #4
0
        private static string GetSignatureStatusMessage(SignatureStatus status,
                                                        DWORD error,
                                                        string filePath)
        {
            string message        = null;
            string resourceString = null;
            string arg            = null;

            switch (status)
            {
            case SignatureStatus.Valid:
                resourceString = MshSignature.MshSignature_Valid;
                break;

            case SignatureStatus.UnknownError:
                int            intError = SecuritySupport.GetIntFromDWORD(error);
                Win32Exception e        = new Win32Exception(intError);
                message = e.Message;
                break;

            case SignatureStatus.Incompatible:
                if (error == Win32Errors.NTE_BAD_ALGID)
                {
                    resourceString = MshSignature.MshSignature_Incompatible_HashAlgorithm;
                }
                else
                {
                    resourceString = MshSignature.MshSignature_Incompatible;
                }

                arg = filePath;
                break;

            case SignatureStatus.NotSigned:
                resourceString = MshSignature.MshSignature_NotSigned;
                arg            = filePath;
                break;

            case SignatureStatus.HashMismatch:
                resourceString = MshSignature.MshSignature_HashMismatch;
                arg            = filePath;
                break;

            case SignatureStatus.NotTrusted:
                resourceString = MshSignature.MshSignature_NotTrusted;
                arg            = filePath;
                break;

            case SignatureStatus.NotSupportedFileFormat:
                resourceString = MshSignature.MshSignature_NotSupportedFileFormat;
                arg            = System.IO.Path.GetExtension(filePath);

                if (string.IsNullOrEmpty(arg))
                {
                    resourceString = MshSignature.MshSignature_NotSupportedFileFormat_NoExtension;
                    arg            = null;
                }

                break;
            }

            if (message == null)
            {
                if (arg == null)
                {
                    message = resourceString;
                }
                else
                {
                    message = StringUtil.Format(resourceString, arg);
                }
            }

            return(message);
        }
Exemple #5
0
        private static Signature GetSignatureFromCatalog(string filename)
        {
            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
            {
                using (FileStream stream = File.OpenRead(filename))
                {
                    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);
                        }
                    }
                }
            }
            catch (TypeLoadException)
            {
                // If we don't have WTGetSignatureInfo, don't return a Signature.
                Signature.CatalogApiAvailable = false;
                return(null);
            }

            return(signature);
        }
Exemple #6
0
        private static string GetSignatureStatusMessage(SignatureStatus status, uint error, string filePath)
        {
            string message    = null;
            string formatSpec = null;
            string extension  = null;

            switch (status)
            {
            case SignatureStatus.Valid:
                formatSpec = MshSignature.MshSignature_Valid;
                goto Label_00A4;

            case SignatureStatus.UnknownError:
            {
                Win32Exception exception = new Win32Exception(SecuritySupport.GetIntFromDWORD(error));
                message = exception.Message;
                goto Label_00A4;
            }

            case SignatureStatus.NotSigned:
                formatSpec = MshSignature.MshSignature_NotSigned;
                extension  = filePath;
                goto Label_00A4;

            case SignatureStatus.HashMismatch:
                formatSpec = MshSignature.MshSignature_HashMismatch;
                extension  = filePath;
                goto Label_00A4;

            case SignatureStatus.NotTrusted:
                formatSpec = MshSignature.MshSignature_NotTrusted;
                extension  = filePath;
                goto Label_00A4;

            case SignatureStatus.NotSupportedFileFormat:
                formatSpec = MshSignature.MshSignature_NotSupportedFileFormat;
                extension  = System.IO.Path.GetExtension(filePath);
                if (string.IsNullOrEmpty(extension))
                {
                    formatSpec = MshSignature.MshSignature_NotSupportedFileFormat_NoExtension;
                    extension  = null;
                }
                goto Label_00A4;

            case SignatureStatus.Incompatible:
                if (error != 0x80090008)
                {
                    formatSpec = MshSignature.MshSignature_Incompatible;
                    break;
                }
                formatSpec = MshSignature.MshSignature_Incompatible_HashAlgorithm;
                break;

            default:
                goto Label_00A4;
            }
            extension = filePath;
Label_00A4:
            if (message != null)
            {
                return(message);
            }
            if (extension == null)
            {
                return(formatSpec);
            }
            return(StringUtil.Format(formatSpec, extension));
        }