Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

certinject: improved error handling #125

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
5 changes: 3 additions & 2 deletions tlshook/tlshook.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ func DomainValueHookTLS(qname string, ncv *ncdomain.Value) (err error) {
continue
}

// TODO: check return value
certinject.InjectCert(derBytes)
if err := certinject.InjectCert(derBytes); err != nil {
log.Info("Failed to inject certificate: ", err)
}

}

Expand Down