Skip to content
Draft
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
3 changes: 3 additions & 0 deletions cmd/fleet/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -1249,6 +1249,9 @@ the way that the Fleet server works.
ds,
logger,
)

// TODO(pssopoc): determine if we will use a middleware chain here to support a
// Fleet-hosted AuthURL flow
}

healthCheckers := make(map[string]health.Checker)
Expand Down
49 changes: 49 additions & 0 deletions server/fleet/apple_mdm.go
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,7 @@ type MDMBootstrapPackageStore interface {
type MDMAppleMachineInfo struct {
IMEI string `plist:"IMEI,omitempty"`
Language string `plist:"LANGUAGE,omitempty"`
MDMCanRequestPSSOConfig bool `plist:"MDM_CAN_REQUEST_PSSO_CONFIG,omitempty"`
MDMCanRequestSoftwareUpdate bool `plist:"MDM_CAN_REQUEST_SOFTWARE_UPDATE"`
MEID string `plist:"MEID,omitempty"`
OSVersion string `plist:"OS_VERSION"`
Expand Down Expand Up @@ -1026,6 +1027,54 @@ type MDMAppleAccountDrivenUserEnrollDeviceInfo struct {
SupplementalBuildVersion string `plist:"SUPPLEMENTAL_BUILD_VERSION,omitempty"`
}

// MDMApplePSSORequiredCode is the [code][1] specified by Apple to indicate that the device
// needs to configure PSSO before enrollment and setup can proceed.
//
// [1]: https://developer.apple.com/documentation/devicemanagement/errorcodeplatformssorequired
const MDMApplePSSORequiredCode = "com.apple.psso.required"

// MDMApplePSSORequiredDetails is the [details][1] specified by Apple for the
// required PSSO.
//
// [1]: https://developer.apple.com/documentation/devicemanagement/errorcodeplatformssorequired/details-data.dictionary
type MDMApplePSSORequiredDetails struct {
AuthURL string `json:"AuthURL"`
Package MDMApplePSSORequiredPackage `json:"Package"`
ProfileURL string `json:"ProfileURL"`
}

// MDMApplePSSORequiredPackage is the [package][1] specified by Apple for the
// required PSSO.
//
// [1]: https://developer.apple.com/documentation/devicemanagement/errorcodeplatformssorequired/details-data.dictionary/package-data.dictionary
type MDMApplePSSORequiredPackage struct {
ManifestURL string `json:"ManifestURL"`
PinningCerts []string `json:"PinningCerts,omitempty"`
PinningRevocationCheckRequired bool `json:"PinningRevocationCheckRequired,omitempty"`
}

type MDMApplePSSORequiredConfig struct {
AuthURL string `json:"AuthURL"`
Package string `json:"Package"`
ProfileURL string `json:"ProfileURL"`
}

// MDMApplePSSORequired is the [error response][1] specified by Apple to indicate that the device
// needs to perform a software update before enrollment and setup can proceed.
//
// [1]: https://developer.apple.com/documentation/devicemanagement/errorcodeplatformssorequired
type MDMApplePSSORequired struct {
Code string `json:"code"` // "com.apple.psso.required"
Details MDMApplePSSORequiredDetails `json:"details"`
}

// func NewMDMApplePSSORequired(asset MDMApplePSSORequiredConfig) *MDMApplePSSORequired {
// return &MDMApplePSSORequired{
// Code: MDMApplePSSORequiredCode,
// Details: MDMApplePSSORequiredDetails{AuthURL: asset.AuthURL, Package: asset.Package, ProfileURL: asset.ProfileURL},
// }
// }

// MDMAppleSoftwareUpdateRequiredCode is the [code][1] specified by Apple to indicate that the device
// needs to perform a software update before enrollment and setup can proceed.
//
Expand Down
10 changes: 10 additions & 0 deletions server/fleet/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,16 @@ type Service interface {
// CheckMDMAppleEnrollmentWithMinimumOSVersion checks if the minimum OS version is met for a MDM enrollment
CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx context.Context, m *MDMAppleMachineInfo) (*MDMAppleSoftwareUpdateRequired, error)

// CheckMDMAppleEnrollmentWithPSSO checks if the PSSO requirement is met for a MDM enrollment
CheckMDMAppleEnrollmentWithPSSO(ctx context.Context, m *MDMAppleMachineInfo) (*MDMApplePSSORequired, error)

// GetMDMApplePSSOInstaller retrieves the PSSO installer
GetMDMApplePSSOInstaller(ctx context.Context, request interface{}) ([]byte, string, error)
// GetMDMApplePSSOProfile retrieves the PSSO profile
GetMDMApplePSSOProfile(ctx context.Context, request interface{}) (string, error)
// GetMDMApplePSSOManifest retrieves the PSSO manifest
GetMDMApplePSSOManifest(ctx context.Context, request interface{}) (string, error)

// GetOTAProfile gets the OTA (over-the-air) profile for a given team based on the enroll secret provided.
GetOTAProfile(ctx context.Context, enrollSecret, idpUUID string) ([]byte, error)

Expand Down
48 changes: 48 additions & 0 deletions server/mock/service/service_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,14 @@ type TriggerLinuxDiskEncryptionEscrowFunc func(ctx context.Context, host *fleet.

type CheckMDMAppleEnrollmentWithMinimumOSVersionFunc func(ctx context.Context, m *fleet.MDMAppleMachineInfo) (*fleet.MDMAppleSoftwareUpdateRequired, error)

type CheckMDMAppleEnrollmentWithPSSOFunc func(ctx context.Context, m *fleet.MDMAppleMachineInfo) (*fleet.MDMApplePSSORequired, error)

type GetMDMApplePSSOInstallerFunc func(ctx context.Context, request interface{}) ([]byte, string, error)

type GetMDMApplePSSOProfileFunc func(ctx context.Context, request interface{}) (string, error)

type GetMDMApplePSSOManifestFunc func(ctx context.Context, request interface{}) (string, error)

type GetOTAProfileFunc func(ctx context.Context, enrollSecret string, idpUUID string) ([]byte, error)

type TriggerCronScheduleFunc func(ctx context.Context, name string) error
Expand Down Expand Up @@ -1759,6 +1767,18 @@ type Service struct {
CheckMDMAppleEnrollmentWithMinimumOSVersionFunc CheckMDMAppleEnrollmentWithMinimumOSVersionFunc
CheckMDMAppleEnrollmentWithMinimumOSVersionFuncInvoked bool

CheckMDMAppleEnrollmentWithPSSOFunc CheckMDMAppleEnrollmentWithPSSOFunc
CheckMDMAppleEnrollmentWithPSSOFuncInvoked bool

GetMDMApplePSSOInstallerFunc GetMDMApplePSSOInstallerFunc
GetMDMApplePSSOInstallerFuncInvoked bool

GetMDMApplePSSOProfileFunc GetMDMApplePSSOProfileFunc
GetMDMApplePSSOProfileFuncInvoked bool

GetMDMApplePSSOManifestFunc GetMDMApplePSSOManifestFunc
GetMDMApplePSSOManifestFuncInvoked bool

GetOTAProfileFunc GetOTAProfileFunc
GetOTAProfileFuncInvoked bool

Expand Down Expand Up @@ -4219,6 +4239,34 @@ func (s *Service) CheckMDMAppleEnrollmentWithMinimumOSVersion(ctx context.Contex
return s.CheckMDMAppleEnrollmentWithMinimumOSVersionFunc(ctx, m)
}

func (s *Service) CheckMDMAppleEnrollmentWithPSSO(ctx context.Context, m *fleet.MDMAppleMachineInfo) (*fleet.MDMApplePSSORequired, error) {
s.mu.Lock()
s.CheckMDMAppleEnrollmentWithPSSOFuncInvoked = true
s.mu.Unlock()
return s.CheckMDMAppleEnrollmentWithPSSOFunc(ctx, m)
}

func (s *Service) GetMDMApplePSSOInstaller(ctx context.Context, request interface{}) ([]byte, string, error) {
s.mu.Lock()
s.GetMDMApplePSSOInstallerFuncInvoked = true
s.mu.Unlock()
return s.GetMDMApplePSSOInstallerFunc(ctx, request)
}

func (s *Service) GetMDMApplePSSOProfile(ctx context.Context, request interface{}) (string, error) {
s.mu.Lock()
s.GetMDMApplePSSOProfileFuncInvoked = true
s.mu.Unlock()
return s.GetMDMApplePSSOProfileFunc(ctx, request)
}

func (s *Service) GetMDMApplePSSOManifest(ctx context.Context, request interface{}) (string, error) {
s.mu.Lock()
s.GetMDMApplePSSOManifestFuncInvoked = true
s.mu.Unlock()
return s.GetMDMApplePSSOManifestFunc(ctx, request)
}

func (s *Service) GetOTAProfile(ctx context.Context, enrollSecret string, idpUUID string) ([]byte, error) {
s.mu.Lock()
s.GetOTAProfileFuncInvoked = true
Expand Down
36 changes: 36 additions & 0 deletions server/service/apple_mdm.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"crypto/md5" // nolint:gosec // used for declarative management token
"crypto/x509"
_ "embed"
"encoding/base64"
"encoding/hex"
"encoding/json"
Expand Down Expand Up @@ -1913,6 +1914,18 @@ func (mdmAppleEnrollRequest) DecodeRequest(ctx context.Context, r *http.Request)
}
}

// // TODO(pssopoc): figure out implementation details for bearer token
// // See https://developer.apple.com/documentation/devicemanagement/implementing-platform-sso-during-device-enrollment#Enroll-the-device
// var bearerToken string
// authHdr := r.Header.Get("Authorization")
// headerParts := strings.Split(authHdr, " ")
// if len(headerParts) == 2 && strings.ToUpper(headerParts[0]) == "BEARER" {
// bearerToken = headerParts[1]
// // TODO(pssopoc): add to the request struct and validate in the endpoint
// } else {
// // TODO(pssopoc): figure out what we want to do if missing/malformed auth header
// }

return &decoded, nil
}

Expand All @@ -1925,6 +1938,7 @@ type mdmAppleEnrollResponse struct {
Profile []byte

SoftwareUpdateRequired *fleet.MDMAppleSoftwareUpdateRequired
PSSORequired *fleet.MDMApplePSSORequired
}

func (r mdmAppleEnrollResponse) HijackRender(ctx context.Context, w http.ResponseWriter) {
Expand All @@ -1937,6 +1951,15 @@ func (r mdmAppleEnrollResponse) HijackRender(ctx context.Context, w http.Respons
return
}

if r.PSSORequired != nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusForbidden)
if err := json.NewEncoder(w).Encode(r.PSSORequired); err != nil {
endpoint_utils.EncodeError(ctx, ctxerr.New(ctx, "failed to encode psso required"), w)
}
return
}

w.Header().Set("Content-Length", strconv.FormatInt(int64(len(r.Profile)), 10))
w.Header().Set("Content-Type", "application/x-apple-aspen-config")
w.Header().Set("X-Content-Type-Options", "nosniff")
Expand Down Expand Up @@ -1968,6 +1991,19 @@ func mdmAppleEnrollEndpoint(ctx context.Context, request interface{}, svc fleet.
}
}

psso, err := svc.CheckMDMAppleEnrollmentWithPSSO(ctx, req.MachineInfo)
if err != nil {
// // TODO: do we instead want to just log the error and continue to next?
// level.Error(logger).Log("msg", "checking pss enrollment for mdm", "err", err)
// next.ServeHTTP(w, r)
return mdmAppleEnrollResponse{Err: err}, nil
}
if psso != nil {
return mdmAppleEnrollResponse{
PSSORequired: psso,
}, nil
}

legacyRef, err := svc.ReconcileMDMAppleEnrollRef(ctx, req.EnrollmentReference, req.MachineInfo)
if err != nil {
return mdmAppleEnrollResponse{Err: err}, nil
Expand Down
Loading
Loading