From 561c99875bfb42587d281511986043aa45a7c074 Mon Sep 17 00:00:00 2001 From: Chad Retz Date: Sun, 12 Aug 2018 20:33:56 -0500 Subject: [PATCH] Add Windows support (#46) Fixes #42 --- truststore_windows.go | 130 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 truststore_windows.go diff --git a/truststore_windows.go b/truststore_windows.go new file mode 100644 index 0000000..12a2777 --- /dev/null +++ b/truststore_windows.go @@ -0,0 +1,130 @@ +package main + +import ( + "crypto/x509" + "encoding/pem" + "fmt" + "io/ioutil" + "math/big" + "os" + "path/filepath" + "syscall" + "unsafe" +) + +var ( + FirefoxPath = "C:\\Program Files\\Mozilla Firefox" + FirefoxProfile = os.Getenv("USERPROFILE") + "\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles" + CertutilInstallHelp = "" + NSSBrowsers = "Firefox" +) + +var ( + modcrypt32 = syscall.NewLazyDLL("crypt32.dll") + procCertAddEncodedCertificateToStore = modcrypt32.NewProc("CertAddEncodedCertificateToStore") + procCertCloseStore = modcrypt32.NewProc("CertCloseStore") + procCertDeleteCertificateFromStore = modcrypt32.NewProc("CertDeleteCertificateFromStore") + procCertDuplicateCertificateContext = modcrypt32.NewProc("CertDuplicateCertificateContext") + procCertEnumCertificatesInStore = modcrypt32.NewProc("CertEnumCertificatesInStore") + procCertOpenSystemStoreW = modcrypt32.NewProc("CertOpenSystemStoreW") +) + +func (m *mkcert) installPlatform() bool { + // Load cert + cert, err := ioutil.ReadFile(filepath.Join(m.CAROOT, rootName)) + fatalIfErr(err, "failed to read root certificate") + // Decode PEM + if certBlock, _ := pem.Decode(cert); certBlock == nil || certBlock.Type != "CERTIFICATE" { + fatalIfErr(fmt.Errorf("Invalid PEM data"), "decode pem") + } else { + cert = certBlock.Bytes + } + // Open root store + store, err := openWindowsRootStore() + fatalIfErr(err, "open root store") + defer store.close() + // Add cert + fatalIfErr(store.addCert(cert), "add cert") + return true +} + +func (m *mkcert) uninstallPlatform() bool { + // We'll just remove all certs with the same serial number + // Open root store + store, err := openWindowsRootStore() + fatalIfErr(err, "open root store") + defer store.close() + // Do the deletion + deletedAny, err := store.deleteCertsWithSerial(m.caCert.SerialNumber) + if err == nil && !deletedAny { + err = fmt.Errorf("No certs found") + } + fatalIfErr(err, "delete cert") + return true +} + +type windowsRootStore uintptr + +func openWindowsRootStore() (windowsRootStore, error) { + store, _, err := procCertOpenSystemStoreW.Call(0, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("ROOT")))) + if store != 0 { + return windowsRootStore(store), nil + } + return 0, fmt.Errorf("Failed to open windows root store: %v", err) +} + +func (w windowsRootStore) close() error { + ret, _, err := procCertCloseStore.Call(uintptr(w), 0) + if ret != 0 { + return nil + } + return fmt.Errorf("Failed to close windows root store: %v", err) +} + +func (w windowsRootStore) addCert(cert []byte) error { + // TODO: ok to always overwrite? + ret, _, err := procCertAddEncodedCertificateToStore.Call( + uintptr(w), // HCERTSTORE hCertStore + uintptr(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING), // DWORD dwCertEncodingType + uintptr(unsafe.Pointer(&cert[0])), // const BYTE *pbCertEncoded + uintptr(len(cert)), // DWORD cbCertEncoded + 3, // DWORD dwAddDisposition (CERT_STORE_ADD_REPLACE_EXISTING is 3) + 0, // PCCERT_CONTEXT *ppCertContext + ) + if ret != 0 { + return nil + } + return fmt.Errorf("Failed adding cert: %v", err) +} + +func (w windowsRootStore) deleteCertsWithSerial(serial *big.Int) (bool, error) { + // Go over each, deleting the ones we find + var cert *syscall.CertContext + deletedAny := false + for { + // Next enum + certPtr, _, err := procCertEnumCertificatesInStore.Call(uintptr(w), uintptr(unsafe.Pointer(cert))) + if cert = (*syscall.CertContext)(unsafe.Pointer(certPtr)); cert == nil { + if errno, ok := err.(syscall.Errno); ok && errno == 0x80092004 { + break + } + return deletedAny, fmt.Errorf("Failed enumerating certs: %v", err) + } + // Parse cert + certBytes := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:cert.Length] + parsedCert, err := x509.ParseCertificate(certBytes) + // We'll just ignore parse failures for now + if err == nil && parsedCert.SerialNumber != nil && parsedCert.SerialNumber.Cmp(serial) == 0 { + // Duplicate the context so it doesn't stop the enum when we delete it + dupCertPtr, _, err := procCertDuplicateCertificateContext.Call(uintptr(unsafe.Pointer(cert))) + if dupCertPtr == 0 { + return deletedAny, fmt.Errorf("Failed duplicating context: %v", err) + } + if ret, _, err := procCertDeleteCertificateFromStore.Call(dupCertPtr); ret == 0 { + return deletedAny, fmt.Errorf("Failed deleting certificate: %v", err) + } + deletedAny = true + } + } + return deletedAny, nil +}