func (a *API) submissionHandler(w http.ResponseWriter, r *http.Request) { a.stats.Inc("submission.http.rate", 1, 1.0) start := time.Now() defer func() { a.stats.TimingDuration("submission.http.latency", time.Since(start), 1.0) }() // XXX: Debugging statements fmt.Println("REQUEST!") err := r.ParseForm() if err != nil { a.stats.Inc("submission.http.error-rate", 1, 1.0) fmt.Println(err) return } sr, err := formToRequest(r.Form) if err != nil { a.stats.Inc("submission.http.error-rate", 1, 1.0) fmt.Println(err) return } a.stats.Inc("submission.http.certificates.rate", int64(len(sr.Certs)), 1.0) sr.ClientIP = net.ParseIP(r.RemoteAddr) // Check if cert has been revoked var revocationInfo []revocationDescription for _, c := range sr.Certs { if revoked, reason, err := a.db.IsRevoked(core.Fingerprint(c.Raw)); revoked { // Send message to client but don't skip submission revocationInfo = append(revocationInfo, revocationDescription{ Serial: core.BigIntToString(c.SerialNumber), Reason: reason, }) } else if err != nil { // BAD, but continue on our way... a.stats.Inc("submission.http.error-rate", 1, 1.0) fmt.Println(err) } } // ...I know it's not reading but channels allow concurrent writes so this is // still safe a.sMu.RLock() defer a.sMu.RUnlock() // Add submission to channel for workers to process a.submissions <- sr if len(revocationInfo) > 0 { a.stats.Inc("submission.http.revoked-certificates-seen", int64(len(revocationInfo)), 1.0) revokedJSON, err := json.Marshal(revocationInfo) if err != nil { a.stats.Inc("submission.http.error-rate", 1, 1.0) // BAD fmt.Println(err) fmt.Fprintf(w, "Couldn't marshal revocation information: %s", err) return } // Set specific error code? fmt.Fprint(w, string(revokedJSON)) } }
func (a *API) addCertificate(chainMeta *db.CertificateChainMeta, cert *x509.Certificate, valid, leaf bool, asnNum int, now time.Time, serverIP net.IP) error { // Check if certificate has already been added, if so no need to do work intensive // decomposition fingerprint := core.Fingerprint(cert.Raw) keyDER, err := x509.MarshalPKIXPublicKey(cert.PublicKey) if err != nil { return err } keyFingerprint := core.Fingerprint(keyDER) certModel := &db.Certificate{ Size: len(cert.Raw), Fingerprint: fingerprint, KeyFingerprint: keyFingerprint, PublicKeyAlg: uint8(cert.PublicKeyAlgorithm), Valid: valid, CertVersion: uint8(cert.Version), Root: cert.IsCA, Expired: time.Now().After(cert.NotAfter), BasicConstraints: cert.BasicConstraintsValid, MaxPathLen: cert.MaxPathLen, MaxPathLenZero: cert.MaxPathLenZero, SignatureAlg: uint8(cert.SignatureAlgorithm), Signature: cert.Signature, NotBefore: cert.NotBefore, NotAfter: cert.NotAfter, SubjectKeyIdentifier: cert.SubjectKeyId, AuthorityKeyIdentifier: cert.AuthorityKeyId, KeyUsage: uint8(cert.KeyUsage), CommonName: cert.Subject.CommonName, Serial: fmt.Sprintf("%X", cert.SerialNumber), IssuerCommonName: cert.Issuer.CommonName, Country: strings.Join(cert.Subject.Country, "; "), Province: strings.Join(cert.Subject.Province, "; "), Locality: strings.Join(cert.Subject.Locality, "; "), Organization: strings.Join(cert.Subject.Organization, "; "), OrganizationalUnit: strings.Join(cert.Subject.OrganizationalUnit, "; "), } exists, err := a.db.CertificateExists(fingerprint) if err != nil { return err } // Decompsoe certificate into all the different bits we want err = a.db.AddCertificate(certModel) if err != nil { return err } if exists { a.stats.Inc("submission.parsing.certificates.previously-seen", 1.0, 1) return nil } if time.Now().After(cert.NotAfter) { a.stats.Inc("submission.parsing.certificates.expired", 1, 1.0) } if valid { a.stats.Inc("submission.parsing.certificates.valid", 1, 1.0) } else { a.stats.Inc("submission.parsing.certificates.invalid", 1, 1.0) } if cert.IsCA && valid { a.stats.Inc("submission.parsing.certificates.roots.rate", 1, 1.0) } if sig, ok := core.SignatureAlgorithms[cert.SignatureAlgorithm]; ok { a.stats.Inc(fmt.Sprintf("submission.parsing.certificates.signature-algorithm.%s", sig), 1, 1.0) } // XXX: All of the db.Add... operations below could be run concurrently to // improve performance somewhat (MySQL might not like this too much, // but my gut says InnoDB should be able to handle it). // Raw certificate because why not err = a.db.AddRawCertificate(&db.RawCertificate{ CertificateFingerprint: fingerprint, DER: cert.Raw, }) if err != nil { // Continue fmt.Println(err) } // Public key keyExists, err := a.db.KeyExists(keyFingerprint) if err != nil { return err } key := &db.Key{ Fingerprint: keyFingerprint, Valid: valid, } switch t := cert.PublicKey.(type) { case *rsa.PublicKey: a.stats.Inc("submission.parsing.certificates.key-types.RSA", 1, 1.0) key.Type = uint8(x509.RSA) key.RSAModulusSize = int64(t.N.BitLen()) key.RSAModulus = t.N.Bytes() key.RSAExponent = int64(t.E) case *dsa.PublicKey: a.stats.Inc("submission.parsing.certificates.key-types.DSA", 1, 1.0) key.Type = uint8(x509.DSA) key.DSAP = t.P.Bytes() key.DSAQ = t.Q.Bytes() key.DSAG = t.G.Bytes() key.DSAY = t.Y.Bytes() case *ecdsa.PublicKey: a.stats.Inc("submission.parsing.certificates.key-types.ECDSA", 1, 1.0) key.Type = uint8(x509.ECDSA) key.ECDSACurve = t.Params().Name key.ECDSAX = t.X.Bytes() key.ECDSAY = t.Y.Bytes() } err = a.db.AddPublicKey(key) if err != nil { return err } if !keyExists { err = a.db.AddRawKey(&db.RawKey{ KeyFingerprint: keyFingerprint, DER: keyDER, }) if err != nil { // Continue... fmt.Println(err) } } // Names sections err = a.db.AddDNSNames(fingerprint, cert.DNSNames) if err != nil { // Continue fmt.Println(err) } err = a.db.AddIPAddresses(fingerprint, cert.IPAddresses) if err != nil { // Continue fmt.Println(err) } err = a.db.AddEmailAddresses(fingerprint, cert.EmailAddresses) if err != nil { // Continue fmt.Println(err) } err = a.db.AddConstrainedDNSNames(fingerprint, cert.PermittedDNSDomains) if err != nil { // Continue fmt.Println(err) } // Remote revocation services err = a.db.AddOCSPEndpoints(fingerprint, cert.OCSPServer) if err != nil { // Continue fmt.Println(err) } err = a.db.AddCRLEndpoints(fingerprint, cert.CRLDistributionPoints) if err != nil { // Continue fmt.Println(err) } // Extra subject sections err = a.db.AddSubjectExtensions(fingerprint, cert.Subject.Names) if err != nil { // Continue fmt.Println(err) } // Various identifiers and extensions err = a.db.AddPolicyIdentifiers(fingerprint, cert.PolicyIdentifiers) if err != nil { // Continue fmt.Println(err) } err = a.db.AddCertificateExtensions(fingerprint, cert.Extensions) if err != nil { // Continue fmt.Println(err) } err = a.db.AddIssuingCertificateURL(fingerprint, cert.IssuingCertificateURL) if err != nil { // Continue fmt.Println(err) } // Add report err = a.db.AddReport(&db.Report{ Source: 0, // XXX: fix this... Domain: "", // XXX: fix this... ServerIP: serverIP.String(), CertificateFingerprint: fingerprint, ChainFingerprint: chainMeta.Fingerprint, Leaf: leaf, ASNNumber: asnNum, Submitted: now, }) if err != nil { return err } return nil }