Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server certificate pinning for Store source #2347

Merged
merged 20 commits into from
Jul 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
7 changes: 6 additions & 1 deletion .github/actions/spelling/allow.txt
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ cref
csproj
CStr
csv
ctl
ctls
CURSORPOSITON
CUSTOMHEADER
cwctype
Expand Down Expand Up @@ -360,7 +362,7 @@ nuspec
OAuth
ODR
ofstream
Oid
oid
opencode
opensource
openxmlformats
Expand Down Expand Up @@ -432,6 +434,7 @@ refactor
refactored
refactoring
REFCLSID
REFCOUNT
regex
regexp
removemanifest
Expand Down Expand Up @@ -646,8 +649,10 @@ WHOLECHAIN
wil
wildcards
WINAPI
wincrypt
WINEVENT
winget
winhttp
wininet
winmd
winmeta
Expand Down
10 changes: 10 additions & 0 deletions .github/actions/spelling/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ kayone
Keivan
KF
KNOWNFOLDERID
kp
ktf
langs
LATN
Expand Down Expand Up @@ -275,7 +276,9 @@ myinstalldir
mylog
mysilent
mysilentwithprogress
mytool
nameof
nativehandle
NESTEDINSTALLER
NETFX
netlify
Expand Down Expand Up @@ -305,6 +308,7 @@ PACL
pathparts
Patil
pb
PCCERT
PCs
pcwsz
pdp
Expand All @@ -316,6 +320,7 @@ pidl
pidlist
pkgmgr
pkindex
pkix
PMS
positionals
powershellgallery
Expand All @@ -329,6 +334,7 @@ pseudocode
psm
psobject
ptstr
publickey
pvk
pvm
pwabuilder
Expand All @@ -351,6 +357,8 @@ REGSAM
reparse
restsource
rgex
rgp
rgpsz
rhs
riid
roblox
Expand All @@ -367,6 +375,8 @@ sddl
semver
seof
serializer
servercert
servercertificate
setmetadatabymanifestid
SETTINGMAPPING
SHCONTF
Expand Down
8 changes: 8 additions & 0 deletions src/AppInstallerCLI.sln
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "COMServer", "COMServer\COMS
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Management.Deployment.InProc", "Microsoft.Management.Deployment.InProc\Microsoft.Management.Deployment.InProc.vcxproj", "{9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CertificateResources", "CertificateResources\CertificateResources.vcxitems", "{B0BBBD92-943B-408F-B2B2-DBBAB4A22D23}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PowerShell", "PowerShell", "{7C218A3E-9BC8-48FF-B91B-BCACD828C0C9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.WinGet.Client", "PowerShell\Microsoft.WinGet.Client\Microsoft.WinGet.Client.csproj", "{463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}"
Expand Down Expand Up @@ -1038,6 +1040,7 @@ Global
{866C3F06-636F-4BE8-BC24-5F86ECC606A1} = {60618CAC-2995-4DF9-9914-45C6FC02C995}
{1A47951F-5C7A-4D6D-BB5F-D77484437940} = {8D53D749-D51C-46F8-A162-9371AAA6C2E7}
{409CD681-22A4-469D-88AE-CB5E4836E07A} = {8D53D749-D51C-46F8-A162-9371AAA6C2E7}
{B0BBBD92-943B-408F-B2B2-DBBAB4A22D23} = {8D53D749-D51C-46F8-A162-9371AAA6C2E7}
{463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9}
{31ED69A8-5310-45A9-953F-56C351D2C3E1} = {60618CAC-2995-4DF9-9914-45C6FC02C995}
{787EC629-C0FB-4BA9-9746-4A82CD06B73E} = {60618CAC-2995-4DF9-9914-45C6FC02C995}
Expand All @@ -1051,22 +1054,27 @@ Global
WinGetSchemas\WinGetSchemas.vcxitems*{1c6e0108-2860-4b17-9f7e-fa5c6c1f3d3d}*SharedItemsImports = 4
COMServer\COMServer.vcxitems*{1cc41a9a-ae66-459d-9210-1e572dd7be69}*SharedItemsImports = 4
binver\binver.vcxitems*{2046b5af-666d-4ce8-8d3e-c32c57908a56}*SharedItemsImports = 4
CertificateResources\CertificateResources.vcxitems*{2046b5af-666d-4ce8-8d3e-c32c57908a56}*SharedItemsImports = 4
COMServer\COMServer.vcxitems*{2046b5af-666d-4ce8-8d3e-c32c57908a56}*SharedItemsImports = 4
ManifestSchema\ManifestSchema.vcxitems*{2046b5af-666d-4ce8-8d3e-c32c57908a56}*SharedItemsImports = 4
WinGetSchemas\WinGetSchemas.vcxitems*{2046b5af-666d-4ce8-8d3e-c32c57908a56}*SharedItemsImports = 4
Valijson\Valijson.vcxitems*{358bc478-0624-4ad1-a933-0422b5292af8}*SharedItemsImports = 9
COMServer\COMServer.vcxitems*{409cd681-22a4-469d-88ae-cb5e4836e07a}*SharedItemsImports = 9
catch2\catch2.vcxitems*{5295e21e-9868-4de2-a177-fbb97b36579b}*SharedItemsImports = 9
CertificateResources\CertificateResources.vcxitems*{5890d6ed-7c3b-40f3-b436-b54f640d9e65}*SharedItemsImports = 4
COMServer\COMServer.vcxitems*{5890d6ed-7c3b-40f3-b436-b54f640d9e65}*SharedItemsImports = 4
ManifestSchema\ManifestSchema.vcxitems*{5890d6ed-7c3b-40f3-b436-b54f640d9e65}*SharedItemsImports = 4
Valijson\Valijson.vcxitems*{5890d6ed-7c3b-40f3-b436-b54f640d9e65}*SharedItemsImports = 4
binver\binver.vcxitems*{5b6f90df-fd19-4bae-83d9-24dad128e777}*SharedItemsImports = 4
CertificateResources\CertificateResources.vcxitems*{5eb88068-5fb9-4e69-89b2-72dbc5e068f9}*SharedItemsImports = 4
binver\binver.vcxitems*{6e36ddd7-1602-474e-b1d7-d0a7e1d5ad86}*SharedItemsImports = 9
ManifestSchema\ManifestSchema.vcxitems*{7d05f64d-ce5a-42aa-a2c1-e91458f061cf}*SharedItemsImports = 9
catch2\catch2.vcxitems*{89b1aab4-2bbc-4b65-9ed7-a01d5cf88230}*SharedItemsImports = 4
CertificateResources\CertificateResources.vcxitems*{89b1aab4-2bbc-4b65-9ed7-a01d5cf88230}*SharedItemsImports = 4
ManifestSchema\ManifestSchema.vcxitems*{89b1aab4-2bbc-4b65-9ed7-a01d5cf88230}*SharedItemsImports = 4
WinGetSchemas\WinGetSchemas.vcxitems*{89b1aab4-2bbc-4b65-9ed7-a01d5cf88230}*SharedItemsImports = 4
WinGetSchemas\WinGetSchemas.vcxitems*{952b513f-8a00-4d74-9271-925afb3c6252}*SharedItemsImports = 9
CertificateResources\CertificateResources.vcxitems*{b0bbbd92-943b-408f-b2b2-dbbab4a22d23}*SharedItemsImports = 9
binver\binver.vcxitems*{fb313532-38b0-4676-9303-ab200aa13576}*SharedItemsImports = 4
ManifestSchema\ManifestSchema.vcxitems*{fb313532-38b0-4676-9303-ab200aa13576}*SharedItemsImports = 4
EndGlobalSection
Expand Down
6 changes: 4 additions & 2 deletions src/AppInstallerCLIE2ETests/BaseCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ public void BaseTeardown()
TestCommon.TearDownTestSource();
}

public void ResetTestSource()
// TODO: If/when cert pinning is implemented on the packaged index source, useGroupPolicyForTestSource should be set to default true
// to enable testing it by default. Until then, leaving this here...
public void ResetTestSource(bool useGroupPolicyForTestSource = false)
{
TestCommon.SetupTestSource();
TestCommon.SetupTestSource(useGroupPolicyForTestSource);
}

public void ConfigureFeature(string featureName, bool status)
Expand Down
11 changes: 11 additions & 0 deletions src/AppInstallerCLIE2ETests/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,14 @@ public class Constants
public const string DefaultWingetSourceUrl = @"https://winget.azureedge.net/cache";
public const string DefaultMSStoreSourceName = @"msstore";
public const string DefaultMSStoreSourceUrl = @"https://storeedgefd.dsx.mp.microsoft.com/v9.0";
public const string DefaultMSStoreSourceType = "Microsoft.Rest";
public const string DefaultMSStoreSourceIdentifier = "StoreEdgeFD";
public const string TestSourceName = @"TestSource";
public const string TestAlternateSourceName = @"TestSource2";
public const string TestSourceUrl = @"https://localhost:5001/TestKit";
public const string TestSourceType = "Microsoft.PreIndexed.Package";
public const string TestSourceIdentifier = @"WingetE2E.Tests_8wekyb3d8bbwe";
public const string TestSourceServerCertificateFileName = "servercert.cer";

public const string AICLIPackageFamilyName = "WinGetDevCLI_8wekyb3d8bbwe";
public const string AICLIPackageName = "WinGetDevCLI";
Expand Down Expand Up @@ -178,6 +183,12 @@ public class ErrorCode
public const int ERROR_INSTALLER_PROHIBITS_ELEVATION = unchecked((int)0x8A150056);
public const int ERROR_PORTABLE_UNINSTALL_FAILED = unchecked((int)0x8A150057);
public const int ERROR_ARP_VERSION_VALIDATION_FAILED = unchecked((int)0x8A150058);
public const int ERROR_UNSUPPORTED_ARGUMENT = unchecked((int)0x8A150059);
public const int ERROR_BIND_WITH_EMBEDDED_NULL = unchecked((int)0x8A15005A);
public const int ERROR_NESTEDINSTALLER_NOT_FOUND = unchecked((int)0x8A15005B);
public const int ERROR_EXTRACT_ARCHIVE_FAILED = unchecked((int)0x8A15005C);
public const int ERROR_NESTEDINSTALLER_INVALID_PATH = unchecked((int)0x8A15005D);
public const int ERROR_PINNED_CERTIFICATE_MISMATCH = unchecked((int)0x8A15005E);

public const int ERROR_INSTALL_PACKAGE_IN_USE = unchecked((int)0x8A150101);
public const int ERROR_INSTALL_INSTALL_IN_PROGRESS = unchecked((int)0x8A150102);
Expand Down
44 changes: 44 additions & 0 deletions src/AppInstallerCLIE2ETests/GroupPolicyHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using System.Linq;
using System.Xml.Linq;
using Microsoft.Win32;
using Newtonsoft.Json;
using NUnit.Framework;

namespace AppInstallerCLIE2ETests
{
Expand Down Expand Up @@ -269,12 +271,24 @@ public void SetEnabledList(IEnumerable<string> values)
int index = 0;
foreach (string value in values)
{
TestContext.Out.WriteLine($"Setting {name} list value: {value}");
listKey.SetValue(index++.ToString(), value);
}

listKey.Close();
}

/// <summary>
/// Sets the list value of the policy when enabled.
/// This sets from the "elements" and also sets the enabled value as lists are also gated by a toggle.
/// This will fail if the value of the policy is not a list.
/// </summary>
/// <param name="values">Values to set in the list.</param>
public void SetEnabledList(IEnumerable<GroupPolicySource> values)
{
SetEnabledList(values.Select(source => JsonConvert.SerializeObject(source)));
}

/// <summary>
/// Gets the value from a "decimal" child element.
/// </summary>
Expand All @@ -299,4 +313,34 @@ private RegistryKey GetKey()
return Registry.LocalMachine.CreateSubKey(this.KeyPath);
}
}

