Documentation
¶
Overview ¶
Package mdm is the protocol core of the Apple MDM check-in and command channels: enrollment identity, request context, check-in message decoding, command envelopes, and command response decoding.
Why ¶
Apple documents the check-in messages and the commands as YAML, but the structure around them only in prose: which of UDID and EnrollmentID identifies a device, user, or Shared iPad user channel; the CommandUUID and Command envelope a queued command travels in; the Status, ErrorChain, and per-command result a device sends back; the Push fields and Enrollment record the service must retain. Phase 2 of the plan of record needs those semantics once, typed, and free of transport, so the service, storage, and httpapi packages can share them (decision record 0004).
Wire types come from the generated schema packages; this package adds the envelope structure and the decoders that dispatch on MessageType and RequestType through the schema/checkin and schema/commands registries. It stores nothing and serves nothing: persistence is storage, state machines are service, and HTTP is httpapi.
References ¶
- Decision record 0002: docs/research/decisions/0002-plist-library.md
- Decision record 0004: docs/research/decisions/0004-checkin-and-command-core.md
- Decision record 0006: docs/research/decisions/0006-mdm-signature-verification.md (request identity)
- Decision record 0016: docs/research/decisions/0016-user-authenticate-state.md (UserAuthenticate responses)
- Plan of record: docs/research/implementation_plan.md (section 3, core domain model; phase 2)
- Threat model: docs/security/threat-model.md (/checkin and /connect rows)
- End-to-end scenarios: docs/testing/e2e-scenarios.md (E2E-001 to E2E-005)
- Apple: https://developer.apple.com/documentation/devicemanagement/check-in
- Apple: https://developer.apple.com/documentation/devicemanagement/commands-and-queries
- Apple: https://developer.apple.com/documentation/devicemanagement/handling-notnow-status-responses
- Schema: third_party/device-management/mdm/checkin/*.yaml
- Schema: third_party/device-management/mdm/commands/*.yaml
Index ¶
Constants ¶
SharedIPadUserID is the sentinel UserID Apple sends on the shared iPad user channel; the real user is in UserShortName.
Variables ¶
var ( ErrNoEnrollmentID = errors.New("mdm: message carries neither UDID nor EnrollmentID") ErrInvalidEnrollment = errors.New("mdm: invalid enrollment identity") ErrUnknownMessageType = errors.New("mdm: unknown check-in MessageType") ErrInvalidCommand = errors.New("mdm: invalid command") ErrInvalidResponse = errors.New("mdm: invalid command response") )
Errors returned when resolving identities.
Functions ¶
This section is empty.
Types ¶
type Channel ¶
type Channel uint8
Channel identifies which MDM channel an enrollment identity belongs to.
const ( ChannelUnknown Channel = iota ChannelDevice ChannelUser ChannelUserEnrollmentDevice ChannelUserEnrollmentUser )
Channels. Apple runs a device channel and, on macOS and shared iPad, a user channel; User Enrollment (BYOD) uses EnrollmentID instead of UDID.
type Checkin ¶
type Checkin struct {
// Type is the MessageType wire value (Authenticate, TokenUpdate, ...).
Type string
// Message is the typed message, one of the schema/checkin types.
Message checkin.Message
// Enrollment holds the identity keys as sent.
Enrollment Enrollment
// ID is the resolved enrollment identity.
ID EnrollmentID
// Raw is the plist exactly as received.
Raw []byte
}
Checkin is a decoded check-in message: the typed message from the generated schema, the identity keys, the resolved enrollment id, and the raw bytes as received.
func DecodeCheckin ¶
func DecodeCheckin(raw []byte, opts ...DecodeOption) (*Checkin, error)
DecodeCheckin parses a check-in request body.
type Command ¶
type Command struct {
UUID string
RequestType string
// Payload is the typed command when known; nil after decoding a command
// whose RequestType is not in the schema registry.
Payload commands.Command
// Raw is the complete command plist sent to the device.
Raw []byte
}
Command is a queued MDM command: the envelope Apple documents as {CommandUUID, Command: {RequestType, ...}} plus the typed payload.
func DecodeCommand ¶
func DecodeCommand(raw []byte, opts ...DecodeOption) (*Command, error)
DecodeCommand parses a command plist, resolving the typed payload through the schema registry when the RequestType is known.
func NewCommand ¶
func NewCommand(payload commands.Command, opts ...CommandOption) (*Command, error)
NewCommand builds the wire plist for a typed command, injecting RequestType and a time-ordered UUID.
type CommandOption ¶
type CommandOption func(*Command)
CommandOption configures NewCommand.
func WithUUID ¶
func WithUUID(id string) CommandOption
WithUUID sets an explicit CommandUUID instead of a generated UUIDv7.
type DecodeOption ¶
type DecodeOption func(*decodeOptions)
DecodeOption configures decoding.
func WithLimits ¶
func WithLimits(d plist.Decoder) DecodeOption
WithLimits overrides the plist size and depth limits.
type Enrollment ¶
type Enrollment struct {
UDID string `plist:"UDID,omitempty" json:"UDID,omitempty"`
UserID string `plist:"UserID,omitempty" json:"UserID,omitempty"`
UserShortName string `plist:"UserShortName,omitempty" json:"UserShortName,omitempty"`
UserLongName string `plist:"UserLongName,omitempty" json:"UserLongName,omitempty"`
EnrollmentID string `plist:"EnrollmentID,omitempty" json:"EnrollmentID,omitempty"`
EnrollmentUserID string `plist:"EnrollmentUserID,omitempty" json:"EnrollmentUserID,omitempty"`
}
Enrollment carries the identity keys present on every check-in message and command response. Which keys are populated depends on the channel.
func (Enrollment) Resolve ¶
func (en Enrollment) Resolve() (EnrollmentID, error)
Resolve derives the EnrollmentID from the identity keys, following the rules in Apple's check-in documentation:
- UDID present: device channel keyed by UDID; with UserID it is the user channel "UDID:UserID", or the shared iPad user channel "UDID:UserShortName" when UserID is the shared iPad sentinel.
- EnrollmentID present (User Enrollment): device channel keyed by EnrollmentID; with EnrollmentUserID it is "EnrollmentID:EnrollmentUserID".
type EnrollmentID ¶
EnrollmentID is the normalised identity of one channel of one enrollment. ID is the UDID or EnrollmentID for device channels and "<device>:<user>" for user channels; ParentID is the device channel id for user channels.
func (EnrollmentID) Device ¶
func (e EnrollmentID) Device() EnrollmentID
Device returns the device-channel identity this id belongs to (itself for device channels).
func (EnrollmentID) Validate ¶
func (e EnrollmentID) Validate() error
Validate checks the id is well formed.
type ErrorChainItem ¶
type ErrorChainItem struct {
ErrorCode int64 `plist:"ErrorCode" json:"ErrorCode"`
ErrorDomain string `plist:"ErrorDomain" json:"ErrorDomain"`
LocalizedDescription string `plist:"LocalizedDescription,omitempty" json:"LocalizedDescription,omitempty"`
USEnglishDescription string `plist:"USEnglishDescription,omitempty" json:"USEnglishDescription,omitempty"`
}
ErrorChainItem is one entry of the ErrorChain array on an Error response.
type ParseError ¶
ParseError wraps a decode failure with the offending content so callers can log it. Content is bounded by the plist size limits.
type Push ¶
Push is what the server needs to wake a device: from TokenUpdate.
func PushFromTokenUpdate ¶
func PushFromTokenUpdate(m *checkin.TokenUpdate) (Push, error)
PushFromTokenUpdate extracts push information from a TokenUpdate.
type Request ¶
type Request struct {
ID EnrollmentID
Enrollment Enrollment
Certificate *x509.Certificate
Params map[string]string
Peer PeerInfo
ReceivedAt time.Time
}
Request is the context of one device request handed to the service layer. It never carries a context.Context: every service method takes one.
type Response ¶
type Response struct {
Enrollment Enrollment
ID EnrollmentID
CommandUUID string
Status Status
ErrorChain []ErrorChainItem
// Payload is the typed response when the caller supplied the RequestType
// of the command being answered and the status is Acknowledged.
Payload commands.Response
Raw []byte
}
Response is a decoded command response (or an Idle poll).
func DecodeResponse ¶
func DecodeResponse(raw []byte, requestType string, opts ...DecodeOption) (*Response, error)
DecodeResponse parses a command response body. requestType may be empty; when set and the registry knows it, Payload is populated for Acknowledged responses.
type Status ¶
type Status string
Status is the Status key of a command response.
type UserAuthenticateResponse ¶
type UserAuthenticateResponse struct {
DigestChallenge *string `plist:"DigestChallenge,omitempty" json:"DigestChallenge,omitempty"`
AuthToken *string `plist:"AuthToken,omitempty" json:"AuthToken,omitempty"`
}
UserAuthenticateResponse is the server's reply to UserAuthenticate, which Apple documents in prose rather than in the schema: the first reply carries DigestChallenge (empty to skip authentication), the second carries AuthToken (empty to reject the password).