Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
<PackageVersion Include="Microsoft.Web.Deployment" Version="$(WebDeploymentPackageVersion)" />
<PackageVersion Include="Microsoft.Web.Xdt" Version="$(MicrosoftWebXdtPackageVersion)" />
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="$(MicrosoftNETCoreAppRefPackageVersion)" />
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="$(MicrosoftWindowsCsWin32PackageVersion)" />
<PackageVersion Include="Moq" Version="$(MoqPackageVersion)" />
<PackageVersion Include="NETStandard.Library.NETFramework" Version="$(NETStandardLibraryNETFrameworkVersion)" />
<PackageVersion Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
Expand Down
3 changes: 2 additions & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
<SystemCommandLineVersion>2.0.0-beta4.23407.1</SystemCommandLineVersion>
<MicrosoftDeploymentDotNetReleasesVersion>2.0.0-preview.1.23601.1</MicrosoftDeploymentDotNetReleasesVersion>
<MicrosoftVisualStudioSetupConfigurationInteropVersion>3.2.2146</MicrosoftVisualStudioSetupConfigurationInteropVersion>
<MicrosoftWindowsCsWin32PackageVersion>0.3.49-beta</MicrosoftWindowsCsWin32PackageVersion>
</PropertyGroup>
<PropertyGroup>
<!-- Dependencies from https://github.com/dotnet/runtime -->
Expand Down Expand Up @@ -195,7 +196,7 @@
<MicrosoftDotNetXUnitExtensionsVersion>9.0.0-beta.23607.2</MicrosoftDotNetXUnitExtensionsVersion>
<MoqPackageVersion>4.18.4</MoqPackageVersion>
<XunitCombinatorialVersion>1.3.2</XunitCombinatorialVersion>
<MicrosoftDotNetInstallerWindowsSecurityTestDataPackageVersion>6.0.0-beta.22262.1</MicrosoftDotNetInstallerWindowsSecurityTestDataPackageVersion>
<MicrosoftDotNetInstallerWindowsSecurityTestDataPackageVersion>8.0.0-beta.23607.1</MicrosoftDotNetInstallerWindowsSecurityTestDataPackageVersion>
</PropertyGroup>
<PropertyGroup>
<ExeExtension>.exe</ExeExtension>
Expand Down
17 changes: 3 additions & 14 deletions src/Cli/dotnet/Installer/Windows/InstallerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ protected bool VerifySignatures
/// </summary>
/// <param name="elevationContext"></param>
/// <param name="logger"></param>
/// <param name="verifySignatures"></param>
/// <param name="verifySignatures">Determines whether MSI signatures should be verified</param>
protected InstallerBase(InstallElevationContextBase elevationContext, ISetupLogger logger, bool verifySignatures)
{
ElevationContext = elevationContext;
Expand All @@ -123,7 +123,7 @@ protected void Elevate()
/// Checks the specified error code to determine whether it indicates a success result. If not, additional extended information
/// is retrieved before throwing a <see cref="WorkloadException"/>.
///
/// The<see cref="Restart"/> property will be set to <see langword="true" /> if the error is either <see cref="Error.SUCCESS_REBOOT_INITIATED"/>
/// The <see cref="Restart"/> property will be set to <see langword="true" /> if the error is either <see cref="Error.SUCCESS_REBOOT_INITIATED"/>
/// or <see cref="Error.SUCCESS_REBOOT_REQUIRED"/>.
/// </summary>
/// <param name="error">The error code to check.</param>
Expand All @@ -133,18 +133,7 @@ protected void ExitOnError(uint error, string message)
{
if (!Error.Success(error))
{
StringBuilder sb = new(2048);
NativeMethods.FormatMessage((uint)(FormatMessage.FromSystem | FormatMessage.IgnoreInserts),
IntPtr.Zero, error, 0, sb, (uint)sb.Capacity, IntPtr.Zero);
string errorDetail = sb.ToString().TrimEnd(Environment.NewLine.ToCharArray());
string errorMessage = $"{message} Error: 0x{error:x8}.";

if (!string.IsNullOrWhiteSpace(errorDetail))
{
errorMessage += $" {errorDetail}";
}

throw new WorkloadException(error, errorMessage);
throw new WorkloadException($"{message} Error: 0x{error:x8}, {Marshal.GetPInvokeErrorMessage((int)error)}");
}

// Once set to true, we retain restart information for the duration of the underlying command.
Expand Down
4 changes: 2 additions & 2 deletions src/Cli/dotnet/Installer/Windows/LocalizableStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AuthentiCodeNoTrustedOrg" xml:space="preserve">
<value>AuthentiCode signature for {0} does not belong to a trusted organization.</value>
<data name="UnableToCheckCertificateChainPolicy" xml:space="preserve">
<value>The requested certificate chain policy could not be checked: {0}</value>
</data>
</root>
63 changes: 23 additions & 40 deletions src/Cli/dotnet/Installer/Windows/MsiPackageCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography.X509Certificates;
#if !DOT_NET_BUILD_FROM_SOURCE
using Microsoft.DotNet.Installer.Windows.Security;
using Microsoft.Win32.Msi;
#endif
using Microsoft.DotNet.Workloads.Workload;
using Newtonsoft.Json;

namespace Microsoft.DotNet.Installer.Windows
Expand All @@ -17,6 +17,10 @@ namespace Microsoft.DotNet.Installer.Windows
internal class MsiPackageCache : InstallerBase
{
/// <summary>
/// Determines whether revocation checks can go online.
/// </summary>
private bool _allowOnlineRevocationChecks;

/// The root directory of the package cache where MSI workload packs are stored.
/// </summary>
public readonly string PackageCacheRoot;
Expand All @@ -27,6 +31,7 @@ public MsiPackageCache(InstallElevationContextBase elevationContext, ISetupLogge
PackageCacheRoot = string.IsNullOrWhiteSpace(packageCacheRoot)
? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "dotnet", "workloads")
: packageCacheRoot;
_allowOnlineRevocationChecks = SignCheck.AllowOnlineRevocationChecks();
}

/// <summary>
Expand Down Expand Up @@ -139,58 +144,36 @@ public bool TryGetMsiPathFromPackageData(string packageDataPath, out string msiP
}

/// <summary>
/// Verifies the AuthentiCode signature of an MSI package if the executing command itself is running
/// from a signed module.
/// Verifies that an MSI package contains an Authenticode signature that terminates in a trusted Microsoft root certificate.
/// </summary>
/// <param name="msiPath">The path of the MSI to verify.</param>
private void VerifyPackageSignature(string msiPath)
{
if (VerifySignatures)
{
bool isAuthentiCodeSigned = AuthentiCode.IsSigned(msiPath);

// Need to capture the error now as other OS calls might change the last error.
uint lastError = !isAuthentiCodeSigned ? unchecked((uint)Marshal.GetLastWin32Error()) : Error.SUCCESS;

bool isTrustedOrganization = AuthentiCode.IsSignedByTrustedOrganization(msiPath, AuthentiCode.TrustedOrganizations);

if (isAuthentiCodeSigned && isTrustedOrganization)
// MSI and authenticode verification only applies to Windows. NET only supports Win7 and later.
#if !DOT_NET_BUILD_FROM_SOURCE
#pragma warning disable CA1416
unsafe
{
Log?.LogMessage($"Successfully verified AuthentiCode signature for {msiPath}.");
}
else
{
// Summarize the failure and then report additional details.
Log?.LogMessage($"Failed to verify signature for {msiPath}. AuthentiCode signed: {isAuthentiCodeSigned}, Trusted organization: {isTrustedOrganization}.");
IEnumerable<X509Certificate2> certificates = AuthentiCode.GetCertificates(msiPath);
int result = Signature.IsAuthenticodeSigned(msiPath, _allowOnlineRevocationChecks);

// Dump all the certificates if there are any.
if (certificates.Any())
if (result != 0)
{
Log?.LogMessage($"Certificate(s):");

foreach (X509Certificate2 certificate in certificates)
{
Log?.LogMessage($" Subject={certificate.Subject}");
Log?.LogMessage($" Issuer={certificate.Issuer}");
Log?.LogMessage($" Not before={certificate.NotBefore}");
Log?.LogMessage($" Not after={certificate.NotAfter}");
Log?.LogMessage($" Thumbprint={certificate.Thumbprint}");
Log?.LogMessage($" Algorithm={certificate.SignatureAlgorithm.FriendlyName}");
}
ExitOnError((uint)result, $"Failed to verify Authenticode signature, package: {msiPath}, allow online revocation checks: {_allowOnlineRevocationChecks}");
}

if (!isAuthentiCodeSigned)
{
// If it was a WinTrust failure, we can exit using that error code and include a proper message from the OS.
ExitOnError(lastError, $"Failed to verify authenticode signature for {msiPath}.");
}
result = Signature.HasMicrosoftTrustedRoot(msiPath);

if (!isTrustedOrganization)
if (result != 0)
{
throw new SecurityException(string.Format(LocalizableStrings.AuthentiCodeNoTrustedOrg, msiPath));
ExitOnError((uint)result, $"Failed to verify the Authenticode signature terminates in a trusted Microsoft root certificate. Package: {msiPath}");
}

}
Log?.LogMessage($"Successfully verified Authenticode signature for {msiPath}");
#pragma warning restore CA1416
#endif
}
else
{
Expand Down
191 changes: 0 additions & 191 deletions src/Cli/dotnet/Installer/Windows/Security/AuthentiCode.cs

This file was deleted.

Loading