fleet/server/datastore/mysql/scep.go
Martin Angers fc3304c902
Move nanomdm dependency in monorepo (#16015)
#15557 

Following the precedent that Lucas used for other similar PRs, the best
way to review is probably by commits.

* The first one simply copies over the files from the fork to the
monorepo
* Second one adjusts all import paths
* Third one tidies up the `go.mod` files
* Last one fixes the linter issues in the nanomdm package

# Checklist for submitter

- ~~Changes file added for user-visible changes in `changes/` or
`orbit/changes/`.~~ (not a user-visible change)
- [x] Manual QA for all new/changed functionality (ran test suite,
re-generated mocks)

I also verified that our Go test suite did run the newly moved `nanomdm`
package steps:

```
ok  	github.com/fleetdm/fleet/v4/server/mdm/nanomdm/cryptoutil	0.003s	coverage: 0.0% of statements in github.com/fleetdm/fleet/v4/...
ok  	github.com/fleetdm/fleet/v4/server/mdm/nanomdm/mdm	0.005s	coverage: 46.2% of statements in github.com/fleetdm/fleet/v4/...
ok  	github.com/fleetdm/fleet/v4/server/mdm/nanomdm/service/certauth	1.320s	coverage: 20.7% of statements in github.com/fleetdm/fleet/v4/...
ok  	github.com/fleetdm/fleet/v4/server/mdm/nanomdm/storage/file	0.007s	coverage: 24.1% of statements in github.com/fleetdm/fleet/v4/...
```
2024-01-11 23:28:48 -03:00

116 lines
3.0 KiB
Go

package mysql
import (
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"database/sql"
_ "embed"
"encoding/pem"
"errors"
"fmt"
"math/big"
apple_mdm "github.com/fleetdm/fleet/v4/server/mdm/apple"
"github.com/fleetdm/fleet/v4/server/mdm/nanomdm/cryptoutil"
"github.com/micromdm/scep/v2/depot"
)
// SCEPDepot is a MySQL-backed SCEP certificate depot.
type SCEPDepot struct {
db *sql.DB
// caCrt holds the CA's certificate.
caCrt *x509.Certificate
// caKey holds the CA private key.
caKey *rsa.PrivateKey
}
var _ depot.Depot = (*SCEPDepot)(nil)
// newSCEPDepot creates and returns a *SCEPDepot.
func newSCEPDepot(db *sql.DB, caCertPEM []byte, caKeyPEM []byte) (*SCEPDepot, error) {
if err := db.Ping(); err != nil {
return nil, err
}
caCrt, err := cryptoutil.DecodePEMCertificate(caCertPEM)
if err != nil {
return nil, err
}
caKey, err := decodeRSAKeyFromPEM(caKeyPEM)
if err != nil {
return nil, err
}
return &SCEPDepot{
db: db,
caCrt: caCrt,
caKey: caKey,
}, nil
}
func decodeRSAKeyFromPEM(key []byte) (*rsa.PrivateKey, error) {
block, _ := pem.Decode(key)
if block.Type != "RSA PRIVATE KEY" {
return nil, errors.New("PEM type is not RSA PRIVATE KEY")
}
return x509.ParsePKCS1PrivateKey(block.Bytes)
}
// CA returns the CA's certificate and private key.
func (d *SCEPDepot) CA(_ []byte) ([]*x509.Certificate, *rsa.PrivateKey, error) {
return []*x509.Certificate{d.caCrt}, d.caKey, nil
}
// Serial allocates and returns a new (increasing) serial number.
func (d *SCEPDepot) Serial() (*big.Int, error) {
result, err := d.db.Exec(`INSERT INTO scep_serials () VALUES ();`)
if err != nil {
return nil, err
}
lid, err := result.LastInsertId()
if err != nil {
return nil, err
}
return big.NewInt(lid), nil
}
// HasCN returns whether the given certificate exists in the depot.
//
// TODO(lucas): Implement and use allowTime and revokeOldCertificate.
// - allowTime are the maximum days before expiration to allow clients to do certificate renewal.
// - revokeOldCertificate specifies whether to revoke the old certificate once renewed.
func (d *SCEPDepot) HasCN(cn string, allowTime int, cert *x509.Certificate, revokeOldCertificate bool) (bool, error) {
var ct int
row := d.db.QueryRow(`SELECT COUNT(*) FROM scep_certificates WHERE name = ?`, cn)
if err := row.Scan(&ct); err != nil {
return false, err
}
return ct >= 1, nil
}
// Put stores a certificate under the given name.
//
// If the provided certificate has empty crt.Subject.CommonName,
// then the hex sha256 of the crt.Raw is used as name.
func (d *SCEPDepot) Put(name string, crt *x509.Certificate) error {
if crt.Subject.CommonName == "" {
name = fmt.Sprintf("%x", sha256.Sum256(crt.Raw))
}
if !crt.SerialNumber.IsInt64() {
return errors.New("cannot represent serial number as int64")
}
certPEM := apple_mdm.EncodeCertPEM(crt)
_, err := d.db.Exec(`
INSERT INTO scep_certificates
(serial, name, not_valid_before, not_valid_after, certificate_pem)
VALUES
(?, ?, ?, ?, ?)`,
crt.SerialNumber.Int64(),
name,
crt.NotBefore,
crt.NotAfter,
certPEM,
)
return err
}