Sfoglia il codice sorgente

Add Windows support (#46)

Fixes #42
Chad Retz 6 anni fa
parent
commit
561c99875b
1 ha cambiato i file con 130 aggiunte e 0 eliminazioni
  1. 130 0
      truststore_windows.go

+ 130 - 0
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 = "<certutil unsupported on Windows>"
+	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
+}