Skip to content

Commit 4a80da2

Browse files
authored
Merge pull request #625 from Azure/dev
huangpf PR: dev <- Azure:dev
2 parents 3a26d27 + e381a29 commit 4a80da2

File tree

4 files changed

+288
-0
lines changed

4 files changed

+288
-0
lines changed

build.proj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
<UsingTask TaskName="ValidateStrongNameSignatureTask" AssemblyFile="$(LibraryToolsFolder)\Microsoft.Azure.Build.Tasks.dll" />
6363
<UsingTask TaskName="FilterOutAutoRestLibraries" AssemblyFile="$(LibraryToolsFolder)\Microsoft.Azure.Build.Tasks.dll" />
6464
<UsingTask TaskName="DebugTask" AssemblyFile="$(LibraryToolsFolder)\Microsoft.Azure.Build.Tasks.dll" />
65+
<UsingTask TaskName="VerifyAuthenticodeSignatureTask" AssemblyFile="$(LibraryToolsFolder)\Microsoft.Azure.Build.Tasks.dll" />
6566

6667
<!--
6768
CI build related
@@ -261,6 +262,11 @@
261262
ExpectedDelaySigned="false"
262263
ContinueOnError="false"
263264
Condition="!$(DelaySign) and '@(DelaySignedAssembliesToSign)' != ''"/>
265+
266+
<VerifyAuthenticodeSignatureTask ProbingDirectory="$(PackageDirectory)\$(Configuration)"
267+
FileFilterPattern="microsoft.*.dll;system.*.dll;*.ps1;*.psm1">
268+
<Output TaskParameter="AuthCodeSignTaskErrorsDetected" PropertyName="AuthTaskFailed" />
269+
</VerifyAuthenticodeSignatureTask>
264270

265271
<Exec Command="$(PowerShellCommand) -NonInteractive -NoLogo -NoProfile -Command &quot;. $(LibraryToolsFolder)\CheckStrongNameSignature.ps1 &quot;"/>
266272

