mirror of
https://github.com/micromdm/micromdm/
synced 2026-08-12 21:35:40 +08:00
checkin: add flag to configure allowable time skew (#887)
This commit is contained in:
@@ -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 != "" {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user