2 Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, version 3 of the License.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
30 "go.cypherpunks.ru/ucspi"
37 verifiedChains [][]*x509.Certificate,
39 var certTheir *x509.Certificate
41 if len(verifiedChains) > 0 {
42 certTheir = verifiedChains[0][0]
44 certTheir, err = x509.ParseCertificate(rawCerts[0])
49 certTheirHash := spkiHash(certTheir)
51 certOurHash := accepted[host]
53 if certTheirHash == certOurHash {
57 certOurHash = rejected[host]
59 if certTheirHash == certOurHash {
60 return ErrRejected{host}
62 daneExists, daneMatched := dane(host, certTheir)
65 sinkCert <- fmt.Sprintf("DANE\t%s\tmatched", host)
67 sinkErr <- fmt.Sprintf("DANE\t%s\tnot matched", host)
70 fn := filepath.Join(*certs, host)
71 certsOur, _, err := ucspi.CertPoolFromFile(fn)
72 if err == nil || dialErr != nil || (daneExists && !daneMatched) {
73 if certsOur != nil && certTheirHash == spkiHash(certsOur[0]) {
74 acceptedAdd(host, certTheirHash)
75 if bytes.Compare(certsOur[0].Raw, rawCerts[0]) != 0 {
76 sinkCert <- fmt.Sprintf("Refresh\t%s\t%s", host, certTheirHash)
84 "Accept:10,Once:11,Reject:12",
88 b.WriteString("Host: " + host)
90 b.WriteString("\nError: " + dialErr.Error())
94 b.WriteString("\nDANE matched")
96 b.WriteString("\nDANE no match!")
99 for i, rawCert := range rawCerts {
100 b.WriteString(fmt.Sprintf("\nTheir %d:\n", i))
101 b.WriteString(certInfo(rawCert))
104 b.WriteString(strings.Repeat("-", 80))
105 for i, cert := range certsOur {
106 b.WriteString(fmt.Sprintf("\nOur %d:\n", i))
107 b.WriteString(certInfo(cert.Raw))
111 switch cmd.Run().(*exec.ExitError).ExitCode() {
113 sinkCert <- fmt.Sprintf("ADD\t%s\t%s", host, certTheirHash)
116 sinkCert <- fmt.Sprintf("ONCE\t%s\t%s", host, certTheirHash)
117 acceptedAdd(host, certTheirHash)
120 rejectedAdd(host, certTheirHash)
123 sinkCert <- fmt.Sprintf("DENY\t%s\t%s", host, certTheirHash)
124 return ErrRejected{host}
127 if !os.IsNotExist(err) {
130 sinkCert <- fmt.Sprintf("TOFU\t%s\t%s", host, certTheirHash)
133 tmp, err := os.CreateTemp(*certs, "")
137 for _, rawCert := range rawCerts {
138 err = pem.Encode(tmp, &pem.Block{Type: "CERTIFICATE", Bytes: rawCert})
144 os.Rename(tmp.Name(), fn)
145 acceptedAdd(host, certTheirHash)