diff options
| -rw-r--r-- | actions.go | 60 | ||||
| -rw-r--r-- | tls.go | 18 |
2 files changed, 67 insertions, 11 deletions
@@ -127,13 +127,13 @@ func Reload(state *BrowserState, conf *Config) error { body := io.LimitReader(bytes.NewBuffer(input), int64(len(input))) state.Url.Fragment = "" - response, err = upload(state.Url.String(), body, tlsConf) + response, err = upload(state, state.Url.String(), body, tlsConf) state.Url.Fragment = "prompt" if err != nil { return err } } else { - response, err = fetch(urlStr, tlsConf) + response, err = fetch(state, urlStr, tlsConf) if err != nil { return err } @@ -152,7 +152,7 @@ outer: state.Url = response.Request.URL state.Url.RawQuery = url.QueryEscape(strings.TrimRight(line, "\n")) - response, err = fetch(state.Url.String(), tlsConf) + response, err = fetch(state, state.Url.String(), tlsConf) if err != nil { return err } @@ -164,7 +164,7 @@ outer: state.Url = response.Request.URL state.Url.RawQuery = url.QueryEscape(strings.TrimRight(string(line), "\n")) - response, err = fetch(state.Url.String(), tlsConf) + response, err = fetch(state, state.Url.String(), tlsConf) if err != nil { return err } @@ -190,14 +190,58 @@ outer: return HandleResource(state, conf) } -func fetch(u string, tlsConf *tls.Config) (*sliderule.Response, error) { +func fetch(state *BrowserState, u string, tlsConf *tls.Config) (*sliderule.Response, error) { tlsConf.ClientSessionCache = nil - return sliderule.NewClient(tlsConf).Fetch(u) + response, err := sliderule.NewClient(tlsConf).Fetch(u) + var tofuErr *TOFUViolation + if errors.As(err, &tofuErr) { + writeError(err.Error()) + state.Readline.SetPrompt("Trust new certificate instead (y/n)? [n] ") + line, err := state.Readline.Readline() + if err != nil { + return nil, err + } + if line != "y" { + return nil, tofuErr + } + + tofuStore[tofuErr.domain] = tofuErr.got + if err := saveTofuStore(tofuStore); err != nil { + return nil, err + } + + return sliderule.NewClient(tlsConf).Fetch(u) + } else if err != nil { + return nil, err + } + return response, nil } -func upload(u string, body io.Reader, tlsConf *tls.Config) (*sliderule.Response, error) { +func upload(state *BrowserState, u string, body io.Reader, tlsConf *tls.Config) (*sliderule.Response, error) { tlsConf.ClientSessionCache = nil - return sliderule.NewClient(tlsConf).Upload(u, body) + response, err := sliderule.NewClient(tlsConf).Upload(u, body) + var tofuErr *TOFUViolation + if errors.As(err, &tofuErr) { + writeError(err.Error()) + state.Readline.SetPrompt("Trust new certificate instead (y/n)? [n] ") + line, err := state.Readline.Readline() + if err != nil { + return nil, err + } + if line != "y" { + return nil, tofuErr + } + + tofuStore[tofuErr.domain] = tofuErr.got + if err := saveTofuStore(tofuStore); err != nil { + return nil, err + } + + return sliderule.NewClient(tlsConf).Upload(u, body) + } else if err != nil { + return nil, err + } + return response, nil } func externalMessage() ([]byte, error) { @@ -8,7 +8,7 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/hex" - "errors" + "fmt" "math/big" "os" "time" @@ -23,7 +23,15 @@ func tlsConfig(state *BrowserState) *tls.Config { var tofuStore map[string]string -var ErrTOFUViolation = errors.New("certificate for this domain has changed") +type TOFUViolation struct { + domain string + expected string + got string +} + +func (tv *TOFUViolation) Error() string { + return fmt.Sprintf("certificate for domain %s has changed from %s to %s", tv.domain, tv.expected, tv.got) +} var anonymousTLS = &tls.Config{ InsecureSkipVerify: true, @@ -43,7 +51,11 @@ func tofuVerify(connState tls.ConnectionState) error { } if certhash != expected { - return ErrTOFUViolation + return &TOFUViolation{ + domain: connState.ServerName, + expected: expected, + got: certhash, + } } return nil } |
