Apple एक और तरीका प्रस्तावित करता है कि कैसे यह प्रमाणित किया जाए कि कनेक्टिंग प्रक्रिया के पास एक एक्सपोज़्ड XPC मेथड को कॉल करने की अनुमति है।
जब एक एप्लिकेशन को एक विशेषाधिकार प्राप्त उपयोगकर्ता के रूप में क्रियाएँ निष्पादित करने की आवश्यकता होती है, तो यह आमतौर पर विशेषाधिकार प्राप्त उपयोगकर्ता के रूप में एप्लिकेशन चलाने के बजाय एक हेल्पर टूल को रूट के रूप में XPC सेवा के रूप में स्थापित करता है, जिसे एप्लिकेशन से उन क्रियाओं को करने के लिए कॉल किया जा सकता है। हालाँकि, सेवा को कॉल करने वाले एप्लिकेशन के पास पर्याप्त प्राधिकरण होना चाहिए।
ShouldAcceptNewConnection हमेशा YES
एक उदाहरण EvenBetterAuthorizationSample में पाया जा सकता है। App/AppDelegate.m में यह HelperTool से कनेक्ट करने की कोशिश करता है। और HelperTool/HelperTool.m में फ़ंक्शन shouldAcceptNewConnectionपहले बताए गए किसी भी आवश्यकताओं की जांच नहीं करेगा। यह हमेशा YES लौटाएगा:
- (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;
}
For more information about how to properly configure this check:
Application rights
हालांकि, जब HelperTool से एक विधि को कॉल किया जाता है, तो कुछ अधिकार प्राधिकरण हो रहा है।
App/AppDelegate.m से applicationDidFinishLaunching फ़ंक्शन ऐप के शुरू होने के बाद एक खाली प्राधिकरण संदर्भ बनाएगा। यह हमेशा काम करना चाहिए।
फिर, यह उस प्राधिकरण संदर्भ में कुछ अधिकार जोड़ने की कोशिश करेगा setupAuthorizationRights को कॉल करके:
- (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];
}
फंक्शन setupAuthorizationRights जो Common/Common.m से है, एप्लिकेशन के अधिकारों को ऑथ डेटाबेस /var/db/auth.db में स्टोर करेगा। ध्यान दें कि यह केवल उन अधिकारों को जोड़ेगा जो अभी तक डेटाबेस में नहीं हैं:
+ (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.
}
}];
}
फंक्शन enumerateRightsUsingBlock वह है जिसका उपयोग एप्लिकेशनों की अनुमतियों को प्राप्त करने के लिए किया जाता है, जो 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);
}];
}
यह मतलब है कि इस प्रक्रिया के अंत में, commandInfo के अंदर घोषित अनुमतियाँ /var/db/auth.db में संग्रहीत की जाएँगी। ध्यान दें कि वहाँ आप प्रत्येक विधि के लिए पा सकते हैं जो प्रमाणीकरण की आवश्यकता होगी, अनुमति नाम और kCommandKeyAuthRightDefault। बाद वाला यह संकेत करता है कि यह अधिकार कौन प्राप्त कर सकता है।
वर्तमान उपयोगकर्ता को एक व्यवस्थापक होना चाहिए (व्यवस्थापक समूह के अंदर)
kAuthorizationRuleAuthenticateAsSessionUser
authenticate-session-owner
उपयोगकर्ता से प्रमाणीकरण करने के लिए कहें।
kAuthorizationRuleAuthenticateAsAdmin
authenticate-admin
उपयोगकर्ता से प्रमाणीकरण करने के लिए कहें। उसे एक व्यवस्थापक होना चाहिए (व्यवस्थापक समूह के अंदर)
kAuthorizationRightRule
rule
नियम निर्दिष्ट करें
kAuthorizationComment
comment
अधिकार पर कुछ अतिरिक्त टिप्पणियाँ निर्दिष्ट करें
अधिकारों की सत्यापन
HelperTool/HelperTool.m में फ़ंक्शन readLicenseKeyAuthorization यह जांचता है कि क्या कॉलर को ऐसी विधि को कार्यान्वित करने के लिए अधिकृत किया गया है, फ़ंक्शन checkAuthorization को कॉल करके। यह फ़ंक्शन यह जांचेगा कि कॉलिंग प्रक्रिया द्वारा भेजा गया authDataसही प्रारूप में है और फिर यह जांचेगा कि विशिष्ट विधि को कॉल करने के लिए क्या आवश्यक है। यदि सब कुछ ठीक है तो वापसी errornil होगी:
- (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;
}
ध्यान दें कि उस विधि को कॉल करने के लिए आवश्यकताओं की जांच करने के लिए फ़ंक्शन authorizationRightForCommand केवल पहले से टिप्पणी किए गए ऑब्जेक्ट commandInfo की जांच करेगा। फिर, यह AuthorizationCopyRights को कॉल करेगा यह जांचने के लिए कि क्या इसके पास अधिकार हैं फ़ंक्शन को कॉल करने के लिए (ध्यान दें कि फ्लैग उपयोगकर्ता के साथ इंटरैक्शन की अनुमति देते हैं)।
इस मामले में, फ़ंक्शन readLicenseKeyAuthorization को कॉल करने के लिए kCommandKeyAuthRightDefault को @kAuthorizationRuleClassAllow पर परिभाषित किया गया है। इसलिए कोई भी इसे कॉल कर सकता है।
DB जानकारी
यह उल्लेख किया गया था कि यह जानकारी /var/db/auth.db में संग्रहीत है। आप सभी संग्रहीत नियमों को सूचीबद्ध कर सकते हैं:
You can find all the permissions configurationsin here, but the combinations that won't require user interaction would be:
'authenticate-user': 'false'
यह सबसे सीधा कुंजी है। यदि इसे false पर सेट किया गया है, तो यह निर्दिष्ट करता है कि एक उपयोगकर्ता को इस अधिकार को प्राप्त करने के लिए प्रमाणीकरण प्रदान करने की आवश्यकता नहीं है।
इसका उपयोग नीचे दिए गए 2 में से एक के साथ या उपयोगकर्ता को संबंधित समूह को इंगित करने के लिए किया जाता है।
'allow-root': 'true'
यदि एक उपयोगकर्ता रूट उपयोगकर्ता के रूप में कार्य कर रहा है (जिसके पास उच्च अनुमतियाँ हैं), और यह कुंजी true पर सेट है, तो रूट उपयोगकर्ता संभावित रूप से बिना किसी अतिरिक्त प्रमाणीकरण के इस अधिकार को प्राप्त कर सकता है। हालाँकि, आमतौर पर, रूट उपयोगकर्ता स्थिति प्राप्त करने के लिए पहले से ही प्रमाणीकरण की आवश्यकता होती है, इसलिए यह अधिकांश उपयोगकर्ताओं के लिए "कोई प्रमाणीकरण नहीं" परिदृश्य नहीं है।
'session-owner': 'true'
यदि इसे true पर सेट किया गया है, तो सत्र का मालिक (वर्तमान में लॉग इन किया हुआ उपयोगकर्ता) स्वचालित रूप से इस अधिकार को प्राप्त करेगा। यदि उपयोगकर्ता पहले से ही लॉग इन है, तो यह अतिरिक्त प्रमाणीकरण को बायपास कर सकता है।
'shared': 'true'
यह कुंजी प्रमाणीकरण के बिना अधिकार नहीं देती है। इसके बजाय, यदि इसे true पर सेट किया गया है, तो इसका अर्थ है कि एक बार जब अधिकार को प्रमाणीकरण किया गया है, तो इसे कई प्रक्रियाओं के बीच साझा किया जा सकता है बिना प्रत्येक को फिर से प्रमाणीकरण की आवश्यकता के। लेकिन अधिकार का प्रारंभिक अनुदान अभी भी प्रमाणीकरण की आवश्यकता होगी जब तक कि इसे 'authenticate-user': 'false' जैसी अन्य कुंजियों के साथ संयोजित नहीं किया जाता है।
यदि आप फ़ंक्शन: [HelperTool checkAuthorization:command:] पाते हैं, तो यह संभवतः प्रक्रिया पहले उल्लेखित स्कीमा का उपयोग कर रही है:
यदि यह फ़ंक्शन AuthorizationCreateFromExternalForm, authorizationRightForCommand, AuthorizationCopyRights, AuhtorizationFree जैसे फ़ंक्शंस को कॉल कर रहा है, तो यह EvenBetterAuthorizationSample का उपयोग कर रहा है।
यह देखने के लिए /var/db/auth.db की जांच करें कि क्या कुछ विशेषाधिकार प्राप्त क्रिया को उपयोगकर्ता इंटरैक्शन के बिना कॉल करने के लिए अनुमतियाँ प्राप्त करना संभव है।
Protocol Communication
फिर, आपको XPC सेवा के साथ संचार स्थापित करने के लिए प्रोटोकॉल स्कीमा खोजने की आवश्यकता है।
फ़ंक्शन shouldAcceptNewConnection निर्यातित प्रोटोकॉल को इंगित करता है:
इस मामले में, हमारे पास EvenBetterAuthorizationSample में वही है, इस पंक्ति की जांच करें।
उपयोग किए गए प्रोटोकॉल का नाम जानने पर, आप इसके हेडर परिभाषा को डंप कर सकते हैं: