Sporadic errSecAuthFailed (-25293) when accessing KeyChain with SecKeychainFindGenericPassword? - cocoa

i'm writing an app that stores passwords on the keychain, and later obtains them with SecKeychainFindGenericPassword(). this works file 90% of the time, but every once in a while, the call to SecKeychainFindGenericPassword() will fail with errSecAuthFailed (-25293). when it does, just trying again, or restarting the app fixes it.
does anyone have an idea what could be causing this? general Google search on this error points to keychain corruption or the keychain being locked - neither of which is the case here, since subsequent calls succeed again...

This link suggests the passphrase you entered is not correct. See Here
Is it possible that sometimes you are sending a null object as a passphrase just by chance?
Alternatively, you could try out EMKeychain. I have a more up-to-date version on GitHub here: http://github.com/ctshryock/EMKeychain

You haven't shared the code around your problem, So I'll just guess your problem is not with dysfunctional keychain, but rather some coding error.
Here is a common pitfall: Since KeyChain APIs are 'C', and they only accept C-style null terminated string buffers, you'll usually need to convert your CFString/NSString objects to C buffers before handing them to the API.
Many use things like:
const char *usernameCStr = [username UTF8String];
For an NSString, or its CFString companion...
const char *CFStringGetCStringPtr(CFStringRef theString, CFStringEncoding encoding); /* May return NULL at any time; be prepared for NULL */
Dismissing the fact these APIs may return NULL. Either because the internal buffer of the CF/NSString is non-contiguous, or not in the encoding you asked for, or otherwise not-c-compatible.
Such issue can behave in runtime exactly like what you describe.
In such cases you should catch the problem and use different API to copy the CF/NS string into a C-buffer:
Boolean CFStringGetCString(CFStringRef theString, char *buffer, CFIndex bufferSize, CFStringEncoding encoding);
or
- (BOOL)getCString:(char *)buffer maxLength:(NSUInteger)maxBufferCount encoding:(NSStringEncoding)encoding;

