mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 17:05:18 +00:00
fc3304c902
#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/... ```
116 lines
3.0 KiB
Go
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
|
|
}
|