Authorization Services Tasks

This chapter provides instructions and code samples for tasks you can accomplish with Authorization Services. You can adapt these samples in your own application to

A simple, self-restricting application needs to restrict a user from the application’s own operations with minimal security concerns—for example, a grades-and-transcripts application might only allow the registrar to create transcripts. Read “Authorizing in a Simple, Self-Restricted Application” if you have a self-restricting application.

If you have a factored application—for example, an application that must perform an operation as root, such as restarting a daemon—you should read both “Authorizing in a Simple, Self-Restricted Application” and “Authorizing in a Factored Application.”

If your installer must perform a privileged operation, read “Calling a Privileged Installer” to see an example of an installer using Authorization Services.

See Sample Code > Security for sample applications that perform system-restricted privileged operations.

Authorizing in a Simple, Self-Restricted Application

A simple, self-restricted application uses Authorization Services to perform the tasks described in the following sections:

Creating an Authorization Reference Without Rights

The Security Server uses the authorization reference to access the state of the authorization session, which includes any stored credentials. Your application needs only one authorization reference.

You use the AuthorizationCreate function to allocate memory for the authorization reference. The code fragment in Listing 2-1 shows a call to the AuthorizationCreate function that creates an authorization reference without rights. An authorization reference without rights is useful if the rights are not needed immediately, but the authorization reference is required so it can be used in different parts of the application. For example, in the grades-and-transcripts application, the authorization reference might be created when the application starts, but rights aren’t requested until the user attempts to create transcripts.

Listing 2-1  Creating an authorization reference without rights

AuthorizationRef myAuthorizationRef;
OSStatus myStatus;
myStatus = AuthorizationCreate (NULL, kAuthorizationEmptyEnvironment,
            kAuthorizationFlagDefaults, &myAuthorizationRef);

The AuthorizationCreate function takes four parameters. The first is an authorization rights set. Since NULL is passed, no rights are authorized at this time. The second parameter is the authorization environment, which is not currently implemented; pass kAuthorizationEmptyEnvironment. The third parameter is the authorization options. The constant kAuthorizationFlagDefaults is passed because the application is not requesting any rights. The fourth parameter is the address of the authorization reference you declared. On return, the authorization reference refers to the current authorization session. If the authorization reference is created successfully, the function returns errAuthorizationSuccess.

“Requesting Authorization” describes how to use the AuthorizationCopyRights and AuthorizationCreate functions to request authorization. When your application is done with the authorization reference, use the AuthorizationFree function as described in “Releasing an Authorization Reference.”

Requesting Authorization

After you create an authorization reference, you can then request authorization. Your application should perform authorization immediately before every privileged operation. In the case of the grades-and-transcripts example, the application requests authorization immediately before creating a transcript.

When your application requests authorization, the Security Server may request the user to authenticate. Authorization Services allows you to take full advantage of the Security Server’s authentication plug-in architecture to deal with authentication for you. Instead of a user name and password, the authentication may use fingerprints or smart cards, but your application code stays the same.

Figure 1-3 shows the authentication dialog that the Security Server provides. The user enters an administrator user name and password and clicks OK. The Security Server then uses the user name and password to authenticate and authorize the user.

Authorization requires the creation of an authorization rights set and authorization options to use in a call to the functions AuthorizationCopyRights or AuthorizationCreate. In your application, request authorization by performing the tasks described in the following sections:

Creating an Authorization Rights Set

To authorize a user for specific rights, you must create an authorization rights set to pass to the Security Server through the AuthorizationCopyRights or AuthorizationCreate functions. The authorization rights set consists of an authorization item array and the number of items in the authorization item array. The authorization item array contains information about the rights that your application is requesting.

Each item in the authorization item array consists of four pieces of information:

  • The name of the right

  • A value that contains optional data pertaining to the right

  • The byte length of the value field

  • Optional flags

Listing 2-2 shows an example of an authorization item array. In most cases, when creating an item for a right, you set the value field to NULL, and the valuelength and flags fields to 0. You should set the name field to the name of the right you are requesting. For information on naming your own rights, see “Rights.”

Listing 2-2  Creating an authorization item array

AuthorizationItem myItems[2];
 
