From ef496fb156abb033f71ae4fa16812778efe144f5 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Tue, 23 Nov 2021 18:05:47 +0100 Subject: [PATCH] windows: re-enable TestWinVerifyTrust with newly signed file Rather than disabling this test, let's just not make it rely on Microsoft files, whose signing validity period we can't depend on. Instead, we include our own EV-signed artifact, with a Digicert timestamp using a certificate valid for a decade. Fixes golang/go#49651. Fixes golang/go#49266. For golang/go#46906. Change-Id: Idadba346810017b8f769d6fac1ddd357d4dee93c Reviewed-on: https://go-review.googlesource.com/c/sys/+/366655 Trust: Jason A. Donenfeld Trust: Brad Fitzpatrick Run-TryBot: Jason A. Donenfeld Reviewed-by: Bryan C. Mills Reviewed-by: Brad Fitzpatrick TryBot-Result: Go Bot --- windows/syscall_windows_test.go | 36 ++++++++++++---------------- windows/testdata/README | 21 ++++++++++++++++ windows/testdata/ev-signed-file.exe | Bin 0 -> 18152 bytes 3 files changed, 36 insertions(+), 21 deletions(-) create mode 100644 windows/testdata/README create mode 100644 windows/testdata/ev-signed-file.exe diff --git a/windows/syscall_windows_test.go b/windows/syscall_windows_test.go index f6776d8b..0e4666b5 100644 --- a/windows/syscall_windows_test.go +++ b/windows/syscall_windows_test.go @@ -627,16 +627,10 @@ func TestCommandLineRecomposition(t *testing.T) { } func TestWinVerifyTrust(t *testing.T) { - t.Skip("skipping fragile test; see https://golang.org/issue/49266 and https://golang.org/issue/49651") - - system32, err := windows.GetSystemDirectory() + evsignedfile := `.\testdata\ev-signed-file.exe` + evsignedfile16, err := windows.UTF16PtrFromString(evsignedfile) if err != nil { - t.Errorf("unable to find system32 directory: %v", err) - } - ntoskrnl := filepath.Join(system32, "ntoskrnl.exe") - ntoskrnl16, err := windows.UTF16PtrFromString(ntoskrnl) - if err != nil { - t.Fatalf("unable to get utf16 of ntoskrnl.exe: %v", err) + t.Fatalf("unable to get utf16 of %s: %v", evsignedfile, err) } data := &windows.WinTrustData{ Size: uint32(unsafe.Sizeof(windows.WinTrustData{})), @@ -646,39 +640,39 @@ func TestWinVerifyTrust(t *testing.T) { StateAction: windows.WTD_STATEACTION_VERIFY, FileOrCatalogOrBlobOrSgnrOrCert: unsafe.Pointer(&windows.WinTrustFileInfo{ Size: uint32(unsafe.Sizeof(windows.WinTrustFileInfo{})), - FilePath: ntoskrnl16, + FilePath: evsignedfile16, }), } verifyErr := windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data) data.StateAction = windows.WTD_STATEACTION_CLOSE closeErr := windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data) if verifyErr != nil { - t.Errorf("ntoskrnl.exe did not verify: %v", verifyErr) + t.Errorf("%s did not verify: %v", evsignedfile, verifyErr) } if closeErr != nil { t.Errorf("unable to free verification resources: %v", closeErr) } - // Now that we've verified legitimate ntoskrnl.exe verifies, let's corrupt it and see if it correctly fails. + // Now that we've verified the legitimate file verifies, let's corrupt it and see if it correctly fails. dir, err := ioutil.TempDir("", "go-build") if err != nil { t.Fatalf("failed to create temp directory: %v", err) } defer os.RemoveAll(dir) - corruptedNtoskrnl := filepath.Join(dir, "ntoskrnl.exe") - ntoskrnlBytes, err := ioutil.ReadFile(ntoskrnl) + corruptedEvsignedfile := filepath.Join(dir, "corrupted-file") + evsignedfileBytes, err := ioutil.ReadFile(evsignedfile) if err != nil { - t.Fatalf("unable to read ntoskrnl.exe bytes: %v", err) + t.Fatalf("unable to read %s bytes: %v", evsignedfile, err) } - if len(ntoskrnlBytes) > 0 { - ntoskrnlBytes[len(ntoskrnlBytes)/2-1]++ + if len(evsignedfileBytes) > 0 { + evsignedfileBytes[len(evsignedfileBytes)/2-1]++ } - err = ioutil.WriteFile(corruptedNtoskrnl, ntoskrnlBytes, 0755) + err = ioutil.WriteFile(corruptedEvsignedfile, evsignedfileBytes, 0755) if err != nil { t.Fatalf("unable to write corrupted ntoskrnl.exe bytes: %v", err) } - ntoskrnl16, err = windows.UTF16PtrFromString(corruptedNtoskrnl) + evsignedfile16, err = windows.UTF16PtrFromString(corruptedEvsignedfile) if err != nil { t.Fatalf("unable to get utf16 of ntoskrnl.exe: %v", err) } @@ -690,14 +684,14 @@ func TestWinVerifyTrust(t *testing.T) { StateAction: windows.WTD_STATEACTION_VERIFY, FileOrCatalogOrBlobOrSgnrOrCert: unsafe.Pointer(&windows.WinTrustFileInfo{ Size: uint32(unsafe.Sizeof(windows.WinTrustFileInfo{})), - FilePath: ntoskrnl16, + FilePath: evsignedfile16, }), } verifyErr = windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data) data.StateAction = windows.WTD_STATEACTION_CLOSE closeErr = windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data) if verifyErr != windows.Errno(windows.TRUST_E_BAD_DIGEST) { - t.Errorf("ntoskrnl.exe did not fail to verify as expected: %v", verifyErr) + t.Errorf("%s did not fail to verify as expected: %v", corruptedEvsignedfile, verifyErr) } if closeErr != nil { t.Errorf("unable to free verification resources: %v", closeErr) diff --git a/windows/testdata/README b/windows/testdata/README new file mode 100644 index 00000000..07cc50f8 --- /dev/null +++ b/windows/testdata/README @@ -0,0 +1,21 @@ +This folder contains various pre-generated artifacts for testing. Descriptions +of each follow below. + +## ev-signed-file.exe + +This was generated with: + + int main(void) + { + puts("Hello Gophers!"); + return 0; + } + +And then a simple clang/mingw compilation: + + i686-w64-mingw32-gcc -Os -s a.c + +After, it was copied to a Windows computer where it was signed with an EV +certificate using: + + signtool sign /sha1 /fd sha256 /tr http://timestamp.digicert.com /td sha256 /d "Go Project EV Signing Test" a.exe diff --git a/windows/testdata/ev-signed-file.exe b/windows/testdata/ev-signed-file.exe new file mode 100644 index 0000000000000000000000000000000000000000..916edfa34c1967d16e77d8374f262c949b792ce2 GIT binary patch literal 18152 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%0Ad31G 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 literal 0 HcmV?d00001