AuthFile.mm   [plain text]


/*
 * Copyright (c) 2003 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@
 */
/*
 *  AuthFile.cpp
 *  PasswordServer
 */

#if 0
#define DEBUGTIME	1
#else
#define DEBUGTIME	0
#endif

#if DEBUGTIME
//#include "CLog.h"
#include <syslog.h>
#define SRVLOG1(A,B, args...)	syslog(LOG_ALERT, (B), ##args)
#endif

#include <TargetConditionals.h>
#include <CoreServices/CoreServices.h>
#include <CommonCrypto/CommonDigest.h>

#include <sys/param.h>
#include <sys/stat.h>
#include <grp.h>
#include <membership.h>

#include "AuthFile.h"
#include "AuthFilePriv.h"
#include "CAuthFileBase.h"
#include "ReplicaFile.h"
#include "SASLCode.h"
#include "SMBAuth.h"
#include "PSUtilitiesDefs.h"

#define kMaxPolicyStrLen		2048

// from DirServicesConst.h (our layer is below DS)
#define kDSValueAuthAuthorityShadowHash				";ShadowHash;"
#define kDSTagAuthAuthorityShadowHash				"ShadowHash"
#define kDSTagAuthAuthorityBetterHashOnly			"BetterHashOnly"
#define kHashNameListPrefix							"HASHLIST:"

static void pwsf_EndianAdjustPWFileEntryNoTimes( PWFileEntry *inOutEntry, int native );

int32_t gDESKeyArray[] = {	3785, 1706062950, 57253, 290150789, 20358, -1895669319, 31632, -1897719547, 
							14472, -356383814, 45160, 1045764877, 42031, 340922616, 17982, 893499693, 
							26821, 1697433225, 49601, 1067086915, 42371, -1280392416, 46874, -1550815422, 
							47922, -788758000, 7254, -590256566, 22097, 1547481608, 22125, -1323694915 };

#if DEBUGTIME
//------------------------------------------------------------------------------------------------
//	GetTimeAsString
//------------------------------------------------------------------------------------------------

void GetTimeAsString( BSDTimeStructCopy *inTime, char *outString )
{
    sprintf( outString, "%d/%d/%d   %d:%d", 
                inTime->tm_mon + 1,
                inTime->tm_mday,
                inTime->tm_year + 1900,
                inTime->tm_hour,
                inTime->tm_min );
}

void LogTimeAsString( BSDTimeStructCopy *inTime, char *preStr )
{
    char timeStr[256];
    
    GetTimeAsString( inTime, timeStr );
    SRVLOG2( kLogMeta, "%s: %s", preStr, timeStr );
}

#else
#define LogTimeAsString(A,B)
#endif

//------------------------------------------------------------------------------------------------
//	TimeIsStale
//
//	Returns: Boolean value
//------------------------------------------------------------------------------------------------

int TimeIsStale( BSDTimeStructCopy *inTime )
{
    time_t theTime, theGMTime;
    
    // get GMT in seconds
    time(&theGMTime);
    
    // get user time in seconds
    theTime = timegm( (struct tm *)inTime );
    if ( theTime <= 0 )
		theTime = 0x7FFFFFFF;
	
#if DEBUGTIME
    LogTimeAsString( (BSDTimeStructCopy *)inTime, "pwrec" );
    SRVLOG1( kLogMeta, "theTime: %ld, %ld", theGMTime, theTime );
    SRVLOG1( kLogMeta, "diff: %ld", theGMTime - theTime );
#endif
    // broken for dates that push the signing bit?
    //return ( difftime( theGMTime, theTime ) > 0 );
    
    return ( (unsigned long)theGMTime > (unsigned long)theTime );
}


//------------------------------------------------------------------------------------------------
//	LoginTimeIsStale
//
//	Returns: Boolean value
//------------------------------------------------------------------------------------------------

int LoginTimeIsStale( BSDTimeStructCopy *inLastLogin, unsigned long inMaxMinutesOfNonUse )
{
    time_t theTime, theGMTime;
    long maxMinutesOfNonUse = (long)(inMaxMinutesOfNonUse & 0x7FFFFFFF);
	
    // get GMT in seconds
    time(&theGMTime);
    
    // get user time in seconds
    theTime = timegm( (struct tm *)inLastLogin );
//    if ( theTime <= 0 )
//		theTime = theGMTime;

#if DEBUGTIME
    LogTimeAsString( (BSDTimeStructCopy *)inLastLogin, "pwrec" );
    SRVLOG1( kLogMeta, "theGMTime, theTime: %ld, %ld", theGMTime, theTime );
    SRVLOG1( kLogMeta, "diff: %ld", ((theGMTime - theTime)/60) );
    SRVLOG1( kLogMeta, "min-nonuse: %ld", maxMinutesOfNonUse );
    SRVLOG1( kLogMeta, "returning: %d", ( ((theGMTime - theTime)/60) > maxMinutesOfNonUse ) );
#endif
    
    return ( ((theGMTime - theTime)/60) > maxMinutesOfNonUse );
}


//------------------------------------------------------------------------------------------------
//	PWGlobalAccessFeaturesToString
//
//	Prepares the PWGlobalAccessFeatures struct for transmission over our text-based protocol
//------------------------------------------------------------------------------------------------

void PWGlobalAccessFeaturesToString( PWGlobalAccessFeatures *inAccessFeatures, char *outString )
{
    char temp1Str[256];
    char temp2Str[256];
    char temp3Str[512];
	int historyValue = 0;
	
    if ( outString == NULL || inAccessFeatures == NULL )
        throw(-1);
    
    // Boolean values are stored in the struct as single bits. They must be unsigned for
    // display.
    
	if ( inAccessFeatures->usingHistory )
		historyValue = 1 + GlobalHistoryCount(*inAccessFeatures);
	
    sprintf( temp1Str, "%s=%d %s=%d %s=%d %s=%d %s=%d %s=%d ",
                kPWPolicyStr_usingHistory, historyValue,
				kPWPolicyStr_canModifyPasswordforSelf, (inAccessFeatures->noModifyPasswordforSelf == 0),
                kPWPolicyStr_usingExpirationDate, (inAccessFeatures->usingExpirationDate != 0),
                kPWPolicyStr_usingHardExpirationDate, (inAccessFeatures->usingHardExpirationDate != 0),
                kPWPolicyStr_requiresAlpha, (inAccessFeatures->requiresAlpha != 0),
                kPWPolicyStr_requiresNumeric, (inAccessFeatures->requiresNumeric != 0) );
                
    sprintf( temp2Str,
#ifdef __LP64__
				"%s=%lu %s=%lu %s=%u ",
#else
				"%s=%lu %s=%lu %s=%lu ",
#endif
                kPWPolicyStr_expirationDateGMT, timegm( (struct tm *)&inAccessFeatures->expirationDateGMT ),
                kPWPolicyStr_hardExpireDateGMT, timegm( (struct tm *)&inAccessFeatures->hardExpireDateGMT ),
                kPWPolicyStr_maxMinutesUntilChangePW, inAccessFeatures->maxMinutesUntilChangePassword );
    
    sprintf( temp3Str,
#ifdef __LP64__
				"%s=%u %s=%u %s=%u %s=%u %s=%u %s=%d %s=%d %s=%d %s=%d ",
#else
				"%s=%lu %s=%lu %s=%u %s=%u %s=%u %s=%d %s=%d %s=%d %s=%d ",
#endif
                kPWPolicyStr_maxMinutesUntilDisabled, inAccessFeatures->maxMinutesUntilDisabled,
                kPWPolicyStr_maxMinutesOfNonUse, inAccessFeatures->maxMinutesOfNonUse,
                kPWPolicyStr_maxFailedLoginAttempts, inAccessFeatures->maxFailedLoginAttempts,
                kPWPolicyStr_minChars, inAccessFeatures->minChars,
                kPWPolicyStr_maxChars, inAccessFeatures->maxChars,
				kPWPolicyStr_passwordCannotBeName, (inAccessFeatures->passwordCannotBeName != 0),
				kPWPolicyStr_requiresMixedCase, (inAccessFeatures->requiresMixedCase != 0),
				kPWPolicyStr_requiresSymbol, (inAccessFeatures->requiresSymbol != 0),
				kPWPolicyStr_newPasswordRequired, (inAccessFeatures->newPasswordRequired != 0) );
    
    strcpy( outString, temp1Str );
    strcat( outString, temp2Str );
    strcat( outString, temp3Str );
}


//------------------------------------------------------------------------------------------------
//	PWGlobalAccessFeaturesToStringExtra
//------------------------------------------------------------------------------------------------

void PWGlobalAccessFeaturesToStringExtra( PWGlobalAccessFeatures *inAccessFeatures, PWGlobalMoreAccessFeatures *inExtraFeatures,
	int inMaxLen, char *outString )
{
    char tempStr[2048];
	long len;
	
    if ( outString == NULL || inAccessFeatures == NULL )
        return;
	
	PWGlobalAccessFeaturesToString( inAccessFeatures, tempStr );
	len = snprintf( outString, inMaxLen, "%s", tempStr );
	if ( len >= inMaxLen )
		return;
	
	snprintf( outString + len, inMaxLen - len,
#ifdef __LP64__
				"%s=%u %s=%u",
#else
				"%s=%lu %s=%lu",
#endif
				kPWPolicyStr_minutesUntilFailedLoginReset, inExtraFeatures->minutesUntilFailedLoginReset,
				kPWPolicyStr_notGuessablePattern, inExtraFeatures->notGuessablePattern );
}


//------------------------------------------------------------------------------------------------
//	PWAccessFeaturesToString
//
//	Prepares the PWAccessFeatures struct for transmission over our text-based protocol
//------------------------------------------------------------------------------------------------

void PWAccessFeaturesToString( PWAccessFeatures *inAccessFeatures, char *outString )
{
    char temp1Str[256];
    char temp2Str[2048];
    char temp3Str[64];
	
    if ( outString == NULL || inAccessFeatures == NULL )
        throw(-1);

    // Boolean values are stored in the struct as single bits. They must be unsigned for
    // display.
    
    snprintf( temp1Str, sizeof(temp1Str), "%s=%d %s=%d %s=%d ",
                kPWPolicyStr_isDisabled, (inAccessFeatures->isDisabled != 0),
                kPWPolicyStr_isAdminUser, (inAccessFeatures->isAdminUser != 0),
                kPWPolicyStr_newPasswordRequired, (inAccessFeatures->newPasswordRequired != 0) );
    
	PWAccessFeaturesToStringWithoutStateInfo( inAccessFeatures, temp2Str );
	
	snprintf( temp3Str, sizeof(temp3Str), " %s=%d",
				kPWPolicyStr_isSessionKeyAgent, (inAccessFeatures->isSessionKeyAgent != 0) );
    
    strcpy( outString, temp1Str );
    strcat( outString, temp2Str );
    strcat( outString, temp3Str );
}


//------------------------------------------------------------------------------------------------
//	PWAccessFeaturesToStringExtra
//
//	Prepares the PWAccessFeatures struct for transmission over our text-based protocol
//------------------------------------------------------------------------------------------------

void PWAccessFeaturesToStringExtra( PWAccessFeatures *inAccessFeatures, PWMoreAccessFeatures *inExtraFeatures, int inMaxLen,
	char *outString )
{
    char temp2Str[2048];
	
    if ( outString == NULL || inAccessFeatures == NULL )
        return;
	
    // Boolean values are stored in the struct as single bits. They must be unsigned for
    // display.
    
    int segment1Length = snprintf( outString, inMaxLen, "%s=%d %s=%d %s=%d ",
							kPWPolicyStr_isDisabled, (inAccessFeatures->isDisabled != 0),
							kPWPolicyStr_isAdminUser, (inAccessFeatures->isAdminUser != 0),
							kPWPolicyStr_newPasswordRequired, (inAccessFeatures->newPasswordRequired != 0) );
	inMaxLen -= segment1Length;
	
	PWAccessFeaturesToStringWithoutStateInfoExtra( inAccessFeatures, inExtraFeatures, sizeof(temp2Str), temp2Str );
	int segment2Length = snprintf( outString + segment1Length, inMaxLen, "%s", temp2Str );
	inMaxLen -= segment2Length;

	snprintf( outString + segment1Length + segment2Length, inMaxLen, " %s=%d %s=%d %s=%u %s=%d %s=%d %s=%d %s=%d %s=%d %s=%d",
				kPWPolicyStr_isSessionKeyAgent, (inAccessFeatures->isSessionKeyAgent != 0),
				kPWPolicyStr_isComputerAccount, (inExtraFeatures->isComputerAccount != 0),
				kPWPolicyStr_adminClass, inExtraFeatures->adminClass,
				kPWPolicyStr_adminNoChangePasswords, (inExtraFeatures->adminNoChangePasswords != 0),
				kPWPolicyStr_adminNoSetPolicies, (inExtraFeatures->adminNoSetPolicies != 0),
				kPWPolicyStr_adminNoCreate, (inExtraFeatures->adminNoCreate != 0),
				kPWPolicyStr_adminNoDelete, (inExtraFeatures->adminNoDelete != 0),
				kPWPolicyStr_adminNoClearState, (inExtraFeatures->adminNoClearState != 0),
				kPWPolicyStr_adminNoPromoteAdmins, (inExtraFeatures->adminNoPromoteAdmins != 0) );
}


//------------------------------------------------------------------------------------------------
//	PWActualAccessFeaturesToString
//
//	Prepares the PWAccessFeatures struct and PWGlobalAccessFeatures defaults for transmission
//	over our text-based protocol
//------------------------------------------------------------------------------------------------

void PWActualAccessFeaturesToString( PWGlobalAccessFeatures *inGAccessFeatures, PWAccessFeatures *inAccessFeatures, char *outString )
{
	int historyValue = 0;
	int usingExpirationDate;
    int usingHardExpirationDate;
    int requiresAlpha;
    int requiresNumeric;
	int passwordCannotBeName;
	UInt32 maxMinutesUntilChangePassword;
    UInt32 maxMinutesUntilDisabled;
    UInt32 maxMinutesOfNonUse;
    UInt16 maxFailedLoginAttempts;
    UInt16 minChars;
    UInt16 maxChars;
	
	// TODO: get the actual time values for expiration dates
	
	char temp1Str[256];
    char temp2Str[256];
    char temp3Str[256];
    char temp4Str[512];
	
    if ( outString == NULL || inAccessFeatures == NULL )
        throw(-1);

	// get values for policies that can be in either the user record
	// or the global record
	usingExpirationDate = (inAccessFeatures->usingExpirationDate != 0);
	if ( usingExpirationDate == 0 )
		usingExpirationDate = (inGAccessFeatures->usingExpirationDate != 0);
	
	usingHardExpirationDate = (inAccessFeatures->usingHardExpirationDate != 0);
	if ( usingHardExpirationDate == 0 )
		usingHardExpirationDate = (inGAccessFeatures->usingHardExpirationDate != 0);
	
	requiresAlpha = (inAccessFeatures->requiresAlpha != 0);
	if ( requiresAlpha == 0 )
		requiresAlpha = (inGAccessFeatures->requiresAlpha != 0);
	
	requiresNumeric = (inAccessFeatures->requiresNumeric != 0);
	if ( requiresNumeric == 0 )
		requiresNumeric = (inGAccessFeatures->requiresNumeric != 0);

	passwordCannotBeName = (inAccessFeatures->passwordCannotBeName != 0);
	if ( passwordCannotBeName == 0 )
		passwordCannotBeName = (inGAccessFeatures->passwordCannotBeName != 0);
	
	maxMinutesUntilChangePassword = inAccessFeatures->maxMinutesUntilChangePassword;
	if ( maxMinutesUntilChangePassword == 0 )
		maxMinutesUntilChangePassword = inGAccessFeatures->maxMinutesUntilChangePassword;
	
	maxMinutesUntilDisabled = inAccessFeatures->maxMinutesUntilDisabled;
	if ( maxMinutesUntilDisabled == 0 )
		maxMinutesUntilDisabled = inGAccessFeatures->maxMinutesUntilDisabled;
	
	maxMinutesOfNonUse = inAccessFeatures->maxMinutesOfNonUse;
	if ( maxMinutesOfNonUse == 0 )
		maxMinutesOfNonUse = inGAccessFeatures->maxMinutesOfNonUse;
	
	maxFailedLoginAttempts = inAccessFeatures->maxFailedLoginAttempts;
	if ( maxFailedLoginAttempts == 0 )
		maxFailedLoginAttempts = inGAccessFeatures->maxFailedLoginAttempts;
	
	minChars = inAccessFeatures->minChars;
	if ( minChars == 0 )
		minChars = inGAccessFeatures->minChars;
	
	maxChars = inAccessFeatures->maxChars;
	if ( maxChars == 0 )
		maxChars = inGAccessFeatures->maxChars;
	
	
    // Boolean values are stored in the struct as single bits. They must be unsigned for
    // display.
    
	if ( inAccessFeatures->usingHistory )
		historyValue = 1 + inAccessFeatures->historyCount;
	else
	if ( inGAccessFeatures->usingHistory )
		historyValue = 1 + GlobalHistoryCount(*inGAccessFeatures);
	
    sprintf( temp1Str, "%s=%d %s=%d %s=%d %s=%d ",
                kPWPolicyStr_isDisabled, (inAccessFeatures->isDisabled != 0),
                kPWPolicyStr_isAdminUser, (inAccessFeatures->isAdminUser != 0),
                kPWPolicyStr_newPasswordRequired, (inAccessFeatures->newPasswordRequired != 0),
                kPWPolicyStr_usingHistory, historyValue );
    
	sprintf( temp2Str, "%s=%d %s=%d %s=%d %s=%d ",
                kPWPolicyStr_canModifyPasswordforSelf, (inAccessFeatures->canModifyPasswordforSelf != 0),
                kPWPolicyStr_usingExpirationDate, usingExpirationDate,
                kPWPolicyStr_usingHardExpirationDate, usingHardExpirationDate,
                kPWPolicyStr_requiresAlpha, requiresAlpha );
    
    sprintf( temp3Str, "%s=%d %s=%lu %s=%lu ",
                kPWPolicyStr_requiresNumeric, requiresNumeric,
                kPWPolicyStr_expirationDateGMT, timegm( (struct tm *)&inAccessFeatures->expirationDateGMT ),
                kPWPolicyStr_hardExpireDateGMT, timegm( (struct tm *)&inAccessFeatures->hardExpireDateGMT ) );
    
    snprintf( temp4Str, sizeof(temp4Str),
#ifdef __LP64__
				"%s=%u %s=%u %s=%u %s=%u %s=%u %s=%u %s=%d %s=%d",
#else
				"%s=%lu %s=%lu %s=%lu %s=%u %s=%u %s=%u %s=%d %s=%d",
#endif
                kPWPolicyStr_maxMinutesUntilChangePW, maxMinutesUntilChangePassword,
                kPWPolicyStr_maxMinutesUntilDisabled, maxMinutesUntilDisabled,
                kPWPolicyStr_maxMinutesOfNonUse, maxMinutesOfNonUse,
                kPWPolicyStr_maxFailedLoginAttempts, maxFailedLoginAttempts,
                kPWPolicyStr_minChars, minChars,
                kPWPolicyStr_maxChars, maxChars,
				kPWPolicyStr_passwordCannotBeName, passwordCannotBeName,
				kPWPolicyStr_isSessionKeyAgent, (inAccessFeatures->isSessionKeyAgent != 0) );
    
    strcpy( outString, temp1Str );
    strcat( outString, temp2Str );
    strcat( outString, temp3Str );
    strcat( outString, temp4Str );
}


