Skip to content

Commit

Permalink
certinject: improved error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
aerth committed Apr 18, 2020
1 parent ca0fe55 commit b72ba82
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 47 deletions.
25 changes: 18 additions & 7 deletions certinject/certinject_notwindows.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,34 @@

package certinject

import "fmt"

// This package is used to add and remove certificates to the system trust
// store.
// Currently only supports NSS sqlite3 stores.

// InjectCert injects the given cert into all configured trust stores.
func InjectCert(derBytes []byte) {

func InjectCert(derBytes []byte) error {
if !nssFlag.Value() {
return fmt.Errorf("no trusted store chosen, did you mean to use %q flag?", nssFlag.CfName())
}
if nssFlag.Value() {
injectCertNss(derBytes)
if err := injectCertNss(derBytes); err != nil {
return err
}
}
return nil
}

// CleanCerts cleans expired certs from all configured trust stores.
func CleanCerts() {

func CleanCerts() error {
if !nssFlag.Value() {
return fmt.Errorf("no trusted store chosen, did you mean to use %q flag?", nssFlag.CfName())
}
if nssFlag.Value() {
cleanCertsNss()
if err := cleanCertsNss(); err != nil {
return err
}
}

return nil
}
33 changes: 24 additions & 9 deletions certinject/certinject_windows.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package certinject

import (
"fmt"

"gopkg.in/hlandau/easyconfig.v1/cflag"
)

Expand All @@ -20,24 +22,37 @@ var (
)

// InjectCert injects the given cert into all configured trust stores.
func InjectCert(derBytes []byte) {

func InjectCert(derBytes []byte) error {
if !cryptoApiFlag.Value() && !nssFlag.Value() {
return fmt.Errorf("no trusted store chosen, did you mean to use %q or %q flags?", cryptoApiFlag.CfName(), nssFlag.CfName())
}
if cryptoApiFlag.Value() {
injectCertCryptoApi(derBytes)
if err := injectCertCryptoApi(derBytes); err != nil {
return err
}
}
if nssFlag.Value() {
injectCertNss(derBytes)
if err := injectCertNss(derBytes); err != nil {
return err
}
}
return nil
}

// CleanCerts cleans expired certs from all configured trust stores.
func CleanCerts() {

func CleanCerts() error {
if !cryptoApiFlag.Value() && !nssFlag.Value() {
return fmt.Errorf("no trusted store chosen, did you mean to use %q or %q flags?", cryptoApiFlag.CfName(), nssFlag.CfName())
}
if cryptoApiFlag.Value() {
cleanCertsCryptoApi()
if err := cleanCertsCryptoApi(); err != nil {
return err
}
}
if nssFlag.Value() {
cleanCertsNss()
if err := cleanCertsNss(); err != nil {
return err
}
}

return nil
}
20 changes: 11 additions & 9 deletions certinject/cryptoapi_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const cryptoApiCertStoreRegistryKey = `SOFTWARE\Microsoft\EnterpriseCertificates
const cryptoApiMagicName = "Namecoin"
const cryptoApiMagicValue = 1

func injectCertCryptoApi(derBytes []byte) {
func injectCertCryptoApi(derBytes []byte) error {

// Format documentation of Microsoft's "Certificate Registry Blob":

Expand Down Expand Up @@ -74,7 +74,7 @@ func injectCertCryptoApi(derBytes []byte) {
certStoreKey, err := registry.OpenKey(cryptoApiCertStoreRegistryBase, cryptoApiCertStoreRegistryKey, registry.ALL_ACCESS)
if err != nil {
log.Errorf("Couldn't open cert store: %s", err)
return
return err
}
defer certStoreKey.Close()

Expand All @@ -96,7 +96,7 @@ func injectCertCryptoApi(derBytes []byte) {
certKey, _, err := registry.CreateKey(certStoreKey, fingerprintHexUpper, registry.ALL_ACCESS)
if err != nil {
log.Errorf("Couldn't create registry key for certificate: %s", err)
return
return err
}
defer certKey.Close()

Expand All @@ -108,33 +108,34 @@ func injectCertCryptoApi(derBytes []byte) {
err = certKey.SetDWordValue(cryptoApiMagicName, cryptoApiMagicValue)
if err != nil {
log.Errorf("Couldn't set magic registry value for certificate: %s", err)
return
return err
}

// Create the registry value which holds the certificate.
err = certKey.SetBinaryValue("Blob", certBlob)
if err != nil {
log.Errorf("Couldn't set blob registry value for certificate: %s", err)
return
return err
}

return nil
}

func cleanCertsCryptoApi() {
func cleanCertsCryptoApi() error {

// Open up the cert store.
certStoreKey, err := registry.OpenKey(cryptoApiCertStoreRegistryBase, cryptoApiCertStoreRegistryKey, registry.ALL_ACCESS)
if err != nil {
log.Errorf("Couldn't open cert store: %s", err)
return
return err
}
defer certStoreKey.Close()

// get all subkey names in the cert store
subKeys, err := certStoreKey.ReadSubKeyNames(0)
if err != nil {
log.Errorf("Couldn't list certs in cert store: %s", err)
return
return err
}

// for all certs in the cert store
Expand All @@ -144,7 +145,7 @@ func cleanCertsCryptoApi() {
expired, err := checkCertExpiredCryptoApi(certStoreKey, subKeyName)
if err != nil {
log.Errorf("Couldn't check if cert is expired: %s", err)
return
return err
}

// delete the cert if it's expired
Expand All @@ -153,6 +154,7 @@ func cleanCertsCryptoApi() {
}

}
return nil

}

Expand Down
8 changes: 2 additions & 6 deletions certinject/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,8 @@ import (
)

// Injects a certificate by writing to a file. Might be relevant for non-CryptoAPI trust stores.
func injectCertFile(derBytes []byte, fileName string) {

func injectCertFile(derBytes []byte, fileName string) error {
pemBytes := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
err := ioutil.WriteFile(fileName, pemBytes, 0644)
if err != nil {
log.Errore(err, "Error writing cert!")
return
}
return err
}
40 changes: 25 additions & 15 deletions certinject/nss.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
package certinject

import "crypto/sha256"
import "encoding/hex"
import "io/ioutil"
import "os"
import "os/exec"
import "strings"
import "math"
import "time"
import "gopkg.in/hlandau/easyconfig.v1/cflag"
import (
"crypto/sha256"
"encoding/hex"
"io/ioutil"
"math"
"os"
"os/exec"
"strings"
"time"

"gopkg.in/hlandau/easyconfig.v1/cflag"
)

var certDir = cflag.String(flagGroup, "nsscertdir", "", "Directory to store "+
"certificate files. Only use a directory that only ncdns can write "+
"to. (Required if nss is set.)")
var nssDir = cflag.String(flagGroup, "nssdbdir", "", "Directory that "+
"contains NSS's cert9.db. (Required if nss is set.)")

func injectCertNss(derBytes []byte) {
func injectCertNss(derBytes []byte) error {

if certDir.Value() == "" {
log.Fatal("Empty nsscertdir configuration.")
Expand All @@ -31,7 +34,9 @@ func injectCertNss(derBytes []byte) {

path := certDir.Value() + "/" + fingerprintHex + ".pem"

injectCertFile(derBytes, path)
if err := injectCertFile(derBytes, path); err != nil {
return err
}

nickname := nicknameFromFingerprintHexNss(fingerprintHex)

Expand All @@ -44,15 +49,18 @@ func injectCertNss(derBytes []byte) {
if strings.Contains(string(stdoutStderr), "SEC_ERROR_PKCS11_GENERAL_ERROR") {
log.Warn("Temporary SEC_ERROR_PKCS11_GENERAL_ERROR injecting certificate to NSS database; retrying in 1ms...")
time.Sleep(1 * time.Millisecond)
injectCertNss(derBytes)
if err := injectCertNss(derBytes); err != nil {
return err
}
} else {
log.Errorf("Error injecting cert to NSS database: %s\n%s", err, stdoutStderr)
}
}
return nil

}

func cleanCertsNss() {
func cleanCertsNss() error {

if certDir.Value() == "" {
log.Fatal("Empty nsscertdir configuration.")
Expand Down Expand Up @@ -97,7 +105,9 @@ func cleanCertsNss() {
} else if strings.Contains(string(stdoutStderr), "SEC_ERROR_PKCS11_GENERAL_ERROR") {
log.Warn("Temporary SEC_ERROR_PKCS11_GENERAL_ERROR deleting certificate from NSS database; retrying in 1ms...")
time.Sleep(1 * time.Millisecond)
cleanCertsNss()
if err := cleanCertsNss(); err != nil {
return err
}
} else {
log.Fatalf("Error deleting cert from NSS database: %s\n%s", err, stdoutStderr)
}
Expand All @@ -110,7 +120,7 @@ func cleanCertsNss() {
}
}
}

return nil
}

func checkCertExpiredNss(certFile os.FileInfo) (bool, error) {
Expand Down
4 changes: 3 additions & 1 deletion certinject/nss_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ func TestCheckCertExpired(t *testing.T) {

bytesDummy := []byte(`TEST DATA`)

injectCertFile(bytesDummy, testFilename)
if err := injectCertFile(bytesDummy, testFilename); err != nil {
t.Fatal(err)
}
defer os.Remove(testFilename)

info1, err := os.Stat(testFilename)
Expand Down

0 comments on commit b72ba82

Please sign in to comment.