1.1b1
MoreAuthSample shows how to access privileged functionality from a non-privileged application on Mac OS X. This technique, described in detail in the Authorization Services documentation, is somewhat complicated to implement, and thus a good code sample can be extremely valuable.
MoreAuthSample is designed to support Mac OS X 10.0 and above, although it has only been tested on Mac OS X 10.1 and above.
MoreAuthSample is a replacement for AuthSample, the original sample that showed this technique. As the two samples share no code, I decided to make a new sample rather than update AuthSample.
MoreAuthSample has the following advantages over AuthSample.
Having said this, I should stress that the basic algorithm used by MoreAuthSample is identical to that used by AuthSample, which means that MoreAuthSample inherits most of the security pros and cons of AuthSample.
The sample contains the following items.
To try out the sample for yourself, launch the MoreSecurityTest application and click the one of the two test buttons. This will issue a command to the embedded helper tool and display the results.
You can build the sample using Project Builder from the Dec 2002 developer tools. Open the project file (MoreSecurityTest.pbproj), make sure that the "App" target is selected, and choose Build from the Build menu. This will automatically build the application and the helper tool.
The project also builds with CodeWarrior Pro 8.3 on Mac OS X 10.2. Open the project file (MoreSecurityTest.mcp) and choose Make from the Project menu. This will automatically build the application and the helper tool.
main
function looks like the one shown in Listing 1. In this listing, MyCommandProc
is a callback that is executed when a request is passed to the tool. It can do privileged operations, such as committing changes to System Configuration framework. Its parameters are passed to it as a CFDictionary. It can pass results back to the application by creating a response dictionary.
Listing 1. The main function for a MoreSecurity-based helper tool
|
MoreSecCopyHelperToolURLAndCheckBundled
to find (or create) the working copy of your tool. The working copy of the helper tool will exist within the user's Application Support folder. See the description of MoreSecCopyHelperToolURLAndCheck[Bundled
] (in "MoreSecurity.h") for an explanation of this.
MoreSecExecuteRequestInHelperTool
to execute the tool, passing it a request dictionary. On success, you will get back a response dictionary. Use MoreSecGetErrorFromResponse
to extract the error result from your tool's MyCommandProc
routine.
kMoreSecFileDescriptorsKey
(in "MoreSecurity.h") for information on how to do this.
AuthorizationCopyRights
to have your tool be self-limiting, as described in the Authorization Services documentation.
This sample inherits much of its architecture from AuthSample. For a high-level overview of how AuthSample works, look at the Authorization Services documentation. For a detail description of how this sample works, look at the commented code.
This sample's complex design is required for a number of reasons.
By placing the setuid root helper tool within the Application Support folder, the MoreSecurity library allows you to copy the application bundle from one disk to another without breaking it, and without any weird error dialogs. In addition, you can copy the program to another machine entirely, and the only thing the user has to do is to reenter their admin password when they first run the application. Finally, the user's Application Support folder is typically inside their home directory, which is typically on the system volume, and thus is not ignoring privileges.
MoreIsBetter code, including MoreSecurity, makes heavy use of asserts to check for programming errors in the debug build. By default these asserts are enabled. If you ship this code in a production system, you must disable these asserts. The tripping of an assert in a privileged program could be used to create a security violation. To disable these asserts, set the NDEBUG
compile time variable.
MoreSecurity requires that both the application and the helper tool set the disposition for SIGPIPE
to be SIG_IGN
. You can set it this way by calling MoreUNIXIgnoreSIGPIPE
. If you're using CodeWarrior, you must make sure that you disable SIGPIPE
via System framework calls. Do not call the signal
function, because MSL provides its own version of signal
that does not disable SIGPIPE
at the system level. If in doubt, call MoreUNIXIgnoreSIGPIPE
, which does the heavy lifting for you (internally it calls sigaction
, which MSL does not override).
MoreAuthSample stores the template helper tool in the "MacOS" directory within your package, so that the tool can be found using CFBundleCopyAuxiliaryExecutableURL
. AuthSample incorrectly puts the helper tool in the "Resources" directory.
When MoreSecHelperToolMain
calls your callback function (the commandProc
parameter), your process's EUID is set to its RUID. This is in line with the standard security policy of only setting the EUID to 0 when privileges are required. You can use MoreSecSetPrivilegedEUID
to switch to EUID 0 and MoreSecTemporarilySetNonPrivilegedEUID
to return to the non-privileged EUID.
The error code returned by MoreSecHelperToolMain
indicates whether the invocation of the helper tool was successful. It does not indicate that the command performed by the helper tool worked. Use MoreSecGetErrorFromResponse
to get the result status from the helper tool command callback.
Your helper tool must call MoreSecHelperToolCopyAuthRef
before it calls MoreSecDestroyInheritedEnvironment
. MoreSecDestroyInheritedEnvironment
destroys the environmental information that MoreSecHelperToolCopyAuthRef
needs to return a meaningful result.
Your application must be prepared to handle the kMoreSecIgnoringPrivsInFolderErr
(5370) error result from MoreSecCopyHelperToolURLAndCheckBundled
. This indicates that the user's home directory is on a volume whose privileges the system is set to ignore. MoreSecurity can not function correctly in that case. The first version of your application should probably just display a meaningful error message in this case. If you get a significant number of users complaining about this error, you will need to modify your program to handle this error more gracefully (perhaps put the helper tool in the Temporary Items folder). If you are forced to do this, please let me know so that I can add support for this in MoreSecurity.
When MoreSecCopyHelperToolURLAndCheckBundled
copies the helper tool from your bundle into the Application Support folder, it only copies the data fork. For the new copy of the helper tool to work properly, it must be a Mach-O executable with no dependence on the resource fork or HFS-specific metadata.
MoreSecExecuteRequestInHelperTool
(actually its sub-routine, ExecuteSelfInPrivilegedSelfRepairMode
) uses a workaround to make up for the fact that AuthorizationExecuteWithPrivileges
does not return the process ID (PID) of the child process [3090277]. Without this workaround, the inopportune termination of another, unrelated, child process could confuse MoreSecurity into reaping the wrong zombie. With this workaround the chance of that happening is minimal, but it's still possible. For optimal results, structure your application so that you don't need to call MoreSecExecuteRequestInHelperTool
while your program has outstanding unterminated child processes.
MoreSecurity does not check the helper tool for tampering before it self-repairs. The rationale for this is explained in the comment "Notes on Code Signing" in "MoreSecurity.c".
Unlike AuthSample, MoreAuthSample does not use preauthorization. This is because the preauthorization flag (kAuthorizationFlagPreAuthorize
) does nothing on current versions of Mac OS X [2907852]. In fact, by attempting to do preauthorization, AuthSample causes the notorious "two authentication dialogs on first run" problem. When the system is fixed to pay attention to the preauthorization flag, I will revise MoreAuthSample to take advantage of it.
If you find any problems with this sample, mail <DTS@apple.com> and Ill try to fix them up.
1.0a1 (Dec 2002) was given to reviewers.
1.0b1 (Jan 2003) was the first version that shipped to the general public. The improvements over 1.0a1 are purely cosmetic.
1.0b2 (May 2003) is a minor update. The update fixes MoreSecIsFolderIgnoringPrivileges
to not return false positives. I also picked up a number of other less relevant changes.
1.1b1 (May 2003) is a an update to support passing descriptors from the helper tool back to the application. This allows you to write a helper tool that, say, binds a low-numbered TCP port on behalf of your application.
Share and Enjoy.
Apple Developer Technical Support
Networking, Communications, Hardware
25 May 2003