//------------------------------------------------------------------------------------------------
//	PWActualAccessFeaturesToStringWithoutStateInfoExtra
//
//	Prepares the PWAccessFeatures struct and PWGlobalAccessFeatures defaults for transmission
//	over our text-based protocol
//------------------------------------------------------------------------------------------------

void PWActualAccessFeaturesToStringWithoutStateInfoExtra(
	PWGlobalAccessFeatures *inGAccessFeatures,
	PWAccessFeatures *inAccessFeatures,
	char **outString )
{
	int historyValue = 0;
	int usingExpirationDate;
    int usingHardExpirationDate;
    int requiresAlpha;
    int requiresNumeric;
	int passwordCannotBeName;
	UInt32 maxMinutesUntilChangePassword;
    UInt32 maxMinutesUntilDisabled;
    UInt32 maxMinutesOfNonUse;
    UInt16 maxFailedLoginAttempts;
    UInt16 minChars;
    UInt16 maxChars;
	/*
	unsigned int requiresMixedCase = (inExtraFeatures->requiresMixedCase || inGAccessFeatures->requiresMixedCase);
	unsigned int requiresSymbol = (inExtraFeatures->requiresSymbol || inGAccessFeatures->requiresSymbol);
	unsigned long notGuessablePattern = inExtraFeatures->notGuessablePattern;
	*/
	
	char tempStr[512];
	
    if ( outString == NULL || inAccessFeatures == NULL )
        throw(-1);
	
	// get values for policies that can be in either the user record
	// or the global record
	usingExpirationDate = (inAccessFeatures->usingExpirationDate != 0);
	if ( usingExpirationDate == 0 )
		usingExpirationDate = (inGAccessFeatures->usingExpirationDate != 0);
	
	usingHardExpirationDate = (inAccessFeatures->usingHardExpirationDate != 0);
	if ( usingHardExpirationDate == 0 )
		usingHardExpirationDate = (inGAccessFeatures->usingHardExpirationDate != 0);
	
	requiresAlpha = (inAccessFeatures->requiresAlpha != 0);
	if ( requiresAlpha == 0 )
		requiresAlpha = (inGAccessFeatures->requiresAlpha != 0);
	
	requiresNumeric = (inAccessFeatures->requiresNumeric != 0);
	if ( requiresNumeric == 0 )
		requiresNumeric = (inGAccessFeatures->requiresNumeric != 0);

	passwordCannotBeName = (inAccessFeatures->passwordCannotBeName != 0);
	if ( passwordCannotBeName == 0 )
		passwordCannotBeName = (inGAccessFeatures->passwordCannotBeName != 0);
	
	maxMinutesUntilChangePassword = inAccessFeatures->maxMinutesUntilChangePassword;
	if ( maxMinutesUntilChangePassword == 0 )
		maxMinutesUntilChangePassword = inGAccessFeatures->maxMinutesUntilChangePassword;
	
	maxMinutesUntilDisabled = inAccessFeatures->maxMinutesUntilDisabled;
	if ( maxMinutesUntilDisabled == 0 )
		maxMinutesUntilDisabled = inGAccessFeatures->maxMinutesUntilDisabled;
	
	maxMinutesOfNonUse = inAccessFeatures->maxMinutesOfNonUse;
	if ( maxMinutesOfNonUse == 0 )
		maxMinutesOfNonUse = inGAccessFeatures->maxMinutesOfNonUse;
	
	maxFailedLoginAttempts = inAccessFeatures->maxFailedLoginAttempts;
	if ( maxFailedLoginAttempts == 0 )
		maxFailedLoginAttempts = inGAccessFeatures->maxFailedLoginAttempts;
	
	minChars = inAccessFeatures->minChars;
	if ( minChars == 0 )
		minChars = inGAccessFeatures->minChars;
	
	maxChars = inAccessFeatures->maxChars;
	if ( maxChars == 0 )
		maxChars = inGAccessFeatures->maxChars;
	
	
    // Boolean values are stored in the struct as single bits. They must be unsigned for
    // display.
    
	if ( inAccessFeatures->usingHistory )
		historyValue = 1 + inAccessFeatures->historyCount;
	else
	if ( inGAccessFeatures->usingHistory )
		historyValue = 1 + GlobalHistoryCount(*inGAccessFeatures);
	
	*outString = (char *)calloc( 1, kMaxPolicyStrLen );
	
    snprintf( *outString, kMaxPolicyStrLen, "%s=%d %s=%d %s=%d %s=%d %s=%d %s=%d %s=%d %s=%lu %s=%lu ",
                kPWPolicyStr_newPasswordRequired, (inAccessFeatures->newPasswordRequired != 0),
                kPWPolicyStr_usingHistory, historyValue,
                kPWPolicyStr_canModifyPasswordforSelf, (inAccessFeatures->canModifyPasswordforSelf != 0),
                kPWPolicyStr_usingExpirationDate, usingExpirationDate,
                kPWPolicyStr_usingHardExpirationDate, usingHardExpirationDate,
                kPWPolicyStr_requiresAlpha, requiresAlpha,
                kPWPolicyStr_requiresNumeric, requiresNumeric,
                kPWPolicyStr_expirationDateGMT, timegm( (struct tm *)&inAccessFeatures->expirationDateGMT ),
                kPWPolicyStr_hardExpireDateGMT, timegm( (struct tm *)&inAccessFeatures->hardExpireDateGMT ) );
    
    snprintf( tempStr, sizeof(tempStr),
#ifdef __LP64__
				"%s=%u %s=%u %s=%u %s=%u %s=%u %s=%u %s=%d %s=%d",
#else
				"%s=%lu %s=%lu %s=%lu %s=%u %s=%u %s=%u %s=%d %s=%d",
#endif
                kPWPolicyStr_maxMinutesUntilChangePW, maxMinutesUntilChangePassword,
                kPWPolicyStr_maxMinutesUntilDisabled, maxMinutesUntilDisabled,
                kPWPolicyStr_maxMinutesOfNonUse, maxMinutesOfNonUse,
                kPWPolicyStr_maxFailedLoginAttempts, maxFailedLoginAttempts,
                kPWPolicyStr_minChars, minChars,
                kPWPolicyStr_maxChars, maxChars,
				kPWPolicyStr_passwordCannotBeName, passwordCannotBeName,
				kPWPolicyStr_isSessionKeyAgent, (inAccessFeatures->isSessionKeyAgent != 0) );
    
    strlcat( *outString, tempStr, kMaxPolicyStrLen );
	
	/*
	snprintf( tempStr, sizeof(tempStr), " %s=%d %s=%d %s=%lu %s=%u %s=%d %s=%d %s=%d %s=%d %s=%d %s=%d",
			kPWPolicyStr_requiresMixedCase, (requiresMixedCase != 0),
			kPWPolicyStr_requiresSymbol, (requiresSymbol != 0),
			kPWPolicyStr_notGuessablePattern, notGuessablePattern,
			kPWPolicyStr_adminClass, inExtraFeatures->adminClass,
			kPWPolicyStr_adminNoChangePasswords, (inExtraFeatures->adminNoChangePasswords != 0),
			kPWPolicyStr_adminNoSetPolicies, (inExtraFeatures->adminNoSetPolicies != 0),
			kPWPolicyStr_adminNoCreate, (inExtraFeatures->adminNoCreate != 0),
			kPWPolicyStr_adminNoDelete, (inExtraFeatures->adminNoDelete != 0),
			kPWPolicyStr_adminNoClearState, (inExtraFeatures->adminNoClearState != 0),
			kPWPolicyStr_adminNoPromoteAdmins, (inExtraFeatures->adminNoPromoteAdmins != 0) );
	
    strlcat( *outString, tempStr, kMaxPolicyStrLen );
	*/
}


//------------------------------------------------------------------------------------------------
//	PWActualAccessFeaturesToStringExtra
//
//	Prepares the PWAccessFeatures struct and PWGlobalAccessFeatures defaults for transmission
//	over our text-based protocol
//------------------------------------------------------------------------------------------------

void PWActualAccessFeaturesToStringExtra( PWGlobalAccessFeatures *inGAccessFeatures, PWAccessFeatures *inAccessFeatures,
	PWMoreAccessFeatures *inExtraFeatures, int inMaxLen, char *outString )
{
	int segment1Length = 0;
	int segment2Length = 0;
	unsigned int requiresMixedCase = (inExtraFeatures->requiresMixedCase || inGAccessFeatures->requiresMixedCase);
	unsigned int requiresSymbol = (inExtraFeatures->requiresSymbol || inGAccessFeatures->requiresSymbol);
	unsigned long notGuessablePattern = inExtraFeatures->notGuessablePattern;
	
	if ( inMaxLen > 1280 )
	{
		PWActualAccessFeaturesToString( inGAccessFeatures, inAccessFeatures, outString );
		segment1Length = strlen( outString );
	}
	else
	{
		char tempStr[1280];
		
		PWActualAccessFeaturesToString( inGAccessFeatures, inAccessFeatures, tempStr );
		segment1Length = snprintf( outString, inMaxLen, "%s", tempStr );
	}
	inMaxLen -= segment1Length;
	
	if ( notGuessablePattern == 0 )
		notGuessablePattern = 0/*inGAccessFeatures->notGuessablePattern*/;
	
	segment2Length = snprintf( outString + segment1Length, inMaxLen, " %s=%d %s=%d %s=%lu",
			kPWPolicyStr_requiresMixedCase, (requiresMixedCase != 0),
			kPWPolicyStr_requiresSymbol, (requiresSymbol != 0),
			kPWPolicyStr_notGuessablePattern, notGuessablePattern );
	inMaxLen -= segment2Length;
	
	snprintf( outString + segment1Length + segment2Length, inMaxLen, " %s=%u %s=%d %s=%d %s=%d %s=%d %s=%d %s=%d",
			kPWPolicyStr_adminClass, inExtraFeatures->adminClass,
			kPWPolicyStr_adminNoChangePasswords, (inExtraFeatures->adminNoChangePasswords != 0),
			kPWPolicyStr_adminNoSetPolicies, (inExtraFeatures->adminNoSetPolicies != 0),
			kPWPolicyStr_adminNoCreate, (inExtraFeatures->adminNoCreate != 0),
			kPWPolicyStr_adminNoDelete, (inExtraFeatures->adminNoDelete != 0),
			kPWPolicyStr_adminNoClearState, (inExtraFeatures->adminNoClearState != 0),
			kPWPolicyStr_adminNoPromoteAdmins, (inExtraFeatures->adminNoPromoteAdmins != 0) );
}


//------------------------------------------------------------------------------------------------
//	PWAccessFeaturesToStringWithoutStateInfo
//
//	Prepares the PWAccessFeatures struct for transmission over our text-based protocol
//  Returns in <outString> the subset of policies that are true policies and not state
//  information, such as: isDisabled, isAdminUser, and newPasswordRequired.
//------------------------------------------------------------------------------------------------

void PWAccessFeaturesToStringWithoutStateInfo( PWAccessFeatures *inAccessFeatures, char *outString )
{
	int historyValue = 0;
	
    if ( outString == NULL || inAccessFeatures == NULL )
        throw(-1);
	
    // Boolean values are stored in the struct as single bits. They must be unsigned for
    // display.
    
	if ( inAccessFeatures->usingHistory )
		historyValue = 1 + inAccessFeatures->historyCount;
	
    snprintf( outString, 2048,
#ifdef __LP64__
				"%s=%d %s=%d %s=%d %s=%d %s=%d %s=%d %s=%lu %s=%lu %s=%u %s=%u %s=%u %s=%u %s=%u %s=%u %s=%d",
#else
				"%s=%d %s=%d %s=%d %s=%d %s=%d %s=%d %s=%lu %s=%lu %s=%lu %s=%lu %s=%lu %s=%u %s=%u %s=%u %s=%d",
#endif
				kPWPolicyStr_usingHistory, historyValue,
				kPWPolicyStr_canModifyPasswordforSelf, (inAccessFeatures->canModifyPasswordforSelf != 0),
                kPWPolicyStr_usingExpirationDate, (inAccessFeatures->usingExpirationDate != 0),
                kPWPolicyStr_usingHardExpirationDate, (inAccessFeatures->usingHardExpirationDate != 0),
                kPWPolicyStr_requiresAlpha, (inAccessFeatures->requiresAlpha != 0),
				kPWPolicyStr_requiresNumeric, (inAccessFeatures->requiresNumeric != 0),
                kPWPolicyStr_expirationDateGMT, timegm( (struct tm *)&inAccessFeatures->expirationDateGMT ),
                kPWPolicyStr_hardExpireDateGMT, timegm( (struct tm *)&inAccessFeatures->hardExpireDateGMT ),
                kPWPolicyStr_maxMinutesUntilChangePW, inAccessFeatures->maxMinutesUntilChangePassword,
                kPWPolicyStr_maxMinutesUntilDisabled, inAccessFeatures->maxMinutesUntilDisabled,
                kPWPolicyStr_maxMinutesOfNonUse, inAccessFeatures->maxMinutesOfNonUse,
                kPWPolicyStr_maxFailedLoginAttempts, inAccessFeatures->maxFailedLoginAttempts,
                kPWPolicyStr_minChars, inAccessFeatures->minChars,
                kPWPolicyStr_maxChars, inAccessFeatures->maxChars,
				kPWPolicyStr_passwordCannotBeName, (inAccessFeatures->passwordCannotBeName != 0) );
}


//------------------------------------------------------------------------------------------------
//	PWAccessFeaturesToStringWithoutStateInfoExtra
//
//	Prepares the policy data for transmission over our text-based protocol.
//  Returns in <outString> the subset of policies that are true policies and not state
//  information, such as: isDisabled, isAdminUser, and newPasswordRequired.
//------------------------------------------------------------------------------------------------

void PWAccessFeaturesToStringWithoutStateInfoExtra( PWAccessFeatures *inAccessFeatures, PWMoreAccessFeatures *inExtraFeatures,
	int inMaxLen, char *outString )
{
	if ( inMaxLen >= 2048 )
	{
		PWAccessFeaturesToStringWithoutStateInfo( inAccessFeatures, outString );
	}
	else
	{
		char tempStr[2048];
		
		PWAccessFeaturesToStringWithoutStateInfo( inAccessFeatures, tempStr );
		snprintf( outString, inMaxLen, "%s", tempStr );
	}
	
	long firstSegmentLen = strlen( outString );
	inMaxLen -= firstSegmentLen;
	if ( inMaxLen > 0 )
	{
		snprintf( outString + firstSegmentLen, inMaxLen,
#ifdef __LP64__
			" %s=%d %s=%d %s=%u",
#else
			" %s=%d %s=%d %s=%lu",
#endif
			kPWPolicyStr_requiresMixedCase, (inExtraFeatures->requiresMixedCase != 0),
			kPWPolicyStr_requiresSymbol, (inExtraFeatures->requiresSymbol != 0),
			kPWPolicyStr_notGuessablePattern, inExtraFeatures->notGuessablePattern );
	}
}


//------------------------------------------------------------------------------------------------
//	StringToPWGlobalAccessFeatures
//
//	Returns: TRUE if the string is successfully parsed
//
//	Features specified in the string overwrite features in <inOutAccessFeatures>. Features
//	not specified in the string remain as-is. If they were undefined before, they are undefined
//	on exit.
//------------------------------------------------------------------------------------------------