myItems[0].name = "com.myOrganization.myProduct.myRight1";
myItems[0].valueLength = 0;
myItems[0].value = NULL;
myItems[0].flags = 0;
 
myItems[1].name = "com.myOrganization.myProduct.myRight2";
myItems[1].valueLength = 0;
myItems[1].value = NULL;
myItems[1].flags = 0;

For example, a grades-and-transcripts application might request the right com.myOrganization.myProduct.transcripts.create. The valueLength, value, and flags fields would be unused and set to 0, NULL, and 0, respectively.

Listing 2-3 shows an example of an authorization rights set. In the authorization rights set, the count field contains the number of rights in the authorization item array, while the items field points to the authorization item array you created.

Listing 2-3  Creating a set of authorization rights

AuthorizationRights myRights;
myRights.count = sizeof (myItems) / sizeof (myItems[0]);
myRights.items = myItems;

Specifying Authorization Options

You use the authorization options to instruct the Security Server how to proceed with the AuthorizationCopyRights and AuthorizationCreate functions. By setting the authorization options, you can use these functions to

  • Authorize partial rights

  • Authorize all rights

  • Preauthorize rights

You can include with these options the option to interact with the user. The Security Server requires user interaction to perform authentication. The most common combination authorizes all rights and allows user interaction. Listing 2-4 shows an example of the authorization options for authorization.

Listing 2-4  Specifying authorization options for authorization

AuthorizationFlags myFlags;
myFlags = kAuthorizationFlagDefaults |
            kAuthorizationFlagInteractionAllowed |
            kAuthorizationFlagExtendRights;

The kAuthorizationFlagDefaults constant zeros the bit mask. The kAuthorizationFlagExtendRights constant instructs the Security Server to grant the rights. Without this flag, the AuthorizationCopyRights and AuthorizationCreate functions would return the appropriate error code, but no rights would be extended to the user.

If your application does not require all of the rights to be authorized, you can include the kAuthorizationFlagPartialRights constant to request partial authorization. You can then determine what to allow the user to do based on which rights the Security Server grants. Listing 2-5 shows an example of setting the authorization options for partial authorization.

Listing 2-5  Specifying authorization options for partial authorization

myFlags = kAuthorizationFlagDefaults |
            kAuthorizationFlagInteractionAllowed |
            kAuthorizationFlagExtendRights |
            kAuthorizationFlagPartialRights;

See “Requesting Preauthorization” to learn what authorization options to set for preauthorization.

Authorizing

The code fragment in Listing 2-6 shows a call to the AuthorizationCopyRights function based on the authorization reference and authorization rights set you created and the authorization options you specified. In the grades-and-transcripts example, the AuthorizationCopyRights function is used to authorize the right to create a transcript.

Listing 2-6  Authorizing rights

myStatus = AuthorizationCopyRights (myAuthorizationRef, &myRights,
        kAuthorizationEmptyEnvironment, myFlags, NULL);

The first parameter is the authorization reference created in “Creating an Authorization Reference Without Rights.” The second parameter is the authorization rights set created in “Creating an Authorization Rights Set.” The third parameter is the authorization environment. The authorization environment is not currently implemented, so pass kAuthorizationEmptyEnvironment. The fourth parameter is the authorization options set in “Specifying Authorization Options .”

The fifth parameter is useful when authorizing partial rights as shown in Listing 2-7. This parameter points to an empty authorized rights set you declare. On return, this consists of the rights that the Security Server actually authorizes. If you create a pointer to an authorized rights set, then you should release it as described in “Releasing an Authorization Item Array.”

Listing 2-7  Authorizing partial rights

AuthorizationRights *myAuthorizedRights;
myStatus = AuthorizationCopyRights (myAuthorizationRef, &myRights,
            kAuthorizationEmptyEnvironment, myFlags,
            &myAuthorizedRights);

The AuthorizationCopyRights function returns errAuthorizationSuccess if the Security Server grants all the rights. You can use the return status to determine whether the user may perform the privileged operation.

You can use an authorization rights set and authorization options to request authorization when you create an authorization reference.

Listing 2-8 shows an example combining authorization with the AuthorizationCreate function.

Listing 2-8  Creating an authorization reference with rights

myStatus = AuthorizationCreate (&myRights, kAuthorizationEmptyEnvironment,
            myFlags, &myAuthorizationRef);

