mirror of
https://gitlab.com/fdroid/fdroidserver.git
synced 2024-11-14 02:50:12 +01:00
🍎 altstore: implement ipa entitlement parser
This adds a parser for reading entitlement values from .ipa files. Entitlement values are stored in files called '.../embedded.mobileprovision' packed into .ipa files. These are CMS signed plist files. https://en.wikipedia.org/wiki/Cryptographic_Message_Syntax This also ignores the 2 non-optional entitlements, as mentioned in altstore docs: https://faq.altstore.io/distribute-your-apps/make-a-source#entitlements-array-of-strings
This commit is contained in:
parent
2658c22933
commit
301f0c8273
@ -34,6 +34,7 @@ import json
|
|||||||
import time
|
import time
|
||||||
import yaml
|
import yaml
|
||||||
import copy
|
import copy
|
||||||
|
import asn1crypto.cms
|
||||||
import defusedxml.ElementTree as ElementTree
|
import defusedxml.ElementTree as ElementTree
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
@ -598,149 +599,8 @@ IPA_PERMISSIONS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# known iOS app entitlements, source:
|
|
||||||
# https://developer.apple.com/documentation/bundleresources/entitlements
|
|
||||||
IPA_ENTITLEMENTS = [
|
|
||||||
b"aps-environment",
|
|
||||||
b"com.apple.developer.ClassKit-environment",
|
|
||||||
b"com.apple.developer.applesignin",
|
|
||||||
b"com.apple.developer.aps-environment",
|
|
||||||
b"com.apple.developer.associated-appclip-app-identifiers",
|
|
||||||
b"com.apple.developer.associated-domains",
|
|
||||||
b"com.apple.developer.associated-domains.applinks.read-write",
|
|
||||||
b"com.apple.developer.authentication-services.autofill-credential-provider",
|
|
||||||
b"com.apple.developer.automated-device-enrollment.add-devices",
|
|
||||||
b"com.apple.developer.automatic-assessment-configuration",
|
|
||||||
b"com.apple.developer.avfoundation.multitasking-camera-access",
|
|
||||||
b"com.apple.developer.browser.app-installation",
|
|
||||||
b"com.apple.developer.carplay-audio",
|
|
||||||
b"com.apple.developer.carplay-charging",
|
|
||||||
b"com.apple.developer.carplay-communication",
|
|
||||||
b"com.apple.developer.carplay-maps",
|
|
||||||
b"com.apple.developer.carplay-messaging",
|
|
||||||
b"com.apple.developer.carplay-parking",
|
|
||||||
b"com.apple.developer.carplay-quick-ordering",
|
|
||||||
b"com.apple.developer.contacts.notes",
|
|
||||||
b"com.apple.developer.default-data-protection",
|
|
||||||
b"com.apple.developer.device-information.user-assigned-device-name",
|
|
||||||
b"com.apple.developer.devicecheck.appattest-environment",
|
|
||||||
b"com.apple.developer.driverkit",
|
|
||||||
b"com.apple.developer.driverkit.allow-any-userclient-access",
|
|
||||||
b"com.apple.developer.driverkit.allow-third-party-userclients",
|
|
||||||
b"com.apple.developer.driverkit.communicates-with-drivers",
|
|
||||||
b"com.apple.developer.driverkit.family.audio",
|
|
||||||
b"com.apple.developer.driverkit.family.block-storage-device",
|
|
||||||
b"com.apple.developer.driverkit.family.hid.device",
|
|
||||||
b"com.apple.developer.driverkit.family.hid.eventservice",
|
|
||||||
b"com.apple.developer.driverkit.family.networking",
|
|
||||||
b"com.apple.developer.driverkit.family.scsicontroller",
|
|
||||||
b"com.apple.developer.driverkit.family.serial",
|
|
||||||
b"com.apple.developer.driverkit.transport.hid",
|
|
||||||
b"com.apple.developer.driverkit.transport.pci",
|
|
||||||
b"com.apple.developer.driverkit.transport.usb",
|
|
||||||
b"com.apple.developer.driverkit.userclient-access",
|
|
||||||
b"com.apple.developer.endpoint-security.client",
|
|
||||||
b"com.apple.developer.endpoint-security.client",
|
|
||||||
b"com.apple.developer.exposure-notification",
|
|
||||||
b"com.apple.developer.family-controls",
|
|
||||||
b"com.apple.developer.fileprovider.testing-mode",
|
|
||||||
b"com.apple.developer.game-center",
|
|
||||||
b"com.apple.developer.group-session",
|
|
||||||
b"com.apple.developer.healthkit",
|
|
||||||
b"com.apple.developer.healthkit.access",
|
|
||||||
b"com.apple.developer.healthkit.background-delivery",
|
|
||||||
b"com.apple.developer.healthkit.recalibrate-estimates",
|
|
||||||
b"com.apple.developer.hid.virtual.device",
|
|
||||||
b"com.apple.developer.homekit",
|
|
||||||
b"com.apple.developer.icloud-container-development-container-identifiers",
|
|
||||||
b"com.apple.developer.icloud-container-environment",
|
|
||||||
b"com.apple.developer.icloud-container-identifiers",
|
|
||||||
b"com.apple.developer.icloud-services",
|
|
||||||
b"com.apple.developer.in-app-identity-presentment",
|
|
||||||
b"com.apple.developer.in-app-identity-presentment.merchant-identifiers",
|
|
||||||
b"com.apple.developer.in-app-payments",
|
|
||||||
b"com.apple.developer.journal.allow",
|
|
||||||
b"com.apple.developer.kernel.extended-virtual-addressing",
|
|
||||||
b"com.apple.developer.kernel.increased-memory-limit",
|
|
||||||
b"com.apple.developer.location.push",
|
|
||||||
b"com.apple.developer.mail-client",
|
|
||||||
b"com.apple.developer.managed-app-distribution.install-ui",
|
|
||||||
b"com.apple.developer.maps",
|
|
||||||
b"com.apple.developer.marketplace.app-installation",
|
|
||||||
b"com.apple.developer.matter.allow-setup-payload",
|
|
||||||
b"com.apple.developer.media-device-discovery-extension",
|
|
||||||
b"com.apple.developer.networking.HotspotConfiguration",
|
|
||||||
b"com.apple.developer.networking.custom-protocol",
|
|
||||||
b"com.apple.developer.networking.manage-thread-network-credentials",
|
|
||||||
b"com.apple.developer.networking.multicast",
|
|
||||||
b"com.apple.developer.networking.multipath",
|
|
||||||
b"com.apple.developer.networking.networkextension",
|
|
||||||
b"com.apple.developer.networking.networkextension",
|
|
||||||
b"com.apple.developer.networking.slicing.appcategory",
|
|
||||||
b"com.apple.developer.networking.slicing.trafficcategory",
|
|
||||||
b"com.apple.developer.networking.vmnet",
|
|
||||||
b"com.apple.developer.networking.vpn.api",
|
|
||||||
b"com.apple.developer.networking.wifi-info",
|
|
||||||
b"com.apple.developer.nfc.hce",
|
|
||||||
b"com.apple.developer.nfc.hce.default-contactless-app",
|
|
||||||
b"com.apple.developer.nfc.hce.iso7816.select-identifier-prefixes",
|
|
||||||
b"com.apple.developer.nfc.readersession.formats",
|
|
||||||
b"com.apple.developer.on-demand-install-capable",
|
|
||||||
b"com.apple.developer.parent-application-identifiers",
|
|
||||||
b"com.apple.developer.pass-type-identifiers",
|
|
||||||
b"com.apple.developer.playable-content",
|
|
||||||
b"com.apple.developer.proximity-reader.identity.display",
|
|
||||||
b"com.apple.developer.proximity-reader.identity.read",
|
|
||||||
b"com.apple.developer.push-to-talk",
|
|
||||||
b"com.apple.developer.sensitivecontentanalysis.client",
|
|
||||||
b"com.apple.developer.sensorkit.reader.allow",
|
|
||||||
b"com.apple.developer.severe-vehicular-crash-event",
|
|
||||||
b"com.apple.developer.siri",
|
|
||||||
b"com.apple.developer.storekit.external-link.account",
|
|
||||||
b"com.apple.developer.storekit.external-purchase",
|
|
||||||
b"com.apple.developer.storekit.external-purchase-link",
|
|
||||||
b"com.apple.developer.sustained-execution",
|
|
||||||
b"com.apple.developer.system-extension.install",
|
|
||||||
b"com.apple.developer.system-extension.redistributable",
|
|
||||||
b"com.apple.developer.team-identifier",
|
|
||||||
b"com.apple.developer.ubiquity-kvstore-identifier",
|
|
||||||
b"com.apple.developer.upi-device-validation",
|
|
||||||
b"com.apple.developer.user-management",
|
|
||||||
b"com.apple.developer.usernotifications.filtering",
|
|
||||||
b"com.apple.developer.video-subscriber-single-sign-on",
|
|
||||||
b"com.apple.developer.weatherkit",
|
|
||||||
b"com.apple.developer.web-browser",
|
|
||||||
b"com.apple.developer.web-browser.public-key-credential",
|
|
||||||
b"com.apple.external-accessory.wireless-configuration",
|
|
||||||
b"com.apple.security.app-sandbox",
|
|
||||||
b"com.apple.security.application-groups",
|
|
||||||
b"com.apple.security.automation.apple-events",
|
|
||||||
b"com.apple.security.cs.allow-dyld-environment-variables",
|
|
||||||
b"com.apple.security.cs.allow-jit",
|
|
||||||
b"com.apple.security.cs.allow-unsigned-executable-memory",
|
|
||||||
b"com.apple.security.cs.debugger",
|
|
||||||
b"com.apple.security.cs.disable-executable-page-protection",
|
|
||||||
b"com.apple.security.cs.disable-library-validation",
|
|
||||||
b"com.apple.security.device.audio-input",
|
|
||||||
b"com.apple.security.device.camera",
|
|
||||||
b"com.apple.security.hypervisor",
|
|
||||||
b"com.apple.security.personal-information.addressbook",
|
|
||||||
b"com.apple.security.personal-information.calendars",
|
|
||||||
b"com.apple.security.personal-information.location",
|
|
||||||
b"com.apple.security.personal-information.photos-library",
|
|
||||||
b"com.apple.security.smartcard",
|
|
||||||
b"com.apple.security.virtualization",
|
|
||||||
b"com.apple.smoot.subscriptionservice",
|
|
||||||
b"com.apple.vm.device-access",
|
|
||||||
b"com.apple.vm.hypervisor",
|
|
||||||
b"com.apple.vm.networking",
|
|
||||||
b"inter-app-audio",
|
|
||||||
b"keychain-access-groups",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def parse_ipa(ipa_path, file_size, sha256):
|
def parse_ipa(ipa_path, file_size, sha256):
|
||||||
from biplist import readPlist
|
import biplist
|
||||||
|
|
||||||
ipa = {
|
ipa = {
|
||||||
"apkName": os.path.basename(ipa_path),
|
"apkName": os.path.basename(ipa_path),
|
||||||
@ -755,7 +615,7 @@ def parse_ipa(ipa_path, file_size, sha256):
|
|||||||
for info in ipa_zip.infolist():
|
for info in ipa_zip.infolist():
|
||||||
if re.match("Payload/[^/]*.app/Info.plist", info.filename):
|
if re.match("Payload/[^/]*.app/Info.plist", info.filename):
|
||||||
with ipa_zip.open(info) as plist_file:
|
with ipa_zip.open(info) as plist_file:
|
||||||
plist = readPlist(plist_file)
|
plist = biplist.readPlist(plist_file)
|
||||||
ipa["name"] = plist['CFBundleName']
|
ipa["name"] = plist['CFBundleName']
|
||||||
ipa["packageName"] = plist["CFBundleIdentifier"]
|
ipa["packageName"] = plist["CFBundleIdentifier"]
|
||||||
# https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleshortversionstring
|
# https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleshortversionstring
|
||||||
@ -768,12 +628,15 @@ def parse_ipa(ipa_path, file_size, sha256):
|
|||||||
ipa["ipa_permissions"][ipap] = str(plist[ipap])
|
ipa["ipa_permissions"][ipap] = str(plist[ipap])
|
||||||
if info.filename.endswith("/embedded.mobileprovision"):
|
if info.filename.endswith("/embedded.mobileprovision"):
|
||||||
print("parsing", info.filename)
|
print("parsing", info.filename)
|
||||||
with ipa_zip.open(info) as mopro:
|
with ipa_zip.open(info) as mopro_file:
|
||||||
for line in mopro.readlines():
|
mopro_content_info = cms.ContentInfo.load(mopro_file.read())
|
||||||
for entitlement in IPA_ENTITLEMENTS:
|
mopro_payload_info = mopro_content_info['content']
|
||||||
if entitlement in line:
|
mopro_payload = mopro_payload_info['encap_content_info']['content'].native
|
||||||
ipa['ipa_entitlements'].add(str(entitlement, encoding="utf-8"))
|
mopro = biplist.readPlistFromString(mopro_payload)
|
||||||
|
# https://faq.altstore.io/distribute-your-apps/make-a-source#entitlements-array-of-strings
|
||||||
|
for entitlement in mopro.get('Entitlements', {}).keys():
|
||||||
|
if entitlement not in ["com.app.developer.team-identifier", 'application-identifier']:
|
||||||
|
ipa["ipa_entitlements"].add(entitlement)
|
||||||
return ipa
|
return ipa
|
||||||
|
|
||||||
|
|
||||||
@ -1563,8 +1426,8 @@ def insert_localized_ios_app_metadata(apps_with_packages):
|
|||||||
fdroidserver.update.copy_ios_screenshots_to_repo(screenshots, package_name)
|
fdroidserver.update.copy_ios_screenshots_to_repo(screenshots, package_name)
|
||||||
|
|
||||||
# lookup icons, copy them and put them into app
|
# lookup icons, copy them and put them into app
|
||||||
icon_path = _get_ipa_icon(pathlib.Path('build') / package_name)
|
icon_path = _get_ipa_icon(Path('build') / package_name)
|
||||||
icon_dest = pathlib.Path('repo') / package_name / 'icon.png' # for now just assume png
|
icon_dest = Path('repo') / package_name / 'icon.png' # for now just assume png
|
||||||
icon_stat = os.stat(icon_path)
|
icon_stat = os.stat(icon_path)
|
||||||
app['iconv2'] = {
|
app['iconv2'] = {
|
||||||
DEFAULT_LOCALE: {
|
DEFAULT_LOCALE: {
|
||||||
@ -1777,7 +1640,7 @@ def _get_apk_icons_src(apkfile, icon_name):
|
|||||||
def _get_ipa_icon(src_dir):
|
def _get_ipa_icon(src_dir):
|
||||||
"""Search source directory of an IPA project for the app icon."""
|
"""Search source directory of an IPA project for the app icon."""
|
||||||
# parse app icon name from project config file
|
# parse app icon name from project config file
|
||||||
src_dir = pathlib.Path(src_dir)
|
src_dir = Path(src_dir)
|
||||||
prj = next(src_dir.glob("**/project.pbxproj"), None)
|
prj = next(src_dir.glob("**/project.pbxproj"), None)
|
||||||
if not prj or not prj.exists():
|
if not prj or not prj.exists():
|
||||||
return
|
return
|
||||||
@ -2688,7 +2551,7 @@ def altstore_index(apps, apks, config, repodir, indent=None):
|
|||||||
# idx["subtitle"] F-Droid doesn't have a corresponding value
|
# idx["subtitle"] F-Droid doesn't have a corresponding value
|
||||||
if config.get("repo_description"):
|
if config.get("repo_description"):
|
||||||
idx['description'] = config['repo_description']
|
idx['description'] = config['repo_description']
|
||||||
if (pathlib.Path(repodir) / 'icons' / config['repo_icon']).exists():
|
if (Path(repodir) / 'icons' / config['repo_icon']).exists():
|
||||||
idx['iconURL'] = f"{config['repo_url']}/icons/{config['repo_icon']}"
|
idx['iconURL'] = f"{config['repo_url']}/icons/{config['repo_icon']}"
|
||||||
# idx["headerURL"] F-Droid doesn't have a corresponding value
|
# idx["headerURL"] F-Droid doesn't have a corresponding value
|
||||||
# idx["website"] F-Droid doesn't have a corresponding value
|
# idx["website"] F-Droid doesn't have a corresponding value
|
||||||
|
Loading…
Reference in New Issue
Block a user