Ejemplo n.º 1
0
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))
	}
}
Ejemplo n.º 2
0
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
}