[Keychain-commit] SF.net SVN: keychain: [447] trunk/Frameworks/Keychain/Keychain
Status: Abandoned
Brought to you by:
wadetregaskis
From: <wad...@us...> - 2007-12-13 07:20:23
|
Revision: 447 http://keychain.svn.sourceforge.net/keychain/?rev=447&view=rev Author: wadetregaskis Date: 2007-12-12 23:20:22 -0800 (Wed, 12 Dec 2007) Log Message: ----------- * Added -[AccessControlList addApplication:], -[AccessControlList removeApplication:], -[AccessControlList removeApplicationIfPresent:] & -[AccessControlList allowsAnyApplication]. * Added internal methods +[AccessControlList _trustedApplicationFromObject:] & +[AccessControlList _removeApplication:explicitOnly]. Modified Paths: -------------- trunk/Frameworks/Keychain/Keychain/AccessControlList.h trunk/Frameworks/Keychain/Keychain/AccessControlList.m Modified: trunk/Frameworks/Keychain/Keychain/AccessControlList.h =================================================================== --- trunk/Frameworks/Keychain/Keychain/AccessControlList.h 2007-12-13 07:17:36 UTC (rev 446) +++ trunk/Frameworks/Keychain/Keychain/AccessControlList.h 2007-12-13 07:20:22 UTC (rev 447) @@ -23,8 +23,14 @@ /*! @class AccessControlList @abstract Defines a set of authorizations for a set of applications - @discussion An AccessControlList contains a list of authorizations, of various pre-defined types, and a list of TrustedApplication's to which these authorizations apply. AccessControlList's are usually grouped together, as appropriate, under an Access instance. */ + @discussion An AccessControlList contains a list of authorizations, of various pre-defined types, and a list of TrustedApplication's to which these authorizations apply. AccessControlList's are usually grouped together, as appropriate, under an Access instance. + + <b>Typical ACLs</b> + + Typically each password in a keychain has three ACLs in its Access initially. One provides encrypt authorisation to any and all applications. A second provides permission to change the ACL, but specifies no applications by default. A third allows for decryption, key derivation, clear & wrapped export, MAC generation and signing. In a nutshell this provides read access to the keychain item's content. + The explanation for this is a little odd but fairly straightforward - every password in a keychain is composed of two parts - one is the actual data itself, encrypted, and the other is the key for that data. The Security framework, and the Keychain framework (typically), represent this pair of items as a single item, a KeychainItem. But the access permissions are with respect to the key. So, having encryption permission means you can encrypt data and thus modify the content. Conversely, having decrypt permission means you can access the password. Strictly speaking decrypt permission is all you need to do this. However, it is wise to follow the convention and also add the additional permissions when creating or modifying "read access" ACLs. */ + @interface AccessControlList : NSCachedObject { SecACLRef _ACL; int _error; @@ -79,25 +85,43 @@ /*! @method setApplications: @abstract Sets the list of trusted applications the receiver governs. If "applications" is nil, all applications will be trusted. If it is an empty array, no applications will be trusted. - @param applications An NSArray containing any mix of TrustedApplications, SecTrustedApplicationRefs or NSStrings (specifying a path to an application). + @param applications An NSArray containing any mix of TrustedApplications, SecTrustedApplicationRefs or NSStrings (specifying a path to an application). May be nil, in which case any and all applications may use the receiver's permissions. @result Returns YES if the list was successfully set, NO otherwise. You can retrieve an error code using lastError. */ - (BOOL)setApplications:(NSArray*)applications; -///*! @method addApplication: -// @abstract Adds the given application to the list of trusted applications the receiver governs. -// @param application An instance of a TrustedApplications, a SecTrustedApplicationRef or an NSString (specifying a path to an application). Should not be nil. -// @result Returns YES if the application was successfully added, NO otherwise (including if 'application' is nil). You can retrieve an error code using lastError. */ -// -//- (BOOL)addApplication:(id)application; +/*! @method addApplication: + @abstract Adds the given application to the list of trusted applications the receiver governs. + @discussion If the receiver applies to all applications (i.e. its applications list is not nil but empty) then this has no effect, as the given application already has access, implicitly. Otherwise, the given application is added to the list. This may require user authorisation, via a modal dialog or similar. If the user refuses access NO will be returned. + @param application An instance of a TrustedApplications, a SecTrustedApplicationRef or an NSString (specifying a path to an application). Should not be nil. + @result Returns YES if the application was successfully added, NO otherwise (including if 'application' is nil). You can retrieve an error code using lastError. */ -///*! @method removeApplication: -// @abstract Removes the given application from the list of trusted applications the receiver governs. If the given application is not in the receiver's list, nothing is changed and this method returns YES. -// @param application An instance of a TrustedApplications, a SecTrustedApplicationRef or an NSString (specifying a path to an application). Should not be nil. -// @result Returns YES if the application was successfully removed (or wasn't a member of the receiver to begin with), NO otherwise. If the receiver is set to trust any application (i.e. its application list is nil), this method returns NO (XXX: and what's lastError set to?). You can retrieve an error code using lastError. */ -// -//- (BOOL)removeApplication:(id)application; +- (BOOL)addApplication:(id)application; +/*! @method removeApplication: + @abstract Removes the given application from the list of trusted applications the receiver governs. + @discussion The behaviour of this method is a little complex, so read the following carefully: + + 1) If the receiver's application list is nil - meaning all applications are allowed by default - this method adds an empty list. <i>This means that not only is the given application no longer able to access the item, but no applications are</i>. + 2) If the receiver's application list is not nil, the given application is removed (if present). + + Often you don't actually intend the behaviour of point 1, above. In that case, use @link removeApplicationIfPresent: removeApplicationIfPresent:@/link, which will only perform point 2, if anything. + @param application An instance of a TrustedApplications, a SecTrustedApplicationRef or an NSString (specifying a path to an application). Should not be nil. + @result Returns YES if the given application is no longer given implicit or explicit access to the item, NO otherwise. You can retrieve an error code using lastError. */ + +- (BOOL)removeApplication:(id)application; + +/*! @method removeApplicationIfPresent: + @abstract Removes the given application, if present, from the list of trusted applications the receiver governs. + @discussion 1) If the receiver's application list is not nil, the given application is removed from the list, if present, and YES is returned. + 2) If the receiver's application list is nil - meaning all applications are allowed by default - then <i>this method does nothing</i>, but returns YES. + + i.e. this method does not remove implicit access, only explicit. If your intention is to completely remove access by the given application (potentially at the expense of all other applications), use @link removeApplication: removeApplication:@/link. + @param application An instance of a TrustedApplications, a SecTrustedApplicationRef or an NSString (specifying a path to an application). Should not be nil. + @result Returns YES if the given application is no longer given explicit access to the item, NO otherwise. You can retrieve an error code using lastError. */ + +- (BOOL)removeApplicationIfPresent:(id)application; + /*! @method setName: @abstract Sets the name of the receiver to the value given. @param name The new name to be given to the receiver. May be an empty string, but should not be nil. @@ -107,7 +131,7 @@ /*! @method setRequiresPassphrase: @abstract Sets whether or not the receiver requires the user's authorization to be used. - @discussion If this is set to YES, the user must provided their authorization (by entering their keychain password) in order to the receiver's authorizations to be applied. + @discussion If this is set to YES, the user must provided their authorization (by entering their keychain password) in order to the receiver's authorizations to be applied. This applies even if your application is in the ACL. @param reqPass Whether or not the user's authorization is required. @result Returns YES if the setting was applied successfully, NO otherwise. You can retrieve an error code using lastError. */ @@ -115,11 +139,18 @@ /*! @method applications @abstract Returns the list of trusted applications the receiver governs. - @discussion The returned list is not mutable. If you wish to change it, you can use either the addApplication:/removeApplication: methods, or create a mutable copy, change it as desired, and then apply it using setApplications:. - @result An NSArray containing 0 or more TrustedApplication's. If empty, no applications are trusted. If nil is returned, check if an error occurred using @link lastError lastError@/link - if it did not, the receiver applies to any and all applications. */ // TODO: this API sucks; using nil as a return with two possible meanings. Need to find a better way - return NSNull instead of nil for the trusts all applications case? + @discussion The returned list is not mutable. If you wish to change it, you can use either the @link addApplication: addApplication:@/link/@link removeApplication: removeApplication:@/link methods, or create a mutable copy, change it as desired, and then apply it using @link setApplications: setApplications:@/link. + @result An NSArray containing 0 or more TrustedApplication's. If empty (but not nil), no applications are trusted. If nil is returned, check if an error occurred using @link lastError lastError@/link - if it did not, the receiver applies to any and all applications. You may also wish to use the convenience method @link allowsAnyApplication allowsAnyApplication@/link. */ - (NSArray*)applications; +/*! @method allowsAnyApplication + @abstract Returns whether or not any and all applications may use the receiver's permissions. + @discussion This is a convenience method over @link applications applications@/link. + @result Returns YES if the receiver allows any application to use its permissions, NO otherwise. */ + +- (BOOL)allowsAnyApplication; + /*! @method name @abstract Returns the name of the receiver. @discussion An AccessControlList's name is not inherantly a unique identifier of that particular instance. Be aware of this, and avoid making such dangerous assumptions. Modified: trunk/Frameworks/Keychain/Keychain/AccessControlList.m =================================================================== --- trunk/Frameworks/Keychain/Keychain/AccessControlList.m 2007-12-13 07:17:36 UTC (rev 446) +++ trunk/Frameworks/Keychain/Keychain/AccessControlList.m 2007-12-13 07:20:22 UTC (rev 447) @@ -19,9 +19,12 @@ #import <Keychain/TrustedApplication.h> +#import <Security/Security.h> + @interface AccessControlList (Internal) ++ (TrustedApplication*)_trustedApplicationFromObject:(id)object; + (NSArray*)_arrayOfSecTrustedApplicationRefsFromArray:(NSArray*)trustedApplications; + (NSArray*)_arrayOfTrustedApplicationsFromArray:(NSArray*)trustedApplications; @@ -116,6 +119,19 @@ return nil; } ++ (TrustedApplication*)_trustedApplicationFromObject:(id)object { + if ([object isKindOfClass:[TrustedApplication class]]) { + return (TrustedApplication*)object; + } else if ([object isKindOfClass:[NSString class]]) { + return [TrustedApplication trustedApplicationWithPath:object]; + } else if (CFGetTypeID(object) == SecTrustedApplicationGetTypeID()) { + return [TrustedApplication trustedApplicationWithTrustedApplicationRef:(SecTrustedApplicationRef)object]; + } else { + PDEBUG(@"Don't know how to convert object of class %@ (0x%x) to TrustedApplication.\n", [object className], object); + return nil; + } +} + + (NSArray*)_arrayOfSecTrustedApplicationRefsFromArray:(NSArray*)trustedApplications { NSMutableArray *result = nil; @@ -224,75 +240,92 @@ return (noErr == _error); } -//- (BOOL)addApplication:(id)application { -// NSArray *currentApplications = [self applications]; -// OSStatus err = [self lastError]; -// -// if (noErr == err) { -// if (nil == currentApplications) { -// // We already trust everything; just return YES. -// return YES; -// } else { -// /* // XXX: Do we need to check if the given app is already in the list? Hopefully the Security framework will handle this gracefully for us. -// -// TrustedApplication *newApp = nil; -// -// if ([application isKindOfClass:[TrustedApplication class]]) { -// newApp = application; -// } else if ([application isKindOfClass:[NSString class]]) { -// newApp = [TrustedApplication trustedApplicationWithPath:application]; -// } else if (CFGetTypeID(application) == SecTrustedApplicationGetTypeID()) { -// newApp = [TrustedApplication trustedApplicationWithTrustedApplicationRef:(SecTrustedApplicationRef)application]; -// } else { -// PSYSLOG(LOG_ERROR, @"Unknown class of object (%p) given as the 'application' - has class %@.", application, [application className]); -// } -// -// if (nil != newApp) { */ -// -// return [self setApplications:[currentApplications arrayByAddingObject:application]]; -// } -// } else { -// return NO; -// } -//} +- (BOOL)addApplication:(id)application { + if (nil != application) { + NSArray *currentApplications = [self applications]; + OSStatus err = [self lastError]; + + if (noErr == err) { + if (nil == currentApplications) { + // We already trust everything; just return YES. + return YES; + } else { + TrustedApplication *newApp = [[self class] _trustedApplicationFromObject:application]; + + if (nil != newApp) { + if ([currentApplications containsObject:newApp]) { + return YES; + } else { + return [self setApplications:[currentApplications arrayByAddingObject:newApp]]; + } + } else { + _error = errSecInvalidItemRef; + return NO; + } + } + } else { + return NO; + } + } else { + PDEBUG(@"'application' is nil.\n"); + _error = errSecInvalidItemRef; + return NO; + } +} -//- (BOOL)removeApplication:(id)application { -// if ( -// NSArray *currentApplications = [self applications]; -// OSStatus err = [self lastError]; -// -// if (noErr = err) { -// if (nil == currentApplications) { -// // We currently trust everything implicitly. Since we can't just magically construct a list of every application ever in existence less the one given, we fail here. -// _error = errSecACLNotSimple; -// return NO; -// } else { -// TrustedApplication *targetApp = nil; -// -// if ([application isKindOfClass:[TrustedApplication class]]) { -// targetApp = application; -// } else if ([application isKindOfClass:[NSString class]]) { -// targetApp = [TrustedApplication trustedApplicationWithPath:application]; -// } else if (CFGetTypeID(application) == SecTrustedApplicationGetTypeID()) { -// targetApp = [TrustedApplication trustedApplicationWithTrustedApplicationRef:(SecTrustedApplicationRef)application]; -// } -// -// if (nil != targetApp) { -// NSMutableArray *newApplications = [currentApplications mutableCopy]; -// -// [currentApplications removeObject:targetApp]; -// -// return [self setApplications:[newApplications autorelease]]; -// } else { -// PSYSLOG(LOG_ERROR, @"Unable to convert given application (%p, class %@) to a TrustedApplication.", application, [application className]); -// return NO; -// } -// } -// } else { -// return NO; -// } -//} +- (BOOL)_removeApplication:(id)application explicitOnly:(BOOL)explicitOnly { + if (nil != application) { + NSArray *currentApplications = [self applications]; + OSStatus err = [self lastError]; + + if (noErr == err) { + if (nil == currentApplications) { + // We trust all applications implicitly... + + if (explicitOnly) { + // ...and this method is only expected to remove *explicit* access, not the implicit access the missing list implies. So, we do nothing and pretend we were successful. + return YES; + } else { + // ...but we're being told to remove even implicit access such as this. So, we have to disallow access by *all* applications... brutal, but hey, we document this behaviour, so I can only pray whoever's calling us knows what they're doing. + return [self setApplications:[NSArray array]]; + } + } else { + TrustedApplication *targetApp = [[self class] _trustedApplicationFromObject:application]; + + if (nil != targetApp) { + if ([currentApplications containsObject:targetApp]) { + NSMutableArray *newApplications = [currentApplications mutableCopy]; + + [newApplications removeObject:targetApp]; + + return [self setApplications:[newApplications autorelease]]; + } else { + // The given app's not in our application list anyway. + return YES; + } + } else { + _error = errSecInvalidItemRef; + return NO; + } + } + } else { + return NO; + } + } else { + PDEBUG(@"'application' is nil.\n"); + _error = errSecInvalidItemRef; + return NO; + } +} +- (BOOL)removeApplication:(id)application { + return [self _removeApplication:application explicitOnly:NO]; +} + +- (BOOL)removeApplicationIfPresent:(id)application { + return [self _removeApplication:application explicitOnly:YES]; +} + - (BOOL)setName:(NSString*)name { CFArrayRef appList = NULL; CFStringRef desc = NULL; @@ -388,6 +421,10 @@ return result; } +- (BOOL)allowsAnyApplication { + return ((nil != [self applications]) && (noErr == [self lastError])); +} + - (NSString*)name { CFArrayRef appList = NULL; CFStringRef desc = NULL; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |