4

I am trying to install a secure helper tool with SMJobBless. When it fails and before calling SMJobBless, I am calling SMJobRemove because I need to remove an older version of the tool and this succeeds. SMJobBless is returning an error code of 4098. The NSError object is only telling me that "The operation couldn't be completed. There was an error in the CodeSigning subsystem."

If I rerun my code, the SMJobBless function works. I would assume this is because it was removed previously, but why would it have not worked the first time? I can then communicate with the tool and everything functions normally. Observing that everything is functioning normally, I believe I can be certain that I am meeting the five requirements for SMJobBless as described in the documentation.

If I increment the version of my tool and try again, SMJobRemove will work, but, again, SMJobBless with the error code 4098.

If it matters, I am using OS X 10.7.3.

ericg
  • 8,413
  • 9
  • 43
  • 77

1 Answers1

4

Could it be that you're calling CFBundleCopyInfoDictionaryForURL on the code signed helper tool?

If so, it appears as though this function seems to break the code signing validity. (Presumably because CFBundle modifies the Info.plist data in memory, but this is just my guess.)

The solution is to use SecCodeCopySigningInformation to read the helper tool's version info:

-(NSString *) bundleVersionForCodeSignedItemAtURL:(NSURL *)url {
    OSStatus status;

    // Sanity check -- nothing begets nothing
    if (!url) {
        return nil;
    }

    // Get the binary's static code
    SecStaticCodeRef codeRef;
    status = SecStaticCodeCreateWithPath((CFURLRef)url, kSecCSDefaultFlags, &codeRef);
    if (status != noErr) {
        NSLog(@"SecStatucCodeCreateWithPath() error for %@: %d", url, status);
        return nil;
    }

    // Get the code signature info
    CFDictionaryRef codeInfo;
    status = SecCodeCopySigningInformation(codeRef, kSecCSDefaultFlags, &codeInfo);
    if (status != noErr) {
        NSLog(@"SecCodeCopySigningInformation() error for %@: %d", url, status);
        CFRelease(codeRef);
        return nil;
    }

    // The code signature info gives us the Info.plist that was signed, and
    // from there we can retrieve the version
    NSDictionary *bundleInfo = (NSDictionary *) CFDictionaryGetValue(codeInfo, kSecCodeInfoPList);
    NSString *version = [bundleInfo objectForKey:@"CFBundleVersion"];

    // We have ownership of the code signature info, so we must release it.
    // Before we do that, we need to hold onto the version otherwise we go
    // crashing and burning.
    [[version retain] autorelease];
    CFRelease(codeInfo);
    CFRelease(codeRef);

    return version;
}

To give credit where it's due: the vital piece of information regarding CFBundleCopyInfoDictionaryForURL came from Ian MacLeod's SMJobKit.

Philipp
  • 891
  • 1
  • 11
  • 12
  • This code is leaking a file descriptor to the helper tool. `codeRef` should be `CFRelease()`d upon return. I tried to edit the post myself but it was rejected as "too minor" an issue. – LCC Feb 08 '14 at 10:02
  • Wow! That was exactly the problem I was having, and this fixed it beautifully. Thanks for the pointer and, better yet, actual working code! I only wish Apple's documentation for SMJobBless and friends was better. – Tim Ruddick Jul 01 '14 at 17:13