Boolean StringToPWGlobalAccessFeatures( const char *inString, PWGlobalAccessFeatures *inOutAccessFeatures )
{
    const char *usingHistory = strstr( inString, kPWPolicyStr_usingHistory );
	const char *canModifyPasswordforSelf = strstr( inString, kPWPolicyStr_canModifyPasswordforSelf );
    const char *usingExpirationDate = strstr( inString, kPWPolicyStr_usingExpirationDate );
    const char *usingHardExpirationDate = strstr( inString, kPWPolicyStr_usingHardExpirationDate );
    const char *requiresAlpha = strstr( inString, kPWPolicyStr_requiresAlpha );
    const char *requiresNumeric = strstr( inString, kPWPolicyStr_requiresNumeric );
    const char *expirationDateGMT = strstr( inString, kPWPolicyStr_expirationDateGMT );
    const char *hardExpireDateGMT = strstr( inString, kPWPolicyStr_hardExpireDateGMT );
    const char *maxMinutesUntilChangePassword = strstr( inString, kPWPolicyStr_maxMinutesUntilChangePW );
    const char *maxMinutesUntilDisabled = strstr( inString, kPWPolicyStr_maxMinutesUntilDisabled );
    const char *maxMinutesOfNonUse = strstr( inString, kPWPolicyStr_maxMinutesOfNonUse );
    const char *maxFailedLoginAttempts = strstr( inString, kPWPolicyStr_maxFailedLoginAttempts );
    const char *minChars = strstr( inString, kPWPolicyStr_minChars );
    const char *maxChars = strstr( inString, kPWPolicyStr_maxChars );
    const char *passwordCannotBeName = strstr( inString, kPWPolicyStr_passwordCannotBeName );
    const char *requiresMixedCase = strstr( inString, kPWPolicyStr_requiresMixedCase );
    const char *requiresSymbol = strstr( inString, kPWPolicyStr_requiresSymbol );
    const char *newPasswordRequired = strstr( inString, kPWPolicyStr_newPasswordRequired );
//    const char *notGuessablePattern = strstr( inString, kPWPolicyStr_notGuessablePattern );
    unsigned long value;
    
    if ( StringToPWAccessFeatures_GetValue( usingHistory, &value ) )
	{
		if ( value > 0 )
		{
			// clamp to the password server's maximum value
			if ( value > kPWFileMaxHistoryCount )
				value = kPWFileMaxHistoryCount;
			
			inOutAccessFeatures->usingHistory = 1;
			SetGlobalHistoryCount(*inOutAccessFeatures, value - 1);
		}
		else
		{
			inOutAccessFeatures->usingHistory = 0;
			SetGlobalHistoryCount(*inOutAccessFeatures, 0);
		}
    }
	
	if ( StringToPWAccessFeatures_GetValue( canModifyPasswordforSelf, &value ) )
		inOutAccessFeatures->noModifyPasswordforSelf = (value==0);
	
    if ( StringToPWAccessFeatures_GetValue( usingExpirationDate, &value ) )
        inOutAccessFeatures->usingExpirationDate = value;
    
    if ( StringToPWAccessFeatures_GetValue( usingHardExpirationDate, &value ) )
        inOutAccessFeatures->usingHardExpirationDate = value;
    
    if ( StringToPWAccessFeatures_GetValue( requiresAlpha, &value ) )
        inOutAccessFeatures->requiresAlpha = value;
    
    if ( StringToPWAccessFeatures_GetValue( requiresNumeric, &value ) )
        inOutAccessFeatures->requiresNumeric = value;
    
    if ( StringToPWAccessFeatures_GetValue( expirationDateGMT, &value ) )
		gmtime_r( (time_t *)&value, (struct tm *)&inOutAccessFeatures->expirationDateGMT );
    
    if ( StringToPWAccessFeatures_GetValue( hardExpireDateGMT, &value ) )
		gmtime_r( (time_t *)&value, (struct tm *)&inOutAccessFeatures->hardExpireDateGMT );
	
    if ( StringToPWAccessFeatures_GetValue( maxMinutesUntilChangePassword, &value ) )
        inOutAccessFeatures->maxMinutesUntilChangePassword = value;
    
    if ( StringToPWAccessFeatures_GetValue( maxMinutesUntilDisabled, &value ) )
        inOutAccessFeatures->maxMinutesUntilDisabled = value;
    
    if ( StringToPWAccessFeatures_GetValue( maxMinutesOfNonUse, &value ) )
        inOutAccessFeatures->maxMinutesOfNonUse = value;
    
    if ( StringToPWAccessFeatures_GetValue( maxFailedLoginAttempts, &value ) )
        inOutAccessFeatures->maxFailedLoginAttempts = (UInt16)value;
    
    if ( StringToPWAccessFeatures_GetValue( minChars, &value ) )
        inOutAccessFeatures->minChars = value;
        
    if ( StringToPWAccessFeatures_GetValue( maxChars, &value ) )
        inOutAccessFeatures->maxChars = value;
        
	if ( StringToPWAccessFeatures_GetValue( passwordCannotBeName, &value ) )
		inOutAccessFeatures->passwordCannotBeName = value;
	
	if ( StringToPWAccessFeatures_GetValue( requiresMixedCase, &value ) )
		inOutAccessFeatures->requiresMixedCase = value;
	
	if ( StringToPWAccessFeatures_GetValue( requiresSymbol, &value ) )
		inOutAccessFeatures->requiresSymbol = value;
	
	if ( StringToPWAccessFeatures_GetValue( newPasswordRequired, &value ) )
		inOutAccessFeatures->newPasswordRequired = value;
	
    // no checking for now
    return true;
}


//------------------------------------------------------------------------------------------------
//	StringToPWGlobalAccessFeaturesExtra
//
//	Returns: TRUE if the string is successfully parsed
//------------------------------------------------------------------------------------------------

Boolean StringToPWGlobalAccessFeaturesExtra( const char *inString, PWGlobalAccessFeatures *inOutAccessFeatures,
	PWGlobalMoreAccessFeatures *inOutExtraFeatures )
{
	Boolean result = StringToPWGlobalAccessFeatures( inString, inOutAccessFeatures );
	const char *notGuessablePattern = strstr( inString, kPWPolicyStr_notGuessablePattern );
	const char *minutesUntilFailedLoginReset = strstr( inString, kPWPolicyStr_minutesUntilFailedLoginReset );
    unsigned long value;
	
	if ( StringToPWAccessFeatures_GetValue( notGuessablePattern, &value ) )
		inOutExtraFeatures->notGuessablePattern = value;
		
	if ( StringToPWAccessFeatures_GetValue( minutesUntilFailedLoginReset, &value ) )
		inOutExtraFeatures->minutesUntilFailedLoginReset = value;
	
	return result;
}


//------------------------------------------------------------------------------------------------
//	StringToPWAccessFeatures
//
//	Returns: TRUE if the string is successfully parsed
//
//	Features specified in the string overwrite features in <inOutAccessFeatures>. Features
//	not specified in the string remain as-is. If they were undefined before, they are undefined
//	on exit.
//------------------------------------------------------------------------------------------------

Boolean StringToPWAccessFeatures( const char *inString, PWAccessFeatures *inOutAccessFeatures )
{
    const char *isDisabled = strstr( inString, kPWPolicyStr_isDisabled );
    const char *isAdminUser = strstr( inString, kPWPolicyStr_isAdminUser );
    const char *newPasswordRequired = strstr( inString, kPWPolicyStr_newPasswordRequired );
    const char *usingHistory = strstr( inString, kPWPolicyStr_usingHistory );
    const char *canModifyPasswordforSelf = strstr( inString, kPWPolicyStr_canModifyPasswordforSelf );
    const char *usingExpirationDate = strstr( inString, kPWPolicyStr_usingExpirationDate );
    const char *usingHardExpirationDate = strstr( inString, kPWPolicyStr_usingHardExpirationDate );
    const char *requiresAlpha = strstr( inString, kPWPolicyStr_requiresAlpha );
    const char *requiresNumeric = strstr( inString, kPWPolicyStr_requiresNumeric );
    const char *expirationDateGMT = strstr( inString, kPWPolicyStr_expirationDateGMT );
    const char *hardExpireDateGMT = strstr( inString, kPWPolicyStr_hardExpireDateGMT );
    const char *maxMinutesUntilChangePassword = strstr( inString, kPWPolicyStr_maxMinutesUntilChangePW );
    const char *maxMinutesUntilDisabled = strstr( inString, kPWPolicyStr_maxMinutesUntilDisabled );
    const char *maxMinutesOfNonUse = strstr( inString, kPWPolicyStr_maxMinutesOfNonUse );
    const char *maxFailedLoginAttempts = strstr( inString, kPWPolicyStr_maxFailedLoginAttempts );
    const char *minChars = strstr( inString, kPWPolicyStr_minChars );
    const char *maxChars = strstr( inString, kPWPolicyStr_maxChars );
    const char *passwordCannotBeName = strstr( inString, kPWPolicyStr_passwordCannotBeName );
	const char *isSessionKeyAgent = strstr( inString, kPWPolicyStr_isSessionKeyAgent );
	const char *resetToGlobalDefaults = strstr( inString, kPWPolicyStr_resetToGlobalDefaults );
    unsigned long value;
    
    if ( StringToPWAccessFeatures_GetValue( isDisabled, &value ) )
        inOutAccessFeatures->isDisabled = value;
    
    if ( StringToPWAccessFeatures_GetValue( isAdminUser, &value ) )
        inOutAccessFeatures->isAdminUser = value;
    
    if ( StringToPWAccessFeatures_GetValue( newPasswordRequired, &value ) )
        inOutAccessFeatures->newPasswordRequired = value;
    
    if ( StringToPWAccessFeatures_GetValue( usingHistory, &value ) )
	{
		if ( value > 0 )
		{
			// clamp to the password server's maximum value
			if ( value > kPWFileMaxHistoryCount )
				value = kPWFileMaxHistoryCount;
			
			inOutAccessFeatures->usingHistory = 1;
			inOutAccessFeatures->historyCount = value - 1;
		}
		else
		{
			inOutAccessFeatures->usingHistory = 0;
			inOutAccessFeatures->historyCount = 0;
		}
    }
	
    if ( StringToPWAccessFeatures_GetValue( canModifyPasswordforSelf, &value ) )
        inOutAccessFeatures->canModifyPasswordforSelf = value;
    
    if ( StringToPWAccessFeatures_GetValue( usingExpirationDate, &value ) )
        inOutAccessFeatures->usingExpirationDate = value;
    
    if ( StringToPWAccessFeatures_GetValue( usingHardExpirationDate, &value ) )
        inOutAccessFeatures->usingHardExpirationDate = value;
    
    if ( StringToPWAccessFeatures_GetValue( requiresAlpha, &value ) )
        inOutAccessFeatures->requiresAlpha = value;
    
    if ( StringToPWAccessFeatures_GetValue( requiresNumeric, &value ) )
        inOutAccessFeatures->requiresNumeric = value;
    
    if ( StringToPWAccessFeatures_GetValue( expirationDateGMT, &value ) )
		gmtime_r( (time_t *)&value, (struct tm *)&inOutAccessFeatures->expirationDateGMT );
    
    if ( StringToPWAccessFeatures_GetValue( hardExpireDateGMT, &value ) )
		gmtime_r( (time_t *)&value, (struct tm *)&inOutAccessFeatures->hardExpireDateGMT );
	
	if ( StringToPWAccessFeatures_GetValue( maxMinutesUntilChangePassword, &value ) )
        inOutAccessFeatures->maxMinutesUntilChangePassword = value;
    
    if ( StringToPWAccessFeatures_GetValue( maxMinutesUntilDisabled, &value ) )
        inOutAccessFeatures->maxMinutesUntilDisabled = value;
    
    if ( StringToPWAccessFeatures_GetValue( maxMinutesOfNonUse, &value ) )
        inOutAccessFeatures->maxMinutesOfNonUse = value;
    
    if ( StringToPWAccessFeatures_GetValue( maxFailedLoginAttempts, &value ) )
        inOutAccessFeatures->maxFailedLoginAttempts = (UInt16)value;
    
    if ( StringToPWAccessFeatures_GetValue( minChars, &value ) )
        inOutAccessFeatures->minChars = value;
        
    if ( StringToPWAccessFeatures_GetValue( maxChars, &value ) )
        inOutAccessFeatures->maxChars = value;
	
	if ( StringToPWAccessFeatures_GetValue( passwordCannotBeName, &value ) )
		inOutAccessFeatures->passwordCannotBeName = value;
	
	if ( StringToPWAccessFeatures_GetValue( isSessionKeyAgent, &value ) )
		inOutAccessFeatures->isSessionKeyAgent = value;

	// this policy must be processed last
	if ( StringToPWAccessFeatures_GetValue( resetToGlobalDefaults, &value ) && value > 0 )
	{
		inOutAccessFeatures->usingHistory = 0;
		inOutAccessFeatures->canModifyPasswordforSelf = 1;
		inOutAccessFeatures->usingExpirationDate = 0;
		inOutAccessFeatures->usingHardExpirationDate = 0;
		inOutAccessFeatures->requiresAlpha = 0;
		inOutAccessFeatures->requiresNumeric = 0;
		inOutAccessFeatures->passwordCannotBeName = 0;
		inOutAccessFeatures->historyCount = 0;
		inOutAccessFeatures->maxMinutesUntilChangePassword = 0;
		inOutAccessFeatures->maxMinutesUntilDisabled = 0;
		inOutAccessFeatures->maxMinutesOfNonUse = 0;
		inOutAccessFeatures->maxFailedLoginAttempts = 0;
		inOutAccessFeatures->minChars = 0;
		inOutAccessFeatures->maxChars = 0;
	}
	
    // no checking for now
    return true;
}


//------------------------------------------------------------------------------------------------
//	StringToPWAccessFeaturesExtra
//
//	Returns: TRUE if the string is successfully parsed
//
//	Features specified in the string overwrite features in <inOutAccessFeatures>. Features
//	not specified in the string remain as-is. If they were undefined before, they are undefined
//	on exit.
//------------------------------------------------------------------------------------------------

Boolean StringToPWAccessFeaturesExtra( const char *inString, PWAccessFeatures *inOutAccessFeatures,
	PWMoreAccessFeatures *inOutExtraFeatures )
{
	const char *requiresMixedCase = strstr( inString, kPWPolicyStr_requiresMixedCase );
	const char *requiresSymbol = strstr( inString, kPWPolicyStr_requiresSymbol );
	const char *notGuessablePattern = strstr( inString, kPWPolicyStr_notGuessablePattern );
	const char *isComputerAccount = strstr( inString, kPWPolicyStr_isComputerAccount );
	const char *adminNoChangePasswords = strstr( inString, kPWPolicyStr_adminNoChangePasswords );
	const char *adminNoSetPolicies = strstr( inString, kPWPolicyStr_adminNoSetPolicies );
	const char *adminNoCreate = strstr( inString, kPWPolicyStr_adminNoCreate );
	const char *adminNoDelete = strstr( inString, kPWPolicyStr_adminNoDelete );
	const char *adminNoClearState = strstr( inString, kPWPolicyStr_adminNoClearState );
	const char *adminNoPromoteAdmins = strstr( inString, kPWPolicyStr_adminNoPromoteAdmins );
	const char *adminClass = strstr( inString, kPWPolicyStr_adminClass );
	const char *resetToGlobalDefaults = strstr( inString, kPWPolicyStr_resetToGlobalDefaults );
    unsigned long value;
    
	if ( inOutExtraFeatures == NULL )
		return false;
	
	Boolean result = StringToPWAccessFeatures( inString, inOutAccessFeatures );
	if ( result )
	{   
		if ( StringToPWAccessFeatures_GetValue( requiresMixedCase, &value ) )
			inOutExtraFeatures->requiresMixedCase = value;
		
		if ( StringToPWAccessFeatures_GetValue( requiresSymbol, &value ) )
			inOutExtraFeatures->requiresSymbol = value;
		
		if ( StringToPWAccessFeatures_GetValue( notGuessablePattern, &value ) )
			inOutExtraFeatures->notGuessablePattern = value;
		
		if ( StringToPWAccessFeatures_GetValue( isComputerAccount, &value ) )
			inOutExtraFeatures->isComputerAccount = value;
		
		if ( StringToPWAccessFeatures_GetValue( adminNoChangePasswords, &value ) )
			inOutExtraFeatures->adminNoChangePasswords = value;
			
		if ( StringToPWAccessFeatures_GetValue( adminNoSetPolicies, &value ) )
			inOutExtraFeatures->adminNoSetPolicies = value;
			
		if ( StringToPWAccessFeatures_GetValue( adminNoCreate, &value ) )
			inOutExtraFeatures->adminNoCreate = value;
			
		if ( StringToPWAccessFeatures_GetValue( adminNoDelete, &value ) )
			inOutExtraFeatures->adminNoDelete = value;
			
		if ( StringToPWAccessFeatures_GetValue( adminNoClearState, &value ) )
			inOutExtraFeatures->adminNoClearState = value;
			
		if ( StringToPWAccessFeatures_GetValue( adminNoPromoteAdmins, &value ) )
			inOutExtraFeatures->adminNoPromoteAdmins = value;
			
		if ( StringToPWAccessFeatures_GetValue( adminClass, &value ) )
			inOutExtraFeatures->adminClass = value;
		
		if ( StringToPWAccessFeatures_GetValue( resetToGlobalDefaults, &value ) && value > 0 )
		{
			inOutExtraFeatures->requiresMixedCase = 0;
			inOutExtraFeatures->requiresSymbol = 0;
			inOutExtraFeatures->notGuessablePattern = 0;
		}
		
		// no checking for now
		result = true;
	}
	
	return result;
}


//------------------------------------------------------------------------------------------------
//	StringToPWAccessFeatures_GetValue
//
//	Returns: TRUE if a value is discovered
//
//	Takes a value like, "isDisabled=0" and returns the "0" in <outValue>.
//	If <inString> is NULL, the function just returns false.
//------------------------------------------------------------------------------------------------

Boolean StringToPWAccessFeatures_GetValue( const char *inString, unsigned long *outValue )
{
    const char *valueStr;
    char valBuffer[64];
    unsigned int idx = 0;
    
    if ( inString == NULL )
        return false;
    
	valueStr = strchr( inString, '=' );
    if ( valueStr == NULL )
        return false;
    
    valueStr++;
   	while ( *valueStr && *valueStr > ' ' && idx < sizeof(valBuffer) - 1 )
        valBuffer[idx++] = *valueStr++;
    valBuffer[idx] = '\0';
    
    if ( idx <= 0 )
        return false;
    
    sscanf( valBuffer, "%lu", outValue ); 
    
    return true;
}


//------------------------------------------------------------------------------------------------
//	CrashIfBuiltWrong
//
//	Compares the current database struct sizes to our hand-checked constant.
//	If the size changes, don't run.
//------------------------------------------------------------------------------------------------

void CrashIfBuiltWrong(void)
{
    if ( sizeof(PWFileEntry) != 4360 ||
         sizeof(PWAccessFeatures) != 112 || 
         sizeof(PWFileHeader) != 4768 || 
         sizeof(BSDTimeStructCopy) != 44 )
    {
        fprintf( stderr, "PasswordServer has been built wrong!!!\n" );
        fprintf( stderr, "DO NOT SHIP THIS PRODUCT\n" );
        
        fprintf( stderr, "PWFileEntry=%ld\n", sizeof(PWFileEntry) );
        fprintf( stderr, "PWAccessFeatures=%ld\n", sizeof(PWAccessFeatures) );
        fprintf( stderr, "PWFileHeader=%ld\n", sizeof(PWFileHeader) );
        fprintf( stderr, "BSDTimeStructCopy=%ld\n", sizeof(BSDTimeStructCopy) );
        
        char *someBadAddress = (char *)0xFFFFFFFF;
        someBadAddress[0] = 0;
    }
}


//------------------------------------------------------------------------------------------------
//	pwsf_PreserveUnrepresentedPolicies
//
//	concatenates policies that are not in one of the policy structs
//------------------------------------------------------------------------------------------------

