checkin: add flag to configure allowable time skew (#887)

This commit is contained in:
Kory Prince
2023-06-15 16:21:19 -05:00
committed by GitHub
parent 58e74371b8
commit f0dd6fe7c5
5 changed files with 50 additions and 16 deletions

View File

@@ -16,6 +16,7 @@ import (
"github.com/micromdm/micromdm/mdm"
"github.com/micromdm/micromdm/mdm/enroll"
"github.com/micromdm/micromdm/pkg/crypto"
httputil2 "github.com/micromdm/micromdm/pkg/httputil"
"github.com/micromdm/micromdm/platform/apns"
"github.com/micromdm/micromdm/platform/appstore"
@@ -127,6 +128,7 @@ func serve(args []string) error {
flQueue = flagset.String("queue", env.String("MICROMDM_QUEUE", "builtin"), "command queue type")
flDMURL = flagset.String("dm", env.String("DM", ""), "URL to send Declarative Management requests to")
flLogTime = flagset.Bool("log-time", false, "Include timestamp in log messages")
flP7Skew = flagset.Int("device-signature-skew", env.Int("MICROMDM_DEVICE_SIGNATURE_SKEW", 0), "Sets the allowable clock skew (in seconds) when verifying device signatures")
)
flagset.Usage = usageFor(flagset, "micromdm serve [flags]")
if err := flagset.Parse(args); err != nil {
@@ -250,7 +252,9 @@ func serve(args []string) error {
scepEndpoints.PostEndpoint = scep.EndpointLoggingMiddleware(scepComponentLogger)(scepEndpoints.PostEndpoint)
scepHandler := scep.MakeHTTPHandler(scepEndpoints, sm.SCEPService, scepComponentLogger)
enrollHandlers := enroll.MakeHTTPHandlers(ctx, enroll.MakeServerEndpoints(sm.EnrollService, sm.SCEPDepot), httptransport.ServerErrorLogger(httpLogger))
pkcs7Verifier := &crypto.PKCS7Verifier{MaxSkew: time.Duration(*flP7Skew) * time.Second}
enrollHandlers := enroll.MakeHTTPHandlers(ctx, enroll.MakeServerEndpoints(sm.EnrollService, sm.SCEPDepot), pkcs7Verifier, httptransport.ServerErrorLogger(httpLogger))
r, options := httputil2.NewRouter(logger)
@@ -266,7 +270,7 @@ func serve(args []string) error {
}
mdmEndpoints := mdm.MakeServerEndpoints(sm.MDMService)
mdm.RegisterHTTPHandlers(r, mdmEndpoints, logger)
mdm.RegisterHTTPHandlers(r, mdmEndpoints, pkcs7Verifier, logger)
// API commands. Only handled if the user provides an api key.
if *flAPIKey != "" {

View File

@@ -22,11 +22,12 @@ type HTTPHandlers struct {
OTAPhase2Phase3Handler http.Handler
}
func MakeHTTPHandlers(ctx context.Context, endpoints Endpoints, opts ...httptransport.ServerOption) HTTPHandlers {
func MakeHTTPHandlers(ctx context.Context, endpoints Endpoints, v *crypto.PKCS7Verifier, opts ...httptransport.ServerOption) HTTPHandlers {
ver := verifier{PKCS7Verifier: v}
h := HTTPHandlers{
EnrollHandler: httptransport.NewServer(
endpoints.GetEnrollEndpoint,
decodeMDMEnrollRequest,
ver.decodeMDMEnrollRequest,
encodeMobileconfigResponse,
opts...,
),
@@ -38,7 +39,7 @@ func MakeHTTPHandlers(ctx context.Context, endpoints Endpoints, opts ...httptran
),
OTAPhase2Phase3Handler: httptransport.NewServer(
endpoints.OTAPhase2Phase3Endpoint,
decodeOTAPhase2Phase3Request,
ver.decodeOTAPhase2Phase3Request,
encodeMobileconfigResponse,
opts...,
),
@@ -50,7 +51,11 @@ func decodeEmptyRequest(_ context.Context, _ *http.Request) (interface{}, error)
return nil, nil
}
func decodeMDMEnrollRequest(_ context.Context, r *http.Request) (interface{}, error) {
type verifier struct {
*crypto.PKCS7Verifier
}
func (v verifier) decodeMDMEnrollRequest(_ context.Context, r *http.Request) (interface{}, error) {
switch r.Method {
case "GET":
return mdmEnrollRequest{}, nil
@@ -63,7 +68,7 @@ func decodeMDMEnrollRequest(_ context.Context, r *http.Request) (interface{}, er
if err != nil {
return nil, err
}
err = p7.Verify()
err = v.Verify(p7)
if err != nil {
return nil, err
}
@@ -93,7 +98,7 @@ func encodeMobileconfigResponse(ctx context.Context, w http.ResponseWriter, resp
return err
}
func decodeOTAPhase2Phase3Request(_ context.Context, r *http.Request) (interface{}, error) {
func (v verifier) decodeOTAPhase2Phase3Request(_ context.Context, r *http.Request) (interface{}, error) {
data, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, err
@@ -102,7 +107,7 @@ func decodeOTAPhase2Phase3Request(_ context.Context, r *http.Request) (interface
if err != nil {
return nil, err
}
err = p7.Verify()
err = v.Verify(p7)
if err != nil {
return nil, err
}

View File

@@ -13,6 +13,7 @@ import (
httptransport "github.com/go-kit/kit/transport/http"
"github.com/gorilla/mux"
"github.com/groob/plist"
"github.com/micromdm/micromdm/pkg/crypto"
"github.com/pkg/errors"
"go.mozilla.org/pkcs7"
)
@@ -29,12 +30,12 @@ func MakeServerEndpoints(s Service) Endpoints {
}
}
func RegisterHTTPHandlers(r *mux.Router, e Endpoints, logger log.Logger) {
func RegisterHTTPHandlers(r *mux.Router, e Endpoints, v *crypto.PKCS7Verifier, logger log.Logger) {
options := []httptransport.ServerOption{
httptransport.ServerErrorEncoder(encodeError),
httptransport.ServerErrorLogger(logger),
httptransport.ServerBefore(httptransport.PopulateRequestContext),
httptransport.ServerBefore(populateDeviceCertificateFromSignRequestHeader),
httptransport.ServerBefore((verifier{PKCS7Verifier: v}).populateDeviceCertificateFromSignRequestHeader),
}
r.Methods(http.MethodPut).Path("/mdm/checkin").Handler(httptransport.NewServer(
@@ -65,7 +66,11 @@ func DeviceCertificateFromContext(ctx context.Context) (*x509.Certificate, error
return cert, err
}
func populateDeviceCertificateFromSignRequestHeader(ctx context.Context, r *http.Request) context.Context {
type verifier struct {
*crypto.PKCS7Verifier
}
func (v verifier) populateDeviceCertificateFromSignRequestHeader(ctx context.Context, r *http.Request) context.Context {
bodyReader := r.Body
defer bodyReader.Close()
@@ -76,7 +81,7 @@ func populateDeviceCertificateFromSignRequestHeader(ctx context.Context, r *http
// Replace our body object with a fully buffered response
r.Body = ioutil.NopCloser(bytes.NewBuffer(body))
cert, err := verifySignature(r.Header.Get("Mdm-Signature"), body)
cert, err := v.verifySignature(r.Header.Get("Mdm-Signature"), body)
ctx = context.WithValue(ctx, ContextKeyDeviceCertificate, cert)
ctx = context.WithValue(ctx, ContextKeyDeviceCertificateVerifyError, err)
@@ -107,7 +112,7 @@ func mdmRequestBody(r *http.Request, s interface{}) ([]byte, error) {
}
// Verify MDM header signature. Note: does NOT verify device certificate
func verifySignature(header string, body []byte) (*x509.Certificate, error) {
func (v verifier) verifySignature(header string, body []byte) (*x509.Certificate, error) {
if header == "" {
return nil, errors.New("signature missing")
}
@@ -120,7 +125,7 @@ func verifySignature(header string, body []byte) (*x509.Certificate, error) {
return nil, errors.Wrap(err, "CMS parse decoded MDM SignMessage signature")
}
p7.Content = body
if err := p7.Verify(); err != nil {
if err := v.Verify(p7); err != nil {
return nil, errors.Wrap(err, "CMS verify MDM Signed Message")
}
cert := p7.GetOnlySigner()

View File

@@ -45,7 +45,7 @@ func Test_mdmMdmSignatureHeader(t *testing.T) {
req.Header.Set("Mdm-Signature", b64sig)
ctx := context.Background()
ctx = populateDeviceCertificateFromSignRequestHeader(ctx, req)
ctx = (verifier{PKCS7Verifier: &crypto.PKCS7Verifier{}}).populateDeviceCertificateFromSignRequestHeader(ctx, req)
reqcert, err := DeviceCertificateFromContext(ctx)
if err != nil {

View File

@@ -14,6 +14,8 @@ import (
"os"
"strings"
"time"
"go.mozilla.org/pkcs7"
)
func GenerateRandomCertificateSerialNumber() (*big.Int, error) {
@@ -192,3 +194,21 @@ func TopicFromCert(cert *x509.Certificate) (string, error) {
return "", errors.New("could not find Push Topic (UserID OID) in certificate")
}
// PKCS7Verifier verifies PKCS7 objects with a configurable clock skew
type PKCS7Verifier struct {
// MaxSkew is the maximum amount of clock skew permitted between the the server time and the pkcs7 signature validity
MaxSkew time.Duration
}
// Verify checks the signatures of a PKCS7 object
func (v *PKCS7Verifier) Verify(p7 *pkcs7.PKCS7) error {
// verify with skew added to beginning of validity window
err := p7.VerifyWithChainAtTime(nil, time.Now().Add(v.MaxSkew))
// if verification fails due to missing the validity window, try verifying with the skew added to the end of the validity window
// the pkcs7 lib doesn't return a concrete error, so check against the error string
if err != nil && strings.Contains(err.Error(), "is outside of certificate validity") {
return p7.VerifyWithChainAtTime(nil, time.Now().Add(-v.MaxSkew))
}
return err
}