You can also use the AuthorizationCreate function to authorize a user for a one-time privileged operation. One-time authorization is useful if your application needs to authorize only once when it is run. Listing 2-9 shows an example of how to use an authorization rights set and authorization options with the AuthorizationCreate function without producing an authorization reference. Pass NULL instead of an authorization reference.

Listing 2-9  A one-time authorization call

myStatus = AuthorizationCreate (&myRights, kAuthorizationEmptyEnvironment,
            myFlags, NULL);

Releasing an Authorization Item Array

When you finish with the authorization item set in Listing 2-7, call the AuthorizationFreeItemSet function, as shown in Listing 2-10, to release the memory it uses. Use this function only on authorization item arrays that the Security Server allocates, such as those used in the AuthorizationCopyRights and AuthorizationCopyInfo functions.

Listing 2-10  Releasing an authorization item array

myStatus = AuthorizationFreeItemSet (myAuthorizedRights);

Releasing an Authorization Reference

Before exiting your application, or at any time you want to end the current authorization session, call the AuthorizationFree function to release the authorization reference. For example, the grades-and-transcripts application would wait until the user quits the application before releasing the authorization reference. Using the same authorization reference every time the user creates a transcript allows the Security Server to reuse any shared credentials that haven’t expired. In contrast, an action such as the user clicking the open-lock button in the Network preferences pane can trigger the release of the authorization reference, requiring the user to reauthorize when she clicks the closed-lock button.

The code segment in Listing 2-11 shows an example of using the AuthorizationFree function. You must pass the authorization reference and authorization options. For authorization options, pass the constant kAuthorizationFlagDefaults if you want to revoke the credentials associated with the current process, or pass the constant kAuthorizationFlagDestroyRights to release all shared credentials from all processes that use them.

Listing 2-11  Releasing an authorization reference

myStatus = AuthorizationFree (myAuthorizationRef,
            kAuthorizationFlagDestroyRights);

Authorizing in a Factored Application

Factored applications, whether system-restricted or self-restricted, use an application to control the graphical user interface and nonprivileged operations and use a separate helper tool to perform the privileged operations.

Read “Using Authorization Services in a Factored Application” for a description of using Authorization Services in a factored application and “Using Authorization Services in a Helper Tool” for a description of using Authorization Services in a helper tool.

Using Authorization Services in a Factored Application

You can use Authorization Services in your factored application to perform the tasks described in the following sections:

An example of a factored application is one that restarts the Internet daemon. The application performs all the nonprivileged operations while the helper tool restarts the daemon. The application creates an authorization reference and preauthorizes the right to restart the Internet daemon. The application uses the result to determine whether to start the helper tool. The application creates an external version of the authorization reference and passes it to the helper tool. When the authorization reference is no longer needed, the application releases it.

Creating an Authorization Reference

Creating an authorization reference in a factored application is the same as in a simple, self-restricting application. See “Creating an Authorization Reference Without Rights” to learn how to create an authorization reference.

Requesting Preauthorization

You should preauthorize rights before calling a helper tool. Using the results of preauthorization, you can prevent an unauthorized user from invoking the helper tool. Doing so saves the time of starting a new process and using resources as well as saving the user from preparing to perform an operation he doesn’t have privileges to perform.

Preauthorization requires the creation of an authorization rights set and authorization options to use in a call to the functions AuthorizationCopyRights or AuthorizationCreate. In your application, you preauthorize a user by performing the steps described in the following sections:

Creating a Preauthorization Rights Set

A preauthorization rights set is the same as an authorization rights set as described in “Creating an Authorization Rights Set.”

Specifying Authorization Options for Preauthorization

Authorization options for preauthorization are similar to the authorization options for authorization and partial authorization described in “Specifying Authorization Options .” The only difference between preauthorization and authorization is that you are not using the result to determine if a user can perform a privileged operation. Instead, you should use the result to determine if the user can be authorized at a later time.

Listing 2-12 shows an example of setting the authorization options for preauthorization. The kAuthorizationFlagDefaults constant zeros out the bit mask. The kAuthorizationFlagExtendRights constant tells the Security Server to extend any rights granted to the user. The kAuthorizationFlagInteractionAllowed constant tells the Security Server that it may interact with the user for authentication purposes. The kAuthorizationFlagPreAuthorize constant tells the Security Server to preauthorize the rights requested.

