Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion certdb/sql/database_accessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ package sql
import (
"errors"
"fmt"
"regexp"
"time"

"github.com/cloudflare/cfssl/certdb"
cferr "github.com/cloudflare/cfssl/errors"
"github.com/go-sql-driver/mysql"
"github.com/mattn/go-sqlite3"

"github.com/jmoiron/sqlx"
"github.com/kisielk/sqlstruct"
Expand Down Expand Up @@ -76,7 +79,37 @@ var _ certdb.Accessor = &Accessor{}

func wrapSQLError(err error) error {
if err != nil {
return cferr.Wrap(cferr.CertStoreError, cferr.Unknown, err)

reason := cferr.Unknown

// Use detailed reason on unique constraint errors (i.e. will allow API client
// to detect already used cert serial in DB when API client is
// allowed to provide cert serial on cert singing). We don't detect this
// kind of problems by querying table for exisitng key before insert/update
// to avoid races. Unique constraint errors have different codes in different
// DB engines so must be detected separately.

// MySQL/MariaDB
var mysqlErr *mysql.MySQLError
if errors.As(err, &mysqlErr) && mysqlErr.Number == 1062 {
reason = cferr.DuplicateEntry
}

// SQLite
var sqliteErr sqlite3.Error
if errors.As(err, &sqliteErr) && (sqliteErr.Code == sqlite3.ErrConstraint) {

// Parsing error message is probably the only way to detect duplicate key
// errors in SQLite now...
if regexp.MustCompile(`(^|\s)UNIQUE constraint failed .*`).MatchString(err.Error()) {
reason = cferr.DuplicateEntry
}
}

// PostgresSQL
// TBD. See also: https://github.com/go-gorm/gorm/issues/4135

return cferr.Wrap(cferr.CertStoreError, reason, err)
}
return nil
}
Expand Down
3 changes: 2 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/cloudflare/cfssl/helpers"
"github.com/cloudflare/cfssl/log"
ocspConfig "github.com/cloudflare/cfssl/ocsp/config"

// empty import of zlint/v3 required to have lints registered.
_ "github.com/zmap/zlint/v3"
"github.com/zmap/zlint/v3/lint"
Expand Down Expand Up @@ -121,7 +122,7 @@ type SigningProfile struct {
CSRWhitelist *CSRWhitelist
NameWhitelist *regexp.Regexp
ExtensionWhitelist map[string]bool
ClientProvidesSerialNumbers bool
ClientProvidesSerialNumbers bool `json:"client_provides_serial_numbers"`
// LintRegistry is the collection of lints that should be used if
// LintErrLevel is configured. By default all ZLint lints are used. If
// ExcludeLints or ExcludeLintSources are set then this registry will be
Expand Down
3 changes: 3 additions & 0 deletions errors/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@ const (
// RecordNotFound occurs when a SQL query targeting on one unique
// record failes to update the specified row in the table.
RecordNotFound
// DuplicateEntry occurs when SQL query tries to insert or update
// using key that must be unique in db table but already exists there.
DuplicateEntry
)

// The error interface implementation, which formats to a JSON object string.
Expand Down