Apple stel ook 'n ander manier voor om te verifieer of die verbindende proses toestemmings het om die blootgestelde XPC-metode te roep.
Wanneer 'n aansoek handelinge moet uitvoer as 'n bevoorregte gebruiker, in plaas daarvan om die aansoek as 'n bevoorregte gebruiker te hardloop, installeer dit gewoonlik as root 'n HelperTool as 'n XPC-diens wat deur die aansoek geroep kan word om daardie handelinge uit te voer. Die aansoek wat die diens roep, moet egter genoeg gemagtiging hê.
ShouldAcceptNewConnection altyd JA
'n Voorbeeld kan gevind word in EvenBetterAuthorizationSample. In App/AppDelegate.m probeer dit om te verbind met die HelperTool. En in HelperTool/HelperTool.m sal die funksie shouldAcceptNewConnectionnie enige van die vooraf aangeduide vereistes nagaan nie. Dit sal altyd JA teruggee:
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection
// Called by our XPC listener when a new connection comes in. We configure the connection
// with our protocol and ourselves as the main object.
{
assert(listener == self.listener);
#pragma unused(listener)
assert(newConnection != nil);
newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(HelperToolProtocol)];
newConnection.exportedObject = self;
[newConnection resume];
return YES;
}
Vir meer inligting oor hoe om hierdie toets behoorlik te konfigureer:
Daar is egter magtiging wat plaasvind wanneer 'n metode van die HelperTool geroep word.
Die funksie applicationDidFinishLaunching van App/AppDelegate.m sal 'n leë magtigingsverwysing skep nadat die program begin het. Dit behoort altyd te werk.
Daarna sal dit probeer om sekere regte by daardie magtigingsverwysing toe te voeg deur setupAuthorizationRights te roep:
- (void)applicationDidFinishLaunching:(NSNotification *)note
{
[...]
err = AuthorizationCreate(NULL, NULL, 0, &self->_authRef);
if (err == errAuthorizationSuccess) {
err = AuthorizationMakeExternalForm(self->_authRef, &extForm);
}
if (err == errAuthorizationSuccess) {
self.authorization = [[NSData alloc] initWithBytes:&extForm length:sizeof(extForm)];
}
assert(err == errAuthorizationSuccess);
// If we successfully connected to Authorization Services, add definitions for our default
// rights (unless they're already in the database).
if (self->_authRef) {
[Common setupAuthorizationRights:self->_authRef];
}
[self.window makeKeyAndOrderFront:self];
}
Die funksie setupAuthorizationRights van Common/Common.m sal die regte van die aansoek stoor in die outorisasiedatabasis /var/db/auth.db. Let op hoe dit slegs die regte sal byvoeg wat nog nie in die databasis is nie:
+ (void)setupAuthorizationRights:(AuthorizationRef)authRef
// See comment in header.
{
assert(authRef != NULL);
[Common enumerateRightsUsingBlock:^(NSString * authRightName, id authRightDefault, NSString * authRightDesc) {
OSStatus blockErr;
// First get the right. If we get back errAuthorizationDenied that means there's
// no current definition, so we add our default one.
blockErr = AuthorizationRightGet([authRightName UTF8String], NULL);
if (blockErr == errAuthorizationDenied) {
blockErr = AuthorizationRightSet(
authRef, // authRef
[authRightName UTF8String], // rightName
(__bridge CFTypeRef) authRightDefault, // rightDefinition
(__bridge CFStringRef) authRightDesc, // descriptionKey
NULL, // bundle (NULL implies main bundle)
CFSTR("Common") // localeTableName
);
assert(blockErr == errAuthorizationSuccess);
} else {
// A right already exists (err == noErr) or any other error occurs, we
// assume that it has been set up in advance by the system administrator or
// this is the second time we've run. Either way, there's nothing more for
// us to do.
}
}];
}
Die funksie enumerateRightsUsingBlock is die een wat gebruik word om aansoekregte te kry, wat gedefinieer is in commandInfo:
static NSString * kCommandKeyAuthRightName = @"authRightName";
static NSString * kCommandKeyAuthRightDefault = @"authRightDefault";
static NSString * kCommandKeyAuthRightDesc = @"authRightDescription";
+ (NSDictionary *)commandInfo
{
static dispatch_once_t sOnceToken;
static NSDictionary * sCommandInfo;
dispatch_once(&sOnceToken, ^{
sCommandInfo = @{
NSStringFromSelector(@selector(readLicenseKeyAuthorization:withReply:)) : @{
kCommandKeyAuthRightName : @"com.example.apple-samplecode.EBAS.readLicenseKey",
kCommandKeyAuthRightDefault : @kAuthorizationRuleClassAllow,
kCommandKeyAuthRightDesc : NSLocalizedString(
@"EBAS is trying to read its license key.",
@"prompt shown when user is required to authorize to read the license key"
)
},
NSStringFromSelector(@selector(writeLicenseKey:authorization:withReply:)) : @{
kCommandKeyAuthRightName : @"com.example.apple-samplecode.EBAS.writeLicenseKey",
kCommandKeyAuthRightDefault : @kAuthorizationRuleAuthenticateAsAdmin,
kCommandKeyAuthRightDesc : NSLocalizedString(
@"EBAS is trying to write its license key.",
@"prompt shown when user is required to authorize to write the license key"
)
},
NSStringFromSelector(@selector(bindToLowNumberPortAuthorization:withReply:)) : @{
kCommandKeyAuthRightName : @"com.example.apple-samplecode.EBAS.startWebService",
kCommandKeyAuthRightDefault : @kAuthorizationRuleClassAllow,
kCommandKeyAuthRightDesc : NSLocalizedString(
@"EBAS is trying to start its web service.",
@"prompt shown when user is required to authorize to start the web service"
)
}
};
});
return sCommandInfo;
}
+ (NSString *)authorizationRightForCommand:(SEL)command
// See comment in header.
{
return [self commandInfo][NSStringFromSelector(command)][kCommandKeyAuthRightName];
}
+ (void)enumerateRightsUsingBlock:(void (^)(NSString * authRightName, id authRightDefault, NSString * authRightDesc))block
// Calls the supplied block with information about each known authorization right..
{
[self.commandInfo enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
#pragma unused(key)
#pragma unused(stop)
NSDictionary * commandDict;
NSString * authRightName;
id authRightDefault;
NSString * authRightDesc;
// If any of the following asserts fire it's likely that you've got a bug
// in sCommandInfo.
commandDict = (NSDictionary *) obj;
assert([commandDict isKindOfClass:[NSDictionary class]]);
authRightName = [commandDict objectForKey:kCommandKeyAuthRightName];
assert([authRightName isKindOfClass:[NSString class]]);
authRightDefault = [commandDict objectForKey:kCommandKeyAuthRightDefault];
assert(authRightDefault != nil);
authRightDesc = [commandDict objectForKey:kCommandKeyAuthRightDesc];
assert([authRightDesc isKindOfClass:[NSString class]]);
block(authRightName, authRightDefault, authRightDesc);
}];
}
Dit beteken dat aan die einde van hierdie proses, sal die toestemmings wat binne commandInfo verklaar is, gestoor word in /var/db/auth.db. Let daarop dat jy vir elke metode wat verifikasie vereis, die toestemming naam en die kCommandKeyAuthRightDefault kan vind. Die laaste een dui aan wie hierdie reg kan verkry.
Daar is verskillende omvang om aan te dui wie 'n reg kan kry. Sommige van hulle is omskryf in AuthorizationDB.h (jy kan al hulle hier vind), maar as 'n opsomming:
Naam
Waarde
Beskrywing
kAuthorizationRuleClassAllow
allow
Enigiemand
kAuthorizationRuleClassDeny
deny
Niemand
kAuthorizationRuleIsAdmin
is-admin
Huidige gebruiker moet 'n admin wees (binne admin groep)
kAuthorizationRuleAuthenticateAsSessionUser
authenticate-session-owner
Vra gebruiker om te verifieer.
kAuthorizationRuleAuthenticateAsAdmin
authenticate-admin
Vra gebruiker om te verifieer. Hy moet 'n admin wees (binne admin groep)
kAuthorizationRightRule
rule
Spesifiseer reëls
kAuthorizationComment
comment
Spesifiseer ekstra opmerkings oor die reg
Regte Verifikasie
In HelperTool/HelperTool.m kontroleer die funksie readLicenseKeyAuthorization of die oproeper gemagtig is om so 'n metode uit te voer deur die funksie checkAuthorization te roep. Hierdie funksie sal die authData wat deur die oproepende proses gestuur is, nagaan vir 'n korrekte formaat en dan sal dit nagaan wat nodig is om die reg te kry om die spesifieke metode te roep. As alles goed verloop, sal die teruggekeerde foutnil wees:
- (NSError *)checkAuthorization:(NSData *)authData command:(SEL)command
{
[...]
// First check that authData looks reasonable.
error = nil;
if ( (authData == nil) || ([authData length] != sizeof(AuthorizationExternalForm)) ) {
error = [NSError errorWithDomain:NSOSStatusErrorDomain code:paramErr userInfo:nil];
}
// Create an authorization ref from that the external form data contained within.
if (error == nil) {
err = AuthorizationCreateFromExternalForm([authData bytes], &authRef);
// Authorize the right associated with the command.
if (err == errAuthorizationSuccess) {
AuthorizationItem oneRight = { NULL, 0, NULL, 0 };
AuthorizationRights rights = { 1, &oneRight };
oneRight.name = [[Common authorizationRightForCommand:command] UTF8String];
assert(oneRight.name != NULL);
err = AuthorizationCopyRights(
authRef,
&rights,
NULL,
kAuthorizationFlagExtendRights | kAuthorizationFlagInteractionAllowed,
NULL
);
}
if (err != errAuthorizationSuccess) {
error = [NSError errorWithDomain:NSOSStatusErrorDomain code:err userInfo:nil];
}
}
if (authRef != NULL) {
junk = AuthorizationFree(authRef, 0);
assert(junk == errAuthorizationSuccess);
}
return error;
}
Merk op dat om die vereistes te toets om die reg te kry om daardie metode te roep, sal die funksie authorizationRightForCommand net die voorheen kommentaarobjek commandInfo toets. Dan sal dit AuthorizationCopyRights roep om te toets of dit die regte het om die funksie te roep (merk op dat die vlae interaksie met die gebruiker toelaat).
In hierdie geval, om die funksie readLicenseKeyAuthorization te roep, is die kCommandKeyAuthRightDefault omskryf as @kAuthorizationRuleClassAllow. So enigiemand kan dit roep.
DB Inligting
Daar is genoem dat hierdie inligting gestoor word in /var/db/auth.db. Jy kan al die gestoorde reëls lys met:
Jy kan alle toestemmingskonfigurasieshier vind, maar die kombinasies wat nie gebruikerinteraksie vereis nie, sal wees:
'authenticate-user': 'false'
Hierdie is die mees direkte sleutel. Indien ingestel op false, dui dit aan dat 'n gebruiker nie verifikasie hoef te verskaf om hierdie reg te verkry nie.
Dit word gebruik in kombinasie met een van die 2 onderstaande of deur 'n groep aan te dui waar die gebruiker by moet behoort.
'allow-root': 'true'
Indien 'n gebruiker as die root-gebruiker optree (wat verhoogde regte het), en hierdie sleutel op true ingestel is, kan die root-gebruiker moontlik hierdie reg verkry sonder verdere verifikasie. Gewoonlik vereis dit egter reeds verifikasie om 'n root-gebruikerstatus te bereik, dus is dit nie 'n "geen verifikasie" scenario vir die meeste gebruikers nie.
'session-owner': 'true'
Indien ingestel op true, sal die eienaar van die sessie (die tans ingeteken gebruiker) hierdie reg outomaties verkry. Dit kan verdere verifikasie omseil indien die gebruiker reeds ingeteken is.
'shared': 'true'
Hierdie sleutel verleen nie regte sonder verifikasie nie. Indien op true ingestel, beteken dit eerder dat sodra die reg ge-verifieer is, dit tussen verskeie prosesse gedeel kan word sonder dat elkeen weer moet herverifieer nie. Maar die aanvanklike toekenning van die reg sal steeds verifikasie vereis tensy dit gekombineer word met ander sleutels soos 'authenticate-user': 'false'.
Kontroleer of EvenBetterAuthorization gebruik word
As jy die funksie vind: [HelperTool checkAuthorization:command:] is dit waarskynlik dat die proses die voorheen genoemde skema vir autorisasie gebruik:
Indien hierdie funksie funksies soos AuthorizationCreateFromExternalForm, authorizationRightForCommand, AuthorizationCopyRights, AuhtorizationFree aanroep, gebruik dit EvenBetterAuthorizationSample.
Kyk na die /var/db/auth.db om te sien of dit moontlik is om toestemming te kry om sekere bevoorregte aksies uit te voer sonder gebruikerinteraksie.
Protokol Kommunikasie
Daarna moet jy die protokolskema vind om kommunikasie met die XPC-diens te kan vestig.
Die funksie shouldAcceptNewConnection dui die uitgevoerde protokol aan:
In hierdie geval het ons dieselfde as in EvenBetterAuthorizationSample, kontroleer hierdie lyn.
Deur die naam van die gebruikte protokol te ken, is dit moontlik om die kopdefinisie daarvan te dump met: