From cb151f75aa3cdfcd61d5e84bd18dcc81d038f682 Mon Sep 17 00:00:00 2001 From: sloum Date: Sat, 9 May 2020 11:04:06 -0700 Subject: [PATCH 1/2] Handles cert expirations silently --- gemini/gemini.go | 17 +++++++++++++---- main.go | 17 ++++++++++++++++- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/gemini/gemini.go b/gemini/gemini.go index e13cb59..b83bee2 100644 --- a/gemini/gemini.go +++ b/gemini/gemini.go @@ -49,8 +49,8 @@ func (t *TofuDigest) Purge(host string) error { return fmt.Errorf("Invalid host %q", host) } -func (t *TofuDigest) Add(host, hash string) { - t.certs[strings.ToLower(host)] = hash +func (t *TofuDigest) Add(host, hash string, time int64) { + t.certs[strings.ToLower(host)] = fmt.Sprintf("%s|%d", hash, time) } func (t *TofuDigest) Exists(host string) bool { @@ -70,9 +70,10 @@ func (t *TofuDigest) Find(host string) (string, error) { func (t *TofuDigest) Match(host string, cState *tls.ConnectionState) error { host = strings.ToLower(host) now := time.Now() + localCert := strings.SplitN(t.certs[host], "|", -1)[0] for _, cert := range cState.PeerCertificates { - if t.certs[host] != hashCert(cert.Raw) { + if localCert != hashCert(cert.Raw) { continue } @@ -118,7 +119,7 @@ func (t *TofuDigest) newCert(host string, cState *tls.ConnectionState) error { continue } - t.Add(host, hashCert(cert.Raw)) + t.Add(host, hashCert(cert.Raw), cert.NotAfter.Unix()) return nil } @@ -132,6 +133,14 @@ func (t *TofuDigest) IniDump() string { var out strings.Builder out.WriteString("[CERTS]\n") for k, v := range t.certs { + vals := strings.SplitN(v, "|", -1) + now := time.Now() + if len(vals) > 1 { + ts, err := strconv.ParseInt(vals[1], 10, 64) + if err != nil || now.Unix() > ts { + continue + } + } out.WriteString(k) out.WriteString("=") out.WriteString(v) diff --git a/main.go b/main.go index 53918f7..1266eeb 100644 --- a/main.go +++ b/main.go @@ -25,8 +25,10 @@ import ( "os" "os/signal" "path/filepath" + "strconv" "strings" "syscall" + "time" "tildegit.org/sloum/bombadillo/config" "tildegit.org/sloum/bombadillo/cui" @@ -132,7 +134,20 @@ func loadConfig() { } for _, v := range settings.Certs { - bombadillo.Certs.Add(v.Key, v.Value) + // Remove expired certs + vals := strings.SplitN(v.Value, "|", -1) + if len(vals) < 2 { + continue + } + ts, err := strconv.ParseInt(vals[1], 10, 64) + now := time.Now() + if err != nil || now.Unix() > ts { + continue + } + // Satisfied that the cert is not expired + // or malformed: add to the current client + // instance + bombadillo.Certs.Add(v.Key, vals[0], ts) } } From 00313442d4c5dd8770c077ff06ea7e9ae341c670 Mon Sep 17 00:00:00 2001 From: sloum Date: Sat, 9 May 2020 16:18:38 -0700 Subject: [PATCH 2/2] Hopefully an improvement to the initial way of dealing with expired certs --- gemini/gemini.go | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/gemini/gemini.go b/gemini/gemini.go index b83bee2..5b3cdb1 100644 --- a/gemini/gemini.go +++ b/gemini/gemini.go @@ -67,10 +67,8 @@ func (t *TofuDigest) Find(host string) (string, error) { return "", fmt.Errorf("Invalid hostname, no key saved") } -func (t *TofuDigest) Match(host string, cState *tls.ConnectionState) error { - host = strings.ToLower(host) +func (t *TofuDigest) Match(host, localCert string, cState *tls.ConnectionState) error { now := time.Now() - localCert := strings.SplitN(t.certs[host], "|", -1)[0] for _, cert := range cState.PeerCertificates { if localCert != hashCert(cert.Raw) { @@ -126,6 +124,33 @@ func (t *TofuDigest) newCert(host string, cState *tls.ConnectionState) error { return fmt.Errorf(reasons.String()) } +func (t *TofuDigest) GetCertAndTimestamp(host string) (string, int64, error) { + certTs, err := t.Find(host) + if err != nil { + return "", -1, err + } + certTsSplit := strings.SplitN(certTs, "|", -1) + if len(certTsSplit) < 2 { + _ = t.Purge(host) + return certTsSplit[0], -1, fmt.Errorf("Invalid certstring, no delimiter") + } + ts, err := strconv.ParseInt(certTsSplit[1], 10, 64) + if err != nil { + _ = t.Purge(host) + return certTsSplit[0], -1, err + } + now := time.Now() + if ts < now.Unix() { + // Ignore error return here since an error would indicate + // the host does not exist and we have already checked for + // that and the desired outcome of the action is that the + // host will no longer exist, so we are good either way + _ = t.Purge(host) + return "", -1, fmt.Errorf("Expired cert") + } + return certTsSplit[0], ts, nil +} + func (t *TofuDigest) IniDump() string { if len(t.certs) < 1 { return "" @@ -133,14 +158,6 @@ func (t *TofuDigest) IniDump() string { var out strings.Builder out.WriteString("[CERTS]\n") for k, v := range t.certs { - vals := strings.SplitN(v, "|", -1) - now := time.Now() - if len(vals) > 1 { - ts, err := strconv.ParseInt(vals[1], 10, 64) - if err != nil || now.Unix() > ts { - continue - } - } out.WriteString(k) out.WriteString("=") out.WriteString(v) @@ -185,9 +202,11 @@ func Retrieve(host, port, resource string, td *TofuDigest) (string, error) { return "", fmt.Errorf("Insecure, no certificates offered by server") } - if td.Exists(host) { + localCert, localTs, err := td.GetCertAndTimestamp(host) + + if localTs > 0 { // See if we have a matching cert - err := td.Match(host, &connState) + err := td.Match(host, localCert, &connState) if err != nil && err.Error() != "EXP" { // If there is no match and it isnt because of an expiration // just return the error