#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <NetInfo/system_log.h>
#include <NetInfo/dsutil.h>
#include <NetInfo/DynaAPI.h>
#include <DirectoryService/DirServices.h>
#include <DirectoryService/DirServicesConst.h>
#include <DirectoryService/DirServicesTypes.h>
#include <DirectoryService/DirServicesUtils.h>
#define DefaultTimeToLive 300
#define kAttrConsts 71
static const char *sAttrMap[kAttrConsts][2] =
{
{ kDSNAttrRecordName, "name" },
{ kDS1AttrDistinguishedName, "realname" },
{ kDS1AttrPasswordPlus, "passwd" },
{ kDS1AttrPassword, "passwd" },
{ kDS1AttrUniqueID, "uid" },
{ kDS1AttrPrimaryGroupID, "gid" },
{ kDS1AttrUserShell, "shell" },
{ kDS1AttrNFSHomeDirectory, "home" },
{ kDSNAttrAuthenticationAuthority, "authentication_authority" },
{ kDSNAttrHomeDirectory, "home_loc" },
{ kDS1StandardAttrHomeLocOwner, "home_loc_owner" },
{ kDS1AttrHomeDirectoryQuota, "homedirectoryquota" },
{ kDS1AttrPicture, "picture" },
{ kDS1AttrInternetAlias, "InetAlias" },
{ kDS1AttrMailAttribute, "applemail" },
{ kDS1AttrAuthenticationHint, "hint" },
{ kDS1AttrRARA, "RARA" },
{ kDS1AttrGeneratedUID, "GeneratedUID" },
{ kDSNAttrGroupMembership, "users" },
{ kDSNAttrEMailAddress, "mail" },
{ kDSNAttrURL, "URL" },
{ kDSNAttrURLForNSL, "URL" },
{ kDSNAttrMIME, "mime" },
{ kDSNAttrHTML, "htmldata" },
{ kDSNAttrNBPEntry, "NBPEntry" },
{ kDSNAttrDNSName, "dnsname" },
{ kDSNAttrIPAddress, "IP_Address" },
{ kDS1AttrENetAddress, "en_address" },
{ kDSNAttrComputers, "computers" },
{ kDS1AttrMCXFlags, "mcx_flags" },
{ kDS1AttrMCXSettings, "mcx_settings" },
{ kDS1AttrPrintServiceInfoText, "PrintServiceInfoText" },
{ kDS1AttrPrintServiceInfoXML, "PrintServiceInfoXML" },
{ kDS1AttrPrintServiceUserData, "appleprintservice" },
{ kDS1AttrVFSType, "vfstype" },
{ kDS1AttrVFSPassNo, "passno" },
{ kDS1AttrVFSDumpFreq, "dump_freq" },
{ kDS1AttrVFSLinkDir, "dir" },
{ kDSNAttrVFSOpts, "opts" },
{ kDS1AttrAliasData, "alias_data" },
{ kDSNAttrPhoneNumber, "phonenumber" },
{ kDS1AttrCapabilities, "capabilities" },
{ kDSNAttrProtocols, "protocols" },
{ kDSNAttrMember, "users" },
{ kDS1AttrComment, "comment" },
{ kDS1AttrAdminStatus, "AdminStatus" },
{ kDS1AttrAdminLimits, "admin_limits" },
{ kDS1AttrPwdAgingPolicy, "PwdAgingPolicy" },
{ kDS1AttrChange, "change" },
{ kDS1AttrExpire, "expire" },
{ kDSNAttrGroup, "groups" },
{ kDS1AttrFirstName, "firstname" },
{ kDS1AttrMiddleName, "middlename" },
{ kDS1AttrLastName, "lastname" },
{ kDSNAttrAreaCode , "areacode" },
{ kDSNAttrAddressLine1, "address1" },
{ kDSNAttrAddressLine2, "address2" },
{ kDSNAttrAddressLine3, "address3" },
{ kDSNAttrCity, "city" },
{ kDSNAttrState, "state" },
{ kDSNAttrPostalCode, "zip" },
{ kDSNAttrOrganizationName, "orgname" },
{ kDS1AttrSetupOccupation, "occupation" },
{ kDS1AttrSetupLocation, "location" },
{ kDS1AttrSetupAdvertising, "spam" },
{ kDS1AttrSetupAutoRegister, "autoregister" },
{ kDS1AttrPresetUserIsAdmin, "preset_user_is_admin" },
{ kDS1AttrPasswordServerLocation, "passwordserverlocation" },
{ kDSNAttrBootParams, "bootparams" },
{ kDSNAttrNetGroups, "netgroups" },
{ kDSNAttrRecordAlias, "RecordAlias" }
};
static const unsigned long kBuffSize = 2048;
typedef struct
{
int gDSRunState;
time_t gSeconds;
tDirReference gDirRef;
tDirNodeReference gNodeRef;
int gTimeToLive;
dynainfo *dyna;
} agent_private;
static int
doWeUseDS()
{
struct stat statResult;
if (stat("/Library/Preferences/DirectoryService/.DSRunningSP3", &statResult) == 0)
{
return 1;
}
return 0;
}
static int
canWeWork(agent_private *ap)
{
tDirStatus status = eDSNoErr;
tDataList *pDataList = NULL;
tDataBuffer *pDataBuff = NULL;
tDirReference dirRef = 0;
tDirNodeReference nodeRef = 0;
unsigned long count = 0;
time_t timenow = 0;
timenow = time(NULL);
if (ap->gSeconds > timenow)
{
if (!ap->gDSRunState)
{
return 0;
}
}
else
{
if (doWeUseDS() == 0)
{
ap->gDSRunState = 0;
ap->gSeconds = timenow + 30;
return 0;
}
}
ap->gDSRunState = 1;
ap->gSeconds = timenow + 30;
if ((ap->gDirRef == 0) || (dsVerifyDirRefNum(ap->gDirRef) != eDSNoErr))
{
ap->gDirRef = 0;
ap->gNodeRef = 0;
status = dsOpenDirService(&dirRef);
if (status != eDSNoErr)
{
system_log(LOG_DEBUG, "-- DS: %d: *** Error *** %s: %s() error = %d.", __LINE__ , "cww", "dsOpenDirService failed", status);
ap->gDSRunState = 0;
return 0;
}
else
{
ap->gDirRef = dirRef;
}
}
if (ap->gNodeRef == 0)
{
pDataBuff = dsDataBufferAllocate(ap->gDirRef, 512);
if (pDataBuff == NULL)
{
dsCloseDirService(ap->gDirRef);
ap->gDirRef = 0;
ap->gDSRunState = 0;
return 0;
}
status = dsFindDirNodes(ap->gDirRef, pDataBuff, NULL, eDSSearchNodeName, &count, NULL);
if ((status != eDSNoErr) || (count != 1))
{
system_log(LOG_DEBUG, "-- DS: %d: *** Error *** %s: %s() error = %d.", __LINE__, "cww", "dsFindDirNodes can't get search node", status);
dsDataBufferDeAllocate(ap->gDirRef, pDataBuff);
dsCloseDirService(ap->gDirRef);
ap->gDirRef = 0;
ap->gDSRunState = 0;
return 0;
}
pDataList = dsDataListAllocate(ap->gDirRef);
if (pDataList == NULL)
{
dsDataBufferDeAllocate(ap->gDirRef, pDataBuff);
dsCloseDirService(ap->gDirRef);
ap->gDirRef = 0;
ap->gDSRunState = 0;
return 0;
}
status = dsGetDirNodeName(ap->gDirRef, pDataBuff, 1, &pDataList);
if (status != eDSNoErr)
{
dsDataBufferDeAllocate(ap->gDirRef, pDataBuff);
dsDataListDeallocate(ap->gDirRef, pDataList);
free(pDataList);
dsCloseDirService(ap->gDirRef);
ap->gDirRef = 0;
ap->gDSRunState = 0;
return 0;
}
status = dsOpenDirNode(ap->gDirRef, pDataList, &nodeRef);
dsDataListDeallocate(ap->gDirRef, pDataList);
free(pDataList);
if (status == eDSNoErr)
{
system_log(LOG_DEBUG, "-- DS: %d: %s: %s.", __LINE__, "cww", "Search node opened");
ap->gNodeRef = nodeRef;
}
else
{
dsDataBufferDeAllocate(ap->gDirRef, pDataBuff);
dsCloseDirService(ap->gDirRef);
ap->gDirRef = 0;
ap->gDSRunState = 0;
return 0;
}
dsDataBufferDeAllocate(ap->gDirRef, pDataBuff);
}
system_log(LOG_DEBUG, "-- DS: %d: %s: ap= %lu, gDirRef= %lu, gNodeRef= %lu.", __LINE__, "canWeWork", (unsigned long)ap, ap->gDirRef, ap->gNodeRef);
ap->gDSRunState = 1;
return 1;
}
static char *
mapDSAttrToNetInfoType(const char *inAttrType)
{
int i = 0;
char *outResult = NULL;
unsigned long uiStdLen = strlen(kDSStdAttrTypePrefix);
if (inAttrType == NULL) return NULL;
if (strncmp(inAttrType, kDSStdAttrTypePrefix, uiStdLen) == 0)
{
for (i = 0; i < kAttrConsts; i++)
{
if (strcmp(inAttrType, sAttrMap[i][0]) == 0)
{
outResult = (char *)malloc(strlen(sAttrMap[i][1]) + 1);
strcpy(outResult, sAttrMap[i][1]);
break;
}
}
}
return outResult;
}
static char *
mapNetInfoAttrToDSType(const char *inAttrType)
{
int i = 0;
char *outResult = NULL;
if (inAttrType == NULL) return NULL;
for (i = 0; i < kAttrConsts; i++)
{
if (strcmp(inAttrType, sAttrMap[i][1]) == 0)
{
outResult = (char *)malloc(strlen(sAttrMap[i][0]) + 1);
strcpy(outResult, sAttrMap[i][0]);
break;
}
}
return outResult;
}
static char *
mapNetInfoRecToDSType(LUCategory inCategory)
{
switch (inCategory)
{
case LUCategoryUser: return(kDSStdRecordTypeUsers);
case LUCategoryGroup: return(kDSStdRecordTypeGroups);
case LUCategoryHost: return NULL;
case LUCategoryNetwork: return NULL;
case LUCategoryService: return NULL;
case LUCategoryProtocol: return NULL;
case LUCategoryRpc: return NULL;
case LUCategoryMount: return(kDSStdRecordTypeMounts);
case LUCategoryPrinter: return NULL;
case LUCategoryBootparam: return NULL;
case LUCategoryBootp: return NULL;
case LUCategoryAlias: return NULL;
case LUCategoryNetDomain: return NULL;
case LUCategoryEthernet: return NULL;
case LUCategoryNetgroup: return NULL;
case LUCategoryInitgroups: return NULL;
case LUCategoryHostServices: return NULL;
default:
system_log(LOG_DEBUG, "-- DS: %d: %s: *** Warning *** Unmapped category: %d", __LINE__, "mapNetInfoRecToDSType", inCategory);
return NULL;
}
return NULL;
}
static dsrecord *
dsrecordFromDS(agent_private *ap, tDataBuffer *buf, int which)
{
dsrecord *item;
dsdata *d;
dsattribute *a;
tDirStatus status;
tAttributeListRef attrListRef;
tRecordEntry *pRecEntry;
tAttributeEntry *pAttrEntry;
tAttributeValueListRef valueRef;
tAttributeValueEntry *pValueEntry;
char *pNIKey;
int i, j;
attrListRef = 0;
pRecEntry = NULL;
status = dsGetRecordEntry(ap->gNodeRef, buf, which, &attrListRef, &pRecEntry);
if (status != eDSNoErr) return NULL;
item = dsrecord_new();
for (i = 1; i <= pRecEntry->fRecordAttributeCount; i++)
{
pAttrEntry = NULL;
valueRef = 0;
status = dsGetAttributeEntry(ap->gNodeRef, buf, attrListRef, i, &valueRef, &pAttrEntry);
if (status != eDSNoErr) continue;
pNIKey = mapDSAttrToNetInfoType(pAttrEntry->fAttributeSignature.fBufferData);
if (pNIKey == NULL)
{
dsCloseAttributeValueList(valueRef);
dsDeallocAttributeEntry(ap->gDirRef, pAttrEntry);
continue;
}
d = cstring_to_dsdata(pNIKey);
a = dsattribute_new(d);
dsdata_release(d);
free(pNIKey);
dsrecord_append_attribute(item, a, SELECT_ATTRIBUTE);
for (j = 1; j <= pAttrEntry->fAttributeValueCount; j++)
{
pValueEntry = NULL;
status = dsGetAttributeValue(ap->gNodeRef, buf, j, valueRef, &pValueEntry);
if (status != eDSNoErr) continue;
d = cstring_to_dsdata(pValueEntry->fAttributeValueData.fBufferData);
dsattribute_append(a, d);
dsDeallocAttributeValueEntry(ap->gDirRef, pValueEntry);
}
dsattribute_release(a);
dsCloseAttributeValueList(valueRef);
dsDeallocAttributeEntry(ap->gDirRef, pAttrEntry);
}
dsCloseAttributeList(attrListRef);
dsDeallocRecordEntry(ap->gDirRef, pRecEntry);
return item;
}
static void
add_validation(dsrecord *r, int ttl)
{
dsdata *d;
dsattribute *a;
time_t best_before;
char str[32];
if (r == NULL) return;
d = cstring_to_dsdata("_lookup_validation");
dsrecord_remove_key(r, d, SELECT_ATTRIBUTE);
a = dsattribute_new(d);
dsrecord_append_attribute(r, a, SELECT_ATTRIBUTE);
dsdata_release(d);
best_before = time(0) + ttl;
sprintf(str, "%lu", best_before);
d = cstring_to_dsdata(str);
dsattribute_append(a, d);
dsdata_release(d);
dsattribute_release(a);
}
u_int32_t
DS_query(void *c, dsrecord *pattern, dsrecord **list)
{
agent_private *ap;
u_int32_t cat;
dsattribute *a;
dsdata *k;
dsrecord *lastrec;
dsrecord *item = NULL;
int match;
tDirStatus status;
char *pDSRecType, *catname;
int i, idx;
unsigned long ulRecCount = 0;
tDataList *pRecName = NULL, *pRecType = NULL, *pAttrType = NULL;
tContextData pContext = NULL;
tDataBuffer *pDataBuffer;
int searchOnName = 0;
tDataNode *pAttrSearchType = NULL, *pAttrSearchValue = NULL;
if (c == NULL) return 1;
if (pattern == NULL) return 1;
if (list == NULL) return 1;
ap = (agent_private *)c;
*list = NULL;
lastrec = NULL;
k = cstring_to_dsdata(CATEGORY_KEY);
a = dsrecord_attribute(pattern, k, SELECT_META_ATTRIBUTE);
dsdata_release(k);
if (a == NULL) return 1;
if (a->count == 0) return 1;
dsrecord_remove_attribute(pattern, a, SELECT_META_ATTRIBUTE);
catname = dsdata_to_cstring(a->value[0]);
if (catname == NULL)
{
dsattribute_release(a);
return 1;
}
cat = atoi(catname);
dsattribute_release(a);
pDSRecType = mapNetInfoRecToDSType(cat);
if (pDSRecType == NULL) return 1;
if (canWeWork(ap) == 0) return 1;
k = cstring_to_dsdata(SINGLE_KEY);
a = dsrecord_attribute(pattern, k, SELECT_META_ATTRIBUTE);
dsdata_release(k);
if (a != NULL)
{
dsrecord_remove_attribute(pattern, a, SELECT_META_ATTRIBUTE);
dsattribute_release(a);
ulRecCount = 1;
}
if (pattern->count == 0)
searchOnName = 1;
for (i = 0; i < pattern->count; i++)
{
a = pattern->attribute[i];
if (a != NULL)
{
if (a->key != NULL)
{
if (a->key->data != NULL)
{
system_log(LOG_DEBUG, "-- DS: %d: %s: ap= %lu search on attr type = %s.", __LINE__, "DS_query", (unsigned long)ap, a->key->data);
if (strcmp(a->key->data,"name") == 0)
{
if (a->value != NULL)
{
searchOnName = 1;
pRecName = dsDataListAllocate(ap->gDirRef);
if (a->count > 0)
{
idx = 0;
if (dsdata_to_cstring(a->value[idx]) != NULL)
{
dsAppendStringToListAlloc(ap->gDirRef, pRecName, a->value[idx]->data);
system_log(LOG_DEBUG, "-- DS: %d: %s: ap= %lu search on attr type value = %s.", __LINE__, "DS_query", (unsigned long)ap, a->value[idx]->data);
}
}
}
break;
}
}
}
}
}
if (searchOnName == 0)
{
for (i = 0; i < pattern->count; i++)
{
a = pattern->attribute[i];
if (a != NULL)
{
if (a->key != NULL)
{
if (a->key->data != NULL)
{
if (a->value != NULL)
{
if (a->count > 0)
{
idx = 0;
if (dsdata_to_cstring(a->value[idx]) != NULL)
{
system_log(LOG_DEBUG, "-- DS: %d: %s: ap= %lu search on attr type value = %s of type = %s.", __LINE__, "DS_query", (unsigned long)ap, a->value[idx]->data, a->key->data);
pAttrSearchValue = dsDataNodeAllocateString(ap->gDirRef, a->value[idx]->data);
if (pAttrSearchValue == NULL)
{
return 1;
}
}
}
pAttrSearchType = dsDataNodeAllocateString(ap->gDirRef, mapNetInfoAttrToDSType(a->key->data));
if (pAttrSearchType == NULL)
{
if (pAttrSearchValue == NULL)
{
dsDataNodeDeAllocate(ap->gDirRef, pAttrSearchValue);
}
return 1;
}
break;
}
}
}
}
}
}
if (pRecName != NULL)
{
if (dsDataListGetNodeCount(pRecName) == 0)
{
pRecName = dsBuildListFromStrings(ap->gDirRef, kDSRecordsAll, NULL);
}
}
else if (searchOnName == 1)
{
if (pattern->count == 0)
{
pRecName = dsBuildListFromStrings(ap->gDirRef, kDSRecordsAll, NULL);
}
else
{
}
}
if (searchOnName == 1)
{
pAttrType = dsBuildListFromStrings(ap->gDirRef, kDSAttributesStandardAll, NULL);
if (pAttrType == NULL)
{
dsDataListDeallocate(ap->gDirRef, pRecName);
free(pRecName);
return 1;
}
}
pRecType = dsBuildListFromStrings(ap->gDirRef, pDSRecType, NULL);
if (pDSRecType != NULL)
{
pDSRecType = NULL;
}
if (pRecType == NULL)
{
dsDataListDeallocate(ap->gDirRef, pAttrType);
free(pAttrType);
dsDataListDeallocate(ap->gDirRef, pRecName);
free(pRecName);
return 1;
}
pDataBuffer = dsDataBufferAllocate(ap->gDirRef, kBuffSize);
if (pDataBuffer == NULL)
{
dsDataListDeallocate(ap->gDirRef, pAttrType);
free(pAttrType);
dsDataListDeallocate(ap->gDirRef, pRecName);
free(pRecName);
dsDataListDeallocate(ap->gDirRef, pRecType);
free(pRecType);
return 1;
}
do
{
do
{
if (searchOnName == 1)
{
system_log(LOG_DEBUG, "-- DS: %d: %s: ap= %lu dsGetRecordList.", __LINE__, "DS_query", (unsigned long)ap);
status = dsGetRecordList(ap->gNodeRef, pDataBuffer, pRecName, eDSExact, pRecType, pAttrType, 0, &ulRecCount, &pContext);
}
else
{
system_log(LOG_DEBUG, "-- DS: %d: %s: ap= %lu dsDoAttributeValueSearch.", __LINE__, "DS_query", (unsigned long)ap);
status = dsDoAttributeValueSearch(ap->gNodeRef, pDataBuffer, pRecType, pAttrSearchType, eDSExact, pAttrSearchValue, &ulRecCount, &pContext);
}
if (status == eDSBufferTooSmall)
{
unsigned long bufSize = pDataBuffer->fBufferSize;
dsDataBufferDeAllocate(ap->gDirRef, pDataBuffer);
pDataBuffer = NULL;
pDataBuffer = dsDataBufferAllocate(ap->gDirRef, bufSize * 2);
}
} while (status == eDSBufferTooSmall);
if (status == eDSInvalidNodeRef) ap->gNodeRef = 0;
if ((status == eDSNoErr) && (ulRecCount != 0))
{
for (i = 1; i <= ulRecCount; i++)
{
item = dsrecordFromDS(ap, pDataBuffer, i);
if (item != NULL)
{
match = dsrecord_match(item, pattern);
if (match == 1)
{
add_validation(item, ap->gTimeToLive);
if (*list == NULL) *list = dsrecord_retain(item);
else lastrec->next = dsrecord_retain(item);
lastrec = item;
}
dsrecord_release(item);
}
}
}
} while ((status == eDSNoErr) && (pContext != NULL));
if (pRecType != NULL)
{
dsDataListDeallocate(ap->gDirRef, pRecType);
free(pRecType);
}
if (pAttrType != NULL)
{
dsDataListDeallocate(ap->gDirRef, pAttrType);
free(pAttrType);
}
if (pRecName != NULL)
{
dsDataListDeallocate(ap->gDirRef, pRecName);
free(pRecName);
}
if (pDataBuffer != NULL)
{
dsDataBufferDeAllocate(ap->gDirRef, pDataBuffer);
pDataBuffer = NULL;
}
if (pAttrSearchType != NULL)
{
dsDataNodeDeAllocate(ap->gDirRef, pAttrSearchType);
pAttrSearchType = NULL;
}
if (pAttrSearchValue != NULL)
{
dsDataNodeDeAllocate(ap->gDirRef, pAttrSearchValue);
pAttrSearchValue = NULL;
}
return 0;
}
u_int32_t
DS_new(void **c, char *args, dynainfo *d)
{
agent_private *ap;
dsrecord *r;
dsattribute *a;
dsdata *x;
int status, didSetTTL;
if (c == NULL) return 1;
ap = (agent_private *)malloc(sizeof(agent_private));
*c = ap;
ap->gDSRunState = 0;
ap->gSeconds = 0;
ap->gDirRef = 0;
ap->gNodeRef = 0;
ap->dyna = d;
system_log(LOG_DEBUG, "Allocated DS 0x%08x\n", (int)ap);
ap->gTimeToLive = DefaultTimeToLive;
r = NULL;
didSetTTL = 0;
if (ap->dyna != NULL)
{
if (ap->dyna->dyna_config_agent != NULL)
{
status = (ap->dyna->dyna_config_agent)(ap->dyna, -1, &r);
if (status == 0)
{
x = cstring_to_dsdata("TimeToLive");
a = dsrecord_attribute(r, x, SELECT_ATTRIBUTE);
dsdata_release(x);
if (a != NULL)
{
x = dsattribute_value(a, 0);
if (x != NULL)
{
ap->gTimeToLive = atoi(dsdata_to_cstring(x));
dsdata_release(x);
didSetTTL = 1;
}
dsattribute_release(a);
}
dsrecord_release(r);
}
}
if ((didSetTTL == 0) && (ap->dyna->dyna_config_global != NULL))
{
status = (ap->dyna->dyna_config_global)(ap->dyna, -1, &r);
if (status == 0)
{
x = cstring_to_dsdata("TimeToLive");
a = dsrecord_attribute(r, x, SELECT_ATTRIBUTE);
dsdata_release(x);
if (a != NULL)
{
x = dsattribute_value(a, 0);
if (x != NULL)
{
ap->gTimeToLive = atoi(dsdata_to_cstring(x));
dsdata_release(x);
didSetTTL = 1;
}
dsattribute_release(a);
}
dsrecord_release(r);
}
}
}
return 0;
}
u_int32_t
DS_free(void *c)
{
agent_private *ap;
if (c == NULL) return 0;
ap = (agent_private *)c;
system_log(LOG_DEBUG, "Deallocated DS 0x%08x\n", (int)ap);
dsCloseDirNode(ap->gNodeRef);
dsCloseDirService(ap->gDirRef);
free(ap);
c = NULL;
return 0;
}
u_int32_t
DS_validate(void *c, char *v)
{
u_int32_t t;
if (v == NULL) return 0;
t = atoi(v);
if (time(0) > t) return 0;
return 1;
}