2

As part of our Mobile Device Management feature, we offer private appstore to our customers. Administrators can upload an ipa file to our server, and we will allow the managed devices to install those enterprise apps directly.

When the administrators upload the ipa, we want to do some validation, and reject immediately if the ipa does not meet the requirements. Specifically:

  1. If the ipa is signed by a certificate other than enterprise certificate (such as an appstore certificate), we want to reject it;
  2. If the ipa is signed by a certificate that has expired, we want to reject it;
  3. If the ipa is signed by a certificate that has been revoked, we want to reject it.

I have the following questions:

  1. For requirement #1, I have noticed that enterprise builds have a file embedded.mobileprovision in the ipa, but appstore builds don't have the file. Is it sufficient to check for the existence of that file to determine whether the uploaded ipa is an enterprise ipa, or is there a more accurate way to identify a non-enterprise ipa?
  2. For requirement #2, it looks like there is a field ExpirationDate in embedded.mobileprovision, I could just check the value of that to determine the expiration date?
  3. To my knowledge, #1 and #2 above are possible, but #3 won't be able to be verified until the user actually tries to install the ipa. I.e., I can't catch the error when an admin uploads the ipa, but instead I'll allow that, and the user will get the error of not able to install the app.

Thanks in advance.

SeaJelly
  • 1,738
  • 1
  • 15
  • 30
  • `[[NSBundle mainBundle] pathForResource:@"embedded.mobileprovision" ofType:nil]` http://stackoverflow.com/questions/17584426/check-if-app-is-ad-hocdevapp-store-build-at-run-time. see the similar question. you can get some ideas – aelam Nov 13 '15 at 01:35
  • @aelam thanks for the reply. Correct me if I'm wrong, it looks like that thread is about checking for build type in the app code. In my situation, we don't own the app (thus we don't have access to its source code), admins upload their own apps - and the apps are signed so we can't inject code into their apps. What we have in hand is just an ipa file, and I'd like a way to get the information directly from this ipa. – SeaJelly Nov 13 '15 at 01:41

2 Answers2

1

The code below accomplishes the task desired.

Boolean foundMobileProvision = false;
Pattern mobileProvisionPattern = Pattern.compile("embedded\\.mobileprovision$");

while ((entry = zipStream.getNextEntry()) != null) {
    Matcher mobileProvisionMatcher = mobileProvisionPattern.matcher(entryName);
    if (!entry.isDirectory()) {
        if (mobileProvisionMatcher.find()) {
            foundMobileProvision = true;
            CMSSignedDataParser parser = new CMSSignedDataParser(new BcDigestCalculatorProvider(), zipStream);
            InputStream plistContentStream = parser.getSignedContent().getContentStream();

            Map<String, Object> mobileProvisionAttributes = PlistParser.parsePlistToMap(plistContentStream);

            plistContentStream.close();

            validateEnterpriseProvision(mobileProvisionAttributes);
        }
    }
}
zipStream.close();
if (!foundMobileProvision) {
    throw new InvalidEnterpriseProvisionException("Uploaded app must have a valid enterprise provisioning profile");
}

private void validateEnterpriseProvision(Map<String, Object> mobileProvisionAttributes) {
    Boolean provisionAllDevices = (Boolean) mobileProvisionAttributes.get(IOS_MOBILE_PROVISION_ALL_DEVICES);

    if (provisionAllDevices == null || !provisionAllDevices) {
        throw new InvalidEnterpriseProvisionException("Uploaded app must have a valid enterprise provisioning profile");
    }

    Date expirationDate = (Date) mobileProvisionAttributes.get(IOS_MOBILE_PROVISION_EXPIRATION_DATE);
    if (new Date().after(expirationDate)) {
        throw new EnterpriseProvisionExpiredException("Profile expired");
    }
}
SeaJelly
  • 1,738
  • 1
  • 15
  • 30
0

There is an embedded.mobileprovision in your ipa file.

  1. unzip it, there is a Payload/appName.app/
  2. find out embedded.mobileprovision

Try to look into embedded.mobileprovision

It's a binary plist (plist is a a little special xml format, but still xml). I can view it with atom and vim. you will notice there are unreadable prefix and suffix, ignore them. Go into the center part. You can see many nodes, such as TeamIdentifier, AppIDName, ProvisionedDevices. It has a lot staff .

As your requirement, I've found some codes you might need in https://github.com/0xc010d/mobileprovision-read/blob/master/main.m

It checks some key nodes of mobileprovision. less than 4?

if ([option isEqualToString:@"type"]) {
    if ([plist valueForKeyPath:@"ProvisionedDevices"]){
        if ([[plist valueForKeyPath:@"Entitlements.get-task-allow"] boolValue]) {
            printf("debug\n");
        }
        else {
            printf("ad-hoc\n");
        }
    }
    else if ([[plist valueForKeyPath:@"ProvisionsAllDevices"] boolValue]) {
            printf("enterprise\n");
    }
    else {
            printf("appstore\n");
    }
}

About parser

You can write a command line parser tool or you can google one. The steps won't be hard

  1. trim the suffix and prefix
  2. fetch values of some nodes like ProvisionedDevices, ProvisionsAllDevices
  3. if ProvisionsAllDevices is true. accept it.

done

aelam
  • 2,796
  • 2
  • 27
  • 32