CDSLocalPluginNode.cpp [plain text]
#include "CDSLocalPluginNode.h"
#include "AuthHelperUtils.h"
#include "CLog.h"
#include "CDSPluginUtils.h"
#include "DirServicesPriv.h"
#include "DirServicesConstPriv.h"
#include "DirServiceMain.h"
#include "DSUtils.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <pthread.h>
#include <pwd.h>
#include <list>
#include <map>
#include <libkern/OSAtomic.h>
#include <syslog.h>
#include "COSUtils.h"
#include "CDSAuthDefs.h"
#include <PasswordServer/KerberosInterface.h>
#include <PasswordServer/PSUtilitiesDefs.h>
#define kGeneratedUIDStr "generateduid"
#define kAuthAuthorityStr "authentication_authority"
#if FILE_ACCESS_INDEXING
struct _sIndexMapping
{
const char *fRecordType; CFStringRef fRecordTypeCF;
char const **fAttributes; CFArrayRef fAttributesCF;
char *fRecordNativeType;
};
#define kIndexPath "/var/db/dslocal/indices/Default/"
extern CFRunLoopRef gPluginRunLoop;
extern dsBool gDSInstallDaemonMode;
extern dsBool gProperShutdown;
extern dsBool gSafeBoot;
#endif
#define kNonURLCharactersNotToEncode " "
#define kURLCharactersToEncode "/"
#pragma mark -
#pragma mark Support Routine
bool IntegrityCheckDB( sqlite3 *inDatabase )
{
sqlite3_stmt *pStmt = NULL;
bool bValidDB = false; int status;
status = sqlite3_prepare( inDatabase, "pragma integrity_check", -1, &pStmt, NULL );
if ( status == SQLITE_OK )
{
status = sqlite3_step( pStmt );
while ( status == SQLITE_ROW ) {
if ( sqlite3_column_type(pStmt, 0) == SQLITE_TEXT ) {
const char *text = (const char *) sqlite3_column_text( pStmt, 0 );
if ( strcmp(text, "ok") == 0 ) {
bValidDB = true;
}
}
status = sqlite3_step( pStmt );
}
sqlite3_finalize( pStmt );
}
return bValidDB;
}
#pragma mark -
#pragma mark Class Routines
CDSLocalPluginNode::CDSLocalPluginNode( CFStringRef inNodeDirFilePath, CDSLocalPlugin* inPlugin ) :
fDBLock("CDSLocalPluginNode:fDBLock"),
mOpenRecordsLock("CDSLocalPluginNode:mOpenRecordsLock"),
mRecordTypeLock("CDSLocalPluginNode:mRecordTypeLock")
{
char *cStr = NULL;
mNodeDirFilePath = (CFStringRef) CFRetain( inNodeDirFilePath );
mPlugin = inPlugin;
mRecordNameAttrNativeName = mPlugin->AttrNativeTypeForStandardType( CFSTR(kDSNAttrRecordName) );
mModCounter = 0;
mNodeDirFilePathCStr = strdup( CStrFromCFString(inNodeDirFilePath, &cStr, NULL, NULL) );
DSFree( cStr );
#if FILE_ACCESS_INDEXING
mFileAccessIndexPtr = NULL;
mUseIndex = false;
mIndexLoading = false;
mIndexPath = NULL;
mProperShutdown = gProperShutdown;
mSafeBoot = gSafeBoot;
AddIndexMapping( kDSStdRecordTypeUsers, kDSNAttrRecordName, kDS1AttrDistinguishedName, kDS1AttrUniqueID, kDS1AttrGeneratedUID, NULL );
AddIndexMapping( kDSStdRecordTypeComputers, kDSNAttrRecordName, kDS1AttrUniqueID, kDS1AttrENetAddress, kDSNAttrIPAddress,
kDSNAttrIPv6Address, NULL );
AddIndexMapping( kDSStdRecordTypeGroups, kDSNAttrRecordName, kDS1AttrDistinguishedName, kDS1AttrPrimaryGroupID, kDSNAttrMember,
kDSNAttrGroupMembers, kDSNAttrGroupMembership, NULL );
CFMutableStringRef aFilePath = CFStringCreateMutableCopy( NULL, 0, inNodeDirFilePath );
CFStringFindAndReplace( aFilePath, CFSTR("nodes"), CFSTR("indices"), CFRangeMake(0,CFStringGetLength(aFilePath)), 0 );
if ( CFStringCompare(CFSTR(kIndexPath), aFilePath, 0) == kCFCompareEqualTo )
{
const char* filePath = CStrFromCFString( aFilePath, &cStr, NULL, NULL );
if ( EnsureDirs(filePath, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, S_IRWXU) == 0 )
{
char fileAccessPath[1024];
snprintf( fileAccessPath, sizeof(fileAccessPath), "%s/%s", filePath, "index" );
mIndexPath = strdup( fileAccessPath );
pthread_t loadIndexThread;
pthread_attr_t defaultAttrs;
pthread_attr_init( &defaultAttrs );
pthread_attr_setdetachstate( &defaultAttrs, PTHREAD_CREATE_DETACHED );
mIndexLoaded.ResetEvent();
pthread_create( &loadIndexThread, &defaultAttrs, LoadIndexAsynchronously, (void *) this );
if ( mIndexLoaded.WaitForEvent(10 * kMilliSecsPerSec) == false )
DbgLog( kLogPlugin, "CDSLocalPluginNode::CDSLocalPluginNode - timed out waiting for index to load continuing without until available" );
pthread_attr_destroy( &defaultAttrs );
}
DSFreeString( cStr );
}
DSCFRelease( aFilePath );
#endif
}
CDSLocalPluginNode::~CDSLocalPluginNode( void )
{
#if FILE_ACCESS_INDEXING
CloseDatabase();
DSFreeString( mIndexPath );
#endif
DSCFRelease( mNodeDirFilePath );
}
void *CDSLocalPluginNode::LoadIndexAsynchronously( void *inPtr )
{
#if FILE_ACCESS_INDEXING
CDSLocalPluginNode *pClassPtr = (CDSLocalPluginNode *) inPtr;
pClassPtr->LoadFileAccessIndex();
pClassPtr->mIndexLoaded.PostEvent();
OSAtomicCompareAndSwap32Barrier( true, false, &pClassPtr->mIndexLoading );
#endif
return NULL;
}
#pragma mark -
#pragma mark Public Methods
void CDSLocalPluginNode::CloseDatabase( void )
{
#if FILE_ACCESS_INDEXING
fDBLock.WaitLock();
if ( mFileAccessIndexPtr != NULL )
{
char nodeName[128] = { 0, };
CFStringGetCString( mNodeDirFilePath, nodeName, sizeof(nodeName), kCFStringEncodingUTF8 );
DbgLog( kLogPlugin, "CDSLocalPluginNode::CloseDatabase is shutting down database for %s", nodeName );
sqlite3_close( mFileAccessIndexPtr );
mFileAccessIndexPtr = NULL;
}
fDBLock.SignalLock();
#endif
}
tDirStatus CDSLocalPluginNode::GetRecords( CFStringRef inNativeRecType,
CFArrayRef inPatternsToMatch,
CFStringRef inAttrTypeToMatch,
tDirPatternMatch inPatternMatch,
bool inAttrInfoOnly,
unsigned long maxRecordsToGet,
CFMutableArrayRef recordsArray,
bool useLongNameAlso,
CFStringRef* outRecFilePath)
{
tDirStatus siResult = eDSNoErr;
CFDataRef fileData = NULL;
CFMutableDictionaryRef mutableRecordDict = NULL;
DIR* recTypeDir = NULL;
unsigned long numRecordsFound = 0;
CFIndex numInPatternsToMatch = 0;
bool bGetAllRecords = false;
bool bCheckUsersType = false;
bool bMatchRealName = useLongNameAlso;
bool lockedRecordType = false;
CFStringRef nativeAttrToMatch = NULL;
CFMutableStringRef nativeType = NULL;
char thisFilePathCStr[768];
char thisRecTypeDirPathCStr[512];
bool bContinueSearch = true;
dirent *theDirEnt = NULL;
#if FILE_ACCESS_INDEXING
bool bIsIndexed = false;
#endif
if ( inNativeRecType == NULL || recordsArray == NULL || inPatternsToMatch == NULL ||
(numInPatternsToMatch = CFArrayGetCount(inPatternsToMatch)) == 0 )
{
return eDSNullParameter;
}
try
{
if ( CFStringCompare( inNativeRecType, CFSTR( "users" ), 0 ) == kCFCompareEqualTo )
bCheckUsersType = true;
for ( CFIndex i = 0; i < numInPatternsToMatch; i++ )
{
CFStringRef aPatternToMatch = (CFStringRef)CFArrayGetValueAtIndex( inPatternsToMatch, i );
if ( aPatternToMatch != NULL &&
CFGetTypeID(aPatternToMatch) == CFStringGetTypeID() &&
CFStringCompare(aPatternToMatch, CFSTR(kDSRecordsAll), 0) == kCFCompareEqualTo )
{
bGetAllRecords = true;
break;
}
}
if ( CFStringHasPrefix(inAttrTypeToMatch, CFSTR(kDSStdAttrTypePrefix)) == true )
nativeAttrToMatch = mPlugin->AttrNativeTypeForStandardType( inAttrTypeToMatch );
else if ( CFStringHasPrefix(inAttrTypeToMatch, CFSTR(kDSNativeAttrTypePrefix)) == true ) {
nativeAttrToMatch = nativeType = CFStringCreateMutableCopy( kCFAllocatorDefault, 0, inAttrTypeToMatch );
CFStringTrim( nativeType, CFSTR(kDSNativeAttrTypePrefix) ); }
else
nativeAttrToMatch = inAttrTypeToMatch;
if ( CFStringCompare( nativeAttrToMatch, CFSTR( "name" ), 0 ) == kCFCompareEqualTo )
{
if (bCheckUsersType)
bMatchRealName = true;
if (maxRecordsToGet == 1) {
CFArrayRef openRecordsOfThisType = mPlugin->CreateOpenRecordsOfTypeArray( inNativeRecType );
CFIndex numOpenRecords = 0;
if ( openRecordsOfThisType != NULL )
{
numOpenRecords = CFArrayGetCount( openRecordsOfThisType );
}
for ( CFIndex i = 0; ( maxRecordsToGet == 0 || numRecordsFound < maxRecordsToGet ) && ( i < numOpenRecords ); i++ )
{
CFMutableDictionaryRef aMutableRecordDict = (CFMutableDictionaryRef)CFArrayGetValueAtIndex( openRecordsOfThisType, i );
if ( bGetAllRecords ||
this->RecordMatchesCriteria( aMutableRecordDict, inPatternsToMatch, nativeAttrToMatch, inPatternMatch ) ||
( bMatchRealName && this->RecordMatchesCriteria( aMutableRecordDict, inPatternsToMatch, CFSTR("realname"), inPatternMatch ) ) )
{
CFArrayAppendValue( recordsArray, aMutableRecordDict );
numRecordsFound++;
CFArrayRef recordNames = (CFArrayRef)::CFDictionaryGetValue( aMutableRecordDict, mRecordNameAttrNativeName );
if ( recordNames == NULL || CFArrayGetCount( recordNames ) == 0 ) break;
CFStringRef recordName = (CFStringRef)CFArrayGetValueAtIndex( recordNames, 0 );
if ( recordName == NULL ) break;
CFStringRef recFilePath = CreateFilePathForRecord( inNativeRecType, recordName );
if (outRecFilePath != NULL)
{
*outRecFilePath = CFStringCreateCopy(NULL, recFilePath);
}
if ( recFilePath == NULL ) break;
this->FlushRecord( recFilePath, inNativeRecType, aMutableRecordDict );
DSCFRelease(recFilePath);
}
}
DSCFRelease( openRecordsOfThisType );
}
}
if (numRecordsFound < maxRecordsToGet)
{
mRecordTypeLock.WaitLock();
lockedRecordType = true;
CFMutableStringRef mutableRecTypeDirPathCFStr = CFStringCreateMutable( NULL, 0 );
if ( mutableRecTypeDirPathCFStr == NULL ) throw( eMemoryAllocError );
CFStringAppend( mutableRecTypeDirPathCFStr, mNodeDirFilePath );
CFStringAppend( mutableRecTypeDirPathCFStr, inNativeRecType );
if ( !CFStringGetCString(mutableRecTypeDirPathCFStr, thisRecTypeDirPathCStr, sizeof(thisRecTypeDirPathCStr), kCFStringEncodingUTF8) )
throw( eMemoryError );
DSCFRelease( mutableRecTypeDirPathCFStr );
#if FILE_ACCESS_INDEXING
if ( mUseIndex && (inPatternMatch == eDSExact || inPatternMatch == eDSiExact) && bGetAllRecords == false )
{
CFIndex iIndex = 0;
CFIndex iCount = CFArrayGetCount( inPatternsToMatch );
while ( iIndex < iCount && numRecordsFound < maxRecordsToGet )
{
CFStringRef cfValue = (CFStringRef) CFArrayGetValueAtIndex( inPatternsToMatch, iIndex );
char **filesToOpen = GetFileAccessIndex( inNativeRecType, inPatternMatch, cfValue, nativeAttrToMatch, &bIsIndexed );
if ( filesToOpen == NULL && bIsIndexed == true && bMatchRealName == true )
filesToOpen = GetFileAccessIndex( inNativeRecType, inPatternMatch, cfValue, CFSTR("realname"),
&bIsIndexed );
if ( bIsIndexed == false )
break;
if ( filesToOpen != NULL )
{
for ( int ii = 0; filesToOpen[ii] != NULL && numRecordsFound < maxRecordsToGet; ii++ )
{
char *theFile = filesToOpen[ii];
snprintf( thisFilePathCStr, sizeof(thisFilePathCStr), "%s/%s", thisRecTypeDirPathCStr, theFile );
struct stat statResult;
if( stat(thisFilePathCStr, &statResult) == 0 )
{
fileData = CreateCFDataFromFile( thisFilePathCStr, statResult.st_size );
if ( fileData != NULL )
{
mutableRecordDict = (CFMutableDictionaryRef) CFPropertyListCreateFromXMLData( NULL, fileData,
kCFPropertyListMutableContainers, NULL );
DSCFRelease( fileData );
if ( mutableRecordDict != NULL )
{
if ( bGetAllRecords ||
this->RecordMatchesCriteria( mutableRecordDict, inPatternsToMatch, nativeAttrToMatch, inPatternMatch ) ||
( bMatchRealName && this->RecordMatchesCriteria( mutableRecordDict, inPatternsToMatch, CFSTR("realname"), inPatternMatch ) ) )
{
CFArrayAppendValue( recordsArray, mutableRecordDict );
numRecordsFound++;
if ( (maxRecordsToGet == 1) && (outRecFilePath != NULL) )
{
*outRecFilePath = CFStringCreateWithCString( NULL, thisFilePathCStr, kCFStringEncodingUTF8 );
}
}
DSCFRelease( mutableRecordDict );
}
}
}
else
{
CFStringRef cfStdType = mPlugin->RecordStandardTypeForNativeType( inNativeRecType );
char *cStr2 = NULL;
DeleteRecordIndex( CStrFromCFString(cfStdType, &cStr2, NULL, NULL), theFile );
DSFree( cStr2 );
}
}
DSFreeStringList( filesToOpen );
}
iIndex++;
}
}
if ( bIsIndexed == false )
{
#endif
if ( (numRecordsFound < maxRecordsToGet) &&
( (CFArrayGetCount( inPatternsToMatch ) == 1) && (inPatternMatch == eDSExact) ) &&
( CFStringCompare( nativeAttrToMatch, CFSTR( "name" ), 0 ) == kCFCompareEqualTo ) &&
( CFStringCompare( (CFStringRef)CFArrayGetValueAtIndex(inPatternsToMatch, 0), CFSTR( "dsRecordsAll" ), 0 ) != kCFCompareEqualTo ) )
{
char* cStr = NULL;
const char* aString = CStrFromCFString( (CFStringRef)CFArrayGetValueAtIndex(inPatternsToMatch, 0), &cStr, NULL, NULL );
if (aString != NULL)
{
snprintf( thisFilePathCStr, sizeof(thisFilePathCStr), "%s/%s.plist", thisRecTypeDirPathCStr, aString );
struct stat statResult;
if ( stat(thisFilePathCStr, &statResult) == 0 &&
(fileData = CreateCFDataFromFile(thisFilePathCStr, statResult.st_size)) != NULL )
{
mutableRecordDict = (CFMutableDictionaryRef)::CFPropertyListCreateFromXMLData( NULL, fileData, kCFPropertyListMutableContainers, NULL );
DSCFRelease( fileData );
if ( mutableRecordDict != NULL )
{
if ( bGetAllRecords ||
this->RecordMatchesCriteria( mutableRecordDict, inPatternsToMatch, nativeAttrToMatch, inPatternMatch ) ||
( bMatchRealName && this->RecordMatchesCriteria( mutableRecordDict, inPatternsToMatch, CFSTR("realname"), inPatternMatch ) ) )
{
CFArrayAppendValue( recordsArray, mutableRecordDict );
numRecordsFound++;
bContinueSearch = false;
if ( (maxRecordsToGet == 1) && (outRecFilePath != NULL) )
{
*outRecFilePath = CFStringCreateWithCString( NULL, thisFilePathCStr, kCFStringEncodingUTF8 );
}
}
DSCFRelease( mutableRecordDict );
}
}
}
DSFreeString(cStr);
}
if ( (numRecordsFound < maxRecordsToGet) && bContinueSearch )
{
for ( recTypeDir = opendir( thisRecTypeDirPathCStr ); ( recTypeDir != NULL ) && ( maxRecordsToGet == 0 || numRecordsFound < maxRecordsToGet ) && ( theDirEnt = readdir( recTypeDir ) ); )
{
char recFileSuffix[] = ".plist";
long suffixOffset = strlen( theDirEnt->d_name )-strlen( recFileSuffix );
char *fileSuffix = "<no suffix>";
if ( suffixOffset > 0 )
{
fileSuffix = &theDirEnt->d_name[suffixOffset];
}
if ( ( suffixOffset <= 0 ) || ( ::strcmp( recFileSuffix, fileSuffix ) != 0 ) ) continue;
snprintf( thisFilePathCStr, sizeof(thisFilePathCStr), "%s/%s", thisRecTypeDirPathCStr, theDirEnt->d_name );
struct stat statResult;
if ( stat(thisFilePathCStr, &statResult) == 0 &&
(fileData = CreateCFDataFromFile( thisFilePathCStr, statResult.st_size)) != NULL )
{
mutableRecordDict = (CFMutableDictionaryRef)::CFPropertyListCreateFromXMLData( NULL, fileData, kCFPropertyListMutableContainers, NULL );
DSCFRelease( fileData );
if ( mutableRecordDict != NULL )
{
if ( bGetAllRecords ||
this->RecordMatchesCriteria( mutableRecordDict, inPatternsToMatch, nativeAttrToMatch, inPatternMatch ) ||
( bMatchRealName && this->RecordMatchesCriteria( mutableRecordDict, inPatternsToMatch, CFSTR("realname"), inPatternMatch ) ) )
{
CFArrayAppendValue( recordsArray, mutableRecordDict );
numRecordsFound++;
if ( (maxRecordsToGet == 1) && (outRecFilePath != NULL) )
{
*outRecFilePath = CFStringCreateWithCString( NULL, thisFilePathCStr, kCFStringEncodingUTF8 );
}
}
DSCFRelease( mutableRecordDict );
}
}
}
}
#if FILE_ACCESS_INDEXING
} #endif
DbgLog( kLogDebug, "CDSLocalPluginNode::GetRecords(): returned %d records from directory %s", numRecordsFound, thisRecTypeDirPathCStr );
mRecordTypeLock.SignalLock();
lockedRecordType = false;
} }
catch( tDirStatus error )
{
DbgLog( kLogPlugin, "CDSLocalPluginNode::GetRecords(): failed with error %d", error );
siResult = error;
}
catch( ... )
{
siResult = eUndefinedError;
}
if ( lockedRecordType )
mRecordTypeLock.SignalLock();
DSCFRelease(mutableRecordDict);
DSCFRelease(fileData);
DSCFRelease( nativeType );
if ( recTypeDir != NULL )
{
closedir( recTypeDir );
recTypeDir = NULL;
}
return siResult;
}
CFStringRef CDSLocalPluginNode::CreateFilePathForRecord( CFStringRef inNativeRecType, CFStringRef inRecordName )
{
CFMutableStringRef recordNameForFS = CFStringCreateMutableCopy( NULL, 0, inRecordName );
CFStringFindAndReplace( recordNameForFS, CFSTR("/"), CFSTR("%2F"), CFRangeMake(0, CFStringGetLength(inRecordName)), 0 );
CFStringRef recFilePath = CFStringCreateWithFormat( NULL, NULL, CFSTR( "%@%@/%@.plist" ), mNodeDirFilePath,
inNativeRecType, recordNameForFS );
CFRelease( recordNameForFS );
return recFilePath;
}
tDirStatus CDSLocalPluginNode::CreateDictionaryForRecord( CFStringRef inNativeRecType, CFStringRef inRecordName,
CFMutableDictionaryRef* outMutableRecordDict, CFStringRef* outRecordFilePath )
{
tDirStatus siResult = eDSNoErr;
CFMutableArrayRef recordsArray = NULL;
CFArrayRef patternsToMatch = NULL;
try
{
recordsArray = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
patternsToMatch = CFArrayCreate( NULL, (const void**)&inRecordName, 1, &kCFTypeArrayCallBacks );
this->GetRecords( inNativeRecType, patternsToMatch, mPlugin->AttrNativeTypeForStandardType( CFSTR( kDSNAttrRecordName ) ), eDSExact, true, 1, recordsArray, false, outRecordFilePath );
*outMutableRecordDict = NULL;
if ( CFArrayGetCount( recordsArray ) == 1 )
*outMutableRecordDict = CFDictionaryCreateMutableCopy(NULL, 0, (CFDictionaryRef)CFArrayGetValueAtIndex( recordsArray, 0 ));
if ( *outMutableRecordDict == NULL )
throw( eDSRecordNotFound );
}
catch( tDirStatus error )
{
DbgLog( kLogDebug, "CDSLocalPluginNode::CreateDictionaryForRecord(): failed with error %d", error );
siResult = error;
}
DSCFRelease( patternsToMatch );
DSCFRelease( recordsArray );
return siResult;
}
tDirStatus CDSLocalPluginNode::CreateDictionaryForNewRecord( CFStringRef inNativeRecType, CFStringRef inRecordName,
CFMutableDictionaryRef* outMutableRecordDict, CFStringRef* outRecordFilePath )
{
tDirStatus siResult = eDSNoErr;
char* cStr = NULL;
char* cStr2 = NULL;
size_t cStrSize = 0;
CFMutableDictionaryRef mutableAttrsValuesDict = NULL;
CFStringRef recFilePath = NULL;
CFMutableArrayRef mutableAttrValues = NULL;
CFArrayRef automountMapArray = NULL;
CFStringRef metaInformation = NULL;
if ( inNativeRecType == NULL )
return eDSInvalidRecordType;
try
{
if ( CFStringCompare( inNativeRecType, CFSTR("automount"), 0 ) == kCFCompareEqualTo )
{
automountMapArray = CFStringCreateArrayBySeparatingStrings( NULL, inRecordName, CFSTR(",automountMapName=") );
if ( automountMapArray == NULL || CFArrayGetCount(automountMapArray) != 2 ) throw eDSInvalidRecordName;
metaInformation = (CFStringRef) CFArrayGetValueAtIndex( automountMapArray, 1 );
if ( CFStringGetLength(metaInformation) == 0 ) throw eDSInvalidRecordName;
}
recFilePath = CreateFilePathForRecord( inNativeRecType, inRecordName );
if ( recFilePath == NULL )
throw( eMemoryAllocError );
const char* recFilePathCStr = CStrFromCFString( recFilePath, &cStr, &cStrSize, NULL );
struct stat statBuffer={};
int result = ::stat( recFilePathCStr, &statBuffer );
if ( result != 0 )
result = errno;
switch ( result )
{
case ENOENT:
break;
case ENAMETOOLONG:
DbgLog( kLogPlugin, "CDSLocalPluginNode::CreateDictionaryForNewRecord(), file name is too long: %s",
recFilePathCStr );
throw( eDSInvalidRecordName );
break;
default:
DbgLog( kLogPlugin, "CDSLocalPluginNode::CreateDictionaryForNewRecord(), file at %s error, stat() result = %d",
recFilePathCStr, result );
throw( eDSRecordAlreadyExists );
}
mutableAttrsValuesDict = ::CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks );
if ( mutableAttrsValuesDict == NULL )
throw( eMemoryAllocError );
mutableAttrValues = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
if ( mutableAttrValues == NULL )
throw( eMemoryAllocError );
CFArrayAppendValue( mutableAttrValues, inRecordName );
::CFDictionaryAddValue( mutableAttrsValuesDict, mRecordNameAttrNativeName, mutableAttrValues );
::CFRelease( mutableAttrValues );
mutableAttrValues = NULL;
if ( metaInformation != NULL )
{
mutableAttrValues = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
if ( mutableAttrValues == NULL )
throw( eMemoryAllocError );
CFArrayAppendValue( mutableAttrValues, metaInformation );
CFDictionaryAddValue( mutableAttrsValuesDict, CFSTR("metaautomountmap"), mutableAttrValues );
CFRelease( mutableAttrValues );
mutableAttrValues = NULL;
}
this->FlushRecord( recFilePath, inNativeRecType, mutableAttrsValuesDict );
if ( CFStringCompare(inNativeRecType, CFSTR("users"), 0) == kCFCompareEqualTo )
{
const char* recNameCStr = CStrFromCFString( inRecordName, &cStr, &cStrSize, NULL );
if (recNameCStr == NULL) recNameCStr = "<unknown>";
CFMutableDictionaryRef eventDict = dsCreateEventLogDict( CFSTR("user.created"), recNameCStr, NULL );
if ( eventDict != NULL ) {
dsPostEvent( CFSTR("user.created"), eventDict );
CFRelease( eventDict );
}
}
const char* nativeType = CStrFromCFString( inNativeRecType, &cStr2, NULL, NULL );
dsNotifyUpdatedRecord( "Local", NULL, nativeType );
if ( outMutableRecordDict != NULL )
{
*outMutableRecordDict = mutableAttrsValuesDict;
mutableAttrsValuesDict = NULL; }
if ( outRecordFilePath!= NULL )
{
*outRecordFilePath = recFilePath;
recFilePath = NULL; }
}
catch( tDirStatus error )
{
DbgLog( kLogPlugin, "CDSLocalPluginNode::CreateDictionaryForNewRecord(): failed with error %d", error );
siResult = error;
}
DSFree( cStr2 );
if ( cStr != NULL )
{
free( cStr );
cStr = NULL;
cStrSize = 0;
}
if ( mutableAttrsValuesDict != NULL )
{
::CFRelease( mutableAttrsValuesDict );
mutableAttrsValuesDict = NULL;
}
if ( recFilePath != NULL )
{
::CFRelease( recFilePath );
recFilePath = NULL;
}
if ( mutableAttrValues != NULL )
{
::CFRelease( mutableAttrValues );
mutableAttrValues = NULL;
}
if ( automountMapArray != NULL )
{
CFRelease( automountMapArray );
automountMapArray = NULL;
}
return siResult;
}
tDirStatus
CDSLocalPluginNode::DeleteRecord( CFStringRef inRecordFilePath, CFStringRef inNativeRecType,
CFStringRef inRecordName, CFMutableDictionaryRef inMutableRecordAttrsValues )
{
tDirStatus siResult = eDSNoErr;
char *cStr = NULL;
size_t cStrSize = 0;
bool isUserRecord = false;
const char* recNameCStr = NULL;
CFArrayRef attrArray = NULL;
CFStringRef krbCertAA = NULL;
if ( inNativeRecType == NULL )
return eDSNullRecType;
if ( inRecordName != NULL )
CFRetain( inRecordName );
mRecordTypeLock.WaitLock();
this->UpdateModValue();
this->RemoveShadowHashFilesIfNecessary( CFSTR(kGeneratedUIDStr), inMutableRecordAttrsValues );
const char* recordFilePathCStr = CStrFromCFString( inRecordFilePath, &cStr, &cStrSize, NULL );
DbgLog( kLogPlugin, "CDSLocalPluginNode::DeleteRecord(): deleting file \"%s\"", recordFilePathCStr );
if ( unlink( recordFilePathCStr ) != 0 )
{
DbgLog( kLogError, "CDSLocalPluginNode::DeleteRecord(): unlink() for \"%s\" failed with error %d",
recordFilePathCStr, errno );
siResult = ePlugInDataError;
}
isUserRecord = (CFStringCompare(inNativeRecType, CFSTR("users"), 0) == kCFCompareEqualTo);
#if FILE_ACCESS_INDEXING
if ( mUseIndex && siResult == eDSNoErr )
{
char *cStr2 = NULL;
char *fileName = strrchr( recordFilePathCStr, '/' );
if ( fileName != NULL )
{
CFStringRef cfStdType = mPlugin->RecordStandardTypeForNativeType( inNativeRecType );
if ( cfStdType != NULL )
DeleteRecordIndex( CStrFromCFString(cfStdType, &cStr2, NULL, NULL), ++fileName );
else
siResult = eDSInvalidRecordType;
DSFreeString( cStr2 );
}
}
#endif
mRecordTypeLock.SignalLock();
if ( isUserRecord )
{
recNameCStr = CStrFromCFString( inRecordName, &cStr, &cStrSize, NULL );
if (recNameCStr == NULL) recNameCStr = "<unknown>";
CFMutableDictionaryRef eventDict = dsCreateEventLogDict( CFSTR("user.deleted"), recNameCStr, NULL );
if ( eventDict != NULL ) {
dsPostEvent( CFSTR("user.deleted"), eventDict );
CFRelease( eventDict );
}
if ( inRecordName != NULL )
{
char *localKDCRealmStr = GetLocalKDCRealmWithCache( kLocalKDCRealmCacheTimeout );
if ( localKDCRealmStr != NULL )
{
if ( mPlugin->mPWSFrameworkAvailable )
{
pwsf_DeletePrincipalInLocalRealm( recNameCStr, localKDCRealmStr );
attrArray = (CFArrayRef) CFDictionaryGetValue( inMutableRecordAttrsValues, CFSTR(kAuthAuthorityStr) );
if ( attrArray != NULL )
{
krbCertAA = GetTagInArray( attrArray, CFSTR(kDSValueAuthAuthorityKerberosv5Cert) );
if ( krbCertAA != NULL )
{
CAuthAuthority aaStorage;
if ( aaStorage.AddValue(krbCertAA) )
{
char *princStr = aaStorage.GetDataForTag( kDSTagAuthAuthorityKerberosv5Cert, 1 );
if ( princStr != NULL )
{
char *endPtr = strchr( princStr, '@' );
if ( endPtr != NULL )
{
*endPtr = '\0';
pwsf_DeletePrincipalInLocalRealm( princStr, localKDCRealmStr );
}
free( princStr );
}
}
}
}
}
else
{
syslog( LOG_NOTICE, "Unable to delete principal for record <%s> because PasswordServer.framework is missing", recNameCStr );
DbgLog( kLogNotice, "Unable to delete principal for record <%s> because PasswordServer.framework is missing", recNameCStr );
}
DSFree( localKDCRealmStr );
}
}
}
DSFreeString( cStr );
if ( inRecordName != NULL )
CFRelease( inRecordName );
return siResult;
}
tDirStatus CDSLocalPluginNode::FlushRecord( CFStringRef inRecordFilePath, CFStringRef inRecordType, CFDictionaryRef inRecordDict )
{
tDirStatus siResult = eDSNoErr;
CFMutableStringRef mutableTempFilePath = NULL;
CFStringRef recordTypeDirPath = NULL;
CFDataRef recordDictData = NULL;
CFStringRef changedNameRecordFilePath = NULL;
FILE* tempFile = NULL;
char* cStr = NULL;
size_t cStrSize = 0;
char* cStr2 = NULL;
size_t cStrSize2 = 0;
bool needRecordTypeDirPath = false;
CFDictionaryRef recordDict = inRecordDict;
CFMutableDictionaryRef recordMutableDict = NULL;
mOpenRecordsLock.WaitLock();
mRecordTypeLock.WaitLock();
try
{
if ( CFDictionaryContainsKey(inRecordDict, CFSTR(kDSNAttrMetaNodeLocation)) )
{
recordMutableDict = CFDictionaryCreateMutableCopy( NULL, 0, inRecordDict );
if ( recordMutableDict == NULL ) throw eMemoryAllocError;
CFDictionaryRemoveValue( recordMutableDict, CFSTR(kDSNAttrMetaNodeLocation) );
recordDict = recordMutableDict;
}
bool zapPassword = false;
CFArrayRef aaArray = (CFArrayRef) CFDictionaryGetValue( inRecordDict, CFSTR(kAuthAuthorityStr) );
if ( aaArray != NULL )
{
CFIndex aaArrayCount = CFArrayGetCount( aaArray );
if ( aaArrayCount > 1 )
{
zapPassword = true;
}
else if ( aaArrayCount == 1 )
{
CFStringRef aaString = (CFStringRef) CFArrayGetValueAtIndex( aaArray, 0 );
if ( aaString != NULL &&
CFStringCompare(aaString, CFSTR(kDSValueAuthAuthorityBasic), kCFCompareCaseInsensitive) != kCFCompareEqualTo )
{
zapPassword = true;
}
}
}
if ( zapPassword )
{
if ( recordDict != recordMutableDict )
{
recordMutableDict = CFDictionaryCreateMutableCopy( NULL, 0, inRecordDict );
if ( recordMutableDict == NULL ) throw eMemoryAllocError;
recordDict = recordMutableDict;
}
CFTypeRef passVal = (CFTypeRef)CFSTR( kDSValueNonCryptPasswordMarker );
CFArrayRef passArray = CFArrayCreate( NULL, &passVal, 1, &kCFTypeArrayCallBacks );
CFDictionarySetValue( recordMutableDict, CFSTR("passwd"), passArray );
CFRelease( passArray );
}
recordDictData = CFPropertyListCreateXMLData( NULL, recordDict );
if ( recordDictData == NULL )
throw( eMemoryAllocError );
mutableTempFilePath = CFStringCreateMutableCopy( NULL, 0, inRecordFilePath );
if ( mutableTempFilePath == NULL )
throw( eMemoryAllocError );
CFStringAppend( mutableTempFilePath, CFSTR( ".temp" ) );
const char* tempFilePathCStr = CStrFromCFString( mutableTempFilePath, &cStr, &cStrSize, NULL );
tempFile = ::fopen( tempFilePathCStr, "w" );
if ( tempFile != NULL )
{
int rc = ::chmod( tempFilePathCStr, S_IRUSR | S_IWUSR );
if ( rc != 0 )
DbgLog( kLogError, "CDSLocalPluginNode::FlushRecord() got an error %d while trying to chmod %s",
errno, tempFilePathCStr );
}
if ( tempFile == NULL )
needRecordTypeDirPath = true;
if ( needRecordTypeDirPath )
{
CFRange lastSlashRange = CFStringFind( inRecordFilePath, CFSTR( "/" ), kCFCompareBackwards );
if ( lastSlashRange.location == kCFNotFound )
{
DbgLog( kLogError, "CDSLocalPluginNode::FlushRecord(): no / found in inRecordFilePath" );
throw( ePlugInDataError );
}
CFRange recordTypeDirRange = CFRangeMake( 0, lastSlashRange.location );
recordTypeDirPath = CFStringCreateWithSubstring( NULL, inRecordFilePath, recordTypeDirRange );
if ( recordTypeDirPath == NULL )
throw( eMemoryAllocError );
}
if ( tempFile == NULL )
{
const char* recordTypeDirCStr = CStrFromCFString( recordTypeDirPath, &cStr2, &cStrSize2, NULL );
if ( EnsureDirs(recordTypeDirCStr, 0700, 0700) == 0 )
{
tempFile = ::fopen( tempFilePathCStr, "w" );
if ( tempFile == NULL )
{
DbgLog( kLogError, "CDSLocalPluginNode::FlushRecord(): fopen() returned NULL while attempting to open \'w\' \"%s\"",
tempFilePathCStr );
throw( ePlugInDataError );
}
else
{
int rc = ::chmod( tempFilePathCStr, S_IRUSR | S_IWUSR );
if ( rc != 0 )
DbgLog( kLogPlugin, "CDSLocalPluginNode::FlushRecord() got an error %d while trying to chmod %s",
errno, tempFilePathCStr );
}
}
else
{
DbgLog( kLogError, "CDSLocalPluginNode::FlushRecord() failed to create Directory structure %s", recordTypeDirCStr );
throw( ePlugInDataError );
}
}
size_t numBlocksWritten = ::fwrite( ::CFDataGetBytePtr( recordDictData ),
(size_t)::CFDataGetLength( recordDictData ), 1, tempFile );
::fclose( tempFile );
tempFile = NULL;
if ( numBlocksWritten != 1 )
{
DbgLog( kLogPlugin, "CDSLocalPluginNode::FlushRecord(): fwrite() returned unexpected number of blocks written: %d",
numBlocksWritten );
throw( ePlugInDataError );
}
const char* recFilePathCStr = CStrFromCFString( inRecordFilePath, &cStr2, &cStrSize2, NULL );
if ( ::rename( tempFilePathCStr, recFilePathCStr ) != 0 )
{
DbgLog( kLogPlugin, "CDSLocalPluginNode::FlushRecord(): rename() failed to move temp file into proper place" );
throw( ePlugInDataError );
}
CFArrayRef recordNames = (CFArrayRef)::CFDictionaryGetValue( recordDict, mRecordNameAttrNativeName );
if ( recordNames == NULL || CFArrayGetCount( recordNames ) == 0 )
throw( ePlugInDataError );
CFStringRef recordName = (CFStringRef)CFArrayGetValueAtIndex( recordNames, 0 );
if ( recordName == NULL )
throw( ePlugInDataError );
changedNameRecordFilePath = CreateFilePathForRecord( inRecordType, recordName );
if ( changedNameRecordFilePath == NULL )
throw( eMemoryAllocError );
bool nameChanged = false;
const char* newFileName = CStrFromCFString( changedNameRecordFilePath, &cStr, &cStrSize, NULL );
if ( CFStringCompare( changedNameRecordFilePath, inRecordFilePath, 0 ) != kCFCompareEqualTo )
nameChanged = true;
if ( nameChanged && rename(recFilePathCStr, newFileName) != 0 )
{
DbgLog( kLogPlugin, "CDSLocalPluginNode::FlushRecord(): rename() failed to rename file for changed record name" );
throw( ePlugInDataError );
}
#if FILE_ACCESS_INDEXING
if ( mFileAccessIndexPtr != NULL )
{
char *pNewName = strrchr( newFileName, '/' );
if ( pNewName != NULL )
{
CFStringRef cfStdType = mPlugin->RecordStandardTypeForNativeType( inRecordType );
if ( cfStdType != NULL )
{
char *cStr3 = NULL;
const char *stdRecType = CStrFromCFString( cfStdType, &cStr3, NULL, NULL );
if ( nameChanged )
{
char *pOldName = strrchr( recFilePathCStr, '/' );
if ( pOldName != NULL )
DeleteRecordIndex( stdRecType, ++pOldName );
}
AddRecordIndex( stdRecType, ++pNewName );
DSFree( cStr3 );
}
}
}
#endif
}
catch( tDirStatus err )
{
DbgLog( kLogPlugin, "CDSLocalPluginNode::FlushRecord(): caught error %d", err );
siResult = err;
}
catch( ... )
{
siResult = eUndefinedError;
}
mRecordTypeLock.SignalLock();
mOpenRecordsLock.SignalLock();
DSCFRelease( recordMutableDict );
DSFreeString( cStr );
DSFreeString( cStr2 );
if ( tempFile != NULL )
{
::fclose( tempFile );
tempFile = NULL;
}
if ( recordDictData != NULL )
{
::CFRelease( recordDictData );
recordDictData = NULL;
}
if ( mutableTempFilePath != NULL )
{
::CFRelease( mutableTempFilePath );
mutableTempFilePath = NULL;
}
if ( recordTypeDirPath != NULL )
{
::CFRelease( recordTypeDirPath );
recordTypeDirPath = NULL;
}
if ( changedNameRecordFilePath != NULL )
{
::CFRelease( changedNameRecordFilePath );
changedNameRecordFilePath = NULL;
}
return siResult;
}
tDirStatus CDSLocalPluginNode::AddAttributeToRecord( CFStringRef inNativeAttrType, CFStringRef inNativeRecType, CFTypeRef inAttrValue, CFMutableDictionaryRef inMutableRecordAttrsValues )
{
tDirStatus siResult = eDSNoErr;
CFMutableArrayRef mutableAttrValues = NULL;
if ( inAttrValue != NULL )
{
siResult = this->AttributeValueMatchesUserAlias( inNativeRecType, inNativeAttrType, inAttrValue, inMutableRecordAttrsValues );
if ( siResult != eDSNoErr )
return siResult;
}
mOpenRecordsLock.WaitLock();
this->UpdateModValue();
try
{
mutableAttrValues = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
if ( inAttrValue != NULL )
CFArrayAppendValue( mutableAttrValues, inAttrValue );
CFDictionarySetValue( inMutableRecordAttrsValues, inNativeAttrType, mutableAttrValues );
}
catch( tDirStatus err )
{
DbgLog( kLogPlugin, "CDSLocalPluginNode::AddAttributeToRecord(): caught error %d", err );
siResult = err;
}
mOpenRecordsLock.SignalLock();
if ( mutableAttrValues != NULL )
{
::CFRelease( mutableAttrValues );
mutableAttrValues = NULL;
}
return siResult;
}
tDirStatus CDSLocalPluginNode::AddAttributeValueToRecord( CFStringRef inNativeAttrType, CFStringRef inNativeRecType, CFTypeRef inAttrValue, CFMutableDictionaryRef inMutableRecordAttrsValues )
{
tDirStatus siResult = eDSNoErr;
if ( inAttrValue == NULL )
return siResult;
siResult = this->AttributeValueMatchesUserAlias( inNativeRecType, inNativeAttrType, inAttrValue, inMutableRecordAttrsValues );
if ( siResult != eDSNoErr )
return siResult;
mOpenRecordsLock.WaitLock();
this->UpdateModValue();
try
{
if ( ::CFDictionaryGetCountOfKey( inMutableRecordAttrsValues, inNativeAttrType ) == 1 )
{
CFMutableArrayRef mutableAttrValues = (CFMutableArrayRef)::CFDictionaryGetValue( inMutableRecordAttrsValues,
inNativeAttrType );
if ( mutableAttrValues == NULL )
throw( ePlugInDataError );
CFArrayAppendValue( mutableAttrValues, inAttrValue );
}
else
{
CFMutableArrayRef mutableAttrValues = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
if ( mutableAttrValues == NULL )
throw( eMemoryAllocError );
CFArrayAppendValue( mutableAttrValues, inAttrValue );
::CFDictionaryAddValue( inMutableRecordAttrsValues, inNativeAttrType, mutableAttrValues );
::CFRelease( mutableAttrValues );
}
}
catch( tDirStatus err )
{
DbgLog( kLogPlugin, "CDSLocalPluginNode::AddAttributeValueToRecord(): caught error %d", err );
siResult = err;
}
mOpenRecordsLock.SignalLock();
return siResult;
}
tDirStatus CDSLocalPluginNode::RemoveAttributeFromRecord( CFStringRef inNativeAttrType, CFStringRef inNativeRecType,
CFMutableDictionaryRef inMutableRecordAttrsValues )
{
tDirStatus siResult = eDSNoErr;
mOpenRecordsLock.WaitLock();
this->UpdateModValue();
try
{
if ( CFDictionaryGetCountOfKey( inMutableRecordAttrsValues, inNativeAttrType ) == 1 )
{
this->RemoveShadowHashFilesIfNecessary( inNativeAttrType, inMutableRecordAttrsValues );
CFDictionaryRemoveValue( inMutableRecordAttrsValues, inNativeAttrType );
}
}
catch( tDirStatus err )
{
DbgLog( kLogPlugin, "CDSLocalPluginNode::RemoveAttributeFromRecord(): caught error %d", err );
siResult = err;
}
mOpenRecordsLock.SignalLock();
return siResult;
}
tDirStatus CDSLocalPluginNode::RemoveAttributeValueFromRecordByCRC( CFStringRef inNativeAttrType,
CFStringRef inNativeRecType, CFMutableDictionaryRef inMutableRecordAttrsValues, unsigned long inCRC )
{
tDirStatus siResult = eDSNoErr;
char* cStr = NULL;
CFStringRef* values = NULL;
mOpenRecordsLock.WaitLock();
this->UpdateModValue();
try
{
CFMutableArrayRef mutableAttrValues = (CFMutableArrayRef)::CFDictionaryGetValue( inMutableRecordAttrsValues,
inNativeAttrType );
if ( mutableAttrValues == NULL )
throw( eDSAttributeNotFound );
CFIndex numValues = CFArrayGetCount( mutableAttrValues );
values = (CFStringRef*)::malloc( sizeof( CFStringRef ) * numValues );
if ( values == NULL )
throw( eMemoryAllocError );
CFArrayGetValues( mutableAttrValues, CFRangeMake( 0, numValues ), (const void**)values );
for ( CFIndex i=0; i<numValues; i++ )
{
if ( CalcCRC( CStrFromCFString( values[i], &cStr, NULL, NULL ) ) == inCRC )
{
if ( CFStringCompare(inNativeAttrType, CFSTR(kGeneratedUIDStr), 0) == kCFCompareEqualTo )
{
this->RemoveShadowHashFilesIfNecessary( inNativeAttrType, inMutableRecordAttrsValues );
}
else if ( CFStringCompare(inNativeAttrType, CFSTR(kAuthAuthorityStr), 0) == kCFCompareEqualTo )
{
if ( !this->ArrayContainsShadowHashOrLocalCachedUser(mutableAttrValues) )
this->RemoveShadowHashFilesIfNecessary( inNativeAttrType, inMutableRecordAttrsValues );
this->SetKerberosTicketsEnabledOrDisabled( mutableAttrValues );
}
CFArrayRemoveValueAtIndex( mutableAttrValues, i );
break;
}
}
}
catch( tDirStatus err )
{
DbgLog( kLogPlugin, "CDSLocalPluginNode::RemoveAttributeValueFromRecordByCRC(): caught error %d", err );
siResult = err;
}
mOpenRecordsLock.SignalLock();
if ( cStr != NULL )
{
::free( cStr );
cStr = NULL;
}
if ( values != NULL )
{
::free( values );
values = NULL;
}
return siResult;
}
tDirStatus CDSLocalPluginNode::ReplaceAttributeValueInRecordByCRC( CFStringRef inNativeAttrType,
CFStringRef inNativeRecType, CFMutableDictionaryRef inMutableRecordAttrsValues, unsigned long inCRC,
CFTypeRef inNewValue )
{
tDirStatus siResult = eDSNoErr;
char* cStr = NULL;
CFTypeRef* values = NULL;
CFArrayRef automountMapArray = NULL;
siResult = this->AttributeValueMatchesUserAlias( inNativeRecType, inNativeAttrType, inNewValue, inMutableRecordAttrsValues );
if ( siResult != eDSNoErr )
return siResult;
mOpenRecordsLock.WaitLock();
this->UpdateModValue();
try
{
CFMutableArrayRef mutableAttrValues = (CFMutableArrayRef)::CFDictionaryGetValue( inMutableRecordAttrsValues,
inNativeAttrType );
if ( mutableAttrValues == NULL )
throw( eDSAttributeNotFound );
CFIndex numValues = CFArrayGetCount( mutableAttrValues );
values = (CFTypeRef*)::malloc( sizeof( CFTypeRef ) * numValues );
if ( values == NULL )
throw( eMemoryAllocError );
CFArrayGetValues( mutableAttrValues, CFRangeMake( 0, numValues ), (const void**)values );
for ( CFIndex i = 0; i < numValues; i++ )
{
size_t attrValueLen = 0;
UInt32 aCRCValue = 0;
const void *dataValue = NULL;
CFTypeID attrValueTypeID = CFGetTypeID( values[i] );
if ( attrValueTypeID == CFStringGetTypeID() )
{
const char* attrValueCStr = CStrFromCFString( (CFStringRef)values[i], &cStr, NULL, NULL );
if ( attrValueCStr == NULL ) throw( eMemoryAllocError );
attrValueLen = strlen( attrValueCStr );
dataValue = attrValueCStr;
aCRCValue = CalcCRCWithLength( attrValueCStr, attrValueLen );
}
else {
attrValueLen = (size_t) CFDataGetLength( (CFDataRef)values[i] );
dataValue = CFDataGetBytePtr( (CFDataRef)values[i] );
aCRCValue = CalcCRCWithLength( dataValue, attrValueLen );
}
if ( aCRCValue == inCRC )
{
if ( i == 0 && CFStringCompare(CFSTR("automount"), inNativeRecType, 0) == kCFCompareEqualTo &&
CFStringCompare(CFSTR("name"), inNativeAttrType, 0) == kCFCompareEqualTo )
{
if ( inNewValue == NULL || CFGetTypeID(inNewValue) != CFStringGetTypeID() ) throw eDSInvalidRecordName;
automountMapArray = CFStringCreateArrayBySeparatingStrings( NULL, (CFStringRef) inNewValue, CFSTR(",automountMapName=") );
if ( automountMapArray == NULL || CFArrayGetCount(automountMapArray) != 2 ) throw eDSInvalidRecordName;
CFStringRef metaInformation = (CFStringRef) CFArrayGetValueAtIndex( automountMapArray, 1 );
if ( CFStringGetLength(metaInformation) == 0 ) throw eDSInvalidRecordName;
CFArrayRef cfMetaInfoArray = CFArrayCreate( NULL, (const void **) &metaInformation, 1, &kCFTypeArrayCallBacks );
CFDictionarySetValue( inMutableRecordAttrsValues, CFSTR("metaautomountmap"), cfMetaInfoArray );
CFRelease( cfMetaInfoArray );
}
CFStringRef pathString = this->GetShadowHashFilePath( inNativeAttrType, inMutableRecordAttrsValues );
if ( CFStringCompare(inNativeAttrType, CFSTR(kGeneratedUIDStr), 0) == kCFCompareEqualTo )
siResult = this->RenameShadowHashFiles( pathString, (CFStringRef)inNewValue );
if ( siResult == eDSNoErr )
{
if ( inNewValue == NULL ||
(CFGetTypeID(inNewValue) == CFDataGetTypeID() && CFDataGetLength((CFDataRef)inNewValue) == 0) ||
(CFGetTypeID(inNewValue) == CFStringGetTypeID() && CFStringGetLength((CFStringRef)inNewValue) == 0) )
{
CFArrayRemoveValueAtIndex( mutableAttrValues, i );
}
else
{
CFArrayReplaceValues( mutableAttrValues, CFRangeMake(i, 1), (const void**)&inNewValue, 1 );
}
}
if ( CFStringCompare(inNativeAttrType, CFSTR(kAuthAuthorityStr), 0) == kCFCompareEqualTo )
{
if ( pathString != NULL && !this->ArrayContainsShadowHashOrLocalCachedUser(mutableAttrValues) )
this->RemoveShadowHashFilesWithPath( pathString );
this->SetKerberosTicketsEnabledOrDisabled( mutableAttrValues );
}
DSCFRelease( pathString );
break;
}
}
}
catch( tDirStatus err )
{
DbgLog( kLogPlugin, "CDSLocalPluginNode::ReplaceAttributeValueInRecordByCRC(): caught error %d", err );
siResult = err;
}
mOpenRecordsLock.SignalLock();
DSFreeString( cStr );
DSFree( values );
DSCFRelease( automountMapArray );
return siResult;
}
tDirStatus CDSLocalPluginNode::ReplaceAttributeValueInRecordByIndex( CFStringRef inNativeAttrType,
CFStringRef inNativeRecType, CFMutableDictionaryRef inMutableRecordAttrsValues, unsigned long inIndex,
CFStringRef inNewValue )
{
tDirStatus siResult = eDSNoErr;
CFArrayRef automountMapArray = NULL;
siResult = this->AttributeValueMatchesUserAlias( inNativeRecType, inNativeAttrType, inNewValue, inMutableRecordAttrsValues );
if ( siResult != eDSNoErr )
return siResult;
mOpenRecordsLock.WaitLock();
this->UpdateModValue();
try
{
CFMutableArrayRef mutableAttrValues = (CFMutableArrayRef)::CFDictionaryGetValue( inMutableRecordAttrsValues,
inNativeAttrType );
if ( mutableAttrValues == NULL )
throw( eDSAttributeNotFound );
if ( inIndex >= (unsigned long)CFArrayGetCount( mutableAttrValues ) )
throw( eDSIndexOutOfRange );
if ( inIndex == 0 && CFStringCompare(CFSTR("automount"), inNativeRecType, 0) == kCFCompareEqualTo &&
CFStringCompare(CFSTR("name"), inNativeAttrType, 0) == kCFCompareEqualTo )
{
automountMapArray = CFStringCreateArrayBySeparatingStrings( NULL, inNewValue, CFSTR(",automountMapName=") );
if ( automountMapArray == NULL || CFArrayGetCount(automountMapArray) != 2 ) throw eDSInvalidRecordName;
CFStringRef metaInformation = (CFStringRef) CFArrayGetValueAtIndex( automountMapArray, 1 );
if ( CFStringGetLength(metaInformation) == 0 ) throw eDSInvalidRecordName;
CFArrayRef cfMetaInfoArray = CFArrayCreate( NULL, (const void **)&metaInformation, 1, &kCFTypeArrayCallBacks );
CFDictionarySetValue( inMutableRecordAttrsValues, CFSTR("metaautomountmap"), cfMetaInfoArray );
CFRelease( cfMetaInfoArray );
}
CFStringRef pathString = this->GetShadowHashFilePath( inNativeAttrType, inMutableRecordAttrsValues );
if ( CFStringCompare(inNativeAttrType, CFSTR(kGeneratedUIDStr), 0) == kCFCompareEqualTo )
siResult = this->RenameShadowHashFiles( pathString, inNewValue );
if ( siResult == eDSNoErr )
CFArrayReplaceValues( mutableAttrValues, CFRangeMake( inIndex, 1 ), (const void**)&inNewValue, 1 );
if ( CFStringCompare(inNativeAttrType, CFSTR(kAuthAuthorityStr), 0) == kCFCompareEqualTo )
{
if ( pathString != NULL && !this->ArrayContainsShadowHashOrLocalCachedUser(mutableAttrValues) )
this->RemoveShadowHashFilesWithPath( pathString );
this->SetKerberosTicketsEnabledOrDisabled( mutableAttrValues );
}
DSCFRelease( pathString );
}
catch( tDirStatus err )
{
DbgLog( kLogPlugin, "CDSLocalPluginNode::ReplaceAttributeValueInRecordByIndex(): caught error %d", err );
siResult = err;
}
mOpenRecordsLock.SignalLock();
DSCFRelease( automountMapArray );
return siResult;
}
tDirStatus CDSLocalPluginNode::SetAttributeValuesInRecord( CFStringRef inNativeAttrType, CFStringRef inNativeRecType,
CFMutableDictionaryRef inMutableRecordAttrsValues, CFMutableArrayRef inMutableAttrValues )
{
tDirStatus siResult = eDSNoErr;
CFArrayRef automountMapArray = NULL;
if ( inNativeRecType == NULL )
return eDSNullRecType;
if ( inNativeAttrType == NULL )
return eDSNullAttributeType;
if ( inMutableAttrValues != NULL &&
CFStringCompare(inNativeRecType, CFSTR("users"), 0) == kCFCompareEqualTo &&
CFStringCompare(inNativeAttrType, CFSTR("name"), 0) == kCFCompareEqualTo )
{
CFIndex attrValueCount = CFArrayGetCount( inMutableAttrValues );
for ( CFIndex attrValueIndex = 0; attrValueIndex < attrValueCount; attrValueIndex++ )
{
siResult = this->AttributeValueMatchesUserAlias( inNativeRecType, inNativeAttrType,
CFArrayGetValueAtIndex(inMutableAttrValues, attrValueIndex), inMutableRecordAttrsValues );
if ( siResult != eDSNoErr )
return siResult;
}
}
mOpenRecordsLock.WaitLock();
this->UpdateModValue();
try
{
if ( CFStringCompare(CFSTR("automount"), inNativeRecType, 0) == kCFCompareEqualTo &&
CFStringCompare(CFSTR("name"), inNativeAttrType, 0) == kCFCompareEqualTo )
{
automountMapArray = CFStringCreateArrayBySeparatingStrings( NULL, (CFStringRef) CFArrayGetValueAtIndex(inMutableAttrValues, 0),
CFSTR(",automountMapName=") );
if ( automountMapArray == NULL || CFArrayGetCount(automountMapArray) != 2 ) throw eDSInvalidRecordName;
CFStringRef metaInformation = (CFStringRef) CFArrayGetValueAtIndex( automountMapArray, 1 );
if ( CFStringGetLength(metaInformation) == 0 ) throw eDSInvalidRecordName;
CFArrayRef cfMetaInfoArray = CFArrayCreate( NULL, (const void **) &metaInformation, 1, &kCFTypeArrayCallBacks );
CFDictionarySetValue( inMutableRecordAttrsValues, CFSTR("metaautomountmap"), cfMetaInfoArray );
CFRelease( cfMetaInfoArray );
}
bool isAuthAuthorityAttr = (CFStringCompare(inNativeAttrType, CFSTR(kAuthAuthorityStr), 0) == kCFCompareEqualTo);
CFStringRef pathString = this->GetShadowHashFilePath( inNativeAttrType, inMutableRecordAttrsValues );
if ( pathString != NULL )
{
if ( CFStringCompare(inNativeAttrType, CFSTR(kGeneratedUIDStr), 0) == kCFCompareEqualTo )
{
CFStringRef newGUIDString = (CFStringRef) CFArrayGetValueAtIndex( inMutableAttrValues, 0 );
siResult = this->RenameShadowHashFiles( pathString, newGUIDString );
}
else if ( isAuthAuthorityAttr )
{
if ( !this->ArrayContainsShadowHashOrLocalCachedUser(inMutableAttrValues) )
this->RemoveShadowHashFilesIfNecessary( inNativeAttrType, inMutableRecordAttrsValues );
}
CFRelease( pathString );
}
if ( isAuthAuthorityAttr )
{
this->SetKerberosTicketsEnabledOrDisabled( inMutableAttrValues );
}
if ( siResult == eDSNoErr )
{
CFMutableArrayRef curAttrValues = NULL;
if ( CFDictionaryGetValueIfPresent(inMutableRecordAttrsValues, inNativeAttrType, (const void **)&curAttrValues) )
{
CFArrayRemoveAllValues( curAttrValues );
CFArrayAppendArray( curAttrValues, inMutableAttrValues, CFRangeMake(0, CFArrayGetCount(inMutableAttrValues)) );
}
else
{
CFDictionarySetValue( inMutableRecordAttrsValues, inNativeAttrType, inMutableAttrValues );
}
}
}
catch( tDirStatus err )
{
DbgLog( kLogPlugin, "CDSLocalPluginNode::SetAttributeValuesInRecord(): caught error %d", err );
siResult = err;
}
mOpenRecordsLock.SignalLock();
DSCFRelease( automountMapArray );
return siResult;
}
tDirStatus CDSLocalPluginNode::GetAttributeValueByCRCFromRecord( CFStringRef inNativeAttrType, CFMutableDictionaryRef inMutableRecordAttrsValues, unsigned long inCRC, CFTypeRef* outAttrValue )
{
tDirStatus siResult = eDSNoErr;
char *cStr = NULL;
mOpenRecordsLock.WaitLock();
try
{
CFArrayRef attrValues = (CFArrayRef)::CFDictionaryGetValue( inMutableRecordAttrsValues, inNativeAttrType );
if ( attrValues == NULL ) throw( eDSAttributeNotFound );
CFIndex numValues = CFArrayGetCount( attrValues );
for ( CFIndex i = 0; i < numValues; i++ )
{
CFTypeRef arrayVal = CFArrayGetValueAtIndex( attrValues, i );
if ( arrayVal != NULL )
{
CFTypeID attrTypeID = CFGetTypeID( arrayVal );
if ( attrTypeID == CFStringGetTypeID() )
{
const char *aStr = CStrFromCFString( (CFStringRef)arrayVal, &cStr, NULL, NULL );
if ( CalcCRC(aStr) == inCRC )
{
*outAttrValue = arrayVal;
break;
}
}
else if ( attrTypeID == CFDataGetTypeID() )
{
CFIndex valueLength = CFDataGetLength( (CFDataRef)arrayVal );
const UInt8 *valueData = CFDataGetBytePtr( (CFDataRef)arrayVal );
if ( CalcCRCWithLength(valueData, valueLength) == inCRC )
{
*outAttrValue = arrayVal;
break;
}
}
}
else
{
throw( ePlugInDataError );
}
}
if ( *outAttrValue == NULL ) throw( eDSAttributeValueNotFound );
}
catch( tDirStatus err )
{
DbgLog( kLogPlugin, "CDSLocalPluginNode::GetAttributeValueByCRCFromRecord(): caught error %d", err );
siResult = err;
}
mOpenRecordsLock.SignalLock();
DSFreeString( cStr );
return siResult;
}
tDirStatus CDSLocalPluginNode::GetAttributeValueByIndexFromRecord( CFStringRef inNativeAttrType, CFMutableDictionaryRef inMutableRecordAttrsValues, unsigned long inIndex, CFTypeRef* outAttrValue )
{
tDirStatus siResult = eDSNoErr;
if ( inNativeAttrType == NULL )
return eDSInvalidAttributeType;
if ( inMutableRecordAttrsValues == NULL )
return eDSRecordNotFound;
if ( outAttrValue == NULL )
return eParameterError;
mOpenRecordsLock.WaitLock();
try
{
CFArrayRef attrValues = (CFArrayRef)::CFDictionaryGetValue( inMutableRecordAttrsValues, inNativeAttrType );
if ( attrValues == NULL ) throw( eDSAttributeNotFound );
if ( (CFIndex)inIndex >= CFArrayGetCount( attrValues ) ) throw( eDSIndexOutOfRange );
CFTypeRef attrValue = (CFTypeRef)CFArrayGetValueAtIndex( attrValues, (CFIndex)inIndex );
if ( attrValue == NULL ) throw( eDSIndexOutOfRange );
*outAttrValue = attrValue;
}
catch( tDirStatus err )
{
DbgLog( kLogPlugin, "CDSLocalPluginNode::GetAttributeValueByIndexFromRecord(): caught error %d", err );
siResult = err;
}
mOpenRecordsLock.SignalLock();
return siResult;
}
bool CDSLocalPluginNode::IsValidRecordName( CFStringRef inRecordName, CFStringRef inNativeRecType )
{
CFMutableArrayRef recordsArray = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
CFArrayRef patternsToMatch = CFArrayCreate( NULL, (const void**)&inRecordName, 1, &kCFTypeArrayCallBacks );
this->GetRecords( inNativeRecType, patternsToMatch,
mPlugin->AttrNativeTypeForStandardType( CFSTR( kDSNAttrRecordName ) ), eDSExact, true, 1, recordsArray );
CFIndex numRecords = CFArrayGetCount( recordsArray );
::CFRelease( patternsToMatch );
::CFRelease( recordsArray );
return numRecords > 0;
}
void CDSLocalPluginNode::FlushRecordCache()
{
}
CFArrayRef CDSLocalPluginNode::CreateAllRecordTypesArray()
{
char* cStr = NULL;
DIR* recTypeDir = NULL;
CFMutableArrayRef mutableAllRecordTypes = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
char thisFilePathCStr[768];
const char* nodeDirFilePathCStr = CStrFromCFString( mNodeDirFilePath, &cStr, NULL, NULL );
dirent* theDirEnt = NULL;
for ( recTypeDir = opendir( nodeDirFilePathCStr ); ( recTypeDir != NULL ) && ( theDirEnt = readdir( recTypeDir ) ); )
{
snprintf( thisFilePathCStr, sizeof(thisFilePathCStr), "%s/%s", nodeDirFilePathCStr, theDirEnt->d_name );
struct stat statBuffer={};
if ( ::stat( thisFilePathCStr, &statBuffer ) != 0 )
{
DbgLog( kLogPlugin, "CDSLocalPluginNode::CreateAllRecordTypesArray, stat returned nonzero error %d for \"%s\"",
errno, nodeDirFilePathCStr );
break;
}
if ( ( statBuffer.st_mode & S_IFDIR ) != 0 )
{
if ( ( ::strcmp( theDirEnt->d_name, "." ) != 0 ) && ( ::strcmp( theDirEnt->d_name, ".." ) != 0 ) )
{
DbgLog( kLogPlugin, "CDSLocalPluginNode::CreateAllRecordTypesArray(): found record type \"%s\"", theDirEnt->d_name );
CFStringRef nativeRecType = CFStringCreateWithCString( NULL, theDirEnt->d_name, kCFStringEncodingUTF8 );
CFStringRef stdRecType = mPlugin->RecordStandardTypeForNativeType( nativeRecType );
if ( stdRecType != NULL )
{
CFArrayAppendValue( mutableAllRecordTypes, stdRecType );
}
else
{
DSCFRelease( nativeRecType );
nativeRecType = CFStringCreateWithFormat( NULL, NULL, CFSTR(kDSNativeRecordTypePrefix "%s"),
theDirEnt->d_name );
CFArrayAppendValue( mutableAllRecordTypes, nativeRecType );
}
::CFRelease( nativeRecType );
}
}
}
if ( recTypeDir != NULL )
{
::closedir( recTypeDir );
recTypeDir = NULL;
}
if ( cStr != NULL )
{
::free( cStr );
cStr = NULL;
}
return mutableAllRecordTypes;
}
bool CDSLocalPluginNode::WriteAccessAllowed( CFDictionaryRef inNodeDict,
CFStringRef inNativeRecType,
CFStringRef inRecordName,
CFStringRef inNativeAttribute,
CFArrayRef inWritersAccessRecord,
CFArrayRef inWritersAccessAttribute,
CFArrayRef inWritersGroupAccessRecord,
CFArrayRef inWritersGroupAccessAttribute )
{
CFStringRef authedUserName = (CFStringRef) mPlugin->NodeDictCopyValue( inNodeDict, CFSTR(kNodeAuthenticatedUserName) );
CFNumberRef effectiveUID = (CFNumberRef) mPlugin->NodeDictCopyValue( inNodeDict, CFSTR(kNodeEffectiveUIDKey) );
bool bReturnValue = WriteAccessAllowed( authedUserName, effectiveUID, inNativeRecType, inRecordName, inNativeAttribute,
inWritersAccessRecord, inWritersAccessAttribute, inWritersGroupAccessRecord,
inWritersGroupAccessAttribute );
DSCFRelease( authedUserName );
DSCFRelease( effectiveUID );
return bReturnValue;
}
bool CDSLocalPluginNode::WriteAccessAllowed( CFStringRef inAuthedUserName,
CFNumberRef inEffectiveUIDNumber,
CFStringRef inNativeRecType,
CFStringRef inRecordName,
CFStringRef inNativeAttribute,
CFArrayRef inWritersAccessRecord,
CFArrayRef inWritersAccessAttribute,
CFArrayRef inWritersGroupAccessRecord,
CFArrayRef inWritersGroupAccessAttribute )
{
uid_t effectiveUID = 99;
if ( inEffectiveUIDNumber != NULL )
{
::CFNumberGetValue( inEffectiveUIDNumber, kCFNumberLongType, &effectiveUID );
}
return this->WriteAccessAllowed( inAuthedUserName,
effectiveUID,
inNativeRecType,
inRecordName,
inNativeAttribute,
inWritersAccessRecord,
inWritersAccessAttribute,
inWritersGroupAccessRecord,
inWritersGroupAccessAttribute );
}
bool CDSLocalPluginNode::WriteAccessAllowed( CFStringRef inAuthedUserName,
uid_t inEffectiveUID,
CFStringRef inNativeRecType,
CFStringRef inRecordName,
CFStringRef inNativeAttribute,
CFArrayRef inWritersAccessRecord,
CFArrayRef inWritersAccessAttribute,
CFArrayRef inWritersGroupAccessRecord,
CFArrayRef inWritersGroupAccessAttribute )
{
bool writeAllowed = false;
CFStringRef cfTempName = NULL;
if ( inEffectiveUID == 0 )
{
writeAllowed = true;
}
else if ( ( inAuthedUserName != NULL ) && ( CFStringCompare( inAuthedUserName, CFSTR("root"), 0 ) == kCFCompareEqualTo ) )
{
writeAllowed = true;
}
else if ( inAuthedUserName != NULL ) {
writeAllowed = mPlugin->UserIsAdmin( inAuthedUserName, this );
}
else {
struct passwd *entry = getpwuid( inEffectiveUID );
if( entry != NULL )
{
cfTempName = CFStringCreateWithCString( NULL, entry->pw_name, kCFStringEncodingUTF8 );
inAuthedUserName = cfTempName;
}
}
if ( !writeAllowed && ( inAuthedUserName != NULL ) && ( CFStringGetLength( inAuthedUserName ) != 0 ) )
{
if (inWritersAccessRecord != NULL)
{
for ( CFIndex i = 0; i < CFArrayGetCount(inWritersAccessRecord); i++ )
{
if ( CFStringCompare( inAuthedUserName, (CFStringRef)CFArrayGetValueAtIndex( inWritersAccessRecord, i ), 0) == kCFCompareEqualTo )
{
writeAllowed = true;
break;
}
}
}
if ( !writeAllowed && (inWritersAccessAttribute != NULL) )
{
for ( CFIndex i = 0; i < CFArrayGetCount(inWritersAccessAttribute); i++ )
{
if ( CFStringCompare( inAuthedUserName, (CFStringRef)CFArrayGetValueAtIndex( inWritersAccessAttribute, i ), 0) == kCFCompareEqualTo )
{
writeAllowed = true;
break;
}
}
}
}
DSCFRelease( cfTempName );
return(writeAllowed);
}
bool CDSLocalPluginNode::IsLocalNode()
{
return true;
}
uint32_t CDSLocalPluginNode::GetModValue( void )
{
uint32_t retValue;
mOpenRecordsLock.WaitLock();
retValue = mModCounter;
mOpenRecordsLock.SignalLock();
return retValue;
}
#pragma mark -
#pragma mark Private Methods
bool CDSLocalPluginNode::RecordMatchesCriteria( CFDictionaryRef inRecordDict, CFArrayRef inPatternsToMatch,
CFStringRef inNativeAttrTypeToMatch, tDirPatternMatch inPatternMatch )
{
if ( CFArrayContainsValue(inPatternsToMatch, CFRangeMake(0, CFArrayGetCount(inPatternsToMatch)), CFSTR(kDSRecordsAll)) )
return true;
CFArrayRef attrValues = (CFArrayRef)::CFDictionaryGetValue( inRecordDict, inNativeAttrTypeToMatch );
if ( attrValues == NULL )
return false;
bool needLowercase = false;
switch( inPatternMatch )
{
case eDSiStartsWith:
case eDSiEndsWith:
needLowercase = true;
break;
default:
break;
}
CFIndex numValues = CFArrayGetCount( attrValues );
bool matches = false;
for ( CFIndex i = 0; i < numValues && !matches; i++ )
{
CFTypeRef value = CFArrayGetValueAtIndex( attrValues, i );
if ( value != NULL )
{
if ( CFGetTypeID(value) == CFStringGetTypeID() )
matches = this->RecordMatchesCriteriaTestString( (CFStringRef)value, inPatternsToMatch, inNativeAttrTypeToMatch,
inPatternMatch, needLowercase );
else if ( CFGetTypeID(value) == CFDataGetTypeID() )
matches = this->RecordMatchesCriteriaTestData( (CFDataRef)value, inPatternsToMatch, inNativeAttrTypeToMatch,
inPatternMatch, needLowercase );
}
}
return matches;
}
bool
CDSLocalPluginNode::RecordMatchesCriteriaTestString(
CFStringRef inAttributeString,
CFArrayRef inPatternsToMatch,
CFStringRef inNativeAttrTypeToMatch,
tDirPatternMatch inPatternMatch,
bool inNeedLowercase )
{
bool matches = false;
CFMutableStringRef mutableLowercaseValue = NULL;
CFMutableStringRef mutableLowercasePattern = NULL;
if ( inNeedLowercase )
{
mutableLowercaseValue = CFStringCreateMutableCopy( NULL, 0, inAttributeString );
if ( mutableLowercaseValue != NULL )
CFStringLowercase( mutableLowercaseValue, NULL );
}
CFIndex numPatterns = CFArrayGetCount( inPatternsToMatch );
for ( CFIndex j = 0; j < numPatterns && !matches; j++ )
{
CFStringRef patternToMatch = (CFStringRef)CFArrayGetValueAtIndex( inPatternsToMatch, j );
if ( patternToMatch == NULL || CFGetTypeID(patternToMatch) != CFStringGetTypeID() )
continue;
if ( CFStringCompare( patternToMatch, CFSTR( kDSRecordsAll ), 0 ) == kCFCompareEqualTo )
return true;
if ( inNeedLowercase )
{
mutableLowercasePattern = CFStringCreateMutableCopy( NULL, 0, patternToMatch );
if ( mutableLowercasePattern == NULL )
continue;
CFStringLowercase( mutableLowercasePattern, NULL );
}
switch( inPatternMatch )
{
case eDSExact:
if ( CFStringCompare( inAttributeString, patternToMatch, 0 ) == kCFCompareEqualTo )
matches = true;
break;
case eDSiExact:
if ( CFStringCompare( inAttributeString, patternToMatch, kCFCompareCaseInsensitive ) == kCFCompareEqualTo )
matches = true;
break;
case eDSStartsWith:
if ( CFStringHasPrefix( inAttributeString, patternToMatch ) )
matches = true;
break;
case eDSiStartsWith:
if ( CFStringHasPrefix( mutableLowercaseValue, mutableLowercasePattern ) )
matches = true;
break;
case eDSEndsWith:
if ( CFStringHasSuffix( inAttributeString, patternToMatch ) )
matches = true;
break;
case eDSiEndsWith:
if ( CFStringHasSuffix( mutableLowercaseValue, mutableLowercasePattern ) )
matches = true;
break;
case eDSContains:
if ( CFStringFind( inAttributeString, patternToMatch, 0 ).location != kCFNotFound )
matches = true;
break;
case eDSiContains:
if ( CFStringFind( inAttributeString, patternToMatch, kCFCompareCaseInsensitive ).location != kCFNotFound )
matches = true;
break;
case eDSAnyMatch:
matches = true;
break;
case eDSLessThan:
case eDSiLessThan:
break;
case eDSGreaterThan:
case eDSiGreaterThan:
break;
case eDSLessEqual:
case eDSiLessEqual:
break;
case eDSGreaterEqual:
case eDSiGreaterEqual:
break;
default: return false;
}
DSCFRelease( mutableLowercasePattern );
}
DSCFRelease( mutableLowercaseValue );
return matches;
}
bool
CDSLocalPluginNode::RecordMatchesCriteriaTestData(
CFDataRef inAttributeData,
CFArrayRef inPatternsToMatch,
CFStringRef inNativeAttrTypeToMatch,
tDirPatternMatch inPatternMatch,
bool inNeedLowercase )
{
bool matches = false;
const UInt8 *attrData = NULL;
CFIndex attrDataLen = 0;
const UInt8 *patternData = NULL;
CFIndex patternDataLen = 0;
char *cStr = NULL;
if ( inNeedLowercase )
return false;
attrDataLen = CFDataGetLength( inAttributeData );
if ( attrDataLen == 0 )
return false;
attrData = CFDataGetBytePtr( inAttributeData );
if ( attrData == NULL )
return false;
CFIndex numPatterns = CFArrayGetCount( inPatternsToMatch );
for ( CFIndex j = 0; j < numPatterns && !matches; j++ )
{
CFTypeRef patternToMatch = CFArrayGetValueAtIndex( inPatternsToMatch, j );
if ( patternToMatch != NULL )
{
if ( CFGetTypeID(patternToMatch) == CFDataGetTypeID() )
{
if ( (patternDataLen = CFDataGetLength((CFDataRef)patternToMatch)) <= 0 ||
(patternData = CFDataGetBytePtr((CFDataRef)patternToMatch)) == NULL )
continue;
}
else if ( CFGetTypeID(patternToMatch) == CFStringGetTypeID() )
{
if ( (patternData = (const UInt8 *)CStrFromCFString((CFStringRef)patternToMatch, &cStr, NULL, NULL)) == NULL ||
(patternDataLen = (CFIndex)strlen((char *)patternData)) == 0 )
continue;
}
switch( inPatternMatch )
{
case eDSExact:
if ( patternDataLen == attrDataLen )
matches = (memcmp(attrData, patternData, patternDataLen) == 0);
break;
case eDSStartsWith:
if ( patternDataLen <= attrDataLen )
matches = (memcmp(attrData, patternData, patternDataLen) == 0);
break;
case eDSEndsWith:
if ( patternDataLen <= attrDataLen )
matches = (memcmp(attrData + attrDataLen-patternDataLen, patternData, patternDataLen) == 0);
break;
case eDSContains:
if ( patternDataLen <= attrDataLen )
{
int maxpos = attrDataLen-patternDataLen;
for ( int idx = 0; idx <= maxpos && !matches; idx++ )
matches = (memcmp(attrData + idx, patternData, patternDataLen) == 0);
}
break;
case eDSiExact:
case eDSiStartsWith:
case eDSiContains:
case eDSiEndsWith:
break;
case eDSLessThan:
case eDSiLessThan:
break;
case eDSGreaterThan:
case eDSiGreaterThan:
break;
case eDSLessEqual:
case eDSiLessEqual:
break;
case eDSGreaterEqual:
case eDSiGreaterEqual:
break;
default: return false;
}
}
DSFreeString( cStr );
}
return matches;
}
void CDSLocalPluginNode::UpdateModValue()
{
mOpenRecordsLock.WaitLock();
mModCounter++;
mOpenRecordsLock.SignalLock();
}
void
CDSLocalPluginNode::RemoveShadowHashFilesIfNecessary(
CFStringRef inNativeAttrType,
CFMutableDictionaryRef inMutableRecordAttrsValues )
{
CFStringRef pathString = NULL;
if ( (pathString = GetShadowHashFilePath(inNativeAttrType, inMutableRecordAttrsValues)) == NULL )
return;
this->RemoveShadowHashFilesWithPath( pathString );
CFRelease( pathString );
}
void
CDSLocalPluginNode::RemoveShadowHashFilesWithPath( CFStringRef inPath )
{
struct stat sb;
char pathStr[256];
if ( inPath != NULL && CFStringGetCString(inPath, pathStr, sizeof(pathStr), kCFStringEncodingUTF8) )
{
if ( lstat(pathStr, &sb) == 0 )
unlink( pathStr );
strlcat( pathStr, kShadowHashStateFileSuffix, sizeof(pathStr) );
if ( lstat(pathStr, &sb) == 0 )
unlink( pathStr );
}
}
CFStringRef
CDSLocalPluginNode::GetShadowHashFilePath(
CFStringRef inNativeAttrType,
CFMutableDictionaryRef inMutableRecordAttrsValues )
{
CFStringRef pathString = NULL;
CFArrayRef attrArray = NULL;
CFStringRef guidString = NULL;
if ( inNativeAttrType == NULL || inMutableRecordAttrsValues == NULL )
return NULL;
if ( CFStringCompare(inNativeAttrType, CFSTR(kGeneratedUIDStr), 0) != kCFCompareEqualTo &&
CFStringCompare(inNativeAttrType, CFSTR(kAuthAuthorityStr), 0) != kCFCompareEqualTo )
return NULL;
if ( (attrArray = (CFArrayRef) CFDictionaryGetValue(inMutableRecordAttrsValues, CFSTR(kAuthAuthorityStr))) == NULL )
return NULL;
if ( this->ArrayContainsShadowHashOrLocalCachedUser(attrArray) )
{
attrArray = (CFArrayRef) CFDictionaryGetValue( inMutableRecordAttrsValues, CFSTR(kGeneratedUIDStr) );
if ( attrArray != NULL && CFGetTypeID(attrArray) == CFArrayGetTypeID() )
guidString = (CFStringRef) CFArrayGetValueAtIndex( attrArray, 0 );
if ( guidString != NULL && CFGetTypeID(guidString) == CFStringGetTypeID() )
pathString = CFStringCreateWithFormat( NULL, NULL, CFSTR(kShadowHashDirPath "%@"), guidString );
}
return pathString;
}
void
CDSLocalPluginNode::GetDataFromEnabledOrDisabledKerberosTag( CFStringRef inAuthAuthority, char **outPrincipal, char **outRealm )
{
CFMutableDictionaryRef aaDict = NULL;
CFArrayRef aaDataArray = NULL;
CFStringRef princString = NULL;
CFStringRef realmString = NULL;
bool canGetStrings = false;
char scratch[1024];
if ( outPrincipal != NULL && outRealm != NULL )
{
*outPrincipal = NULL;
*outRealm = NULL;
CAuthAuthority aaStorage;
if ( aaStorage.AddValue(inAuthAuthority) )
{
if ( (aaDict = aaStorage.GetValueForTagAsCFDict(kDSTagAuthAuthorityDisabledUser)) != NULL &&
(aaDataArray = (CFArrayRef)CFDictionaryGetValue(aaDict, CFSTR("data"))) != NULL &&
CFArrayGetCount(aaDataArray) >= 5 &&
(princString = (CFStringRef)CFArrayGetValueAtIndex(aaDataArray, 3)) != NULL &&
(realmString = (CFStringRef)CFArrayGetValueAtIndex(aaDataArray, 4)) != NULL )
{
canGetStrings = true;
}
else if ( (aaDict = aaStorage.GetValueForTagAsCFDict(kDSTagAuthAuthorityKerberosv5)) != NULL &&
(aaDataArray = (CFArrayRef)CFDictionaryGetValue(aaDict, CFSTR("data"))) != NULL &&
CFArrayGetCount(aaDataArray) >= 3 &&
(princString = (CFStringRef)CFArrayGetValueAtIndex(aaDataArray, 1)) != NULL &&
(realmString = (CFStringRef)CFArrayGetValueAtIndex(aaDataArray, 2)) != NULL )
{
canGetStrings = true;
}
if ( canGetStrings )
{
if ( CFStringGetCString(princString, scratch, sizeof(scratch), kCFStringEncodingUTF8) &&
(*outPrincipal = strdup(scratch)) != NULL )
{
char *atSign = strchr( *outPrincipal, '@' );
if ( atSign != NULL )
*atSign = '\0';
}
if ( CFStringGetCString(realmString, scratch, sizeof(scratch), kCFStringEncodingUTF8) )
*outRealm = strdup( scratch );
}
}
}
}
CFStringRef
CDSLocalPluginNode::GetKerberosTagIfPresent( CFArrayRef inAttrValues )
{
return GetTagInArray( inAttrValues, CFSTR(kDSValueAuthAuthorityKerberosv5) );
}
CFStringRef
CDSLocalPluginNode::GetDisabledKerberosTagIfPresent( CFArrayRef inAttrValues )
{
return GetTagInArray( inAttrValues, CFSTR(kDSValueAuthAuthorityDisabledUser kDSValueAuthAuthorityKerberosv5) );
}
bool
CDSLocalPluginNode::ArrayContainsShadowHashOrLocalCachedUser( CFArrayRef inAttrValues )
{
if (GetTagInArray(inAttrValues, CFSTR(kDSValueAuthAuthorityShadowHash)) != NULL)
return true;
return (GetTagInArray(inAttrValues, CFSTR(kDSValueAuthAuthorityLocalCachedUser)) != NULL);
}
CFStringRef
CDSLocalPluginNode::GetTagInArray( CFArrayRef inAttrValues, CFStringRef inTag )
{
CFStringRef returnString = NULL;
CFStringRef aaString = NULL;
CFRange resultRange = { 0, 0 };
CFIndex attrArrayCount = CFArrayGetCount( inAttrValues );
for ( CFIndex idx = 0; idx < attrArrayCount; idx++ )
{
aaString = (CFStringRef) CFArrayGetValueAtIndex( inAttrValues, idx );
if ( aaString != NULL &&
CFStringFindWithOptions(
aaString,
inTag,
CFRangeMake(0, CFStringGetLength(aaString)),
kCFCompareCaseInsensitive,
&resultRange ) )
{
returnString = aaString;
break;
}
}
return returnString;
}
tDirStatus
CDSLocalPluginNode::RenameShadowHashFiles( CFStringRef inCurrentPath, CFStringRef inNewGUIDString )
{
tDirStatus result = eDSNoErr;
CFStringRef newPathString = NULL;
char oldpath[PATH_MAX] = {0,};
char newpath[PATH_MAX] = {0,};
struct stat sb;
if ( inCurrentPath != NULL && inNewGUIDString != NULL )
{
newPathString = CFStringCreateWithFormat( NULL, NULL, CFSTR(kShadowHashDirPath "%@"), inNewGUIDString );
if ( newPathString != NULL )
{
if ( CFStringGetCString( inCurrentPath, oldpath, sizeof(oldpath), kCFStringEncodingUTF8 ) &&
CFStringGetCString( newPathString, newpath, sizeof(newpath), kCFStringEncodingUTF8 ) )
{
if ( lstat(oldpath, &sb) == 0 )
{
if ( lstat(newpath, &sb) != 0 )
{
rename( oldpath, newpath );
strlcat( oldpath, kShadowHashStateFileSuffix, sizeof(oldpath) );
strlcat( newpath, kShadowHashStateFileSuffix, sizeof(newpath) );
if ( lstat(oldpath, &sb) == 0 )
{
if ( lstat(newpath, &sb) == 0 )
unlink( newpath );
rename( oldpath, newpath );
}
}
else
{
result = eDSInvalidFilePath;
DbgLog( kLogError, "CDSLocalPluginNode::RenameShadowHashFiles(): Cannot rename %s to %s", oldpath, newpath );
}
}
}
CFRelease( newPathString );
}
}
return result;
}
tDirStatus
CDSLocalPluginNode::AttributeValueMatchesUserAlias(
CFStringRef inNativeRecType,
CFStringRef inNativeAttrType,
CFTypeRef inAttrValue,
CFDictionaryRef inRecordDict )
{
tDirStatus status = eDSNoErr;
#if FILE_ACCESS_INDEXING
char **filesWithAlias = NULL;
if ( inNativeRecType != NULL &&
inNativeAttrType != NULL &&
inAttrValue != NULL &&
CFGetTypeID(inAttrValue) == CFStringGetTypeID() &&
CFStringCompare(inNativeRecType, CFSTR("users"), 0) == kCFCompareEqualTo &&
CFStringCompare(inNativeAttrType, CFSTR("name"), 0) == kCFCompareEqualTo )
{
filesWithAlias = GetFileAccessIndex( inNativeRecType, eDSiExact, (CFStringRef) inAttrValue, CFSTR("name"), NULL );
if ( filesWithAlias != NULL )
{
for ( int ii = 0; filesWithAlias[ii] != NULL; ii++ )
{
char thisFilePathCStr[768] = { 0, };
char *theFile = filesWithAlias[ii];
struct stat statResult;
CFArrayRef recordNames = (CFArrayRef)::CFDictionaryGetValue( inRecordDict, mRecordNameAttrNativeName );
if ( recordNames == NULL || CFArrayGetCount( recordNames ) == 0 )
status = ePlugInDataError;
CFStringRef recordName = (CFStringRef)CFArrayGetValueAtIndex( recordNames, 0 );
if ( recordName == NULL )
status = ePlugInDataError;
if ( status == eDSNoErr )
{
CFStringRef thisFilePath = CFStringCreateWithFormat( NULL, NULL, CFSTR( "%@%@/%s" ), mNodeDirFilePath,
inNativeRecType, theFile );
CFStringRef filePath = CreateFilePathForRecord( inNativeRecType, recordName );
if ( thisFilePath != NULL && filePath != NULL && CFStringCompare(filePath, thisFilePath, 0) != kCFCompareEqualTo )
{
if ( CFStringGetCString( thisFilePath, thisFilePathCStr, sizeof(thisFilePathCStr), kCFStringEncodingUTF8 ) )
{
if( stat(thisFilePathCStr, &statResult) == 0 )
{
DbgLog( kLogError, "CDSLocalPluginNode::AttributeValueMatchesUserAlias(), alias exists in file %s", thisFilePathCStr );
status = eDSRecordAlreadyExists;
break;
}
}
else
{
DbgLog( kLogError, "CDSLocalPluginNode::AttributeValueMatchesUserAlias(), alias exists in file %s according to index",
theFile );
status = eDSRecordAlreadyExists;
break;
}
}
DSCFRelease( thisFilePath );
DSCFRelease( filePath );
}
}
DSFreeStringList( filesWithAlias );
}
}
#endif
return status;
}
void CDSLocalPluginNode::SetKerberosTicketsEnabledOrDisabled( CFMutableArrayRef inMutableAttrValues )
{
char *princ = NULL;
char *realm = NULL;
CFStringRef aaString = this->GetDisabledKerberosTagIfPresent( inMutableAttrValues );
if ( aaString != NULL )
{
this->GetDataFromEnabledOrDisabledKerberosTag( aaString, &princ, &realm );
if ( princ != NULL && realm != NULL )
SetPrincipalStateInLocalRealm( princ, realm, false );
}
else
{
aaString = this->GetKerberosTagIfPresent( inMutableAttrValues );
if ( aaString != NULL )
{
this->GetDataFromEnabledOrDisabledKerberosTag( aaString, &princ, &realm );
if ( princ != NULL && realm != NULL )
SetPrincipalStateInLocalRealm( princ, realm, true );
}
}
DSFreeString( princ );
DSFreeString( realm );
}
void CDSLocalPluginNode::SetPrincipalStateInLocalRealm(char* principalName, const char *realmName, bool enabled)
{
const char *argv[6];
int ai = 0;
char commandBuf[1024];
char stdoutBuff[512];
char stderrBuff[512];
if ( principalName == NULL || realmName == NULL || mPlugin->mPWSFrameworkAvailable == false )
return;
argv[ai++] = "kadmin.local";
argv[ai++] = "-r";
argv[ai++] = realmName;
argv[ai++] = "-q";
argv[ai++] = commandBuf;
argv[ai] = NULL;
snprintf(commandBuf, sizeof(commandBuf), "modify_principal %callow_tix %s", enabled ? '+' : '-', principalName);
pwsf_LaunchTaskWithIO2(kKAdminLocalFilePath, (char * const *)argv, NULL, stdoutBuff, sizeof(stdoutBuff), stderrBuff, sizeof(stderrBuff) );
}
CFDataRef CDSLocalPluginNode::CreateCFDataFromFile( const char *filename, size_t inLength )
{
CFDataRef cfData = NULL;
if ( filename == NULL || inLength <= 0 )
return NULL;
int fd = open( filename, O_RDONLY | O_NOFOLLOW );
if ( fd != -1 )
{
void *data = calloc( inLength, sizeof(char) );
read( fd, data, inLength );
cfData = CFDataCreateWithBytesNoCopy( kCFAllocatorDefault, (UInt8 *) data, inLength, kCFAllocatorMalloc );
if ( cfData == NULL )
{
DSFree( data );
}
close( fd );
}
return cfData;
}
#if FILE_ACCESS_INDEXING
#pragma mark -
#pragma mark Index routines
void CDSLocalPluginNode::AddIndexMapping( const char *inRecordType, ... )
{
va_list args;
int iCount = 0;
IndexMapping *newMap = (IndexMapping *) calloc( 1, sizeof(IndexMapping) );
va_start( args, inRecordType );
while (va_arg( args, char * ) != NULL)
iCount++;
CFMutableArrayRef cfArray = CFArrayCreateMutable( kCFAllocatorDefault, iCount, &kCFTypeArrayCallBacks );
char *cStr = NULL;
newMap->fRecordType = inRecordType;
newMap->fRecordTypeCF = CFStringCreateWithCString( kCFAllocatorDefault, inRecordType, kCFStringEncodingUTF8 );
newMap->fAttributes = (const char **) calloc( iCount+1, sizeof(const char *) );
newMap->fAttributesCF = cfArray;
CFStringRef cfNativeType = mPlugin->RecordNativeTypeForStandardType( newMap->fRecordTypeCF );
newMap->fRecordNativeType = strdup( CStrFromCFString(cfNativeType, &cStr, NULL, NULL) );
DSFree( cStr );
iCount = 0;
va_start( args, inRecordType );
char *attrib = va_arg( args, char * );
do
{
newMap->fAttributes[iCount] = attrib; iCount++;
CFStringRef cfTempString = CFStringCreateWithCString( kCFAllocatorDefault, attrib, kCFStringEncodingUTF8 );
CFArrayAppendValue( cfArray, cfTempString );
CFRelease( cfTempString );
attrib = va_arg( args, char * );
} while( attrib != NULL );
mIndexMap[inRecordType] = newMap;
}
struct sIndexContext
{
CDSLocalPluginNode *pNode;
sqlite3_stmt *pStmt;
const char *pAttrib;
const char *pFileName;
};
void CDSLocalPluginNode::IndexObject( const void *inValue, void *inContext )
{
sIndexContext *pContext = (sIndexContext *) inContext;
if ( pContext->pNode == NULL )
return;
if ( CFGetTypeID(inValue) == CFStringGetTypeID() )
{
char *cStr = NULL;
const char *pValue = CStrFromCFString( (CFStringRef) inValue, &cStr, NULL, NULL );
int status = sqlite3_reset( pContext->pStmt );
if ( status == SQLITE_OK )
status = sqlite3_bind_text( pContext->pStmt, 3, pValue, strlen(pValue), SQLITE_TRANSIENT );
if ( status == SQLITE_OK )
sqlite3_step( pContext->pStmt );
if ( status == SQLITE_OK ) {
DbgLog( kLogDebug, "CDSLocalPluginNode::IndexObject - added <%s> to table <%s> for <%s>", pValue, pContext->pAttrib,
pContext->pFileName );
}
else if ( status == SQLITE_CORRUPT && pContext->pNode != NULL ) {
pContext->pNode->DatabaseCorrupt();
DbgLog( kLogCritical, "CDSLocalPluginNode::IndexObject - failed to add <%s> to table <%s> for <%s> - DB corrupt detected, will rebuild", pValue,
pContext->pAttrib, pContext->pFileName );
pContext->pNode = NULL; }
DSFree( cStr );
}
}
void CDSLocalPluginNode::DatabaseCorrupt( void )
{
if ( OSAtomicCompareAndSwap32Barrier(false, true, &mIndexLoading) == TRUE )
{
pthread_t loadIndexThread;
pthread_attr_t defaultAttrs;
fDBLock.WaitLock();
CloseDatabase();
RemoveIndex();
fDBLock.SignalLock();
pthread_attr_init( &defaultAttrs );
pthread_attr_setdetachstate( &defaultAttrs, PTHREAD_CREATE_DETACHED );
mIndexLoaded.ResetEvent();
pthread_create( &loadIndexThread, &defaultAttrs, LoadIndexAsynchronously, (void *) this );
if ( mIndexLoaded.WaitForEvent(10 * kMilliSecsPerSec) == false )
DbgLog( kLogPlugin, "CDSLocalPluginNode::DatabaseCorrupt - timed out waiting for index to load continuing without until available" );
pthread_attr_destroy( &defaultAttrs );
}
}
void CDSLocalPluginNode::AddRecordIndex( const char *inRecordType, const char *inFileName )
{
LocalNodeIndexMapI iter = mIndexMap.find( inRecordType );
if ( iter == mIndexMap.end() ) return;
char sPath[PATH_MAX];
IndexMapping *mapping = iter->second;
struct stat statBuffer;
CFDataRef aFileData = NULL;
snprintf( sPath, sizeof(sPath), "%s%s/%s", mNodeDirFilePathCStr, mapping->fRecordNativeType, inFileName );
fDBLock.WaitLock();
if ( mFileAccessIndexPtr != NULL && lstat(sPath, &statBuffer) == 0 && (aFileData = CreateCFDataFromFile(sPath, statBuffer.st_size)) != NULL )
{
CFDictionaryRef recordDict = (CFDictionaryRef) CFPropertyListCreateFromXMLData( kCFAllocatorDefault, aFileData, kCFPropertyListImmutable, NULL );
DSCFRelease( aFileData );
if ( recordDict != NULL )
{
CFIndex iCount = CFArrayGetCount( mapping->fAttributesCF );
char command[256];
sqlExecSync( "BEGIN TRANSACTION" );
for ( CFIndex ii = 0; ii < iCount; ii++ )
{
CFStringRef cfStdAttrib = (CFStringRef) CFArrayGetValueAtIndex( mapping->fAttributesCF, ii );
CFStringRef cfNativeAttr = mPlugin->AttrNativeTypeForStandardType( cfStdAttrib );
const char *pAttrib = mapping->fAttributes[ii];
sqlite3_stmt *pStmt = NULL;
if ( cfNativeAttr == NULL ) continue;
snprintf( command, sizeof(command), "DELETE FROM '%s' WHERE filename='%s' AND recordtype='%s';", pAttrib, inFileName,
mapping->fRecordNativeType );
int status = sqlExecSync( command );
if ( status == SQLITE_CORRUPT )
break;
if ( status != SQLITE_OK )
{
snprintf( command, sizeof(command), "CREATE TABLE '%s' ('filename' TEXT, 'recordtype' TEXT, 'value' TEXT);", pAttrib );
if ( sqlExecSync(command) == SQLITE_CORRUPT )
break;
snprintf( command, sizeof(command), "CREATE INDEX '%s.index' on '%s' ('value');", pAttrib, pAttrib );
if ( sqlExecSync(command) == SQLITE_CORRUPT )
break;
DbgLog( kLogDebug, "CDSLocalPluginNode::AddRecordIndex - created table for attribute %s", pAttrib );
status = SQLITE_OK;
}
if ( status == SQLITE_OK ) {
snprintf( command, sizeof(command), "INSERT INTO '%s' ('filename','recordtype','value') VALUES (?,?,?);", pAttrib );
status = sqlite3_prepare_v2( mFileAccessIndexPtr, command, -1, &pStmt, NULL );
}
if ( status == SQLITE_OK )
status = sqlite3_bind_text( pStmt, 1, inFileName, strlen(inFileName), SQLITE_STATIC );
if ( status == SQLITE_OK )
status = sqlite3_bind_text( pStmt, 2, mapping->fRecordNativeType, strlen(mapping->fRecordNativeType), SQLITE_STATIC );
CFArrayRef cfAttribute = (CFArrayRef) CFDictionaryGetValue( recordDict, cfNativeAttr );
if ( cfAttribute != NULL && status == SQLITE_OK )
{
CFMutableSetRef cfSet = CFSetCreateMutable( kCFAllocatorDefault, 0, &kCFTypeSetCallBacks );
CFIndex iAttrCnt = CFArrayGetCount( cfAttribute );
sIndexContext sContext = { this, pStmt, pAttrib, inFileName };
for ( CFIndex zz = 0; zz < iAttrCnt; zz++ )
CFSetAddValue( cfSet, CFArrayGetValueAtIndex(cfAttribute, zz) );
CFSetApplyFunction( cfSet, IndexObject, &sContext );
DSCFRelease( cfSet );
}
if ( pStmt != NULL )
{
sqlite3_finalize( pStmt );
pStmt = NULL;
}
}
snprintf( command, sizeof(command), "REPLACE INTO '%s' ('filetime','filename') VALUES (%d,'%s');", mapping->fRecordNativeType,
(int) statBuffer.st_mtimespec.tv_sec, inFileName );
sqlExecSync( command );
sqlExecSync( "END TRANSACTION" );
sqlExecSync( "COMMIT" );
DSCFRelease( recordDict );
}
}
fDBLock.SignalLock();
}
void CDSLocalPluginNode::DeleteRecordIndex( const char *inRecordType, const char* inFileName )
{
LocalNodeIndexMapI iter = mIndexMap.find( inRecordType );
if ( iter == mIndexMap.end() ) return;
IndexMapping *mapping = iter->second;
const char **attribs = mapping->fAttributes;
char command[256];
fDBLock.WaitLock();
sqlExecSync( "BEGIN TRANSACTION" );
while ( (*attribs) != NULL )
{
snprintf( command, sizeof(command), "DELETE FROM '%s' WHERE filename='%s' AND recordtype='%s';", (*attribs), inFileName,
mapping->fRecordNativeType );
if ( sqlExecSync(command) == SQLITE_CORRUPT )
break;
snprintf( command, sizeof(command), "DELETE FROM '%s' WHERE filename='%s';", mapping->fRecordNativeType, inFileName );
if ( sqlExecSync(command) == SQLITE_CORRUPT )
break;
attribs++;
}
DbgLog( kLogPlugin, "CDSLocalPluginNode::DeleteRecordIndex - deleting index for %s type %s", inFileName, inRecordType );
sqlExecSync( "END TRANSACTION" );
sqlExecSync( "COMMIT" );
fDBLock.SignalLock();
}
void CDSLocalPluginNode::RemoveIndex( void )
{
char journalPath[PATH_MAX];
strlcpy( journalPath, mIndexPath, sizeof(journalPath) );
strlcat( journalPath, "-journal", sizeof(journalPath) );
unlink( mIndexPath );
unlink( journalPath );
}
void CDSLocalPluginNode::LoadFileAccessIndex( void )
{
struct stat statBuffer = { 0 };
bool bRecreate = false;
if ( gDSInstallDaemonMode == true )
return;
fDBLock.WaitLock();
if ( (stat(mIndexPath, &statBuffer) == 0 && statBuffer.st_size == 0) || mSafeBoot == true || mProperShutdown == false ) {
mProperShutdown = true;
mSafeBoot = false;
RemoveIndex();
bRecreate = true;
}
TryAgain:
int status = sqlite3_open( mIndexPath, &mFileAccessIndexPtr );
if ( status != SQLITE_OK ) {
RemoveIndex();
bRecreate = true;
}
if ( status == SQLITE_OK || sqlite3_open(mIndexPath, &mFileAccessIndexPtr) == SQLITE_OK )
{
LocalNodeIndexMapI iter = mIndexMap.begin();
if ( false == bRecreate ) {
sqlExecSync( "PRAGMA cache_size = 50" );
if ( IntegrityCheckDB(mFileAccessIndexPtr) == false ) {
sqlite3_close( mFileAccessIndexPtr );
mFileAccessIndexPtr = NULL;
DbgLog( kLogCritical, "CDSLocalPluginNode::LoadFileAccessIndex - index failed integrity check - rebuilding" );
RemoveIndex();
bRecreate = true;
goto TryAgain;
}
else {
SrvrLog( kLogApplication, "Local Plugin - index passed integrity check" );
}
}
sqlExecSync( "PRAGMA cache_size = 500" );
while( iter != mIndexMap.end() )
{
IndexMapping *mapping = iter->second;
char sPath[PATH_MAX];
char command[1024];
snprintf( sPath, sizeof(sPath), "%s%s", mNodeDirFilePathCStr, mapping->fRecordNativeType );
DbgLog( kLogDebug, "CDSLocalPluginNode::LoadFileAccessIndex - Checking index for path %s", sPath );
snprintf( command, sizeof(command), "CREATE TABLE '%s' ('filetime' INTEGER, 'filename' TEXT UNIQUE);", mapping->fRecordNativeType );
if ( sqlExecSync(command) == SQLITE_CORRUPT ) {
sqlite3_close( mFileAccessIndexPtr );
mFileAccessIndexPtr = NULL;
DbgLog( kLogDebug, "CDSLocalPluginNode::LoadFileAccessIndex - database is corrupted attempting to create table, deleting" );
RemoveIndex();
bRecreate = true;
goto TryAgain;
}
DIR *directory = opendir( sPath );
if ( directory != NULL )
{
dirent *entry;
sqlite3_stmt *pStmt = NULL;
while ( (entry = readdir(directory)) != NULL )
{
bool bIndex = true;
char sPath2[PATH_MAX];
if ( entry->d_name[0] == '.' ) continue;
snprintf( sPath2, sizeof(sPath2), "%s/%s", sPath, entry->d_name );
if ( stat(sPath2, &statBuffer) == 0 )
{
snprintf( command, sizeof(command), "SELECT filetime FROM '%s' WHERE filename='%s';", mapping->fRecordNativeType, entry->d_name );
status = sqlite3_prepare_v2( mFileAccessIndexPtr, command, -1, &pStmt, NULL );
if ( status == SQLITE_OK )
status = sqlite3_step( pStmt );
if ( status == SQLITE_ROW )
{
if ( sqlite3_column_int(pStmt, 0) == (int) statBuffer.st_mtimespec.tv_sec )
bIndex = false;
}
if ( pStmt != NULL )
sqlite3_finalize( pStmt );
}
if ( bIndex == true )
{
fDBLock.SignalLock();
DbgLog( kLogPlugin, "CDSLocalPluginNode::LoadFileAccessIndex - re-indexing type <%s> filename <%s>", mapping->fRecordNativeType,
entry->d_name );
AddRecordIndex( mapping->fRecordType, entry->d_name );
fDBLock.WaitLock();
}
}
closedir( directory );
}
iter++;
}
OSAtomicCompareAndSwap32Barrier( false, true, &mUseIndex );
}
fDBLock.SignalLock();
}
char** CDSLocalPluginNode::GetFileAccessIndex( CFStringRef inNativeRecType, tDirPatternMatch inPatternMatch, CFStringRef inPatternToMatch,
CFStringRef inNativeAttrToMatch, bool *outPreferIndex )
{
if ( outPreferIndex != NULL )
(*outPreferIndex) = false;
if ( mUseIndex == false || inPatternToMatch == NULL || (inPatternMatch != eDSExact && inPatternMatch != eDSiExact) )
return NULL;
CFStringRef cfStdType = mPlugin->RecordStandardTypeForNativeType( inNativeRecType );
if ( cfStdType == NULL )
return NULL;
CFStringRef cfStdAttr = mPlugin->AttrStandardTypeForNativeType( inNativeAttrToMatch );
if ( cfStdAttr == NULL )
return NULL;
char *cStr = NULL;
LocalNodeIndexMapI iter = mIndexMap.find( CStrFromCFString(cfStdType, &cStr, NULL, NULL) );
char **fileNames = NULL;
DSFree( cStr );
if ( iter == mIndexMap.end() )
return NULL;
IndexMapping *mapping = iter->second;
CFIndex iIndex = CFArrayGetFirstIndexOfValue( mapping->fAttributesCF, CFRangeMake(0, CFArrayGetCount(mapping->fAttributesCF)),
cfStdAttr );
if ( iIndex != kCFNotFound )
{
char command[512];
sqlite3_stmt *pStmt = NULL;
if ( outPreferIndex != NULL )
(*outPreferIndex) = true;
if ( inPatternMatch == eDSExact )
snprintf( command, sizeof(command), "SELECT filename FROM '%s' WHERE value=? AND recordtype=?;", mapping->fAttributes[iIndex] );
else
snprintf( command, sizeof(command), "SELECT filename FROM '%s' WHERE value LIKE ? AND recordtype=?;", mapping->fAttributes[iIndex] );
fDBLock.WaitLock();
int status = sqlite3_prepare_v2( mFileAccessIndexPtr, command, -1, &pStmt, NULL );
if ( status == SQLITE_OK )
{
if ( CFGetTypeID(inPatternToMatch) == CFStringGetTypeID() )
{
const char *pPattern = CStrFromCFString( inPatternToMatch, &cStr, NULL, NULL );
status = sqlite3_bind_text( pStmt, 1, pPattern, strlen(pPattern), SQLITE_TRANSIENT );
DSFree( cStr );
}
else if ( CFGetTypeID(inPatternToMatch) == CFDataGetTypeID() )
{
CFDataRef cfData = (CFDataRef) inPatternToMatch;
status = sqlite3_bind_text( pStmt, 1, (char *) CFDataGetBytePtr(cfData), CFDataGetLength(cfData), SQLITE_TRANSIENT );
}
}
if ( status == SQLITE_OK )
status = sqlite3_bind_text( pStmt, 2, mapping->fRecordNativeType, strlen(mapping->fRecordNativeType), SQLITE_STATIC );
if ( status == SQLITE_OK )
status = sqlite3_step( pStmt );
int iCount = 0;
int iMax = -1; while ( status == SQLITE_ROW )
{
const char *pTemp = (const char *) sqlite3_column_text( pStmt, 0 );
if ( pTemp != NULL )
{
if ( iCount == iMax || fileNames == NULL )
{
iMax += 20; char **temp = (char **) calloc( iMax + 1, sizeof(char *) );
if ( iCount != 0 )
bcopy( fileNames, temp, sizeof(char *) * iCount );
DSFree( fileNames );
fileNames = temp;
}
fileNames[iCount] = strdup( pTemp );
DbgLog( kLogPlugin, "CDSLocalPluginNode::GetFileAccessIndex - found match in index - type <%s> file <%s>",
mapping->fRecordNativeType, fileNames[iCount] );
iCount++;
}
status = sqlite3_step( pStmt );
}
if ( pStmt != NULL )
sqlite3_finalize( pStmt );
fDBLock.SignalLock();
}
return fileNames;
}
int CDSLocalPluginNode::sqlExecSync(const char *command, UInt32 length)
{
int status = SQLITE_OK;
sqlite3_stmt *pStmt = NULL;
fDBLock.WaitLock();
if ( mFileAccessIndexPtr != NULL )
{
status = sqlite3_prepare_v2( mFileAccessIndexPtr, command, length, &pStmt, NULL );
if (status == SQLITE_OK)
{
status = sqlite3_step( pStmt );
sqlite3_finalize( pStmt );
if ( status == SQLITE_CORRUPT ) {
DbgLog( kLogCritical, "CDSLocalPluginNode::sqlExecSync - SQL database corruption detected, rebuilding" );
DatabaseCorrupt();
}
}
}
fDBLock.SignalLock();
return status;
}
int CDSLocalPluginNode::EnsureDirs( const char *inPath, mode_t inPathMode, mode_t inFinalMode )
{
register const char *szpSrc = inPath;
register char c, *szpPath;
char szPath [PATH_MAX];
struct stat ssStatBuf;
if (!inPath)
return (errno = EFAULT), -1;
if (PATH_MAX < strlen (szpSrc))
return (errno = ENAMETOOLONG), -1;
if (!mkdir (inPath, inFinalMode))
if (!chmod (inPath, inFinalMode))
return 0;
szpPath = szPath;
if (*szpSrc == '/') {
*szpPath++ = '/';
while (*++szpSrc == '/');
}
do {
while ((c = *szpSrc++) && (c != '/'))
*szpPath++ = c;
while (c && ((*szpSrc == '/') || (*szpSrc == '\0')))
c = *szpSrc++;
*szpPath = '\0';
if (stat (szPath, &ssStatBuf)) {
if (errno == ENOENT) {
errno = 0;
if (!mkdir (szPath, (c ? inPathMode : inFinalMode)))
chmod (szPath, (c ? inPathMode : inFinalMode));
}
if (errno)
return -1;
} else if (!S_ISDIR (ssStatBuf.st_mode)) {
return (errno = ENOTDIR), -1;
}
*szpPath++ = '/';
} while (c);
return 0;
}
#endif