/// <summary>
/// A group policy source object as used by AdditionalSources and AllowedSources.
/// </summary>
public class GroupPolicySource
{
public string Name { get; set; }
public string Arg { get; set; }
public string Type { get; set; }
public string Data { get; set; }
public string Identifier { get; set; }
public GroupPolicyCertificatePinning CertificatePinning { get; set; }
}

public class GroupPolicyCertificatePinning
{
public GroupPolicyCertificatePinningChain[] Chains { get; set; }
}

public class GroupPolicyCertificatePinningChain
{
public GroupPolicyCertificatePinningDetails[] Chain { get; set; }
}

public class GroupPolicyCertificatePinningDetails
{
public string[] Validation { get; set; }
public string EmbeddedCertificate { get; set; }
}

}
73 changes: 67 additions & 6 deletions src/AppInstallerCLIE2ETests/SearchCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,72 @@ public void SearchWithSingleSourceFailure()
{
TestCommon.RunAICLICommand("source add", "failSearch \"{ \"\"OpenHR\"\": \"\"0x80070002\"\" }\" Microsoft.Test.Configurable --header \"{}\"");

var result = TestCommon.RunAICLICommand("search", "--exact AppInstallerTest.TestExampleInstaller");
Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode);
Assert.True(result.StdOut.Contains("Failed when searching source; results will not be included: failSearch"));
Assert.True(result.StdOut.Contains("TestExampleInstaller"));
Assert.True(result.StdOut.Contains("AppInstallerTest.TestExampleInstaller"));
try
{
var result = TestCommon.RunAICLICommand("search", "--exact AppInstallerTest.TestExampleInstaller");
Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode);
Assert.True(result.StdOut.Contains("Failed when searching source; results will not be included: failSearch"));
Assert.True(result.StdOut.Contains("TestExampleInstaller"));
Assert.True(result.StdOut.Contains("AppInstallerTest.TestExampleInstaller"));
}
finally
{
ResetTestSource();
}
}