tools/BuildPackagesTask/Microsoft.Azure.Build.Tasks.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
<Reference Include="Microsoft.CSharp" />
4242
<Reference Include="System.Data" />
4343
<Reference Include="System.Xml" />
44+
<Reference Include="System.Management.Automation" />
4445
</ItemGroup>
4546
<ItemGroup>
4647
<Compile Include="DebugTask.cs" />
@@ -49,6 +50,10 @@
4950
<Compile Include="RegexReplacementTask.cs" />
5051
<Compile Include="StrongNameUtility.cs" />
5152
<Compile Include="ValidateStrongNameSignatureTask.cs" />
53+
<Compile Include="VerifyAuthenticodeSignatureTask.cs" />
54+
</ItemGroup>
55+
<ItemGroup>
56+
<None Include="Tests\AuthenticodeSignedTaskSmokeTest.proj" />
5257
</ItemGroup>
5358
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
5459
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project DefaultTargets="RunTest" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup>
4+
<LibraryRoot>..\..\..\</LibraryRoot>
5+
<LibraryToolsFolder>$(LibraryRoot)tools</LibraryToolsFolder>
6+
</PropertyGroup>
7+
<UsingTask TaskName="VerifyAuthenticodeSignatureTask" AssemblyFile="$(LibraryToolsFolder)\Microsoft.Azure.Build.Tasks.dll" />
8+
<UsingTask TaskName="DebugTask" AssemblyFile="$(LibraryToolsFolder)\Microsoft.Azure.Build.Tasks.dll" />
9+
10+
<Target Name="VerifyOneFile">
11+
<Message Text="Executing Test 'VerifyOneFile'" />
12+
<VerifyAuthenticodeSignatureTask FilesToCheckAuthCodeSignature="$(FrameworkDir)$(FrameworkVersion)\Microsoft.Build.dll">
13+
<Output TaskParameter="AuthCodeSignTaskErrorsDetected" PropertyName="AuthTaskFailed" />
14+
</VerifyAuthenticodeSignatureTask>
15+
<Message Condition="'$(AuthTaskFailed)' == 'false'" Text="Test Passed !!!!!!!!!!"/>
16+
<Message Condition="'$(AuthTaskFailed)' == 'true'" Text="!!!!!!!!!! Not expecting errors Test Failed !!!!!!!!!!"/>
17+
<Message Text="'VerifyOneFile' finished executing" />
18+
</Target>
19+
20+
<Target Name="AllFilesUnderDirectory">
21+
<Message Text="Executing Test 'AllFilesUnderDirectory'" />
22+
<VerifyAuthenticodeSignatureTask ProbingDirectory="C:\Windows\Microsoft.NET\Framework\v4.0.30319\NativeImages">
23+
<Output TaskParameter="AuthCodeSignTaskErrorsDetected" PropertyName="AuthTaskFailed" />
24+
</VerifyAuthenticodeSignatureTask>
25+
<Message Condition="'$(AuthTaskFailed)' == 'false'" Text="Test Passed !!!!!!!!!!"/>
26+
<Message Condition="'$(AuthTaskFailed)' == 'true'" Text="!!!!!!!!!! Not expecting errors Test Failed !!!!!!!!!!"/>
27+
28+
<Message Text="'AllFilesUnderDirectory' finished executing" />
29+
</Target>
30+
31+
<Target Name="FilesWithFileFilter">
32+
<Message Text="Executing Test 'FilesWithFileFilter'" />
33+
<VerifyAuthenticodeSignatureTask ProbingDirectory="C:\Windows\Microsoft.NET\Framework\v4.0.30319"
34+
FileFilterPattern="microsoft.build.*.dll;*.rsp;*.exe">
35+
<Output TaskParameter="AuthCodeSignTaskErrorsDetected" PropertyName="AuthTaskFailed" />
36+
</VerifyAuthenticodeSignatureTask>
37+
<Message Condition="'$(AuthTaskFailed)' == 'false'" Text="Test Passed !!!!!!!!!!"/>
38+
<Message Condition="'$(AuthTaskFailed)' == 'true'" Text="!!!!!!!!!! Not expecting errors Test Failed !!!!!!!!!!"/>
39+
40+
<Message Text="'FilesWithFileFilter' finished executing" />
41+
</Target>
42+
43+
<Target Name="FileCollectionVerification">
44+
<Message Text="Executing Test 'FileCollectionVerification'" />
45+
<ItemGroup>
46+
<FilesToSign Include="$(FrameworkDir)$(FrameworkVersion)\Microsoft.foo.dll"/>
47+
<FilesToSign Include="$(FrameworkDir)$(FrameworkVersion)\Microsoft.VisualBasic.Activities.Compiler.dll"/>
48+
<FilesToSign Include="$(FrameworkDir)$(FrameworkVersion)\Microsoft.Common.targets"/>
49+
<FilesToSign Include="$(MSBuildProgramFiles32)\Microsoft SDKs\Azure\PowerShell\ResourceManager\AzureResourceManager\AzureRM.Compute\AzureRmProfileStartup.ps1"/>
50+
</ItemGroup>
51+
<VerifyAuthenticodeSignatureTask FilesToCheckAuthCodeSignature="@(FilesToSign)" ContinueOnError="true">
52+
<Output TaskParameter="AuthCodeSignTaskErrorsDetected" PropertyName="AuthTaskFailed" />
53+
</VerifyAuthenticodeSignatureTask>
54+
<Message Condition="'$(AuthTaskFailed)' == 'true'" Text="Expecting Errors Test Passed !!!!!!!!!!"/>
55+
<Message Condition="'$(AuthTaskFailed)' == 'false'" Text="!!!!!!!!!! Test Failed !!!!!!!!!!"/>
56+
57+
<Message Text="'FileCollectionVerification' finished executing" />
58+
</Target>
59+
<Target Name="RunTest" DependsOnTargets="VerifyOneFile;AllFilesUnderDirectory;FilesWithFileFilter;FileCollectionVerification">
60+
<Message Text="Test Suite completed"/>
61+
</Target>
62+
</Project>
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
namespace Microsoft.Azure.Build.Tasks
2+
{
3+
using Microsoft.Build.Framework;
4+
using Microsoft.Build.Utilities;
5+
using System;
6+
using System.Collections.Generic;
7+
using System.IO;
8+
using System.Linq;
9+
using System.Management.Automation;
10+
using System.Text;
11+
12+
/// <summary>
13+
/// Authenticode Signature task
14+
/// Flow:
15+
/// FilesToCheckAuthCodeSignature:
16+
/// Support 1 or many files provided
17+
/// ProbingDirectory:
18+
/// When provided value to this property, we will ignore FilesToCheckAuthCodeSignature property
19+
/// FileFilterPattern:
20+
/// This is applicable when probingDirectory is specified and you want to filter selected group of files from contents of the directory (recurrsively searched)
21+
/// E.g. if FileFilterPattern specified = "microsoft.*.dll;system.*.dll;*.exe"
22+
/// We will first find all the files microsoft*.dll, then system.*.dll and finally *.exe
23+
/// All three set of search results will be combined and will then be verified for Authenticode Signature
24+
/// </summary>
25+
public class VerifyAuthenticodeSignatureTask : Task
26+
{
27+
/// <summary>
28+
/// Gets or sets the assembly on which authenticode signature verification is performed.
29+
/// </summary>
30+
//public ITaskItem SignedFilePath { get; set; }
31+
32+
/// <summary>
33+
/// Gets or sets list of files/assemblies for which authenticode signature verification is performed
34+
/// If ProbingDirectory is specified, FilesToCheckAuthCodeSignature collection is ignored
35+
/// </summary>
36+
public ITaskItem[] FilesToCheckAuthCodeSignature { get; set; }
37+
38+
/// <summary>
39+
/// Specify Directory path under which all the files will be verified for Authenticode Signature
40+
///
41+
/// </summary>
42+
public string ProbingDirectory { get; set; }
43+
44+
/// <summary>
45+
/// Specifiy file filter pattern (e.g. *.dll,*.ps1)
46+
/// In above example, we will first find all dll files, then combine with all the ps1 files that are found
47+
/// </summary>
48+
public string FileFilterPattern { get; set; }
49+
50+
/// <summary>
51+
/// Returns True if any error occurs, False if no AutheticodeSignature occured
52+
/// </summary>
53+
[Output]
54+
public bool AuthCodeSignTaskErrorsDetected { get; private set; }
55+
56+
57+
private List<string> ErrorList { get; set; }
58+
private bool IsFileSigned { get; set; }
59+
60+
/// <summary>
61+
/// Execute VerifyAuthenticodeSignature task
62+
/// </summary>
63+
/// <returns>True: if files are authenticode signed, False: if any of the files provided are not authenticode signed</returns>
64+
public override bool Execute()
65+
{
66+
ErrorList = new List<string>();
67+
AuthCodeSignTaskErrorsDetected = false;
68+
IsFileSigned = true;
69+
70+
string[] filesToCheck = GetFilesToVerifyAuthCodeSignature();
71+
IsFileSigned = VerifyAllFiles(filesToCheck);
72+
73+
if (ErrorList.Count > 0)
74+
{
75+
StringBuilder sb = new StringBuilder();
76+
IsFileSigned = false;
77+
AuthCodeSignTaskErrorsDetected = true;
78+
79+
ErrorList.ForEach((err) => sb.AppendLine(err));
80+
81+
Log.LogError("Errors detected during AuthenticodeSignature Verification");
82+
Log.LogError(sb.ToString());
83+
}
84+
85+
return IsFileSigned;
86+
}
87+
88+
/// <summary>
89+
/// Get set of files that will ultimately be verified for AuthCode Signature
90+
/// If we find user has sepcified a probing directory, we will give priority to it and will ignore FilesToCheckAuthCodeSignature collection
91+
/// </summary>
92+
/// <returns></returns>
93+
private string[] GetFilesToVerifyAuthCodeSignature()
94+
{
95+
bool isProbingDirValid = false;
96+
string[] filesToCheck = null;
97+
98+
//First priority to probing directory
99+
if (!string.IsNullOrEmpty(ProbingDirectory))
100+
{
101+
if (Directory.Exists(ProbingDirectory))
102+
{
103+
isProbingDirValid = true;
104+
filesToCheck = ApplyFileFilter(ProbingDirectory);
105+
}
106+
}
107+
108+
//if Probing directory is not specified then we honor all the files provided
109+
if ((isProbingDirValid == false) && (FilesToCheckAuthCodeSignature != null))
110+
{
111+
if (FilesToCheckAuthCodeSignature.Length > 0)
112+
{
113+
filesToCheck = FilesToCheckAuthCodeSignature.Select<ITaskItem, string>((item) => item.ItemSpec).ToArray<string>();
114+
}
115+
}
116+
117+
return filesToCheck;
118+
}
119+
120+
/// <summary>
121+
/// Apply filters to filter list of files that will be checked for authenticode signed
122+
/// </summary>
123+
/// <param name="dirToProbeForFiles"></param>
124+
/// <returns></returns>
125+
private string[] ApplyFileFilter(string dirToProbeForFiles)
126+
{
127+
string[] filteredFiles = null;
128+
IEnumerable<string> startupCollection = new string[] { "" };
129+
IEnumerable<string> files = startupCollection.Except<string>(new string[] { "" }); //we do this to construct an empty IEnumerable (TODO: is there a better way)
130+
131+
if (string.IsNullOrEmpty(FileFilterPattern))
132+
{
133+
files = Directory.EnumerateFiles(dirToProbeForFiles, "*", SearchOption.AllDirectories);
134+
}
135+
else
136+
{
137+
string[] listOfFilters = FileFilterPattern.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
138+
foreach (string filter in listOfFilters)
139+
{
140+
files = files.Concat<string>(Directory.EnumerateFiles(dirToProbeForFiles, filter, SearchOption.AllDirectories));
141+
}
142+
}
143+
144+
if (files.Any<string>() || files != null)
145+
filteredFiles = files.ToArray<string>();
146+
147+
return filteredFiles;
148+
}
149+
150+
/// <summary>
151+
/// Verify if file is Authenticode Signed using PS cmdLet Get-AuthenticodeSignature
152+
/// </summary>
153+
/// <returns>True: If signature status is Valid, False: if signature status is other than Valid</returns>
154+
private bool VerifyAllFiles(string[] signedFilesArray)
155+
{
156+
bool isSigned = true;
157+
if (signedFilesArray.Length > 0)
158+
{
159+
bool authSigned = false;
160+
for (int i = 0; i <= signedFilesArray.Length - 1; i++)
161+
{
162+
authSigned = VerifyAuthenticodeSignature(signedFilesArray[i]);
163+
isSigned = isSigned && authSigned;
164+
}
165+
}
166+
167+
return isSigned;
168+
}
169+
170+
/// <summary>
171+
/// Check for Authenticode Signature
172+
/// </summary>
173+
/// <param name="providedFilePath"></param>
174+
/// <returns></returns>
175+
private bool VerifyAuthenticodeSignature(string providedFilePath)
176+
{
177+
bool isSigned = true;
178+
string fileName = Path.GetFileName(providedFilePath);
179+
string calculatedFullPath = Path.GetFullPath(providedFilePath);
180+
181+
if (File.Exists(calculatedFullPath))
182+
{
183+
Log.LogMessage(string.Format("Verifying file '{0}'", calculatedFullPath));
184+
using (PowerShell ps = PowerShell.Create())
185+
{
186+
ps.AddCommand("Get-AuthenticodeSignature", true);
187+
ps.AddParameter("FilePath", calculatedFullPath);
188+
var cmdLetResults = ps.Invoke();
189+
190+
foreach (PSObject result in cmdLetResults)
191+
{
192+
Signature s = (Signature)result.BaseObject;
193+
isSigned = s.Status.Equals(SignatureStatus.Valid);
194+
if (isSigned == false)
195+
{
196+
ErrorList.Add(string.Format("!!!AuthenticodeSignature status is '{0}' for file '{1}' !!!", s.Status.ToString(), calculatedFullPath));
197+
}
198+
else
199+
{
200+
Log.LogMessage(string.Format("!!!AuthenticodeSignature status is '{0}' for file '{1}' !!!", s.Status.ToString(), calculatedFullPath));
201+
}
202+
break;
203+
}
204+
}
205+
}
206+
else
207+
{
208+
ErrorList.Add(string.Format("File '{0}' does not exist. Unable to verify AuthenticodeSignature", calculatedFullPath));
209+
isSigned = false;
210+
}
211+
212+
return isSigned;
213+
}
214+
}
215+
}

0 commit comments

Comments
 (0)