Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Relevant replacement documents include:
SampleApp.m
/* |
File: SampleApp.m |
Contains: Application side of the example of how to use BetterAuthorizationSampleLib. |
Written by: DTS |
Copyright: Copyright (c) 2007 Apple Inc. All Rights Reserved. |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple, Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under Apple's |
copyrights in this original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks or logos of |
Apple, Inc. may be used to endorse or promote products derived from the |
Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that |
may be infringed by your derivative works or by other works in which the Apple |
Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
#include <unistd.h> |
#include <netinet/in.h> |
#import <Cocoa/Cocoa.h> |
#include "BetterAuthorizationSampleLib.h" |
#include "SampleCommon.h" |
///////////////////////////////////////////////////////////////// |
#pragma mark ***** Globals |
static AuthorizationRef gAuth; |
///////////////////////////////////////////////////////////////// |
#pragma mark ***** Objective-C Wrapper |
// Our trivial application object, SampleApp, is instantiated by our nib. It |
// has four actions, three for the buttons and one for the Destroy Rights menu item. |
// It has a two outlets, one pointing to the text view where we log our results |
// and the other referencing the "Force failure" checkbox. |
@interface SampleApp : NSObject { |
NSTextView * textView; |
NSButton * forceFailure; |
} |
- (IBAction)doGetVersion:(id)sender; |
- (IBAction)doGetUIDs:(id)sender; |
- (IBAction)doLowNumberedPorts:(id)sender; |
- (IBAction)destroyRights:(id)sender; |
@end |
@implementation SampleApp |
- (IBAction)doGetVersion:(id)sender |
// Called when the user clicks the "GetVersion" button. This is the simplest |
// possible BetterAuthorizationSample operation, in that it doesn't handle any failures. |
{ |
OSStatus err; |
NSString * bundleID; |
NSDictionary * request; |
CFDictionaryRef response; |
response = NULL; |
// Create our request. Note that NSDictionary is toll-free bridged to CFDictionary, so |
// we can use an NSDictionary as our request. Also, if the "Force failure" checkbox is |
// checked, we use the wrong command ID to deliberately cause an "unknown command" error |
// so that we can test that code path. |
if ( [forceFailure state] == 0 ) { |
request = [NSDictionary dictionaryWithObjectsAndKeys:@kSampleGetVersionCommand, @kBASCommandKey, nil]; |
} else { |
request = [NSDictionary dictionaryWithObjectsAndKeys:@"Utter Gibberish", @kBASCommandKey, nil]; |
} |
assert(request != NULL); |
bundleID = [[NSBundle mainBundle] bundleIdentifier]; |
assert(bundleID != NULL); |
// Execute it. |
err = BASExecuteRequestInHelperTool( |
gAuth, |
kSampleCommandSet, |
(CFStringRef) bundleID, |
(CFDictionaryRef) request, |
&response |
); |
// If the above went OK, it means that the IPC to the helper tool worked. We |
// now have to check the response dictionary to see if the command's execution |
// within the helper tool was successful. For the GetVersion command, this |
// is unlikely to ever fail, but we should still check. |
if (err == noErr) { |
err = BASGetErrorFromResponse(response); |
} |
// Log our results. |
if (err == noErr) { |
[textView insertText: |
[NSString stringWithFormat:@"version = %@\n", |
[(NSDictionary *)response objectForKey:@kSampleGetVersionResponse] |
] |
]; |
} else { |
[textView insertText: |
[NSString stringWithFormat:@"Failed with error %ld.\n", (long) err] |
]; |
} |
if (response != NULL) { |
CFRelease(response); |
} |
} |
- (IBAction)doGetUIDs:(id)sender |
// Called when the user clicks the "GetUIDs" button. This is a typical BetterAuthorizationSample |
// privileged operation implemented in Objective-C. |
{ |
OSStatus err; |
BASFailCode failCode; |
NSString * bundleID; |
NSDictionary * request; |
CFDictionaryRef response; |
response = NULL; |
// Create our request. Note that NSDictionary is toll-free bridged to CFDictionary, so |
// we can use an NSDictionary as our request. |
request = [NSDictionary dictionaryWithObjectsAndKeys:@kSampleGetUIDsCommand, @kBASCommandKey, nil]; |
assert(request != NULL); |
bundleID = [[NSBundle mainBundle] bundleIdentifier]; |
assert(bundleID != NULL); |
// Execute it. |
err = BASExecuteRequestInHelperTool( |
gAuth, |
kSampleCommandSet, |
(CFStringRef) bundleID, |
(CFDictionaryRef) request, |
&response |
); |
// If it failed, try to recover. |
if ( (err != noErr) && (err != userCanceledErr) ) { |
int alertResult; |
failCode = BASDiagnoseFailure(gAuth, (CFStringRef) bundleID); |
// At this point we tell the user that something has gone wrong and that we need |
// to authorize in order to fix it. Ideally we'd use failCode to describe the type of |
// error to the user. |
alertResult = NSRunAlertPanel(@"Needs Install", @"BAS needs to install", @"Install", @"Cancel", NULL); |
if ( alertResult == NSAlertDefaultReturn ) { |
// Try to fix things. |
err = BASFixFailure(gAuth, (CFStringRef) bundleID, CFSTR("InstallTool"), CFSTR("HelperTool"), failCode); |
// If the fix went OK, retry the request. |
if (err == noErr) { |
err = BASExecuteRequestInHelperTool( |
gAuth, |
kSampleCommandSet, |
(CFStringRef) bundleID, |
(CFDictionaryRef) request, |
&response |
); |
} |
} else { |
err = userCanceledErr; |
} |
} |
// If all of the above went OK, it means that the IPC to the helper tool worked. We |
// now have to check the response dictionary to see if the command's execution within |
// the helper tool was successful. |
if (err == noErr) { |
err = BASGetErrorFromResponse(response); |
} |
// Log our results. |
if (err == noErr) { |
[textView insertText: |
[NSString stringWithFormat:@"RUID = %@, EUID=%@\n", |
[(NSDictionary *)response objectForKey:@kSampleGetUIDsResponseRUID], |
[(NSDictionary *)response objectForKey:@kSampleGetUIDsResponseEUID] |
] |
]; |
} else { |
[textView insertText: |
[NSString stringWithFormat:@"Failed with error %ld.\n", (long) err] |
]; |
} |
if (response != NULL) { |
CFRelease(response); |
} |
} |
static OSStatus DoLowNumberedPorts( |
Boolean forceFailure, |
int fdArray[] |
) |
// This code shows how to do a typical BetterAuthorizationSample privileged operation |
// in straight C. In this case, it does the low-numbered ports operation, which |
// returns three file descriptors that are bound to low-numbered TCP ports. |
{ |
OSStatus err; |
Boolean success; |
CFBundleRef bundle; |
CFStringRef bundleID; |
CFIndex keyCount; |
CFStringRef keys[2]; |
CFTypeRef values[2]; |
CFDictionaryRef request; |
CFDictionaryRef response; |
BASFailCode failCode; |
// Pre-conditions |
assert(fdArray != NULL); |
assert(fdArray[0] == -1); |
assert(fdArray[1] == -1); |
assert(fdArray[2] == -1); |
// Get our bundle information. |
bundle = CFBundleGetMainBundle(); |
assert(bundle != NULL); |
bundleID = CFBundleGetIdentifier(bundle); |
assert(bundleID != NULL); |
// Create the request. The request always contains the kBASCommandKey that |
// describes the command to do. It also, optionally, contains the |
// kSampleLowNumberedPortsForceFailure key that tells the tool to always return |
// an error. The purpose of this is to test our error handling path (do we leak |
// descriptors, for example). |
keyCount = 0; |
keys[keyCount] = CFSTR(kBASCommandKey); |
values[keyCount] = CFSTR(kSampleLowNumberedPortsCommand); |
keyCount += 1; |
if (forceFailure) { |
keys[keyCount] = CFSTR(kSampleLowNumberedPortsForceFailure); |
values[keyCount] = kCFBooleanTrue; |
keyCount += 1; |
} |
request = CFDictionaryCreate( |
NULL, |
(const void **) keys, |
(const void **) values, |
keyCount, |
&kCFTypeDictionaryKeyCallBacks, |
&kCFTypeDictionaryValueCallBacks |
); |
assert(request != NULL); |
response = NULL; |
// Execute it. |
err = BASExecuteRequestInHelperTool( |
gAuth, |
kSampleCommandSet, |
bundleID, |
request, |
&response |
); |
// If it failed, try to recover. |
if ( (err != noErr) && (err != userCanceledErr) ) { |
int alertResult; |
failCode = BASDiagnoseFailure(gAuth, bundleID); |
// At this point we tell the user that something has gone wrong and that we need |
// to authorize in order to fix it. Ideally we'd use failCode to describe the type of |
// error to the user. |
alertResult = NSRunAlertPanel(@"Needs Install", @"BAS needs to install", @"Install", @"Cancel", NULL); |
if ( alertResult == NSAlertDefaultReturn ) { |
// Try to fix things. |
err = BASFixFailure(gAuth, (CFStringRef) bundleID, CFSTR("InstallTool"), CFSTR("HelperTool"), failCode); |
// If the fix went OK, retry the request. |
if (err == noErr) { |
err = BASExecuteRequestInHelperTool( |
gAuth, |
kSampleCommandSet, |
bundleID, |
request, |
&response |
); |
} |
} else { |
err = userCanceledErr; |
} |
} |
// If all of the above went OK, it means that the IPC to the helper tool worked. We |
// now have to check the response dictionary to see if the command's execution within |
// the helper tool was successful. |
if (err == noErr) { |
err = BASGetErrorFromResponse(response); |
} |
// Extract the descriptors from the response and copy them out to our caller. |
if (err == noErr) { |
CFArrayRef descArray; |
CFIndex arrayIndex; |
CFIndex arrayCount; |
CFNumberRef thisNum; |
descArray = (CFArrayRef) CFDictionaryGetValue(response, CFSTR(kBASDescriptorArrayKey)); |
assert( descArray != NULL ); |
assert( CFGetTypeID(descArray) == CFArrayGetTypeID() ); |
arrayCount = CFArrayGetCount(descArray); |
assert(arrayCount == kNumberOfLowNumberedPorts); |
for (arrayIndex = 0; arrayIndex < kNumberOfLowNumberedPorts; arrayIndex++) { |
thisNum = CFArrayGetValueAtIndex(descArray, arrayIndex); |
assert(thisNum != NULL); |
assert( CFGetTypeID(thisNum) == CFNumberGetTypeID() ); |
success = CFNumberGetValue(thisNum, kCFNumberIntType, &fdArray[arrayIndex]); |
assert(success); |
} |
} |
if (response != NULL) { |
CFRelease(response); |
} |
assert( (err == noErr) == (fdArray[0] >= 0) ); |
assert( (err == noErr) == (fdArray[1] >= 0) ); |
assert( (err == noErr) == (fdArray[2] >= 0) ); |
return err; |
} |
- (IBAction)doLowNumberedPorts:(id)sender |
// Called when the user clicks the "LowNumberedPorts" button. It calls the helper |
// tool to open three low-numbered ports (which can't otherwise by opened by |
// non-privileged code). |
// |
// The code is divided into two. This core code is DoLowNumberedPorts; its goal |
// is to show how to use the BetterAuthorizationSample library from plain C. This |
// routine is an Objective-C wrapper that glues DoLowNumberedPorts into the rest |
// of the Cocoa application. |
{ |
OSStatus err; |
int junk; |
int descriptors[kNumberOfLowNumberedPorts] = { -1, -1, -1 }; |
uint16_t ports[kNumberOfLowNumberedPorts]; |
int portIndex; |
// Call the C code to do the real work. |
err = DoLowNumberedPorts( [forceFailure state] != 0, descriptors ); |
// Log our results. |
if (err == noErr) { |
// Get the port numbers for each descriptor. |
for (portIndex = 0; portIndex < kNumberOfLowNumberedPorts; portIndex++) { |
int sockErr; |
struct sockaddr_in boundAddr; |
socklen_t boundAddrLen; |
memset(&boundAddr, 0, sizeof(boundAddr)); |
boundAddrLen = sizeof(boundAddr); |
sockErr = getsockname(descriptors[portIndex], (struct sockaddr *) &boundAddr, &boundAddrLen); |
assert(sockErr == 0); |
assert(boundAddrLen == sizeof(boundAddr)); |
ports[portIndex] = ntohs(boundAddr.sin_port); |
} |
// Log it. |
[textView insertText: |
[NSString stringWithFormat:@"ports[0] = %u, port[1] = %u, port[2] = %u\n", |
(unsigned int) ports[0], |
(unsigned int) ports[1], |
(unsigned int) ports[2] |
] |
]; |
// Close the descriptors. |
for (portIndex = 0; portIndex < kNumberOfLowNumberedPorts; portIndex++) { |
junk = close(descriptors[portIndex]); |
assert(junk == 0); |
} |
} else { |
[textView insertText: |
[NSString stringWithFormat:@"Failed with error %ld.\n", (long) err] |
]; |
} |
} |
- (IBAction)destroyRights:(id)sender |
// Called when the user chooses the "Destroy Rights" menu item. This is just a testing |
// convenience; it allows you to destroy the credentials that are stored in the cache |
// associated with gAuth, so you can force the system to ask you for a password again. |
// However, this isn't as convenient as you might think because the credentials might |
// be cached globally. See DTS Q&A 1277 "Security Credentials" for the gory details. |
// |
// <http://developer.apple.com/qa/qa2001/qa1277.html> |
{ |
OSStatus junk; |
// Free gAuth, destroying any credentials that it has acquired along the way. |
junk = AuthorizationFree(gAuth, kAuthorizationFlagDestroyRights); |
assert(junk == noErr); |
gAuth = NULL; |
// Recreate it from scratch. |
junk = AuthorizationCreate(NULL, NULL, kAuthorizationFlagDefaults, &gAuth); |
assert(junk == noErr); |
assert( (junk == noErr) == (gAuth != NULL) ); |
} |
@end |
int main(int argc, char *argv[]) |
{ |
OSStatus junk; |
// Create the AuthorizationRef that we'll use through this application. We ignore |
// any error from this. A failure from AuthorizationCreate is very unusual, and if it |
// happens there's no way to recover; Authorization Services just won't work. |
junk = AuthorizationCreate(NULL, NULL, kAuthorizationFlagDefaults, &gAuth); |
assert(junk == noErr); |
assert( (junk == noErr) == (gAuth != NULL) ); |
// For each of our commands, check to see if a right specification exists and, if not, |
// create it. |
// |
// The last parameter is the name of a ".strings" file that contains the localised prompts |
// for any custom rights that we use. |
BASSetDefaultRules( |
gAuth, |
kSampleCommandSet, |
CFBundleGetIdentifier(CFBundleGetMainBundle()), |
CFSTR("SampleAuthorizationPrompts") |
); |
// And now, the miracle that is Cocoa... |
return NSApplicationMain(argc, (const char **) argv); |
} |
Copyright © 2007 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2007-11-27