#include <sys/types.h>
#include <syslog.h>
#include <stdlib.h>
#include <string.h>
#include <CoreServices/CoreServices.h>
#include <CoreFoundation/CFData.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFPropertyList.h>
#include <DirectoryService/DirServices.h>
#include <DirectoryService/DirServicesUtils.h>
#include <DirectoryService/DirServicesConst.h>
#include "fasterauth.h"
#include "dserr.h"
typedef struct FasterDirectoryService {
char *mNodeName;
tDirReference mDir;
tDirNodeReference mNode;
} FasterDirectoryService;
static CFDictionaryValueCallBacks kNoOpValueCallBacks = { 0 };
static CFMutableDictionaryRef nodes = NULL;
const int cBufferSize = 32 * 1024;
static int CreateFasterDirectoryService(FasterDirectoryService **out, char *nodename);
static int DeleteFasterDirectoryService(FasterDirectoryService **out);
static int GetCachedFasterDirectoryService(char *nodename, FasterDirectoryService **out);
static int RemoveCachedFasterDirectoryService(char *nodename);
static int GetFasterDirectoryServiceDirReference(FasterDirectoryService *svc, tDirReference *out);
static int GetFasterDirectoryServiceDirNodeReference(FasterDirectoryService *svc, tDirNodeReference *out);
static int GetFasterDirectoryServiceDataBufferPtr(FasterDirectoryService *svc, tDataBufferPtr *out);
#define CF_SAFE_RELEASE(cfobj) \
do { if ((cfobj) != NULL) CFRelease((cfobj)); cfobj = NULL; } while (0)
int
FasterAuthentication(char *nodename, char *user,
char *challenge, char *response,
char **serverresponse)
{
int retval = -1;
tDirNodeReference node = 0;
tDataNodePtr authType = NULL;
tDataBufferPtr authData = NULL;
tContextData context = 0;
FasterDirectoryService *svc = 0;
tDirReference dir = 0;
tDataBufferPtr data = NULL;
tDirStatus dirStatus = eDSNoErr;
if (nodename == 0 || user == 0 || challenge == 0 || response == 0 || serverresponse == 0)
goto done;
*serverresponse = 0;
if (GetCachedFasterDirectoryService(nodename, &svc) != 0)
goto done;
if (GetFasterDirectoryServiceDirReference(svc, &dir) != 0)
goto done;
authType = dsDataNodeAllocateString(dir, kDSStdAuthDIGEST_MD5);
UInt32 aDataBufSize = sizeof(UInt32) + strlen(user) +
sizeof(UInt32) + strlen(challenge) +
sizeof(UInt32) + strlen(response);
authData = dsDataBufferAllocate(dir, aDataBufSize);
if (authData == NULL)
goto done;
dsFillAuthBuffer(authData, 3,
strlen(user), user,
strlen(challenge), challenge,
strlen(response), response);
if (GetFasterDirectoryServiceDirNodeReference(svc, &node) != 0)
goto done;
data = dsDataBufferAllocate(dir, cBufferSize);
if (data == NULL)
goto done;
dirStatus = dsDoDirNodeAuth(node, authType, true, authData, data, &context);
if (dirStatus != eDSNoErr)
goto done;
if (data->fBufferLength >= data->fBufferSize)
goto done;
data->fBufferData[data->fBufferLength] = '\0'; *serverresponse = strdup(&data->fBufferData[4]);
if (*serverresponse == 0)
goto done;
retval = 0;
done:
if (data != NULL)
dsDataBufferDeAllocate(dir, data);
data = NULL;
if (authData != NULL)
dsDataBufferDeAllocate(dir, authData);
authData = NULL;
if (authType != NULL)
dsDataNodeDeAllocate(dir, authType);
authType = NULL;
if (retval != 0) {
if (! IS_EXPECTED_DS_ERROR(dirStatus)) {
(void)RemoveCachedFasterDirectoryService(nodename);
}
}
return retval;
}
int
GetCachedFasterDirectoryService(char *nodename, FasterDirectoryService **out)
{
int retval = -1;
FasterDirectoryService *svc = 0;
CFStringRef cfNodeName = NULL;
*out = 0;
if (nodes == NULL) {
nodes = CFDictionaryCreateMutable(kCFAllocatorDefault, 4,
&kCFTypeDictionaryKeyCallBacks,
&kNoOpValueCallBacks);
if (nodes == NULL)
goto done;
}
cfNodeName = CFStringCreateWithCString(kCFAllocatorDefault, nodename, kCFStringEncodingUTF8);
if (cfNodeName == NULL)
goto done;
svc = (FasterDirectoryService*)CFDictionaryGetValue(nodes, cfNodeName);
if (svc != 0) {
if (dsVerifyDirRefNum(svc->mDir) != eDSNoErr) {
svc = 0;
RemoveCachedFasterDirectoryService(nodename);
}
}
if (svc == 0) {
if (CreateFasterDirectoryService(&svc, nodename) != 0)
goto done;
CFDictionarySetValue(nodes, cfNodeName, svc);
}
*out = svc;
retval = 0;
done:
CF_SAFE_RELEASE(cfNodeName);
return retval;
}
int
RemoveCachedFasterDirectoryService(char *nodename)
{
int retval = -1;
CFStringRef cfNodeName = NULL;
FasterDirectoryService *svc = 0;
cfNodeName = CFStringCreateWithCString(kCFAllocatorDefault, nodename, kCFStringEncodingUTF8);
if (cfNodeName == NULL)
goto done;
svc = (FasterDirectoryService*)CFDictionaryGetValue(nodes, cfNodeName);
CFDictionaryRemoveValue(nodes, cfNodeName);
(void)DeleteFasterDirectoryService(&svc);
retval = 0;
done:
CF_SAFE_RELEASE(cfNodeName);
return retval;
}
int
CreateFasterDirectoryService(FasterDirectoryService **out, char *nodename)
{
int retval = -1;
FasterDirectoryService *svc = 0;
*out = 0;
svc = (FasterDirectoryService*)calloc(1, sizeof(*svc));
if (svc == 0)
goto done;
svc->mNodeName = strdup(nodename);
if (svc->mNodeName == 0)
goto done;
*out = svc;
retval = 0;
done:
return retval;
}
int
DeleteFasterDirectoryService(FasterDirectoryService **out)
{
int retval = -1;
FasterDirectoryService *svc = 0;
if (out == 0)
goto done;
if (*out != 0) {
svc = *out;
if (svc->mNodeName != 0)
free(svc->mNodeName);
if (svc->mNode != 0)
dsCloseDirNode(svc->mNode);
if (svc->mDir != 0)
dsCloseDirService(svc->mDir);
memset(svc, 0, sizeof(*svc));
free(svc);
*out = 0;
}
retval = 0;
done:
return retval;
}
int
GetFasterDirectoryServiceDirReference(FasterDirectoryService *svc, tDirReference *out)
{
int retval = -1;
if (svc->mDir == 0) {
tDirStatus dirStatus = dsOpenDirService(&svc->mDir);
if (dirStatus != eDSNoErr) {
svc->mDir = 0;
goto done;
}
}
*out = svc->mDir;
retval = 0;
done:
return retval;
}
int
GetFasterDirectoryServiceDirNodeReference(FasterDirectoryService *svc, tDirNodeReference *out)
{
int retval = -1;
tDirReference dir = 0;
tDirStatus dirStatus = eDSNoErr;
tDataListPtr nodePath = NULL;
if (svc->mNode == 0) {
if (GetFasterDirectoryServiceDirReference(svc, &dir) != 0)
goto done;
nodePath = dsDataListAllocate(dir);
if (nodePath == NULL)
goto done;
dirStatus = dsBuildListFromPathAlloc(dir, nodePath, svc->mNodeName, "/");
if (dirStatus != eDSNoErr)
goto done;
dirStatus = dsOpenDirNode(dir, nodePath, &svc->mNode);
if (dirStatus != eDSNoErr)
goto done;
}
*out = svc->mNode;
retval = 0;
done:
if (nodePath != NULL) {
dirStatus = dsDataListDeallocate(dir, nodePath);
free(nodePath);
nodePath = NULL;
}
return retval;
}