enroll

package
v0.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 10, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package enroll builds MDM enrollment profiles and serves the over-the-air Profile Service protocol.

Design

The builder combines MDM settings, a SCEP, ACME or pre-issued PKCS #12 identity, and optional trust anchors, then validates against generated schema types. Profile composition/signing uses mdmprotocol/profile and certificate issuance uses the PKI packages.

OTAService distinguishes device-signed and enrollment-identity-signed protocol phases and calls injected authorization and profile callbacks. Automated Device Enrollment and account-driven enrollment have separate handler packages. Callers provide admission policy, trust roots and stable profile identifiers.

References

Index

Constants

View Source
const (
	CapabilityPerUserConnections = "com.apple.mdm.per-user-connections"
	CapabilityBootstrapToken     = "com.apple.mdm.bootstraptoken" // #nosec G101 -- capability identifier
	CapabilityToken              = "com.apple.mdm.token"          // #nosec G101 -- capability identifier
)

ServerCapabilities values: Apple's capability identifiers for the enrollment profile, not credentials.

View Source
const (
	// KeyTypeEC is Apple's name for an elliptic curve key on a NIST prime
	// curve. It is the only type a hardware bound key may have.
	KeyTypeEC = "ECSECPrimeRandom"
	// KeyTypeRSA can only be used for a key that is not hardware bound, and
	// so cannot be attested.
	KeyTypeRSA = "RSA"
)

PKCS12 describes a pre-issued identity. Key types Apple's ACME payload accepts.

View Source
const (
	AttrUDID    = "UDID"
	AttrVersion = "VERSION"
	AttrProduct = "PRODUCT"
	AttrSerial  = "SERIAL"
	AttrIMEI    = "IMEI"
	AttrMEID    = "MEID"
	AttrICCID   = "ICCID"
)

Device attribute names a Profile Service profile may request.

View Source
const AttrChallenge = "CHALLENGE"

AttrChallenge is the key the device echoes the profile's Challenge back under. It is not a device attribute a profile requests, so it is not part of DefaultDeviceAttributes, but it is read from the same dictionary.

View Source
const ContentTypeProfile = "application/x-apple-aspen-config"

ContentTypeProfile is the response content type for a profile.

View Source
const PayloadTypeProfileService = "Profile Service"

PayloadTypeProfileService is the payload type of the initial OTA profile.

Variables

View Source
var DefaultDeviceAttributes = []string{AttrUDID, AttrVersion, AttrProduct, AttrSerial}

DefaultDeviceAttributes is a sensible request list.

View Source
var ErrOTA = errors.New("enroll: ota")

ErrOTA is returned by the OTA service.

View Source
var ErrProfile = errors.New("enroll: profile")

ErrProfile is returned for an enrollment profile that cannot be built or read.

Functions

func NameFromSubject

func NameFromSubject(s [][][]string) pkix.Name

NameFromSubject is the inverse of SubjectFromName.

func SubjectFromName

func SubjectFromName(n pkix.Name) [][][]string

SubjectFromName renders a name in the SCEP payload's array-of-arrays form: [[["CN","value"]], [["O","value"]], ...].

Types

type ACME

type ACME struct {
	// DirectoryURL is the ACME directory, which must use https.
	DirectoryURL string
	// ClientIdentifier is what the device orders with, as the
	// permanent-identifier. Apple treats it as an anti-replay code, so it
	// should be unguessable and issued for one device.
	ClientIdentifier string
	// KeyType is KeyTypeEC or KeyTypeRSA, and KeySize is the curve size or
	// the modulus size. Apple requires both.
	KeyType string
	KeySize int64
	// HardwareBound generates the key in the Secure Enclave, where it
	// cannot be exported. Required for Attest.
	HardwareBound bool
	// Attest asks the device for an attestation of the key and of the
	// hardware, which the ACME server verifies. Requires HardwareBound.
	Attest bool
	// Subject, SubjectAltName, UsageFlags and ExtendedKeyUsage express the device's
	// requested certificate properties. Apple permits the server to override them;
	// the reference server selects the subject.
	Subject          pkix.Name
	SubjectAltName   *profiles.ACMECertificateSubjectAltName
	UsageFlags       *int64
	ExtendedKeyUsage []string
	// KeyIsExtractable and AllowAllAppsAccess are macOS only.
	KeyIsExtractable   *bool
	AllowAllAppsAccess *bool
}

