1 // Copyright (c) Microsoft. All rights reserved. 2 // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 4 using Microsoft.Build.Framework; 5 using Microsoft.Build.Utilities; 6 using Microsoft.Win32; 7 using System; 8 using System.Collections; 9 using System.Collections.Generic; 10 using System.ComponentModel; 11 using System.Deployment.Internal.CodeSigning; 12 using System.Diagnostics; 13 using System.Diagnostics.CodeAnalysis; 14 using System.Globalization; 15 using System.IO; 16 using System.Reflection; 17 using System.Runtime.InteropServices; 18 using System.Security; 19 using System.Security.Cryptography; 20 using System.Security.Cryptography.X509Certificates; 21 using System.Security.Permissions; 22 using System.Security.Policy; 23 using System.Text; 24 using System.Xml; 25 using FrameworkNameVersioning = System.Runtime.Versioning.FrameworkName; 26 27 namespace Microsoft.Build.Tasks.Deployment.ManifestUtilities 28 { 29 /// <summary> 30 /// Provides a set of utility functions for manipulating security permision sets and signing. 31 /// </summary> 32 [ComVisible(false)] 33 public static class SecurityUtilities 34 { 35 private const string PermissionSetsFolder = "PermissionSets"; 36 private const string Everything = "Everything"; 37 private const string LocalIntranet = "LocalIntranet"; 38 private const string Internet = "Internet"; 39 private const string Custom = "Custom"; 40 private const string ToolName = "signtool.exe"; 41 private const int Fx2MajorVersion = 2; 42 private const int Fx3MajorVersion = 3; 43 private static readonly Version s_dotNet40Version = new Version("4.0"); 44 private static readonly Version s_dotNet45Version = new Version("4.5"); 45 46 private const string InternetPermissionSetXml = "<PermissionSet class=\"System.Security.PermissionSet\" version=\"1\" ID=\"Custom\" SameSite=\"site\">\n" + 47 "<IPermission class=\"System.Security.Permissions.FileDialogPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Access=\"Open\" />\n" + 48 "<IPermission class=\"System.Security.Permissions.IsolatedStorageFilePermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Allowed=\"ApplicationIsolationByUser\" UserQuota=\"512000\" />\n" + 49 "<IPermission class=\"System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Flags=\"Execution\" />\n" + 50 "<IPermission class=\"System.Security.Permissions.UIPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Window=\"SafeTopLevelWindows\" Clipboard=\"OwnClipboard\" />\n" + 51 "<IPermission class=\"System.Drawing.Printing.PrintingPermission, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a\" version=\"1\" Level=\"SafePrinting\" />\n" + 52 "</PermissionSet>"; 53 54 private const string LocalIntranetPermissionSetXml = "<PermissionSet class=\"System.Security.PermissionSet\" version=\"1\" ID=\"Custom\" SameSite=\"site\">\n" + 55 "<IPermission class=\"System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Read=\"USERNAME\" />\n" + 56 "<IPermission class=\"System.Security.Permissions.FileDialogPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Unrestricted=\"true\" />\n" + 57 "<IPermission class=\"System.Security.Permissions.IsolatedStorageFilePermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Allowed=\"AssemblyIsolationByUser\" UserQuota=\"9223372036854775807\" Expiry=\"9223372036854775807\" Permanent=\"True\" />\n" + 58 "<IPermission class=\"System.Security.Permissions.ReflectionPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Flags=\"ReflectionEmit\" />\n" + 59 "<IPermission class=\"System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Flags=\"Assertion, Execution, BindingRedirects\" />\n" + 60 "<IPermission class=\"System.Security.Permissions.UIPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Unrestricted=\"true\" />\n" + 61 "<IPermission class=\"System.Net.DnsPermission, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Unrestricted=\"true\" />\n" + 62 "<IPermission class=\"System.Drawing.Printing.PrintingPermission, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a\" version=\"1\" Level=\"DefaultPrinting\" />\n" + 63 "</PermissionSet>"; 64 65 private const string InternetPermissionSetWithWPFXml = "<PermissionSet class=\"System.Security.PermissionSet\" version=\"1\" ID=\"Custom\" SameSite=\"site\">\n" + 66 "<IPermission class=\"System.Security.Permissions.FileDialogPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Access=\"Open\" />\n" + 67 "<IPermission class=\"System.Security.Permissions.IsolatedStorageFilePermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Allowed=\"ApplicationIsolationByUser\" UserQuota=\"512000\" />\n" + 68 "<IPermission class=\"System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Flags=\"Execution\" />\n" + 69 "<IPermission class=\"System.Security.Permissions.UIPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Window=\"SafeTopLevelWindows\" Clipboard=\"OwnClipboard\" />\n" + 70 "<IPermission class=\"System.Drawing.Printing.PrintingPermission, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a\" version=\"1\" Level=\"SafePrinting\" />\n" + 71 "<IPermission class=\"System.Security.Permissions.MediaPermission, WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" version=\"1\" Audio=\"SafeAudio\" Video=\"SafeVideo\" Image=\"SafeImage\" />\n" + 72 "<IPermission class=\"System.Security.Permissions.WebBrowserPermission, WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" version=\"1\" Level=\"Safe\" />\n" + 73 "</PermissionSet>"; 74 75 private const string LocalIntranetPermissionSetWithWPFXml = "<PermissionSet class=\"System.Security.PermissionSet\" version=\"1\" ID=\"Custom\" SameSite=\"site\">\n" + 76 "<IPermission class=\"System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Read=\"USERNAME\" />\n" + 77 "<IPermission class=\"System.Security.Permissions.FileDialogPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Unrestricted=\"true\" />\n" + 78 "<IPermission class=\"System.Security.Permissions.IsolatedStorageFilePermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Allowed=\"AssemblyIsolationByUser\" UserQuota=\"9223372036854775807\" Expiry=\"9223372036854775807\" Permanent=\"True\" />\n" + 79 "<IPermission class=\"System.Security.Permissions.ReflectionPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Flags=\"ReflectionEmit\" />\n" + 80 "<IPermission class=\"System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Flags=\"Assertion, Execution, BindingRedirects\" />\n" + 81 "<IPermission class=\"System.Security.Permissions.UIPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Unrestricted=\"true\" />\n" + 82 "<IPermission class=\"System.Net.DnsPermission, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" version=\"1\" Unrestricted=\"true\" />\n" + 83 "<IPermission class=\"System.Drawing.Printing.PrintingPermission, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a\" version=\"1\" Level=\"DefaultPrinting\" />\n" + 84 "<IPermission class=\"System.Security.Permissions.MediaPermission, WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" version=\"1\" Audio=\"SafeAudio\" Video=\"SafeVideo\" Image=\"SafeImage\" />\n" + 85 "<IPermission class=\"System.Security.Permissions.WebBrowserPermission, WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" version=\"1\" Level=\"Safe\" />\n" + 86 "</PermissionSet>"; 87 88 /// <summary> 89 /// Generates a permission set by computed the zone default permission set and adding any included permissions. 90 /// </summary> 91 /// <param name="targetZone">Specifies a zone default permission set, which is obtained from machine policy. Valid values are "Internet", "LocalIntranet", or "Custom". If "Custom" is specified, the generated permission set is based only on the includedPermissionSet parameter.</param> 92 /// <param name="includedPermissionSet">A PermissionSet object containing the set of permissions to be explicitly included in the generated permission set. Permissions specified in this parameter will be included verbatim in the generated permission set, regardless of targetZone parameter.</param> 93 /// <param name="excludedPermissions">This property is no longer used.</param> 94 /// <returns>The generated permission set.</returns> ComputeZonePermissionSet(string targetZone, PermissionSet includedPermissionSet, string[] excludedPermissions)95 public static PermissionSet ComputeZonePermissionSet(string targetZone, PermissionSet includedPermissionSet, string[] excludedPermissions) 96 { 97 return ComputeZonePermissionSetHelper(targetZone, includedPermissionSet, null, string.Empty); 98 } 99 ComputeZonePermissionSetHelper(string targetZone, PermissionSet includedPermissionSet, ITaskItem[] dependencies, string targetFrameworkMoniker)100 internal static PermissionSet ComputeZonePermissionSetHelper(string targetZone, PermissionSet includedPermissionSet, ITaskItem[] dependencies, string targetFrameworkMoniker) 101 { 102 // Custom Set. 103 if (String.IsNullOrEmpty(targetZone) || String.Equals(targetZone, Custom, StringComparison.OrdinalIgnoreCase)) 104 { 105 // just return the included set, no magic 106 return includedPermissionSet.Copy(); 107 } 108 109 PermissionSet retSet = GetNamedPermissionSetFromZone(targetZone, dependencies, targetFrameworkMoniker); 110 111 return retSet; 112 } 113 GetNamedPermissionSetFromZone(string targetZone, ITaskItem[] dependencies, string targetFrameworkMoniker)114 private static PermissionSet GetNamedPermissionSetFromZone(string targetZone, ITaskItem[] dependencies, string targetFrameworkMoniker) 115 { 116 switch (targetZone) 117 { 118 case LocalIntranet: 119 return GetNamedPermissionSet(LocalIntranet, dependencies, targetFrameworkMoniker); 120 case Internet: 121 return GetNamedPermissionSet(Internet, dependencies, targetFrameworkMoniker); 122 default: 123 throw new ArgumentException(String.Empty /* no message */, "targetZone"); 124 } 125 } 126 GetNamedPermissionSet(string targetZone, ITaskItem[] dependencies, string targetFrameworkMoniker)127 private static PermissionSet GetNamedPermissionSet(string targetZone, ITaskItem[] dependencies, string targetFrameworkMoniker) 128 { 129 FrameworkNameVersioning fn = null; 130 131 if (!string.IsNullOrEmpty(targetFrameworkMoniker)) 132 { 133 fn = new FrameworkNameVersioning(targetFrameworkMoniker); 134 } 135 else 136 { 137 fn = new FrameworkNameVersioning(".NETFramework", s_dotNet40Version); 138 } 139 140 int majorVersion = fn.Version.Major; 141 142 if (majorVersion == Fx2MajorVersion) 143 { 144 return SecurityUtilities.XmlToPermissionSet((GetXmlElement(targetZone, majorVersion))); 145 } 146 else if (majorVersion == Fx3MajorVersion) 147 { 148 return SecurityUtilities.XmlToPermissionSet((GetXmlElement(targetZone, majorVersion))); 149 } 150 else 151 { 152 return SecurityUtilities.XmlToPermissionSet((GetXmlElement(targetZone, fn))); 153 } 154 } 155 GetXmlElement(string targetZone, FrameworkNameVersioning fn)156 private static XmlElement GetXmlElement(string targetZone, FrameworkNameVersioning fn) 157 { 158 IList<string> paths = ToolLocationHelper.GetPathToReferenceAssemblies(fn); 159 160 // Is the targeted CLR even installed? 161 if (paths.Count > 0) 162 { 163 // first one is always framework requested. 164 string path = Path.Combine(paths[0], PermissionSetsFolder); 165 166 // PermissionSets folder doesn't exit 167 if (Directory.Exists(path)) 168 { 169 string[] files = Directory.GetFiles(path, "*.xml"); 170 FileInfo[] filesInfo = new FileInfo[files.Length]; 171 172 int indexFound = -1; 173 174 // trim the extension. 175 for (int i = 0; i < files.Length; i++) 176 { 177 filesInfo[i] = new FileInfo(files[i]); 178 179 string fileInfoNoExt = Path.GetFileNameWithoutExtension(files[i]); 180 181 if (string.Equals(fileInfoNoExt, targetZone, StringComparison.OrdinalIgnoreCase)) 182 { 183 indexFound = i; 184 break; 185 } 186 } 187 188 if (indexFound != -1) 189 { 190 string data = string.Empty; 191 FileInfo resultFile = filesInfo[indexFound]; 192 using (FileStream fs = resultFile.OpenRead()) 193 { 194 try 195 { 196 StreamReader sr = new StreamReader(fs); 197 data = sr.ReadToEnd(); // fs.Position value will be the length of the stream. 198 if (!string.IsNullOrEmpty(data)) 199 { 200 XmlDocument doc = new XmlDocument(); 201 XmlReaderSettings xrSettings = new XmlReaderSettings(); 202 xrSettings.DtdProcessing = DtdProcessing.Ignore; 203 204 // http://msdn.microsoft.com/en-us/library/h2344bs2(v=vs.110).aspx 205 // PermissionSets do not conform to document level, which is the default setting. 206 xrSettings.ConformanceLevel = ConformanceLevel.Auto; 207 try 208 { 209 fs.Position = 0; // Reset to 0 before using this stream in any other reader. 210 using (XmlReader xr = XmlReader.Create(fs, xrSettings)) 211 { 212 doc.Load(xr); 213 return (XmlElement)doc.DocumentElement; 214 } 215 } 216 catch (Exception) 217 { 218 //continue. 219 } 220 } 221 } 222 catch (ArgumentException) 223 { 224 //continue. 225 } 226 } 227 } 228 } 229 } 230 231 #if !MONO 232 return GetCurrentCLRPermissions(targetZone); 233 #else 234 throw new NotImplementedException(); 235 #endif 236 } 237 238 #if !MONO 239 [SuppressMessage("Microsoft.Security.Xml", "CA3057: DoNotUseLoadXml.")] GetCurrentCLRPermissions(string targetZone)240 private static XmlElement GetCurrentCLRPermissions(string targetZone) 241 { 242 string resultInString = string.Empty; 243 SecurityZone zone = SecurityZone.NoZone; 244 switch (targetZone) 245 { 246 case LocalIntranet: 247 zone = SecurityZone.Intranet; 248 break; 249 case Internet: 250 zone = SecurityZone.Internet; 251 break; 252 default: 253 throw new ArgumentException(String.Empty /* no message */, "targetZone"); 254 } 255 256 Evidence evidence = new Evidence(new EvidenceBase[] { new Zone(zone), new System.Runtime.Hosting.ActivationArguments(new System.ApplicationIdentity("")) }, null); 257 258 PermissionSet sandbox = SecurityManager.GetStandardSandbox(evidence); 259 resultInString = sandbox.ToString(); 260 261 if (!string.IsNullOrEmpty(resultInString)) 262 { 263 XmlDocument doc = new XmlDocument(); 264 // CA3057: DoNotUseLoadXml. Suppressed since the xml being loaded is a string representation of the PermissionSet. 265 doc.LoadXml(resultInString); 266 267 return (XmlElement)doc.DocumentElement; 268 } 269 270 return null; 271 } 272 #endif 273 GetXmlElement(string targetZone, int majorVersion)274 private static XmlElement GetXmlElement(string targetZone, int majorVersion) 275 { 276 XmlDocument doc = null; 277 278 switch (majorVersion) 279 { 280 case Fx2MajorVersion: 281 doc = CreateXmlDocV2(targetZone); 282 break; 283 case Fx3MajorVersion: 284 doc = CreateXmlDocV3(targetZone); 285 break; 286 default: 287 throw new ArgumentException(String.Empty /* no message */, "majorVersion"); 288 } 289 290 XmlElement rootElement = (XmlElement)doc.DocumentElement; 291 292 if (rootElement == null) 293 return null; 294 295 return rootElement; 296 } 297 298 [SuppressMessage("Microsoft.Security.Xml", "CA3057: DoNotUseLoadXml.")] CreateXmlDocV2(string targetZone)299 private static XmlDocument CreateXmlDocV2(string targetZone) 300 { 301 XmlDocument doc = new XmlDocument(); 302 303 switch (targetZone) 304 { 305 case LocalIntranet: 306 // CA3057: DoNotUseLoadXml. Suppressed since is LocalIntranetPermissionSetXml a constant string. 307 doc.LoadXml(LocalIntranetPermissionSetXml); 308 return doc; 309 case Internet: 310 // CA3057: DoNotUseLoadXml. Suppressed since is InternetPermissionSetXml a constant string. 311 doc.LoadXml(InternetPermissionSetXml); 312 return doc; 313 default: 314 throw new ArgumentException(String.Empty /* no message */, "targetZone"); 315 } 316 } 317 318 [SuppressMessage("Microsoft.Security.Xml", "CA3057: DoNotUseLoadXml.")] CreateXmlDocV3(string targetZone)319 private static XmlDocument CreateXmlDocV3(string targetZone) 320 { 321 XmlDocument doc = new XmlDocument(); 322 323 switch (targetZone) 324 { 325 case LocalIntranet: 326 // CA3057: DoNotUseLoadXml. Suppressed since is LocalIntranetPermissionSetXml a constant string. 327 doc.LoadXml(LocalIntranetPermissionSetWithWPFXml); 328 return doc; 329 case Internet: 330 // CA3057: DoNotUseLoadXml. Suppressed since is InternetPermissionSetXml a constant string. 331 doc.LoadXml(InternetPermissionSetWithWPFXml); 332 return doc; 333 default: 334 throw new ArgumentException(String.Empty /* no message */, "targetZone"); 335 } 336 } 337 GetRegistryPermissionSetByName(string name)338 private static string[] GetRegistryPermissionSetByName(string name) 339 { 340 string[] extensibleNamedPermissionSetRegistryInfo = null; 341 RegistryKey localMachineKey = Registry.LocalMachine; 342 343 using (RegistryKey versionIndependentFXKey = localMachineKey.OpenSubKey(@"Software\Microsoft\.NETFramework", false)) 344 { 345 if (versionIndependentFXKey != null) 346 { 347 using (RegistryKey namedPermissionSetsKey = versionIndependentFXKey.OpenSubKey(@"Security\Policy\Extensions\NamedPermissionSets", false)) 348 { 349 if (namedPermissionSetsKey != null) 350 { 351 using (RegistryKey permissionSetKey = namedPermissionSetsKey.OpenSubKey(name, false)) 352 { 353 if (permissionSetKey != null) 354 { 355 string[] permissionKeys = permissionSetKey.GetSubKeyNames(); 356 extensibleNamedPermissionSetRegistryInfo = new string[permissionKeys.Length]; 357 for (int i = 0; i < permissionKeys.Length; i++) 358 { 359 using (RegistryKey permissionKey = permissionSetKey.OpenSubKey(permissionKeys[i], false)) 360 { 361 string permissionXml = permissionKey.GetValue("Xml") as string; 362 extensibleNamedPermissionSetRegistryInfo[i] = permissionXml; 363 } 364 } 365 } 366 } 367 } 368 } 369 } 370 } 371 return extensibleNamedPermissionSetRegistryInfo; 372 } 373 374 #if !MONO RemoveNonReferencedPermissions(string[] setToFilter, ITaskItem[] dependencies)375 private static PermissionSet RemoveNonReferencedPermissions(string[] setToFilter, ITaskItem[] dependencies) 376 { 377 PermissionSet retSet = new PermissionSet(PermissionState.None); 378 if (dependencies == null || setToFilter == null || setToFilter.Length == 0) 379 return retSet; 380 381 List<string> assemblyNameList = new List<string>(); 382 foreach (ITaskItem dependency in dependencies) 383 { 384 AssemblyName dependentAssemblyName = AssemblyName.GetAssemblyName(dependency.ItemSpec); 385 assemblyNameList.Add(dependentAssemblyName.Name + ", " + dependentAssemblyName.Version.ToString()); 386 } 387 SecurityElement retSetElement = retSet.ToXml(); 388 foreach (string permissionXml in setToFilter) 389 { 390 if (!String.IsNullOrEmpty(permissionXml)) 391 { 392 string permissionAssemblyName; 393 string className; 394 string assemblyVersion; 395 396 SecurityElement permission = SecurityElement.FromString(permissionXml); 397 398 if (!ParseElementForAssemblyIdentification(permission, out className, out permissionAssemblyName, out assemblyVersion)) 399 continue; 400 if (assemblyNameList.Contains(permissionAssemblyName + ", " + assemblyVersion)) 401 { 402 retSetElement.AddChild(SecurityElement.FromString(permissionXml)); 403 } 404 } 405 } 406 retSet = new ReadOnlyPermissionSet(retSetElement); 407 return retSet; 408 } 409 #endif 410 ParseElementForAssemblyIdentification(SecurityElement el, out String className, out String assemblyName, out String assemblyVersion)411 internal static bool ParseElementForAssemblyIdentification(SecurityElement el, 412 out String className, 413 out String assemblyName, // for example "WindowsBase" 414 out String assemblyVersion) 415 { 416 className = null; 417 assemblyName = null; 418 assemblyVersion = null; 419 420 String fullClassName = el.Attribute("class"); 421 422 if (fullClassName == null) 423 { 424 return false; 425 } 426 if (fullClassName.IndexOf('\'') >= 0) 427 { 428 fullClassName = fullClassName.Replace('\'', '\"'); 429 } 430 431 int commaIndex = fullClassName.IndexOf(','); 432 int namespaceClassNameLength; 433 434 // If the classname is tagged with assembly information, find where 435 // the assembly information begins. 436 437 if (commaIndex == -1) 438 { 439 return false; 440 } 441 442 namespaceClassNameLength = commaIndex; 443 className = fullClassName.Substring(0, namespaceClassNameLength); 444 String assemblyFullName = fullClassName.Substring(commaIndex + 1); 445 AssemblyName an = new AssemblyName(assemblyFullName); 446 assemblyName = an.Name; 447 assemblyVersion = an.Version.ToString(); 448 return true; 449 } 450 451 452 /// <summary> 453 /// Converts an array of permission identity strings to a permission set object. 454 /// </summary> 455 /// <param name="ids">An array of permission identity strings.</param> 456 /// <returns>The converted permission set.</returns> IdentityListToPermissionSet(string[] ids)457 public static PermissionSet IdentityListToPermissionSet(string[] ids) 458 { 459 XmlDocument document = new XmlDocument(); 460 XmlElement permissionSetElement = document.CreateElement("PermissionSet"); 461 document.AppendChild(permissionSetElement); 462 foreach (string id in ids) 463 { 464 XmlElement permissionElement = document.CreateElement("IPermission"); 465 XmlAttribute a = document.CreateAttribute("class"); 466 a.Value = id; 467 permissionElement.Attributes.Append(a); 468 permissionSetElement.AppendChild(permissionElement); 469 } 470 return XmlToPermissionSet(permissionSetElement); 471 } 472 473 /// <summary> 474 /// Converts a permission set object to an array of permission identity strings. 475 /// </summary> 476 /// <param name="permissionSet">The input permission set to be converted.</param> 477 /// <returns>An array of permission identity strings.</returns> 478 [SuppressMessage("Microsoft.Security.Xml", "CA3057: DoNotUseLoadXml.")] PermissionSetToIdentityList(PermissionSet permissionSet)479 public static string[] PermissionSetToIdentityList(PermissionSet permissionSet) 480 { 481 string psXml = permissionSet != null ? permissionSet.ToString() : "<PermissionSet/>"; 482 XmlDocument psDocument = new XmlDocument(); 483 // CA3057: DoNotUseLoadXml. Suppressed since 'psXml' is a trusted or a constant string. 484 psDocument.LoadXml(psXml); 485 return XmlToIdentityList(psDocument.DocumentElement); 486 } 487 488 [SuppressMessage("Microsoft.Security.Xml", "CA3057: DoNotUseLoadXml.")] PermissionSetToXml(PermissionSet ps)489 internal static XmlDocument PermissionSetToXml(PermissionSet ps) 490 { 491 XmlDocument inputDocument = new XmlDocument(); 492 string xml = (ps != null) ? ps.ToString() : "<PermissionSet/>"; 493 494 // CA3057: DoNotUseLoadXml. Suppressed since 'xml' is a trusted or a constant string. 495 inputDocument.LoadXml(xml); 496 XmlDocument outputDocument = new XmlDocument(); 497 XmlElement psElement = XmlUtil.CloneElementToDocument(inputDocument.DocumentElement, outputDocument, XmlNamespaces.asmv2); 498 outputDocument.AppendChild(psElement); 499 return outputDocument; 500 } 501 XmlElementToSecurityElement(XmlElement xe)502 private static SecurityElement XmlElementToSecurityElement(XmlElement xe) 503 { 504 SecurityElement se = new SecurityElement(xe.Name); 505 foreach (XmlAttribute xa in xe.Attributes) 506 se.AddAttribute(xa.Name, xa.Value); 507 foreach (XmlNode xn in xe.ChildNodes) 508 if (xn.NodeType == XmlNodeType.Element) 509 se.AddChild(XmlElementToSecurityElement((XmlElement)xn)); 510 return se; 511 } 512 XmlToIdentityList(XmlElement psElement)513 private static string[] XmlToIdentityList(XmlElement psElement) 514 { 515 XmlNamespaceManager nsmgr = XmlNamespaces.GetNamespaceManager(psElement.OwnerDocument.NameTable); 516 XmlNodeList nodes = psElement.SelectNodes(XPaths.permissionClassAttributeQuery, nsmgr); 517 if (nodes == null || nodes.Count == 0) 518 nodes = psElement.SelectNodes(XmlUtil.TrimPrefix(XPaths.permissionClassAttributeQuery)); 519 string[] a; 520 if (nodes != null) 521 { 522 a = new string[nodes.Count]; 523 int i = 0; 524 foreach (XmlNode node in nodes) 525 a[i++] = node.Value; 526 } 527 else 528 a = Array.Empty<string>(); 529 return a; 530 } 531 532 /// <summary> 533 /// Converts an XmlElement into a PermissionSet object. 534 /// </summary> 535 /// <param name="element">An XML representation of the permission set.</param> 536 /// <returns>The converted permission set.</returns> XmlToPermissionSet(XmlElement element)537 public static PermissionSet XmlToPermissionSet(XmlElement element) 538 { 539 #if !MONO 540 if (element == null) 541 return null; 542 543 SecurityElement se = XmlElementToSecurityElement(element); 544 if (se == null) 545 return null; 546 547 PermissionSet ps = new PermissionSet(PermissionState.None); 548 try 549 { 550 ps = new ReadOnlyPermissionSet(se); 551 } 552 catch (ArgumentException ex) 553 { 554 //UNDONE: Need to log exception thrown from PermissionSet.FromXml 555 Debug.Fail(String.Format(CultureInfo.CurrentCulture, "PermissionSet.FromXml failed: {0}\r\n\r\n{1}", ex.Message, element.OuterXml)); 556 return null; 557 } 558 return ps; 559 #else 560 throw new NotImplementedException(); 561 #endif 562 } 563 564 /// <summary> 565 /// Signs a ClickOnce manifest or PE file. 566 /// </summary> 567 /// <param name="certThumbprint">Hexadecimal string that contains the SHA-1 hash of the certificate.</param> 568 /// <param name="timestampUrl">URL that specifies an address of a time stamping server.</param> 569 /// <param name="path">Path of the file to sign with the certificate.</param> SignFile(string certThumbprint, Uri timestampUrl, string path)570 public static void SignFile(string certThumbprint, Uri timestampUrl, string path) 571 { 572 SignFile(certThumbprint, timestampUrl, path, null); 573 } 574 575 /// <summary> 576 /// Signs a ClickOnce manifest or PE file. 577 /// </summary> 578 /// <param name="certThumbprint">Hexadecimal string that contains the SHA-1 hash of the certificate.</param> 579 /// <param name="timestampUrl">URL that specifies an address of a time stamping server.</param> 580 /// <param name="path">Path of the file to sign with the certificate.</param> 581 /// <param name="targetFrameworkVersion">Version of the .NET Framework for the target.</param> SignFile(string certThumbprint, Uri timestampUrl, string path, string targetFrameworkVersion)582 public static void SignFile(string certThumbprint, Uri timestampUrl, string path, string targetFrameworkVersion) 583 { 584 System.Resources.ResourceManager resources = new System.Resources.ResourceManager("Microsoft.Build.Tasks.Core.Strings.ManifestUtilities", typeof(SecurityUtilities).Module.Assembly); 585 586 if (String.IsNullOrEmpty(certThumbprint)) 587 throw new ArgumentNullException("certThumbprint"); 588 589 X509Certificate2 cert = GetCert(certThumbprint); 590 591 if (cert == null) 592 throw new ArgumentException(resources.GetString("CertNotInStore"), "certThumbprint"); 593 594 if (!String.IsNullOrEmpty(targetFrameworkVersion)) 595 { 596 Version targetVersion = Util.GetTargetFrameworkVersion(targetFrameworkVersion); 597 598 if (targetVersion == null) 599 throw new ArgumentException("TargetFrameworkVersion"); 600 601 // SHA-256 digest can be parsed only with .NET 4.5 or higher. 602 bool isTargetFrameworkSha256Supported = targetVersion.CompareTo(s_dotNet45Version) >= 0; 603 SignFileInternal(cert, timestampUrl, path, isTargetFrameworkSha256Supported, resources); 604 } 605 else 606 { 607 SignFile(cert, timestampUrl, path); 608 } 609 } 610 611 // We need to refactor these functions to handle real sign tool 612 /// <summary> 613 /// Signs a ClickOnce manifest. 614 /// </summary> 615 /// <param name="certPath">The certificate to be used to sign the file.</param> 616 /// <param name="certPassword">The certificate password.</param> 617 /// <param name="timestampUrl">URL that specifies an address of a time stamping server.</param> 618 /// <param name="path">Path of the file to sign with the certificate.</param> 619 /// <remarks>This function is only for signing a manifest, not a PE file.</remarks> SignFile(string certPath, SecureString certPassword, Uri timestampUrl, string path)620 public static void SignFile(string certPath, SecureString certPassword, Uri timestampUrl, string path) 621 { 622 X509Certificate2 cert = new X509Certificate2(certPath, certPassword, X509KeyStorageFlags.PersistKeySet); 623 SignFile(cert, timestampUrl, path); 624 } 625 UseSha256Algorithm(X509Certificate2 cert)626 private static bool UseSha256Algorithm(X509Certificate2 cert) 627 { 628 Oid oid = cert.SignatureAlgorithm; 629 return string.Equals(oid.FriendlyName, "sha256RSA", StringComparison.OrdinalIgnoreCase); 630 } 631 632 /// <summary> 633 /// Signs a ClickOnce manifest or PE file. 634 /// </summary> 635 /// <param name="cert">The certificate to be used to sign the file.</param> 636 /// <param name="timestampUrl">URL that specifies an address of a time stamping server.</param> 637 /// <param name="path">Path of the file to sign with the certificate.</param> 638 /// <remarks>This function can only sign a PE file if the X509Certificate2 parameter represents a certificate in the 639 /// current user's personal certificate store.</remarks> SignFile(X509Certificate2 cert, Uri timestampUrl, string path)640 public static void SignFile(X509Certificate2 cert, Uri timestampUrl, string path) 641 { 642 // setup resources 643 System.Resources.ResourceManager resources = new System.Resources.ResourceManager("Microsoft.Build.Tasks.Core.Strings.ManifestUtilities", typeof(SecurityUtilities).Module.Assembly); 644 SignFileInternal(cert, timestampUrl, path, true, resources); 645 } 646 SignFileInternal(X509Certificate2 cert, Uri timestampUrl, string path, bool targetFrameworkSupportsSha256, System.Resources.ResourceManager resources)647 private static void SignFileInternal(X509Certificate2 cert, Uri timestampUrl, string path, bool targetFrameworkSupportsSha256, System.Resources.ResourceManager resources) 648 { 649 if (cert == null) 650 throw new ArgumentNullException("cert"); 651 652 if (String.IsNullOrEmpty(path)) 653 throw new ArgumentNullException("path"); 654 655 if (!File.Exists(path)) 656 throw new FileNotFoundException(String.Format(CultureInfo.InvariantCulture, resources.GetString("SecurityUtil.SignTargetNotFound"), path), path); 657 658 bool useSha256 = UseSha256Algorithm(cert) && targetFrameworkSupportsSha256; 659 660 if (PathUtil.IsPEFile(path)) 661 { 662 if (IsCertInStore(cert)) 663 SignPEFile(cert, timestampUrl, path, resources, useSha256); 664 else 665 throw new InvalidOperationException(resources.GetString("SignFile.CertNotInStore")); 666 } 667 else 668 { 669 using(RSA rsa = CngLightup.GetRSAPrivateKey(cert)) 670 { 671 if (rsa == null) 672 throw new ApplicationException(resources.GetString("SecurityUtil.OnlyRSACertsAreAllowed")); 673 try 674 { 675 XmlDocument doc = new XmlDocument(); 676 doc.PreserveWhitespace = true; 677 XmlReaderSettings xrSettings = new XmlReaderSettings(); 678 xrSettings.DtdProcessing = DtdProcessing.Ignore; 679 using (XmlReader xr = XmlReader.Create(path, xrSettings)) 680 { 681 doc.Load(xr); 682 } 683 SignedCmiManifest2 manifest = new SignedCmiManifest2(doc, useSha256); 684 CmiManifestSigner2 signer; 685 if (useSha256 && rsa is RSACryptoServiceProvider) 686 { 687 RSACryptoServiceProvider csp = SignedCmiManifest2.GetFixedRSACryptoServiceProvider(rsa as RSACryptoServiceProvider, useSha256); 688 signer = new CmiManifestSigner2(csp, cert, useSha256); 689 } 690 else 691 { 692 signer = new CmiManifestSigner2(rsa, cert, useSha256); 693 } 694 695 if (timestampUrl == null) 696 manifest.Sign(signer); 697 else 698 manifest.Sign(signer, timestampUrl.ToString()); 699 doc.Save(path); 700 } 701 catch (Exception ex) 702 { 703 int exceptionHR = System.Runtime.InteropServices.Marshal.GetHRForException(ex); 704 if (exceptionHR == -2147012889 || exceptionHR == -2147012867) 705 { 706 throw new ApplicationException(resources.GetString("SecurityUtil.TimestampUrlNotFound"), ex); 707 } 708 throw new ApplicationException(ex.Message, ex); 709 } 710 } 711 } 712 } 713 714 SignPEFile(X509Certificate2 cert, System.Uri timestampUrl, string path, System.Resources.ResourceManager resources, bool useSha256)715 private static void SignPEFile(X509Certificate2 cert, System.Uri timestampUrl, string path, System.Resources.ResourceManager resources, bool useSha256) 716 { 717 ProcessStartInfo startInfo = new ProcessStartInfo(GetPathToTool(resources), GetCommandLineParameters(cert.Thumbprint, timestampUrl, path, useSha256)); 718 startInfo.CreateNoWindow = true; 719 startInfo.UseShellExecute = false; 720 startInfo.RedirectStandardError = true; 721 startInfo.RedirectStandardOutput = true; 722 723 Process signTool = null; 724 725 try 726 { 727 signTool = Process.Start(startInfo); 728 signTool.WaitForExit(); 729 730 while (!signTool.HasExited) 731 { 732 System.Threading.Thread.Sleep(50); 733 } 734 switch (signTool.ExitCode) 735 { 736 case 0: 737 // everything was fine 738 break; 739 case 1: 740 // error, report it 741 throw new ApplicationException(String.Format(CultureInfo.InvariantCulture, resources.GetString("SecurityUtil.SigntoolFail"), path, signTool.StandardError.ReadToEnd())); 742 case 2: 743 // warning, report it 744 throw new WarningException(String.Format(CultureInfo.InvariantCulture, resources.GetString("SecurityUtil.SigntoolWarning"), path, signTool.StandardError.ReadToEnd())); 745 default: 746 // treat as error 747 throw new ApplicationException(String.Format(CultureInfo.InvariantCulture, resources.GetString("SecurityUtil.SigntoolFail"), path, signTool.StandardError.ReadToEnd())); 748 } 749 } 750 finally 751 { 752 if (signTool != null) 753 signTool.Close(); 754 } 755 } 756 GetCommandLineParameters(string certThumbprint, Uri timestampUrl, string path, bool useSha256)757 internal static string GetCommandLineParameters(string certThumbprint, Uri timestampUrl, string path, bool useSha256) 758 { 759 StringBuilder commandLine = new StringBuilder(); 760 if (useSha256) 761 commandLine.Append(String.Format(CultureInfo.InvariantCulture, "sign /fd sha256 /sha1 {0} ", certThumbprint)); 762 else 763 { 764 commandLine.Append(String.Format(CultureInfo.InvariantCulture, "sign /sha1 {0} ", certThumbprint)); 765 } 766 if (timestampUrl != null) 767 commandLine.Append(String.Format(CultureInfo.InvariantCulture, "/t {0} ", timestampUrl.ToString())); 768 commandLine.Append(string.Format(CultureInfo.InvariantCulture, "\"{0}\"", path)); 769 return commandLine.ToString(); 770 } 771 GetPathToTool(System.Resources.ResourceManager resources)772 internal static string GetPathToTool(System.Resources.ResourceManager resources) 773 { 774 #pragma warning disable 618 // Disabling warning on using internal ToolLocationHelper API. At some point we should migrate this. 775 string toolPath = ToolLocationHelper.GetPathToWindowsSdkFile(ToolName, TargetDotNetFrameworkVersion.VersionLatest, VisualStudioVersion.VersionLatest); 776 if (toolPath == null || !File.Exists(toolPath)) 777 { 778 toolPath = ToolLocationHelper.GetPathToWindowsSdkFile(ToolName, TargetDotNetFrameworkVersion.Version45, 779 VisualStudioVersion.Version110); 780 } 781 if (toolPath == null || !File.Exists(toolPath)) 782 { 783 var pathToDotNetFrameworkSdk = ToolLocationHelper.GetPathToDotNetFrameworkSdk(TargetDotNetFrameworkVersion.Version40, VisualStudioVersion.Version100); 784 if (pathToDotNetFrameworkSdk != null) 785 { 786 toolPath = Path.Combine(pathToDotNetFrameworkSdk, "bin", ToolName); 787 } 788 } 789 if (toolPath == null || !File.Exists(toolPath)) 790 { 791 toolPath = GetVersionIndependentToolPath(ToolName); 792 } 793 if (toolPath == null || !File.Exists(toolPath)) 794 { 795 toolPath = Path.Combine(Directory.GetCurrentDirectory(), ToolName); 796 } 797 if (!File.Exists(toolPath)) 798 { 799 throw new ApplicationException(String.Format(CultureInfo.CurrentCulture, 800 resources.GetString("SecurityUtil.SigntoolNotFound"), toolPath)); 801 } 802 803 return toolPath; 804 #pragma warning restore 618 805 } 806 GetCert(string thumbprint)807 internal static X509Certificate2 GetCert(string thumbprint) 808 { 809 X509Store personalStore = new X509Store(StoreName.My, StoreLocation.CurrentUser); 810 try 811 { 812 personalStore.Open(OpenFlags.ReadOnly); 813 X509Certificate2Collection foundCerts = personalStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false); 814 if (foundCerts.Count == 1) 815 { 816 return foundCerts[0]; 817 } 818 } 819 finally 820 { 821 personalStore.Close(); 822 } 823 return null; 824 } 825 IsCertInStore(X509Certificate2 cert)826 private static bool IsCertInStore(X509Certificate2 cert) 827 { 828 X509Store personalStore = new X509Store(StoreName.My, StoreLocation.CurrentUser); 829 try 830 { 831 personalStore.Open(OpenFlags.ReadOnly); 832 X509Certificate2Collection foundCerts = personalStore.Certificates.Find(X509FindType.FindByThumbprint, cert.Thumbprint, false); 833 if (foundCerts.Count == 1) 834 return true; 835 } 836 finally 837 { 838 personalStore.Close(); 839 } 840 return false; 841 } 842 GetVersionIndependentToolPath(string toolName)843 private static string GetVersionIndependentToolPath(string toolName) 844 { 845 RegistryKey localMachineKey = Registry.LocalMachine; 846 const string versionIndependentToolKeyName = @"Software\Microsoft\ClickOnce\SignTool"; 847 848 using (RegistryKey versionIndependentToolKey = localMachineKey.OpenSubKey(versionIndependentToolKeyName, writable: false)) 849 { 850 string versionIndependentToolPath = null; 851 852 if (versionIndependentToolKey != null) 853 { 854 versionIndependentToolPath = versionIndependentToolKey.GetValue("Path") as string; 855 } 856 857 return versionIndependentToolPath != null ? Path.Combine(versionIndependentToolPath, toolName) : null; 858 } 859 } 860 } 861 } 862