[Test]
public void SearchStoreWithBadPin()
{
// Configure as close as possible to the real chain but use the test cert for everything
// This will at least force the public key to be checked rather than simply failing based on chain length
GroupPolicyHelper.EnableAdditionalSources.SetEnabledList(new GroupPolicySource[]
{
new GroupPolicySource
{
Name = Constants.TestAlternateSourceName,
Arg = Constants.DefaultMSStoreSourceUrl,
Type = Constants.DefaultMSStoreSourceType,
Data = "",
Identifier = Constants.DefaultMSStoreSourceIdentifier,
CertificatePinning = new GroupPolicyCertificatePinning
{
Chains = new GroupPolicyCertificatePinningChain[] {
new GroupPolicyCertificatePinningChain
{
Chain = new GroupPolicyCertificatePinningDetails[]
{
new GroupPolicyCertificatePinningDetails
{
Validation = new string[] { "publickey" },
EmbeddedCertificate = TestCommon.GetTestServerCertificateHexString()
},
new GroupPolicyCertificatePinningDetails
{
Validation = new string[] { "subject", "issuer" },
EmbeddedCertificate = TestCommon.GetTestServerCertificateHexString()
},
new GroupPolicyCertificatePinningDetails
{
Validation = new string[] { "subject", "issuer" },
EmbeddedCertificate = TestCommon.GetTestServerCertificateHexString()
}
}
}
}
}
}
});

try
{
var result = TestCommon.RunAICLICommand("search", $"-s {Constants.TestAlternateSourceName} foo --verbose-logs");
Assert.AreEqual(Constants.ErrorCode.ERROR_PINNED_CERTIFICATE_MISMATCH, result.ExitCode);
}
finally
{
ResetTestSource();
}
}
}
}
}
2 changes: 1 addition & 1 deletion src/AppInstallerCLIE2ETests/SetUpFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ public void InitializeWingetSettings()
debugging = new
{
enableSelfInitiatedMinidump = true
}
},
};

// Run winget one time to initialize settings directory
Expand Down
4 changes: 2 additions & 2 deletions src/AppInstallerCLIE2ETests/SourceCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class SourceCommand : BaseCommand
[SetUp]
public void Setup()
{
ResetTestSource();
ResetTestSource(false);
}

[Test]
Expand Down Expand Up @@ -100,7 +100,7 @@ public void SourceRemoveValidName()
var result = TestCommon.RunAICLICommand("source remove", $"-n {Constants.TestSourceName}");
Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode);
Assert.True(result.StdOut.Contains("Done"));
ResetTestSource();
ResetTestSource(false);
}

[Test]
Expand Down
Loading