WindowsInstallerTests.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. using System.IO.Pipes;
  4. using System.Reflection;
  5. using System.Runtime.Versioning;
  6. using Microsoft.DotNet.Installer.Windows;
  7. using Microsoft.DotNet.Installer.Windows.Security;
  8. namespace Microsoft.DotNet.Tests
  9. {
  10. [SupportedOSPlatform("windows5.1.2600")]
  11. public class WindowsInstallerTests
  12. {
  13. private static string s_testDataPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "TestData");
  14. private void LogTask(string pipeName)
  15. {
  16. using NamedPipeServerStream serverPipe = CreateServerPipe(pipeName);
  17. PipeStreamSetupLogger logger = new(serverPipe, pipeName);
  18. logger.Connect();
  19. for (int i = 0; i < 10; i++)
  20. {
  21. logger.LogMessage($"Hello from {pipeName} ({i}).");
  22. }
  23. }
  24. [WindowsOnlyFact]
  25. public void MultipleProcessesCanWriteToTheLog()
  26. {
  27. var logFile = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
  28. TimestampedFileLogger logger = new(logFile);
  29. logger.AddNamedPipe("np1");
  30. logger.AddNamedPipe("np2");
  31. logger.AddNamedPipe("np3");
  32. var t1 = Task.Run(() => { LogTask("np1"); });
  33. var t2 = Task.Run(() => { LogTask("np2"); });
  34. var t3 = Task.Run(() => { LogTask("np3"); });
  35. Task.WaitAll(t1, t2, t3);
  36. logger.Dispose();
  37. string logContent = File.ReadAllText(logFile);
  38. Assert.Contains("Hello from np1", logContent);
  39. Assert.Contains("Hello from np2", logContent);
  40. Assert.Contains("Hello from np3", logContent);
  41. Assert.Contains("=== Logging ended ===", logContent);
  42. }
  43. [WindowsOnlyFact]
  44. public void InstallMessageDispatcherProcessesMessages()
  45. {
  46. string pipeName = Guid.NewGuid().ToString();
  47. NamedPipeServerStream serverPipe = CreateServerPipe(pipeName);
  48. NamedPipeClientStream clientPipe = new(".", pipeName, PipeDirection.InOut);
  49. InstallMessageDispatcher sd = new(serverPipe);
  50. InstallMessageDispatcher cd = new(clientPipe);
  51. Task.Run(() =>
  52. {
  53. ServerDispatcher server = new(sd);
  54. server.Run();
  55. });
  56. cd.Connect();
  57. InstallResponseMessage r1 = cd.SendMsiRequest(InstallRequestType.UninstallMsi, "");
  58. InstallResponseMessage r2 = cd.SendShutdownRequest();
  59. Assert.Equal("Received request: UninstallMsi", r1.Message);
  60. Assert.Equal("Shutting down!", r2.Message);
  61. }
  62. [WindowsOnlyTheory]
  63. [InlineData("1033,1041,1049", UpgradeAttributes.MigrateFeatures, 1041, false)]
  64. [InlineData(null, UpgradeAttributes.LanguagesExclusive, 3082, false)]
  65. [InlineData("1033,1041,1049", UpgradeAttributes.LanguagesExclusive, 1033, true)]
  66. public void RelatedProductExcludesLanguages(string language, UpgradeAttributes attributes, int lcid,
  67. bool expectedResult)
  68. {
  69. RelatedProduct rp = new()
  70. {
  71. Attributes = attributes,
  72. Language = language
  73. };
  74. Assert.Equal(expectedResult, rp.ExcludesLanguage(lcid));
  75. }
  76. [WindowsOnlyTheory]
  77. [InlineData("72.13.638", UpgradeAttributes.MigrateFeatures, "72.13.639", true)]
  78. [InlineData("72.13.638", UpgradeAttributes.VersionMaxInclusive, "72.13.638", false)]
  79. public void RelatedProductExcludesMaxVersion(string maxVersion, UpgradeAttributes attributes, string installedVersionValue,
  80. bool expectedResult)
  81. {
  82. Version installedVersion = new(installedVersionValue);
  83. RelatedProduct rp = new()
  84. {
  85. Attributes = attributes,
  86. VersionMax = maxVersion == null ? null : new Version(maxVersion),
  87. VersionMin = null
  88. };
  89. Assert.Equal(expectedResult, rp.ExcludesMaxVersion(installedVersion));
  90. }
  91. [WindowsOnlyTheory]
  92. [InlineData("72.13.638", UpgradeAttributes.MigrateFeatures, "72.13.638", true)]
  93. [InlineData("72.13.638", UpgradeAttributes.VersionMinInclusive, "72.13.638", false)]
  94. public void RelatedProductExcludesMinVersion(string minVersion, UpgradeAttributes attributes, string installedVersionValue,
  95. bool expectedResult)
  96. {
  97. Version installedVersion = new(installedVersionValue);
  98. RelatedProduct rp = new()
  99. {
  100. Attributes = attributes,
  101. VersionMin = minVersion == null ? null : new Version(minVersion),
  102. VersionMax = null
  103. };
  104. Assert.Equal(expectedResult, rp.ExcludesMinVersion(installedVersion));
  105. }
  106. [WindowsOnlyTheory]
  107. // This verifies E_TRUST_BAD_DIGEST (file was modified after being signed)
  108. [InlineData(@"tampered.msi", -2146869232)]
  109. [InlineData(@"dual_signed.dll", 0)]
  110. [InlineData(@"dotnet_realsigned.exe", 0)]
  111. // Signed by the .NET Foundation, terminates in a DigiCert root, so should be accepted by the Authenticode trust provider.
  112. [InlineData(@"BootstrapperCore.dll", 0)]
  113. // Old SHA1 certificate, but still a valid signature.
  114. [InlineData(@"system.web.mvc.dll", 0)]
  115. public void AuthentiCodeSignaturesCanBeVerified(string file, int expectedStatus)
  116. {
  117. int status = Signature.IsAuthenticodeSigned(Path.Combine(s_testDataPath, file));
  118. Assert.Equal(expectedStatus, status);
  119. }
  120. [WindowsOnlyTheory]
  121. [InlineData(@"dotnet_realsigned.exe", 0)]
  122. // Valid SHA1 signature, but no longer considered a trusted root certificate, should return CERT_E_UNTRUSTEDROOT.
  123. [InlineData(@"system.web.mvc.dll", -2146762487)]
  124. // The first certificate chain terminates in a non-Microsoft root so it fails the policy. Workloads do not currently support
  125. // 3rd party installers. If we change that policy and we sign installers with the Microsoft 3rd Party certificate we will need to extract the nested
  126. // signature and verify that at least one chain terminates in a Microsoft root. The WinTrust logic will also need to be updated to verify each
  127. // chain.
  128. [InlineData(@"dual_signed.dll", -2146762487)]
  129. // DigiCert root should fail the policy check because it's not a trusted Microsoft root certificate.
  130. [InlineData(@"BootstrapperCore.dll", -2146762487)]
  131. // Digest will fail verification, BUT the root certificate in the chain is a trusted root.
  132. [InlineData(@"tampered.msi", 0)]
  133. public void ItVerifiesTrustedMicrosoftRootCertificateChainPolicy(string file, int expectedResult)
  134. {
  135. int result = Signature.HasMicrosoftTrustedRoot(Path.Combine(s_testDataPath, file));
  136. Assert.Equal(expectedResult, result);
  137. }
  138. private NamedPipeServerStream CreateServerPipe(string name)
  139. {
  140. return new NamedPipeServerStream(name, PipeDirection.InOut, 1, PipeTransmissionMode.Message);
  141. }
  142. }
  143. [SupportedOSPlatform("windows")]
  144. internal class ServerDispatcher
  145. {
  146. InstallMessageDispatcher _dispatcher;
  147. public ServerDispatcher(InstallMessageDispatcher dispatcher)
  148. {
  149. _dispatcher = dispatcher;
  150. }
  151. public void Run()
  152. {
  153. _dispatcher.Connect();
  154. bool done = false;
  155. while (!done)
  156. {
  157. if (_dispatcher == null || !_dispatcher.IsConnected)
  158. {
  159. throw new IOException("Server dispatcher disconnected or not initialized.");
  160. }
  161. var request = _dispatcher.ReceiveRequest();
  162. if (request.RequestType == InstallRequestType.Shutdown)
  163. {
  164. done = true;
  165. _dispatcher.ReplySuccess("Shutting down!");
  166. }
  167. else
  168. {
  169. _dispatcher.ReplySuccess($"Received request: {request.RequestType}");
  170. }
  171. }
  172. }
  173. }
  174. }