Listing 2-12  Specifying authorization options for preauthorization

AuthorizationFlags myFlags;
myFlags = kAuthorizationFlagDefaults |
            kAuthorizationFlagExtendRights |
            kAuthorizationFlagInteractionAllowed |
            kAuthorizationFlagPreAuthorize;
Preauthorizing

Calling the AuthorizationCopyRights or AuthorizationCreate function is the same for preauthorization as it is for authorization. See Listing 2-6 in the section “Authorizing” for examples.

Creating an External Authorization Reference

After creating the authorization reference and preauthorizing rights, you need to pass the authorization reference to your helper tool. Sharing the authorization reference allows the helper tool to use any credentials that are part of the factored application’s authorization session. When you pass the authorization reference to your helper tool, the authorization dialog can show your application’s path rather than the path to the helper tool. It also enables the system to determine whether the authorization dialog should have keyboard focus.

One problem with the authorization reference is that it is not in a form that can be transferred from one process to another. To solve this problem, Authorization Services provides a function to translate the authorization reference into an external authorization reference that you can pass to your helper tool.

To create an external authorization reference, declare a variable of type AuthorizationExternalForm and pass it to the AuthorizationMakeExternalForm function along with the existing authorization reference. On return, that variable contains a transferable form of the authorization reference. Listing 2-13 shows an example of creating an external authorization reference.

Listing 2-13  Creating an external authorization reference

AuthorizationExternalForm myExternalAuthorizationRef;
myStatus = AuthorizationMakeExternalForm (myAuthorizationRef,
            &myExternalAuthorizationRef);

Read “Retrieving an Authorization Reference” to learn how to retrieve the authorization reference from the external authorization reference in your helper tool.

Calling a Helper Tool

When you are ready to call your helper tool, pass the external authorization reference to the tool using some form of interprocess communication, such as a communications pipe. For a sample application and self-repairing helper tool, see http://developer.apple.com/samplecode/Sample_Code/Security.htm. For more information on interprocess communications, see Inside OS X: System Overview.

Releasing an Authorization Reference

Releasing the authorization reference in a factored application is the same as described in “Releasing an Authorization Reference.”

Using Authorization Services in a Helper Tool

You can use Authorization Services in your helper tool to perform the tasks described in the following sections:

For example, a helper tool that restarts the Internet daemon retrieves the authorization reference from the external authorization reference passed by the application. Then the helper tool requests authorization immediately before restarting the Internet daemon.

If your helper tool is actually a self-repairing helper tool, you should also read “Repairing a Helper Tool.”

Retrieving an Authorization Reference

To share the authorization session, the factored application passes an external authorization reference to the helper tool (see “Creating an External Authorization Reference”). In the helper tool, you use the AuthorizationCreateFromExternalForm function to retrieve an authorization reference from the external authorization reference.

Listing 2-14 shows an example using the AuthorizationCreateFromExternalForm function. In this example, the external authorization reference is read in from a communications pipe between the helper tool process and the parent process. You then pass the external authorization reference to the function AuthorizationCreateFromExternalForm. On return, myAuthorizationRef is the authorization reference.

Listing 2-14  Retrieving an authorization reference

AuthorizationRef myAuthorizationRef;
AuthorizationExternalForm myExternalAuthorizationRef;
OSStatus myStatus;
 
/* *** You should read in the external authorization reference into
        myExternalAuthorizationRef here. *** */
 
myStatus = AuthorizationCreateFromExternalForm (&myExternalAuthorizationRef,
            &myAuthorizationRef);

Performing Authorization

Performing authorization in your helper tool is the same as it is for simple, self-restricted applications. See “Requesting Authorization” for more information.

Executing the Privileged Operation

You should use the result of the authorization to determine whether the user is allowed to perform the privileged operation. There are no Authorization Services functions required for actually executing the privileged operation.

Repairing a Helper Tool

If your helper tool needs to run as root to perform privileged operations, such as restarting the Internet daemon, then it should have its setuid bit set. Tools that have the setuid bit set (sometimes referred to as setuid tools) must be Mach-O binaries because CFM binaries don’t support the setuid or setgid (set group identifier) bit. When you install your program, your installer should set the helper tool’s setuid bit, and set its owner to root.

