diff --git a/command_verify.go b/command_verify.go index 82bfda9..aff28de 100644 --- a/command_verify.go +++ b/command_verify.go @@ -8,11 +8,11 @@ import ( "io" "os" - "github.com/certifi/gocertifi" "github.com/github/ietf-cms" "github.com/pkg/errors" ) + func commandVerify() error { sNewSig.emit() @@ -165,21 +165,15 @@ func verifyDetached() error { } func verifyOpts() x509.VerifyOptions { - roots, err := x509.SystemCertPool() - if err != nil { - // SystemCertPool isn't implemented for Windows. fall back to mozilla trust - // store. - roots, err = gocertifi.CACerts() - if err != nil { - // Fall back to an empty store. Verification will likely fail. - roots = x509.NewCertPool() - } - } + var ( + roots *x509.CertPool + ) - for _, ident := range idents { - if cert, err := ident.Certificate(); err == nil { - roots.AddCert(cert) - } + // Depending on the operating system, enumerate the trusted root certificate store + err := parseRoots(roots) + if err != nil{ + // Fall back to an empty store. Verifications will likely fail. + roots = x509.NewCertPool() } return x509.VerifyOptions{ @@ -187,3 +181,4 @@ func verifyOpts() x509.VerifyOptions { KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, } } + diff --git a/parse_roots.go b/parse_roots.go new file mode 100644 index 0000000..2e02995 --- /dev/null +++ b/parse_roots.go @@ -0,0 +1,22 @@ +// +build !windows + +package main + +import ( + "crypto/x509" + "github.com/pkg/errors" +) + +func parseRoots(roots *x509.CertPool ) error{ + roots, err := x509.SystemCertPool() + if err != nil { + return errors.Wrap(err, "Failed to parse root store") + } + + for _, ident := range idents { + if cert, err := ident.Certificate(); err == nil { + roots.AddCert(cert) + } + } + return nil +} diff --git a/parse_roots_windows.go b/parse_roots_windows.go new file mode 100644 index 0000000..ab75109 --- /dev/null +++ b/parse_roots_windows.go @@ -0,0 +1,65 @@ +// +build windows + +package main + +import ( + "crypto/x509" + "github.com/certifi/gocertifi" + "github.com/pkg/errors" + "syscall" + "unsafe" +) + +const ( + CryptENotFound = 0x80092004 +) + +func parseRoots(roots *x509.CertPool) error{ + + // The windows trust store is dynamically populated, to prevent issues with generally trusted + // roots not being enumerated, use the mozilla trust store as a baseline + roots, err := gocertifi.CACerts() + if err != nil { + roots = x509.NewCertPool() + } + + // Enumerate the local machine trust store and add any missing certificates. + storeName, err:= syscall.UTF16PtrFromString("Root") + if err != nil { + return errors.Wrap(err, "Failed to get root store name") + } + storeHandle, err := syscall.CertOpenSystemStore(0, storeName) + if err != nil { + return errors.New(syscall.GetLastError().Error()) + } + defer syscall.CertCloseStore(storeHandle, 0) + + var cert *syscall.CertContext + for { + cert, err = syscall.CertEnumCertificatesInStore(storeHandle, cert) + if err != nil { + if errno, ok := err.(syscall.Errno); ok { + if errno == CryptENotFound { + break + } + } + return errors.New(syscall.GetLastError().Error()) + } + if cert == nil { + break + } + // Copy the buf, since ParseCertificate does not create its own copy. + buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:] + buf2 := make([]byte, cert.Length) + copy(buf2, buf) + if c, err := x509.ParseCertificate(buf2); err == nil { + // AddCert contains logic to prevent adding a duplicate certificate to the pool + roots.AddCert(c) + } + } + + + + + return nil +}