From 4a14c199d8a2acf64fe7448c1fd8ccd1b77edf14 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Fri, 1 Feb 2019 16:53:10 +0300 Subject: [PATCH] /tls/configure -- allow submitting empty certificates and keys to clear them out from config --- control.go | 176 ++++++++++++++++++++++++++++------------------------- 1 file changed, 92 insertions(+), 84 deletions(-) diff --git a/control.go b/control.go index ca699f66..344cd6af 100644 --- a/control.go +++ b/control.go @@ -1049,105 +1049,113 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { return } - certPEM, err := base64.StdEncoding.DecodeString(data.CertificateChain) - if err != nil { - httpError(w, http.StatusBadRequest, "Failed to base64-decode certificate chain: %s", err) - return - } + var mainCert *x509.Certificate - keyPEM, err := base64.StdEncoding.DecodeString(data.PrivateKey) - if err != nil { - httpError(w, http.StatusBadRequest, "Failed to base64-decode private key: %s", err) - return - } - - log.Printf("got certificate: %s", certPEM) - - _, err = tls.X509KeyPair(certPEM, keyPEM) - if err != nil { - httpError(w, http.StatusBadRequest, "Invalid certificate or key: %s", err) - return - } - - // now do a more extended validation - var certs []*pem.Block // PEM-encoded certificates - var skippedBytes []string // skipped bytes - - pemblock := []byte(certPEM) - for { - var decoded *pem.Block - decoded, pemblock = pem.Decode(pemblock) - if decoded == nil { - break - } - if decoded.Type == "CERTIFICATE" { - certs = append(certs, decoded) - } else { - skippedBytes = append(skippedBytes, decoded.Type) - } - } - - var parsedCerts []*x509.Certificate - - for _, cert := range certs { - parsed, err := x509.ParseCertificate(cert.Bytes) + if data.CertificateChain != "" { + certPEM, err := base64.StdEncoding.DecodeString(data.CertificateChain) if err != nil { - httpError(w, http.StatusBadRequest, "failed to parse certificate: %s", err) + httpError(w, http.StatusBadRequest, "Failed to base64-decode certificate chain: %s", err) return } - parsedCerts = append(parsedCerts, parsed) - } - if len(parsedCerts) == 0 { - httpError(w, http.StatusBadRequest, "You have specified an empty certificate") - return - } + log.Printf("got certificate: %s", certPEM) - // spew.Dump(parsedCerts) + if data.PrivateKey != "" { + keyPEM, err := base64.StdEncoding.DecodeString(data.PrivateKey) + if err != nil { + httpError(w, http.StatusBadRequest, "Failed to base64-decode private key: %s", err) + return + } - opts := x509.VerifyOptions{ - DNSName: data.ServerName, - } - - log.Printf("number of certs - %d", len(parsedCerts)) - if len(parsedCerts) > 1 { - // set up an intermediate - pool := x509.NewCertPool() - for _, cert := range parsedCerts[1:] { - log.Printf("got an intermediate cert") - pool.AddCert(cert) + _, err = tls.X509KeyPair(certPEM, keyPEM) + if err != nil { + httpError(w, http.StatusBadRequest, "Invalid certificate or key: %s", err) + return + } } - opts.Intermediates = pool - } - // TODO: save it as a warning rather than error it out -- shouldn't be a big problem - mainCert := parsedCerts[0] - _, err = mainCert.Verify(opts) - if err != nil { - // TODO: let self-signed certs through - httpError(w, http.StatusBadRequest, "Your certificate does not verify: %s", err) - return + // now do a more extended validation + var certs []*pem.Block // PEM-encoded certificates + var skippedBytes []string // skipped bytes + + pemblock := []byte(certPEM) + for { + var decoded *pem.Block + decoded, pemblock = pem.Decode(pemblock) + if decoded == nil { + break + } + if decoded.Type == "CERTIFICATE" { + certs = append(certs, decoded) + } else { + skippedBytes = append(skippedBytes, decoded.Type) + } + } + + var parsedCerts []*x509.Certificate + + for _, cert := range certs { + parsed, err := x509.ParseCertificate(cert.Bytes) + if err != nil { + httpError(w, http.StatusBadRequest, "failed to parse certificate: %s", err) + return + } + parsedCerts = append(parsedCerts, parsed) + } + + if len(parsedCerts) == 0 { + httpError(w, http.StatusBadRequest, "You have specified an empty certificate") + return + } + + // spew.Dump(parsedCerts) + + opts := x509.VerifyOptions{ + DNSName: data.ServerName, + } + + log.Printf("number of certs - %d", len(parsedCerts)) + if len(parsedCerts) > 1 { + // set up an intermediate + pool := x509.NewCertPool() + for _, cert := range parsedCerts[1:] { + log.Printf("got an intermediate cert") + pool.AddCert(cert) + } + opts.Intermediates = pool + } + + // TODO: save it as a warning rather than error it out -- shouldn't be a big problem + mainCert := parsedCerts[0] + _, err = mainCert.Verify(opts) + if err != nil { + // TODO: let self-signed certs through + httpError(w, http.StatusBadRequest, "Your certificate does not verify: %s", err) + return + } + // spew.Dump(chains) } - // spew.Dump(chains) config.TLS = data // update status - config.TLS.StatusCertificate = fmt.Sprintf("Certificate expires on %s", mainCert.NotAfter) //, valid for hostname %s", mainCert.NotAfter, mainCert.Subject.CommonName) - if len(mainCert.DNSNames) == 1 { - config.TLS.StatusCertificate += fmt.Sprintf(", valid for hostname %s", mainCert.DNSNames[0]) - } else if len(mainCert.DNSNames) > 1 { - config.TLS.StatusCertificate += ", valid for hostnames " + strings.Join(mainCert.DNSNames, ", ") - } + if mainCert != nil { + config.TLS.StatusCertificate = fmt.Sprintf("Certificate expires on %s", mainCert.NotAfter) //, valid for hostname %s", mainCert.NotAfter, mainCert.Subject.CommonName) + if len(mainCert.DNSNames) == 1 { + config.TLS.StatusCertificate += fmt.Sprintf(", valid for hostname %s", mainCert.DNSNames[0]) + } else if len(mainCert.DNSNames) > 1 { + config.TLS.StatusCertificate += ", valid for hostnames " + strings.Join(mainCert.DNSNames, ", ") + } - // issue a warning if certificate is about to expire - now := time.Now() - if mainCert.NotAfter.AddDate(0, 0, -30).After(now) { - timeLeft := time.Until(mainCert.NotAfter) - if timeLeft > 0 { - config.TLS.Warning = fmt.Sprintf("Your certificate expires in %.0f days, we recommend you update it soon", timeLeft.Hours()/24) - } else { - config.TLS.Warning = fmt.Sprintf("Your certificate has expired on %s, we recommend you update it immediatedly", mainCert.NotAfter) + // issue a warning if certificate is about to expire + now := time.Now() + if mainCert.NotAfter.AddDate(0, 0, -30).After(now) { + timeLeft := time.Until(mainCert.NotAfter) + if timeLeft > 0 { + config.TLS.Warning = fmt.Sprintf("Your certificate expires in %.0f days, we recommend you update it soon", timeLeft.Hours()/24) + } else { + config.TLS.Warning = fmt.Sprintf("Your certificate has expired on %s, we recommend you update it immediatedly", mainCert.NotAfter) + } } } httpUpdateConfigReloadDNSReturnOK(w, r)