#include <fcntl.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <syslog.h>
#include <unistd.h>
#include <openssl/err.h>
#include "DSMutexSemaphore.h"
#include "CAuthFileBase.h"
#include "SASLCode.h"
extern "C" {
#include "saslutil.h"
#if COMPILE_WITH_RSA_LOAD
#include "bufaux.h"
#include "buffer.h"
#include "cipher.h"
#include "xmalloc.h"
#include "ssh.h"
#endif
};
#define kFixedDESKey "1POTATO2potato3PotatoFOUR"
#define kFixedDESChunk 8
#define kMaxWriteSuspendTime 2 // seconds
#define kPWUserIDSize 4*sizeof(long)
#define AUTHFILE_ID_STRING "SSH PRIVATE KEY FILE FORMAT 1.1\n"
#define Max(A,B) (((A) > (B)) ? (A):(B))
extern int errno;
CAuthFileBase::CAuthFileBase()
{
this->Init();
}
CAuthFileBase::CAuthFileBase( const char *inDBFilePath )
{
this->Init();
if ( inDBFilePath != NULL && strlen(inDBFilePath) < sizeof(fFilePath) )
strcpy( fFilePath, inDBFilePath );
}
CAuthFileBase::~CAuthFileBase()
{
this->closePasswordFile();
}
void
CAuthFileBase::Init(void)
{
time_t now;
pwFile = NULL;
freeListFile = NULL;
pwFileHeader.signature = 'null';
pwFileValidated = false;
pwFilePermission[0] = '\0';
pwFileBasePtr = NULL;
pwFileLen = 0;
rsaKey = NULL;
fWriteSuspended = false;
fGotHeader = false;
strcpy( fFilePath, kPWFilePath );
time(&now);
srandom((UInt32)now);
}
int
CAuthFileBase::validateFiles(void)
{
int err;
err = this->validatePasswordFile();
this->validateFreeListFile();
return err;
}
int
CAuthFileBase::validatePasswordFile(void)
{
int err;
struct stat sb;
PWFileHeader dbHeader;
err = lstat( fFilePath, &sb );
if ( err == 0 )
err = this->getHeader( &dbHeader );
if ( err == 0 )
{
if ( pwFile != NULL )
{
if ( pwFileHeader.signature != kPWFileSignature ||
pwFileHeader.version != kPWFileVersion ||
sb.st_size != sizeof(PWFileHeader) + pwFileHeader.numberOfSlotsCurrentlyInFile * sizeof(PWFileEntry) )
{
err = -1;
}
}
else
{
err = -1;
}
}
if ( err == 0 )
pwFileValidated = true;
return err;
}
int
CAuthFileBase::validateFreeListFile(void)
{
return 0;
}
int
CAuthFileBase::createPasswordFile(void)
{
int err = -1;
size_t writeCount;
err = mkdir( kPWDirPath, S_IRWXU );
if ( err != 0 && errno == EEXIST )
err = chmod( kPWDirPath, S_IRWXU );
pwFile = fopen( fFilePath, "w+" );
if ( pwFile != NULL )
{
err = chmod( fFilePath, S_IRUSR | S_IWUSR );
if ( err == -1 )
err = errno;
err = 0;
bzero( &pwFileHeader, sizeof(PWFileHeader) );
pwFileHeader.signature = kPWFileSignature;
pwFileHeader.version = kPWFileVersion;
pwFileHeader.sequenceNumber = 0;
pwFileHeader.numberOfSlotsCurrentlyInFile = kPWFileInitialSlots;
pwFileHeader.deepestSlotUsed = 0;
pwFileHeader.deepestSlotUsedByThisServer = 0;
pwFileHeader.access.usingHistory = false;
pwFileHeader.access.usingExpirationDate = false;
pwFileHeader.access.usingHardExpirationDate = false;
pwFileHeader.access.requiresAlpha = false;
pwFileHeader.access.requiresNumeric = false;
pwFileHeader.access.passwordIsHash = false;
pwFileHeader.access.maxMinutesUntilChangePassword = 0;
pwFileHeader.access.maxMinutesUntilDisabled = 0;
pwFileHeader.access.maxMinutesOfNonUse = 0;
pwFileHeader.access.maxFailedLoginAttempts = 0;
pwFileHeader.access.minChars = 0;
pwFileHeader.access.maxChars = 0;
writeCount = fwrite( &pwFileHeader, sizeof(PWFileHeader), 1, pwFile );
if ( writeCount != 1 )
{
err = -1;
}
if ( err == 0 )
{
PWFileEntry anEntry;
int i;
bzero( &anEntry, sizeof(PWFileEntry) );
for ( i = kPWFileInitialSlots; i > 0; i-- )
{
writeCount = fwrite( &anEntry, sizeof(PWFileEntry), 1, pwFile );
if ( writeCount != 1 )
{
err = -1;
break;
}
}
}
this->closePasswordFile();
if ( err != 0 )
remove( fFilePath );
}
else
{
if ( errno )
err = errno;
}
return err;
}
int
CAuthFileBase::openPasswordFile(const char *mode, Boolean map)
{
int err = 0;
if ( pwFile && strcmp( mode, pwFilePermission ) == 0 )
{
return err;
}
else
{
this->closePasswordFile();
pwFile = fopen( fFilePath, mode );
if ( pwFile )
{
strcpy( pwFilePermission, mode );
if ( map && strcmp( mode, "r" ) == 0 ) {
err = this->mapPasswordFile();
err = 0;
}
}
else
{
err = errno;
if ( err == 0 )
err = -1;
}
}
return err;
}
int
CAuthFileBase::mapPasswordFile(void)
{
int err;
struct stat sb;
int fileNum;
if ( pwFile == NULL )
return -1;
if ( pwFileBasePtr )
{
munmap( pwFileBasePtr, pwFileLen );
pwFileBasePtr = nil;
}
pwFileLen = 0;
err = lstat( fFilePath, &sb );
if ( err == 0 )
pwFileLen = sb.st_size;
if ( pwFileLen > 0 )
{
fileNum = fileno( pwFile );
pwFileBasePtr = (caddr_t) mmap( 0, pwFileLen, PROT_READ | PROT_WRITE, MAP_FILE, fileNum, 0 );
if ( (long)pwFileBasePtr == -1 )
{
err = errno;
if ( err == 0 )
err = -1;
pwFileBasePtr = NULL;
}
}
return err;
}
void
CAuthFileBase::closePasswordFile(void)
{
if ( pwFile )
{
if ( pwFileBasePtr )
{
munmap( pwFileBasePtr, pwFileLen );
pwFileBasePtr = nil;
pwFileLen = 0;
}
fclose( pwFile );
pwFile = nil;
}
fGotHeader = false;
}
void
CAuthFileBase::closeFreeListFile(void)
{
if ( freeListFile )
{
fclose( freeListFile );
freeListFile = nil;
}
}
void
CAuthFileBase::resetPasswordFileState(void)
{
if ( pwFile )
fflush( pwFile );
pwWait();
closePasswordFile();
pwSignal();
rsaWait();
RSA_free( rsaKey );
rsaKey = NULL;
rsaSignal();
}
void
CAuthFileBase::carryOn( void )
{
}
void
CAuthFileBase::pwLock(void)
{
int tries = 3;
if ( pwFile != NULL )
{
while ( flock( fileno(pwFile), LOCK_EX | LOCK_NB ) == -1 && tries-- > 0 )
usleep( 25000 );
}
}
void
CAuthFileBase::pwUnlock(void)
{
if ( pwFile != NULL )
flock( fileno(pwFile), LOCK_UN );
}
void
CAuthFileBase::pwWait(void)
{
}
void
CAuthFileBase::pwSignal(void)
{
}
void
CAuthFileBase::rsaWait(void)
{
}
void
CAuthFileBase::rsaSignal(void)
{
}
int
CAuthFileBase::getHeader( PWFileHeader *outHeader, bool inCanUseCachedCopy )
{
int err = -1;
ssize_t readCount;
if ( outHeader == NULL )
return -1;
if ( inCanUseCachedCopy && fGotHeader )
{
memcpy( outHeader, &pwFileHeader, sizeof(PWFileHeader) );
return 0;
}
pwWait();
err = this->openPasswordFile( "r+", false );
if ( err == 0 && pwFile )
{
if ( pwFileBasePtr != NULL )
{
memcpy( outHeader, pwFileBasePtr, sizeof(PWFileHeader) );
}
else
{
readCount = pread( fileno(pwFile), outHeader, sizeof(PWFileHeader), 0 );
}
memcpy( &pwFileHeader, outHeader, sizeof(PWFileHeader) );
fGotHeader = true;
}
pwSignal();
return err;
}
int
CAuthFileBase::setHeader( const PWFileHeader *inHeader )
{
int err = -1;
long writeCount;
if ( inHeader == NULL )
return -1;
pwWait();
err = this->openPasswordFile( "r+", false );
if ( err == 0 && pwFile )
{
err = fseek( pwFile, 0, SEEK_SET );
if ( err == 0 )
{
if ( inHeader != &pwFileHeader )
memcpy( &pwFileHeader, inHeader, sizeof(PWFileHeader) );
writeCount = fwrite( &pwFileHeader, sizeof(PWFileHeader), 1, pwFile );
if ( writeCount != 1 )
{
err = -1;
}
fflush( pwFile );
}
}
pwSignal();
return err;
}
int
CAuthFileBase::getRSAPublicKey( char *outRSAKeyStr )
{
PWFileHeader dbHeader;
int result = 0;
long len;
if ( outRSAKeyStr == NULL )
return -1;
*outRSAKeyStr = '\0';
result = this->getHeader( &dbHeader, true );
if ( result == 0 )
{
strncpy(outRSAKeyStr, (char *)dbHeader.publicKey, kPWFileMaxPublicKeyBytes);
len = strlen(outRSAKeyStr);
if ( len > 0 && outRSAKeyStr[len-1] == '\n' )
outRSAKeyStr[len-1] = '\0';
}
bzero(&dbHeader, sizeof(dbHeader));
return result;
}
int
CAuthFileBase::loadRSAKeys( void )
{
int result = 0;
#if COMPILE_WITH_RSA_LOAD
PWFileHeader dbHeader;
char passphrase[1] = "";
rsaWait();
if ( rsaKey != NULL )
{
rsaSignal();
return 1;
}
result = this->getHeader( &dbHeader, true );
if ( result == 0 )
{
int check1, check2, cipher_type;
off_t len;
Buffer buffer, decrypted;
char *cp;
CipherContext cipher;
BN_CTX *ctx;
BIGNUM *aux;
time_t now;
len = dbHeader.privateKeyLen;
buffer_init(&buffer);
buffer_append_space(&buffer, &cp, len);
memcpy(cp, dbHeader.privateKey, len);
if (len < strlen(AUTHFILE_ID_STRING) + 1) {
syslog(LOG_INFO, "Bad key.");
buffer_free(&buffer);
rsaSignal();
return 0;
}
for (unsigned int i = 0; i < (unsigned int) strlen(AUTHFILE_ID_STRING) + 1; i++)
if (buffer_get_char(&buffer) != (unsigned char) AUTHFILE_ID_STRING[i]) {
syslog(LOG_INFO, "Bad key.");
buffer_free(&buffer);
rsaSignal();
return 0;
}
cipher_type = buffer_get_char(&buffer);
(void) buffer_get_int(&buffer);
buffer_get_int(&buffer);
rsaKey = RSA_new();
rsaKey->n = BN_new();
buffer_get_bignum(&buffer, rsaKey->n);
rsaKey->e = BN_new();
buffer_get_bignum(&buffer, rsaKey->e);
xfree(buffer_get_string(&buffer, NULL));
if (((cipher_mask1() | SSH_CIPHER_NONE | SSH_AUTHFILE_CIPHER) & (1 << cipher_type)) == 0) {
syslog(LOG_INFO, "Unsupported cipher %.100s used in key.", cipher_name(cipher_type));
buffer_free(&buffer);
goto fail;
}
buffer_init(&decrypted);
buffer_append_space(&decrypted, &cp, buffer_len(&buffer));
cipher_set_key_string(&cipher, cipher_type, passphrase);
cipher_decrypt(&cipher, (unsigned char *) cp,
(unsigned char *) buffer_ptr(&buffer),
buffer_len(&buffer));
buffer_free(&buffer);
check1 = buffer_get_char(&decrypted);
check2 = buffer_get_char(&decrypted);
if (check1 != buffer_get_char(&decrypted) ||
check2 != buffer_get_char(&decrypted)) {
if (strcmp(passphrase, "") != 0)
syslog(LOG_INFO, "Bad passphrase supplied for key.");
buffer_free(&decrypted);
fail:
BN_clear_free(rsaKey->n);
rsaKey->n = NULL;
BN_clear_free(rsaKey->e);
rsaKey->e = NULL;
rsaSignal();
return 0;
}
rsaKey->d = BN_new();
buffer_get_bignum(&decrypted, rsaKey->d);
rsaKey->iqmp = BN_new();
buffer_get_bignum(&decrypted, rsaKey->iqmp);
rsaKey->q = BN_new();
buffer_get_bignum(&decrypted, rsaKey->q);
rsaKey->p = BN_new();
buffer_get_bignum(&decrypted, rsaKey->p);
ctx = BN_CTX_new();
aux = BN_new();
BN_sub(aux, rsaKey->q, BN_value_one());
rsaKey->dmq1 = BN_new();
BN_mod(rsaKey->dmq1, rsaKey->d, aux, ctx);
BN_sub(aux, rsaKey->p, BN_value_one());
rsaKey->dmp1 = BN_new();
BN_mod(rsaKey->dmp1, rsaKey->d, aux, ctx);
BN_clear_free(aux);
BN_CTX_free(ctx);
buffer_free(&decrypted);
time(&now);
srand((int)now);
if ( RSA_blinding_on( rsaKey, NULL ) != 1 )
syslog( LOG_INFO, "could not enable RSA_blinding" );
bzero(&dbHeader, sizeof(dbHeader));
rsaSignal();
return 1;
}
bzero(&dbHeader, sizeof(dbHeader));
rsaSignal();
#else
syslog(LOG_INFO, "RSA key loading not compiled\n");
result = -1;
#endif
return result;
}
int
CAuthFileBase::decryptRSA( unsigned char *inBlob, int inBlobLen, unsigned char *outBlob )
{
int len;
int result = 0;
if ( this->loadRSAKeys() != 1 )
return -1;
rsaWait();
len = RSA_private_decrypt( inBlobLen, inBlob, outBlob, rsaKey, RSA_PKCS1_PADDING );
rsaSignal();
if (len <= 0)
{
syslog( LOG_INFO, "rsa_private_decrypt() failed" );
result = -1;
rsaWait();
RSA_free( rsaKey );
rsaKey = NULL;
rsaSignal();
if ( this->loadRSAKeys() == 1 )
{
rsaWait();
len = RSA_private_decrypt( inBlobLen, inBlob, outBlob, rsaKey, RSA_PKCS1_PADDING );
rsaSignal();
if ( len > 0 )
result = 0;
}
}
return result;
}
int
CAuthFileBase::encryptRSA( unsigned char *inBlob, int inBlobLen, unsigned char *outBlob )
{
int len;
int maxRSASize;
if ( this->loadRSAKeys() != 1 )
return -1;
maxRSASize = RSA_size( rsaKey );
if ( inBlobLen > maxRSASize - 11 )
inBlobLen = maxRSASize - 11;
rsaWait();
len = RSA_public_encrypt( inBlobLen, inBlob, outBlob, rsaKey, RSA_PKCS1_PADDING );
rsaSignal();
if ( len <= 0 ) {
return -1;
}
outBlob[len] = '\0';
return len;
}
int
CAuthFileBase::isWeakAuthMethod( const char *inMethod )
{
int index;
int result;
PWFileHeader dbHeader;
result = this->getHeader( &dbHeader, true );
if ( result != 0 )
return 1;
for ( index = 0; index < kPWFileMaxWeakMethods; index++ )
if ( strcmp( inMethod, dbHeader.weakAuthMethods[index].method ) == 0 )
return 1;
return 0;
}
int
CAuthFileBase::addWeakAuthMethod( const char *inMethod )
{
int index;
PWFileHeader ourHeader;
int result = 0;
pwLock();
result = this->getHeader( &ourHeader );
if ( result == 0 )
{
for ( index = 0; index < kPWFileMaxWeakMethods; index++ )
{
if ( ourHeader.weakAuthMethods[index].method[0] == 0 )
{
strcpy( ourHeader.weakAuthMethods[index].method, inMethod );
result = this->setHeader( &ourHeader );
break;
}
}
}
pwUnlock();
return result;
}
int
CAuthFileBase::removeWeakAuthMethod( const char *inMethod )
{
int index;
PWFileHeader ourHeader;
int result = 0;
pwLock();
result = this->getHeader( &ourHeader );
if ( result == 0 )
{
for ( index = 0; index < kPWFileMaxWeakMethods; index++ )
{
if ( strcmp( inMethod, pwFileHeader.weakAuthMethods[index].method ) == 0 )
{
bzero( pwFileHeader.weakAuthMethods[index].method, SASL_MECHNAMEMAX+1 );
result = this->setHeader( &ourHeader );
break;
}
}
}
pwUnlock();
return result;
}
int
CAuthFileBase::expandDatabase( unsigned long inNumSlots, long *outSlot )
{
int err;
int writeCount;
pwWait();
err = this->openPasswordFile( "r+", false );
if ( err == 0 && pwFile != NULL )
{
err = fseek( pwFile, 0, SEEK_END );
if ( err == 0 )
{
PWFileEntry anEntry;
int i;
bzero( &anEntry, sizeof(PWFileEntry) );
for ( i = inNumSlots; i > 0; i-- )
{
writeCount = fwrite( &anEntry, sizeof(PWFileEntry), 1, pwFile );
if ( writeCount != 1 )
{
err = -1;
break;
}
}
}
if ( err == 0 )
err = fseek( pwFile, 0, SEEK_SET );
if ( err == 0 )
{
pwFileHeader.numberOfSlotsCurrentlyInFile += inNumSlots;
if ( outSlot != NULL )
{
pwFileHeader.deepestSlotUsed++;
pwFileHeader.deepestSlotUsedByThisServer = pwFileHeader.deepestSlotUsed;
*outSlot = pwFileHeader.deepestSlotUsed;
}
writeCount = fwrite( &pwFileHeader, sizeof(PWFileHeader), 1, pwFile );
if ( writeCount != 1 )
{
err = -1;
}
}
}
pwSignal();
return err;
}
long
CAuthFileBase::nextSlot(void)
{
long slot = 0;
int err = -1;
off_t curpos;
long readCount;
PWFileEntry dbEntry;
if ( pwFileValidated )
{
if ( pwFileHeader.deepestSlotUsedByThisServer < pwFileHeader.numberOfSlotsCurrentlyInFile - 1 )
{
err = this->getPasswordRec( pwFileHeader.deepestSlotUsedByThisServer + 1, &dbEntry );
if ( err == 0 &&
dbEntry.time == 0 &&
dbEntry.rnd == 0 &&
dbEntry.sequenceNumber == 0 &&
dbEntry.slot == 0 )
{
pwFileHeader.deepestSlotUsedByThisServer++;
slot = pwFileHeader.deepestSlotUsedByThisServer;
if ( pwFileHeader.deepestSlotUsedByThisServer > pwFileHeader.deepestSlotUsed )
pwFileHeader.deepestSlotUsed = pwFileHeader.deepestSlotUsedByThisServer;
return slot;
}
}
if ( pwFileHeader.deepestSlotUsed < pwFileHeader.numberOfSlotsCurrentlyInFile - 1 )
{
pwFileHeader.deepestSlotUsed++;
pwFileHeader.deepestSlotUsedByThisServer = pwFileHeader.deepestSlotUsed;
slot = pwFileHeader.deepestSlotUsed;
}
else
{
freeListFile = fopen( kFreeListFilePath, "r+" );
if ( freeListFile )
{
err = fseek( freeListFile, -sizeof(long), SEEK_END );
if ( err == 0 )
{
curpos = ftell( freeListFile );
readCount = fread( &slot, sizeof(long), 1, freeListFile );
this->closeFreeListFile();
if ( readCount == 1 )
{
err = truncate( kFreeListFilePath, curpos );
}
else
{
err = -1;
}
}
}
if ( err != 0 )
{
err = this->expandDatabase( kPWFileInitialSlots, &slot );
}
}
}
return slot;
}
void
CAuthFileBase::getGMTime(struct tm *inOutGMT)
{
fUtils.getGMTime( inOutGMT );
}
UInt32
CAuthFileBase::getTimeForRef(void)
{
time_t theTime;
time(&theTime);
return (UInt32)theTime;
}
UInt32
CAuthFileBase::getRandom(void)
{
UInt32 result;
UInt32 uiNow;
time_t now;
result = (UInt32) random();
time(&now);
uiNow = (UInt32)now + result;
srandom(uiNow);
return result;
}
int
CAuthFileBase::addRSAKeys(void)
{
char commandStr[256];
FILE *aFile;
struct stat sb;
int result;
unsigned char *publicKey;
unsigned long publicKeyLen;
unsigned char *privateKey;
unsigned long privateKeyLen;
sprintf(commandStr, "ssh-keygen -t rsa1 -b 1024 -f %s -P \"\"", kTempKeyFile);
aFile = popen( commandStr, "r" );
if ( !aFile )
return -1;
pclose(aFile);
result = stat( kTempKeyFile, &sb );
if ( result != 0 )
return result;
aFile = fopen( kTempKeyFile, "r" );
if ( !aFile )
return -1;
privateKeyLen = (unsigned long)sb.st_size;
privateKey = (unsigned char *) malloc( privateKeyLen + 1 );
fread((char*)privateKey, (unsigned long)sb.st_size, 1, aFile);
fclose(aFile);
sprintf(commandStr, "%s.pub", kTempKeyFile);
result = stat( commandStr, &sb );
if ( result != 0 )
return result;
aFile = fopen( commandStr, "r" );
if ( !aFile )
return -1;
publicKeyLen = (unsigned long)sb.st_size;
publicKey = (unsigned char *) malloc( publicKeyLen + 1 );
fread(publicKey, (unsigned long)sb.st_size, 1, aFile);
fclose(aFile);
result = this->addRSAKeys( publicKey, publicKeyLen, privateKey, privateKeyLen );
remove(commandStr);
remove(kTempKeyFile);
free(privateKey);
free(publicKey);
return result;
}
int
CAuthFileBase::addRSAKeys(
unsigned char *publicKey,
unsigned long publicKeyLen,
unsigned char *privateKey,
unsigned long privateKeyLen )
{
PWFileHeader ourHeader;
int result;
if ( privateKeyLen > kPWFileMaxPrivateKeyBytes )
return -1;
if ( publicKeyLen > kPWFileMaxPublicKeyBytes )
return -1;
result = this->getHeader( &ourHeader );
if ( result != 0 )
return result;
ourHeader.privateKeyLen = privateKeyLen;
memcpy( ourHeader.privateKey, privateKey, privateKeyLen );
ourHeader.publicKeyLen = publicKeyLen;
memcpy( ourHeader.publicKey, publicKey, publicKeyLen );
result = this->setHeader( &ourHeader );
bzero(&ourHeader, sizeof(ourHeader));
return result;
}
int
CAuthFileBase::addGenesisPassword(const char *username, const char *password, PWFileEntry *outPWRec )
{
PWFileHeader dbHeader;
PWFileEntry passwordRec;
int err;
int err2 = 0;
bzero(&passwordRec, sizeof(passwordRec));
passwordRec.time = 0;
passwordRec.rnd = 0;
passwordRec.sequenceNumber = 0;
passwordRec.slot = 1;
passwordRec.access.isDisabled = false;
passwordRec.access.isAdminUser = true;
passwordRec.access.newPasswordRequired = false; passwordRec.access.usingHistory = false;
passwordRec.access.canModifyPasswordforSelf = true;
passwordRec.access.usingExpirationDate = false;
passwordRec.access.usingHardExpirationDate = false;
passwordRec.access.requiresAlpha = false;
passwordRec.access.requiresNumeric = false;
passwordRec.access.passwordIsHash = false;
passwordRec.access.maxMinutesOfNonUse = 0;
passwordRec.access.maxFailedLoginAttempts = 0;
passwordRec.access.minChars = 0;
passwordRec.access.maxChars = 0;
strcpy( passwordRec.usernameStr, (username) ? username : "admin" );
strcpy( passwordRec.passwordStr, (password) ? password : "admin" );
pwLock();
err = this->getHeader( &dbHeader );
if ( err == 0 )
{
if ( dbHeader.sequenceNumber == 0 && dbHeader.deepestSlotUsed == 0 )
{
dbHeader.sequenceNumber++;
dbHeader.deepestSlotUsed++;
dbHeader.deepestSlotUsedByThisServer++;
}
err = this->setPasswordAtSlot( &passwordRec, passwordRec.slot );
if ( err == 0 && outPWRec != NULL )
{
memcpy( outPWRec, &passwordRec, sizeof(PWFileEntry) );
}
err2 = this->setHeader( &dbHeader );
}
pwUnlock();
if ( err == 0 && err2 != 0 )
err = err2;
return err;
}
int
CAuthFileBase::addPassword(PWFileEntry *passwordRec, bool obfuscate)
{
PWFileHeader ignoreHeader;
int err, err2;
pwLock();
err = this->getHeader( &ignoreHeader );
if ( err != 0 )
return err;
passwordRec->time = this->getTimeForRef();
passwordRec->rnd = this->getRandom();
passwordRec->sequenceNumber = ++pwFileHeader.sequenceNumber;
passwordRec->slot = this->nextSlot();
fUtils.getGMTime( (struct tm *)&passwordRec->creationDate );
memcpy( &passwordRec->lastLogin, &passwordRec->creationDate, sizeof(struct tm) );
memcpy( &passwordRec->modDateOfPassword, &passwordRec->creationDate, sizeof(struct tm) );
err = this->setPasswordAtSlot( passwordRec, passwordRec->slot, obfuscate );
err2 = this->setHeader( &pwFileHeader );
pwUnlock();
if ( err == 0 && err2 != 0 )
err = err2;
return err;
}
int
CAuthFileBase::addPasswordAtSlot(PWFileEntry *passwordRec, long slot, bool obfuscate, bool setModDate)
{
PWFileEntry dbEntry;
int err;
bool bGoesInMainDB = false;
err = this->getPasswordRec( passwordRec->slot, &dbEntry, false );
if ( err != 0 )
return err;
if ( passwordRec->time == dbEntry.time &&
passwordRec->rnd == dbEntry.rnd &&
passwordRec->sequenceNumber == dbEntry.sequenceNumber &&
passwordRec->slot == dbEntry.slot )
{
bGoesInMainDB = true;
}
else
if ( dbEntry.time == 0 && dbEntry.rnd == 0 &&
dbEntry.sequenceNumber == 0 && dbEntry.slot == 0 )
{
bGoesInMainDB = true;
}
if ( bGoesInMainDB )
{
err = this->setPasswordAtSlot( passwordRec, passwordRec->slot, obfuscate, setModDate );
}
else
{
err = this->SaveOverflowRecord( passwordRec, obfuscate, setModDate );
}
return err;
}
int
CAuthFileBase::setPasswordAtSlot(PWFileEntry *passwordRec, long slot, bool obfuscate, bool setModDate)
{
long offset;
int err = -1;
int writeCount;
unsigned int encodeLen;
if ( slot > 0 )
{
if ( setModDate )
fUtils.getGMTime( (struct tm *)&passwordRec->modificationDate );
pwWait();
err = this->openPasswordFile( "r+", false );
if ( err == 0 && pwFile )
{
offset = fUtils.slotToOffset( slot );
err = fseek( pwFile, offset, SEEK_SET );
if ( err == 0 )
{
encodeLen = strlen(passwordRec->passwordStr);
encodeLen += (kFixedDESChunk - (encodeLen % kFixedDESChunk));
if ( encodeLen > sizeof(passwordRec->passwordStr) )
encodeLen = sizeof(passwordRec->passwordStr);
if ( obfuscate )
fUtils.DESEncode(kFixedDESKey, passwordRec->passwordStr, encodeLen);
writeCount = fwrite( passwordRec, sizeof(PWFileEntry), 1, pwFile );
if ( obfuscate )
fUtils.DESDecode(kFixedDESKey, passwordRec->passwordStr, encodeLen);
if ( writeCount != 1 )
err = -1;
}
}
pwSignal();
}
return err;
}
#if 0
void
CAuthFileBase::addHashes( const char *inRealm, PWFileEntry *inOutPasswordRec )
{
unsigned char smbntHash[32];
unsigned char smblmHash[16];
long pwLen;
CalculateSMBNTHash(inOutPasswordRec->passwordStr, smbntHash);
strcpy( inOutPasswordRec->digest[0].method, kSMBNTStorageTag );
inOutPasswordRec->digest[0].digest[0] = 64;
ConvertBinaryToHex( smbntHash, 32, &inOutPasswordRec->digest[0].digest[1] );
CalculateSMBLANManagerHash(inOutPasswordRec->passwordStr, smblmHash);
strcpy( inOutPasswordRec->digest[1].method, "*cmusaslsecretSMBLM" );
inOutPasswordRec->digest[1].digest[0] = 32;
ConvertBinaryToHex( smblmHash, 16, &inOutPasswordRec->digest[1].digest[1] );
pwLen = strlen(inOutPasswordRec->passwordStr);
{
HASH HA1;
char userID[35];
this->passwordRecRefToString( inOutPasswordRec, userID );
DigestCalcSecret( (unsigned char *)userID,
(unsigned char *)inRealm,
(unsigned char *)inOutPasswordRec->passwordStr,
pwLen,
HA1 );
strncpy( inOutPasswordRec->digest[2].method, "*cmusaslsecretDIGEST-MD5", SASL_MECHNAMEMAX );
inOutPasswordRec->digest[2].method[SASL_MECHNAMEMAX] = '\0';
inOutPasswordRec->digest[2].digest[0] = HASHLEN;
memcpy( &inOutPasswordRec->digest[2].digest[1], HA1, HASHLEN );
}
{
HMAC_MD5_STATE state;
hmac_md5_precalc( &state, (unsigned char *)inOutPasswordRec->passwordStr, pwLen );
strncpy( inOutPasswordRec->digest[3].method, "*cmusaslsecretCRAM-MD5", SASL_MECHNAMEMAX );
inOutPasswordRec->digest[3].method[SASL_MECHNAMEMAX] = '\0';
inOutPasswordRec->digest[3].digest[0] = sizeof(HMAC_MD5_STATE);
memcpy( &inOutPasswordRec->digest[3].digest[1], &state, sizeof(HMAC_MD5_STATE) );
}
}
#endif
bool
CAuthFileBase::ConvertBinaryToHex( const unsigned char *inData, long len, char *outHexStr )
{
bool result = true;
char *tptr = outHexStr;
char base16table[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
if ( inData == nil || outHexStr == nil )
return false;
for ( int idx = 0; idx < len; idx++ )
{
*tptr++ = base16table[(inData[idx] >> 4) & 0x0F];
*tptr++ = base16table[(inData[idx] & 0x0F)];
}
*tptr = '\0';
return result;
}
int
CAuthFileBase::getPasswordRec(long slot, PWFileEntry *passRec, bool unObfuscate)
{
long offset;
int err = -1;
ssize_t readCount;
if ( slot > 0 )
{
pwWait();
err = this->openPasswordFile( "r+", false );
if ( err == 0 && pwFile )
{
offset = fUtils.slotToOffset( slot );
if ( pwFileBasePtr )
{
memcpy( passRec, pwFileBasePtr + offset, sizeof(PWFileEntry) );
}
else
{
readCount = pread( fileno(pwFile), passRec, sizeof(PWFileEntry), offset );
if ( readCount != sizeof(PWFileEntry) )
{
this->closePasswordFile();
err = -2;
}
}
if ( unObfuscate )
fUtils.DESAutoDecode( kFixedDESKey, passRec->passwordStr );
}
pwSignal();
}
return err;
}
int
CAuthFileBase::getValidPasswordRec(PWFileEntry *passwordRec, bool *outFromSpillBucket, bool unObfuscate)
{
int err;
PWFileEntry dbEntry;
if ( outFromSpillBucket != NULL )
*outFromSpillBucket = false;
err = this->getPasswordRec( passwordRec->slot, &dbEntry, unObfuscate );
if ( err != 0 )
return err;
if ( passwordRec->time == dbEntry.time &&
passwordRec->rnd == dbEntry.rnd &&
passwordRec->sequenceNumber == dbEntry.sequenceNumber &&
passwordRec->slot == dbEntry.slot )
{
memcpy( passwordRec, &dbEntry, sizeof(PWFileEntry) );
}
else
{
err = this->getPasswordRecFromSpillBucket( passwordRec, &dbEntry );
if ( err == 0 )
{
if ( passwordRec->time == dbEntry.time &&
passwordRec->rnd == dbEntry.rnd &&
passwordRec->sequenceNumber == dbEntry.sequenceNumber &&
passwordRec->slot == dbEntry.slot )
{
memcpy( passwordRec, &dbEntry, sizeof(PWFileEntry) );
if ( outFromSpillBucket != NULL )
*outFromSpillBucket = true;
}
else
{
err = -3;
}
}
else
{
err = -3;
}
}
bzero( &dbEntry, sizeof(PWFileEntry) );
return err;
}
int
CAuthFileBase::freeSlot(PWFileEntry *passwordRec)
{
int err;
long slot = passwordRec->slot;
long writeCount;
PWFileEntry deleteRec;
bool fromSpillBucket;
err = this->getValidPasswordRec( passwordRec, &fromSpillBucket );
if ( err == 0 )
{
bzero( &deleteRec, sizeof(PWFileEntry) );
deleteRec.time = passwordRec->time;
deleteRec.rnd = passwordRec->rnd;
deleteRec.sequenceNumber = passwordRec->sequenceNumber;
deleteRec.slot = passwordRec->slot;
fUtils.getGMTime( (struct tm *)&deleteRec.modDateOfPassword );
deleteRec.recordIsDead = true;
if ( fromSpillBucket )
err = this->SaveOverflowRecord( &deleteRec );
else
err = this->setPasswordAtSlot( &deleteRec, slot );
freeListFile = fopen( kFreeListFilePath, "a+" );
if ( freeListFile )
{
writeCount = fwrite( &slot, sizeof(long), 1, freeListFile );
if ( writeCount != 1 )
{
err = -1;
}
this->closeFreeListFile();
}
}
return err;
}
void
CAuthFileBase::passwordRecRefToString(PWFileEntry *inPasswordRec, char *outRefStr)
{
fUtils.passwordRecRefToString( inPasswordRec, outRefStr );
}
int
CAuthFileBase::stringToPasswordRecRef(const char *inRefStr, PWFileEntry *outPasswordRec)
{
return fUtils.stringToPasswordRecRef( inRefStr, outPasswordRec );
}
int
CAuthFileBase::getUserIDFromName(const char *inName, bool inAllUsers, long inMaxBuffSize, char *outID)
{
PWFileHeader dbHeader;
int result = 0;
int err = 0;
UInt32 index;
PWFileEntry passRec;
char theAdminID[256];
long buffRemaining = inMaxBuffSize;
long len;
if ( outID == NULL || buffRemaining < 1 )
return 0;
*outID = '\0';
buffRemaining--;
err = this->getHeader( &dbHeader, true );
if ( err != 0 )
return result;
for ( index = dbHeader.deepestSlotUsed; index > 0; index-- )
{
err = this->getPasswordRec( index, &passRec, false );
if ( err != 0 )
break;
if ( (inAllUsers || passRec.access.isAdminUser) && !passRec.access.isDisabled && strcmp( inName, passRec.usernameStr ) == 0 )
{
if ( result == 1 )
{
if ( buffRemaining < 1 )
break;
strcat( outID, ";" );
buffRemaining--;
}
fUtils.passwordRecRefToString( &passRec, theAdminID );
len = strlen( theAdminID );
if ( buffRemaining <= len )
break;
strcat( outID, theAdminID );
buffRemaining -= len;
result = 1;
}
}
if ( result == 0 )
{
}
return result;
}
#pragma mark -
int
CAuthFileBase::AddPassword( const char *inUser, const char *inPassword, char *outPasswordRef )
{
int result;
PWFileEntry anEntry;
char refStr[256];
if ( strlen(inPassword) > sizeof(anEntry.passwordStr) - 1 )
return kAuthPasswordTooLong;
bzero( &anEntry, sizeof(anEntry) );
anEntry.access.isDisabled = false;
anEntry.access.isAdminUser = false;
anEntry.access.newPasswordRequired = false;
anEntry.access.usingHistory = false;
anEntry.access.canModifyPasswordforSelf = true;
anEntry.access.usingExpirationDate = false;
anEntry.access.usingHardExpirationDate = false;
anEntry.access.requiresAlpha = false;
anEntry.access.requiresNumeric = false;
anEntry.access.passwordIsHash = false;
anEntry.access.maxMinutesOfNonUse = 0;
anEntry.access.maxFailedLoginAttempts = 0;
anEntry.access.minChars = 0;
anEntry.access.maxChars = 0;
strcpy( anEntry.usernameStr, inUser );
strncpy( anEntry.passwordStr, inPassword, sizeof(anEntry.passwordStr) );
result = this->addPassword( &anEntry );
fUtils.passwordRecRefToString( &anEntry, refStr );
strcpy( outPasswordRef, refStr );
return result;
}
#if 0
int
CAuthFileBase::MakeSyncFile( const char *inFileName, time_t inAfterDate, long inTimeSkew, long *outNumRecordsUpdated )
{
PWFileHeader dbHeader;
int result = 0;
int err;
UInt32 index;
PWFileEntry passRec;
CKerberosPrincipal* kerberosRec;
time_t theTime;
FILE *syncFile;
int writeCount;
int zeroLen = 0;
CKerberosPrincipalList kerbList;
if ( inFileName == NULL )
return -1;
if ( outNumRecordsUpdated != NULL )
*outNumRecordsUpdated = 0;
syncFile = fopen( inFileName, "w+" );
if ( syncFile == NULL )
return -1;
err = chmod( inFileName, S_IRUSR | S_IWUSR );
try
{
kerbList.ReadAllPrincipalsFromDB();
writeCount = fwrite( &inAfterDate, sizeof(inAfterDate), 1, syncFile );
if ( writeCount != 1 )
throw( -1 );
err = this->getHeader(&dbHeader);
if ( err != 0 )
throw( err );
writeCount = fwrite( &dbHeader, sizeof(dbHeader), 1, syncFile );
if ( writeCount != 1 )
throw( -1 );
for ( index = dbHeader.deepestSlotUsed; index > 0; index-- )
{
err = this->getPasswordRec( index, &passRec, false );
if ( err != 0 )
throw( err );
theTime = ::timegm( (struct tm *)&passRec.modificationDate ) + inTimeSkew;
if ( theTime >= inAfterDate )
{
writeCount = fwrite( &passRec, sizeof(passRec), 1, syncFile );
if ( writeCount != 1 )
throw( -1 );
if (strlen(passRec.digest[4].digest) > 0)
{
char principalName[600];
strcpy(principalName, passRec.usernameStr);
strcat(principalName, "@");
strcat(principalName, passRec.digest[4].digest);
kerberosRec = kerbList.GetPrincipalByName(principalName);
}
else
kerberosRec = NULL;
if (kerberosRec != NULL)
{
writeCount = kerberosRec->WritePrincipalToFile(syncFile);
delete kerberosRec;
}
else
writeCount = fwrite( &zeroLen, sizeof(zeroLen), 1, syncFile );
if ( writeCount != 1 )
throw( -1 );
if ( outNumRecordsUpdated != NULL )
(*outNumRecordsUpdated)++;
}
}
this->AddOverflowToSyncFile( syncFile, inAfterDate, inTimeSkew, outNumRecordsUpdated );
memset(&passRec, 0, sizeof(passRec));
passRec.slot = (UInt32)-1;
kerbList.DeleteOldPrincipals(inAfterDate);
index = 0;
kerberosRec = kerbList.GetPrincipalByIndex(index++);
while (kerberosRec != NULL)
{
writeCount = fwrite( &passRec, sizeof(passRec), 1, syncFile );
if ( writeCount != 1 )
throw( -1 );
writeCount = kerberosRec->WritePrincipalToFile(syncFile);
if ( writeCount != 1 )
throw( -1 );
kerberosRec = kerbList.GetPrincipalByIndex(index++);
}
}
catch( int error )
{
result = error;
}
fclose( syncFile );
bzero( &dbHeader, sizeof(dbHeader) );
bzero( &passRec, sizeof(passRec) );
return result;
}
#endif
int
CAuthFileBase::GetSyncTimeFromSyncFile( const char *inSyncFile, time_t *outSyncTime )
{
int err;
FILE *syncFile;
int readCount;
unsigned long remoteFileLen;
struct stat sb;
time_t syncTime = 0;
if ( inSyncFile == NULL || outSyncTime == NULL )
return -1;
*outSyncTime = 0;
remoteFileLen = 0;
err = lstat( inSyncFile, &sb );
if ( err == 0 )
remoteFileLen = sb.st_size;
if ( remoteFileLen < sizeof(PWFileHeader) )
return -1;
syncFile = fopen( inSyncFile, "r" );
if ( syncFile == NULL )
return -1;
readCount = fread( &syncTime, sizeof(syncTime), 1, syncFile );
if ( readCount == 1 )
*outSyncTime = syncTime;
fclose( syncFile );
return 0;
}
#if 0
int
CAuthFileBase::ProcessSyncFile( const char *inSyncFile, long inTimeSkew, long *outNumAccepted, long *outNumTrumped )
{
int result = 0;
int err;
UInt32 index;
FILE *syncFile;
time_t localTime, remoteTime;
PWFileHeader localHeader, remoteHeader;
PWFileEntry localRec, remoteRec;
int readCount;
unsigned long remoteFileLen;
struct stat sb;
time_t syncTime = 0;
bool bFromSpillBucket;
bool bNeedsUpdate;
int kerbRecordLen;
CKerberosPrincipal* remoteKerbRec;
CKerberosPrincipal* localKerbRec;
CKerberosPrincipalList kerbList;
if ( inSyncFile == NULL )
return -1;
if ( outNumAccepted != NULL )
*outNumAccepted = 0;
if ( outNumTrumped != NULL )
*outNumTrumped = 0;
remoteFileLen = 0;
err = lstat( inSyncFile, &sb );
if ( err == 0 )
remoteFileLen = sb.st_size;
if ( remoteFileLen < sizeof(PWFileHeader) )
return -1;
syncFile = fopen( inSyncFile, "r" );
if ( syncFile == NULL )
return -1;
pwLock();
try
{
err = this->getHeader( &localHeader );
if ( err != 0 )
throw( err );
readCount = fread( &syncTime, sizeof(syncTime), 1, syncFile );
if ( readCount != 1 )
throw( -1 );
readCount = fread( &remoteHeader, sizeof(remoteHeader), 1, syncFile );
if ( readCount != 1 )
throw( -1 );
if ( localHeader.signature != remoteHeader.signature ||
localHeader.version != remoteHeader.version ||
localHeader.entrySize != remoteHeader.entrySize )
{
throw( -2 );
}
localHeader.sequenceNumber = Max(localHeader.sequenceNumber, remoteHeader.sequenceNumber);
localHeader.deepestSlotUsed = Max(localHeader.deepestSlotUsed, remoteHeader.deepestSlotUsed);
if ( remoteHeader.accessModDate > localHeader.accessModDate )
localHeader.access = remoteHeader.access;
err = this->setHeader( &localHeader );
if ( remoteHeader.numberOfSlotsCurrentlyInFile > localHeader.numberOfSlotsCurrentlyInFile )
this->expandDatabase( remoteHeader.numberOfSlotsCurrentlyInFile - localHeader.numberOfSlotsCurrentlyInFile, NULL );
for ( index = (remoteFileLen - sizeof(PWFileHeader) - sizeof(time_t))/sizeof(PWFileEntry); index > 0; index-- )
{
bNeedsUpdate = false;
readCount = fread( &remoteRec, sizeof(remoteRec), 1, syncFile );
if ( readCount != 1 )
throw( -1 );
readCount = fread( &kerbRecordLen, sizeof(kerbRecordLen), 1, syncFile );
if ( readCount != 1 )
throw( -1 );
localKerbRec = NULL;
if (kerbRecordLen > 0)
remoteKerbRec = kerbList.ReadPrincipalFromFile(syncFile, kerbRecordLen);
else
remoteKerbRec = NULL;
if (remoteKerbRec != NULL)
localKerbRec = CKerberosPrincipal::ReadPrincipalFromDB(remoteKerbRec->GetName());
localRec = remoteRec;
if (remoteRec.slot != (UInt32)-1)
{
err = this->getValidPasswordRec( &localRec, &bFromSpillBucket, false );
if ( err != 0 )
{
localRec = remoteRec;
bNeedsUpdate = true;
}
localTime = ::timegm( (struct tm *)&localRec.modDateOfPassword );
remoteTime = ::timegm( (struct tm *)&remoteRec.modDateOfPassword ) - inTimeSkew;
::gmtime_r( &remoteTime, (struct tm *)&remoteRec.modDateOfPassword );
if ( remoteTime > localTime )
{
memcpy( &localRec.modDateOfPassword, &remoteRec.modDateOfPassword, sizeof(BSDTimeStructCopy) );
memcpy( localRec.passwordStr, remoteRec.passwordStr, sizeof(localRec.passwordStr) );
for ( int idx = 0; idx < kPWFileMaxDigests; idx++ )
localRec.digest[idx] = remoteRec.digest[idx];
bNeedsUpdate = true;
}
else if (localKerbRec != NULL)
remoteKerbRec->CopyPassword(localKerbRec);
localTime = ::timegm( (struct tm *)&localRec.lastLogin );
remoteTime = ::timegm( (struct tm *)&remoteRec.lastLogin ) - inTimeSkew;
::gmtime_r( &remoteTime, (struct tm *)&remoteRec.lastLogin );
if ( remoteTime > localTime )
{
memcpy( &localRec.lastLogin, &remoteRec.lastLogin, sizeof(BSDTimeStructCopy) );
bNeedsUpdate = true;
}
else if (localKerbRec != NULL)
remoteKerbRec->CopyLastLogin(localKerbRec);
localTime = ::timegm( (struct tm *)&localRec.modificationDate );
remoteTime = ::timegm( (struct tm *)&remoteRec.modificationDate ) - inTimeSkew;
::gmtime_r( &remoteTime, (struct tm *)&remoteRec.modificationDate );
if ( remoteTime > localTime )
{
memcpy( &localRec.modificationDate, &remoteRec.modificationDate, sizeof(BSDTimeStructCopy) );
memcpy( &localRec.access, &remoteRec.access, sizeof(PWAccessFeatures) );
memcpy( &localRec.usernameStr, &remoteRec.usernameStr, sizeof(localRec.usernameStr) );
memcpy( &localRec.userdata, &remoteRec.userdata, sizeof(localRec.userdata) );
localRec.failedLoginAttempts = remoteRec.failedLoginAttempts;
localRec.recordIsDead = remoteRec.recordIsDead;
localRec.doNotReplicate = remoteRec.doNotReplicate;
localRec.unused511 = remoteRec.unused511;
bNeedsUpdate = true;
}
if ( bNeedsUpdate )
{
if ( outNumAccepted != NULL )
(*outNumAccepted)++;
err = this->addPasswordAtSlot( &localRec, localRec.slot, false, false );
}
else
{
if (remoteKerbRec != NULL)
delete remoteKerbRec;
if ( outNumTrumped != NULL )
(*outNumTrumped)++;
}
}
else if (remoteKerbRec != NULL)
{
if ((localKerbRec != NULL) && (remoteKerbRec->GetRecordModDate() <= localKerbRec->GetRecordModDate()))
delete remoteKerbRec;
}
if (localKerbRec != NULL)
delete localKerbRec;
}
kerbList.WriteAllPrincipalsToDB();
}
catch( int error )
{
result = error;
}
pwUnlock();
fclose( syncFile );
bzero( &localHeader, sizeof(localHeader) );
bzero( &remoteHeader, sizeof(remoteHeader) );
bzero( &localRec, sizeof(localRec) );
bzero( &remoteRec, sizeof(remoteRec) );
return result;
}
#endif
#pragma mark -
#pragma mark ¥POLICY TESTING¥
#pragma mark -
int
CAuthFileBase::DisableStatus(PWFileEntry *inOutPasswordRec, Boolean *outChanged)
{
PWAccessFeatures *access;
if ( inOutPasswordRec == NULL || outChanged == NULL )
return kAuthFail;
*outChanged = false;
access = &inOutPasswordRec->access;
if ( access->isAdminUser )
return kAuthOK;
if ( inOutPasswordRec->access.isDisabled )
return kAuthUserDisabled;
if ( (access->maxFailedLoginAttempts > 0 && inOutPasswordRec->failedLoginAttempts >= access->maxFailedLoginAttempts) )
{
inOutPasswordRec->access.isDisabled = true;
inOutPasswordRec->failedLoginAttempts = 0;
*outChanged = true;
return kAuthUserDisabled;
}
if ( (access->usingHardExpirationDate && TimeIsStale(&(access->hardExpireDateGMT))) ||
(access->maxMinutesUntilDisabled > 0 && LoginTimeIsStale( &inOutPasswordRec->creationDate, access->maxMinutesUntilDisabled ))
)
{
inOutPasswordRec->access.isDisabled = true;
*outChanged = true;
return kAuthUserDisabled;
}
if ( access->maxMinutesOfNonUse > 0 && LoginTimeIsStale(&inOutPasswordRec->lastLogin, access->maxMinutesOfNonUse) )
return kAuthUserDisabled;
if ( pwFileHeader.access.maxFailedLoginAttempts > 0 &&
inOutPasswordRec->failedLoginAttempts >= pwFileHeader.access.maxFailedLoginAttempts )
{
inOutPasswordRec->access.isDisabled = true;
inOutPasswordRec->failedLoginAttempts = 0;
*outChanged = true;
return kAuthUserDisabled;
}
if ( (pwFileHeader.access.usingHardExpirationDate && TimeIsStale(&pwFileHeader.access.hardExpireDateGMT)) ||
(pwFileHeader.access.maxMinutesUntilDisabled > 0 && LoginTimeIsStale( &inOutPasswordRec->creationDate, pwFileHeader.access.maxMinutesUntilDisabled ))
)
{
inOutPasswordRec->access.isDisabled = true;
*outChanged = true;
return kAuthUserDisabled;
}
if ( pwFileHeader.access.maxMinutesOfNonUse > 0 &&
LoginTimeIsStale(&inOutPasswordRec->lastLogin, pwFileHeader.access.maxMinutesOfNonUse) )
return kAuthUserDisabled;
return kAuthOK;
}
int
CAuthFileBase::ChangePasswordStatus(PWFileEntry *inPasswordRec)
{
if ( inPasswordRec->access.isAdminUser )
return kAuthOK;
if ( (inPasswordRec->access.newPasswordRequired) ||
(inPasswordRec->access.usingExpirationDate && TimeIsStale( &inPasswordRec->access.expirationDateGMT )) ||
(inPasswordRec->access.maxMinutesUntilChangePassword > 0 && LoginTimeIsStale( &inPasswordRec->modDateOfPassword, inPasswordRec->access.maxMinutesUntilChangePassword )) ||
(pwFileHeader.access.usingExpirationDate && TimeIsStale( &pwFileHeader.access.expirationDateGMT )) ||
(pwFileHeader.access.maxMinutesUntilChangePassword > 0 && LoginTimeIsStale( &inPasswordRec->modDateOfPassword, pwFileHeader.access.maxMinutesUntilChangePassword ))
)
{
if ( inPasswordRec->access.canModifyPasswordforSelf || inPasswordRec->access.isAdminUser )
return kAuthPasswordNeedsChange;
else
return kAuthPasswordExpired;
}
return kAuthOK;
}
int
CAuthFileBase::RequiredCharacterStatus(PWFileEntry *inPasswordRec, const char *inPassword)
{
Boolean requiresAlpha = (inPasswordRec->access.requiresAlpha || pwFileHeader.access.requiresAlpha );
Boolean requiresNumeric = (inPasswordRec->access.requiresNumeric || pwFileHeader.access.requiresNumeric );
UInt16 minChars = (inPasswordRec->access.minChars > 0) ? inPasswordRec->access.minChars : pwFileHeader.access.minChars;
UInt16 maxChars = (inPasswordRec->access.maxChars > 0) ? inPasswordRec->access.maxChars : pwFileHeader.access.maxChars;
Boolean passwordCannotBeName = (inPasswordRec->access.passwordCannotBeName || pwFileHeader.access.passwordCannotBeName );
UInt16 len = strlen(inPassword);
if ( len == 0 )
return kAuthPasswordTooShort;
if ( len < minChars )
return kAuthPasswordTooShort;
if ( maxChars > 0 && len > maxChars )
return kAuthPasswordTooLong;
if ( requiresAlpha )
{
Boolean hasAlpha = false;
for ( int index = 0; index < len; index++ )
{
if ( isalpha(inPassword[index]) )
{
hasAlpha = true;
break;
}
}
if ( !hasAlpha )
return kAuthPasswordNeedsAlpha;
}
if ( requiresNumeric )
{
Boolean hasDecimal = false;
for ( int index = 0; index < len; index++ )
{
if ( isdigit(inPassword[index]) )
{
hasDecimal = true;
break;
}
}
if ( !hasDecimal )
return kAuthPasswordNeedsDecimal;
}
if ( passwordCannotBeName )
{
UInt16 unameLen = strlen( inPasswordRec->usernameStr );
UInt16 smallerLen = ((len < unameLen) ? len : unameLen);
if ( strncasecmp( inPassword, inPasswordRec->usernameStr, smallerLen ) == 0 )
return kAuthPasswordNeedsChange;
}
return kAuthOK;
}
#pragma mark -
#pragma mark ¥SPILL-BUCKET ACCESSORS¥
#pragma mark -
int
CAuthFileBase::getPasswordRecFromSpillBucket(PWFileEntry *inRec, PWFileEntry *passRec)
{
PWFileEntry recBuff;
off_t offset = 0;
off_t byteCount;
FILE *fp;
int err = -1;
char uidStr[35];
char buff[35];
unsigned int encodeLen;
if ( inRec == NULL || passRec == NULL )
return -1;
err = this->OpenOverflowFile( inRec, false, &fp );
if ( err != 0 )
return err;
fUtils.passwordRecRefToString( inRec, uidStr );
syslog(LOG_INFO, "looking in spillbucket for: %s", uidStr);
do
{
byteCount = pread( fileno(fp), buff, sizeof(buff), offset );
syslog(LOG_INFO, "found %s", buff);
if ( strncmp( uidStr, buff, 34 ) == 0 )
{
syslog(LOG_INFO, "found it");
byteCount = pread( fileno(fp), (char *)&recBuff, sizeof(recBuff), offset+34 );
encodeLen = strlen(recBuff.passwordStr);
encodeLen += (kFixedDESChunk - (encodeLen % kFixedDESChunk));
if ( encodeLen > sizeof(recBuff.passwordStr) )
encodeLen = sizeof(recBuff.passwordStr);
fUtils.DESDecode(kFixedDESKey, recBuff.passwordStr, encodeLen);
memcpy( passRec, &recBuff, sizeof(PWFileEntry) );
memset( &recBuff, 0, sizeof(recBuff) );
err = 0;
break;
}
offset += sizeof(PWFileEntry) + 34;
}
while ( byteCount == sizeof(uidStr) );
fclose( fp );
return err;
}
int
CAuthFileBase::SaveOverflowRecord( PWFileEntry *inPasswordRec, bool obfuscate, bool setModDate )
{
off_t offset = 0;
off_t byteCount;
FILE *fp;
int err = -1;
char uidStr[35];
char buff[35];
unsigned int encodeLen;
int writeCount;
if ( inPasswordRec == NULL )
return -1;
if ( inPasswordRec->slot <= 0 )
return -1;
err = this->OpenOverflowFile( inPasswordRec, true, &fp );
if ( err != 0 )
return err;
fUtils.passwordRecRefToString( inPasswordRec, uidStr );
if ( setModDate )
fUtils.getGMTime( (struct tm *)&inPasswordRec->modificationDate );
encodeLen = strlen(inPasswordRec->passwordStr);
encodeLen += (kFixedDESChunk - (encodeLen % kFixedDESChunk));
if ( encodeLen > sizeof(inPasswordRec->passwordStr) )
encodeLen = sizeof(inPasswordRec->passwordStr);
if ( obfuscate )
fUtils.DESEncode(kFixedDESKey, inPasswordRec->passwordStr, encodeLen);
err = -1;
do
{
byteCount = pread( fileno(fp), buff, sizeof(buff), offset );
if ( strncmp( uidStr, buff, 34 ) == 0 )
{
byteCount = pwrite( fileno(fp), inPasswordRec, sizeof(PWFileEntry), offset+34 );
err = 0;
break;
}
offset += 34+sizeof(PWFileEntry);
}
while ( byteCount == sizeof(uidStr) );
if ( err == -1 )
{
err = fseek( fp, 0, SEEK_END );
if ( err == 0 )
{
writeCount = fwrite( uidStr, 34, 1, fp );
if ( writeCount != 1 )
err = -1;
}
if ( err == 0 )
{
writeCount = fwrite( inPasswordRec, sizeof(PWFileEntry), 1, fp );
if ( writeCount != 1 )
err = -1;
}
}
if ( obfuscate )
fUtils.DESDecode(kFixedDESKey, inPasswordRec->passwordStr, encodeLen);
fclose( fp );
return err;
}
int
CAuthFileBase::OpenOverflowFile( PWFileEntry *inPasswordRec, bool create, FILE **outFP )
{
char overflowFileName[50];
char overflowPath[1024];
FILE *fp;
int err = -1;
if ( inPasswordRec == NULL || outFP == NULL )
return -1;
*outFP = NULL;
this->PWRecToOverflowFileName( inPasswordRec, overflowFileName );
sprintf( overflowPath, "%s/%s", kPWDirPath, overflowFileName );
fp = fopen( overflowPath, create ? "a+" : "r+" );
if ( fp == NULL )
{
err = errno;
if ( err == 0 )
err = -1;
return err;
}
*outFP = fp;
return 0;
}
void
CAuthFileBase::PWRecToOverflowFileName( PWFileEntry *inPasswordRec, char *outFileName )
{
if ( outFileName == NULL )
return;
strcpy( outFileName, "authserveroverflow.exception" );
if ( inPasswordRec == NULL )
return;
long simpleHash = ( (inPasswordRec->time ^ inPasswordRec->rnd) * (inPasswordRec->sequenceNumber + inPasswordRec->slot) ) % 100;
sprintf( outFileName, "authserveroverflow.%lu", simpleHash );
}