From 1b3ef99af2896156902264aaccced15426874484 Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Thu, 9 Sep 2021 13:54:26 +0300 Subject: [PATCH] Ability to remove hosts from the states, refactoring --- caches/caches.go | 20 +++++++++ cert.pem.do | 3 +- cmd/tofuproxy/main.go | 3 +- doc/index.texi | 2 +- doc/usage.texi | 20 ++++++++- fifos/del.go | 49 ++++++++++++++++++++ fifos/ensure.do | 8 +++- fifos/list.go | 91 ++++++++++++++++++++++++++++++++++++++ fifos/{fifos.go => log.go} | 37 ++++++---------- fifos/multitail.sh | 19 ++++---- fifos/start.go | 43 ++++++++++++++++++ auth.go => httpauth.go | 6 --- rounds/denyFonts.go | 7 +-- rounds/redirectHTML.go | 4 +- rounds/spy.go | 7 +-- rounds/transcodeJXL.go | 8 +--- rounds/transcodeWebP.go | 7 +-- tls.go | 12 ++--- tlsauth.go | 14 +++++- trip.go | 36 ++++++++------- verify.go | 67 +++++++++++++--------------- 21 files changed, 332 insertions(+), 131 deletions(-) create mode 100644 caches/caches.go create mode 100644 fifos/del.go create mode 100644 fifos/list.go rename fifos/{fifos.go => log.go} (60%) create mode 100644 fifos/start.go rename auth.go => httpauth.go (96%) diff --git a/caches/caches.go b/caches/caches.go new file mode 100644 index 0000000..f451da4 --- /dev/null +++ b/caches/caches.go @@ -0,0 +1,20 @@ +package caches + +import ( + "crypto/tls" + "sync" +) + +var ( + Accepted = make(map[string]string) + AcceptedM sync.RWMutex + + Rejected = make(map[string]string) + RejectedM sync.RWMutex + + HTTPAuthCache = make(map[string][2]string) + HTTPAuthCacheM sync.RWMutex + + TLSAuthCache = make(map[string]*tls.Certificate) + TLSAuthCacheM sync.RWMutex +) diff --git a/cert.pem.do b/cert.pem.do index 0110e61..a25ff36 100644 --- a/cert.pem.do +++ b/cert.pem.do @@ -1,2 +1,3 @@ [ -e certgen.cmd ] || redo certgen.cmd -./certgen.cmd +umask 077 +./certgen.cmd > $3 diff --git a/cmd/tofuproxy/main.go b/cmd/tofuproxy/main.go index 8a0be00..96dbd32 100644 --- a/cmd/tofuproxy/main.go +++ b/cmd/tofuproxy/main.go @@ -51,8 +51,7 @@ func main() { } fifos.NoTAI = *notai - fifos.FIFOs = *fifosDir - fifos.Init() + fifos.Start(*fifosDir) tofuproxy.Certs = *certs tofuproxy.CCerts = *ccerts tofuproxy.DNSSrv = *dnsSrv diff --git a/doc/index.texi b/doc/index.texi index 10c8582..3958d31 100644 --- a/doc/index.texi +++ b/doc/index.texi @@ -111,7 +111,7 @@ to forcefully trust the domain. @item @strong{HTTP-based authorization} requests are intercepted and user/password input dialogue is shown. It automatically loads -@strong{initial form} values from @file{.netrc}. +@strong{initial form} values from @strong{@file{.netrc}}. @item TLS @strong{client certificates} supported: separate dialogue window for diff --git a/doc/usage.texi b/doc/usage.texi index 064d57e..8942839 100644 --- a/doc/usage.texi +++ b/doc/usage.texi @@ -48,6 +48,10 @@ main.go:70: listening: [::1]:8080 dns: [::1]:53 certs: ./certs ccerts: ./ccerts @item Point you HTTP/HTTPS clients to @code{http://localhost:8080}. +@item +If you want to use TLS client certificates, then place them to +@file{-ccerts} directory. + @item Watch logs: @example @@ -63,7 +67,19 @@ will be shown Tk-dialog through the @command{wish} invocation. GnuTLS'es @image{dialog,,,Example dialog,.webp} -@item If you want to use TLS client certificates, then place them to -@file{-ccerts} directory. +@item +To list currently accepted, rejected, HTTP authorized, TLS client +authenticated hosts: + +@example +$ cat fifos/list-@{accepted,rejected,http-auth,tls-auth@} +@end example + +@item +To remove knowledge of the host from any of the states mentioned above: + +@example +$ echo www.example.com > fifos/del-tls-auth +@end example @end itemize diff --git a/fifos/del.go b/fifos/del.go new file mode 100644 index 0000000..2ab755d --- /dev/null +++ b/fifos/del.go @@ -0,0 +1,49 @@ +/* +tofuproxy -- HTTP proxy with TLS certificates management +Copyright (C) 2021 Sergey Matveev + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 3 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +package fifos + +import ( + "bufio" + "log" + "os" + "sync" +) + +func del(l *sync.RWMutex, deleter func(string), p string) { + for { + fd, err := os.OpenFile(p, os.O_RDONLY, os.FileMode(0666)) + if err != nil { + log.Fatalln(err) + } + var hosts []string + scanner := bufio.NewScanner(fd) + for scanner.Scan() { + t := scanner.Text() + if len(t) > 0 { + hosts = append(hosts, t) + } + } + fd.Close() + l.Lock() + for _, host := range hosts { + log.Printf("%s: deleting host %s\n", p, host) + deleter(host) + } + l.Unlock() + } +} diff --git a/fifos/ensure.do b/fifos/ensure.do index 7aa937b..5e5cc7e 100644 --- a/fifos/ensure.do +++ b/fifos/ensure.do @@ -1,3 +1,7 @@ -for f in cert dane err ok other redir req tls ; do - [ -p $f ] || mkfifo $f +for f in cert dane err http-auth non-ok ok redir req tls tls-auth various ; do + [ -p log-$f ] || mkfifo log-$f +done +for f in accepted http-auth rejected tls-auth ; do + [ -p list-$f ] || mkfifo list-$f + [ -p del-$f ] || mkfifo del-$f done diff --git a/fifos/list.go b/fifos/list.go new file mode 100644 index 0000000..b06a4cc --- /dev/null +++ b/fifos/list.go @@ -0,0 +1,91 @@ +/* +tofuproxy -- HTTP proxy with TLS certificates management +Copyright (C) 2021 Sergey Matveev + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 3 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +package fifos + +import ( + "crypto/x509" + "fmt" + "log" + "os" + "sync" + + "go.stargrave.org/tofuproxy/caches" +) + +func list(l *sync.RWMutex, m map[string]string, p string) { + for { + fd, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND, os.FileMode(0666)) + if err != nil { + log.Fatalln(err) + } + l.RLock() + for host, hsh := range m { + if _, err = fd.WriteString(fmt.Sprintf("%s\t%s\n", host, hsh)); err != nil { + break + } + } + l.RUnlock() + fd.Close() + } +} + +func listAccepted(p string) { + list(&caches.AcceptedM, caches.Accepted, p) +} + +func listRejected(p string) { + list(&caches.RejectedM, caches.Rejected, p) +} + +func listHTTPAuth(p string) { + for { + fd, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND, os.FileMode(0666)) + if err != nil { + log.Fatalln(err) + } + caches.HTTPAuthCacheM.RLock() + for host, creds := range caches.HTTPAuthCache { + if _, err = fd.WriteString(fmt.Sprintf("%s\t%s\n", host, creds[0])); err != nil { + break + } + } + caches.HTTPAuthCacheM.RUnlock() + fd.Close() + } +} + +func listTLSAuth(p string) { + for { + fd, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND, os.FileMode(0666)) + if err != nil { + log.Fatalln(err) + } + caches.TLSAuthCacheM.RLock() + for host, tlsCert := range caches.TLSAuthCache { + cert, err := x509.ParseCertificate(tlsCert.Certificate[0]) + if err != nil { + log.Fatalln(err) + } + if _, err = fd.WriteString(fmt.Sprintf("%s\t%s\n", host, cert.Subject)); err != nil { + break + } + } + caches.TLSAuthCacheM.RUnlock() + fd.Close() + } +} diff --git a/fifos/fifos.go b/fifos/log.go similarity index 60% rename from fifos/fifos.go rename to fifos/log.go index f07085f..398aa25 100644 --- a/fifos/fifos.go +++ b/fifos/log.go @@ -20,26 +20,28 @@ package fifos import ( "log" "os" - "path/filepath" "time" "go.cypherpunks.ru/tai64n/v2" ) var ( - NoTAI bool - FIFOs string - SinkCert = make(chan string) - SinkDANE = make(chan string) - SinkErr = make(chan string) - SinkOK = make(chan string) - SinkOther = make(chan string) - SinkRedir = make(chan string) - SinkReq = make(chan string) - SinkTLS = make(chan string) + NoTAI bool + + LogCert = make(chan string) + LogDANE = make(chan string) + LogErr = make(chan string) + LogHTTPAuth = make(chan string) + LogNonOK = make(chan string) + LogOK = make(chan string) + LogRedir = make(chan string) + LogReq = make(chan string) + LogTLS = make(chan string) + LogTLSAuth = make(chan string) + LogVarious = make(chan string) ) -func sinker(c chan string, p string) { +func logger(c chan string, p string) { tai := new(tai64n.TAI64N) for { fd, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND, os.FileMode(0666)) @@ -60,14 +62,3 @@ func sinker(c chan string, p string) { fd.Close() } } - -func Init() { - go sinker(SinkCert, filepath.Join(FIFOs, "cert")) - go sinker(SinkDANE, filepath.Join(FIFOs, "dane")) - go sinker(SinkErr, filepath.Join(FIFOs, "err")) - go sinker(SinkOK, filepath.Join(FIFOs, "ok")) - go sinker(SinkOther, filepath.Join(FIFOs, "other")) - go sinker(SinkRedir, filepath.Join(FIFOs, "redir")) - go sinker(SinkReq, filepath.Join(FIFOs, "req")) - go sinker(SinkTLS, filepath.Join(FIFOs, "tls")) -} diff --git a/fifos/multitail.sh b/fifos/multitail.sh index 8a8ccee..ca458e1 100755 --- a/fifos/multitail.sh +++ b/fifos/multitail.sh @@ -2,11 +2,14 @@ multitail \ -wh 10 \ - -t "Certificates" -ci magenta -l "while :; do tai64nlocal < cert ; done" \ - -t "DANE" --label "DANE " -L "while :; do tai64nlocal < dane ; done" \ - -t "Errors" -ci red -L "while :; do tai64nlocal < err ; done" \ - -t "Responses" -ci green --label "< " -l "while :; do tai64nlocal < ok ; done" \ - -t "Others" -ci white -L "while :; do tai64nlocal < other ; done" \ - -t "Redirections" -ci cyan --label "R " -L "while :; do tai64nlocal < redir ; done" \ - -t "Requests" -ci blue --label "> " -L "while :; do tai64nlocal < req ; done" \ - -t "TLS connections" -ci yellow --label "S " -L "while :; do tai64nlocal < tls ; done" + -t "Trust" -ci magenta -l "while :; do tai64nlocal < log-cert ; done" \ + -t "DANE" --label "DANE " -L "while :; do tai64nlocal < log-dane ; done" \ + -t "Error" -ci red -L "while :; do tai64nlocal < log-err ; done" \ + -t "HTTPAuth" -ci green --label "Auth " -L "while :; do tai64nlocal < log-http-auth ; done" \ + -t "TLSAuth" -ci green --label "Auth " -l "while :; do tai64nlocal < log-tls-auth ; done" \ + -t "Response non-OK" --label "< " -L "while :; do tai64nlocal < log-non-ok ; done" \ + -t "Respone OK" -ci green --label "< " -L "while :; do tai64nlocal < log-ok ; done" \ + -t "Redirect " -ci cyan --label "R " -L "while :; do tai64nlocal < log-redir ; done" \ + -t "Request" -ci blue --label "> " -L "while :; do tai64nlocal < log-req ; done" \ + -t "TLS connection" -ci yellow --label "S " -L "while :; do tai64nlocal < log-tls ; done" \ + -t "Various" -ci yellow -L "while :; do tai64nlocal < log-various ; done" diff --git a/fifos/start.go b/fifos/start.go new file mode 100644 index 0000000..f37abe6 --- /dev/null +++ b/fifos/start.go @@ -0,0 +1,43 @@ +package fifos + +import ( + "path/filepath" + + "go.stargrave.org/tofuproxy/caches" +) + +func Start(fifos string) { + go logger(LogCert, filepath.Join(fifos, "log-cert")) + go logger(LogDANE, filepath.Join(fifos, "log-dane")) + go logger(LogErr, filepath.Join(fifos, "log-err")) + go logger(LogHTTPAuth, filepath.Join(fifos, "log-http-auth")) + go logger(LogNonOK, filepath.Join(fifos, "log-non-ok")) + go logger(LogOK, filepath.Join(fifos, "log-ok")) + go logger(LogRedir, filepath.Join(fifos, "log-redir")) + go logger(LogReq, filepath.Join(fifos, "log-req")) + go logger(LogTLS, filepath.Join(fifos, "log-tls")) + go logger(LogTLSAuth, filepath.Join(fifos, "log-tls-auth")) + go logger(LogVarious, filepath.Join(fifos, "log-various")) + + go listAccepted(filepath.Join(fifos, "list-accepted")) + go listHTTPAuth(filepath.Join(fifos, "list-http-auth")) + go listRejected(filepath.Join(fifos, "list-rejected")) + go listTLSAuth(filepath.Join(fifos, "list-tls-auth")) + + go del( + &caches.AcceptedM, func(host string) { delete(caches.Accepted, host) }, + filepath.Join(fifos, "del-accepted"), + ) + go del( + &caches.HTTPAuthCacheM, func(host string) { delete(caches.HTTPAuthCache, host) }, + filepath.Join(fifos, "del-http-auth"), + ) + go del( + &caches.RejectedM, func(host string) { delete(caches.Rejected, host) }, + filepath.Join(fifos, "del-rejected"), + ) + go del( + &caches.TLSAuthCacheM, func(host string) { delete(caches.TLSAuthCache, host) }, + filepath.Join(fifos, "del-tls-auth"), + ) +} diff --git a/auth.go b/httpauth.go similarity index 96% rename from auth.go rename to httpauth.go index 62155d8..5e97636 100644 --- a/auth.go +++ b/httpauth.go @@ -27,12 +27,6 @@ import ( "os/exec" "path/filepath" "strings" - "sync" -) - -var ( - authCache = make(map[string][2]string) - authCacheM sync.Mutex ) func findInNetrc(host string) (string, string) { diff --git a/rounds/denyFonts.go b/rounds/denyFonts.go index 4fe1195..ad55310 100644 --- a/rounds/denyFonts.go +++ b/rounds/denyFonts.go @@ -36,12 +36,7 @@ func RoundDenyFonts( fallthrough case "font/otf", "font/ttf", "font/woff", "font/woff2": http.NotFound(w, req) - fifos.SinkOther <- fmt.Sprintf( - "%s %s\t%d\tdeny fonts", - req.Method, - req.URL.String(), - http.StatusNotFound, - ) + fifos.LogVarious <- fmt.Sprintf("%s %s\tdeny fonts", req.Method, req.URL) resp.Body.Close() return false, nil } diff --git a/rounds/redirectHTML.go b/rounds/redirectHTML.go index e92a4f9..2bf4ce2 100644 --- a/rounds/redirectHTML.go +++ b/rounds/redirectHTML.go @@ -82,8 +82,8 @@ func RoundRedirectHTML( resp.StatusCode, http.StatusText(resp.StatusCode), redirType, location, location, ))) - fifos.SinkRedir <- fmt.Sprintf( - "%s %s\t%s\t%s", req.Method, resp.Status, req.URL.String(), location, + fifos.LogRedir <- fmt.Sprintf( + "%s %s\t%s\t%s", req.Method, req.URL, resp.Status, location, ) return false, nil } diff --git a/rounds/spy.go b/rounds/spy.go index 3b242f8..cb12480 100644 --- a/rounds/spy.go +++ b/rounds/spy.go @@ -62,12 +62,7 @@ func RoundDenySpy( ) (bool, error) { if IsSpy(host) { http.NotFound(w, req) - fifos.SinkOther <- fmt.Sprintf( - "%s %s\t%d\tdeny spy", - req.Method, - req.URL.String(), - http.StatusNotFound, - ) + fifos.LogVarious <- fmt.Sprintf("%s %s\tdeny spy", req.Method, req.URL) return false, nil } return true, nil diff --git a/rounds/transcodeJXL.go b/rounds/transcodeJXL.go index 2043fb1..e8a9628 100644 --- a/rounds/transcodeJXL.go +++ b/rounds/transcodeJXL.go @@ -64,12 +64,8 @@ func transcodeCmd2Png( w.Header().Add("Content-Type", "image/png") w.WriteHeader(http.StatusOK) w.Write(data) - fifos.SinkOther <- fmt.Sprintf( - "%s %s\t%d\t%s transcoded to PNG", - req.Method, - req.URL.String(), - http.StatusOK, - contentType, + fifos.LogVarious <- fmt.Sprintf( + "%s %s\t%s transcoded to PNG", req.Method, req.URL, contentType, ) return false, nil } diff --git a/rounds/transcodeWebP.go b/rounds/transcodeWebP.go index ff1fd8c..a38b7f2 100644 --- a/rounds/transcodeWebP.go +++ b/rounds/transcodeWebP.go @@ -65,11 +65,6 @@ func RoundTranscodeWebP( w.Header().Add("Content-Type", "image/png") w.WriteHeader(http.StatusOK) w.Write(data) - fifos.SinkOther <- fmt.Sprintf( - "%s %s\t%d\tWebP transcoded to PNG", - req.Method, - req.URL.String(), - http.StatusOK, - ) + fifos.LogVarious <- fmt.Sprintf("%s %s\tWebP transcoded to PNG", req.Method, req.URL) return false, nil } diff --git a/tls.go b/tls.go index 2db3ceb..6f8ce8c 100644 --- a/tls.go +++ b/tls.go @@ -103,8 +103,8 @@ func (h *HTTPSHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { } func dialTLS(ctx context.Context, network, addr string) (net.Conn, error) { - host := strings.TrimSuffix(addr, ":443") - ccg := ClientCertificateGetter{host} + host := strings.Split(addr, ":")[0] + ccg := ClientCertificateGetter{host: host} cfg := tls.Config{ VerifyPeerCertificate: func( rawCerts [][]byte, @@ -131,15 +131,15 @@ func dialTLS(ctx context.Context, network, addr string) (net.Conn, error) { var err error conn, err = tls.Dial(network, addr, &cfg) if err != nil { - fifos.SinkErr <- fmt.Sprintf("%s\t%s", addr, dialErr.Error()) + fifos.LogErr <- fmt.Sprintf("%s\t%s", addr, dialErr.Error()) return nil, err } } connState := conn.ConnectionState() - if connState.DidResume { - fifos.SinkTLS <- fmt.Sprintf( + if !connState.DidResume { + fifos.LogTLS <- fmt.Sprintf( "%s\t%s %s\t%s\t%s", - strings.TrimSuffix(addr, ":443"), + addr, ucspi.TLSVersion(connState.Version), tls.CipherSuiteName(connState.CipherSuite), spkiHash(connState.PeerCertificates[0]), diff --git a/tlsauth.go b/tlsauth.go index 531245d..8622a0c 100644 --- a/tlsauth.go +++ b/tlsauth.go @@ -31,6 +31,7 @@ import ( "strings" "go.cypherpunks.ru/ucspi" + "go.stargrave.org/tofuproxy/caches" "go.stargrave.org/tofuproxy/fifos" ) @@ -38,11 +39,18 @@ var CCerts string type ClientCertificateGetter struct { host string + auth bool } func (g *ClientCertificateGetter) get( cri *tls.CertificateRequestInfo, ) (*tls.Certificate, error) { + caches.TLSAuthCacheM.RLock() + tlsCert := caches.TLSAuthCache[g.host] + caches.TLSAuthCacheM.RUnlock() + if tlsCert != nil { + return tlsCert, nil + } var b bytes.Buffer b.WriteString(fmt.Sprintf(` wm title . "TLS client authentication: %s" @@ -107,6 +115,10 @@ grid .submit if err != nil { return &tls.Certificate{}, nil } - fifos.SinkCert <- fmt.Sprintf("ClientAuth\t%s\t%s", g.host, certs[i].Subject) + fifos.LogTLSAuth <- fmt.Sprintf("%s\t%s", g.host, certs[i].Subject) + caches.TLSAuthCacheM.Lock() + caches.TLSAuthCache[g.host] = tlsCerts[i] + caches.TLSAuthCacheM.Unlock() + g.auth = true return tlsCerts[i], nil } diff --git a/trip.go b/trip.go index a2e2565..90a5ad3 100644 --- a/trip.go +++ b/trip.go @@ -27,6 +27,7 @@ import ( "time" "github.com/dustin/go-humanize" + "go.stargrave.org/tofuproxy/caches" "go.stargrave.org/tofuproxy/fifos" "go.stargrave.org/tofuproxy/rounds" ) @@ -58,7 +59,7 @@ type Round func( ) (bool, error) func roundTrip(w http.ResponseWriter, req *http.Request) { - fifos.SinkReq <- fmt.Sprintf("%s %s", req.Method, req.URL.String()) + fifos.LogReq <- fmt.Sprintf("%s %s", req.Method, req.URL) host := strings.TrimSuffix(req.URL.Host, ":443") for _, round := range []Round{ rounds.RoundNoHead, @@ -74,40 +75,44 @@ func roundTrip(w http.ResponseWriter, req *http.Request) { reqFlags := []string{} unauthorized := false - authCacheM.Lock() - if creds, ok := authCache[req.URL.Host]; ok { + caches.HTTPAuthCacheM.RLock() + if creds, ok := caches.HTTPAuthCache[req.URL.Host]; ok { req.SetBasicAuth(creds[0], creds[1]) + fifos.LogHTTPAuth <- fmt.Sprintf("%s %s\t%s", req.Method, req.URL, creds[0]) unauthorized = true } - authCacheM.Unlock() + caches.HTTPAuthCacheM.RUnlock() Retry: resp, err := transport.RoundTrip(req) if err != nil { - fifos.SinkErr <- fmt.Sprintf("%s\t%s", req.URL.Host, err.Error()) + fifos.LogErr <- fmt.Sprintf("%s\t%s", req.URL.Host, err.Error()) http.Error(w, err.Error(), http.StatusBadGateway) return } if resp.StatusCode == http.StatusUnauthorized { resp.Body.Close() - authCacheM.Lock() + caches.HTTPAuthCacheM.Lock() if unauthorized { - delete(authCache, req.URL.Host) + delete(caches.HTTPAuthCache, req.URL.Host) } else { unauthorized = true } - fifos.SinkOther <- fmt.Sprintf("%s\tauthorization required", req.URL.Host) + fifos.LogVarious <- fmt.Sprintf( + "%s %s\tHTTP authorization required", req.Method, req.URL.Host, + ) user, pass, err := authDialog(host, resp.Header.Get("WWW-Authenticate")) if err != nil { - authCacheM.Unlock() - fifos.SinkErr <- fmt.Sprintf("%s\t%s", req.URL.Host, err.Error()) + caches.HTTPAuthCacheM.Unlock() + fifos.LogErr <- fmt.Sprintf("%s\t%s", req.URL.Host, err.Error()) http.Error(w, err.Error(), http.StatusInternalServerError) return } - authCache[req.URL.Host] = [2]string{user, pass} - authCacheM.Unlock() + caches.HTTPAuthCache[req.URL.Host] = [2]string{user, pass} + caches.HTTPAuthCacheM.Unlock() req.SetBasicAuth(user, pass) + fifos.LogHTTPAuth <- fmt.Sprintf("%s %s\t%s", req.Method, req.URL, user) goto Retry } if unauthorized { @@ -156,16 +161,15 @@ Retry: resp.Body.Close() msg := fmt.Sprintf( "%s %s\t%s\t%s\t%s\t%s", - req.Method, - req.URL.String(), + req.Method, req.URL, resp.Status, resp.Header.Get("Content-Type"), humanize.IBytes(uint64(n)), strings.Join(reqFlags, ","), ) if resp.StatusCode == http.StatusOK { - fifos.SinkOK <- msg + fifos.LogOK <- msg } else { - fifos.SinkOther <- msg + fifos.LogNonOK <- msg } } diff --git a/verify.go b/verify.go index b2eb606..de71f91 100644 --- a/verify.go +++ b/verify.go @@ -32,6 +32,7 @@ import ( "sync" "go.cypherpunks.ru/ucspi" + "go.stargrave.org/tofuproxy/caches" "go.stargrave.org/tofuproxy/fifos" ) @@ -39,12 +40,8 @@ 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 + Certs string + VerifyM sync.Mutex ) func spkiHash(cert *x509.Certificate) string { @@ -52,18 +49,6 @@ func spkiHash(cert *x509.Certificate) string { 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 } @@ -108,33 +93,35 @@ func verifyCert( certTheirHash := spkiHash(certTheir) VerifyM.Lock() defer VerifyM.Unlock() - acceptedM.RLock() - certOurHash := accepted[host] - acceptedM.RUnlock() + caches.AcceptedM.RLock() + certOurHash := caches.Accepted[host] + caches.AcceptedM.RUnlock() if certTheirHash == certOurHash { return nil } - rejectedM.RLock() - certOurHash = rejected[host] - rejectedM.RUnlock() + caches.RejectedM.RLock() + certOurHash = caches.Rejected[host] + caches.RejectedM.RUnlock() if certTheirHash == certOurHash { return ErrRejected{host} } daneExists, daneMatched := dane(host, certTheir) if daneExists { if daneMatched { - fifos.SinkDANE <- fmt.Sprintf("%s\tmatched", host) + fifos.LogDANE <- fmt.Sprintf("%s\tACK", host) } else { - fifos.SinkDANE <- fmt.Sprintf("%s\tNOT matched", host) + fifos.LogDANE <- fmt.Sprintf("%s\tNAK", 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) + caches.AcceptedM.Lock() + caches.Accepted[host] = certTheirHash + caches.AcceptedM.Unlock() if bytes.Compare(certsOur[0].Raw, rawCerts[0]) != 0 { - fifos.SinkCert <- fmt.Sprintf("Refresh\t%s\t%s", host, certTheirHash) + fifos.LogCert <- fmt.Sprintf("Refresh\t%s\t%s", host, certTheirHash) goto CertUpdate } return nil @@ -190,7 +177,7 @@ proc doAccept {} { exit 10 } proc doOnce {} { exit 11 } proc doReject {} { exit 12 } button .bAccept -text "Accept" -bg green -command doAccept -button .bOnce -text "Once" -command doOnce +button .bOnce -text "Once" -bg green -command doOnce button .bReject -text "Reject" -bg red -command doReject grid .bAccept .bOnce .bReject grid rowconfigure . 0 -weight 1 @@ -203,29 +190,33 @@ grid columnconfigure . 0 -weight 1 err = cmd.Run() exitError, ok := err.(*exec.ExitError) if !ok { - fifos.SinkCert <- fmt.Sprintf("DENY\t%s\t%s", host, certTheirHash) + fifos.LogCert <- fmt.Sprintf("Reject\t%s\t%s", host, certTheirHash) return ErrRejected{host} } switch exitError.ExitCode() { case 10: - fifos.SinkCert <- fmt.Sprintf("ADD\t%s\t%s", host, certTheirHash) + fifos.LogCert <- fmt.Sprintf("Accept\t%s\t%s", host, certTheirHash) goto CertUpdate case 11: - fifos.SinkCert <- fmt.Sprintf("ONCE\t%s\t%s", host, certTheirHash) - acceptedAdd(host, certTheirHash) + fifos.LogCert <- fmt.Sprintf("Once\t%s\t%s", host, certTheirHash) + caches.AcceptedM.Lock() + caches.Accepted[host] = certTheirHash + caches.AcceptedM.Unlock() return nil case 12: - rejectedAdd(host, certTheirHash) + caches.RejectedM.Lock() + caches.Rejected[host] = certTheirHash + caches.RejectedM.Unlock() fallthrough default: - fifos.SinkCert <- fmt.Sprintf("DENY\t%s\t%s", host, certTheirHash) + fifos.LogCert <- fmt.Sprintf("Reject\t%s\t%s", host, certTheirHash) return ErrRejected{host} } } else { if !os.IsNotExist(err) { return err } - fifos.SinkCert <- fmt.Sprintf("TOFU\t%s\t%s", host, certTheirHash) + fifos.LogCert <- fmt.Sprintf("TOFU\t%s\t%s", host, certTheirHash) } CertUpdate: tmp, err := os.CreateTemp(Certs, "") @@ -240,6 +231,8 @@ CertUpdate: } tmp.Close() os.Rename(tmp.Name(), fn) - acceptedAdd(host, certTheirHash) + caches.AcceptedM.Lock() + caches.Accepted[host] = certTheirHash + caches.AcceptedM.Unlock() return nil } -- 2.44.0