void pwsf_PreserveUnrepresentedPolicies( const char *inOriginalStr, int inMaxLen, char *inOutString )
{
	if ( inOriginalStr == NULL || inOutString == NULL )
		return;
	
	const char *warnOfExpirationMinutes = strstr( inOriginalStr, kPWPolicyStr_warnOfExpirationMinutes );
	const char *warnOfDisableMinutes = strstr( inOriginalStr, kPWPolicyStr_warnOfDisableMinutes );
	const char *endPtr = NULL;
	long curLen = strlen( inOutString );
	
	if ( warnOfExpirationMinutes != NULL )
	{
		endPtr = strchr( warnOfExpirationMinutes, ' ' );
		if ( endPtr != NULL )
			strlcat( inOutString, 
					 warnOfExpirationMinutes,
					 MIN(curLen + (endPtr - warnOfExpirationMinutes) + 1, inMaxLen) );
		else
			strlcat( inOutString, warnOfExpirationMinutes, inMaxLen );
		
		curLen = strlen( inOutString );
	}
	
	if ( warnOfDisableMinutes != NULL )
	{
		endPtr = strchr( warnOfDisableMinutes, ' ' );
		if ( endPtr != NULL )
			strlcat( inOutString,
					 warnOfDisableMinutes, 
					 MIN(curLen + (endPtr - warnOfDisableMinutes) + 1, inMaxLen) );
		else
			strlcat( inOutString, warnOfDisableMinutes, inMaxLen );
	}
}


//------------------------------------------------------------------------------------------------
//	pwsf_GetPublicKey
//
//  RETURNS: 0=ok, -1 fail
//
//	Retrieves the RSA public key from the password server database.
//  <outPublicKey> must be a pointer to a buffer at least kPWFileMaxPublicKeyBytes long.
//------------------------------------------------------------------------------------------------

int pwsf_GetPublicKey(char *outPublicKey)
{
	int err = -1;
	CAuthFileBase authFile;
		
	err = authFile.validateFiles();
	if ( err == 0 )
		err = authFile.getRSAPublicKey( outPublicKey );
	
	return err;
}


//------------------------------------------------------------------------------------------------
//	pwsf_GetPublicKeyFromFile
//
//  RETURNS: 0=ok, -1 fail
//
//	Retrieves the RSA public key from the password server database.
//  <outPublicKey> must be a pointer to a buffer at least kPWFileMaxPublicKeyBytes long.
//------------------------------------------------------------------------------------------------

int pwsf_GetPublicKeyFromFile(const char *inFile, char *outPublicKey)
{
	int err = -1;
	CAuthFileBase authFile(inFile);
	
	err = authFile.validateFiles();
	if ( err == 0 )
		err = authFile.getRSAPublicKey( outPublicKey );
	
	return err;
}


//------------------------------------------------------------------------------------------------
//	pwsf_CreateReplicaFile
//
//  Creates the '/var/db/authserver/authserverreplicas' file.
//  Client must be running as root.
//------------------------------------------------------------------------------------------------

void pwsf_CreateReplicaFile( const char *inIPStr, const char *inDNSStr, const char *inPublicKey )
{
	ReplicaFile *replicaFile = nil;
	struct stat sb;
	
	if ( lstat(kPWReplicaFile, &sb) == 0 )
	{
		// replace the existing file
		unlink( kPWReplicaFile );
	}
	
	replicaFile = [[ReplicaFile alloc] init];
	if ( replicaFile != nil )
	{
		[replicaFile setParentWithIP:inIPStr andDNS:(inDNSStr != NULL && inDNSStr[0] != '\0') ? inDNSStr : NULL];

		if ( inPublicKey != NULL )
		{
			[replicaFile addServerUniqueID:inPublicKey];
		}
		else
		{
			CAuthFileBase authFile;
			char rsaKey[kPWFileMaxPublicKeyBytes];
			
			if ( authFile.getRSAPublicKey( rsaKey ) == 0 )
				[replicaFile addServerUniqueID:rsaKey];
		}
		
		[replicaFile setReplicaPolicy:kReplicaAllowAll];
		[replicaFile saveXMLData];
		[replicaFile free];
	}
}


//------------------------------------------------------------------------------------------------
//	pwsf_ResetReplicaFile
//
//  Resets the '/var/db/authserver/authserverreplicas' file to have no replicas.
//  Client must be running as root.
//------------------------------------------------------------------------------------------------

void pwsf_ResetReplicaFile( const char *inPublicKey )
{
	CFStringRef firstIPAddressString;
	CFStringRef dnsString;
	CFMutableArrayRef ipAddressArray = NULL;
	CFIndex ipAddressCount = 0;
	ReplicaFile *replicaFile = nil;
	CFMutableDictionaryRef serverDict = NULL;
	char ipStr[256] = {0,};
	char dnsStr[256] = {0,};
	
	strcpy( ipStr, "0.0.0.0" );
	
	replicaFile = [[ReplicaFile alloc] init];
	if ( replicaFile != nil )
	{
		serverDict = (CFMutableDictionaryRef)[replicaFile getParent];
		if ( serverDict != NULL )
		{
			// fetch data to preserve
			ipAddressArray = [replicaFile getIPAddressesFromDict:serverDict];
			if ( ipAddressArray != NULL )
			{
				ipAddressCount = CFArrayGetCount( ipAddressArray );
				firstIPAddressString = (CFStringRef) CFArrayGetValueAtIndex( ipAddressArray, 0 );
				if ( firstIPAddressString != NULL )
					CFStringGetCString( firstIPAddressString, ipStr, sizeof(ipStr), kCFStringEncodingUTF8 );
			}
			
			if ( CFDictionaryGetValueIfPresent(serverDict, CFSTR(kPWReplicaDNSKey), (const void **)&dnsString) )
				CFStringGetCString( dnsString, dnsStr, sizeof(dnsStr), kCFStringEncodingUTF8 );
		}
		[replicaFile free];
	}
	
	// replace the replica file
	pwsf_CreateReplicaFile( ipStr, dnsStr, inPublicKey );
	
	// restore IP list (if > 1 address)
	if ( ipAddressCount > 1 )
	{
		ReplicaFile *newReplicaFile = [[ReplicaFile alloc] init];
		if ( newReplicaFile != nil )
		{
			serverDict = (CFMutableDictionaryRef) [newReplicaFile getParent];
			if ( serverDict != NULL )
			{
				CFDictionarySetValue( serverDict, CFSTR(kPWReplicaIPKey), ipAddressArray );
				[newReplicaFile setParentWithDict:serverDict];
				[newReplicaFile saveXMLData];
			}
			[newReplicaFile free];
		}
	}
}


// ----------------------------------------------------------------------------------------
//  pwsf_GetPrincName
//
//  Returns: the kerberos principal name for the record; must never return NULL.
// ----------------------------------------------------------------------------------------

char* pwsf_GetPrincName( PWFileEntry *userRec )
{
	if ( strcmp( userRec->digest[kPWHashSlotKERBEROS_NAME].method, "KerberosPrincName" ) == 0 )
		return userRec->digest[kPWHashSlotKERBEROS_NAME].digest;
	
	return userRec->usernameStr;
}


// ----------------------------------------------------------------------------------------
//  pwsf_ShadowHashDataToArray
//
//	Returns: TRUE if an array is returned in <outHashTypeArray>.
// ----------------------------------------------------------------------------------------

int pwsf_ShadowHashDataToArray( const char *inAAData, CFMutableArrayRef *outHashTypeArray )
{
	CFMutableArrayRef hashTypeArray = NULL;
	char hashType[256];
	
	if ( inAAData == NULL || outHashTypeArray == NULL || *inAAData == '\0' )
		return 0;
	
	*outHashTypeArray = NULL;
	
	// get the existing list (if any)
	if ( strncmp( inAAData, kDSTagAuthAuthorityBetterHashOnly, sizeof(kDSTagAuthAuthorityBetterHashOnly)-1 ) == 0 )
	{	
		hashTypeArray = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
		if ( hashTypeArray == NULL )
			return 0;
		
		pwsf_AppendUTF8StringToArray( "SMB-NT", hashTypeArray );
		pwsf_AppendUTF8StringToArray( "SALTED-SHA1", hashTypeArray );
	}
	else
	if ( strncmp( inAAData, kHashNameListPrefix, sizeof(kHashNameListPrefix)-1 ) == 0 )
	{
		// comma delimited list
		const char *endPtr;
		const char *tptr = inAAData + sizeof(kHashNameListPrefix) - 1;
		
		hashTypeArray = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
		if ( hashTypeArray == NULL )
			return 0;
		
		if ( *tptr++ == '<' && strchr(tptr, '>') != NULL )
		{
			while ( (endPtr = strchr( tptr, ',' )) != NULL )
			{
				strlcpy( hashType, tptr, (endPtr - tptr) + 1 );
				pwsf_AppendUTF8StringToArray( hashType, hashTypeArray );
				
				tptr += (endPtr - tptr) + 1;
			}
			
			endPtr = strchr( tptr, '>' );
			if ( endPtr != NULL )
			{
				strlcpy( hashType, tptr, (endPtr - tptr) + 1 );
				pwsf_AppendUTF8StringToArray( hashType, hashTypeArray );
			}
		}
	}
	
	*outHashTypeArray = hashTypeArray;
	
	return 1;
}


// ----------------------------------------------------------------------------------------
//  pwsf_ShadowHashArrayToData
// ----------------------------------------------------------------------------------------

char *pwsf_ShadowHashArrayToData( CFArrayRef inHashTypeArray, long *outResultLen )
{
	char *aaNewData = NULL;
	char *newDataCStr = NULL;
	long len = 0;
	CFMutableStringRef newDataString = NULL;
	CFStringRef stringRef;
	CFIndex stringLen;
	
	// build the new string
	CFIndex typeCount = CFArrayGetCount( inHashTypeArray );
	if ( typeCount > 0 )
	{
		newDataString = CFStringCreateMutable( kCFAllocatorDefault, 0 );
		if ( newDataString == NULL )
			return NULL;
		
		for ( CFIndex index = 0; index < typeCount; index++ )
		{
			stringRef = (CFStringRef) CFArrayGetValueAtIndex( inHashTypeArray, index );
			if ( stringRef != NULL )
			{
				if ( CFStringGetLength(newDataString) > 0 )
					CFStringAppend( newDataString, CFSTR(",") );
				CFStringAppend( newDataString, stringRef );
			}
		}
	}
	
	// build the auth-authority
	stringLen = CFStringGetLength( newDataString );
	newDataCStr = (char *) calloc( 1, stringLen + 1 );
	CFStringGetCString( newDataString, newDataCStr, stringLen + 1, kCFStringEncodingUTF8 );
	aaNewData = (char *) calloc( 1, sizeof(kDSValueAuthAuthorityShadowHash) + sizeof(kHashNameListPrefix) + stringLen + 2 );
	
	// build string
	if ( newDataCStr != NULL && aaNewData != NULL )
		len = sprintf( aaNewData, "%s<%s>", kHashNameListPrefix, newDataCStr );
	
	// clean up
	if ( newDataString != NULL )
		CFRelease( newDataString );
	if ( newDataCStr != NULL )
		free( newDataCStr );
	
	// return string length (if requested)
	if ( outResultLen != NULL )
		*outResultLen = len;
	
	return aaNewData;
}


// ----------------------------------------------------------------------------------------
//  pwsf_AppendUTF8StringToArray
// ----------------------------------------------------------------------------------------

void pwsf_AppendUTF8StringToArray( const char *inUTF8Str, CFMutableArrayRef inArray )
{
	CFStringRef stringRef = CFStringCreateWithCString( kCFAllocatorDefault, inUTF8Str, kCFStringEncodingUTF8 );
	if ( stringRef != NULL ) {
		CFArrayAppendValue( inArray, stringRef );
		CFRelease( stringRef );
	}
}


#pragma mark -
#pragma mark ENDIAN UTILS
#pragma mark -

// ----------------------------------------------------------------------------------------
//  pwsf_EndianAdjustTimeStruct
// ----------------------------------------------------------------------------------------

void pwsf_EndianAdjustTimeStruct( BSDTimeStructCopy *inOutTimeStruct, int native )
{
#if TARGET_RT_LITTLE_ENDIAN
	if ( native == 1 )
	{
		// adjust to native byte order
		inOutTimeStruct->tm_sec = EndianS32_BtoN(inOutTimeStruct->tm_sec);
		inOutTimeStruct->tm_min = EndianS32_BtoN(inOutTimeStruct->tm_min);
		inOutTimeStruct->tm_hour = EndianS32_BtoN(inOutTimeStruct->tm_hour);
		inOutTimeStruct->tm_mday = EndianS32_BtoN(inOutTimeStruct->tm_mday);
		inOutTimeStruct->tm_mon = EndianS32_BtoN(inOutTimeStruct->tm_mon);
		inOutTimeStruct->tm_year = EndianS32_BtoN(inOutTimeStruct->tm_year);
		inOutTimeStruct->tm_wday = EndianS32_BtoN(inOutTimeStruct->tm_wday);
		inOutTimeStruct->tm_yday = EndianS32_BtoN(inOutTimeStruct->tm_yday);
		inOutTimeStruct->tm_isdst = EndianS32_BtoN(inOutTimeStruct->tm_isdst);
		inOutTimeStruct->tm_gmtoff = EndianS32_BtoN(inOutTimeStruct->tm_gmtoff);
	}
	else
	{
		// adjust to big-endian byte order
		inOutTimeStruct->tm_sec = EndianS32_NtoB(inOutTimeStruct->tm_sec);
		inOutTimeStruct->tm_min = EndianS32_NtoB(inOutTimeStruct->tm_min);
		inOutTimeStruct->tm_hour = EndianS32_NtoB(inOutTimeStruct->tm_hour);
		inOutTimeStruct->tm_mday = EndianS32_NtoB(inOutTimeStruct->tm_mday);
		inOutTimeStruct->tm_mon = EndianS32_NtoB(inOutTimeStruct->tm_mon);
		inOutTimeStruct->tm_year = EndianS32_NtoB(inOutTimeStruct->tm_year);
		inOutTimeStruct->tm_wday = EndianS32_NtoB(inOutTimeStruct->tm_wday);
		inOutTimeStruct->tm_yday = EndianS32_NtoB(inOutTimeStruct->tm_yday);
		inOutTimeStruct->tm_isdst = EndianS32_NtoB(inOutTimeStruct->tm_isdst);
		inOutTimeStruct->tm_gmtoff = EndianS32_NtoB(inOutTimeStruct->tm_gmtoff);
	}
#endif
}


// ----------------------------------------------------------------------------------------
//  pwsf_EndianAdjustPWFileHeader
// ----------------------------------------------------------------------------------------

void pwsf_EndianAdjustPWFileHeader( PWFileHeader *inOutHeader, int native )
{
#if TARGET_RT_LITTLE_ENDIAN
	if ( native == 1 )
	{
		// adjust to native byte order
		inOutHeader->signature = EndianU32_BtoN(inOutHeader->signature);
		inOutHeader->version = EndianU32_BtoN(inOutHeader->version);
		inOutHeader->entrySize = EndianU32_BtoN(inOutHeader->entrySize);
		inOutHeader->sequenceNumber = EndianU32_BtoN(inOutHeader->sequenceNumber);
		inOutHeader->numberOfSlotsCurrentlyInFile = EndianU32_BtoN(inOutHeader->numberOfSlotsCurrentlyInFile);
		inOutHeader->deepestSlotUsed = EndianU32_BtoN(inOutHeader->deepestSlotUsed);

		inOutHeader->access.maxMinutesUntilChangePassword = EndianU32_BtoN(inOutHeader->access.maxMinutesUntilChangePassword);
		inOutHeader->access.maxMinutesUntilDisabled = EndianU32_BtoN(inOutHeader->access.maxMinutesUntilDisabled);
		inOutHeader->access.maxMinutesOfNonUse = EndianU32_BtoN(inOutHeader->access.maxMinutesOfNonUse);
		inOutHeader->access.maxFailedLoginAttempts = EndianU16_BtoN(inOutHeader->access.maxFailedLoginAttempts);
		inOutHeader->access.minChars = EndianU16_BtoN(inOutHeader->access.minChars);
		inOutHeader->access.maxChars = EndianU16_BtoN(inOutHeader->access.maxChars);
		
		pwsf_EndianAdjustTimeStruct(&(inOutHeader->access.expirationDateGMT), native);
		pwsf_EndianAdjustTimeStruct(&(inOutHeader->access.hardExpireDateGMT), native);
		
		inOutHeader->publicKeyLen = EndianU32_BtoN(inOutHeader->publicKeyLen);                                                    // 1024-bit RSA public key - expected size is 233
		inOutHeader->privateKeyLen = EndianU32_BtoN(inOutHeader->privateKeyLen);
		inOutHeader->deepestSlotUsedByThisServer = EndianU32_BtoN(inOutHeader->deepestSlotUsedByThisServer);
		inOutHeader->accessModDate = EndianU32_BtoN(inOutHeader->accessModDate);
		inOutHeader->extraAccess.minutesUntilFailedLoginReset = EndianU32_BtoN(inOutHeader->extraAccess.minutesUntilFailedLoginReset);
		inOutHeader->extraAccess.notGuessablePattern = EndianU32_BtoN(inOutHeader->extraAccess.notGuessablePattern);
	}
	else
	{
		// adjust to big-endian byte order
		inOutHeader->signature = EndianU32_NtoB(inOutHeader->signature);
		inOutHeader->version = EndianU32_NtoB(inOutHeader->version);
		inOutHeader->entrySize = EndianU32_NtoB(inOutHeader->entrySize);
		inOutHeader->sequenceNumber = EndianU32_NtoB(inOutHeader->sequenceNumber);
		inOutHeader->numberOfSlotsCurrentlyInFile = EndianU32_NtoB(inOutHeader->numberOfSlotsCurrentlyInFile);
		inOutHeader->deepestSlotUsed = EndianU32_NtoB(inOutHeader->deepestSlotUsed);
		inOutHeader->access.maxMinutesUntilChangePassword = EndianU32_NtoB(inOutHeader->access.maxMinutesUntilChangePassword);
		inOutHeader->access.maxMinutesUntilDisabled = EndianU32_NtoB(inOutHeader->access.maxMinutesUntilDisabled);
		inOutHeader->access.maxMinutesOfNonUse = EndianU32_NtoB(inOutHeader->access.maxMinutesOfNonUse);
		inOutHeader->access.maxFailedLoginAttempts = EndianU16_NtoB(inOutHeader->access.maxFailedLoginAttempts);
		pwsf_EndianAdjustTimeStruct(&(inOutHeader->access.expirationDateGMT), native);
		pwsf_EndianAdjustTimeStruct(&(inOutHeader->access.hardExpireDateGMT), native);
		inOutHeader->access.minChars = EndianU16_NtoB(inOutHeader->access.minChars);
		inOutHeader->access.maxChars = EndianU16_NtoB(inOutHeader->access.maxChars);
		inOutHeader->publicKeyLen = EndianU32_NtoB(inOutHeader->publicKeyLen);
		inOutHeader->privateKeyLen = EndianU32_NtoB(inOutHeader->privateKeyLen);
		inOutHeader->deepestSlotUsedByThisServer = EndianU32_NtoB(inOutHeader->deepestSlotUsedByThisServer);
		inOutHeader->accessModDate = EndianU32_NtoB(inOutHeader->accessModDate);
		inOutHeader->extraAccess.minutesUntilFailedLoginReset = EndianU32_NtoB(inOutHeader->extraAccess.minutesUntilFailedLoginReset);
		inOutHeader->extraAccess.notGuessablePattern = EndianU32_NtoB(inOutHeader->extraAccess.notGuessablePattern);
	}
#endif
}


