Implement handling of returned certificates

This commit is contained in:
Jan Dittberner 2024-01-14 13:13:58 +01:00
parent 73aad9d74e
commit 8c99fe2fab

View file

@ -100,12 +100,14 @@ WHERE crt = ''
FROM emailcerts FROM emailcerts
WHERE crt_name = '' WHERE crt_name = ''
AND csr_name != '' AND csr_name != ''
AND keytype = 'MS'
AND codesign = 0 AND codesign = 0
AND warning < ?` AND warning < ?`
sqlFindPersonalCodeSigningCertRequests = `SELECT id, csr_name, type, md, subject sqlFindPersonalCodeSigningCertRequests = `SELECT id, csr_name, type, md, subject
FROM emailcerts FROM emailcerts
WHERE crt_name = '' WHERE crt_name = ''
AND csr_name != '' AND csr_name != ''
AND keytype = 'MS'
AND codesign = 1 AND codesign = 1
AND warning < ?` AND warning < ?`
sqlFindPersonalServerCertRequests = `SELECT id, csr_name, type, md, subject sqlFindPersonalServerCertRequests = `SELECT id, csr_name, type, md, subject
@ -117,12 +119,14 @@ WHERE crt_name = ''
FROM orgemailcerts FROM orgemailcerts
WHERE crt_name = '' WHERE crt_name = ''
AND csr_name != '' AND csr_name != ''
AND keytype = 'MS'
AND codesign = 0 AND codesign = 0
AND warning < ?` AND warning < ?`
sqlFindOrganizationalCodeSigningCertRequests = `SELECT id, csr_name, type, md, subject sqlFindOrganizationalCodeSigningCertRequests = `SELECT id, csr_name, type, md, subject
FROM orgemailcerts FROM orgemailcerts
WHERE crt_name = '' WHERE crt_name = ''
AND csr_name != '' AND csr_name != ''
AND keytype = 'MS'
AND codesign = 1 AND codesign = 1
AND warning < ?` AND warning < ?`
sqlFindOrganizationalServerCertRequests = `SELECT id, csr_name, type, md, subject sqlFindOrganizationalServerCertRequests = `SELECT id, csr_name, type, md, subject
@ -155,7 +159,6 @@ SET crt_name=?,
serial=?, serial=?,
expire=? expire=?
WHERE id = ?` WHERE id = ?`
sqlRecordFailedOpenPGP = `UPDATE gpg sqlRecordFailedOpenPGP = `UPDATE gpg
SET warning = warning + 1 SET warning = warning + 1
WHERE id = ?` WHERE id = ?`
@ -394,36 +397,54 @@ func (d *LegacyDB) Run(ctx context.Context) error { //nolint:gocognit,cyclop
if err := d.requestCerts( if err := d.requestCerts(
ctx, sqlFindPersonalClientCertRequests, respPersonalClientCertificate, ctx, sqlFindPersonalClientCertRequests, respPersonalClientCertificate,
func(ctx context.Context, rowID int) {
d.recordFailure(ctx, sqlRecordFailedPersonalClientCertificate, rowID)
},
); err != nil { ); err != nil {
return err return err
} }
if err := d.requestCerts( if err := d.requestCerts(
ctx, sqlFindPersonalCodeSigningCertRequests, respPersonalCodeSigningCertificate, ctx, sqlFindPersonalCodeSigningCertRequests, respPersonalCodeSigningCertificate,
func(ctx context.Context, rowID int) {
d.recordFailure(ctx, sqlRecordFailedPersonalClientCertificate, rowID)
},
); err != nil { ); err != nil {
return err return err
} }
if err := d.requestCerts( if err := d.requestCerts(
ctx, sqlFindPersonalServerCertRequests, respPersonalServerCertificate, ctx, sqlFindPersonalServerCertRequests, respPersonalServerCertificate,
func(ctx context.Context, rowID int) {
d.recordFailure(ctx, sqlRecordFailedPersonalServerCertificate, rowID)
},
); err != nil { ); err != nil {
return err return err
} }
if err := d.requestCerts( if err := d.requestCerts(
ctx, sqlFindOrganizationalClientCertRequests, respOrganizationalClientCertificate, ctx, sqlFindOrganizationalClientCertRequests, respOrganizationalClientCertificate,
func(ctx context.Context, rowID int) {
d.recordFailure(ctx, sqlRecordFailedOrganizationalClientCertificate, rowID)
},
); err != nil { ); err != nil {
return err return err
} }
if err := d.requestCerts( if err := d.requestCerts(
ctx, sqlFindOrganizationalCodeSigningCertRequests, respOrganizationalCodeSigningCertificate, ctx, sqlFindOrganizationalCodeSigningCertRequests, respOrganizationalCodeSigningCertificate,
func(ctx context.Context, rowID int) {
d.recordFailure(ctx, sqlRecordFailedOrganizationalClientCertificate, rowID)
},
); err != nil { ); err != nil {
return err return err
} }
if err := d.requestCerts( if err := d.requestCerts(
ctx, sqlFindOrganizationalServerCertRequests, respOrganizationalServerCertificate, ctx, sqlFindOrganizationalServerCertRequests, respOrganizationalServerCertificate,
func(ctx context.Context, rowID int) {
d.recordFailure(ctx, sqlRecordFailedOrganizationalServerCertificate, rowID)
},
); err != nil { ); err != nil {
return err return err
} }
@ -527,7 +548,7 @@ func (d *LegacyDB) requestSignedOpenPGPKeys(ctx context.Context) error {
return nil return nil
} }
func (d *LegacyDB) requestCerts(ctx context.Context, query string, rt responseType) error { func (d *LegacyDB) requestCerts(ctx context.Context, query string, rt responseType, recordFailureCallback func(ctx context.Context, rowID int)) error {
issuerID, ok := d.issuerIDs[rt] issuerID, ok := d.issuerIDs[rt]
if !ok { if !ok {
return fmt.Errorf("no known issuer id for type %s", rt) return fmt.Errorf("no known issuer id for type %s", rt)
@ -634,6 +655,10 @@ func (d *LegacyDB) requestCerts(ctx context.Context, query string, rt responseTy
"ids_with_issues": idsWithIssues, "ids_with_issues": idsWithIssues,
"rt": rt, "rt": rt,
}).Warn("some certificates failed") }).Warn("some certificates failed")
for _, id := range idsWithIssues {
recordFailureCallback(ctx, id)
}
} }
return nil return nil
@ -783,9 +808,16 @@ func (d *LegacyDB) revokeOrganizationServerCerts(_ context.Context) error {
} }
func (d *LegacyDB) writeCertificate(prefix string, rowID int, signatureData []byte) (string, error) { func (d *LegacyDB) writeCertificate(prefix string, rowID int, signatureData []byte) (string, error) {
crtFileName := path.Join("..", "crt", prefix, strconv.Itoa(rowID/1000), fmt.Sprintf("%s-%d.crt", prefix, rowID)) crtDir := path.Join("..", "crt", prefix, strconv.Itoa(rowID/1000))
err := os.WriteFile(crtFileName, signatureData, 0o644) //nolint:gosec,gomnd err := os.MkdirAll(crtDir, 0o755)
if err != nil {
return "", fmt.Errorf("could not create directory: %w", err)
}
crtFileName := path.Join(crtDir, fmt.Sprintf("%s-%d.crt", prefix, rowID))
err = os.WriteFile(crtFileName, signatureData, 0o644) //nolint:gosec,gomnd
if err != nil { if err != nil {
return "", fmt.Errorf("could not write to file: %w", err) return "", fmt.Errorf("could not write to file: %w", err)
} }
@ -793,11 +825,45 @@ func (d *LegacyDB) writeCertificate(prefix string, rowID int, signatureData []by
return crtFileName, nil return crtFileName, nil
} }
func (d *LegacyDB) recordCertificate(_ context.Context, prefix, query string, rowID int, certBytes []byte) error { type errUnexpectedRowCount struct {
panic(fmt.Sprintf( count int
"not implemented: record certificate to prefix %s with query %s for rowID %d (%d bytes)", }
prefix, query, rowID, len(certBytes),
)) func (e errUnexpectedRowCount) Error() string {
return fmt.Sprintf("unexpected row count %d", e.count)
}
func (d *LegacyDB) recordCertificate(ctx context.Context, prefix, query string, rowID int, certBytes []byte) error {
certificate, err := x509.ParseCertificate(certBytes)
if err != nil {
return fmt.Errorf("could not parse certificate for prefix %s id %d: %w", prefix, rowID, err)
}
expiry := certificate.NotAfter
serial := certificate.SerialNumber.Text(16) //nolint:gomnd
pemData := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certBytes})
crtName, err := d.writeCertificate(prefix, rowID, pemData)
if err != nil {
return fmt.Errorf("could not write certificate data for prefix %s id %d: %w", prefix, rowID, err)
}
res, err := d.db.ExecContext(ctx, query, crtName, serial, expiry, rowID)
if err != nil {
return fmt.Errorf("could not update record for prefix %s id %d: %w", prefix, rowID, err)
}
rows, err := res.RowsAffected()
if err != nil {
return fmt.Errorf("error getting number of affected rows: %w", err)
}
if rows != 1 {
return errUnexpectedRowCount{count: int(rows)}
}
return nil
} }
func (d *LegacyDB) recordPersonalClientRevoke(_ context.Context, rowID int, revokedAt time.Time) error { func (d *LegacyDB) recordPersonalClientRevoke(_ context.Context, rowID int, revokedAt time.Time) error {
@ -912,13 +978,29 @@ func (d *LegacyDB) handleSignedCertificate(
switch pending.responseType { switch pending.responseType {
case respPersonalClientCertificate, respPersonalCodeSigningCertificate: case respPersonalClientCertificate, respPersonalCodeSigningCertificate:
err = d.recordCertificate(ctx, prefixPersonalClient, sqlRecordPersonalClientCert, pending.rowID, r.CertificateData) err = d.recordCertificate(
ctx,
prefixPersonalClient, sqlRecordPersonalClientCert,
pending.rowID, r.CertificateData,
)
case respPersonalServerCertificate: case respPersonalServerCertificate:
err = d.recordCertificate(ctx, prefixPersonalServer, sqlRecordPersonalServerCert, pending.rowID, r.CertificateData) err = d.recordCertificate(
ctx,
prefixPersonalServer, sqlRecordPersonalServerCert,
pending.rowID, r.CertificateData,
)
case respOrganizationalClientCertificate, respOrganizationalCodeSigningCertificate: case respOrganizationalClientCertificate, respOrganizationalCodeSigningCertificate:
err = d.recordCertificate(ctx, prefixOrganizationalClient, sqlRecordOrganizationalClientCert, pending.rowID, r.CertificateData) err = d.recordCertificate(
ctx,
prefixOrganizationalClient, sqlRecordOrganizationalClientCert,
pending.rowID, r.CertificateData,
)
case respOrganizationalServerCertificate: case respOrganizationalServerCertificate:
err = d.recordCertificate(ctx, prefixOrganizationalServer, sqlRecordOrganizationalServerCert, pending.rowID, r.CertificateData) err = d.recordCertificate(
ctx,
prefixOrganizationalServer, sqlRecordOrganizationalServerCert,
pending.rowID, r.CertificateData,
)
default: default:
return fmt.Errorf("unexpected response type for pending request %s", announce.ID) return fmt.Errorf("unexpected response type for pending request %s", announce.ID)
} }