ACME configures the com.apple.security.acme identity payload. The device generates a key and requests a certificate from the selected ACME server. HardwareBound and Attest select Secure Enclave key generation and Managed Device Attestation when supported.

The client identifier authorizes an issuance attempt and must be protected. An attested key and verified device properties still require the server's admission policy.

type AccessRights

type AccessRights int64

AccessRights is the MDM payload AccessRights bit mask.

const (
	RightInspectProfiles     AccessRights = 1
	RightInstallProfiles     AccessRights = 2
	RightLockAndPasscode     AccessRights = 4
	RightErase               AccessRights = 8
	RightQueryDeviceInfo     AccessRights = 16
	RightQueryNetworkInfo    AccessRights = 32
	RightInspectProvisioning AccessRights = 64
	RightInstallProvisioning AccessRights = 128
	RightInspectApps         AccessRights = 256
	RightQueryRestrictions   AccessRights = 512
	RightQuerySecurity       AccessRights = 1024
	RightManipulateSettings  AccessRights = 2048
	RightManageApps          AccessRights = 4096
	AccessRightsAll          AccessRights = 8191
)

AccessRights bits, from the MDM payload documentation.

func (AccessRights) Has

func (a AccessRights) Has(r AccessRights) bool

Has reports whether every bit in r is set.

type DeviceAttributes

type DeviceAttributes struct {
	UDID, Version, Product, Serial, IMEI, MEID, ICCID string
	Challenge                                         string
	// Raw keeps every key received.
	Raw map[string]any
}

DeviceAttributes is what the device signs and sends.

type OTAProfile

type OTAProfile struct {
	Identifier   string
	DisplayName  string
	Organization string
	Description  string
	UUID         string
	PayloadUUID  string
	// URL the device POSTs its attributes to.
	URL string
	// Challenge is echoed back by the device in phase 1.
	Challenge        string
	DeviceAttributes []string
}

OTAProfile builds the Profile Service profile the device installs first.

func (OTAProfile) Build

func (o OTAProfile) Build() (*profile.Profile, error)

Build assembles the profile.

type OTARequest

type OTARequest struct {
	Phase      Phase
	Attributes DeviceAttributes
	Signer     *x509.Certificate
}

OTARequest is a verified request.

type OTAService

type OTAService struct {
	// DeviceRoots verify phase 1 (the Apple iPhone Device CA chain).
	DeviceRoots *x509.CertPool
	// IdentityRoots verify phase 2 (the CA behind the SCEP endpoint).
	IdentityRoots *x509.CertPool
	// ClockSkew for signing-time checks; default 5 minutes.
	ClockSkew time.Duration
	// Now for verification; default time.Now.
	Now func() time.Time
	// Authorize vets a verified request: challenge, allow-lists. Nil allows.
	Authorize func(ctx context.Context, r *OTARequest) error
	// Profile returns the profile bytes for the phase: the SCEP-bearing
	// profile for phase 1, the MDM enrollment profile for phase 2.
	Profile func(ctx context.Context, r *OTARequest) ([]byte, error)
	// Logger defaults to slog.Default.
	Logger *slog.Logger
	// MaxBytes bounds the request body; default 64 KiB.
	MaxBytes int64
}

OTAService serves the profile-service URL.

func (*OTAService) Handler

func (s *OTAService) Handler() http.Handler

Handler serves POST requests from devices.