I'm not sure this was the problem (i don't see how it could have been) but i recently changed my code to properly pass the strlen() of the cStrings, rather then the length of the NSStrings into the call. Technically this is more correct (since the string length might differ from the cString, if UTF-8 dual-byte characters are involved.
BUT, none of the usernames/passwords i tested with contained non-ASCII characters, so i don;t see how this problem could have actually affected the errors I was seeing. My new code is as follows, and i have not seen the error with it:
UInt32 length;
void *data;
const char *account = [[BC_HOST stringByAppendingFormat:#":%#", login] cStringUsingEncoding:NSUTF8StringEncoding];
NSLog(#"Getting password from keychain.");
OSStatus s = SecKeychainFindGenericPassword (nil,
strlen(BC_APPNAME), BC_APPNAME,
strlen(account), account,
&length, &data, &keychainItem);
if (s != 0) NSLog(#"Error %d obtaining password from keychain.", s);
if (s == 0)
{
password = [[NSString alloc] initWithBytes:data length:length encoding:NSUTF8StringEncoding];
}

I was having the same issue and in my case this turned out to be the cause: Cannot access keychain item after SMJobBless update

One possible reason that this issue can occur is if the executable making the call does not have access to the keychain item. In Keychain Access you can see a list of the apps that have permission to access the item under the Access Control tab for the item in question.
If your app is running from a different location, you will get this error. For example, I have a Privileged Helper Tool that on my dev machine I typically run through Xcode as root. The path to this executable is the path at which Xcode creates it which is a path in ~/Library/Developer/Xcode/DerivedData//myexecutable. When I run it as a user would, it is being run from /Library/PrivilegedHelperTools/myexecutable. So if the password was initially created by one version of the app and I try to read it using the other path, I will see the errSecAuthFailed error.
This isn't the only reason. Someone else mentioned the upgrade in place problem that SMJobBless has. That can also result in the same error code, but for sure I see it for both reasons - although I programmatically solved the upgrade in place issue by ordering the helper tool to move itself to a different location before I do the upgrade.

Related

greenline Xcode EXC_BAD_INSTRUCTION on NSUserDefaults Optional

I've been writing an app that involves using NSUserDefaults to store a few Int variables and it's been working fine. I thought I was finished and was doing some final testing and one of the first lines of code that I wrote, and that has been working consistently before, has failed me.
Apparently the green line error is supposed to occur if I try to unwrap an optional that has a value of nil, but this variable is still very much an optional
var savedTotalSeconds: Int? = userDefaults.objectForKey("totalSecondsKey") as Int?
Why would this possibly return an error? It was working fine before and I only changed things I thought were unrelated to it. In the app I have a button to remove this stored value via:
userDefaults.removeObjectForKey("totalSecondsKey")
What could possibly have gone wrong?
Try using 'as? Int' instead of 'as Int?'
The difference is that the first one tries, and might fail, at casting to Int. That failure will be captured in the optionality of the resulting variable.
The second one tries to coerce the object to 'Int?'.

SHFileOperation FOF_ALLOWUNDO fails on long filenames

I'm using the following function to delete a file to the recycle bin: (C++, MFC, Unicode)
bool DeleteFileToPaperbasket (CString filename)
{
TCHAR Buffer[2048+4];
_tcsncpy_s (Buffer, 2048+4, filename, 2048);
Buffer[_tcslen(Buffer)+1]=0; //Double-Null-Termination
SHFILEOPSTRUCT s;
s.hwnd = NULL;
s.wFunc = FO_DELETE;
s.pFrom = Buffer;
s.pTo = NULL;
s.fFlags = FOF_ALLOWUNDO | FOF_SILENT | FOF_NOERRORUI;
s.fAnyOperationsAborted = false;
s.hNameMappings = NULL;
s.lpszProgressTitle = NULL;
int rc = SHFileOperation(&s);
return (rc==0);
}
This works nicely for most files. But if path+filename exceed 255 characters (and still much shorter that 2048 characters), SHFileOperation returns 124. Which is DE_INVALIDFILES.
But what's wrong? I checked everything a million times. The path is double-null terminated, I'm not using \\?\ and it works for short filenames.
I'm totally out of ideas...
I think backwards comparability is biting you in the --- in several ways, and I'd need to actually see the paths your using and implement some error checking code to help. But here are some hints.
You would not get a DE_INVALIDFILES 0x7C "The path in the source or destination or both was invalid." for a max path violation, you'd get a DE_PATHTOODEEP 0x79 "The source or destination path exceeded or would exceed MAX_PATH."
These error codes(return value) do, can, and have changed over time, to be sure what your specific error code means, you need to check it with GetLastError function(msdn)
Also, taken from the SHFileOperation function documentation: "If you do not check fAnyOperationsAborted as well as the return value, you cannot know that the function accomplished the full task you asked of it and you might proceed under incorrect assumptions."
You should not be using this API for extremely long path names, it has been replaced in vista+ by IFileOperation interface
The explanation for why it may work in explorer and not thru this LEGACY api is - Taken from the msdn page on Naming Files, Paths, and Namespaces
The shell and the file system have different requirements. It is
possible to create a path with the Windows API that the shell user
interface is not able to interpret properly.
Hope this was helpful
The recycle bin doesn't support files whose paths exceed MAX_PATH in length. You can verify this for yourself by trying to recycle such a file in Explorer - you will get an error message about the path being too long.

How does a DataObjects::SQLError object's .code correspond to the error issued by the database?

So I'm doing some business logic and want to run some code that goes like
select id from blah where foo = 1234 for update nolock
This code throws a DataMapper::SQLError when the corresponding row in blah is locked. This is desirable behavior; I would like to catch this error and use it to inform my application logic. But I want to re-throw any other SQL errors, because they're different than the case I'm programming for, and catching them in the same way would be wrong.
The error object returned has a string error message, and a numeric code (50463045). It seems like comparing on the numeric code would be great, but I don't want to embed the constant 50463045 in my code without some modicum of understanding of how the heck it was determined. Notably, the Postgres manual suggests that the error code for this state is 55P03, and that doesn't seem to be the same thing. I don't have any idea how much I can trust this magic number, and how to determine it except for experimentally, so I'm not really comfortable with using it.
How is the error code determined?
The Internet was distressingly unhelpful, since searching for stuff about DataObjects SQL errors seems to mostly return problems with other software raising the errors, not information on the errors themselves... but after locating the right source code and browsing around through the source code I finally located do_postgres.c:
void do_postgres_raise_error(VALUE self, PGresult *result, VALUE query) {
const char *message = PQresultErrorMessage(result);
char *sql_state = PQresultErrorField(result, PG_DIAG_SQLSTATE);
int postgres_errno = MAKE_SQLSTATE(sql_state[0], sql_state[1], sql_state[2], sql_state[3], sql_state[4]);
PQclear(result);
data_objects_raise_error(self, do_postgres_errors, postgres_errno, message, query, rb_str_new2(sql_state));
}
Notice how a 5-character state is passed to MAKE_SQLSTATE... and then also passed to data_objects_raise_error itself. I couldn't track down where MAKE_SQLSTATE is defined to figure out what crazy manipulations are going on to make this integer, but it appears I can just use the error object's .sqlstate property directly, and make my condition e.sqlstate == '55P03'.

Why are LoadResource/LockResource sometimes returning concatenated resource data?

I'm working with a Visual Studio C++ project that contains a number of HTML resources. They are loaded by a method that looks like this:
LPCTSTR loadHTML(HMODULE hModule, LPCTSTR sResourceName)
{
HRSRC hResource = FindResource(hModule, sResourceName, RT_HTML);
if(!hResource)
return 0;
HGLOBAL hResourceData = LoadResource(hModule, hResource);
if(!hResourceData)
return 0;
return reinterpret_cast<LPCTSTR>(LockResource(hResourceData));
}
Most of the time, this works fine. Some times, though, it returns a resource concatenated with another resource. When this happens, it is a persistent problem in that build. I can "fix" it by adding a few blank lines to the resource in question and then rebuilding the project. It happens periodically, even when the resources haven't changed.
I am keen to get to the bottom of why it is happening. Has anyone else come across it? Could there be something peculiar about my resources that is causing the problem? Is my code wrong?
Sadly, I'm reluctant to post an example resource here; they're pretty long and this is proprietary software.
Whats peculiar about your resources is you are expecting them to be zero terminated. iirc resource sections are aligned on 16 byte boundries, which means that whenever a "blob" is a multiple of 16 bytes long there won't be any separating byte's between the resource and the next.
Either ensure that the resources are saved with a terminating zero character, or use SizeofResource to determine where the resource ends.
How do you determine the end of a resource? Do your resource files end in a (double for unicode) NULL? I don't think there is any guarantee that a resource is NULL terminated in the PE file and you seem to be treating it as a string.

NSURLConnection using basic auth fails on Tiger, succeeds on Panther and Leopard

I have a program running on Panther, Tiger, and Leopard systems and using the following method for NSURLConnection authentication ("encodedUserPass" is the auth string with the word Basic followed by the base64-encoded user:pass)
[theRequest addValue:encodedUserPass forHTTPHeaderField:#"Authorization"];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
...later...in didReceiveAuthenticationChallenge
if ([challenge previousFailureCount] == 0) {
NSURLCredential *newCredential;
newCredential=[NSURLCredential credentialWithUser:login_name password:password persistence:NSURLCredentialPersistenceNone];
[[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
}
This works perfectly fine on Panther and Leopard systems but fails utterly on Tiger. It is odd, though, that even on Panther and Leopard "didReceiveAuthenticationChallenge" is usually called (i.e., setting the headers manually seems not to work).
On Tiger, didReceiveAuthenticationChallenge is always called, tries to respond as shown above, and is then called again with failure.
Two questions: (i) Why doesn't setting the headers manually work? and (2) Why does the method shown above fail on Tiger (10.4)?
LATER UPDATE:
After some thought, I realized that there had to be something wrong with my base64-encoding method, and there was: I didn't append equals signs to bring the base64 string up to a multiple of 4 characters. I solved it with
while ([bareString length] % 4) [bareString appendString:#"="];
And now the program works on all three platforms. So question (i) is answered: setting the headers manually didn't work because I wasn't padding with equals signs.
Question (ii) remains, though: why can't I use didReceiveAuthenticationChallenge successfuly in Tiger?
I have just stumbled into the same problem that you describe, and found that NSURLCredentialPersistenceNone simply does not work on Tiger, whereas specifying NSURLCredentialPersistenceForSession does.
Depending on your application that may or may not be an acceptable workaround.
Apple's documentation leaves a bit to be desired as it doesn't specify what the scope of a 'session' actually is - until the application quits, maybe?

Resources