func (edb *EntriesDatabase) InsertCTEntry(entry *ct.LogEntry) error { var cert *x509.Certificate var err error switch entry.Leaf.TimestampedEntry.EntryType { case ct.X509LogEntryType: cert, err = x509.ParseCertificate(entry.Leaf.TimestampedEntry.X509Entry) case ct.PrecertLogEntryType: cert, err = x509.ParseTBSCertificate(entry.Leaf.TimestampedEntry.PrecertEntry.TBSCertificate) } if err != nil { return err } // Skip unimportant entries, if configured if edb.IssuerFilter != nil && !strings.HasPrefix(cert.Issuer.CommonName, *edb.IssuerFilter) { return nil } backoff := &backoff.Backoff{ Jitter: true, } for count := 0; count < 10; count++ { txn, certId, err := edb.insertCertificate(cert) if err != nil { if edb.Verbose { fmt.Printf("Error inserting cert, retrying (%d/10) %s\n", count, err) } if txn != nil { txn.Rollback() } time.Sleep(backoff.Duration()) continue } // // Insert the appropriate CertificateLogEntry, ignoring errors if there was a collision // certLogEntry := &CertificateLogEntry{ CertID: certId, LogID: edb.LogId, EntryID: uint64(entry.Index), EntryTime: Uint64ToTimestamp(entry.Leaf.TimestampedEntry.Timestamp), } err = txn.Insert(certLogEntry) if errorIsNotDuplicate(err) { txn.Rollback() continue } return txn.Commit() } return err }
func (edb *EntriesDatabase) insertCertificate(cert *x509.Certificate) (*gorp.Transaction, uint64, error) { // // Find the Certificate's issuing CA, using a loop since this is contentious. // Also, this is lame. TODO: Be smarter with insertion mutexes // var issuerID int authorityKeyId := base64.StdEncoding.EncodeToString(cert.AuthorityKeyId) edb.IssuersLock.RLock() issuerID, issuerIsInMap := edb.KnownIssuers[authorityKeyId] edb.IssuersLock.RUnlock() if !issuerIsInMap { // Select until we find it, as there may be contention. backoff := &backoff.Backoff{ Jitter: true, } for { // Try to find a matching one first err := edb.DbMap.SelectOne(&issuerID, "SELECT issuerID FROM issuer WHERE authorityKeyID = ?", authorityKeyId) if err != nil { // // This is a new issuer, so let's add it to the database // issuerObj := &Issuer{ AuthorityKeyId: authorityKeyId, CommonName: cert.Issuer.CommonName, } err = edb.DbMap.Insert(issuerObj) if err == nil { issuerID = issuerObj.IssuerID // It worked! Proceed. break } log.Printf("Collision on issuer %v, retrying", issuerObj) time.Sleep(backoff.Duration()) } else { break } } if issuerID == 0 { // Can't continue, so abort return nil, 0, fmt.Errorf("Failed to obtain an issuerID for aki=%s", authorityKeyId) } // Cache for the future edb.IssuersLock.Lock() edb.KnownIssuers[authorityKeyId] = issuerID edb.IssuersLock.Unlock() } // // Find/insert the Certificate from/into the DB // txn, err := edb.DbMap.Begin() if err != nil { return nil, 0, err } var certId uint64 // Parse the serial number serialNum := fmt.Sprintf("%036x", cert.SerialNumber) err = txn.SelectOne(&certId, "SELECT certID FROM certificate WHERE serial = ? AND issuerID = ?", serialNum, issuerID) if err != nil { // // This is a new certificate, so we need to add it to the certificate DB // as well as pull out its metadata // if edb.Verbose { fmt.Println(fmt.Sprintf("Processing %s %#v", serialNum, cert.Subject.CommonName)) } certObj := &Certificate{ Serial: serialNum, IssuerID: issuerID, Subject: cert.Subject.CommonName, NotBefore: cert.NotBefore.UTC(), NotAfter: cert.NotAfter.UTC(), } err = txn.Insert(certObj) if err != nil { return txn, 0, fmt.Errorf("DB error on cert insertion: %#v: %s", certObj, err) } certId = certObj.CertID if certId == 0 { // Can't continue, so abort return txn, 0, fmt.Errorf("Failed to obtain a certId for certificate serial=%s obj=%+v", serialNum, certObj) } } if certId == 0 { // CertID was not located return txn, 0, fmt.Errorf("Failed to locate a certId for certificate serial=%s", serialNum) } // // Insert the raw certificate, if not already there // if edb.FullCerts != nil { err := edb.FullCerts.Store(certId, cert.Raw) if err != nil { return txn, certId, fmt.Errorf("DB error on raw certificate: %d: %s", certId, err) } } // // Process the DNS Names in the Certificate // // De-dupe the CN and the SAN names := make(map[string]struct{}) if cert.Subject.CommonName != "" { names[cert.Subject.CommonName] = struct{}{} } for _, name := range cert.DNSNames { names[name] = struct{}{} } // Loop and insert names into the DB for name, _ := range names { nameId, err := edb.getOrInsertName(txn, name) if errorIsNotDuplicate(err) { return txn, certId, fmt.Errorf("DB error on FQDN ID creation: %s: %s", name, err) } certNameObj := &CertToFQDN{ CertID: certId, NameID: nameId, } err = txn.Insert(certNameObj) if errorIsNotDuplicate(err) { return txn, certId, fmt.Errorf("DB error on FQDN: %s: %s", name, err) } } err = edb.insertRegisteredDomains(txn, certId, names) if err != nil { return txn, certId, fmt.Errorf("DB error on certId %d registered domains: %#v: %s", certId, names, err) } return txn, certId, nil }