I am using LaunchAtLoginController for one of my project and in the following method there is always a NSArray leaking memory. As you can see I cast a CFArrayRef to a NSArray (__bridge NSArray*). I thought I could solve it using __bridge_transfer, but the app crashes (EXC_BAD_ACCESS). Is it because one of the items in the array is returned by the method?
- (LSSharedFileListItemRef) findItemWithURL: (NSURL*) wantedURL inFileList: (LSSharedFileListRef) fileList
{
if (wantedURL == NULL || fileList == NULL)
return NULL;
NSArray *listSnapshot = (__bridge NSArray*)(LSSharedFileListCopySnapshot(fileList, NULL));
LSSharedFileListItemRef itemToReturn = NULL;
for (id itemObject in listSnapshot) {
LSSharedFileListItemRef item = (__bridge LSSharedFileListItemRef) itemObject;
UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
CFURLRef currentItemURL = NULL;
LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, NULL);
if (currentItemURL && CFEqual(currentItemURL, (__bridge CFTypeRef)(wantedURL)))
{
CFRelease(currentItemURL);
itemToReturn = item;
} else {
if (currentItemURL)
CFRelease(currentItemURL);
if (item)
CFRelease(item);
}
}
return itemToReturn;
}
This are two methods that call (LSSharedFileListItemRef) findItemWithURL: (NSURL*) wantedURL inFileList: (LSSharedFileListRef) fileList. Although I managed to release the item returned, I can never release the NSArray that, obviously, leaks memory.
- (BOOL) willLaunchAtLogin: (NSURL*) itemURL
{
LSSharedFileListItemRef item = [self findItemWithURL:itemURL inFileList:loginItems];
BOOL willLaunchAtLogin = !!item;
if (item)
CFRelease(item);
return willLaunchAtLogin;
}
- (void) setLaunchAtLogin: (BOOL) enabled forURL: (NSURL*) itemURL
{
LSSharedFileListItemRef appItem = [self findItemWithURL:itemURL inFileList:loginItems];
if (enabled && !appItem) {
LSSharedFileListItemRef itemRef = LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst,
NULL, NULL, (__bridge CFURLRef)itemURL, NULL, NULL);
if (itemRef)
CFRelease(itemRef);
} else if (!enabled && appItem) {
LSSharedFileListItemRemove(loginItems, appItem);
}
if (appItem)
CFRelease(appItem);
}
I tried everything that I could think of to solve this but with no success. What am I missing?
Related
I need two functions one which stores the p12 certificate in iOS Keychain and the other function to retrieve the stored p12 certificate.
-(BOOL)addItem:(NSData*)item forKey:(NSString*)key {
NSMutableDictionary *dict = [self newItemDictionaryForKey:key];
[dict setObject:item forKey:(id)kSecValueData];
OSStatus ossstatus = SecItemAdd((CFDictionaryRef)dict, NULL);
if(errSecSuccess != ossstatus) {
NSLog(#"Unable to add item for key %# %ld request dict:%#",key,ossstatus,dict);
}
return (errSecSuccess == ossstatus);
}
To the above function i am sending the NSData that i get from p12 file.
You have to split this into two tasks:
Converting certificate from p12 data to SecIdentityRef. With this I can help
Store private key and certificate chain in keychain - I'm struggling with this too, but I'm couple steps ahead comparing with you.
To perform a conversion, here is a code I'm using:
- (NSError *)setClientIdentityCertificateFromPKCS12Data: (NSData *)PKCS12Data withPassword: (NSString *)password
{
OSStatus securityError = errSecSuccess;
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { (__bridge CFStringRef)password };
CFDictionaryRef optionsDictionary = NULL;
optionsDictionary = CFDictionaryCreate(
NULL, keys,
values, (password.length!=0 ? 1 : 0),
NULL, NULL);
CFArrayRef items = NULL;
securityError = SecPKCS12Import((__bridge CFDataRef)PKCS12Data,
optionsDictionary,
&items);
if (securityError == 0) {
CFDictionaryRef identityDic = (CFDictionaryRef)CFArrayGetValueAtIndex(items, 0);
SecIdentityRef secIdentity = (SecIdentityRef)CFDictionaryGetValue(identityDic, kSecImportItemIdentity);
CFArrayRef identityCertChain = (CFArrayRef)CFDictionaryGetValue(identityDic, kSecImportItemCertChain);
securityError = [self setClientIdentity: secIdentity withCertificateChain: identityCertChain];
}
if (optionsDictionary) {
CFRelease(optionsDictionary);
}
if (items) {
CFRelease(items);
}
NSError *error = nil;
if (securityError != errSecSuccess)
{
NSDictionary *info = nil;
#if !TARGET_OS_IPHONE
NSString *errorDescription = nil;
errorDescription = (__bridge_transfer NSString *)SecCopyErrorMessageString(securityError, NULL);
if (errorDescription)
{
info = #{ NSLocalizedDescriptionKey:errorDescription };
}
#endif
error = [NSError errorWithDomain: NSOSStatusErrorDomain
code: securityError
userInfo: info];
}
return error;
}
I am trying to get all the startup(login) application of my OSX10. In order to do so I have written the this code (given below):
-(NSMutableArray*)getStartUpApplicaitonPaths{
// Get the LoginItems list.
LSSharedFileListRef loginItemsRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
if (loginItemsRef == nil) return nil;
// Iterate over the LoginItems.
NSArray *loginItems = (__bridge NSArray *)LSSharedFileListCopySnapshot(loginItemsRef, nil);
NSMutableArray* data = [NSMutableArray arrayWithArray:loginItems];
return data;
}
From the above code I am getting an NSMutableArray of __NSCFType objects. When I am trying to get the path by converting a object of the array
NSString* file = [NSString stringWithFormat:#"%#", [startupFiles objectAtIndex:0]];
I am getting the result given below:
BundleBinding [0x103] URL:
file:///Applications/iTunes.app/Contents/MacOS/iTunesHelper.app/
bundle identifier: com.apple.iTunesHelper iTunesHelper
I need to parse the URL of and Identifier from the string given above. Please help.
The objects ar eof type: LSSharedFileListItem which is only documented in the header file.
Here is some code that may help, it will NSLog() all the file names:
NSURL *itemURL = [[NSBundle mainBundle] bundleURL];
CFURLRef URLToToggle = (__bridge CFURLRef)itemURL;
LSSharedFileListRef loginItems = LSSharedFileListCreate(kCFAllocatorDefault, kLSSharedFileListSessionLoginItems, /*options*/ NULL);
if (loginItems) {
UInt32 seed = 0U;
Boolean found;
CFArrayRef currentLoginItems = LSSharedFileListCopySnapshot(loginItems,
&seed);
const CFIndex count = CFArrayGetCount(currentLoginItems);
for (CFIndex idx = 0; idx < count; ++idx) {
LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(currentLoginItems, idx);
CFURLRef outURL = NULL;
const UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
#if (__MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10)
outURL = LSSharedFileListItemCopyResolvedURL(item, resolutionFlags, /*outError*/ NULL);
if (outURL == NULL) {
if (outURL)
CFRelease(outURL);
continue;
}
#else
OSStatus err = LSSharedFileListItemResolve(item, resolutionFlags, &outURL, /*outRef*/ NULL);
if (err != noErr || outURL == NULL) {
if (outURL)
CFRelease(outURL);
continue;
}
#endif
found = CFEqual(outURL, URLToToggle);
NSLog(#"%#", outURL);
CFRelease(outURL);
}
CFRelease(currentLoginItems);
CFRelease(loginItems);
}
Output in my instance:
file:///Volumes/User/dgrassi/Library/PreferencePanes/MouseLocator.prefPane/Contents/Resources/MouseLocatorAgent.app/
file:///Applications/iTunes.app/Contents/MacOS/iTunesHelper.app/
file:///Applications/Dropbox.app/
file:///Library/PreferencePanes/Screens%20Connect.prefPane/Contents/MacOS/Screens%20Connect.app/
file:///Library/Application%20Support/EyeTV/EyeTV%20Helper.app/
file:///Applications/Carbon%20Copy%20Cloner.app/Contents/Library/LoginItems/CCC%20User%20Agent.app/
This came from seafile-client
<\RESOLVED>, Please see the first reply
My mac(10.9) has joined into a AD domain. In my program, I tried to recognize whether the current login user is local account or AD user. I can successfully distinguish them by using the following code.
+ (bool)isLocalUser:(NSString*)user
{
NSError *dirSearchError = nil;
ODRecord *foundUser = findUser(user, &dirSearchError);
if(foundUser !=nil)
{
return YES;
}else
{
return NO;
}
}
ODRecord *findUser(NSString *user, NSError **error)
{
NSLog(#"[MacLogonUI] findUser");
ODNode *searchNode = [ODNode nodeWithSession: [ODSession defaultSession]
type: kODNodeTypeLocalNodes
error: error];
if (searchNode == nil) {
return nil;
}
NSDictionary *nodeInfo = [searchNode nodeDetailsForKeys:nil error:error];
/* query this node for the user record we're interested in.
* We only need one result, which is why maximumResults is set to 1.
*/
ODQuery *userSearch = [ODQuery queryWithNode: searchNode
forRecordTypes: kODRecordTypeUsers
attribute: kODAttributeTypeRecordName
matchType: kODMatchEqualTo
queryValues: user
returnAttributes: kODAttributeTypeStandardOnly
maximumResults: 1
error: error];
if (userSearch == nil) {
return nil;
}
/* For this example we'll use a synchronous search. This could take a while
* so asynchronous searching is preferable.
*/
NSArray *foundRecords = [userSearch resultsAllowingPartial: NO error: error];
if (foundRecords == nil || [foundRecords count] == 0) {
return nil;
}
ODRecord *userRecord = [foundRecords objectAtIndex: 0];
return [[userRecord retain] autorelease];
}
While when the AD user create a mobile card, it is viewed as a managed user(from the System preference -> Users & Groups). The code also recognize this kind of AD user as local. How to deal with this kind of situation?
Do you guys have any idea of this problem?
I have solved this problem by myself. Hope the following code helps:
#import "DasUser.h"
#import <OpenDirectory/OpenDirectory.h>
#import <Collaboration/Collaboration.h>
#implementation DasUser
+ (bool)isLocalUser:(NSString*)user
{
NSError *dirSearchError = nil;
ODRecord *foundUser = findUser(user, &dirSearchError);
if(foundUser !=nil)
{
return YES;
}else
{
return NO;
}
}
ODRecord *findUser(NSString *user, NSError **error)
{
NSLog(#"[MacLogonUI] findUser");
CSIdentityAuthorityRef defaultAuthority = CSGetManagedIdentityAuthority();
CSIdentityClass identityClass = kCSIdentityClassUser;
CSIdentityQueryRef query = CSIdentityQueryCreate(NULL, identityClass, defaultAuthority);
CFErrorRef err = NULL;
CSIdentityQueryExecute(query, 0, &err);
CFArrayRef results = CSIdentityQueryCopyResults(query);
int numResults = CFArrayGetCount(results);
NSMutableArray * managedUsers = [NSMutableArray array];
for (int i = 0; i < numResults; ++i) {
CSIdentityRef identity = (CSIdentityRef)CFArrayGetValueAtIndex(results, i);
CBIdentity * identityObject = [CBIdentity identityWithCSIdentity:identity];
NSString* posixName = [identityObject posixName];
[managedUsers addObject:posixName];
}
CFRelease(results);
CFRelease(query);
ODNode *searchNode = [ODNode nodeWithSession: [ODSession defaultSession]
type: kODNodeTypeLocalNodes
error: error];
if (searchNode == nil) {
return nil;
}
/* query this node for the user record we're interested in.
* We only need one result, which is why maximumResults is set to 1.
*/
ODQuery *userSearch = [ODQuery queryWithNode: searchNode
forRecordTypes: kODRecordTypeUsers
attribute: kODAttributeTypeRecordName
matchType: kODMatchEqualTo
queryValues: user
returnAttributes: kODAttributeTypeStandardOnly
maximumResults: 1
error: error];
if (userSearch == nil) {
return nil;
}
/* For this example we'll use a synchronous search. This could take a while
* so asynchronous searching is preferable.
*/
NSArray *foundRecords = [userSearch resultsAllowingPartial: NO error: error];
if([foundRecords count]>0)
{
NSString *nameStr = [foundRecords[0] recordName];
NSLog(#"[MacLogonUI] findUser nameStr %#", nameStr);
int j;
for( j = 0; j<[managedUsers count]; j++)
{
if([nameStr isEqualToString:managedUsers[j]])
{
break;
}
}
if(j<[managedUsers count])
{
foundRecords = nil;
}
}
if (foundRecords == nil || [foundRecords count] == 0) {
return nil;
}
ODRecord *userRecord = [foundRecords objectAtIndex: 0];
return [[userRecord retain] autorelease];
}
#end
While when network of the mac is disconnected. The managed user can not be listed. Is there anybody has any idea of this?
I have the following Code to read from a .p12 certificate and I want to hand the certifcate data over to an autentication challenge:
- (SecIdentityRef)getClientCertificate {
NSString *thePath = [[NSBundle mainBundle] pathForResource:#"fest" ofType:#"p12"];
if(!thePath)
return NULL;
NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath] ;
CFDataRef inPKCS12Data = (CFDataRef)CFBridgingRetain(PKCS12Data);
CFStringRef password = CFSTR("fest");
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus ret = SecPKCS12Import(inPKCS12Data, optionsDictionary, &items);
if (ret != errSecSuccess)
{
// TODO: handle error.
NSLog(#"-> SecPKCS12Import error (%ld)", ret);
}
CFRelease(optionsDictionary);
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef identityApp = nil;
if(!identityDict)
return nil;
identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
SecIdentityRef identity;
SecCertificateRef cert;
OSStatus err;
CFStringRef certName;
identity = identityApp;
assert( (identity != NULL) && (CFGetTypeID(identity) == SecIdentityGetTypeID()) );
cert = NULL;
err = SecIdentityCopyCertificate(identity, &cert);
assert(err == noErr);
assert(cert != NULL);
certName = SecCertificateCopySubjectSummary(cert);
assert(certName != NULL);
NSLog(#"%#" , (id) CFBridgingRelease(certName));
//NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:(id)CFBridgingRelease(certName),#"USERID",nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"UserInfoReceived" object:nil];
//CFRelease(cert);
//CFRelease(certName);
return identityApp;
_identities = [[NSMutableArray alloc] initWithArray:(__bridge NSArray *)(items)];
_certs = [[NSMutableArray alloc] initWithArray:(__bridge NSArray *)(cert)];
}
At the end I want to assign the items CFArrayRef to the identities Array. But is does not work. Has somebody an idea?
Thanks!
A CFArrayRef is toll-free bridged to NSArray *, so you can cast it as such and then create a mutable copy. Try this:
_identities = [(NSArray *)items mutableCopy];
I am getting leak at:
NSString *firstNameStr = [NSString stringWithFormat:#"%s",firstNameString];
CODE:
+(NSString *)getValueForProperty:(ABPropertyID)propertyId
forContact:(NSString *)contactId
{
if (addressBook == nil)
{
addressBook = ABAddressBookCreate();
}
ABRecordID contactIntId = [contactId intValue];
ABRecordRef person;
person = ABAddressBookGetPersonWithRecordID(addressBook, contactIntId);
CFStringRef firstName;
char *firstNameString;
firstName = ABRecordCopyValue(person, propertyId);
// Paso a char* los datos para que se puedan escribir
static char* fallback = "";
int fbLength = strlen(fallback);
int firstNameLength = fbLength;
bool firstNameFallback = true;
if (firstName != NULL)
{
firstNameLength = (int) CFStringGetLength(firstName);
firstNameFallback = false;
}
if (firstNameLength == 0)
{
firstNameLength = fbLength;
firstNameFallback = true;
}
firstNameString = malloc(sizeof(char)*(firstNameLength+1));
if (firstNameFallback == true)
{
strcpy(firstNameString, fallback);
}
else
{
CFStringGetCString(firstName, firstNameString,
10*CFStringGetLength(firstName), kCFStringEncodingASCII);
}
if (firstName != NULL)
{
CFRelease(firstName);
}
NSString *firstNameStr = [NSString stringWithFormat:#"%s",firstNameString];
free(firstNameString);
return firstNameStr;
}
That means that the object allocated at that point is leaked. In this case, most likely because you over-retained it somewhere and failed to release it.
You need to carefully examine the lifespan of that particular string and figure out where you might be overwriting the reference without a release.
Build & Analyze might help considerably.