qtkeychain/keychain_mac.cpp

146 lines
5.4 KiB
C++
Raw Normal View History

2011-10-27 16:15:46 +00:00
/******************************************************************************
* Copyright (C) 2011 Frank Osterfeld <frank.osterfeld@gmail.com> *
* *
* This program is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
* or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
* details, check the accompanying file 'COPYING'. *
*****************************************************************************/
2011-10-27 16:14:37 +00:00
#include "keychain_p.h"
2011-10-27 16:15:46 +00:00
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
#include <QDebug>
2011-10-27 19:17:54 +00:00
using namespace QKeychain;
2011-10-27 19:01:42 +00:00
template <typename T>
struct Releaser {
explicit Releaser( const T& v ) : value( v ) {}
~Releaser() {
CFRelease( value );
}
const T value;
};
static QString strForStatus( OSStatus os ) {
const Releaser<CFStringRef> str( SecCopyErrorMessageString( os, 0 ) );
const char * const buf = CFStringGetCStringPtr( str.value, kCFStringEncodingUTF8 );
if ( !buf )
return QString();
return QString::fromUtf8( buf, strlen( buf ) );
}
static OSStatus readPw( QByteArray* pw,
2011-10-27 16:15:46 +00:00
const QString& service,
const QString& account,
SecKeychainItemRef* ref ) {
Q_ASSERT( pw );
pw->clear();
const QByteArray serviceData = service.toUtf8();
const QByteArray accountData = account.toUtf8();
void* data = 0;
UInt32 len = 0;
const OSStatus ret = SecKeychainFindGenericPassword( NULL, // default keychain
serviceData.size(),
serviceData.constData(),
accountData.size(),
accountData.constData(),
&len,
&data,
ref );
if ( ret == noErr ) {
*pw = QByteArray( reinterpret_cast<const char*>( data ), len );
2011-10-27 16:15:46 +00:00
const OSStatus ret2 = SecKeychainItemFreeContent ( 0, data );
2011-10-27 19:01:42 +00:00
if ( ret2 != noErr )
qWarning() << "Could not free item content: " << strForStatus( ret2 );
2011-10-27 16:15:46 +00:00
}
return ret;
}
Keychain::Error Keychain::Private::readEntryImpl( QByteArray* pw,
const QString& account,
QString* err ) {
2011-10-27 16:15:46 +00:00
Q_ASSERT( pw );
Q_ASSERT( err );
err->clear();
const OSStatus ret = readPw( pw, service, account, 0 );
switch ( ret ) {
case noErr:
return NoError;
case errSecItemNotFound:
*err = tr("Password not found");
return EntryNotFound;
2011-10-27 16:15:46 +00:00
default:
2011-10-27 19:01:42 +00:00
*err = strForStatus( ret );
2011-10-27 16:15:46 +00:00
return OtherError;
}
}
Keychain::Error Keychain::Private::writeEntryImpl( const QString& account,
const QByteArray& data,
Keychain::OverwriteMode ov,
QString* err ) {
2011-10-27 16:15:46 +00:00
Q_ASSERT( err );
err->clear();
const QByteArray serviceData = service.toUtf8();
const QByteArray accountData = account.toUtf8();
const OSStatus ret = SecKeychainAddGenericPassword( NULL, //default keychain
serviceData.size(),
serviceData.constData(),
accountData.size(),
accountData.constData(),
data.size(),
data.constData(),
2011-10-27 16:15:46 +00:00
NULL //item reference
);
if ( ret != noErr ) {
switch ( ret ) {
case errSecDuplicateItem:
{
if ( ov == Keychain::DoNotOverwrite ) {
*err = tr("Entry already exists");
return EntryAlreadyExists;
}
Error derr = deleteEntryImpl( account, err );
2011-10-27 16:15:46 +00:00
if ( derr != NoError )
return CouldNotDeleteEntry;
2011-10-27 16:15:46 +00:00
else
return writeEntryImpl( account, data, ov, err );
2011-10-27 16:15:46 +00:00
}
default:
2011-10-27 19:01:42 +00:00
*err = strForStatus( ret );
2011-10-27 16:15:46 +00:00
return OtherError;
}
}
return NoError;
2011-10-27 16:14:37 +00:00
}
Keychain::Error Keychain::Private::deleteEntryImpl( const QString& account,
QString* err ) {
2011-10-27 16:15:46 +00:00
SecKeychainItemRef ref;
QByteArray pw;
2011-10-27 16:15:46 +00:00
const OSStatus ret1 = readPw( &pw, service, account, &ref );
if ( ret1 == errSecItemNotFound )
2011-10-27 18:46:23 +00:00
return NoError; // No item stored, we're done
2011-10-27 16:15:46 +00:00
if ( ret1 != noErr ) {
2011-10-27 19:01:42 +00:00
*err = strForStatus( ret1 );
2011-10-27 16:15:46 +00:00
//TODO map error code, set errstr
return OtherError;
}
2011-10-27 19:01:42 +00:00
const Releaser<SecKeychainItemRef> releaser( ref );
2011-10-27 16:15:46 +00:00
const OSStatus ret2 = SecKeychainItemDelete( ref );
2011-10-27 19:01:42 +00:00
2011-10-27 16:15:46 +00:00
if ( ret2 == noErr )
return NoError;
2011-10-27 19:01:42 +00:00
//TODO map error code
*err = strForStatus( ret2 );
return CouldNotDeleteEntry;
2011-10-27 16:14:37 +00:00
}