Authority Issues: Gotchas with the Security Framework

You see those pesky authorization dialogs every so often, right? "Finder requires that you type your password." "BBEdit requires that you type your password." This means the application involved is using the Security framework to perform an action with, say, administrative privileges that it wouldn’t normally have.

If you turn down the Details disclosure triangle in such a dialog, you see the name of the app and an arcane Requested Right string.

These rights should be added to what’s called the "policy database," stored in the file /etc/authorization, when the application that wants to use them is first installed. For a full explanation, see Performing Privileged Operations With Authorization Services, but for this post, I’m interested in the API you use to add those rights.

Here’s the example given in the AuthorizationDB.h header in the Security framework.

OSStatus status =
   AuthorizationRightSet(
      NULL,
      "com.ifoo.ifax.send",
      CFSTR(kAuthorizationRuleIsAdmin),
      CFSTR("Authorize sending of a fax"),
      NULL,
      NULL);

Looks reasonable, right?

Bzzzt. Sorry! If you actually try to run this code, at least on Mac OS X 10.4 "Tiger," you get a −2147418108 error. What’s a −2147418108 error? No clue. But it’s not good.

The first parameter is of type AuthorizationRef. You get a valid authorization reference if the user types her password into one of those dialogs, but you don’t need special permission to add a new right to the policy database. Hence the null value in the example. But it doesn’t work.

Here’s what you need to do instead:

AuthorizationRef authorization = NULL;

OSStatus result =
   AuthorizationCreate(
      NULL, NULL,
      kAuthorizationFlagDefaults,
      &authorization);

if (result == noErr)
{
   result =
      AuthorizationRightSet(
         authorization,
         "com.ifoo.ifax.send",
         CFSTR(kAuthorizationRuleAuthenticateAsAdmin),
         CFSTR("Authorize sending of a fax"),
         NULL,
         NULL);

   AuthorizationFree(
      authorization,
      kAuthorizationFlagDefaults);
}

AuthorizationCreate() actually has a whole bunch of options you can use, but here I’m using it with minimal values to create a blank, good-for-nothing AuthorizationRef. Even so, that’s good enough for AuthorizationRightSet(). With this code, there’s no −2147418108 error.

So we’re done? Not yet!

You’ll notice above that I changed the third, "rule" parameter of AuthorizationRightSet() from the kAuthorizationRuleIsAdmin in the original sample to kAuthorizationRuleAuthenticateAsAdmin. With that rule, you are shown an authorization dialog when I use AuthorizationCreate() like so:

AuthorizationItem myItems =
   { "com.ifoo.ifax.send", 0, NULL, 0 };

AuthorizationRights myRights =
   { 0, &myItems };

AuthorizationFlags myFlags =
   kAuthorizationFlagInteractionAllowed |
   kAuthorizationFlagExtendRights;

AuthorizationRef authorization = NULL;

OSStatus result =
   AuthorizationCreate(&myRights, kAuthorizationEmptyEnvironment,
      myFlags, &authorization);

This call’s a lot more complicated, eh? But it boils down to trying to get a valid authorization for the right we added.

Here’s the dialog you see in response:

Authorization dialog with erroneous caption

Oops! If you fill in the fourth, "description key" parameter of AuthorizationRightSet() with a string as shown in the header example, that "description key" is concatenated with the default system prompt in any authorization dialog that refers to that right.

Passing NULL for the "description key" parameter uses just the default system prompt, which works much better.

Now we’re done.

4 comments

  1. Eric Albert

    -2147418108 is 0x80010004. I don’t know what that means either — it isn’t a Mach error code — but it’s closer to being meaningful than the decimal representation.

  2. Michael McCracken

    I’d suggest using the fact that the description key is prepended to the default prompt to make it say something more useful, since the default prompt doesn’t say why the app wants the password.

    Why not set it to something like “Before you can send a fax,” so you get “Before you can send a fax, AuthorizationIssues requires that you type your password”…

  3. Andrew Pontious

    Michael:

    Maybe, but it’s a lousy way to do things, localization-wise, since the best way to augment the default prompt may not be through prepending, in other languages.

    Also, I didn’t make up the description key, it’s what was listed in the documentation. If what you’re describing was the official intent, you’d think they would’ve made an example that actually *did* that.

    So I think it’s a bug, one way or another.