Error with SecKeychainGetPath - objective-c

Sometimes when trying to get a path to a keychain returned by SecKeychainCopySearchList I get error with code -25301 which from the list of errors stands for errSecBufferTooSmall. The SecCopyErrorMessageString states:
There is not enough memory available to use the specified item.
Weird thing is that it doesn't always return the error on the very same keychain reference.
Here's how I try to get the path to the keychain:
- (NSString *)getKeychainPath:(SecKeychainRef)keychain {
char *pathName = malloc(sizeof(*pathName) * 1024);
UInt32 pathLength;
OSStatus errCode = SecKeychainGetPath(keychain, &pathLength, pathName);
if (errCode != errSecSuccess) {
NSString *errString = (NSString *)SecCopyErrorMessageString(errCode, NULL);
DLog(#"%d: %#", errCode, errString);
}
NSData *d = [NSData dataWithBytes:pathName length:pathLength];
return [[[NSString alloc] initWithData:d encoding:NSUTF8StringEncoding] autorelease];
}
I'm interested in what buffer does the function use? I've tried outputting the pathLength variable but it's way bellow the 1K bytes. What am I doing wrong? What should I do to avoid these errors? Can they be bypassed by any way at all?

From the SecKeychainGetPath documentation:
ioPathLength
On entry, a pointer to a variable containing the length (in bytes) of the buffer specified by pathName.
On return, the string length of pathName, not including the null termination.
You're not doing the "on input" part. You need to initialize pathLength to the size of the pathName buffer. For example:
UInt32 pathLength = 1024;
char *pathName = malloc(pathLength);

Related

Objective C stringWithCString "Method cache corrupted"

I am creating a method for executing shell commands. It looks like this:
NSString *cShellStr(NSString *command, int maxBufferSize) {
if (maxBufferSize<1) {
maxBufferSize = INT_MAX;
}
NSString *newCommand = [NSString stringWithFormat:#"%# 2>&1", command];
const char *cStrCommand = [newCommand cStringUsingEncoding:NSUTF8StringEncoding];
FILE *fp;
fp = popen(cStrCommand, "r");
if (fp == NULL) {
NSLog(#"Failed to open process");
return nil;
}
char *buffer = NULL;
buffer = (char*)malloc(4);
if (buffer == NULL) {
NSLog(#"Failed to allocate memory");
return nil;
}
while (!feof(fp)) {
buffer = realloc(buffer, sizeof(buffer)+1);
sprintf(buffer, "%s%c", buffer, fgetc(fp));
}
NSString *ret = [NSString stringWithCString:buffer encoding:NSASCIIStringEncoding];
fclose(fp);
free(buffer);
return ret;
}
However, it usually (strangely not always) gets this error at [NSString stringWithCString:buffer encoding:NSASCIIStringEncoding]:
objc[33674]: Method cache corrupted. This may be a message to an invalid object, or a memory error somewhere else.
objc[33674]: receiver 0x7fff7d62aec0, SEL 0x7fff99ac69f5, isa 0x7fff7d62aee8, cache 0x7fff7d62aef8, buckets 0x100200780, mask 0x3, occupied 0x2, wrap bucket 0x100200780
objc[33674]: receiver 0 bytes, buckets 64 bytes
objc[33674]: selector 'class'
objc[33674]: isa 'NSString'
objc[33674]: Method cache corrupted.
objc[33674]: Method cache corrupted. This may be a message to an invalid object, or a memory error somewhere else.
objc[33674]: receiver 0x7fff7d62aec0, SEL 0x7fff99aea2d4, isa 0x7fff7d62aee8, cache 0x7fff7d62aef8, buckets 0x100200780, mask 0x3, occupied 0x2, wrap bucket 0x100200780
objc[33674]: receiver 0 bytes, buckets 64 bytes
objc[33674]: selector 'stringWithCString:encoding:'
objc[33674]: isa 'NSString'
objc[33674]: Method cache corrupted.
I have stepped through it with lldb and confirmed that there are no other errors and that buffer contains exactly the text I would expect. Am I incorrectly using stringWithCString?
Extra info:
Xcode version:
Laptop info:
Clang version:
Apple LLVM version
6.0 (clang-600.0.51) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin13.3.0
Thread model: posix
while (!feof(fp)) {
buffer = realloc(buffer, sizeof(buffer)+1);
sprintf(buffer, "%s%c", buffer, fgetc(fp));
}
This loop should be rewritten as:
size_t len = 0, cap = 4;
buffer = malloc(cap); // TODO check NULL
int c;
while (EOF != (c = fgetc(fp))) {
if (len >= cap) {
cap += cap;
buffer = realloc(buffer, cap); // TODO check NULL
}
buffer[len++] = c;
}
// check feof/ferror
sprintf(buffer, "%s%c", buffer, fgetc(fp));
This is undefined behaviour because you are using buffer twice. Anything could go wrong, and lucky for you, it does. I'm saying "lucky for you" because with a bit of bad luck it would only go wrong in the hand of customers.
I didn't watch out; the issue that user3125367 found is ten times worse.

Persisting bookmark in core-data

I have an OSX application that is supposed to have a list of files from anywhere in the user's disk.
The first version of the app saves the path to these files in a core-data model.
However, if the file is moved or renamed, the tool loses its purpose and the app can crash.
So I decided to use bookmarks. It seems to be working, but every time I try to recover the data, I get the old path of the files. Why is that? What am I missing?
My core-data entity uses a binary data field to persist the bookmark.
The bookmark itself is done like this:
NSData * bookmark = [filePath bookmarkDataWithOptions:NSURLBookmarkCreationMinimalBookmark
includingResourceValuesForKeys:NULL
relativeToURL:NULL
error:NULL];
And on loading the application, I have a loop to iterate all the table and recover the bookmark like this:
while (object = [rowEnumerator nextObject]) {
NSError * error = noErr;
NSURL * bookmark = [NSURL URLByResolvingBookmarkData:[object fileBookmark]
options:NSURLBookmarkResolutionWithoutUI
relativeToURL:NULL
bookmarkDataIsStale:NO
error:&error];
if (error != noErr)
DDLogCError(#"%#", [error description]);
DDLogCInfo(#"File Path: %#", [bookmark fileReferenceURL]);
}
If I rename the file, the path is null. I see no difference between storing this NSData object and a string with the path. So I am obviously missing something.
Edit:
I also often get an error like this: CFURLSetTemporaryResourcePropertyForKey failed because it was passed this URL which has no scheme.
I appreciate any help, thanks!
I can't find any issues in my code, so I changed it.
After looking for the reason of the "no scheme" message, I came to the conclusion some third-party application is required for this code to work, and that's undesirable.
I am now using aliases. This is how I create them:
FSRef fsFile, fsOriginal;
AliasHandle aliasHandle;
NSString * fileOriginalPath = [[filePath absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
OSStatus status = FSPathMakeRef((unsigned char*)[fileOriginalPath cStringUsingEncoding: NSUTF8StringEncoding], &fsOriginal, NULL);
status = FSPathMakeRef((unsigned char*)[fileOriginalPath cStringUsingEncoding: NSUTF8StringEncoding], &fsFile, NULL);
OSErr err = FSNewAlias(&fsOriginal, &fsFile, &aliasHandle);
NSData * aliasData = [NSData dataWithBytes: *aliasHandle length: GetAliasSize(aliasHandle)];
And now I recover the path like this:
while (object = [rowEnumerator nextObject]) {
NSData * aliasData = [object fileBookmark];
NSUInteger aliasLen = [aliasData length];
if (aliasLen > 0) {
FSRef fsFile, fsOriginal;
AliasHandle aliasHandle;
OSErr err = PtrToHand([aliasData bytes], (Handle*)&aliasHandle, aliasLen);
Boolean changed;
err = FSResolveAlias(&fsOriginal, aliasHandle, &fsFile, &changed);
if (err == noErr) {
char pathC[2*1024];
OSStatus status = FSRefMakePath(&fsFile, (UInt8*) &pathC, sizeof(pathC));
NSAssert(status == 0, #"FSRefMakePath failed");
NSLog(#"%#", [NSString stringWithCString: pathC encoding: NSUTF8StringEncoding]);
} else {
NSLog(#"The file disappeared!");
}
} else {
NSLog(#"CardCollectionUserDefault was zero length");
}
}
However, I am still curious on why my previous code failed. I appreciate any thoughts on that. Thanks!

NSData pointer vs reference

I'm dealing with the garmin GDL90 protocol which sends across various types of messages in binary to my IOS device. I'm going through and trying to process all these messages but have been running into an issue. Specifically the messages are byte packed so that if you ever see an occurrence of
0x7d 0x5e or 0x7d 0x5d you have to convert them to 0x7d or 0x7e
I've set my code up so that I detect the message type I'm parsing and then call a function:
- (void) parseMessage:(NSMutableData *)message
to do my data parsing. My individual message parsing functions call the parent function [super parseMessage:message]; which handles both the parsing of common elements as well as dealing with my byte-stuffing. Each of these function calls takes an NSData * so shouldn't a modification made in my super function return back out the same data?
My top level class gets a parse message call and the NSMutableData pointer's address is: 0x170048f10
Once I step into the parent's parseData call my address is still 0x170048f10
After I make modifications to the data I'm now pointing at the memory address 0x17805e840
Once I return from this function, however, I'm back pointing at 0x170048f10 again with the wrong data.
Should I be using pass by reference or something? Any suggestions?
I have two variations of my function - unstuff1 throws an error and unstuff2 doesn't work.
- (NSMutableData *)unstuff1:(NSMutableData *)mutableData {
int dataLength = [mutableData length];
char *bytes = [mutableData bytes];
// Scan bytes ignoring 1st and last byte because they will be 7e's
for (int i = dataLength - 1; i > 0; i--) {
bytes[i + 1] ^= 0x20;
if (i + 1 == dataLength) {
NSLog(#"Terminal character padding detected on character %d with length %d", i, dataLength);
} else {
/* Replace 2 bytes with a single byte should remove the flag when you do this */
[mutableData replaceBytesInRange:NSMakeRange(i, 2) withBytes:&bytes[i + 1] length:1];
dataLength--;
}
}
return mutableData;
}
- (NSMutableData *)unstuff2:(NSMutableData *)data {
NSMutableData *mutableData = [[NSMutableData alloc] initWithData:data];
int dataLength = [mutableData length];
char *bytes = [mutableData bytes];
// Scan bytes ignoring 1st and last byte because they will be 7e's
for (int i = dataLength - 1; i > 0; i--) {
bytes[i + 1] ^= 0x20;
if (i + 1 == dataLength) {
NSLog(#"Terminal character padding detected on character %d with length %d", i, dataLength);
} else {
/* Replace 2 bytes with a single byte should remove the flag when you do this */
[mutableData replaceBytesInRange:NSMakeRange(i, 2) withBytes:&bytes[i + 1] length:1];
dataLength--;
}
}
return mutableData;
}
In unstuff2 obviously i'm making a new MutableData so I guess that accounts for the memory address change (that is the function i was using that gave me the error specified).
unstuff1 throws the following exception:
-[_NSInlineData replaceBytesInRange:withBytes:length:]: unrecognized selector sent to instance 0x178250d40
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_NSInlineData replaceBytesInRange:withBytes:length:]: unrecognized selector sent to instance
Unlike languages like C++ and C# (just to name two), Objective C has no concept of "pass by reference". However, passing a copy of a pointer to your NSMutableData is functionally equivalent to passing the object by reference. That is, if you pass in an NSMutableData (or NSMutableAnything for that matter) to a function and that function modifies it, the calling function will see the changes reflected in the object that it passed in.
Ok looks like I tracked down the problem. I realized the compiler was sending out warnings:
incompatible pointer types initializing 'NSMutableData *' with an expression of type 'NSData *'
It turns out I had some code
NSMutableData *message = [data subdataWithRange:NSMakeRange(5, len - 5)];
Which i needed to convert into:
NSMutableData *message = [NSMutableData dataWithData:[data subdataWithRange:NSMakeRange(5, len - 5)]];
And then things all work out. Moral of the story (read your warnings!!!!)

Objective C- Confusing EXC_BAD_ACCESS when using [NSString initwithdata]

I am having some trouble using the data that I receive from a remote server. This is how I take in the data from my nsinputstream:
case NSStreamEventHasBytesAvailable:
{
if(!_data)
{
_data = [NSMutableData data];
}
uint8_t buffer = malloc(1024);
NSInteger *len = [inputStream read:buffer maxLength:1024];
if(len)
{
_data = [[NSData alloc]initWithBytesNoCopy:buffer length:1024];
[self closeThread];
}
shouldClose = YES;
break;
}
In the same class I have this function to return the data in order to use it in different classes:
-(NSData *)returnData {
return self.data;
}
In the view controller that I want to use the data in I have this code to retrieve the data for use:
_schools = [_server returnData];
NSString *schoolString = [[NSString alloc] initWithData:self.schools encoding:NSUTF8StringEncoding];//exc_bad_access
From what I understand about EXC_BAD_ACCESS exceptions they usually mean that you are trying to access data that either doesn't exist or is not allocated. The _schools variable shows a size of 1024 bytes so I know there is memory correctly allocated for it. Is there something else going wrong that I am missing?
You appear to have mixed up the types of the variables on these two lines:
uint8_t buffer = malloc(1024);
NSInteger *len = [inputStream read:buffer maxLength:1024];
In its current form, you will malloc'ate 1024 bytes of memory, and attempt to store the pointer to said memory in a uint8_t (which CLANG will rightly scream at you for), thus truncating the pointer and not providing a buffer, but rather a single unsigned 8-bit byte for the stream to attempt to read into. Also, -[NSInputStream read:maxLength:] does not return NSInteger *, just plain NSInteger, so all you need to do is swap the pointers on the two variables:
uint8_t *buffer = malloc(1024);
NSInteger len = [inputStream read:buffer maxLength:1024];
and it should work just fine.

Try to create NSString from bytes. It always returns an empty string

I am trying to create a string from bytes a received via network. The NSString I het is always an empty one.
if (stringLength > 0) {
NSData *bytes = [[NSData alloc] initWithBytes:data+1 length:stringLength];
result = [[NSString alloc] initWithData:bytes encoding:NSUTF8StringEncoding];
//result = [[NSString alloc] initWithBytes:data+1 length:stringLength encoding:NSASCIIStringEncoding];
}
As I said I get an empty NSString. The string is a base64 encoded value so it should be valid utf-8 since it only contains ascii symbols.
Maybe your raw bytes aren't UTF-8 after all. Or maybe, because you're passing data+1 instead of data itself, you're causing the encoding attempt to fail because the process thinks there's an improper UTF-8 encoding sequence. (Forgive me if I'm being presumptuous, but you have to take everything into account.) In any case, you're relying heavily on your assumption, and that's a trap we all fall into now and then.
Here's a strategy for you. If your attempt at creating an NSString instance using NSUTF8StringEncoding returns nil, then try using NSISOLatin1StringEncoding. And if that returns nil, then try using NSMacOSRomanStringEncoding.
Even if, after all of that, you get a string that's not quite right, it's still better than a nil string in that it could help to show you if there's some other area in which you've made a mistake.
Good luck to you in your endeavors.
Check if it's really base64 encoded..
CFErrorRef error = NULL;
CFDataRef decodedData;
SecTransformRef decoder;
decoder = SecDecodeTransformCreate(kSecBase64Encoding, &error);
if (error) {
CFShow(error);
exit(-1);
}
SecTransformSetAttribute(decoder,
kSecTransformInputAttributeName,
(CFDataRef *)bytes,
&error);
if (error) {
CFShow(error);
exit(-1);
}
decodedData = SecTransformExecute(decoder, &error);
if (error) {
CFShow(error);
exit(-1);
}