Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
DSUtility.c
/* |
File: DSUtility.c |
Abstract: Utility functions for common authentication/lookup with Directory |
Services |
Version: <1.0> |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
Computer, Inc. ("Apple") in consideration of your agreement to the |
following terms, and your use, installation, modification or |
redistribution of this Apple software constitutes acceptance of these |
terms. If you do not agree with these terms, please do not use, |
install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and |
subject to these terms, Apple grants you a personal, non-exclusive |
license, under Apple's copyrights in this original Apple software (the |
"Apple Software"), to use, reproduce, modify and redistribute the Apple |
Software, with or without modifications, in source and/or binary forms; |
provided that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the following |
text and disclaimers in all such redistributions of the Apple Software. |
Neither the name, trademarks, service marks or logos of Apple Computer, |
Inc. may be used to endorse or promote products derived from the Apple |
Software without specific prior written permission from Apple. Except |
as expressly stated in this notice, no other rights or licenses, express |
or implied, are granted by Apple herein, including but not limited to |
any patent rights that may be infringed by your derivative works or by |
other works in which the Apple Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
POSSIBILITY OF SUCH DAMAGE. |
Copyright © 2005 Apple Computer, Inc., All Rights Reserved |
*/ |
#include "DSUtility.h" |
#include <unistd.h> |
#include <string.h> |
#include <stdlib.h> |
#pragma mark Internal Prototypes |
tDirStatus AppendStringToBuffer( tDataBufferPtr inBuffer, const char *inString, long inLength ); |
#pragma mark - |
#pragma mark Directory Utility routines |
tDirStatus OpenSearchNode( tDirReference inDSRef, tDirNodeReference *outNodeRef ) |
{ |
tDataBufferPtr pWorkingBuffer = NULL; |
tDataListPtr pSearchNode = NULL; |
tDirStatus dsStatus; |
tContextData dsContext = NULL; |
unsigned long ulReturnCount = 0; |
// verify none of the parameters are NULL, if so return an eDSNullParameter |
if( outNodeRef == NULL || inDSRef == 0 ) { |
return eDSNullParameter; |
} |
// allocate a buffer to hold return information, defaulting to 4k |
pWorkingBuffer = dsDataBufferAllocate( inDSRef, 4096 ); |
if( pWorkingBuffer == NULL ) { |
return eMemoryAllocError; |
} |
// locate the name of the search node |
dsStatus = dsFindDirNodes( inDSRef, pWorkingBuffer, NULL, eDSSearchNodeName, &ulReturnCount, &dsContext ); |
if( dsStatus == eDSNoErr ) { |
// pass 1 for node index since there should only be one value |
dsStatus = dsGetDirNodeName( inDSRef, pWorkingBuffer, 1, &pSearchNode ); |
} |
// if we ended up with a context, we should release it |
if( dsContext != NULL ) { |
dsReleaseContinueData( inDSRef, dsContext ); |
dsContext = NULL; |
} |
// release the current working buffer |
if( pWorkingBuffer != NULL ) { |
dsDataBufferDeAllocate( inDSRef, pWorkingBuffer ); |
pWorkingBuffer = NULL; |
} |
// open search node |
if( dsStatus == eDSNoErr && pSearchNode != NULL ) { |
dsStatus = dsOpenDirNode( inDSRef, pSearchNode, outNodeRef ); |
} |
// deallocate the tDataListPtr item used to locate the Search node |
if( pSearchNode != NULL ) { |
dsDataListDeallocate( inDSRef, pSearchNode ); |
// need to free pointer as dsDataListDeallocate does not free it, just the list items |
free( pSearchNode ); |
pSearchNode = NULL; |
} |
return dsStatus; |
} // OpenSearchNode |
tDirStatus LocateUserRecordNameAndNode( tDirReference inDSRef, tDirNodeReference inSearchNode, const char *inUsername, |
char **outRecordName, char **outNodeName ) |
{ |
tDirStatus dsStatus = eDSRecordNotFound; |
tDataListPtr pAttribsToGet = NULL; |
tDataListPtr pRecTypeList = NULL; |
tDataListPtr pRecNameList = NULL; |
tDataBufferPtr pSearchBuffer = NULL; |
unsigned long ulRecCount = 0; // do not limit the number of records we are expecting |
unsigned long ulBufferSize = 2048; // start with a 2k buffer for any data |
// ensure none of the parameters are NULL |
if( inDSRef == 0 || inSearchNode == 0 || inUsername == NULL || outRecordName == NULL || outNodeName == NULL ) { |
return eDSNullParameter; |
} |
// we will want the actual record name and the name of the node where the user resides |
pAttribsToGet = dsBuildListFromStrings( inDSRef, kDSNAttrRecordName, kDSNAttrMetaNodeLocation, NULL ); |
if( pAttribsToGet == NULL ) { |
dsStatus = eMemoryAllocError; |
goto cleanup; |
} |
// build a list to search for user record |
pRecNameList = dsBuildListFromStrings( inDSRef, inUsername, NULL ); |
if( pRecNameList == NULL ) { |
dsStatus = eMemoryAllocError; |
goto cleanup; |
} |
// build a list of record types to search, in this case users |
pRecTypeList = dsBuildListFromStrings( inDSRef, kDSStdRecordTypeUsers, NULL); |
if( pRecTypeList == NULL ) { |
dsStatus = eMemoryAllocError; |
goto cleanup; |
} |
// allocate a working buffer, this may be grown if we receive a eDSBufferTooSmall error |
pSearchBuffer = dsDataBufferAllocate( inDSRef, ulBufferSize ); |
if( pSearchBuffer == NULL ) { |
dsStatus = eMemoryAllocError; |
goto cleanup; |
} |
// now search for the record using dsGetRecordList |
dsStatus = dsGetRecordList( inSearchNode, pSearchBuffer, pRecNameList, eDSExact, pRecTypeList, |
pAttribsToGet, 0, &ulRecCount, NULL ); |
// if there was not an error and we found only 1 record for this user |
if( dsStatus == eDSNoErr && ulRecCount == 1 ) { |
tAttributeListRef dsAttributeListRef = 0; |
tRecordEntryPtr dsRecordEntryPtr = 0; |
int ii; |
// Get the 1st record entry from the buffer since we only expect 1 result |
dsStatus = dsGetRecordEntry( inSearchNode, pSearchBuffer, 1, &dsAttributeListRef, &dsRecordEntryPtr ); |
if (dsStatus == eDSNoErr) |
{ |
// loop through the attributes in the record to get the data we requested |
// all indexes with Open Directory APIs start with 1 not 0 |
for (ii = 1 ; ii <= dsRecordEntryPtr->fRecordAttributeCount; ii++) |
{ |
tAttributeEntryPtr dsAttributeEntryPtr = NULL; |
tAttributeValueEntryPtr dsAttributeValueEntryPtr = NULL; |
tAttributeValueListRef dsAttributeValueListRef = 0; |
// get the attribute entry from the record |
dsStatus = dsGetAttributeEntry( inSearchNode, pSearchBuffer, dsAttributeListRef, ii, |
&dsAttributeValueListRef, &dsAttributeEntryPtr ); |
// get the value from the attribute if we were successful at getting an entry |
if( dsStatus == eDSNoErr ) { |
dsStatus = dsGetAttributeValue( inSearchNode, pSearchBuffer, 1, |
dsAttributeValueListRef, &dsAttributeValueEntryPtr ); |
} |
// if we were still sucessful, see which attribute we were getting and fill in the return |
// values appropriately |
if( dsStatus == eDSNoErr ) { |
// always check for the specific attributes, since a plugin is not restricted from |
// returning more data than you requested |
// check the signature of the attribute and see if it is the metanode location |
if (strcmp(dsAttributeEntryPtr->fAttributeSignature.fBufferData, kDSNAttrMetaNodeLocation) == 0) { |
*outNodeName = (char *) calloc( dsAttributeValueEntryPtr->fAttributeValueData.fBufferSize + 1, sizeof(char)); |
if( *outNodeName != NULL ) { |
strncpy( *outNodeName, dsAttributeValueEntryPtr->fAttributeValueData.fBufferData, dsAttributeValueEntryPtr->fAttributeValueData.fBufferSize); |
} |
// if not, see if it is the Record Name |
} else if (strcmp(dsAttributeEntryPtr->fAttributeSignature.fBufferData, kDSNAttrRecordName) == 0) { |
*outRecordName = (char *) calloc( dsAttributeValueEntryPtr->fAttributeValueData.fBufferSize + 1, sizeof(char)); |
if( *outRecordName != NULL ) { |
strncpy( *outRecordName, dsAttributeValueEntryPtr->fAttributeValueData.fBufferData, |
dsAttributeValueEntryPtr->fAttributeValueData.fBufferSize ); |
} |
} |
} |
// close any value list references that may have been opened |
if( dsAttributeValueListRef != 0 ) { |
dsCloseAttributeValueList( dsAttributeValueListRef ); |
dsAttributeValueListRef = 0; |
} |
// free the attribute value entry if we got an entry |
if( dsAttributeValueEntryPtr != NULL ) { |
dsDeallocAttributeValueEntry( inDSRef, dsAttributeValueEntryPtr ); |
dsAttributeValueEntryPtr = NULL; |
} |
// free the attribute entry itself as well |
if( dsAttributeEntryPtr != NULL ) { |
dsDeallocAttributeEntry( inDSRef, dsAttributeEntryPtr ); |
dsAttributeEntryPtr = NULL; |
} |
} |
// close any reference to attribute list |
if( dsAttributeListRef != 0 ) { |
dsCloseAttributeList( dsAttributeListRef ); |
dsAttributeListRef = 0; |
} |
// deallocate the record entry |
if( dsRecordEntryPtr != NULL ) { |
dsDeallocRecordEntry( inDSRef, dsRecordEntryPtr ); |
dsRecordEntryPtr = NULL; |
} |
} |
} else if( dsStatus == eDSNoErr && ulRecCount > 1 ) { |
// if we have more than 1 user, then we shouldn't attempt to authenticate |
// we chose to return eDSAuthInvalidUserName as an error since we can't distinguish |
// the specific user to return |
dsStatus = eDSAuthInvalidUserName; |
} |
cleanup: |
// if we allocated pAttribsToGet, we need to clean up |
if( pAttribsToGet != NULL ) { |
dsDataListDeallocate( inDSRef, pAttribsToGet ); |
// need to free pointer as dsDataListDeallocate does not free it, just the list items |
free( pAttribsToGet ); |
pAttribsToGet = NULL; |
} |
// if we allocated pRecTypeList, we need to clean up |
if( pRecTypeList != NULL ) { |
dsDataListDeallocate( inDSRef, pRecTypeList ); |
// need to free pointer as dsDataListDeallocate does not free it, just the list items |
free( pRecTypeList ); |
pRecTypeList = NULL; |
} |
// if we allocated pRecNameList, we need to clean up |
if( pRecNameList != NULL ) { |
dsDataListDeallocate( inDSRef, pRecNameList ); |
// need to free pointer as dsDataListDeallocate does not free it, just the list items |
free( pRecNameList ); |
pRecNameList = NULL; |
} |
// if we allocated pSearchBuffer, we need to clean up |
if( pSearchBuffer != NULL ) { |
dsDataBufferDeAllocate( inDSRef, pSearchBuffer ); |
pSearchBuffer = NULL; |
} |
return dsStatus; |
} // LocateUserRecordNameAndNode |
#pragma mark - |
#pragma mark Authentication Routines |
tDirStatus DoPasswordAuth( tDirReference inDSRef, tDirNodeReference inNodeRef, const char *inAuthMethod, |
const char *inRecordName, const char *inPassword ) |
{ |
tDirStatus dsStatus = eDSAuthFailed; |
tDataNodePtr pAuthMethod = NULL; |
tDataBufferPtr pAuthStepData = NULL; |
tDataBufferPtr pAuthRespData = NULL; |
tContextData pContextData = NULL; |
// if any of our parameters are NULL, return a NULL parameter |
// if a password is not set for a user, an empty string should be sent for the password |
if( inDSRef == 0 || inNodeRef == 0 || inRecordName == NULL || inPassword == NULL ) { |
return eDSNullParameter; |
} |
// since this is password based, we can only support password-based methods |
if( strcmp(inAuthMethod, kDSStdAuthNodeNativeNoClearText) == 0 || |
strcmp(inAuthMethod, kDSStdAuthNodeNativeClearTextOK) == 0 || |
strcmp(inAuthMethod, kDSStdAuthClearText) == 0 || |
strcmp(inAuthMethod, kDSStdAuthCrypt) == 0 ) { |
// turn the specified method into a tDataNode |
pAuthMethod = dsDataNodeAllocateString( inDSRef, inAuthMethod ); |
if( pAuthMethod == NULL ) { |
dsStatus = eMemoryAllocError; |
goto cleanup; |
} |
// allocate a buffer large enough to hold all the username and password plus length bytes |
pAuthStepData = dsDataBufferAllocate( inDSRef, 4 + strlen(inRecordName) + 4 + strlen(inPassword) ); |
if( pAuthStepData == NULL ) { |
dsStatus = eMemoryAllocError; |
goto cleanup; |
} |
// allocate a buffer for the out step data even though we don't expect anything, |
// it is a required parameter |
pAuthRespData = dsDataBufferAllocate( inDSRef, 128 ); |
if( pAuthRespData == NULL ) { |
dsStatus = eMemoryAllocError; |
goto cleanup; |
} |
// now place the username and password into the buffer |
AppendStringToBuffer( pAuthStepData, inRecordName, strlen(inRecordName) ); |
AppendStringToBuffer( pAuthStepData, inPassword, strlen(inPassword) ); |
// attemp the authentication |
dsStatus = dsDoDirNodeAuth( inNodeRef, pAuthMethod, 1, pAuthStepData, pAuthRespData, &pContextData ); |
} else { |
// otherwise, return a parameter error |
dsStatus = eDSAuthParameterError; |
} |
cleanup: |
// release pContextData if we had continue data |
if( pContextData != NULL ) { |
dsReleaseContinueData( inDSRef, pContextData ); |
pContextData = NULL; |
} |
// deallocate memory for pAuthRespData if it was allocated |
if( pAuthRespData != NULL ) { |
dsDataNodeDeAllocate( inDSRef, pAuthRespData ); |
pAuthRespData = NULL; |
} |
// deallocate memory for pAuthStepData if it was allocated |
if( pAuthStepData != NULL ) { |
dsDataBufferDeAllocate( inDSRef, pAuthStepData ); |
pAuthStepData = NULL; |
} |
// deallocate memory for pAuthMethod if it was allocated |
if( pAuthMethod != NULL ) { |
dsDataNodeDeAllocate( inDSRef, pAuthMethod ); |
pAuthMethod = NULL; |
} |
return dsStatus; |
} // DoPasswordAuth |
tDirStatus DoChallengeResponseAuth( tDirReference inDSRef, tDirNodeReference inNodeRef, const char *inAuthMethod, |
const char *inRecordName, const char *inChallenge, long inChallengeLen, |
const char *inResponse, long inResponseLen ) |
{ |
tDirStatus dsStatus = eDSAuthFailed; |
tDataNodePtr pAuthMethod = NULL; |
tDataBufferPtr pAuthStepData = NULL; |
tDataBufferPtr pAuthRespData = NULL; |
tContextData pContextData = NULL; |
// if any of our parameters are NULL, return a NULL parameter |
// if a password is not set for a user, an empty string should be sent for the password |
if( inDSRef == 0 || inNodeRef == 0 || inRecordName == NULL || inChallenge == NULL ) { |
return eDSNullParameter; |
} |
// we will only authenticate using methods which are challenge-response based |
if( strcmp(inAuthMethod, kDSStdAuthCRAM_MD5) == 0 || |
strcmp(inAuthMethod, kDSStdAuthAPOP) == 0 || |
strcmp(inAuthMethod, kDSStdAuthCHAP) == 0 || |
strcmp(inAuthMethod, kDSStdAuthSMB_LM_Key) == 0 || |
strcmp(inAuthMethod, kDSStdAuthSMB_NT_Key) == 0 ) { |
// turn the specified method into a tDataNode |
pAuthMethod = dsDataNodeAllocateString( inDSRef, inAuthMethod ); |
if( pAuthMethod == NULL ) { |
dsStatus = eMemoryAllocError; |
goto cleanup; |
} |
// allocate a buffer large enough to hold all the username and password plus length bytes |
pAuthStepData = dsDataBufferAllocate( inDSRef, 4 + strlen(inRecordName) + 4 + inChallengeLen + 4 + inResponseLen ); |
if( pAuthStepData == NULL ) { |
dsStatus = eMemoryAllocError; |
goto cleanup; |
} |
// allocate a buffer for the out step data even though we don't expect anything, |
// it is a required parameter |
pAuthRespData = dsDataBufferAllocate( inDSRef, 128 ); |
if( pAuthRespData == NULL ) { |
dsStatus = eMemoryAllocError; |
goto cleanup; |
} |
// now place the username and password into the buffer |
AppendStringToBuffer( pAuthStepData, inRecordName, strlen(inRecordName) ); |
AppendStringToBuffer( pAuthStepData, inChallenge, inChallengeLen ); |
AppendStringToBuffer( pAuthStepData, inResponse, inResponseLen ); |
// attempt the authentication |
dsStatus = dsDoDirNodeAuth( inNodeRef, pAuthMethod, 1, pAuthStepData, pAuthRespData, &pContextData ); |
} else { |
// otherwise, return a parameter error |
dsStatus = eDSAuthParameterError; |
} |
cleanup: |
// release pContextData if we had continue data |
if( pContextData != NULL ) { |
dsReleaseContinueData( inDSRef, pContextData ); |
pContextData = NULL; |
} |
// deallocate memory for pAuthRespData if it was allocated |
if( pAuthRespData != NULL ) { |
dsDataNodeDeAllocate( inDSRef, pAuthRespData ); |
pAuthRespData = NULL; |
} |
// deallocate memory for pAuthStepData if it was allocated |
if( pAuthStepData != NULL ) { |
dsDataBufferDeAllocate( inDSRef, pAuthStepData ); |
pAuthStepData = NULL; |
} |
// deallocate memory for pAuthMethod if it was allocated |
if( pAuthMethod != NULL ) { |
dsDataNodeDeAllocate( inDSRef, pAuthMethod ); |
pAuthMethod = NULL; |
} |
return dsStatus; |
} // DoChallengeResponseAuth |
#pragma mark - |
#pragma mark Support Functions |
tDirStatus AppendStringToBuffer( tDataBufferPtr inBuffer, const char *inString, long inLength ) |
{ |
tDirStatus dsStatus = eDSBufferTooSmall; |
// ensure neither of our parameters are NULL |
if( inString == NULL || inBuffer == NULL ) { |
return eDSNullParameter; |
} |
// check to see if we have enough room in the buffer for the string and the 4 byte length |
if( inBuffer->fBufferSize >= (inBuffer->fBufferLength + 4 + inLength) ) { |
char *pBufferEnd = inBuffer->fBufferData + inBuffer->fBufferLength; |
// prepend the data with the length of the string |
bcopy( &inLength, pBufferEnd, sizeof(long) ); |
pBufferEnd += sizeof( long ); |
// now add the string to the buffer |
bcopy( inString, pBufferEnd, inLength ); |
// increase the buffer accordingly |
inBuffer->fBufferLength += 4 + inLength; |
// set successful error status |
dsStatus = eDSNoErr; |
} |
return dsStatus; |
} // AppendStringToBuffer |
Copyright © 2005 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2005-06-01