From 8c99fe2fabbdea2fc71a55773e4ca11179cd4ce7 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sun, 14 Jan 2024 13:13:58 +0100 Subject: [PATCH] Implement handling of returned certificates --- internal/legacydb/legacydb.go | 108 ++++++++++++++++++++++++++++++---- 1 file changed, 95 insertions(+), 13 deletions(-) diff --git a/internal/legacydb/legacydb.go b/internal/legacydb/legacydb.go index b343096..e9188e2 100644 --- a/internal/legacydb/legacydb.go +++ b/internal/legacydb/legacydb.go @@ -100,12 +100,14 @@ WHERE crt = '' FROM emailcerts WHERE crt_name = '' AND csr_name != '' + AND keytype = 'MS' AND codesign = 0 AND warning < ?` sqlFindPersonalCodeSigningCertRequests = `SELECT id, csr_name, type, md, subject FROM emailcerts WHERE crt_name = '' AND csr_name != '' + AND keytype = 'MS' AND codesign = 1 AND warning < ?` sqlFindPersonalServerCertRequests = `SELECT id, csr_name, type, md, subject @@ -117,12 +119,14 @@ WHERE crt_name = '' FROM orgemailcerts WHERE crt_name = '' AND csr_name != '' + AND keytype = 'MS' AND codesign = 0 AND warning < ?` sqlFindOrganizationalCodeSigningCertRequests = `SELECT id, csr_name, type, md, subject FROM orgemailcerts WHERE crt_name = '' AND csr_name != '' + AND keytype = 'MS' AND codesign = 1 AND warning < ?` sqlFindOrganizationalServerCertRequests = `SELECT id, csr_name, type, md, subject @@ -155,7 +159,6 @@ SET crt_name=?, serial=?, expire=? WHERE id = ?` - sqlRecordFailedOpenPGP = `UPDATE gpg SET warning = warning + 1 WHERE id = ?` @@ -394,36 +397,54 @@ func (d *LegacyDB) Run(ctx context.Context) error { //nolint:gocognit,cyclop if err := d.requestCerts( ctx, sqlFindPersonalClientCertRequests, respPersonalClientCertificate, + func(ctx context.Context, rowID int) { + d.recordFailure(ctx, sqlRecordFailedPersonalClientCertificate, rowID) + }, ); err != nil { return err } if err := d.requestCerts( ctx, sqlFindPersonalCodeSigningCertRequests, respPersonalCodeSigningCertificate, + func(ctx context.Context, rowID int) { + d.recordFailure(ctx, sqlRecordFailedPersonalClientCertificate, rowID) + }, ); err != nil { return err } if err := d.requestCerts( ctx, sqlFindPersonalServerCertRequests, respPersonalServerCertificate, + func(ctx context.Context, rowID int) { + d.recordFailure(ctx, sqlRecordFailedPersonalServerCertificate, rowID) + }, ); err != nil { return err } if err := d.requestCerts( ctx, sqlFindOrganizationalClientCertRequests, respOrganizationalClientCertificate, + func(ctx context.Context, rowID int) { + d.recordFailure(ctx, sqlRecordFailedOrganizationalClientCertificate, rowID) + }, ); err != nil { return err } if err := d.requestCerts( ctx, sqlFindOrganizationalCodeSigningCertRequests, respOrganizationalCodeSigningCertificate, + func(ctx context.Context, rowID int) { + d.recordFailure(ctx, sqlRecordFailedOrganizationalClientCertificate, rowID) + }, ); err != nil { return err } if err := d.requestCerts( ctx, sqlFindOrganizationalServerCertRequests, respOrganizationalServerCertificate, + func(ctx context.Context, rowID int) { + d.recordFailure(ctx, sqlRecordFailedOrganizationalServerCertificate, rowID) + }, ); err != nil { return err } @@ -527,7 +548,7 @@ func (d *LegacyDB) requestSignedOpenPGPKeys(ctx context.Context) error { 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] if !ok { 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, "rt": rt, }).Warn("some certificates failed") + + for _, id := range idsWithIssues { + recordFailureCallback(ctx, id) + } } 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) { - 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 { 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 } -func (d *LegacyDB) recordCertificate(_ context.Context, prefix, query string, rowID int, certBytes []byte) error { - panic(fmt.Sprintf( - "not implemented: record certificate to prefix %s with query %s for rowID %d (%d bytes)", - prefix, query, rowID, len(certBytes), - )) +type errUnexpectedRowCount struct { + count int +} + +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 { @@ -912,13 +978,29 @@ func (d *LegacyDB) handleSignedCertificate( switch pending.responseType { 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: - err = d.recordCertificate(ctx, prefixPersonalServer, sqlRecordPersonalServerCert, pending.rowID, r.CertificateData) + err = d.recordCertificate( + ctx, + prefixPersonalServer, sqlRecordPersonalServerCert, + pending.rowID, r.CertificateData, + ) 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: - err = d.recordCertificate(ctx, prefixOrganizationalServer, sqlRecordOrganizationalServerCert, pending.rowID, r.CertificateData) + err = d.recordCertificate( + ctx, + prefixOrganizationalServer, sqlRecordOrganizationalServerCert, + pending.rowID, r.CertificateData, + ) default: return fmt.Errorf("unexpected response type for pending request %s", announce.ID) }