From bf84b96ba4647e14be2b57e5eb8bdf9212ae21ec Mon Sep 17 00:00:00 2001 From: osy Date: Mon, 9 Nov 2020 18:41:23 -0800 Subject: [PATCH] Fix wildcard application-identifier in entitlement The provisioning profile defines a list of supported entitlements which may include wildcard identifiers. The actual application entitlement should not contain any wildcards. This results in bugs such as broken file import on iOS 14. This patch only fixes wildcards in application-identifier. It does not fix any other keys. It does not support wildcards except '*'. As part of the change, the feature to remove get-task-allow is also moved to unify the entitlement modification code. --- AppSigner/MainView.swift | 28 ++++++++++----- ProvisioningProfile.swift | 76 +++++++++++++++++---------------------- 2 files changed, 53 insertions(+), 51 deletions(-) diff --git a/AppSigner/MainView.swift b/AppSigner/MainView.swift index ade438d..370ad9c 100644 --- a/AppSigner/MainView.swift +++ b/AppSigner/MainView.swift @@ -854,16 +854,32 @@ class MainView: NSView, URLSessionDataDelegate, URLSessionDelegate, URLSessionDo } } + let bundleID = getPlistKey(appBundleInfoPlist, keyName: "CFBundleIdentifier") + //MARK: Generate entitlements.plist if provisioningFile != nil || useAppBundleProfile { setStatus("Parsing entitlements") - if let profile = ProvisioningProfile(filename: useAppBundleProfile ? appBundleProvisioningFilePath : provisioningFile!, skipGetTaskAllow: shouldSkipGetTaskAllow){ - if let entitlements = profile.getEntitlementsPlist(tempFolder) { + if var profile = ProvisioningProfile(filename: useAppBundleProfile ? appBundleProvisioningFilePath : provisioningFile!){ + if shouldSkipGetTaskAllow { + profile.removeGetTaskAllow() + } + let isWildcard = profile.appID == "*" // TODO: support com.example.* wildcard + if !isWildcard && (newApplicationID != "" && newApplicationID != profile.appID) { + setStatus("Unable to change App ID to \(newApplicationID), provisioning profile won't allow it") + cleanup(tempFolder); return + } else if isWildcard { + if newApplicationID != "" { + profile.update(trueAppID: newApplicationID) + } else if let existingBundleID = bundleID { + profile.update(trueAppID: existingBundleID) + } + } + if let entitlements = profile.getEntitlementsPlist() { Log.write("–––––––––––––––––––––––\n\(entitlements)") Log.write("–––––––––––––––––––––––") do { - try entitlements.write(toFile: entitlementsPlist, atomically: false, encoding: String.Encoding.utf8.rawValue) + try entitlements.write(toFile: entitlementsPlist, atomically: false, encoding: .utf8) setStatus("Saved entitlements to \(entitlementsPlist)") } catch let error as NSError { setStatus("Error writing entitlements.plist, \(error.localizedDescription)") @@ -872,10 +888,6 @@ class MainView: NSView, URLSessionDataDelegate, URLSessionDelegate, URLSessionDo setStatus("Unable to read entitlements from provisioning profile") warnings += 1 } - if profile.appID != "*" && (newApplicationID != "" && newApplicationID != profile.appID) { - setStatus("Unable to change App ID to \(newApplicationID), provisioning profile won't allow it") - cleanup(tempFolder); return - } } else { setStatus("Unable to parse provisioning profile, it may be corrupt") warnings += 1 @@ -891,7 +903,7 @@ class MainView: NSView, URLSessionDataDelegate, URLSessionDelegate, URLSessionDo //MARK: Change Application ID if newApplicationID != "" { - if let oldAppID = getPlistKey(appBundleInfoPlist, keyName: "CFBundleIdentifier") { + if let oldAppID = bundleID { func changeAppexID(_ appexFile: String){ guard allowRecursiveSearchAt(appexFile.stringByDeletingLastPathComponent) else { return diff --git a/ProvisioningProfile.swift b/ProvisioningProfile.swift index f8a91d4..606cce6 100644 --- a/ProvisioningProfile.swift +++ b/ProvisioningProfile.swift @@ -15,8 +15,7 @@ struct ProvisioningProfile { expires: Date, appID: String, teamID: String, - rawXML: String, - entitlements: AnyObject? + entitlements: [String : AnyObject] fileprivate let delegate = NSApplication.shared.delegate as! AppDelegate static func getProfiles() -> [ProvisioningProfile] { @@ -55,41 +54,27 @@ struct ProvisioningProfile { return newProfiles; } - init?(filename: String, skipGetTaskAllow: Bool = false){ + init?(filename: String){ let securityArgs = ["cms","-D","-i", filename] let taskOutput = Process().execute("/usr/bin/security", workingDirectory: nil, arguments: securityArgs) + let rawXML: String if taskOutput.status == 0 { if let xmlIndex = taskOutput.output.range(of: " - var entitlements = resultsdict["Entitlements"] as! Dictionary - entitlements.removeValue(forKey: "get-task-allow") - resultsdict["Entitlements"] = entitlements as AnyObject - - let data = PropertyListSerialization.dataFromPropertyList(resultsdict, format: PropertyListSerialization.PropertyListFormat.xml, errorDescription: nil)! - self.rawXML = String(data: data, encoding: .utf8)! - Log.write("Skipped get-task-allow entitlement!"); - } + rawXML = taskOutput.output } - if let results = try? PropertyListSerialization.propertyList(from: self.rawXML.data(using: String.Encoding.utf8)!, options: PropertyListSerialization.MutabilityOptions(), format: nil) { - if let expirationDate = (results as AnyObject).value(forKey: "ExpirationDate") as? Date, - let creationDate = (results as AnyObject).value(forKey: "CreationDate") as? Date, - let name = (results as AnyObject).value(forKey: "Name") as? String, - let entitlements = (results as AnyObject).value(forKey: "Entitlements"), - let applicationIdentifier = (entitlements as AnyObject).value(forKey: "application-identifier") as? String, + if let results = try? PropertyListSerialization.propertyList(from: rawXML.data(using: String.Encoding.utf8)!, options: .mutableContainers, format: nil) as? [String : AnyObject] { + if let expirationDate = results["ExpirationDate"] as? Date, + let creationDate = results["CreationDate"] as? Date, + let name = results["Name"] as? String, + let entitlements = results["Entitlements"] as? [String : AnyObject], + let applicationIdentifier = entitlements["application-identifier"] as? String, let periodIndex = applicationIdentifier.firstIndex(of: ".") { self.filename = filename self.expires = expirationDate @@ -97,7 +82,7 @@ struct ProvisioningProfile { self.appID = applicationIdentifier.substring(from: applicationIdentifier.index(periodIndex, offsetBy: 1)) self.teamID = applicationIdentifier.substring(to: periodIndex) self.name = name - self.entitlements = entitlements as AnyObject? + self.entitlements = entitlements } else { Log.write("Error processing \(filename.lastPathComponent)") return nil @@ -112,22 +97,27 @@ struct ProvisioningProfile { } } - func getEntitlementsPlist(_ tempFolder: String) -> NSString? { - let mobileProvisionPlist = tempFolder.stringByAppendingPathComponent("mobileprovision.plist") - do { - try self.rawXML.write(toFile: mobileProvisionPlist, atomically: false, encoding: String.Encoding.utf8) - let plistBuddy = Process().execute("/usr/libexec/PlistBuddy", workingDirectory: nil, arguments: ["-c", "Print :Entitlements",mobileProvisionPlist, "-x"]) - if plistBuddy.status == 0 { - return plistBuddy.output as NSString? - } else { - Log.write("PlistBuddy Failed") - Log.write(plistBuddy.output) - return nil - } - } catch let error as NSError { - Log.write("Error writing mobileprovision.plist") - Log.write(error.localizedDescription) - return nil + mutating func removeGetTaskAllow() { + if let _ = entitlements.removeValue(forKey: "get-task-allow") { + Log.write("Skipped get-task-allow entitlement!"); + } else { + Log.write("get-task-allow entitlement not found!"); } } + + mutating func update(trueAppID: String) { + guard let oldIdentifier = entitlements["application-identifier"] as? String else { + Log.write("Error reading application-identifier") + return + } + let newIdentifier = teamID + "." + trueAppID + entitlements["application-identifier"] = newIdentifier as AnyObject + Log.write("Updated application-identifier from '\(oldIdentifier)' to '\(newIdentifier)'") + // TODO: update any other wildcard entitlements + } + + func getEntitlementsPlist() -> String? { + let data = PropertyListSerialization.dataFromPropertyList(entitlements, format: PropertyListSerialization.PropertyListFormat.xml, errorDescription: nil)! + return String(data: data, encoding: .utf8) + } }