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
Using Authorization Services in a Helper Tool
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 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.
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:
A preauthorization rights set is the same as an authorization rights set as described in “Creating an Authorization Rights Set.”
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; |
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.
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, AuthorizationExternalForm is 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.
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 Mac OS X: System Overview.
Releasing the authorization reference in a factored application is the same as described in “Releasing an Authorization Reference.”
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.”
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 in your helper tool is the same as it is for simple, self-restricted applications. See “Requesting Authorization” for more information.
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.
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 Mac 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.
Note: In Mac OS X v10.1, setuid tools that are copied or moved lose their setuid bit. and the owner and group are changed to match the permissions of the user performing the action. In later releases, users can move setuid tools and preserve the permission set.
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:
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.
Important: Because of the security risk, calling the AuthorizationExecuteWithPrivileges function is recommended only for special and infrequent use such as repairing setuid bits and installing your application. In the most secure computer systems, the right this function requests times out immediately so every time you call it, the user must authenticate.
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.
Important: You may be tempted to use the function AuthorizationExecuteWithPrivileges to perform privileged operations rather than creating and calling your own setuid tool. Although this might seem like an easy solution, using the AuthorizationExecuteWithPrivileges function without the rest of the Authorization Services functions produces a severe security hole because the function indiscriminately runs any tool as the root user. Setuid tools also have security risks, but they are far less severe than using the function AuthorizationExecuteWithPrivileges for purposes other than those described in this document. Read “Factored Applications” for instructions on creating your own helper tool.
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 http://developer.apple.com/samplecode/Sample_Code/Security.htm for a sample self-repairing setuid tool.
Note: The purpose of the self-repair code in the helper tool discussed here is to allow the tool to execute as root after the user has moved or copied the tool, even if the file system has reset the setuid bit and changed the owner and group to match the permissions of the user performing the action. This self-repair code (that is, the call to the AuthorizationExecuteWithPrivileges function) works if the setuid bit has been cleared, whether the tool is owned by root or by the user. If the tool has the setuid bit set and is owned by root, the self-repair code is not called. However, if the setuid bit is set and the tool is owned by the user, the call to the AuthorizationExecuteWithPrivileges function fails because the setuid bit is respected in this case and the tool executes with the user’s privileges rather than root privileges. If you want your self-repairing helper tool to handle this unlikely circumstance, you need to add code to clear the setuid bit before you call the self-repair code.
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) |
Last updated: 2004-02-01