]> Sergey Matveev's repositories - tofuproxy.git/blobdiff - verify.go
Refactoring
[tofuproxy.git] / verify.go
index 05fa9e992d775dc8cdaa42f07cbd43bb7234f8db..4c20c0e6f55f8c1e65e1a5899e8bad1c957e5fa0 100644 (file)
--- a/verify.go
+++ b/verify.go
@@ -1,4 +1,5 @@
 /*
+tofuproxy -- HTTP proxy with TLS certificates management
 Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
 
 This program is free software: you can redistribute it and/or modify
@@ -14,25 +15,63 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-package main
+package tofuproxy
 
 import (
        "bytes"
+       "crypto/sha256"
        "crypto/x509"
+       "encoding/hex"
        "encoding/pem"
        "fmt"
-       "io/ioutil"
        "log"
        "os"
        "os/exec"
        "path/filepath"
        "strings"
+       "sync"
 
        "go.cypherpunks.ru/ucspi"
+       "go.stargrave.org/tofuproxy/fifos"
 )
 
+var (
+       CmdCerttool = "certtool"
+       CmdWish     = "wish8.6"
+
+       Certs     string
+       accepted  = make(map[string]string)
+       acceptedM sync.RWMutex
+       rejected  = make(map[string]string)
+       rejectedM sync.RWMutex
+       VerifyM   sync.Mutex
+)
+
+func spkiHash(cert *x509.Certificate) string {
+       hsh := sha256.Sum256(cert.RawSubjectPublicKeyInfo)
+       return hex.EncodeToString(hsh[:])
+}
+
+func acceptedAdd(addr, h string) {
+       acceptedM.Lock()
+       accepted[addr] = h
+       acceptedM.Unlock()
+}
+
+func rejectedAdd(addr, h string) {
+       rejectedM.Lock()
+       rejected[addr] = h
+       rejectedM.Unlock()
+}
+
+type ErrRejected struct {
+       addr string
+}
+
+func (err ErrRejected) Error() string { return err.addr + " was rejected" }
+
 func certInfo(certRaw []byte) string {
-       cmd := exec.Command("certtool", "--certificate-info", "--inder")
+       cmd := exec.Command(CmdCerttool, "--certificate-info", "--inder")
        cmd.Stdin = bytes.NewReader(certRaw)
        out, err := cmd.Output()
        if err != nil {
@@ -67,6 +106,8 @@ func verifyCert(
                }
        }
        certTheirHash := spkiHash(certTheir)
+       VerifyM.Lock()
+       defer VerifyM.Unlock()
        acceptedM.RLock()
        certOurHash := accepted[host]
        acceptedM.RUnlock()
@@ -82,18 +123,18 @@ func verifyCert(
        daneExists, daneMatched := dane(host, certTheir)
        if daneExists {
                if daneMatched {
-                       sinkCert <- fmt.Sprintf("DANE\t%s\tmatched", host)
+                       fifos.SinkCert <- fmt.Sprintf("DANE\t%s\tmatched", host)
                } else {
-                       sinkErr <- fmt.Sprintf("DANE\t%s\tnot matched", host)
+                       fifos.SinkErr <- fmt.Sprintf("DANE\t%s\tnot matched", host)
                }
        }
-       fn := filepath.Join(*certs, host)
+       fn := filepath.Join(Certs, host)
        certsOur, _, err := ucspi.CertPoolFromFile(fn)
        if err == nil || dialErr != nil || (daneExists && !daneMatched) {
                if certsOur != nil && certTheirHash == spkiHash(certsOur[0]) {
                        acceptedAdd(host, certTheirHash)
                        if bytes.Compare(certsOur[0].Raw, rawCerts[0]) != 0 {
-                               sinkCert <- fmt.Sprintf("Refresh\t%s\t%s", host, certTheirHash)
+                               fifos.SinkCert <- fmt.Sprintf("Refresh\t%s\t%s", host, certTheirHash)
                                goto CertUpdate
                        }
                        return nil
@@ -156,38 +197,38 @@ grid rowconfigure . 0 -weight 1
 grid columnconfigure . 0 -weight 1
 `)
 
-               cmd := exec.Command("wish8.7")
-               ioutil.WriteFile("/tmp/w.tcl", b.Bytes(), 0666)
+               cmd := exec.Command(CmdWish)
+               // ioutil.WriteFile("/tmp/w.tcl", b.Bytes(), 0666)
                cmd.Stdin = &b
                err = cmd.Run()
                exitError, ok := err.(*exec.ExitError)
                if !ok {
-                       sinkCert <- fmt.Sprintf("DENY\t%s\t%s", host, certTheirHash)
+                       fifos.SinkCert <- fmt.Sprintf("DENY\t%s\t%s", host, certTheirHash)
                        return ErrRejected{host}
                }
                switch exitError.ExitCode() {
                case 10:
-                       sinkCert <- fmt.Sprintf("ADD\t%s\t%s", host, certTheirHash)
+                       fifos.SinkCert <- fmt.Sprintf("ADD\t%s\t%s", host, certTheirHash)
                        goto CertUpdate
                case 11:
-                       sinkCert <- fmt.Sprintf("ONCE\t%s\t%s", host, certTheirHash)
+                       fifos.SinkCert <- fmt.Sprintf("ONCE\t%s\t%s", host, certTheirHash)
                        acceptedAdd(host, certTheirHash)
                        return nil
                case 12:
                        rejectedAdd(host, certTheirHash)
                        fallthrough
                default:
-                       sinkCert <- fmt.Sprintf("DENY\t%s\t%s", host, certTheirHash)
+                       fifos.SinkCert <- fmt.Sprintf("DENY\t%s\t%s", host, certTheirHash)
                        return ErrRejected{host}
                }
        } else {
                if !os.IsNotExist(err) {
                        return err
                }
-               sinkCert <- fmt.Sprintf("TOFU\t%s\t%s", host, certTheirHash)
+               fifos.SinkCert <- fmt.Sprintf("TOFU\t%s\t%s", host, certTheirHash)
        }
 CertUpdate:
-       tmp, err := os.CreateTemp(*certs, "")
+       tmp, err := os.CreateTemp(Certs, "")
        if err != nil {
                log.Fatalln(err)
        }