// ----------------------------------------------------------------------------------------
//  pwsf_EndianAdjustPWFileEntry
// ----------------------------------------------------------------------------------------

void pwsf_EndianAdjustPWFileEntry( PWFileEntry *inOutEntry, int native )
{
#if TARGET_RT_LITTLE_ENDIAN
	if ( native == 1 )
	{
		// adjust to native byte order
		inOutEntry->time = EndianU32_BtoN(inOutEntry->time);
		inOutEntry->rnd = EndianU32_BtoN(inOutEntry->rnd);
		inOutEntry->sequenceNumber = EndianU32_BtoN(inOutEntry->sequenceNumber);
		inOutEntry->slot = EndianU32_BtoN(inOutEntry->slot);
		
		pwsf_EndianAdjustTimeStruct(&inOutEntry->creationDate, native);
		pwsf_EndianAdjustTimeStruct(&inOutEntry->modificationDate, native);
		pwsf_EndianAdjustTimeStruct(&inOutEntry->modDateOfPassword, native);
		pwsf_EndianAdjustTimeStruct(&inOutEntry->lastLogin, native);
		
		inOutEntry->failedLoginAttempts = EndianU16_BtoN(inOutEntry->failedLoginAttempts);
		
		pwsf_EndianAdjustTimeStruct(&(inOutEntry->access.expirationDateGMT), native);
		pwsf_EndianAdjustTimeStruct(&(inOutEntry->access.hardExpireDateGMT), native);

		inOutEntry->access.maxMinutesUntilChangePassword = EndianU32_BtoN(inOutEntry->access.maxMinutesUntilChangePassword);
		inOutEntry->access.maxMinutesUntilDisabled = EndianU32_BtoN(inOutEntry->access.maxMinutesUntilDisabled);
		inOutEntry->access.maxMinutesOfNonUse = EndianU32_BtoN(inOutEntry->access.maxMinutesOfNonUse);
		inOutEntry->access.maxFailedLoginAttempts = EndianU16_BtoN(inOutEntry->access.maxFailedLoginAttempts);
		inOutEntry->access.minChars = EndianU16_BtoN(inOutEntry->access.minChars);
		inOutEntry->access.maxChars = EndianU16_BtoN(inOutEntry->access.maxChars);
		
		inOutEntry->disableReason = (PWDisableReasonCode) EndianS32_BtoN(inOutEntry->disableReason);

		inOutEntry->extraAccess.minutesUntilFailedLoginReset = EndianU32_BtoN(inOutEntry->extraAccess.minutesUntilFailedLoginReset);
		inOutEntry->extraAccess.notGuessablePattern = EndianU32_BtoN(inOutEntry->extraAccess.notGuessablePattern);
		inOutEntry->extraAccess.logOffTime = EndianU32_BtoN(inOutEntry->extraAccess.logOffTime);
		inOutEntry->extraAccess.kickOffTime = EndianU32_BtoN(inOutEntry->extraAccess.kickOffTime);
		
		inOutEntry->changeTransactionID = EndianS64_BtoN(inOutEntry->changeTransactionID);
	}
	else
	{
		// adjust to big-endian byte order
		inOutEntry->time = EndianU32_NtoB(inOutEntry->time);
		inOutEntry->rnd = EndianU32_NtoB(inOutEntry->rnd);
		inOutEntry->sequenceNumber = EndianU32_NtoB(inOutEntry->sequenceNumber);
		inOutEntry->slot = EndianU32_NtoB(inOutEntry->slot);
		
		pwsf_EndianAdjustTimeStruct(&inOutEntry->creationDate, native);
		pwsf_EndianAdjustTimeStruct(&inOutEntry->modificationDate, native);
		pwsf_EndianAdjustTimeStruct(&inOutEntry->modDateOfPassword, native);
		pwsf_EndianAdjustTimeStruct(&inOutEntry->lastLogin, native);
		
		inOutEntry->failedLoginAttempts = EndianU16_NtoB(inOutEntry->failedLoginAttempts);
		
		pwsf_EndianAdjustTimeStruct(&(inOutEntry->access.expirationDateGMT), native);
		pwsf_EndianAdjustTimeStruct(&(inOutEntry->access.hardExpireDateGMT), native);

		inOutEntry->access.maxMinutesUntilChangePassword = EndianU32_NtoB(inOutEntry->access.maxMinutesUntilChangePassword);
		inOutEntry->access.maxMinutesUntilDisabled = EndianU32_NtoB(inOutEntry->access.maxMinutesUntilDisabled);
		inOutEntry->access.maxMinutesOfNonUse = EndianU32_NtoB(inOutEntry->access.maxMinutesOfNonUse);
		inOutEntry->access.maxFailedLoginAttempts = EndianU16_NtoB(inOutEntry->access.maxFailedLoginAttempts);
		inOutEntry->access.minChars = EndianU16_NtoB(inOutEntry->access.minChars);
		inOutEntry->access.maxChars = EndianU16_NtoB(inOutEntry->access.maxChars);
		
		inOutEntry->disableReason = (PWDisableReasonCode) EndianS32_NtoB(inOutEntry->disableReason);

		inOutEntry->extraAccess.minutesUntilFailedLoginReset = EndianU32_NtoB(inOutEntry->extraAccess.minutesUntilFailedLoginReset);
		inOutEntry->extraAccess.notGuessablePattern = EndianU32_NtoB(inOutEntry->extraAccess.notGuessablePattern);
		inOutEntry->extraAccess.logOffTime = EndianU32_NtoB(inOutEntry->extraAccess.logOffTime);
		inOutEntry->extraAccess.kickOffTime = EndianU32_NtoB(inOutEntry->extraAccess.kickOffTime);

		inOutEntry->changeTransactionID = EndianS64_NtoB(inOutEntry->changeTransactionID);
	}
#endif
}


// ----------------------------------------------------------------------------------------
//  pwsf_EndianAdjustPWFileEntryNoTimes
//
//	Discussion:
//		in the compress_slot/expand_slot case, the compressed dates are sent
//		in net byte order. They must be converted to host byte order in order
//		to expand them. Therefore, the dates are in host byte order but the
//		rest of the record is still in net byte order.
// ----------------------------------------------------------------------------------------

void pwsf_EndianAdjustPWFileEntryNoTimes( PWFileEntry *inOutEntry, int native )
{
#if TARGET_RT_LITTLE_ENDIAN
	if ( native == 1 )
	{
		// adjust to native byte order
		inOutEntry->time = EndianU32_BtoN(inOutEntry->time);
		inOutEntry->rnd = EndianU32_BtoN(inOutEntry->rnd);
		inOutEntry->sequenceNumber = EndianU32_BtoN(inOutEntry->sequenceNumber);
		inOutEntry->slot = EndianU32_BtoN(inOutEntry->slot);
				
		inOutEntry->failedLoginAttempts = EndianU16_BtoN(inOutEntry->failedLoginAttempts);
		
		inOutEntry->access.maxMinutesUntilChangePassword = EndianU32_BtoN(inOutEntry->access.maxMinutesUntilChangePassword);
		inOutEntry->access.maxMinutesUntilDisabled = EndianU32_BtoN(inOutEntry->access.maxMinutesUntilDisabled);
		inOutEntry->access.maxMinutesOfNonUse = EndianU32_BtoN(inOutEntry->access.maxMinutesOfNonUse);
		inOutEntry->access.maxFailedLoginAttempts = EndianU16_BtoN(inOutEntry->access.maxFailedLoginAttempts);
		inOutEntry->access.minChars = EndianU16_BtoN(inOutEntry->access.minChars);
		inOutEntry->access.maxChars = EndianU16_BtoN(inOutEntry->access.maxChars);
		
		inOutEntry->disableReason = (PWDisableReasonCode) EndianS32_BtoN(inOutEntry->disableReason);

		inOutEntry->extraAccess.minutesUntilFailedLoginReset = EndianU32_BtoN(inOutEntry->extraAccess.minutesUntilFailedLoginReset);
		inOutEntry->extraAccess.notGuessablePattern = EndianU32_BtoN(inOutEntry->extraAccess.notGuessablePattern);
		inOutEntry->extraAccess.logOffTime = EndianU32_BtoN(inOutEntry->extraAccess.logOffTime);
		inOutEntry->extraAccess.kickOffTime = EndianU32_BtoN(inOutEntry->extraAccess.kickOffTime);
		
		inOutEntry->changeTransactionID = EndianS64_BtoN(inOutEntry->changeTransactionID);
	}
	else
	{
		// adjust to big-endian byte order
		inOutEntry->time = EndianU32_NtoB(inOutEntry->time);
		inOutEntry->rnd = EndianU32_NtoB(inOutEntry->rnd);
		inOutEntry->sequenceNumber = EndianU32_NtoB(inOutEntry->sequenceNumber);
		inOutEntry->slot = EndianU32_NtoB(inOutEntry->slot);
				
		inOutEntry->failedLoginAttempts = EndianU16_NtoB(inOutEntry->failedLoginAttempts);
		
		inOutEntry->access.maxMinutesUntilChangePassword = EndianU32_NtoB(inOutEntry->access.maxMinutesUntilChangePassword);
		inOutEntry->access.maxMinutesUntilDisabled = EndianU32_NtoB(inOutEntry->access.maxMinutesUntilDisabled);
		inOutEntry->access.maxMinutesOfNonUse = EndianU32_NtoB(inOutEntry->access.maxMinutesOfNonUse);
		inOutEntry->access.maxFailedLoginAttempts = EndianU16_NtoB(inOutEntry->access.maxFailedLoginAttempts);
		inOutEntry->access.minChars = EndianU16_NtoB(inOutEntry->access.minChars);
		inOutEntry->access.maxChars = EndianU16_NtoB(inOutEntry->access.maxChars);
		
		inOutEntry->disableReason = (PWDisableReasonCode) EndianS32_NtoB(inOutEntry->disableReason);

		inOutEntry->extraAccess.minutesUntilFailedLoginReset = EndianU32_NtoB(inOutEntry->extraAccess.minutesUntilFailedLoginReset);
		inOutEntry->extraAccess.notGuessablePattern = EndianU32_NtoB(inOutEntry->extraAccess.notGuessablePattern);
		inOutEntry->extraAccess.logOffTime = EndianU32_NtoB(inOutEntry->extraAccess.logOffTime);
		inOutEntry->extraAccess.kickOffTime = EndianU32_NtoB(inOutEntry->extraAccess.kickOffTime);

		inOutEntry->changeTransactionID = EndianS64_NtoB(inOutEntry->changeTransactionID);
	}
#endif
}


#pragma mark -
#pragma mark HASH UTILS
#pragma mark -

// --------------------------------------------------------------------------------
//	AddHashesToPWRecord
//
//	inRealm				 ->		the realm to use for the DIGEST-MD5 hash
//	inOutPasswordRec	<->		in clear-text, out hash values
//	Takes the clear-text password and adds the hashes for auth methods
// --------------------------------------------------------------------------------

void pwsf_AddHashesToPWRecord( const char *inRealm, bool inAddNT, bool inAddLM, PWFileEntry *inOutPasswordRec )
{
	unsigned char smbntHash[32];
	unsigned char smblmHash[16];
	
	// SMB-NT			[ 0 ]
	if ( inAddNT )
	{
		pwsf_CalculateSMBNTHash(inOutPasswordRec->passwordStr, smbntHash);
		strcpy( inOutPasswordRec->digest[0].method, kSMBNTStorageTag );
		inOutPasswordRec->digest[0].digest[0] = 64;
		ConvertBinaryToHex( smbntHash, 32, &inOutPasswordRec->digest[0].digest[1] );
	}
	else
	{
		bzero( &inOutPasswordRec->digest[0], sizeof(inOutPasswordRec->digest[0]) );
	}
	
	// SMB-LAN-MANAGER	[ 1 ]
	if ( inAddLM )
	{
		pwsf_CalculateSMBLANManagerHash(inOutPasswordRec->passwordStr, smblmHash);
		strcpy( inOutPasswordRec->digest[1].method, "*cmusaslsecretSMBLM" );
		inOutPasswordRec->digest[1].digest[0] = 32;
		ConvertBinaryToHex( smblmHash, 16, &inOutPasswordRec->digest[1].digest[1] );
	}
	else
	{
		bzero( &inOutPasswordRec->digest[1], sizeof(inOutPasswordRec->digest[1]) );
	}
	
	// DIGEST-MD5		[ 2 ]
	pwsf_addHashDigestMD5( inRealm, inOutPasswordRec );
	
	// CRAM-MD5			[ 3 ]
	pwsf_addHashCramMD5( inOutPasswordRec );
	
	// KERBEROS			[ 4 ]
	// Kerberos doesn't currently store a hash here, we just store the realm name.
	// combined with the user name, we can call the KDC to get the kerberos hashes
	
	// KERBEROS_NAME	[ 5 ]
	// The principal name to use
	
	// SALTED_SHA1		[ 6 ]
	pwsf_addHashSaltedSHA1( inOutPasswordRec );
}


// ----------------------------------------------------------------------------------------
//  pwsf_getHashCramMD5
// ----------------------------------------------------------------------------------------

void
pwsf_getHashCramMD5(const unsigned char *inPassword, long inPasswordLen, unsigned char *outHash, unsigned long *outHashLen )
{
	HMAC_MD5_STATE state;
	
	if ( inPassword == NULL || outHash == NULL || outHashLen == NULL )
		return;
	
	pwsf_hmac_md5_precalc( &state, inPassword, inPasswordLen );
	*outHashLen = sizeof(HMAC_MD5_STATE);
	memcpy( outHash, &state, sizeof(HMAC_MD5_STATE) );
}


// ----------------------------------------------------------------------------------------
//  pwsf_getSaltedSHA1
// ----------------------------------------------------------------------------------------

void
pwsf_getSaltedSHA1(const unsigned char *inPassword, long inPasswordLen, unsigned char *outHash, unsigned long *outHashLen)
{
	CC_SHA1_CTX ctx;
	unsigned long salt = pwsf_getRandom();
	
	if ( inPassword == NULL || outHash == NULL || outHashLen == NULL )
		return;
	
	CC_SHA1_Init(&ctx);
	CC_SHA1_Update(&ctx, &salt, 4);
	CC_SHA1_Update(&ctx, inPassword, inPasswordLen);
	CC_SHA1_Final(outHash + 4, &ctx);
	memcpy(outHash, &salt, 4);
	*outHashLen = (4 + CC_SHA1_DIGEST_LENGTH);
}


//----------------------------------------------------------------------------------------------------
//	pwsf_addHashDigestMD5
//----------------------------------------------------------------------------------------------------

void pwsf_addHashDigestMD5( const char *inRealm, PWFileEntry *inOutPasswordRec )
{
	long pwLen;
	HASH HA1;
	char userID[35];
		
	// DIGEST-MD5		[ 2 ]
	pwLen = strlen(inOutPasswordRec->passwordStr);
	
	pwsf_passwordRecRefToString( inOutPasswordRec, userID );
	DigestCalcSecret( (unsigned char *)userID,
						(unsigned char *)inRealm,
						(unsigned char *)inOutPasswordRec->passwordStr,
						pwLen,
						HA1 );
	
	/*
		* A1 = { H( { username-value, ":", realm-value, ":", passwd } ),
		* ":", nonce-value, ":", cnonce-value }
		*/
	
	// not enough room to store "*cmusaslsecretDIGEST-MD5" so truncate to 20 chars
	strncpy( inOutPasswordRec->digest[kPWHashSlotDIGEST_MD5].method, "*cmusaslsecretDIGEST-MD5", SASL_MECHNAMEMAX );
	inOutPasswordRec->digest[kPWHashSlotDIGEST_MD5].method[SASL_MECHNAMEMAX] = '\0';
	inOutPasswordRec->digest[kPWHashSlotDIGEST_MD5].digest[0] = HASHLEN;
	memcpy( &inOutPasswordRec->digest[kPWHashSlotDIGEST_MD5].digest[1], HA1, HASHLEN );
}


//----------------------------------------------------------------------------------------------------
//	pwsf_addHashCramMD5
//----------------------------------------------------------------------------------------------------

void pwsf_addHashCramMD5( PWFileEntry *inOutPasswordRec )
{
	unsigned long pwLen;
	
	// CRAM-MD5			[ 3 ]
	strncpy( inOutPasswordRec->digest[kPWHashSlotCRAM_MD5].method, "*cmusaslsecretCRAM-MD5", SASL_MECHNAMEMAX );
	inOutPasswordRec->digest[kPWHashSlotCRAM_MD5].method[SASL_MECHNAMEMAX] = '\0';
	
	pwsf_getHashCramMD5( (unsigned char *)inOutPasswordRec->passwordStr,
						  strlen(inOutPasswordRec->passwordStr),
						  (unsigned char *)&inOutPasswordRec->digest[kPWHashSlotCRAM_MD5].digest[1],
						  &pwLen );
						  
	inOutPasswordRec->digest[kPWHashSlotCRAM_MD5].digest[0] = (unsigned char)pwLen;
}


//----------------------------------------------------------------------------------------------------
//	pwsf_addHashSaltedSHA1
//----------------------------------------------------------------------------------------------------

void pwsf_addHashSaltedSHA1( PWFileEntry *inOutPasswordRec )
{
	unsigned long pwLen = 0;

	if ( inOutPasswordRec == NULL )
		return;
	
	strcpy( inOutPasswordRec->digest[kPWHashSlotSALTED_SHA1].method, "*cmusaslsecretPPS" );
	pwsf_getSaltedSHA1( (const unsigned char *)inOutPasswordRec->passwordStr, strlen(inOutPasswordRec->passwordStr),
						(unsigned char *)&inOutPasswordRec->digest[kPWHashSlotSALTED_SHA1].digest[1],
						&pwLen );
	
	inOutPasswordRec->digest[kPWHashSlotSALTED_SHA1].digest[0] = (unsigned char)pwLen;
}


#pragma mark -
#pragma mark POLICY UTILS
#pragma mark -

//------------------------------------------------------------------------------------
//	pwsf_TestDisabledStatus
//
//	Returns: kAuthUserDisabled or kAuthOK
//
//  <inOutFailedLoginAttempts> is set to 0 if the failed login count is exceeded.
//------------------------------------------------------------------------------------

int pwsf_TestDisabledStatus( PWAccessFeatures *inAccess, PWGlobalAccessFeatures *inGAccess, struct tm *inCreationDate, struct tm *inLastLoginTime, UInt16 *inOutFailedLoginAttempts )
{
	PWDisableReasonCode ignored;
	
	return pwsf_TestDisabledStatusWithReasonCode( inAccess, inGAccess, inCreationDate, inLastLoginTime,
				inOutFailedLoginAttempts, &ignored );
}


//------------------------------------------------------------------------------------
//	pwsf_TestDisabledStatusWithReasonCode
//
//	Returns: kAuthUserDisabled or kAuthOK
//
//  <inOutFailedLoginAttempts> is set to 0 if the failed login count is exceeded.
//  <outReasonCode> is only valid if the return value is kAuthUserDisabled.
//------------------------------------------------------------------------------------

int pwsf_TestDisabledStatusWithReasonCode( PWAccessFeatures *inAccess, PWGlobalAccessFeatures *inGAccess, struct tm *inCreationDate, struct tm *inLastLoginTime, UInt16 *inOutFailedLoginAttempts, PWDisableReasonCode *outReasonCode )
{
	bool setToDisabled = false;
	
	*outReasonCode = kPWDisabledNotSet;
	
    // test policies in the user record
	if ( inAccess->maxFailedLoginAttempts > 0 )
	{
		if ( *inOutFailedLoginAttempts >= inAccess->maxFailedLoginAttempts )
		{
			// for failed login attempts, if the maximum is exceeded, set the isDisabled flag on the record
			// and reset <maxFailedLoginAttempts> so that the account can be re-enabled later.
			*inOutFailedLoginAttempts = 0;
			*outReasonCode = kPWDisabledTooManyFailedLogins;
			setToDisabled = true;
		}
    }
	else
	// test policies in the global record
    if ( inGAccess->maxFailedLoginAttempts > 0 &&
         *inOutFailedLoginAttempts >= inGAccess->maxFailedLoginAttempts )
    {
        // for failed login attempts, if the maximum is exceeded, set the isDisabled flag on the record
        // and reset <maxFailedLoginAttempts> so that the account can be re-enabled later.
		*inOutFailedLoginAttempts = 0;
		*outReasonCode = kPWDisabledTooManyFailedLogins;
		setToDisabled = true;
    }
	
	// usingHardExpirationDate
	if ( inAccess->usingHardExpirationDate )
	{
		if ( TimeIsStale(&(inAccess->hardExpireDateGMT)) )
		{
			*outReasonCode = kPWDisabledExpired;
			setToDisabled = true;
		}
	}
	else
	if ( inGAccess->usingHardExpirationDate && TimeIsStale(&inGAccess->hardExpireDateGMT) )
	{
		*outReasonCode = kPWDisabledExpired;
		setToDisabled = true;
	}
	
	// maxMinutesUntilDisabled
	if ( inAccess->maxMinutesUntilDisabled > 0 )
	{
		if ( LoginTimeIsStale((BSDTimeStructCopy *)inCreationDate, inAccess->maxMinutesUntilDisabled) )
		{
			*outReasonCode = kPWDisabledExpired;
			setToDisabled = true;
		}
	}
	else
	if ( inGAccess->maxMinutesUntilDisabled > 0 && LoginTimeIsStale((BSDTimeStructCopy *)inCreationDate, inGAccess->maxMinutesUntilDisabled) )
	{
		*outReasonCode = kPWDisabledExpired;
		setToDisabled = true;
	}
	
	if ( inAccess->maxMinutesOfNonUse > 0 )
	{
		if ( LoginTimeIsStale( (BSDTimeStructCopy *)inLastLoginTime, inAccess->maxMinutesOfNonUse) )
		{
			*outReasonCode = kPWDisabledInactive;
			setToDisabled = true;
		}
	}
	else
    if ( inGAccess->maxMinutesOfNonUse > 0 &&
		 LoginTimeIsStale( (BSDTimeStructCopy *)inLastLoginTime, inGAccess->maxMinutesOfNonUse) )
    {
		*outReasonCode = kPWDisabledInactive;
		setToDisabled = true;
    }
	
	return ( setToDisabled ? kAuthUserDisabled : kAuthOK );
}


//------------------------------------------------------------------------------------------------
//	pwsf_ChangePasswordStatus
//
//	Returns: kAuthOK, kAuthPasswordNeedsChange, kAuthPasswordExpired
//------------------------------------------------------------------------------------------------

int pwsf_ChangePasswordStatus( PWAccessFeatures *inAccess, PWGlobalAccessFeatures *inGAccess, struct tm *inModDateOfPassword )
{
	bool needsChange = false;
	
	if ( inAccess->newPasswordRequired )
	{
		needsChange = true;
	}
	else
	{
		// usingExpirationDate
		if ( inAccess->usingExpirationDate )
		{
			if ( TimeIsStale( &inAccess->expirationDateGMT ) )
				needsChange = true;
		}
		else
		if ( inGAccess->usingExpirationDate && TimeIsStale( &inGAccess->expirationDateGMT ) )
		{
			needsChange = true;
		}
		
		// maxMinutesUntilChangePassword
		if ( inAccess->maxMinutesUntilChangePassword > 0 )
		{
			if ( LoginTimeIsStale( (BSDTimeStructCopy *)inModDateOfPassword, inAccess->maxMinutesUntilChangePassword ) )
				needsChange = true;
		}
		else
		if ( inGAccess->maxMinutesUntilChangePassword > 0 && LoginTimeIsStale( (BSDTimeStructCopy *)inModDateOfPassword, inGAccess->maxMinutesUntilChangePassword ) )
		{
			needsChange = true;
		}
	}
	
	if ( needsChange )
    {
        if ( inAccess->canModifyPasswordforSelf )
            return kAuthPasswordNeedsChange;
        else
        	return kAuthPasswordExpired;
    }
    
    // not implemented
    return kAuthOK;
}


//------------------------------------------------------------------------------------------------
//	pwsf_RequiredCharacterStatus
//
//	Returns: enum of Reposonse Codes (CAuthFileCPP.h)
//------------------------------------------------------------------------------------------------

int pwsf_RequiredCharacterStatus(PWAccessFeatures *access, PWGlobalAccessFeatures *inGAccess, const char *inUsername, const char *inPassword)
{
    Boolean requiresAlpha = (access->requiresAlpha || inGAccess->requiresAlpha );
    Boolean requiresNumeric = (access->requiresNumeric || inGAccess->requiresNumeric );
    UInt16 minChars = (access->minChars > 0) ? access->minChars : inGAccess->minChars;
    UInt16 maxChars = (access->maxChars > 0) ? access->maxChars : inGAccess->maxChars;
	Boolean passwordCannotBeName = (access->passwordCannotBeName || inGAccess->passwordCannotBeName );
    UInt16 len;
	Boolean passwordIsEmptySubstituteString = false;
	
	if ( inPassword == NULL )
		return kAuthPasswordTooShort;
		
	len = strlen(inPassword);
    passwordIsEmptySubstituteString = (strcmp(inPassword, kEmptyPasswordAltStr) == 0);
	
	// The password server is not accepting blank passwords because some auth 
	// methods, such as DIGEST-MD5, will not authenticate them.
	if ( len == 0 )
		return kAuthPasswordTooShort;
	
    if ( len < minChars || (minChars > 0 && passwordIsEmptySubstituteString) )
        return kAuthPasswordTooShort;
    
    if ( maxChars > 0 && len > maxChars && !passwordIsEmptySubstituteString )
        return kAuthPasswordTooLong;
    
    if ( requiresAlpha )
    {
        Boolean hasAlpha = false;
        
		if ( !passwordIsEmptySubstituteString )
		{
			for ( int index = 0; index < len; index++ )
			{
				if ( isalpha(inPassword[index]) )
				{
					hasAlpha = true;
					break;
				}
			}
        }
		
        if ( !hasAlpha )
            return kAuthPasswordNeedsAlpha;
    }
	
    if ( requiresNumeric )
    {
        Boolean hasDecimal = false;
        
        if ( !passwordIsEmptySubstituteString )
		{
			for ( int index = 0; index < len; index++ )
			{
				if ( isdigit(inPassword[index]) )
				{
					hasDecimal = true;
					break;
				}
			}
        }
		
        if ( !hasDecimal )
            return kAuthPasswordNeedsDecimal;
    }
	
	if ( passwordCannotBeName )
	{
		UInt16 unameLen = strlen( inUsername );
		UInt16 smallerLen = ((len < unameLen) ? len : unameLen);
		
		// disallow the smaller substring, case-insensitive
		if ( strncasecmp( inPassword, inUsername, smallerLen ) == 0 )
			return kAuthPasswordCannotBeUsername;
	}
	
    return kAuthOK;
}


//------------------------------------------------------------------------------------------------
//	pwsf_RequiredCharacterStatusExtra
//
//	Returns: enum of Response Codes (CAuthFileCPP.h)
//------------------------------------------------------------------------------------------------

int pwsf_RequiredCharacterStatusExtra(PWAccessFeatures *access, PWGlobalAccessFeatures *inGAccess, const char *inUsername, const char *inPassword, PWMoreAccessFeatures *inExtraFeatures )
{
	int responseCode;
	int index;
	
	responseCode = pwsf_RequiredCharacterStatus( access, inGAccess, inUsername, inPassword );
	if ( responseCode != kAuthOK )
		return responseCode;
	
	UInt16 len = strlen( inPassword );
	
	if ( inGAccess->requiresMixedCase || inExtraFeatures->requiresMixedCase )
	{
		Boolean hasUpper = false;
        Boolean hasLower = false;
   
		if ( strcmp(inPassword, kEmptyPasswordAltStr) != 0 )
		{
			for ( index = 0; index < len; index++ )
			{
				if ( inPassword[index] >= 'A' && inPassword[index] <= 'Z' )
					hasUpper = true;
				else
				if ( inPassword[index] >= 'a' && inPassword[index] <= 'z' )
					hasLower = true;
				
				if ( hasUpper && hasLower )
					break;
			}
        }
		
        if ( !(hasUpper && hasLower) )
            return kAuthPasswordNeedsMixedCase;
	}
	
	if ( inGAccess->requiresSymbol || inExtraFeatures->requiresSymbol )
	{
		Boolean hasSymbol = false;
			
		if ( strcmp(inPassword, kEmptyPasswordAltStr) != 0 )
		{
			for ( index = 0; index < len; index++ )
			{
				if ( inPassword[index] >= 'A' && inPassword[index] <= 'Z' )
					continue;
				if ( inPassword[index] >= 'a' && inPassword[index] <= 'z' )
					continue;
				if ( inPassword[index] >= '0' && inPassword[index] <= '9' )
					continue;
				
				hasSymbol = true;
				break;
			}
        }
		
        if ( !hasSymbol )
            return kAuthPasswordNeedsSymbol;
	}
	
	/*if ( inGAccess->notGuessablePattern || inExtraFeatures->notGuessablePattern )
	{
	}*/
	
	return kAuthOK;
}


#pragma mark -

//----------------------------------------------------------------------------------------------------
//	pwsf_slotToOffset
//
//	Returns: the position in the database file (from SEEK_SET) for the slot
//----------------------------------------------------------------------------------------------------

long pwsf_slotToOffset(long slot)
{
   	return sizeof(PWFileHeader) + (slot - 1) * sizeof(PWFileEntry);
}


//----------------------------------------------------------------------------------------------------
//	getGMTime
//
//	Returns: a time struct based on GMT
//----------------------------------------------------------------------------------------------------

void pwsf_getGMTime(struct tm *inOutGMT)
{
    time_t theTime;
    struct tm gmt;
    
    time(&theTime);
    gmtime_r(&theTime, &gmt);
    memcpy( inOutGMT, &gmt, sizeof(struct tm) );
}


//----------------------------------------------------------------------------------------------------
//	getTimeForRef
//
//	Returns: a timestamp based on GMT
//----------------------------------------------------------------------------------------------------

unsigned long pwsf_getTimeForRef(void)
{
    time_t theTime;
    
    time(&theTime);
    return (unsigned long)theTime;
}


//----------------------------------------------------------------------------------------------------
//	getRandom
//
//	Returns: a random number for user IDs
//----------------------------------------------------------------------------------------------------

unsigned long pwsf_getRandom(void)
{
	UInt32 result;
	UInt32 uiNow;
	time_t now;
	
	result = (unsigned long) random();
	
	time(&now);
	uiNow = (unsigned long)now + result;
	srandom(uiNow);
	
	return result;
}


//-----------------------------------------------------------------------------
//	ConvertBinaryToHex
//-----------------------------------------------------------------------------

bool pwsf_ConvertBinaryToHex( const unsigned char *inData, long len, char *outHexStr )
{
    bool result = true;
	char *tptr = outHexStr;
	static const 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;
}


//----------------------------------------------------------------------------------------------------
//	pwsf_passwordRecRefToString
//----------------------------------------------------------------------------------------------------

void pwsf_passwordRecRefToString(PWFileEntry *inPasswordRec, char *outRefStr)
{
#if TARGET_RT_BIG_ENDIAN
	pwsf_ConvertBinaryToHex( (const unsigned char *)inPasswordRec, 16, outRefStr + 2 );
	*((uint16_t *)outRefStr) = 0x3078;
#else
	sprintf( outRefStr,
#ifdef __LP64__
			"0x%.8x%.8x%.8x%.8x",
#else
			"0x%.8lx%.8lx%.8lx%.8lx",
#endif
			inPasswordRec->time,
			inPasswordRec->rnd,
			inPasswordRec->sequenceNumber,
			inPasswordRec->slot );

//	*((uint16_t *)outRefStr) = 0x7830;
#endif
}


//-----------------------------------------------------------------------------
//	ConvertHexToBinaryFastWithNtoHL
//-----------------------------------------------------------------------------

static inline void ConvertHexToBinaryFastWithNtoHL( const char *inHex, int len, unsigned char *outBin )
{
	static const unsigned char nibbleTable[] = {	0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
													0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 
													0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
													0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
													0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
													0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
													0xA, 0xB, 0xC, 0xD, 0xE, 0xF };

	register int idx;
	register unsigned char *tptr = outBin;

#if TARGET_RT_BIG_ENDIAN
	for ( idx = 0; idx < len; idx++ ) {
		*tptr = nibbleTable[(int)(*inHex++) - 0x30] << 4;
		*tptr++ |= nibbleTable[(int)(*inHex++) - 0x30];
	}
#else
	register int idx2;
	const int lc = len / (sizeof(UInt32)*2);
	
	tptr += sizeof(UInt32) - 1;
	for ( idx2 = 0; idx2 < lc; idx2++ ) {
		for ( idx = 0; idx < (int)sizeof(UInt32); idx++ ) {
			*tptr = nibbleTable[(int)(*inHex++) - 0x30] << 4;
			*tptr-- |= nibbleTable[(int)(*inHex++) - 0x30];
		}
		tptr += sizeof(UInt32) * 2;
	}
#endif
}


//----------------------------------------------------------------------------------------------------
//	pwsf_stringToPasswordRecRef
//
//	Returns: Boolean (1==valid ref, 0==fail)
//----------------------------------------------------------------------------------------------------

int pwsf_stringToPasswordRecRef(const char *inRefStr, PWFileEntry *outPasswordRec)
{
    int result = false;
    
    // invalid slot value
    outPasswordRec->slot = 0;    
    if ( *inRefStr++ == '0' && *inRefStr++ == 'x' )
    {
        ConvertHexToBinaryFastWithNtoHL( inRefStr, 32, (unsigned char *)outPasswordRec );
        result = true;
    }
    
    return result;
}


//----------------------------------------------------------------------------------------------------
//	pwsf_compress_header
//----------------------------------------------------------------------------------------------------

int pwsf_compress_header( PWFileHeader *inHeader, unsigned char **outCompressedHeader, unsigned int *outCompressedHeaderLength )
{
	PWFileHeaderCompressed *outPWHeader = NULL;
	
	outPWHeader = (PWFileHeaderCompressed *) calloc( 1, sizeof(PWFileHeaderCompressed) );
	if ( outPWHeader == NULL )
		return -1;
	
#if TARGET_RT_BIG_ENDIAN
	outPWHeader->signature = inHeader->signature;
	outPWHeader->version = inHeader->version;
	outPWHeader->entrySize = inHeader->entrySize;
	outPWHeader->sequenceNumber = inHeader->sequenceNumber;
	outPWHeader->numberOfSlotsCurrentlyInFile = inHeader->numberOfSlotsCurrentlyInFile;
	outPWHeader->deepestSlotUsed = inHeader->deepestSlotUsed;
	memcpy( &outPWHeader->access, &inHeader->access, sizeof(PWGlobalAccessFeatures) );
	memcpy( &outPWHeader->weakAuthMethods, &inHeader->weakAuthMethods, sizeof(AuthMethName) * kPWFileMaxWeakMethods );
	strlcpy( outPWHeader->replicationName, inHeader->replicationName, kPWFileMaxReplicaName );
	outPWHeader->deepestSlotUsedByThisServer = inHeader->deepestSlotUsedByThisServer;
	outPWHeader->accessModDate = inHeader->accessModDate;
	memcpy( &outPWHeader->extraAccess, &inHeader->extraAccess, sizeof(PWGlobalMoreAccessFeatures) );
#else
	PWFileHeader bigEndianHeader = *inHeader;
		
	pwsf_EndianAdjustPWFileHeader( &bigEndianHeader, 0 );
	
	outPWHeader->signature = bigEndianHeader.signature;
	outPWHeader->version = bigEndianHeader.version;
	outPWHeader->entrySize = bigEndianHeader.entrySize;
	outPWHeader->sequenceNumber = bigEndianHeader.sequenceNumber;
	outPWHeader->numberOfSlotsCurrentlyInFile = bigEndianHeader.numberOfSlotsCurrentlyInFile;
	outPWHeader->deepestSlotUsed = bigEndianHeader.deepestSlotUsed;
	memcpy( &outPWHeader->access, &bigEndianHeader.access, sizeof(PWGlobalAccessFeatures) );
	memcpy( &outPWHeader->weakAuthMethods, &bigEndianHeader.weakAuthMethods, sizeof(AuthMethName) * kPWFileMaxWeakMethods );
	strlcpy( outPWHeader->replicationName, bigEndianHeader.replicationName, kPWFileMaxReplicaName );
	outPWHeader->deepestSlotUsedByThisServer = bigEndianHeader.deepestSlotUsedByThisServer;
	outPWHeader->accessModDate = bigEndianHeader.accessModDate;
	memcpy( &outPWHeader->extraAccess, &bigEndianHeader.extraAccess, sizeof(PWGlobalMoreAccessFeatures) );
#endif
	
	*outCompressedHeader = (unsigned char *)outPWHeader;
	*outCompressedHeaderLength = sizeof(PWFileHeaderCompressed);
	
	return 0;
}


//----------------------------------------------------------------------------------------------------
//	pwsf_expand_header
//----------------------------------------------------------------------------------------------------

int pwsf_expand_header( const unsigned char *inCompressedHeader, unsigned int inCompressedHeaderLength, PWFileHeader *outHeader )
{
	PWFileHeaderCompressed *cHeader = (PWFileHeaderCompressed *)inCompressedHeader;
	
	bzero( outHeader, sizeof(PWFileHeader) );
	
	outHeader->signature = cHeader->signature;
	outHeader->version = cHeader->version;
	outHeader->entrySize = cHeader->entrySize;
	outHeader->sequenceNumber = cHeader->sequenceNumber;
	outHeader->numberOfSlotsCurrentlyInFile = cHeader->numberOfSlotsCurrentlyInFile;
	outHeader->deepestSlotUsed = cHeader->deepestSlotUsed;
	memcpy( &outHeader->access, &cHeader->access, sizeof(PWGlobalAccessFeatures) );
	memcpy( &outHeader->weakAuthMethods, &cHeader->weakAuthMethods, sizeof(AuthMethName) * kPWFileMaxWeakMethods );
	strlcpy( outHeader->replicationName, cHeader->replicationName, kPWFileMaxReplicaName );
	outHeader->deepestSlotUsedByThisServer = cHeader->deepestSlotUsedByThisServer;
	outHeader->accessModDate = cHeader->accessModDate;
	memcpy( &outHeader->extraAccess, &cHeader->extraAccess, sizeof(PWGlobalMoreAccessFeatures) );
	
	pwsf_EndianAdjustPWFileHeader( outHeader, 1 );
						  
	return 0;
}


//----------------------------------------------------------------------------------------------------
//	pwsf_compress_slot
//----------------------------------------------------------------------------------------------------

int pwsf_compress_slot( PWFileEntry *inPasswordRec, unsigned char **outCompressedRecord, unsigned int *outCompressedRecordLength )
{
	PWFileEntryCompressed *outPWRec = NULL;
	long requiredDigestLength = sizeof(UInt16);		// 2 bytes for the in-use flags
	long requiredLength = 0;
	unsigned long len;
	UInt16 digestFlags = 0x000;
	SInt16 groupCount = 0;
	size_t usernameStrLen = 0;
	char *tptr = NULL;
	uuid_t *groupList = NULL;
	int idx;
	PWFileEntry bigEndianEntry = *inPasswordRec;
	
	pwsf_EndianAdjustPWFileEntry( &bigEndianEntry, kPWByteOrderDiskAndNet );
	
	for ( idx = kPWHashSlotSMB_NT; idx <= kPWHashSlotSALTED_SHA1; idx++ )
	{
		if ( bigEndianEntry.digest[idx].method[0] != '\0' )
		{
			digestFlags |= (1 << idx);
			switch( idx )
			{
				case kPWHashSlotSMB_NT:
				case kPWHashSlotSMB_LAN_MANAGER:
				case kPWHashSlotDIGEST_MD5:
					requiredDigestLength += 16;
					break;
				
				case kPWHashSlotCRAM_MD5:
					requiredDigestLength += 32;
					break;
				
				case kPWHashSlotKERBEROS:
				case kPWHashSlotKERBEROS_NAME:
					requiredDigestLength += strlen(bigEndianEntry.digest[idx].digest) + 1;
					break;
				
				case kPWHashSlotSALTED_SHA1:
					requiredDigestLength += 24;
					break;
			}
		}
	}
	
	usernameStrLen = strlen(bigEndianEntry.usernameStr);
	
	if ( bigEndianEntry.access.isAdminUser )
		groupCount = pwsf_GetGroupList( inPasswordRec, &groupList );	
	
	requiredLength = sizeof(PWFileEntryCompressed)
						+ sizeof(bigEndianEntry.passwordStr)
						+ usernameStrLen + 1
						+ requiredDigestLength;
	
	if ( groupCount > 0 )
		requiredLength += groupCount * sizeof(uuid_t);
	
	outPWRec = (PWFileEntryCompressed *) calloc( 1, requiredLength );
	if ( outPWRec == NULL )
		return -1;
	
	outPWRec->time = bigEndianEntry.time;
	outPWRec->rnd = bigEndianEntry.rnd;
	outPWRec->sequenceNumber = bigEndianEntry.sequenceNumber;
	outPWRec->slot = bigEndianEntry.slot;
	
	// times need to be converted from host byte order
	outPWRec->creationDate = htonl( timegm((struct tm *)&inPasswordRec->creationDate) );
	outPWRec->modificationDate = htonl( timegm((struct tm *)&inPasswordRec->modificationDate) );
	outPWRec->modDateOfPassword = htonl( timegm((struct tm *)&inPasswordRec->modDateOfPassword) );
	outPWRec->lastLogin = htonl( timegm((struct tm *)&inPasswordRec->lastLogin) );
	
	outPWRec->failedLoginAttempts = bigEndianEntry.failedLoginAttempts;
	
	// copy over the bit fields in two bytes
	memcpy( &outPWRec->access, &bigEndianEntry.access, sizeof(char) * 2 );
	
	// times need to be converted from host byte order
	outPWRec->access.expirationDateGMT = htonl( timegm((struct tm *)&inPasswordRec->access.expirationDateGMT) );
	outPWRec->access.hardExpireDateGMT = htonl( timegm((struct tm *)&inPasswordRec->access.hardExpireDateGMT) );
	
	outPWRec->access.maxMinutesUntilChangePassword = bigEndianEntry.access.maxMinutesUntilChangePassword;
	outPWRec->access.maxMinutesUntilDisabled = bigEndianEntry.access.maxMinutesUntilDisabled;
	outPWRec->access.maxMinutesOfNonUse = bigEndianEntry.access.maxMinutesOfNonUse;
	outPWRec->access.maxFailedLoginAttempts = bigEndianEntry.access.maxFailedLoginAttempts;
	outPWRec->access.minChars = bigEndianEntry.access.minChars;
	outPWRec->access.maxChars = bigEndianEntry.access.maxChars;
	
	memcpy( outPWRec->userGUID, bigEndianEntry.userGUID, sizeof(uuid_t) );
	memcpy( outPWRec->userdata, bigEndianEntry.userdata, sizeof(bigEndianEntry.userdata) );
	outPWRec->disableReason = bigEndianEntry.disableReason;
	outPWRec->extraAccess = bigEndianEntry.extraAccess;
	
	tptr = outPWRec->data;
	memcpy( tptr, bigEndianEntry.passwordStr, sizeof(bigEndianEntry.passwordStr) );
	tptr += sizeof(bigEndianEntry.passwordStr);
	memcpy( tptr, bigEndianEntry.usernameStr, usernameStrLen + 1 );
	tptr += usernameStrLen + 1;
	
	*((UInt16 *)tptr) = htons(digestFlags);
	tptr += sizeof(UInt16);
	
	for ( idx = kPWHashSlotSMB_NT; idx <= kPWHashSlotSALTED_SHA1; idx++ )
	{
		if ( bigEndianEntry.digest[idx].method[0] != '\0' )
		{
			switch( idx )
			{
				case kPWHashSlotSMB_NT:
				case kPWHashSlotSMB_LAN_MANAGER:
					ConvertHexToBinary( &bigEndianEntry.digest[idx].digest[1], (unsigned char *)tptr, &len );
					tptr += 16;
					break;
					
				case kPWHashSlotDIGEST_MD5:
					memcpy( tptr, &bigEndianEntry.digest[idx].digest[1], 16 );
					tptr += 16;
					break;
				
				case kPWHashSlotCRAM_MD5:
					memcpy( tptr, &bigEndianEntry.digest[idx].digest[1], 32 );
					tptr += 32;
					break;
				
				case kPWHashSlotKERBEROS:
				case kPWHashSlotKERBEROS_NAME:
					strcpy( tptr, bigEndianEntry.digest[idx].digest );
					tptr += strlen(bigEndianEntry.digest[idx].digest) + 1;
					break;
				
				case kPWHashSlotSALTED_SHA1:
					memcpy( tptr, &bigEndianEntry.digest[idx].digest[1], 24 );
					tptr += 24;
					break;
			}
		}
	}

	// groups
	*((SInt16 *)tptr) = htons(groupCount);
	tptr += sizeof(SInt16);
	
	for ( idx = 0; idx < groupCount; idx++ )
	{
		memcpy( tptr, &groupList[idx], sizeof(uuid_t) );
		tptr += sizeof(uuid_t);
	}
	
	// set out params
	*outCompressedRecord = (unsigned char *)outPWRec;
	*outCompressedRecordLength = requiredLength;
	return 0;
}


//----------------------------------------------------------------------------------------------------
//	pwsf_expand_slot
//----------------------------------------------------------------------------------------------------

int pwsf_expand_slot( const unsigned char *inCompressedRecord, unsigned int inCompressedRecordLength, PWFileEntry *inOutPasswordRec )
{
	PWFileEntryCompressed *inRec = (PWFileEntryCompressed *)inCompressedRecord;
	UInt16 digestFlags = 0x000;
	SInt16 groupCount = 0;
	CFMutableDictionaryRef groupDict = NULL;
	char *tptr;
	int idx;
	char adminID[35] = {0};
	char filePath[PATH_MAX];
	
	if ( inCompressedRecord == NULL || inCompressedRecordLength < sizeof(PWFileEntryCompressed) )
		return -1;
	
	bzero( inOutPasswordRec, sizeof(PWFileEntry) );
	
	inOutPasswordRec->time = inRec->time;
	inOutPasswordRec->rnd = inRec->rnd;
	inOutPasswordRec->sequenceNumber = inRec->sequenceNumber;
	inOutPasswordRec->slot = inRec->slot;
	
	// convert times
	inRec->creationDate = ntohl( inRec->creationDate );
	if ( inRec->creationDate != -1 )
		gmtime_r(&inRec->creationDate, (struct tm *)&inOutPasswordRec->creationDate);

	inRec->modificationDate = ntohl( inRec->modificationDate );
	if ( inRec->modificationDate != -1 )
		gmtime_r(&inRec->modificationDate, (struct tm *)&inOutPasswordRec->modificationDate);

	inRec->modDateOfPassword = ntohl( inRec->modDateOfPassword );
	if ( inRec->modDateOfPassword != -1 )
		gmtime_r(&inRec->modDateOfPassword, (struct tm *)&inOutPasswordRec->modDateOfPassword);

	inRec->lastLogin = ntohl( inRec->lastLogin );
	if ( inRec->lastLogin != -1 )
		gmtime_r(&inRec->lastLogin, (struct tm *)&inOutPasswordRec->lastLogin);
	
	inOutPasswordRec->failedLoginAttempts = inRec->failedLoginAttempts;
	
	// copy over the bit fields in two bytes
	memcpy( &inOutPasswordRec->access, &inRec->access, sizeof(char) * 2 );
	
	// convert times
	if ( inRec->access.expirationDateGMT != -1 ) {
		inRec->access.expirationDateGMT = ntohl( inRec->access.expirationDateGMT );
		gmtime_r(&inRec->access.expirationDateGMT, (struct tm *)&inOutPasswordRec->access.expirationDateGMT);
	}
	if ( inRec->access.hardExpireDateGMT != -1 ) {
		inRec->access.hardExpireDateGMT = ntohl( inRec->access.hardExpireDateGMT );
		gmtime_r(&inRec->access.hardExpireDateGMT, (struct tm *)&inOutPasswordRec->access.hardExpireDateGMT);
	}
	
	inOutPasswordRec->access.maxMinutesUntilChangePassword = inRec->access.maxMinutesUntilChangePassword;
	inOutPasswordRec->access.maxMinutesUntilDisabled = inRec->access.maxMinutesUntilDisabled;
	inOutPasswordRec->access.maxMinutesOfNonUse = inRec->access.maxMinutesOfNonUse;
	inOutPasswordRec->access.maxFailedLoginAttempts = inRec->access.maxFailedLoginAttempts;
	inOutPasswordRec->access.minChars = inRec->access.minChars;
	inOutPasswordRec->access.maxChars = inRec->access.maxChars;
	
	memcpy( inOutPasswordRec->userGUID, inRec->userGUID, sizeof(uuid_t) );
	memcpy( inOutPasswordRec->userdata, inRec->userdata, sizeof(inRec->userdata) );
	inOutPasswordRec->disableReason = inRec->disableReason;
	inOutPasswordRec->extraAccess = inRec->extraAccess;
	
	tptr = inRec->data;
	memcpy( inOutPasswordRec->passwordStr, tptr, sizeof(inOutPasswordRec->passwordStr) );
	tptr += sizeof(inOutPasswordRec->passwordStr);
	strcpy( inOutPasswordRec->usernameStr, tptr );
	tptr += strlen(inOutPasswordRec->usernameStr) + 1;
	
	digestFlags = ntohs( *((UInt16 *)tptr) );
	tptr += sizeof(UInt16);
	
	for ( idx = kPWHashSlotSMB_NT; idx <= kPWHashSlotSALTED_SHA1; idx++ )
	{
		if ( (digestFlags & 0x01) != 0 )
		{
			switch( idx )
			{
				case kPWHashSlotSMB_NT:
					strcpy( inOutPasswordRec->digest[kPWHashSlotSMB_NT].method, kSMBNTStorageTag );
					ConvertBinaryToHex( (unsigned char *)tptr, 16, &inOutPasswordRec->digest[kPWHashSlotSMB_NT].digest[1] );
					inOutPasswordRec->digest[kPWHashSlotSMB_NT].digest[0] = 64;
					tptr += 16;
					break;
				
				case kPWHashSlotSMB_LAN_MANAGER:
					strcpy( inOutPasswordRec->digest[kPWHashSlotSMB_LAN_MANAGER].method, "*cmusaslsecretSMBLM" );
					ConvertBinaryToHex( (unsigned char *)tptr, 16, &inOutPasswordRec->digest[kPWHashSlotSMB_LAN_MANAGER].digest[1] );
					inOutPasswordRec->digest[kPWHashSlotSMB_LAN_MANAGER].digest[0] = 32;
					tptr += 16;
					break;
				
				case kPWHashSlotDIGEST_MD5:
					strncpy( inOutPasswordRec->digest[kPWHashSlotDIGEST_MD5].method, "*cmusaslsecretDIGEST-MD5", SASL_MECHNAMEMAX );
					memcpy( &inOutPasswordRec->digest[idx].digest[1], tptr, 16 );
					inOutPasswordRec->digest[kPWHashSlotDIGEST_MD5].digest[0] = 16;
					tptr += 16;
					break;
				
				case kPWHashSlotCRAM_MD5:
					strncpy( inOutPasswordRec->digest[idx].method, "*cmusaslsecretCRAM-MD5", SASL_MECHNAMEMAX );
					memcpy( &inOutPasswordRec->digest[idx].digest[1], tptr, 32 );
					inOutPasswordRec->digest[idx].digest[0] = 32;
					tptr += 32;
					break;
				
				case kPWHashSlotKERBEROS:
					strcpy( inOutPasswordRec->digest[idx].method, "KerberosRealmName" );
					strlcpy( inOutPasswordRec->digest[idx].digest, tptr, sizeof(inOutPasswordRec->digest[idx].digest) );
					tptr += strlen(tptr) + 1;
					break;
					
				case kPWHashSlotKERBEROS_NAME:
					strcpy( inOutPasswordRec->digest[idx].method, "KerberosPrincName" );
					strlcpy( inOutPasswordRec->digest[idx].digest, tptr, sizeof(inOutPasswordRec->digest[idx].digest) );
					tptr += strlen(tptr) + 1;
					break;
				
				case kPWHashSlotSALTED_SHA1:
					strncpy( inOutPasswordRec->digest[idx].method, "*cmusaslsecretPPS", SASL_MECHNAMEMAX );
					memcpy( &inOutPasswordRec->digest[idx].digest[1], tptr, 24 );
					inOutPasswordRec->digest[idx].digest[0] = 24;
					tptr += 24;
					break;
			}
		}
		
		digestFlags >>= 1;
	}
	
	pwsf_EndianAdjustPWFileEntryNoTimes( inOutPasswordRec, kPWByteOrderHost );
	
	// groups
	groupCount = ntohs( *((SInt16 *)tptr) );
	tptr += sizeof(SInt16);
	switch( groupCount )
	{
		case -1:
		case 0:
			inOutPasswordRec->admingroup.list_type = kPWGroupNotSet;
			break;
		
		case 1:
			inOutPasswordRec->admingroup.list_type = kPWGroupInSlot;
			memcpy( &inOutPasswordRec->admingroup.group_uuid, tptr, sizeof(uuid_t) );
			tptr += sizeof(uuid_t);
			break;
		
		default:
			inOutPasswordRec->admingroup.list_type = kPWGroupInFile;
			groupDict = pwsf_CreateAdditionalDataDictionaryWithUUIDList( groupCount, (uuid_t *)tptr );
			if ( groupDict != NULL ) {
				pwsf_passwordRecRefToString( inOutPasswordRec, adminID );
				snprintf( filePath, sizeof(filePath), "%s/%s.plist", kPWAuxDirPath, adminID );
				if ( pwsf_savexml(filePath, groupDict) != 0 ) {
					inOutPasswordRec->admingroup.list_type = kPWGroupInSlot;
					memcpy( &inOutPasswordRec->admingroup.group_uuid, tptr, sizeof(uuid_t) );
				}
			}
			tptr += groupCount * sizeof(uuid_t);
	}
	
	return 0;
}


/*----------------------------------------------------------------------------------*/

#pragma mark -
#pragma mark DES ACCESSORS
#pragma mark -


//----------------------------------------------------------------------------------------------------
//	pwsf_DESEncode
//----------------------------------------------------------------------------------------------------

void pwsf_DESEncode(void *data, unsigned long inDataLen)
{
	char *tptr = (char *)data;
        
    while ( inDataLen > 0 )
    {
        Encode(gDESKeyArray, kFixedDESChunk, tptr);
        tptr += kFixedDESChunk;
        inDataLen -= kFixedDESChunk;
    }
}


//----------------------------------------------------------------------------------------------------
//	pwsf_DESDecode
//----------------------------------------------------------------------------------------------------

void pwsf_DESDecode(void *data, unsigned long inDataLen)
{
    char *tptr = (char *)data;
    
    while ( inDataLen > 0 )
    {
        Decode(gDESKeyArray, kFixedDESChunk, tptr);
        tptr += kFixedDESChunk;
        inDataLen -= kFixedDESChunk;
    }
}


//----------------------------------------------------------------------------------------------------
//	pwsf_DESAutoDecode
//----------------------------------------------------------------------------------------------------

void pwsf_DESAutoDecode(void *data)
{
	PWFileEntry anEntry;
	unsigned long offset;
	unsigned char *dataPtr;
	
	// decrypt each block of 8
	// because decryption is expensive, look for the 0-terminator in the decrypted data
	// to determine the stopping point.
	
	for ( offset = 0; offset <= sizeof(anEntry.passwordStr) - kFixedDESChunk; offset += kFixedDESChunk )
	{
		dataPtr = (unsigned char *)data + offset;
		pwsf_DESDecode( dataPtr, kFixedDESChunk );
		
		if ( dataPtr[0] == 0 || dataPtr[1] == 0 || dataPtr[2] == 0 ||
			 dataPtr[3] == 0 || dataPtr[4] == 0 || dataPtr[5] == 0 ||
			 dataPtr[6] == 0 || dataPtr[7] == 0 )
			break;
	}
}

#pragma mark -
#pragma mark GROUP LIST HANDLERS
#pragma mark -

//------------------------------------------------------------------------------------------------
//	pwsf_is_guid
//------------------------------------------------------------------------------------------------

bool pwsf_is_guid( const char *inStr )
{
	int idx;
	
	if ( inStr != NULL && strlen(inStr) == 36 &&
		 inStr[8] == '-' && inStr[13] == '-' &&
		 inStr[18] == '-' && inStr[23] == '-' )
	{
		for ( idx = 0; idx < 8; idx++ )
			if ( !ishexnumber(inStr[idx]) )
				return false;
		for ( idx = 9; idx < 13; idx++ )
			if ( !ishexnumber(inStr[idx]) )
				return false;
		for ( idx = 14; idx < 18; idx++ )
			if ( !ishexnumber(inStr[idx]) )
				return false;
		for ( idx = 19; idx < 23; idx++ )
			if ( !ishexnumber(inStr[idx]) )
				return false;
		for ( idx = 24; idx < 36; idx++ )
			if ( !ishexnumber(inStr[idx]) )
				return false;
				
		return true;
	}
	
	return false;
}


//----------------------------------------------------------------------------------------------------
//	pwsf_uuid_for_group
//----------------------------------------------------------------------------------------------------

bool pwsf_uuid_for_group( const char *groupName, uuid_t uuid )
{
	gid_t gid = 0;
	int result = -1;
	struct group *theGroup = NULL;
	
	if ( groupName != NULL )
	{
		theGroup = getgrnam( groupName );
		if ( theGroup != NULL )
		{
			gid = theGroup->gr_gid;
			result = mbr_gid_to_uuid( gid, uuid );
		}
	}
	
	return (result == 0);
}


//----------------------------------------------------------------------------------------------------
//	pwsf_PolicyStringToGroupList
//
//	Returns: The number of groups in the list. <outGroupList> is a NULL terminated
//				array. The group count is returned as a convenience.
//				-1 is returned on error.
//----------------------------------------------------------------------------------------------------

int pwsf_PolicyStringToGroupList( const char *inString, uuid_t *outGroupList[] )
{
	uuid_t *groupArray = NULL;
	const char *groupPtr = NULL;
	char *groupStrCopy = NULL;
	char *tptr = NULL;
	char *curptr = NULL;
	int groupCount = 0;
	int idx = 0;
	uuid_t aGroupUUID;
	
	if ( inString == NULL || outGroupList == NULL )
		return -1;
	
	*outGroupList = NULL;
	
	groupPtr = strstr( inString, kPWPolicyStr_adminAuthorityGroups );
	if ( groupPtr == NULL )
		return -1;
	
	groupPtr += sizeof( kPWPolicyStr_adminAuthorityGroups );
	if ( *groupPtr == '\0' )
		return -1;
	
	groupStrCopy = strdup( groupPtr );
	if ( groupStrCopy != NULL )
	{
		// find the end of the group list and chop
		tptr = strchr( groupStrCopy, ' ' );
		if ( tptr != NULL )
			*tptr = '\0';
		
		if ( *groupStrCopy == '\0' ) {
			free( groupStrCopy );
			return 0;
		}
		
		// get a count
		for ( tptr = groupStrCopy; tptr != NULL; ) {
			groupCount++;
			tptr = strchr( tptr, ',' );
			if ( tptr != NULL )
				tptr++;
		}
		
		// create the array
		groupArray = (uuid_t *) calloc( groupCount + 1, sizeof(uuid_t) );
		if ( groupArray != NULL )
		{
			for ( tptr = groupStrCopy; tptr != NULL; )
			{
				curptr = strsep( &tptr, "," );
				if ( curptr != NULL )
				{
					if ( pwsf_is_guid(curptr) && pwsf_UUIDStrToUUID(curptr, &aGroupUUID) )
					{
						memcpy( groupArray[idx++], &aGroupUUID, sizeof(uuid_t) );
					}
					else
					if ( pwsf_uuid_for_group(curptr, aGroupUUID) )
					{
						memcpy( groupArray[idx++], &aGroupUUID, sizeof(uuid_t) );
					}
					else
					{
						// invalid group
						idx = -1;
						break;
					}
				}
			}
			
			if ( idx > 0 ) {
				*outGroupList = groupArray;
			}
			else {
				// invalid group in the list.
				free( groupArray );
				idx = -1;
			}
		}
		
		free( groupStrCopy );
	}
	
	return idx;
}


// ----------------------------------------------------------------------------------------
//  pwsf_CreateAdditionalDataDictionaryWithUUIDList
//
//	Returns: A dictionary that contains the key kPWKey_ScopeOfAuthority. The value is a
//			 CFArray of UUIDs in string form.
//
// ----------------------------------------------------------------------------------------

CFMutableDictionaryRef pwsf_CreateAdditionalDataDictionaryWithUUIDList( int uuidCount, uuid_t *uuidList )
{
	CFMutableDictionaryRef groupDict = NULL;
	CFMutableArrayRef groupArray = NULL;
	CFStringRef uuidString = NULL;
	BOOL complete = NO;
	int idx;
	
	groupDict = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
	if ( groupDict == NULL )
		return NULL;

	do
	{
		groupArray = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
		if ( groupArray == NULL )
			break;
		
		for ( idx = 0; idx < uuidCount; idx++ )
		{
			if ( uuidList != NULL )
			{
				uuidString = pwsf_UUIDToString( uuidList[idx] );
				if ( uuidString == NULL )
					break;
				
				CFArrayAppendValue( groupArray, uuidString );
				CFRelease( uuidString );
				uuidString = NULL;
			}
		}
		
		CFDictionarySetValue( groupDict, CFSTR(kPWKey_ScopeOfAuthority), groupArray );
		CFRelease( groupArray );
		groupArray = NULL;
		
		complete = YES;
	}
	while (0);
	
	if ( complete == NO && groupDict != NULL )
	{
		if ( groupArray != NULL ) {
			CFRelease( groupArray );
			groupArray = NULL;
		}
		
		CFRelease( groupDict );
		groupDict = NULL;
	}
	
	return groupDict;
}


// ----------------------------------------------------------------------------------------
//  pwsf_CreateAdditionalDataDictionaryWithOwners
//
//	Returns: A dictionary that contains the key kPWKey_ComputerAccountOwnerList. The value
//			 is a CFArray of slot IDs in string form.
//
// ----------------------------------------------------------------------------------------

CFMutableDictionaryRef pwsf_CreateAdditionalDataDictionaryWithOwners( const char *inSlotIDList )
{
	CFMutableDictionaryRef additionalDataDict = NULL;
	CFStringRef slotListString = NULL;
	CFArrayRef slotListArray = NULL;
	
	try
	{
		slotListString = CFStringCreateWithCString( kCFAllocatorDefault, inSlotIDList, kCFStringEncodingUTF8 );
		if ( slotListString == NULL )
			throw( 1 );
		
		slotListArray = CFStringCreateArrayBySeparatingStrings( kCFAllocatorDefault, slotListString, CFSTR(",") );
		if ( slotListArray == NULL )
			throw( 1 );
		
		additionalDataDict = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
		if ( additionalDataDict == NULL )
			throw( 1 );
		
		CFDictionarySetValue( additionalDataDict, CFSTR(kPWKey_ComputerAccountOwnerList), slotListArray );
	}
	catch( ... )
	{
	}
	
	if ( slotListArray != NULL )
		CFRelease( slotListArray );
	if ( slotListString != NULL )
		CFRelease( slotListString );
	
	return additionalDataDict;
}


// ----------------------------------------------------------------------------------------
//  pwsf_UUIDToString
// ----------------------------------------------------------------------------------------

CFStringRef pwsf_UUIDToString( uuid_t uuid )
{
	CFUUIDRef uuidRef = NULL;
	CFStringRef uuidString = NULL;
	
	uuidRef = CFUUIDCreateWithBytes( kCFAllocatorDefault, uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
										uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15] );
	if ( uuidRef != NULL )
	{
		uuidString = CFUUIDCreateString( kCFAllocatorDefault, uuidRef );
		CFRelease( uuidRef );
	}
	
	return uuidString;
}


//----------------------------------------------------------------------------------------------------
//	pwsf_UUIDStrToUUID
//----------------------------------------------------------------------------------------------------

bool pwsf_UUIDStrToUUID( const char *inUUIDStr, uuid_t *outUUID )
{
	CFStringRef uuidString = NULL;
	CFUUIDRef uuidRef = NULL;
	CFUUIDBytes uuidBytes;
	bool result = false;
	
	if ( inUUIDStr == NULL || outUUID == NULL )
		return false;
	
	uuidString = CFStringCreateWithCString( kCFAllocatorDefault, inUUIDStr, kCFStringEncodingUTF8 );
	if ( uuidString != NULL )
	{
		uuidRef = CFUUIDCreateFromString( kCFAllocatorDefault, uuidString );
		if ( uuidRef != NULL )
		{
			uuidBytes = CFUUIDGetUUIDBytes( uuidRef );
			memcpy( outUUID, &uuidBytes, sizeof(uuid_t) );
			CFRelease( uuidRef );
			result = true;
		}
		
		CFRelease( uuidString );
	}
	
	return result;
}


//----------------------------------------------------------------------------------------------------
//	pwsf_GetGroupList
//
//  Returns: The number of groups in the scope-of-authority list, or -1 for invalid.
//
//	<outGroupList> is a NULL-terminated array. The count is returned as a convenience.
//----------------------------------------------------------------------------------------------------

int pwsf_GetGroupList( PWFileEntry *adminRec, uuid_t **outGroupList )
{
	return pwsf_GetGroupListWithPath( kPWAuxDirPath, adminRec, outGroupList );
}


//----------------------------------------------------------------------------------------------------
//	pwsf_GetGroupListWithPath
//
//  Returns: The number of groups in the scope-of-authority list, or -1 for invalid.
//
//	<outGroupList> is a NULL-terminated array. The count is returned as a convenience.
//----------------------------------------------------------------------------------------------------

int pwsf_GetGroupListWithPath( const char *basePath, PWFileEntry *adminRec, uuid_t **outGroupList )
{
	CFMutableDictionaryRef adminInfoDict = NULL;
	CFArrayRef groupArray = NULL;
	CFStringRef uuidString = NULL;
	CFUUIDRef uuidRef = NULL;
	CFUUIDBytes uuidBytes;
	int groupCount = -1;
	int idx = 0;
	char slotStr[35];
	char pathStr[PATH_MAX];
	
	if ( adminRec == NULL || outGroupList == NULL )
		return groupCount;
		
	*outGroupList = NULL;
			
	switch( adminRec->admingroup.list_type )
	{
		case kPWGroupNotSet:
			groupCount = 0;
			break;
		
		case kPWGroupInSlot:
			*outGroupList = (uuid_t *) calloc( 2, sizeof(uuid_t) );
			if ( *outGroupList != NULL )
			{
				groupCount = 1;
				memcpy( (*outGroupList), adminRec->admingroup.group_uuid, sizeof(uuid_t) );
			}
			break;
			
		case kPWGroupInFile:
			pwsf_passwordRecRefToString( adminRec, slotStr );
			snprintf( pathStr, sizeof(pathStr), "%s/%s.plist", basePath, slotStr );
			if ( pwsf_loadxml(pathStr, &adminInfoDict) == 0 )
			{
				groupArray = (CFArrayRef) CFDictionaryGetValue( adminInfoDict, CFSTR(kPWKey_ScopeOfAuthority) );
				if ( groupArray != NULL )
				{
					groupCount = CFArrayGetCount( groupArray );
					*outGroupList = (uuid_t *) calloc( groupCount + 1, sizeof(uuid_t) );
					if ( *outGroupList != NULL )
					{
						for ( idx = 0; idx < groupCount; idx++ )
						{
							uuidString = (CFStringRef) CFArrayGetValueAtIndex( groupArray, idx );
							if ( uuidString == NULL )
								break;
							
							// convert string to uuid
							uuidRef = CFUUIDCreateFromString( kCFAllocatorDefault, uuidString );
							if ( uuidRef == NULL )
								break;
							
							uuidBytes = CFUUIDGetUUIDBytes( uuidRef );
							memcpy( &((*outGroupList)[idx]), &uuidBytes, sizeof(uuid_t) );
							CFRelease( uuidRef );
						}
					}
				}
			}
			break;
		
		default:
			break;
	}
	
	return groupCount;
}


//----------------------------------------------------------------------------------------------------
//	pwsf_loadxml
//
//	Returns: 0 = success, -1 = failure.
//----------------------------------------------------------------------------------------------------

int pwsf_loadxml( const char *inFilePath, CFMutableDictionaryRef *outPList )
{
	CFStringRef myDataFilePathRef;
	CFURLRef myDataFileRef;
	CFReadStreamRef myReadStreamRef;
	CFPropertyListRef myPropertyListRef;
	CFStringRef errorString;
	CFPropertyListFormat myPLFormat;
	int result = -1;
	
	if ( inFilePath == NULL || outPList == NULL )
		return result;
	
	do
	{
		myDataFilePathRef = CFStringCreateWithCString( kCFAllocatorDefault, inFilePath, kCFStringEncodingUTF8 );
		if ( myDataFilePathRef == NULL )
			break;
		
		myDataFileRef = CFURLCreateWithFileSystemPath( kCFAllocatorDefault, myDataFilePathRef, kCFURLPOSIXPathStyle, false );
		
		CFRelease( myDataFilePathRef );
		
		if ( myDataFileRef == NULL )
			break;
		
		myReadStreamRef = CFReadStreamCreateWithFile( kCFAllocatorDefault, myDataFileRef );
		
		CFRelease( myDataFileRef );
		
		if ( myReadStreamRef == NULL )
			break;
		
		if ( ! CFReadStreamOpen( myReadStreamRef ) ) {
			CFRelease( myReadStreamRef );
			break;
		}
		
		errorString = NULL;
		myPLFormat = kCFPropertyListXMLFormat_v1_0;
		myPropertyListRef = CFPropertyListCreateFromStream( kCFAllocatorDefault, myReadStreamRef, 0,
															kCFPropertyListMutableContainersAndLeaves, &myPLFormat,
															&errorString );
		CFReadStreamClose( myReadStreamRef );
		CFRelease( myReadStreamRef );
		
		if ( errorString != NULL )
			CFRelease( errorString );
		
		if ( myPropertyListRef == NULL )
			break;
		
		if ( CFGetTypeID(myPropertyListRef) != CFDictionaryGetTypeID() ) {
			CFRelease( myPropertyListRef );
			break;
		}
		
		*outPList = (CFMutableDictionaryRef) myPropertyListRef;
		result = 0;
	}
	while (0);
	
	return result;
}


//----------------------------------------------------------------------------------------------------
//	pwsf_savexml
//
//	Returns: 0 = success, -1 = failure.
//----------------------------------------------------------------------------------------------------

int pwsf_savexml(const char *inSaveFile, CFDictionaryRef inPList )
{
	CFStringRef myDataFilePathRef;
	CFURLRef myDataFileRef;
	CFWriteStreamRef myWriteStreamRef = NULL;
	CFStringRef errorString;
	int returnValue = -1;
	struct stat sb;
	int err;
	char *saveDir;
	char *slash;
	mode_t saved_umask;
	
	// ensure the directory exists
	saveDir = strdup( inSaveFile );
	if ( saveDir != NULL ) {
		slash = rindex( saveDir, '/' );
		if ( slash != NULL ) {
			*slash = '\0';
			if ( lstat(saveDir, &sb) != 0 ) {
				err = pwsf_mkdir_p( saveDir, 0700 );
				if ( err != 0 ) {
					free( saveDir );
					return err;
				}
			}
		}
		free( saveDir );
	}
	
	saved_umask = umask((S_IRWXG | S_IRWXO));
	
	do
	{
		myDataFilePathRef = CFStringCreateWithCString( kCFAllocatorDefault, inSaveFile, kCFStringEncodingUTF8 );
		if ( myDataFilePathRef == NULL )
			break;
		
		myDataFileRef = CFURLCreateWithFileSystemPath( kCFAllocatorDefault, myDataFilePathRef, kCFURLPOSIXPathStyle, false );
		
		CFRelease( myDataFilePathRef );
		
		if ( myDataFileRef == NULL )
			break;
		
		myWriteStreamRef = CFWriteStreamCreateWithFile( kCFAllocatorDefault, myDataFileRef );
		
		CFRelease( myDataFileRef );
		
		if ( myWriteStreamRef == NULL )
			break;
		
		if ( CFWriteStreamOpen( myWriteStreamRef ) )
		{		
			errorString = NULL;
			CFPropertyListWriteToStream( (CFPropertyListRef)inPList, myWriteStreamRef, kCFPropertyListXMLFormat_v1_0, &errorString );
			CFWriteStreamClose( myWriteStreamRef );
			
			if ( errorString != NULL ) {
				CFRelease( errorString );
				break;
			}
			
			returnValue = 0;
		}
	}
	while (0);
	
	if ( myWriteStreamRef != NULL )
		CFRelease( myWriteStreamRef );
	
	umask( saved_umask );
	
	return returnValue;
}