/* * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #import #import #import #import "AuthOverflowFile.h" #import "PSUtilitiesDefs.h" #import "CKerberosPrincipal.h" #define kFixedDESKey "1POTATO2potato3PotatoFOUR" @implementation AuthOverflowFile -(id)init { self = [super init]; mOverflowPath = strdup( kPWDirPath ); return self; } -(id)initWithUTF8Path:(const char *)inOverflowPath { self = [super init]; mOverflowPath = strdup( inOverflowPath ); return self; } -(void)dealloc { if ( mOverflowPath != NULL ) free( mOverflowPath ); [super dealloc]; } -free { [self release]; return 0; } -(void)pwWait { } -(void)pwSignal { } -(FILE *)fopenOverflow:(const char *)path mode:(const char *)mode { return fopen(path, mode); } -(int)fcloseOverflow:(FILE *)filePtr { return fclose(filePtr); } -(int)getPasswordRecFromSpillBucket:(PWFileEntry *)inOutRec unObfuscate:(BOOL)unObfuscate { PWFileEntry recBuff; off_t offset = 0; off_t byteCount; FILE *fp; int err = -1; char uidStr[35] = {0,}; char buff[35] = {0,}; if ( inOutRec == NULL ) return -1; err = [self openOverflowFile:inOutRec create:NO fileDesc:&fp filePath:NULL]; if ( err != 0 ) return err; // still not found err = -1; // use text-based matching to avoid endian problems pwsf_passwordRecRefToString( inOutRec, uidStr ); do { byteCount = pread( fileno(fp), buff, sizeof(buff), offset ); if ( byteCount >= 34 && strncmp( uidStr, buff, 34 ) == 0 ) { // found it byteCount = pread( fileno(fp), (char *)&recBuff, sizeof(recBuff), offset+34 ); if ( byteCount > 0 ) { pwsf_EndianAdjustPWFileEntry( &recBuff, 1 ); // recover the password if ( unObfuscate ) pwsf_DESAutoDecode( recBuff.passwordStr ); // copy the record memcpy( inOutRec, &recBuff, sizeof(PWFileEntry) ); // zero our copy bzero( &recBuff, sizeof(recBuff) ); err = 0; } break; } offset += sizeof(PWFileEntry) + 34; } while ( byteCount == sizeof(uidStr) ); [self fcloseOverflow:fp]; return err; } //------------------------------------------------------------------------------------------------ // saveOverflowRecord // // Returns: -1, errno, or 0 for success // // Updates and existing record in the overflow bucket //------------------------------------------------------------------------------------------------ -(int)saveOverflowRecord:(PWFileEntry *)inPasswordRec obfuscate:(BOOL)obfuscate setModDate:(BOOL)setModDate { off_t offset = 0; off_t byteCount; FILE *fp = NULL; int err = -1; char uidStr[35] = {0,}; char buff[35] = {0,}; int fileNumber = 0; size_t encodeLen; size_t writeCount; char *filePath = NULL; bool bad = false; PWFileEntry passRec; if ( inPasswordRec == NULL ) return -1; if ( inPasswordRec->slot <= 0 ) return -1; if ( mReadOnlyFileSystem ) return -1; err = [self openOverflowFile:inPasswordRec create:YES fileDesc:&fp filePath:&filePath]; if ( err != 0 ) return err; if ( fp == NULL || filePath == NULL ) { err = -1; goto done; } fileNumber = [self getFileNumberFromPath:filePath]; // use text-based matching to avoid endian problems pwsf_passwordRecRefToString( inPasswordRec, uidStr ); if ( setModDate ) pwsf_getGMTime( &inPasswordRec->modificationDate ); encodeLen = strlen(inPasswordRec->passwordStr); encodeLen += (kFixedDESChunk - (encodeLen % kFixedDESChunk)); if ( encodeLen > sizeof(inPasswordRec->passwordStr) ) encodeLen = sizeof(inPasswordRec->passwordStr); if ( obfuscate ) pwsf_DESEncode(inPasswordRec->passwordStr, encodeLen); err = -1; do { byteCount = pread( fileno(fp), buff, sizeof(buff), offset ); if ( byteCount == 0 ) break; bad = (byteCount != sizeof(buff)); if ( !bad ) { pwsf_stringToPasswordRecRef( buff, &passRec ); bad = (fileNumber >= 0 && [self simpleHash:&passRec] != fileNumber); } if ( bad ) { [self pwWait]; if ( offset > 0 ) { ftruncate( fileno(fp), offset ); } else { [self fcloseOverflow:fp]; fp = NULL; unlink( filePath ); free( filePath ); filePath = NULL; } [self pwSignal]; err = -1; break; } if ( byteCount >= 34 && strncmp( uidStr, buff, 34 ) == 0 ) { // found it #if TARGET_RT_LITTLE_ENDIAN memcpy( &passRec, inPasswordRec, sizeof(PWFileEntry) ); pwsf_EndianAdjustPWFileEntry( &passRec, 0 ); byteCount = pwrite( fileno(fp), &passRec, sizeof(PWFileEntry), offset+34 ); bzero( &passRec, sizeof(PWFileEntry) ); #else byteCount = pwrite( fileno(fp), inPasswordRec, sizeof(PWFileEntry), offset+34 ); #endif err = 0; break; } offset += 34+sizeof(PWFileEntry); } while ( byteCount == sizeof(buff) ); // if not found, append the new record if ( err == -1 ) { // if the file was damaged, it may be gone if ( fp == NULL ) { err = [self openOverflowFile:inPasswordRec create:YES fileDesc:&fp filePath:&filePath]; if ( err != 0 ) goto done; if ( fp == NULL || filePath == NULL ) { err = -1; goto done; } } [self pwWait]; err = fseek( fp, 0, SEEK_END ); if ( err == 0 ) { offset = ftell( fp ); byteCount = offset % (34 + sizeof(PWFileEntry)); if ( byteCount != 0 ) { #if DEBUG errmsg( kOverflowInvalidLengthRepairMsg, filePath ); #endif ftruncate( fileno(fp), offset - byteCount ); } writeCount = fwrite( uidStr, 34, 1, fp ); if ( writeCount != 1 ) err = -1; if ( err == 0 ) { #if TARGET_RT_LITTLE_ENDIAN memcpy( &passRec, inPasswordRec, sizeof(PWFileEntry) ); pwsf_EndianAdjustPWFileEntry( &passRec, 0 ); writeCount = fwrite( &passRec, sizeof(PWFileEntry), 1, fp ); bzero( &passRec, sizeof(PWFileEntry) ); #else writeCount = fwrite( inPasswordRec, sizeof(PWFileEntry), 1, fp ); #endif if ( writeCount != 1 ) { err = -1; // We don't know what happened (disk full error?) // However, we must make sure the file is the correct length offset = ftell( fp ); byteCount = offset % (34 + sizeof(PWFileEntry)); if ( byteCount != 0 ) { #if DEBUG errmsg( kOverflowInvalidLengthRepairMsg, filePath ); #endif ftruncate( fileno(fp), offset - byteCount ); } } } fflush( fp ); } [self pwSignal]; } if ( obfuscate ) pwsf_DESDecode(inPasswordRec->passwordStr, encodeLen); done: if ( fp != NULL ) [self fcloseOverflow:fp]; if ( filePath != NULL ) free( filePath ); return err; } //------------------------------------------------------------------------------------------------ // DeleteSlot //------------------------------------------------------------------------------------------------ -(int)deleteSlot:(PWFileEntry *)inPasswordRec { off_t offset = 0; off_t byteCount; FILE *fp; int err = -1; char uidStr[35]; char idBuff[35]; struct stat sb; ssize_t copyLen; unsigned char *buff = NULL; if ( inPasswordRec == NULL || inPasswordRec->slot <= 0 || mReadOnlyFileSystem ) return -1; err = [self openOverflowFile:inPasswordRec create:NO fileDesc:&fp filePath:NULL]; if ( err != 0 ) return err; [self pwWait]; err = fstat( fileno(fp), &sb ); if ( err != 0 ) { [self fcloseOverflow:fp]; #if DEBUG errmsg( "fstat() failed reading overflow file (err = %d).", err ); #endif [self pwSignal]; return err; } // use text-based matching to avoid endian problems pwsf_passwordRecRefToString( inPasswordRec, uidStr ); err = -1; do { byteCount = pread( fileno(fp), idBuff, sizeof(idBuff), offset ); if ( byteCount >= 34 && strncmp(uidStr, idBuff, 34) == 0 ) { // found it #if DEBUG srvmsg( "deleting id: %s", uidStr); #endif copyLen = (ssize_t)sb.st_size - (ssize_t)offset - 34 - sizeof(PWFileEntry); if ( copyLen > 0 ) { buff = (unsigned char *) malloc( copyLen ); if ( buff != NULL ) { byteCount = pread( fileno(fp), buff, copyLen, offset + 34 + sizeof(PWFileEntry) ); if ( byteCount == copyLen ) { byteCount = pwrite( fileno(fp), buff, copyLen, offset ); if ( byteCount == copyLen ) ftruncate( fileno(fp), sb.st_size - 34 - sizeof(PWFileEntry) ); } free( buff ); err = 0; } } else { if ( sb.st_size == 34 + sizeof(PWFileEntry) ) { char overflowFileName[50]; char overflowPath[1024]; [self getOverflowFileName:overflowFileName forRec:inPasswordRec]; sprintf( overflowPath, "%s/%s", kPWDirPath, overflowFileName ); [self fcloseOverflow:fp]; fp = NULL; unlink( overflowPath ); } else { ftruncate( fileno(fp), sb.st_size - 34 - sizeof(PWFileEntry) ); } } break; } offset += 34 + sizeof(PWFileEntry); } while ( byteCount == sizeof(idBuff) ); [self pwSignal]; [self fcloseOverflow:fp]; return err; } //------------------------------------------------------------------------------------------------ // AddOverflowToSyncFile //------------------------------------------------------------------------------------------------ -(void)addOverflowToSyncFile:(FILE *)inSyncFile afterDate:(time_t)inAfterDate timeSkew:(long)inTimeSkew numUpdated:(long *)outNumRecordsUpdated { CFMutableArrayRef fileListArray = NULL; CFIndex index, flCount; CFStringRef filePathString = NULL; off_t offset = 0; off_t byteCount; FILE *fp; time_t theTime; size_t writeCount; PWFileEntry recBuff; const char *filePathPtr = NULL; PWSFKerberosPrincipal* kerberosRec; int zeroLen = 0; if ( [self getOverflowFileList:&fileListArray customPath:NULL] ) { flCount = CFArrayGetCount( fileListArray ); for (index = 0; index < flCount; index++) { filePathString = (CFStringRef) CFArrayGetValueAtIndex( fileListArray, index ); filePathPtr = CFStringGetCStringPtr( filePathString, kCFStringEncodingUTF8 ); if ( filePathPtr == NULL ) continue; fp = [self fopenOverflow:filePathPtr mode:(mReadOnlyFileSystem ? "r" : "r+")]; if ( fp == NULL && errno == EROFS ) { mReadOnlyFileSystem = YES; fp = [self fopenOverflow:filePathPtr mode:"r"]; } if ( fp == NULL ) continue; offset = 0; do { // Because every record gets evaluated for sync, there is no reason to // check the string form of the ID (first 34 chars). byteCount = pread( fileno(fp), (char *)&recBuff, sizeof(recBuff), offset+34 ); // adjust time skew for comparison purposes. The record itself is // adjusted on the processing side. theTime = BSDTimeStructCopy_timegm( &recBuff.modificationDate ) + inTimeSkew; if ( theTime >= inAfterDate ) { writeCount = fwrite( &recBuff, sizeof(recBuff), 1, inSyncFile ); if ( writeCount != 1 ) break; if ( recBuff.digest[4].digest[0] != '\0' ) { char principalName[600] = {0,}; strcpy(principalName, recBuff.usernameStr); strcat(principalName, "@"); strcat(principalName, recBuff.digest[4].digest); PWSFKerberosPrincipal::ReadPrincipalFromDB(principalName, &kerberosRec); } else kerberosRec = NULL; if (kerberosRec != NULL) writeCount = kerberosRec->WritePrincipalToFile(inSyncFile); else writeCount = fwrite( &zeroLen, sizeof(zeroLen), 1, inSyncFile ); if ( outNumRecordsUpdated != NULL ) (*outNumRecordsUpdated)++; } offset += 34 + sizeof(PWFileEntry); } while ( byteCount == sizeof(recBuff) ); [self fcloseOverflow:fp]; } CFRelease( fileListArray ); } } //------------------------------------------------------------------------------------------------ // GetUserRecordFromName // // Returns: 1 if found, 0 if not. // // Only looks at the usernameStr for the user's short name. // Use GetUserRecordFromPrincipal to get the correct name for Kerberos. //------------------------------------------------------------------------------------------------ -(int)getUserRecord:(PWFileEntry *)inOutUserRec fromName:(const char *)inName { return [self doActionForAllOverflowFiles:kOverflowActionGetFromName principal:inName userRec:inOutUserRec purgeBefore:0]; } //------------------------------------------------------------------------------------------------ // GetUserRecordFromPrincipal // // Returns: 1 if found, 0 if not. //------------------------------------------------------------------------------------------------ -(int)getUserRecord:(PWFileEntry *)inOutUserRec fromPrincipal:(const char *)inPrincipal { return [self doActionForAllOverflowFiles:kOverflowActionGetFromPrincipal principal:inPrincipal userRec:inOutUserRec purgeBefore:0]; } -(void)requireNewPasswordForAllAccounts:(BOOL)inRequired { [self doActionForAllOverflowFiles:inRequired ? kOverflowActionRequireNewPassword : kOverflowActionDoNotRequireNewPassword principal:NULL userRec:NULL purgeBefore:0]; } -(void)kerberizeOrNewPassword { [self doActionForAllOverflowFiles:kOverflowActionKerberizeOrNewPassword principal:NULL userRec:NULL purgeBefore:0]; } //------------------------------------------------------------------------------------------------ // DoActionForAllOverflowFiles // // RETURNS: 1 if (inAction==kOverflowActionGetFromPrincipal && userRecord==found), else 0 //------------------------------------------------------------------------------------------------ -(int)doActionForAllOverflowFiles:(OverflowAction)inAction principal:(const char *)inPrincipal userRec:(PWFileEntry *)inOutUserRec purgeBefore:(time_t)beforeSecs { CFMutableArrayRef fileListArray = NULL; CFStringRef filePathString = NULL; CFIndex index, flCount; off_t offset = 0; off_t byteCount = 0; FILE *fp = NULL; PWFileEntry recBuff = {0}; int returnValue = 0; int fileNumber = -1; bool repairedAFile = false; char *thePrincDomain = NULL; long len = 0; bool bad = false; //unsigned long encodeLen = 0; char filePathStr[PATH_MAX] = {0}; char thePrincName[256] = {0,}; if ( mReadOnlyFileSystem ) { if ( inAction == kOverflowActionRequireNewPassword || inAction == kOverflowActionDoNotRequireNewPassword || inAction == kOverflowActionKerberizeOrNewPassword || inAction == kOverflowActionPurgeDeadSlots ) { return 0; } } // prep work if ( inAction == kOverflowActionGetFromPrincipal ) { if ( inPrincipal == NULL || inOutUserRec == NULL ) return 0; // break principal into name and domain thePrincDomain = strchr( inPrincipal, '@' ); if ( thePrincDomain == NULL ) return 0; // must have a principal name len = thePrincDomain - inPrincipal; if ( len == 0 ) return 0; // advance past the '@' thePrincDomain++; // save the name as a c-str strlcpy( thePrincName, inPrincipal, len + 1 ); } if ( [self getOverflowFileList:&fileListArray customPath:NULL] ) { flCount = CFArrayGetCount( fileListArray ); for ( index = 0; index < flCount; index++ ) { filePathString = (CFStringRef) CFArrayGetValueAtIndex( fileListArray, index ); if ( !CFStringGetCString(filePathString, filePathStr, sizeof(filePathStr), kCFStringEncodingUTF8) ) continue; fp = [self fopenOverflow:filePathStr mode:(mReadOnlyFileSystem ? "r" : "r+")]; if ( fp == NULL && errno == EROFS ) { mReadOnlyFileSystem = true; if ( inAction == kOverflowActionRequireNewPassword || inAction == kOverflowActionDoNotRequireNewPassword || inAction == kOverflowActionKerberizeOrNewPassword ) break; fp = [self fopenOverflow:filePathStr mode:"r"]; } if ( fp == NULL ) continue; fileNumber = [self getFileNumberFromPath:filePathStr]; offset = 0; do { byteCount = pread( fileno(fp), (char *)&recBuff, sizeof(recBuff), offset+34 ); if ( byteCount == 0 ) break; // file integrity check bad = (byteCount != sizeof(recBuff)); if ( !bad ) { pwsf_EndianAdjustPWFileEntry( &recBuff, 1 ); bad = (fileNumber >= 0 && [self simpleHash:&recBuff] != fileNumber); } if ( bad ) { if ( offset > 0 ) { #if DEBUG errmsg( kOverflowInvalidRepairMsg, filePathStr ); #endif [self pwWait]; ftruncate( fileno(fp), offset ); [self pwSignal]; } else { [self fcloseOverflow:fp]; fp = NULL; #if DEBUG errmsg( kOverflowInvalidRemoveMsg, filePathStr ); #endif unlink( filePathStr ); } repairedAFile = true; break; } if ( byteCount == sizeof(recBuff) ) { switch( inAction ) { case kOverflowActionRequireNewPassword: recBuff.access.newPasswordRequired = 1; pwrite( fileno(fp), (char *)&recBuff, sizeof(recBuff), offset+34 ); break; case kOverflowActionDoNotRequireNewPassword: recBuff.access.newPasswordRequired = 0; pwrite( fileno(fp), (char *)&recBuff, sizeof(recBuff), offset+34 ); break; case kOverflowActionGetFromName: if ( strcmp( inPrincipal, recBuff.usernameStr ) == 0 ) { memcpy( inOutUserRec, &recBuff, sizeof(PWFileEntry) ); returnValue = 1; break; } break; case kOverflowActionGetFromPrincipal: if ( strcmp( thePrincName, pwsf_GetPrincName(&recBuff) ) == 0 ) { memcpy( inOutUserRec, &recBuff, sizeof(PWFileEntry) ); returnValue = 1; break; } break; case kOverflowActionKerberizeOrNewPassword: if ( recBuff.access.passwordIsHash ) { // new password required recBuff.access.newPasswordRequired = 1; pwrite( fileno(fp), (char *)&recBuff, sizeof(recBuff), offset+34 ); } else { #if 0 // un-obfuscate pwsf_DESAutoDecode( recBuff.passwordStr ); if ( AddPrincipal(pwsf_GetPrincName(&recBuff), recBuff.passwordStr, recBuff.digest[kPWHashSlotKERBEROS].digest, sizeof(recBuff.digest[kPWHashSlotKERBEROS].digest)) ) { if ( recBuff.digest[kPWHashSlotKERBEROS].digest[0] != '\0' ) { strcpy( recBuff.digest[kPWHashSlotKERBEROS].method, "KerberosRealmName" ); strcpy( recBuff.digest[kPWHashSlotKERBEROS_NAME].method, "KerberosPrincName" ); strcpy( recBuff.digest[kPWHashSlotKERBEROS_NAME].digest, pwsf_GetPrincName(&recBuff) ); // re-obfuscate encodeLen = strlen( recBuff.passwordStr ); encodeLen += (kFixedDESChunk - (encodeLen % kFixedDESChunk)); if ( encodeLen > sizeof(recBuff.passwordStr) ) encodeLen = sizeof(recBuff.passwordStr); pwsf_DESEncode( recBuff.passwordStr, encodeLen ); // write pwrite( fileno(fp), (char *)&recBuff, sizeof(recBuff), offset+34 ); } else { recBuff.digest[kPWHashSlotKERBEROS].method[0] = 0; } } else { srvmsg( "Could not add kerberos principal %s.", pwsf_GetPrincName(&recBuff) ); } #endif } break; case kOverflowActionDumpRecords: { char idStr[35] = {0,}; char modDateStr[256] = {0,}; time_t secs; struct tm localTime; pwsf_passwordRecRefToString( &recBuff, idStr ); secs = BSDTimeStructCopy_timegm( &recBuff.modificationDate ); localtime_r( &secs, &localTime ); strftime( modDateStr, sizeof(modDateStr), "%m/%d/%Y %r", &localTime ); printf( "overflow %.2ld: %s %15s\t%s\n", [self simpleHash:&recBuff], idStr, recBuff.usernameStr, modDateStr ); } break; case kOverflowActionPurgeDeadSlots: if ( recBuff.extraAccess.recordIsDead ) { time_t deleteSecs = BSDTimeStructCopy_timegm( &recBuff.modDateOfPassword ); if ( difftime(deleteSecs, beforeSecs) < 0 ) [self deleteSlot:&recBuff]; } break; } bzero( &recBuff, sizeof(recBuff) ); } offset += 34 + sizeof(PWFileEntry); } while ( byteCount == sizeof(recBuff) ); [self fcloseOverflow:fp]; } CFRelease( fileListArray ); } return returnValue; } //------------------------------------------------------------------------------------------------ // GetOverflowFileList // // RETURNS: TRUE if the list is retrieved and contains at least one overflow file //------------------------------------------------------------------------------------------------ -(BOOL)getOverflowFileList:(CFMutableArrayRef *)outFileArray customPath:(const char *)inCustomPath { int result; CFMutableArrayRef fileArray = NULL; const char *pathPtr = mOverflowPath; if ( outFileArray == nil ) return -1; *outFileArray = nil; if ( inCustomPath != NULL ) pathPtr = inCustomPath; result = pwsf_EnumerateDirectory( pathPtr, kOverflowFilePrefix, &fileArray ); if ( result == 0 ) { if ( CFArrayGetCount( fileArray ) == 0 ) { CFRelease( fileArray ); result = -1; } } if ( result == 0 ) *outFileArray = fileArray; return (result == 0); } //------------------------------------------------------------------------------------------------ // openOverflowFile //------------------------------------------------------------------------------------------------ -(int)openOverflowFile:(PWFileEntry *)inPasswordRec create:(BOOL)create fileDesc:(FILE **)outFP filePath:(char **)outFilePath { char overflowFileName[50] = {0,}; char overflowPath[1024] = {0,}; FILE *fp; int err = -1; struct stat sb; if ( inPasswordRec == NULL || outFP == NULL ) return -1; *outFP = NULL; if ( outFilePath != NULL ) *outFilePath = NULL; [self getOverflowFileName:overflowFileName forRec:inPasswordRec]; snprintf( overflowPath, sizeof(overflowPath), "%s/%s", mOverflowPath, overflowFileName ); [self pwWait]; if ( create && lstat(overflowPath, &sb) != 0 ) { // For file creation, don't use the wrapper methods. fp = fopen( overflowPath, "w" ); if ( fp != NULL ) fclose( fp ); else { [self pwSignal]; return -1; } } fp = [self fopenOverflow:overflowPath mode:(mReadOnlyFileSystem ? "r" : "r+")]; if ( fp == NULL && errno == EROFS ) { mReadOnlyFileSystem = YES; fp = [self fopenOverflow:overflowPath mode:"r"]; } [self pwSignal]; if ( fp == NULL ) { err = errno; if ( err == 0 ) err = -1; return err; } *outFP = fp; if ( outFilePath != NULL ) *outFilePath = strdup( overflowPath ); return 0; } //------------------------------------------------------------------------------------------------ // GetFileNumberFromPath //------------------------------------------------------------------------------------------------ -(int)getFileNumberFromPath:(const char *)inFilePath { int fileNumber = -1; const char *numberInPathPtr = NULL; if ( inFilePath != NULL ) { numberInPathPtr = strstr( inFilePath, kOverflowFilePrefix ); if ( numberInPathPtr != NULL ) { numberInPathPtr += sizeof( kOverflowFilePrefix ); sscanf( numberInPathPtr, "%d", &fileNumber ); } } return fileNumber; } //------------------------------------------------------------------------------------------------ // getOverflowFileName // // Returns: void // outFileName <- The name of the file that contains the overflow for a given slot // // The user ID overflows are put into multiple files as an optimization because we are doing // sequential searching. //------------------------------------------------------------------------------------------------ -(void)getOverflowFileName:(char *)outFileName forRec:(PWFileEntry *)inPasswordRec; { if ( outFileName == NULL ) return; if ( inPasswordRec == NULL ) { strcpy( outFileName, kOverflowFilePrefix".exception" ); return; } sprintf( outFileName, "%s.%lu", kOverflowFilePrefix, [self simpleHash:inPasswordRec] ); } //------------------------------------------------------------------------------------------------ // simpleHash //------------------------------------------------------------------------------------------------ -(long)simpleHash:(PWFileEntry *)inPasswordRec { return ( (inPasswordRec->time ^ inPasswordRec->rnd) * (inPasswordRec->sequenceNumber + inPasswordRec->slot) ) % 100; } @end