In OS X v10.1 and earlier, when a user moves a setuid tool to another volume, or copies it from one place to another, the setuid bit is reset by the file system and the group and owner change to match the user moving the setuid tool. This is done purposely to reduce the security risk that a setuid tool poses by allowing any user to run the setuid tool as root. On the other hand, most users expect that when they copy an application or tool from one folder to another, it will still work. Thus, the setuid bit, group, and owner need to be reset without editing the permissions in the terminal window. This section provides code to allow your setuid tool to repair its own setuid bit when this problem occurs.

All setuid tools are potential security problems. This case poses a particular problem because the tool self-repairs its setuid bit even if a user tampers with the setuid tool’s code. As added security, you might want to display a warning to users whenever performing this action so they can decide to continue or cancel the self-repair operation, or possibly force the user to reinstall the application from the installer.

You can repair the setuid bit on your helper tool by performing the tasks described in the following sections:

Calling a Helper Tool as Root

For your helper tool to set its own setuid bit, the tool must have root privileges. This is a circular problem, since you can’t change permissions on your helper tool unless your helper tool is already running as root. This is where the function AuthorizationExecuteWithPrivileges comes into play.

The AuthorizationExecuteWithPrivileges function executes any application as root through a special security process. The code sample in Listing 2-15 demonstrates how a helper tool can recursively call itself with root privileges so it can repair its own setuid bit.

Listing 2-15  Executing a helper tool with root privileges

FILE *myCommunicationsPipe = NULL;
char *myArguments[] = {"--self-repair", NULL};
char myPath[MAXPATHLEN];
 
/* *** You should determine the path of your tool here and put the result in
        myPath. *** */
 
myStatus = AuthorizationExecuteWithPrivileges (myAuthorizationRef,
            myPath, kAuthorizationFlagDefaults, myArguments,
            &myCommunicationsPipe);

The AuthorizationExecuteWithPrivileges function expects you to pass five parameters. The first parameter is the authorization reference you retrieved as shown in “Retrieving an Authorization Reference.” The authorization reference allows the helper tool to use any credentials that are part of the factored application’s authorization session. The second parameter is the full POSIX pathname of the helper tool—in this case, the setuid tool—that is being called. The third parameter is the authorization options. In this function, this parameter is not implemented, so for now, set it to kAuthorizationFlagDefaults. The fourth parameter is a null-terminated array of arguments for the tool being called. You can use this parameter to pass any information you need from the parent process to the child process. In this case, the string "--self-repair" is passed to indicate to the helper tool that it should execute the code in Listing 2-16. The fifth parameter is a communications pipe so the helper tool can pass the data it received from the factored application to itself.

Setting the Setuid Bit

In Listing 2-15, the helper tool recursively calls itself, passing the self-repair argument, --self-repair. Therefore, in the same helper tool, you need to check for the self-repair argument and, if it is found, fix the setuid bit. See the More Is Better sample code (MoreIsBetter) for a sample self-repairing setuid tool.

When you call the AuthorizationExecuteWithPrivileges function, you need a way to retrieve the authorization reference that is passed in the call. Listing 2-16 shows code using the AuthorizationCopyPrivilegedReference function to retrieve the authorization reference. The only time you use this function is to retrieve an authorization reference passed by a call to AuthorizationExecuteWithPrivileges.

The first parameter of the call to the AuthorizationCopyPrivilegedReference function is an empty authorization reference you declare. You should not call the AuthorizationCreate function. On return, the authorization reference points to a copy of the original authorization reference. The second parameter is not implemented, so set it to kAuthorizationFlagDefaults.

Listing 2-16  Setting the setuid bit

myStatus = AuthorizationCopyPrivilegedReference (&myAuthorizationRef,
            kAuthorizationFlagDefaults)

Calling a Privileged Installer

Occasionally, an installer must install files in directories that are not owned by the user running the installer. This should be a rare case and you should avoid it if at all possible. In the event that it can’t be avoided, the code in Listing 2-17 shows a tool that runs the /usr/bin/id utility with optional flag -un. By replacing the utility path and including your own flags, you can use this sample code to call your installer with root privileges. Your installer will then be able to perform any privileged operations it requires.

