Skip to content

Commit

Permalink
Add supervised configuration profile installs
Browse files Browse the repository at this point in the history
  • Loading branch information
zner0L committed Jun 23, 2023
1 parent 4eccc9f commit 90fb33a
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 0 deletions.
11 changes: 11 additions & 0 deletions pymobiledevice3/cli/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ def profile_install(lockdown: LockdownClient, profiles):
logger.info(f'installing {profile.name}')
service.install_profile(profile.read())

@profile_group.command('install-silent', cls=Command)
@click.option('--keystore', type=click.File('rb'), required=True, help="A PKCS#12 keystore containing the certificate and private key which can supervise the device.")
@click.option('--keystore-password', prompt=True, required=True, hide_input=True, help="The password for the PKCS#12 keystore.")
@click.argument('profiles', nargs=-1, type=click.File('rb'))
def profile_install_silent(lockdown: LockdownClient, profiles, keystore, keystore_password):
""" install given profiles without user interaction (requires the device to be supervised) """
service = MobileConfigService(lockdown=lockdown)
for profile in profiles:
logger.info(f'installing {profile.name}')
service.install_profile_silent(profile.read(), keystore.read(), keystore_password)


@profile_group.command('cloud-configuration', cls=Command)
@click.option('--color/--no-color', default=True)
Expand Down
17 changes: 17 additions & 0 deletions pymobiledevice3/services/mobile_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
from pymobiledevice3.exceptions import ProfileError
from pymobiledevice3.lockdown import LockdownClient
from pymobiledevice3.services.base_service import BaseService
from cryptography.hazmat.primitives.serialization.pkcs12 import load_pkcs12
from cryptography.hazmat.primitives.serialization import Encoding
from cryptography.hazmat.primitives.serialization.pkcs7 import PKCS7SignatureBuilder
from cryptography.hazmat.primitives import hashes


class Purpose(Enum):
Expand All @@ -23,6 +27,15 @@ def hello(self) -> None:
def flush(self) -> None:
self._send_recv({'RequestType': 'Flush'})

def escalate(self, pkcs12: bytes, password: str) -> None:
decrypted_p12 = load_pkcs12(pkcs12, password.encode('utf-8'))

escalate_response = self._send_recv({'RequestType': 'Escalate', 'SupervisorCertificate': decrypted_p12.cert.certificate.public_bytes(Encoding.DER)})
signed_challenge = PKCS7SignatureBuilder().set_data(escalate_response['Challenge']).add_signer(decrypted_p12.cert.certificate, decrypted_p12.key, hashes.SHA256()).sign(Encoding.DER, [])
self._send_recv({'RequestType': 'EscalateResponse', 'SignedRequest': signed_challenge})
self._send_recv({'RequestType': 'ProceedWithKeybagMigration'})


def get_stored_profile(self, purpose: Purpose = Purpose.PostSetupInstallation) -> Mapping:
return self._send_recv({'RequestType': 'GetStoredProfile', 'Purpose': purpose.value})

Expand Down Expand Up @@ -54,6 +67,10 @@ def get_profile_list(self) -> Mapping:
def install_profile(self, payload: bytes) -> None:
self._send_recv({'RequestType': 'InstallProfile', 'Payload': payload})

def install_profile_silent(self, profile: bytes, pkcs12: bytes, password: str) -> None:
self.escalate(pkcs12, password)
self._send_recv({'RequestType': 'InstallProfileSilent', 'Payload': profile})

def remove_profile(self, identifier: str) -> None:
profiles = self.get_profile_list()
if not profiles:
Expand Down

0 comments on commit 90fb33a

Please sign in to comment.