func (*OTAService) Verify

func (s *OTAService) Verify(body []byte) (*OTARequest, error)

Verify checks the signed body and classifies the phase.

type PKCS12

type PKCS12 struct {
	Data     []byte
	Password string
	FileName string
}

type Phase

type Phase int

Phase of the OTA flow, derived from which CA the request signature chains to.

const (
	PhaseDevice   Phase = 1 // signed by the Apple-issued device certificate
	PhaseIdentity Phase = 2 // signed by the SCEP identity issued in phase 1
)

Phases.

type Profile

type Profile struct {
	Identifier   string // top-level PayloadIdentifier, e.g. com.example.mdm
	DisplayName  string
	Description  string
	Organization string

	Topic      string
	ServerURL  string
	CheckInURL string

	// Exactly one identity source.
	SCEP   *SCEP
	ACME   *ACME
	PKCS12 *PKCS12

	// Roots are installed as com.apple.security.root payloads so the device
	// trusts the MDM server and SCEP CA.
	Roots []*x509.Certificate

	AccessRights       AccessRights // default AccessRightsAll
	ServerCapabilities []string
	// SharedIPad marks a profile for Shared iPad (DEP is_multi_user):
	// Apple requires com.apple.mdm.per-user-connections in
	// ServerCapabilities, which Build adds when absent (decision record 0029).
	SharedIPad          bool
	SignMessage         *bool // default true
	CheckOutWhenRemoved bool
	UseDevelopmentAPNS  bool

	// Account-driven and user-enrollment keys.
	AssignedManagedAppleID string
	EnrollmentMode         string

	// UUIDs make the profile stable across rebuilds; empty values are
	// generated and reported back through Built.
	UUID         string
	MDMUUID      string
	IdentityUUID string
	RootUUIDs    []string

	// Target for schema validation; the zero value skips OS checks.
	Target support.Target
}

Profile is the input to Build.

func Parse

func Parse(data []byte, o profile.ParseOptions) (*Profile, error)

Parse reads an enrollment profile (signed or not) back into Profile so a client can follow it.

func (Profile) Build

func (p Profile) Build() (*profile.Profile, error)

Build assembles and validates the profile.

func (Profile) Marshal

func (p Profile) Marshal() ([]byte, error)

Marshal builds and renders the unsigned profile.

type SCEP

type SCEP struct {
	KeyIsExtractable   *bool
	AllowAllAppsAccess *bool
	URL                string
	Name               string
	Challenge          string
	Subject            pkix.Name
	// KeySize default 2048; KeyUsage default 5 (signing and encryption).
	KeySize       int64
	KeyUsage      int64
	CAFingerprint []byte
	Retries       int64
	RetryDelay    int64
}

SCEP describes the SCEP identity payload.

Directories

Path Synopsis
Package accountdriven implements account-driven Device Enrollment and account-driven User Enrollment authentication.
Package accountdriven implements account-driven Device Enrollment and account-driven User Enrollment authentication.
Package ade serves Automated Device Enrollment profiles after parsing and verifying signed MachineInfo.
Package ade serves Automated Device Enrollment profiles after parsing and verifying signed MachineInfo.
Package adetest constructs signed MachineInfo and its request carriers for Automated Device Enrollment tests.
Package adetest constructs signed MachineInfo and its request carriers for Automated Device Enrollment tests.
Package discovery serves account-driven enrollment service discovery at /.well-known/com.apple.remotemanagement.
Package discovery serves account-driven enrollment service discovery at /.well-known/com.apple.remotemanagement.
Package webauth implements an OpenID Connect relying party for enrollment browser authentication.
Package webauth implements an OpenID Connect relying party for enrollment browser authentication.
webauthtest
Package webauthtest supplies an OpenID Connect provider and browser-flow helpers for enrollment tests.
Package webauthtest supplies an OpenID Connect provider and browser-flow helpers for enrollment tests.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL