Refactor client.TrustCertificate workflow
This commit is contained in:
parent
b9cb7fe71d
commit
65a5065250
2
cert.go
2
cert.go
@ -16,7 +16,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CertificateStore maps hostnames to certificates.
|
// CertificateStore maps certificate scopes to certificates.
|
||||||
// The zero value of CertificateStore is an empty store ready to use.
|
// The zero value of CertificateStore is an empty store ready to use.
|
||||||
type CertificateStore struct {
|
type CertificateStore struct {
|
||||||
store map[string]tls.Certificate
|
store map[string]tls.Certificate
|
||||||
|
|||||||
31
client.go
31
client.go
@ -43,12 +43,10 @@ type Client struct {
|
|||||||
// the request will not be sent again and the response will be returned.
|
// the request will not be sent again and the response will be returned.
|
||||||
CreateCertificate func(hostname, path string) (tls.Certificate, error)
|
CreateCertificate func(hostname, path string) (tls.Certificate, error)
|
||||||
|
|
||||||
// TrustCertificate determines whether the client should trust
|
// TrustCertificate is called to determine whether the client
|
||||||
// the provided certificate.
|
// should trust a certificate it has not seen before.
|
||||||
// If the returned error is not nil, the connection will be aborted.
|
// If TrustCertificate is nil, the certificate will not be trusted.
|
||||||
// If TrustCertificate is nil, the client will check KnownHosts
|
TrustCertificate func(hostname string, cert *x509.Certificate) Trust
|
||||||
// for the certificate.
|
|
||||||
TrustCertificate func(hostname string, cert *x509.Certificate, knownHosts *KnownHosts) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get performs a Gemini request for the given url.
|
// Get performs a Gemini request for the given url.
|
||||||
@ -198,10 +196,25 @@ func (c *Client) verifyConnection(req *Request, cs tls.ConnectionState) error {
|
|||||||
if err := verifyHostname(cert, hostname); err != nil {
|
if err := verifyHostname(cert, hostname); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Check that the client trusts the certificate
|
// Check the known hosts
|
||||||
var err error
|
err := c.KnownHosts.Lookup(hostname, cert)
|
||||||
|
switch err {
|
||||||
|
case ErrCertificateExpired, ErrCertificateNotFound:
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// See if the client trusts the certificate
|
||||||
if c.TrustCertificate != nil {
|
if c.TrustCertificate != nil {
|
||||||
return c.TrustCertificate(hostname, cert, &c.KnownHosts)
|
switch c.TrustCertificate(hostname, cert) {
|
||||||
|
case TrustOnce:
|
||||||
|
c.KnownHosts.AddTemporary(hostname, cert)
|
||||||
|
return nil
|
||||||
|
case TrustAlways:
|
||||||
|
c.KnownHosts.Add(hostname, cert)
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return ErrCertificateNotTrusted
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
err = c.KnownHosts.Lookup(hostname, cert)
|
err = c.KnownHosts.Lookup(hostname, cert)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,12 +8,22 @@ import (
|
|||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.sr.ht/~adnano/go-gemini"
|
"git.sr.ht/~adnano/go-gemini"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const trustPrompt = `The certificate offered by %s is of unknown trust. Its fingerprint is:
|
||||||
|
%s
|
||||||
|
|
||||||
|
If you knew the fingerprint to expect in advance, verify that this matches.
|
||||||
|
Otherwise, this should be safe to trust.
|
||||||
|
|
||||||
|
[t]rust always; trust [o]nce; [a]bort
|
||||||
|
=> `
|
||||||
|
|
||||||
var (
|
var (
|
||||||
scanner = bufio.NewScanner(os.Stdin)
|
scanner = bufio.NewScanner(os.Stdin)
|
||||||
client = &gemini.Client{}
|
client = &gemini.Client{}
|
||||||