diff --git a/certificates.go b/certificates.go index 97738ee..868d286 100644 --- a/certificates.go +++ b/certificates.go @@ -14,6 +14,7 @@ import ( "crypto/x509/pkix" "encoding/base64" "fmt" + "io/ioutil" "log" "math/big" mathrand "math/rand" @@ -235,15 +236,23 @@ func matchClientCert(u *url.URL) tls.Certificate { var bestMatchScore int splitPath := strings.Split(u.Path, "/") + splitPathLen := len(splitPath) muClientCerts.RLock() for i, c := range clientCerts { if u.Host != c.Host { continue } + certPathLen := len(c.Path) + var l int + if splitPathLen < certPathLen { + l = splitPathLen + } else { + l = certPathLen + } score := 1 - for i, p := range splitPath { - if p == c.Path[i] { + for i := 0; i < l; i++ { + if splitPath[i] == c.Path[i] { score++ } } @@ -403,3 +412,25 @@ func saveTOFU() { time.Sleep(10 * time.Minute) } } + +// Return a PEM certificate by first trying to interpret the argument as the +// certificate data and if that fails as a path to a certificate. +func loadCertificate(certificate string) ([]byte, error) { + if strings.HasPrefix(certificate, "-----BEGIN ") { + return []byte(certificate), nil + } else { + if strings.HasPrefix(certificate, "~") { + h, err := os.UserHomeDir() + if err != nil { + return []byte{}, err + } + certificate = strings.Replace(certificate, "~", h, 1) + } + keyPEM, err := ioutil.ReadFile(certificate) + if err != nil { + return []byte{}, err + } + return keyPEM, nil + } +} + diff --git a/gneto.go b/gneto.go index 68613ba..3bacf40 100644 --- a/gneto.go +++ b/gneto.go @@ -225,7 +225,17 @@ func init() { continue } c.URL = pc.URL - c.Cert, err = tls.X509KeyPair([]byte(pc.CertPEM), []byte(pc.KeyPEM)) + certPEM, err := loadCertificate(pc.CertPEM) + if err != nil { + log.Printf("init: failed to read certificate from %s: %v", pc.CertPEM, err) + continue + } + keyPEM, err := loadCertificate(pc.KeyPEM) + if err != nil { + log.Printf("init: failed to read certificate private key from %s: %v", pc.KeyPEM, err) + continue + } + c.Cert, err = tls.X509KeyPair(certPEM, keyPEM) if err != nil { log.Printf("init: failed to parse client certificate PEM data for %s: %v", c.URL, err) continue diff --git a/sample-client-certs.json b/sample-client-certs.json index 09b9192..e24ad6b 100644 --- a/sample-client-certs.json +++ b/sample-client-certs.json @@ -11,7 +11,7 @@ }, { "url": "gemini://example.com/foo/bar/", - "certPEM": "-----BEGIN CERTIFICATE-----\nMIIDfzCCAmegAwIBAgIUAgQAyx0G0Pn6gIH5cZQFuqs7DgMwDQYJKoZIhvcNAQEL\nBQAwTzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxEzARBgNVBAoM\nCk9yZyBuYW1lIDQxFjAUBgNVBAMMDUNvbW1vbiBuYW1lIDQwHhcNMjAxMjMwMjI1\nMjA3WhcNMjExMjMwMjI1MjA3WjBPMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29t\nZS1TdGF0ZTETMBEGA1UECgwKT3JnIG5hbWUgNDEWMBQGA1UEAwwNQ29tbW9uIG5h\nbWUgNDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMHQb1ssymqUa8ud\nOLWspoz3E5UBHnIQwecz+pQZJE9G4eGDMN+ryj4r/NqMb7cwLEjSixk4is/ElVL8\nYPnreTlls+AJD1kvmaACrabgDh18WErIVpJT4MIMk25LXdcmUzKlBubwiAPcteRx\ntXvwZhHvdK38SCx+LpbkTvk+v08uuThRyt/su2ryx0RZDBDxYvUBBgYYnDCswxgA\n0IiL6Mcg7dySC/VP8w7Vd3qeu2L4/PRRmZpk9BldaXy84r+ZMuuA3KagFl0nn3NK\nn0G9oN+NXqrv9wcBd/1LboD1TPzYZAoy5obZlGRkudaBCT41x/MdGQteJykAW81H\noe//n58CAwEAAaNTMFEwHQYDVR0OBBYEFMbsTI6LWo3CDHc+8W23vY+8JlPLMB8G\nA1UdIwQYMBaAFMbsTI6LWo3CDHc+8W23vY+8JlPLMA8GA1UdEwEB/wQFMAMBAf8w\nDQYJKoZIhvcNAQELBQADggEBACk/oXfdg6arJB9DD0BbUu2m6fwvLoQBSZGfIYJ7\nkRnlpy30gFZw+fh5aygnG2JEB4iJ42VS8kAsjMo1QJ6G/HdfQCsK2GpeTfSR/FzF\n6w6gTfpBtmpxZ4x8697n93pw7RRNAMcE796/uIAEI1CTJhdyy95a4yS5qO+8k2tz\nCFvITWBeY0gKhooOxTGsTe56iTSW7bu/IYsumNnK8LydgEhF5bSgidlY8scdNIGF\nPl4vQMOSner5GV92QTigMgto+V6gCIKZ/ocJytMzsZjQaU9HYWKW9w0aO8+MJtfw\n7hkt6tM6gmvQJPa6B3tgLWIZYIAukPFP2Uw6nTFCybgRxlg=\n-----END CERTIFICATE-----", - "keyPEM": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDB0G9bLMpqlGvL\nnTi1rKaM9xOVAR5yEMHnM/qUGSRPRuHhgzDfq8o+K/zajG+3MCxI0osZOIrPxJVS\n/GD563k5ZbPgCQ9ZL5mgAq2m4A4dfFhKyFaSU+DCDJNuS13XJlMypQbm8IgD3LXk\ncbV78GYR73St/Egsfi6W5E75Pr9PLrk4Ucrf7Ltq8sdEWQwQ8WL1AQYGGJwwrMMY\nANCIi+jHIO3ckgv1T/MO1Xd6nrti+Pz0UZmaZPQZXWl8vOK/mTLrgNymoBZdJ59z\nSp9BvaDfjV6q7/cHAXf9S26A9Uz82GQKMuaG2ZRkZLnWgQk+NcfzHRkLXicpAFvN\nR6Hv/5+fAgMBAAECggEAT3jhgHhgU82iWe6RB2XRMXJK1EmOK2WVrPFSaZHZoqxF\nCQybaMvvdQjieNcnI+2b6wxyPxhBSyqntEyKk1Ar+A4s8MumQILVMpiGjivHfe2z\n4o49eWjte4Rem9ZJl2Q+r0CeEOpGS5cf+plpunV71RTkOkd9bMkc34CkpW1GUvf1\nXwxXbA+6nvCf3AsJg99ML1evBMih3qE7G4RxQvcN5GCAs5ik2vg/2tPZ9Bz3D1aL\nA0z1WkT15ully2lp2UMMhXelKNp33TLcNEad9zNPsVyP/1HGVOu8OFTT+kduwbks\ndzp2l+l2l9U7z3ovYRtSwNYvenCqe0I4iW8SVMuOWQKBgQDyw3nBbwgS6wgQNqH6\ntqrV+BWcJh9hRyvSVa2tTuJgom6VTGA63kCAXMBQP5NeDMgdT8POs9LhuwSJjsTm\n1oam8CxafMBA56g7Yk8v1vmusQLc3sTmgM1aJCSz6nuqAqii4juY2WLxqHuPtA+K\nXPuaZY3TNdqYJteTrjefQDJgKwKBgQDMYbfEUHu6qPG6cra9Ewye7e0sKL0afd17\nOeJNAPCPqj3xhqVdyZxQoP7JoCqvdb7qsdzE0hrqT8wz6kewIcOuQw+94Q8WQB1r\nLmHK/ElQqqqKX1A/vUsheiUncZXfSWhn0wLhK2jzfE4vTPCut9G+GAZeEo6XP9hJ\nWUgL3xAQXQKBgFUawBFW2k6/hGkirfBVn1g5i2HdX3zb1Xn8PVGXaPcVmozOZKHe\njmAxo92+G+yJsJHOB8fr9I7b4EBVA6QlKrlVU0c5mrBmRezjjv0ncQqT1XwWwJJK\nLzNdnlxQZJ/6JKJNizUPaIdtWDvNlPEdnbrHxUt5ArP6UfLczXUPrVlXAoGBAI/s\nCAJAQsDRE1geS7ydW3fXR/qevXuYQozx2AAttzkTwwb/sBd5T5AfrwvAtA5WthiD\nI1meMsHpa3zrb4wCWs+t/bnScFNU1uKRCOBuBBq/89P5BoO8AtoSL3O1MrrH8LOc\nDh0N+pvrXwtD/QCsBRD9zg4Ut0Ce/um361ogvCkpAoGAca3QwbHO/8x45sM8UwCf\nErHcAfRHZgJbl5fugpyAjM4/Gm8jK1y2ShB0CKcfQGNwfNoNRXiRw1FZ6ExgCQhZ\nELUQt+1txpQai0wcylOumrx2GWrqSCEyQZLGmifDZ4v+bIYteSh2ymP1eHDBl3HJ\n1lI8cht4BYtPNrCnuYlFES8=\n-----END PRIVATE KEY-----" + "certPEM": "~/.local/share/certificates/example.com.crt", + "keyPEM": "~/.local/share/certificates/example.com.key" } ]