Listing 2-17  Calling a privileged installer

#include <Security/Authorization.h>
#include <Security/AuthorizationTags.h>
 
int read (long,StringPtr,int);
int write (long,StringPtr,int);
 
int main() {
 
    OSStatus myStatus;
    AuthorizationFlags myFlags = kAuthorizationFlagDefaults;              // 1
    AuthorizationRef myAuthorizationRef;                                  // 2
 
    myStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,  // 3
                myFlags, &myAuthorizationRef);
    if (myStatus != errAuthorizationSuccess)
        return myStatus;
 
    {
        AuthorizationItem myItems = {kAuthorizationRightExecute, 0,    // 4
                NULL, 0};
        AuthorizationRights myRights = {1, &myItems};                  // 5
 
        myFlags = kAuthorizationFlagDefaults |                         // 6
                kAuthorizationFlagInteractionAllowed |
                kAuthorizationFlagPreAuthorize |
                kAuthorizationFlagExtendRights;
        myStatus = AuthorizationCopyRights (myAuthorizationRef,        // 7
                                     &myRights, NULL, myFlags, NULL );
    }
 
    if (myStatus != errAuthorizationSuccess) goto DoneWorking;
 
    {
        char myToolPath[] = "/usr/bin/id";
        char *myArguments[] = { "-un", NULL };
        FILE *myCommunicationsPipe = NULL;
        char myReadBuffer[128];
 
        myFlags = kAuthorizationFlagDefaults;                          // 8
        myStatus = AuthorizationExecuteWithPrivileges                  // 9
                (myAuthorizationRef, myToolPath, myFlags, myArguments,
                &myCommunicationsPipe);
 
        if (myStatus == errAuthorizationSuccess)
            for(;;)
            {
                int bytesRead = read (fileno (myCommunicationsPipe),
                        myReadBuffer, sizeof (myReadBuffer));
                if (bytesRead < 1) goto DoneWorking;
                write (fileno (stdout), myReadBuffer, bytesRead);
            }
    }
 
    DoneWorking:
 
    AuthorizationFree (myAuthorizationRef, kAuthorizationFlagDefaults); // 10
 
    if (myStatus) printf("Status: %ld\n", myStatus);
    return myStatus;
}

Here are explanations of the numbered lines of code in Listing 2-17:

  1. Declare a variable to store authorization options.

  2. Declare an authorization reference.

  3. Use the AuthorizationCreate function to initialize the authorization reference. See “Creating an Authorization Reference Without Rights” for more information.

  4. Create an authorization item array. The user must have the right to execute to use the AuthorizationExecuteWithPrivileges function. To create a right to execute authorization item, set the name field to kAuthorizationRightExecute, the value fields to NULL, the valueLength and flags fields to 0. See “Creating an Authorization Rights Set” for more information.

  5. Create an authorization rights set. Set the count field to the number of items in the authorization item array, and set the items field to point to the authorization item array. See “Creating an Authorization Rights Set” for more information.

  6. Set the authorization options to preauthorize the rights. See “Specifying Authorization Options for Preauthorization” for more information.

  7. Use the AuthorizationCopyRights function to preauthorize the right to execute your installer as root. In this case, there is no reason to continue if the user can’t preauthorize. See “Authorizing” for more information.

  8. Set the authorization options for the AuthorizationExecuteWithPrivileges function to kAuthorizationFlagDefaults. Other authorization options, such as that specified by the kAuthorizationFlagInteractionAllowed constant, are not necessary because the AuthorizationExecuteWithPrivileges function interacts with the user whether you specify the option or not.

  9. Use the AuthorizationExecuteWithPrivileges function to invoke your installer. Pass the authorization reference in the first parameter. Pass the installer’s full POSIX pathname in the second parameter. Pass the authorization options default in the third parameter. Pass any arguments for the installer in the fourth parameter. A communications pipe to the tool may be set up through the fifth parameter. See “Calling a Helper Tool as Root” for more information about the AuthorizationExecuteWithPrivileges function.

  10. Release the authorization reference using the AuthorizationFree function. See “Releasing an Authorization Reference” for more details.