windows-nt/Source/XPSP1/NT/ds/security/services/scerpc/server/dsgroups.cpp
2020-09-26 16:20:57 +08:00

2047 lines
54 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
dsgroups.cpp
Abstract:
Routines to configure/analyze groups in DS
Author:
Jin Huang (jinhuang) 7-Nov-1996
--*/
#include "headers.h"
#include "serverp.h"
#include <io.h>
#include <lm.h>
#include <lmcons.h>
#include <lmapibuf.h>
#pragma hdrstop
//
// LDAP handle
//
PLDAP Thread pGrpLDAP = NULL;
HANDLE Thread hDS = NULL;
HINSTANCE Thread hNtdsApi = NULL;
#define SCEGRP_MEMBERS 1
#define SCEGRP_MEMBERSHIP 2
#if _WIN32_WINNT>=0x0500
typedef DWORD (WINAPI *PFNDSBIND) (TCHAR *, TCHAR *, HANDLE *);
typedef DWORD (WINAPI *PFNDSUNBIND) (HANDLE *);
typedef DWORD (WINAPI *PFNDSCRACKNAMES) ( HANDLE, DS_NAME_FLAGS, DS_NAME_FORMAT, \
DS_NAME_FORMAT, DWORD, LPTSTR *, PDS_NAME_RESULT *);
typedef void (WINAPI *PFNDSFREENAMERESULT) (DS_NAME_RESULT *);
DWORD
ScepDsConfigGroupMembers(
IN PSCE_OBJECT_LIST pRoots,
IN PWSTR GroupName,
IN OUT DWORD *pStatus,
IN PSCE_NAME_LIST pMembers,
IN PSCE_NAME_LIST pMemberOf,
IN OUT DWORD *nGroupCount
);
DWORD
ScepDsGetDsNameList(
IN PSCE_NAME_LIST pNameList,
OUT PSCE_NAME_LIST *pRealNames
);
DWORD
ScepDsCompareNames(
IN PWSTR *Values,
IN OUT PSCE_NAME_LIST *pAddList,
OUT PSCE_NAME_LIST *pDeleteList OPTIONAL
);
DWORD
ScepDsChangeMembers(
IN ULONG Flag,
IN PWSTR RealGroupName,
IN PSCE_NAME_LIST pAddList OPTIONAL,
IN PSCE_NAME_LIST pDeleteList OPTIONAL
);
DWORD
ScepDsAnalyzeGroupMembers(
IN LSA_HANDLE LsaPolicy,
IN PSCE_OBJECT_LIST pRoots,
IN PWSTR GroupName,
IN PWSTR KeyName,
IN DWORD KeyLen,
IN OUT DWORD *pStatus,
IN PSCE_NAME_LIST pMembers,
IN PSCE_NAME_LIST pMemberOf,
IN OUT DWORD *nGroupCount
);
DWORD
ScepDsMembersDifferent(
IN ULONG Flag,
IN PWSTR *Values,
IN OUT PSCE_NAME_LIST *pNameList,
OUT PSCE_NAME_LIST *pCurrentList,
OUT PBOOL pbDifferent
);
PWSTR
ScepGetLocalAdminsName();
DWORD
ScepDsConvertDsNameList(
IN OUT PSCE_NAME_LIST pDsNameList
);
//
// helpers
//
SCESTATUS
ScepCrackOpen(
OUT HANDLE *phDS
)
{
if ( !phDS ) {
return(SCESTATUS_INVALID_PARAMETER);
}
DWORD Win32rc;
*phDS = NULL;
hNtdsApi = LoadLibrary(TEXT("ntdsapi.dll"));
if ( hNtdsApi == NULL ) {
return (SCESTATUS_MOD_NOT_FOUND);
}
PFNDSBIND pfnDsBind;
PFNDSUNBIND pfnDsUnBind;
#if defined(UNICODE)
pfnDsBind = (PFNDSBIND)GetProcAddress(hNtdsApi, "DsBindW");
pfnDsUnBind = (PFNDSUNBIND)GetProcAddress(hNtdsApi, "DsUnBindW");
#else
pfnDsBind = (PFNDSBIND)GetProcAddress(hNtdsApi, "DsBindA");
pfnDsUnBind = (PFNDSUNBIND)GetProcAddress(hNtdsApi, "DsUnBindA");
#endif
if ( pfnDsBind == NULL || pfnDsUnBind == NULL ) {
return(SCESTATUS_MOD_NOT_FOUND);
}
Win32rc = (*pfnDsBind) (
NULL,
NULL,
phDS);
if ( Win32rc != ERROR_SUCCESS ) {
ScepLogOutput3(3, Win32rc, IDS_ERROR_BIND, L"the GC");
}
return(ScepDosErrorToSceStatus(Win32rc));
}
SCESTATUS
ScepCrackClose(
IN HANDLE *phDS
)
{
if ( hNtdsApi ) {
if ( phDS ) {
PFNDSUNBIND pfnDsUnBind;
#if defined(UNICODE)
pfnDsUnBind = (PFNDSUNBIND)GetProcAddress(hNtdsApi, "DsUnBindW");
#else
pfnDsUnBind = (PFNDSUNBIND)GetProcAddress(hNtdsApi, "DsUnBindA");
#endif
if ( pfnDsUnBind ) {
(*pfnDsUnBind) (phDS);
}
}
FreeLibrary(hNtdsApi);
hNtdsApi = NULL;
}
return(SCESTATUS_SUCCESS);
}
#endif
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// Functions to configure group membership in DS
//
//
//
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
SCESTATUS
ScepConfigDsGroups(
IN OUT PSCE_GROUP_MEMBERSHIP pGroupMembership,
IN DWORD ConfigOptions
)
/* ++
Routine Description:
Configure the ds group membership. The main difference of ds groups
from NT4 groups is that now group can be a member of another group.
Members in the group are configured exactly as the pMembers list in
the restricted group. The group is only validated (added) as a member
of the MemberOf group list. Other existing members in those groups
won't be removed.
The restricted groups are specified in the SCP profile by group name.
It could be a global group, or a alias (no difference in NT5 DS),
but must be defined in the local domain.
Arguments:
pGroupMembership - The restricted group list with members/memberof info to configure
ConfigOptions - options passed in for the configuration
Return value:
SCESTATUS error codes
++ */
{
#if _WIN32_WINNT<0x0500
return(SCESTATUS_SUCCESS);
#else
if ( pGroupMembership == NULL ) {
ScepPostProgress(TICKS_GROUPS,
AREA_GROUP_MEMBERSHIP,
NULL);
return(SCESTATUS_SUCCESS);
}
SCESTATUS rc;
//
// open the Ldap server, should open two ldap server, one for the local domain
// the other is for the global search (for members, membership)
//
rc = ScepLdapOpen(&pGrpLDAP);
if ( rc == SCESTATUS_SUCCESS ) {
rc = ScepCrackOpen(&hDS);
}
if ( rc == SCESTATUS_SUCCESS ) {
//
// get the root of the domain
//
PSCE_OBJECT_LIST pRoots=NULL;
rc = ScepEnumerateDsObjectRoots(
pGrpLDAP,
&pRoots
);
if ( rc == SCESTATUS_SUCCESS ) {
PSCE_GROUP_MEMBERSHIP pGroup;
DWORD Win32rc;
DWORD rc32=NO_ERROR; // the saved status
BOOL bAdminFound=FALSE;
DWORD nGroupCount=0;
//
// configure each group
//
for ( pGroup=pGroupMembership; pGroup != NULL; pGroup=pGroup->Next ) {
//
// if both members and memberof are not defined for the group
// we don't need to do anything for the group
//
if ( ( pGroup->Status & SCE_GROUP_STATUS_NC_MEMBERS ) &&
( pGroup->Status & SCE_GROUP_STATUS_NC_MEMBEROF ) ) {
continue;
}
//
// if within policy propagation and a system shutdown
// is requested, we need to quit as soon as possible
//
if ( (ConfigOptions & SCE_POLICY_TEMPLATE) &&
ScepIsSystemShutDown() ) {
rc = SCESTATUS_SERVICE_NOT_SUPPORT;
break;
}
LPTSTR pTemp = wcschr(pGroup->GroupName, L'\\');
if ( pTemp ) {
//
// there is a domain name, check it with computer name
// to determine if the account is local
//
UNICODE_STRING uName;
uName.Buffer = pGroup->GroupName;
uName.Length = ((USHORT)(pTemp-pGroup->GroupName))*sizeof(TCHAR);
if ( !ScepIsDomainLocal(&uName) ) {
//
// non local groups are not supported for the configuration
//
ScepLogOutput3(1, 0, SCEDLL_NO_MAPPINGS, pGroup->GroupName);
rc = SCESTATUS_INVALID_DATA;
pGroup->Status |= SCE_GROUP_STATUS_DONE_IN_DS;
continue;
}
pTemp++;
} else {
pTemp = pGroup->GroupName;
}
//
// local groups will be handled outside (in SAM)
// find the group (validate) in this domain
//
Win32rc = ScepDsConfigGroupMembers(
pRoots,
pTemp, // pGroup->GroupName,
&(pGroup->Status),
pGroup->pMembers,
pGroup->pMemberOf,
&nGroupCount
);
if ( Win32rc != ERROR_SUCCESS &&
(pGroup->Status & SCE_GROUP_STATUS_DONE_IN_DS) ) {
//
// the group should be handled by the DS function
// but it failed.
//
ScepLogOutput3(1,Win32rc, SCEDLL_SCP_ERROR_CONFIGURE, pGroup->GroupName);
rc32 = Win32rc;
if ( Win32rc == ERROR_FILE_NOT_FOUND ||
Win32rc == ERROR_SHARING_VIOLATION ||
Win32rc == ERROR_ACCESS_DENIED ) {
Win32rc = ERROR_SUCCESS;
} else
break;
}
}
if ( rc32 != NO_ERROR ) {
rc = ScepDosErrorToSceStatus(rc32);
}
//
// free the root DN buffer
//
ScepFreeObjectList(pRoots);
}
}
if ( pGrpLDAP ) {
ScepLdapClose(&pGrpLDAP);
pGrpLDAP = NULL;
}
ScepCrackClose(&hDS);
hDS = NULL;
//
// ticks will be called within ConfigureGroupMembership, so ignore it here
//
return(rc);
#endif
}
#if _WIN32_WINNT>=0x0500
DWORD
ScepDsConfigGroupMembers(
IN PSCE_OBJECT_LIST pRoots,
IN PWSTR GroupName,
IN OUT DWORD *pStatus,
IN PSCE_NAME_LIST pMembers,
IN PSCE_NAME_LIST pMemberOf,
IN OUT DWORD *nGroupCount
)
/*
Description:
Configure group membership (members and Memberof) of a group, specified by
GroupName.
The group membership is configured using ldap based on info stored in DS.
Since foreign wellknown principals may not be present in the Active
Directory, this function cannot configure membership with well known
principals.
Global groups and Universal groups cannot have wellknown pricipals as
members (or memberof) but local groups (such as builtin groups) can. In
order to solve this problem, local groups are configured using the old
SAM apis outside of this function. This function only configures global
and Universal group defined in the local domain. If the group is a
global or universal group, the pStatus parameter will be marked to
indicate the group is processed by this function (SCE_GROUP_STATUS_DONE_IN_DS)
so the old SAM function can skip it.
Arguments:
pRoots - contains the local domain's base DN
GroupName - the group name to configure
pStatus - the status of the group (such as member defined, memberof defined, etc)
pMembers - the list of members to configure
pMemberOf - the list of memberOf to configure
nGroupCount - the count maintained for progress indication only. If the group is
processed in this function, the count will be incremented.
Return:
WIN32 error code.
*/
{
if ( GroupName == NULL ) {
return(ERROR_SUCCESS);
}
if ( pRoots == NULL ) {
return(ERROR_INVALID_PARAMETER);
}
DWORD retErr = ERROR_SUCCESS;
DWORD retSave = ERROR_SUCCESS;
//
// search for the group name, if find it, get members and memberof attributes
//
LDAPMessage *Message = NULL;
PWSTR Attribs[4];
Attribs[0] = L"distinguishedName";
Attribs[1] = L"member";
Attribs[2] = L"memberOf";
Attribs[3] = NULL;
WCHAR tmpBuf[128];
//
// define a filter for global or universal group only
//
wcscpy(tmpBuf, L"( &(&(|");
swprintf(tmpBuf+wcslen(L"( &(&(|"), L"(groupType=%d)(groupType=%d))(objectClass=group))(samAccountName=\0",
GROUP_TYPE_ACCOUNT_GROUP | GROUP_TYPE_SECURITY_ENABLED, GROUP_TYPE_UNIVERSAL_GROUP | GROUP_TYPE_SECURITY_ENABLED);
PWSTR Filter;
Filter = (PWSTR)LocalAlloc(LMEM_ZEROINIT,
(wcslen(tmpBuf)+wcslen(GroupName)+4)*sizeof(WCHAR));
if ( Filter == NULL ) {
return(SCESTATUS_NOT_ENOUGH_RESOURCE);
}
swprintf(Filter, L"%s%s) )", tmpBuf, GroupName);
//
// no chased referrel search because the group must be defined locally
// on the domain
//
pGrpLDAP->ld_options = 0;
retErr = ldap_search_s(
pGrpLDAP,
pRoots->Name,
LDAP_SCOPE_SUBTREE,
Filter,
Attribs,
0,
&Message);
retErr = LdapMapErrorToWin32(retErr);
if(retErr == ERROR_SUCCESS) {
LDAPMessage *Entry = NULL;
//
// find the group, should have only one entry, unless there are duplicate
// groups within the domain, in which case, we only care the first entry anyway
//
Entry = ldap_first_entry(pGrpLDAP, Message);
if(Entry != NULL) {
//
// get the values of requested attributes
// Note, Value pointer returned must be freed
//
PWSTR *Values;
PWSTR RealGroupName;
//
// the DN name
//
Values = ldap_get_values(pGrpLDAP, Entry, Attribs[0]);
if(Values != NULL) {
ScepLogOutput3(1,0, SCEDLL_SCP_CONFIGURE, GroupName);
if ( *nGroupCount < TICKS_GROUPS ) {
ScepPostProgress(1,
AREA_GROUP_MEMBERSHIP,
GroupName);
*nGroupCount++;
}
//
// Save the real group name for add/remove members later.
//
RealGroupName = (PWSTR)LocalAlloc(0,(wcslen(Values[0]) + 1)*sizeof(WCHAR));
if ( RealGroupName != NULL ) {
wcscpy(RealGroupName, Values[0]);
ldap_value_free(Values);
ScepLogOutput3(3, 0, SCEDLL_SCP_CONFIGURE, RealGroupName);
PSCE_NAME_LIST pRealNames=NULL;
PSCE_NAME_LIST pDeleteNames=NULL;
//
// translate each name in the pMembers list to real ds names (search)
//
if ( !( *pStatus & SCE_GROUP_STATUS_NC_MEMBERS) ) {
retErr = ScepDsGetDsNameList(pMembers, &pRealNames);
retSave = retErr;
//
// continue to configure group membership even if
// there are some members not resolved
//
// BUT if no member is resolved, do not proceed to remove
// all members
//
if ( retErr == ERROR_SUCCESS ||
(retErr == ERROR_FILE_NOT_FOUND && pRealNames) ) {
//
// get members attribute
//
Values = ldap_get_values(pGrpLDAP, Entry, Attribs[1]);
if ( Values != NULL ) {
//
// process each member
//
retErr = ScepDsCompareNames(Values, &pRealNames, &pDeleteNames);
ldap_value_free(Values);
} else {
//
// it is OK if no members are found
//
ScepLogOutput3(3, 0, SCEDLL_EMPTY_MEMBERSHIP);
retErr = ERROR_SUCCESS;
}
if ( NO_ERROR == retErr ) {
//
// add/remove members of the group
//
retErr = ScepDsChangeMembers(SCEGRP_MEMBERS,
RealGroupName,
pRealNames,
pDeleteNames);
}
if ( ERROR_SUCCESS == retSave ) {
retSave = retErr;
}
}
//
// free buffers
//
ScepFreeNameList(pRealNames);
ScepFreeNameList(pDeleteNames);
pRealNames = NULL;
pDeleteNames = NULL;
}
if ( !( *pStatus & SCE_GROUP_STATUS_NC_MEMBEROF) ) {
//
// memberof is also defined for the group
// crack the memberof list first
//
retErr = ScepDsGetDsNameList(pMemberOf, &pRealNames);
if ( ERROR_SUCCESS == retSave ) {
retSave = retErr;
}
if ( ( ERROR_SUCCESS == retErr ||
ERROR_FILE_NOT_FOUND == retErr ) && pRealNames ) {
//
// get memberof attribute of the group
//
Values = ldap_get_values(pGrpLDAP, Entry, Attribs[2]);
if ( Values != NULL ) {
//
// process each membership
//
retErr = ScepDsCompareNames(Values, &pRealNames, NULL);
ldap_value_free(Values);
} else {
//
// it is OK if no membership is defined
//
ScepLogOutput3(3, 0, SCEDLL_EMPTY_MEMBERSHIP);
retErr = NO_ERROR;
}
if ( retErr == NO_ERROR ) {
//
// add the group to the defined membership
// Note, other existing membership is not removed
//
retErr = ScepDsChangeMembers(SCEGRP_MEMBERSHIP,
RealGroupName,
pRealNames,
NULL);
}
ScepFreeNameList(pRealNames);
pRealNames = NULL;
//
// remember the error
//
if ( ERROR_SUCCESS == retSave ) {
retSave = retErr;
}
}
}
LocalFree(RealGroupName);
} else {
ldap_value_free(Values);
retErr = ERROR_NOT_ENOUGH_MEMORY;
}
//
// regardless success or failure, this group has been
// processed by this function. Mark it so that it will
// be skipped by the old SAM API
//
*pStatus |= SCE_GROUP_STATUS_DONE_IN_DS;
} else {
//
// Value[0] (group name) can not be empty
//
retErr = LdapMapErrorToWin32(pGrpLDAP->ld_errno);
ScepLogOutput3(3,retErr, SCEDLL_CANNOT_FIND, GroupName);
}
} else {
//
// the group is not found
//
retErr = ERROR_FILE_NOT_FOUND;
ScepLogOutput3(3,retErr, SCEDLL_CANNOT_FIND, GroupName);
}
} else {
//
// error finding the group (with the filter defined)
//
ScepLogOutput3(3,retErr, SCEDLL_CANNOT_FIND, Filter);
}
//
// free Filter
//
if ( Message )
ldap_msgfree(Message);
LocalFree(Filter);
//
// return the error
//
if ( ERROR_SUCCESS == retSave ) {
retSave = retErr;
}
return(retSave);
}
DWORD
ScepDsGetDsNameList(
IN PSCE_NAME_LIST pNameList,
OUT PSCE_NAME_LIST *pRealNames
)
/*
Description:
Translate account names in the list to FQDN format (CN=<account>,DC=<domain>,...).
The output list pRealNames can be filled up even if the function returns
error, to handle valid accounts while there are invalid accounts defined in
the list.
Arguments:
pNameList - the link list for accounts in name format to convert
pRealNames - the output link list for converted FQDN format accounts
Return:
WIN32 error code.
If ERROR_FILE_NOT_FOUND is returned, it means that some accounts in the
input list cannot be cracked.
*/
{
if ( pNameList == NULL ) {
return(ERROR_SUCCESS);
}
if ( pRealNames == NULL ) {
return(ERROR_INVALID_PARAMETER);
}
//
// find the procedure address of DsCrackNames and DsFreeNameResult
// ntdsapi.dll is dynamically loaded in ScepCrackOpen
//
PFNDSCRACKNAMES pfnDsCrackNames=NULL;
PFNDSFREENAMERESULT pfnDsFreeNameResult=NULL;
if ( hNtdsApi ) {
#if defined(UNICODE)
pfnDsCrackNames = (PFNDSCRACKNAMES)GetProcAddress(hNtdsApi, "DsCrackNamesW");
pfnDsFreeNameResult = (PFNDSFREENAMERESULT)GetProcAddress(hNtdsApi, "DsFreeNameResultW");
#else
pfnDsCrackNames = (PFNDSCRACKNAMES)GetProcAddress(hNtdsApi, "DsCrackNamesA");
pfnDsFreeNameResult = (PFNDSFREENAMERESULT)GetProcAddress(hNtdsApi, "DsFreeNameResultA");
#endif
}
//
// the two entry points must exist before continue
//
if ( pfnDsCrackNames == NULL || pfnDsFreeNameResult == NULL ) {
return(ERROR_PROC_NOT_FOUND);
}
DWORD retErr=ERROR_SUCCESS;
DWORD retSave=ERROR_SUCCESS;
PWSTR pTemp;
DS_NAME_RESULT *pDsResult=NULL;
//
// loop through each name in the list to crack
//
for ( PSCE_NAME_LIST pName = pNameList; pName != NULL; pName = pName->Next ) {
//
// Crack the name from NT4 account name to FQDN. Note, hDS is bound to
// the GC in order to crack foreign domain accounts
//
retErr = (*pfnDsCrackNames) (
hDS, // in
DS_NAME_FLAG_TRUST_REFERRAL, // in
DS_NT4_ACCOUNT_NAME,// in
DS_FQDN_1779_NAME, // in
1, // in
&(pName->Name), // in
&pDsResult); // out
if(retErr == ERROR_SUCCESS && pDsResult &&
pDsResult->cItems > 0 && pDsResult->rItems ) {
if ( pDsResult->rItems[0].pName ) {
//
// find the member
// Save the real group name for add/remove members later.
//
ScepLogOutput3(3,0, SCEDLL_PROCESS, pDsResult->rItems[0].pName);
retErr = ScepAddToNameList(pRealNames, pDsResult->rItems[0].pName, 0);
} else {
//
// this name cannot be cracked.
//
retErr = pDsResult->rItems[0].status;
ScepLogOutput3(1,retErr, SCEDLL_CANNOT_FIND_INDS, pName->Name);
}
} else {
//
// no match is found
//
retErr = ERROR_FILE_NOT_FOUND;
ScepLogOutput3(1,retErr, SCEDLL_CANNOT_FIND_INDS, pName->Name);
}
if ( pDsResult ) {
(*pfnDsFreeNameResult) (pDsResult);
pDsResult = NULL;
}
//
// remember the error to return
//
if ( ERROR_SUCCESS != retErr )
retSave = retErr;
}
return(retSave);
}
DWORD
ScepDsCompareNames(
IN PWSTR *Values,
IN OUT PSCE_NAME_LIST *pAddList,
OUT PSCE_NAME_LIST *pDeleteList OPTIONAL
)
/*
Description:
Arguments:
Values
pAddList
pDeleteList
Return Value:
WIN32 error
*/
{
if ( Values == NULL || pAddList == NULL ) {
return(ERROR_INVALID_PARAMETER);
}
//
// count how many existing members (memberof)
//
ULONG ValCount = ldap_count_values(Values);
DWORD rc=NO_ERROR;
PSCE_NAME_LIST pTemp;
//
// loop through each existing value to compare with the ones defined
// for configuration to determine which one should be added and which
// one should be removed from the membership
//
for(ULONG index = 0; index < ValCount; index++) {
if ( Values[index] == NULL ) {
continue;
}
pTemp = *pAddList;
PSCE_NAME_LIST pParent = NULL, pTemp2;
BOOL bFound=FALSE;
while (pTemp != NULL ) {
if ( _wcsicmp(Values[index], pTemp->Name) == 0 ) {
//
// find this member in both place, no need to add or remove
// from the membership so take this one out of the list
//
if ( pParent == NULL ) {
*pAddList = pTemp->Next;
} else
pParent->Next = pTemp->Next;
pTemp2 = pTemp;
pTemp = pTemp->Next;
pTemp2->Next = NULL;
ScepFreeNameList(pTemp2);
bFound=TRUE;
break;
} else {
//
// move to the next one
//
pParent = pTemp;
pTemp = pTemp->Next;
}
}
if ( !bFound && pDeleteList != NULL ) {
//
// did not find in the real name list, should be deleted
// if the remove buffer is passed in
//
rc = ScepAddToNameList(pDeleteList, Values[index], 0);
if ( rc != ERROR_SUCCESS ) {
ScepLogOutput3(1,rc, SCEDLL_SCP_ERROR_ADD, Values[index]);
}
}
if ( rc != NO_ERROR ) {
//
// pDeleteList will be freed outside
//
break;
}
}
return(rc);
}
DWORD
ScepDsChangeMembers(
IN ULONG Flag,
IN PWSTR RealGroupName,
IN PSCE_NAME_LIST pAddList OPTIONAL,
IN PSCE_NAME_LIST pDeleteList OPTIONAL
)
{
if ( RealGroupName == NULL ) {
return(ERROR_INVALID_PARAMETER);
}
if ( pAddList == NULL && pDeleteList == NULL ) {
//
// nothing to do
//
return(ERROR_SUCCESS);
}
PLDAP pSrhLDAP = NULL;
SCESTATUS rc = ScepLdapOpen(&pSrhLDAP);
if ( rc != SCESTATUS_SUCCESS ) {
return(ScepSceStatusToDosError(rc));
}
PLDAPMod rgMods[2];
LDAPMod Mod;
DWORD retErr=NO_ERROR;
DWORD retSave=NO_ERROR;
PWSTR rgpszVals[2];
PSCE_NAME_LIST pName;
rgMods[0] = &Mod;
rgMods[1] = NULL;
rgpszVals[1] = NULL;
//
// need to do one at a time because individual members/memberof may fail
//
Mod.mod_op = LDAP_MOD_ADD;
Mod.mod_values = rgpszVals;
if ( Flag == SCEGRP_MEMBERS )
Mod.mod_type = L"member";
else
Mod.mod_type = L"memberOf";
for ( pName=pAddList; pName != NULL; pName = pName->Next ) {
ScepLogOutput3(2,0, SCEDLL_SCP_ADD, pName->Name);
rgpszVals[0] = pName->Name;
//
// Now, we'll do the write for members...
//
retErr = ldap_modify_s(pSrhLDAP,
RealGroupName,
rgMods
);
retErr = LdapMapErrorToWin32(retErr);
//
// if the same member already exist, do not consider it as an error
//
if ( retErr == ERROR_ALREADY_EXISTS )
retErr = ERROR_SUCCESS;
if ( retErr != ERROR_SUCCESS ) {
ScepLogOutput3(1,retErr, SCEDLL_SCP_ERROR_ADDTO, RealGroupName);
retSave = retErr;
}
}
if ( Flag == SCEGRP_MEMBERS && pDeleteList ) {
//
// remove existing members. Note, memberof won't be removed
//
if ( NO_ERROR == retSave ) {
//
// only remove existing members if all members are added successfully
//
Mod.mod_op = LDAP_MOD_DELETE;
Mod.mod_type = L"member";
Mod.mod_values = rgpszVals;
for ( pName=pDeleteList; pName != NULL; pName = pName->Next ) {
ScepLogOutput3(2,0, SCEDLL_SCP_REMOVE, pName->Name);
rgpszVals[0] = pName->Name;
//
// Now, we'll do the write for members...
//
retErr = ldap_modify_s(pSrhLDAP,
RealGroupName,
rgMods
);
retErr = LdapMapErrorToWin32(retErr);
//
// if the member doesn't exist in the group, ignore
//
if ( retErr == ERROR_FILE_NOT_FOUND ) {
retErr = ERROR_SUCCESS;
}
if ( retErr != ERROR_SUCCESS) {
ScepLogOutput3(1,retErr, SCEDLL_SCP_ERROR_REMOVE, RealGroupName);
retSave = retErr;
}
}
} else {
//
// something is wrong when adding new members
// so existing members won't be removed
//
ScepLogOutput3(1,retSave, SCEDLL_SCP_ERROR_NOREMOVE);
}
}
if ( pSrhLDAP ) {
ScepLdapClose(&pSrhLDAP);
pSrhLDAP = NULL;
}
return(retSave);
}
#endif
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// Functions to analyze group membership in DS
//
//
//
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
SCESTATUS
ScepAnalyzeDsGroups(
IN PSCE_GROUP_MEMBERSHIP pGroupMembership
)
/* ++
Routine Description:
Analyze the ds group membership. The main difference of ds groups
from NT4 groups is that now group can be a member of another group.
Members in the group are configured exactly as the pMembers list in
the restricted group. The group is only validated (added) as a member
of the MemberOf group list. Other existing members in those groups
won't be removed.
The restricted groups are specified in the SCP profile by group name.
It could be a global group, or a alias (no difference in NT5 DS),
but must be defined in the local domain.
Arguments:
pGroupMembership - The restricted group list with members/memberof info to configure
Return value:
SCESTATUS error codes
++ */
{
#if _WIN32_WINNT<0x0500
return(SCESTATUS_SUCCESS);
#else
if ( pGroupMembership == NULL ) {
ScepPostProgress(TICKS_GROUPS,
AREA_GROUP_MEMBERSHIP,
NULL);
return(SCESTATUS_SUCCESS);
}
SCESTATUS rc;
DWORD nGroupCount=0;
PSCE_GROUP_MEMBERSHIP pGroup=pGroupMembership;
PWSTR KeyName=NULL;
DWORD GroupLen;
//
// open local policy
//
LSA_HANDLE PolicyHandle=NULL;
rc = RtlNtStatusToDosError(
ScepOpenLsaPolicy(
POLICY_LOOKUP_NAMES,
&PolicyHandle,
TRUE
));
if (ERROR_SUCCESS != rc ) {
ScepLogOutput3(1, rc, SCEDLL_LSA_POLICY);
return(ScepDosErrorToSceStatus(rc));
}
//
// open the Ldap server, should open two ldap server, one for the local domain only
// the other is for the global search (for members, membership)
//
rc = ScepLdapOpen(&pGrpLDAP);
if ( rc == SCESTATUS_SUCCESS ) {
ScepCrackOpen(&hDS);
}
if ( rc == SCESTATUS_SUCCESS ) {
//
// get the root of the domain
//
PSCE_OBJECT_LIST pRoots=NULL;
rc = ScepEnumerateDsObjectRoots(
pGrpLDAP,
&pRoots
);
if ( rc == SCESTATUS_SUCCESS ) {
//
// configure each group
//
DWORD Win32rc;
DWORD rc32=NO_ERROR; // saved status
BOOL bAdminFound=FALSE;
//
// get the local administratos group name
//
for ( pGroup=pGroupMembership; pGroup != NULL; pGroup=pGroup->Next ) {
if ( KeyName ) {
LocalFree(KeyName);
KeyName = NULL;
}
LPTSTR pTemp = wcschr(pGroup->GroupName, L'\\');
if ( pTemp ) {
//
// there is a domain name, check it with computer name
//
UNICODE_STRING uName;
uName.Buffer = pGroup->GroupName;
uName.Length = ((USHORT)(pTemp-pGroup->GroupName))*sizeof(TCHAR);
if ( !ScepIsDomainLocal(&uName) ) {
ScepLogOutput3(1, 0, SCEDLL_NO_MAPPINGS, pGroup->GroupName);
rc = SCESTATUS_INVALID_DATA;
ScepRaiseErrorString(
NULL,
KeyName ? KeyName : pGroup->GroupName,
szMembers
);
pGroup->Status |= SCE_GROUP_STATUS_DONE_IN_DS;
continue;
}
ScepConvertNameToSidString(
PolicyHandle,
pGroup->GroupName,
FALSE,
&KeyName,
&GroupLen
);
pTemp++;
} else {
pTemp = pGroup->GroupName;
GroupLen = wcslen(pTemp);
}
//
// find the group (validate) in this domain
//
Win32rc = ScepDsAnalyzeGroupMembers(
PolicyHandle,
pRoots,
pTemp, // pGroup->GroupName,
KeyName ? KeyName : pGroup->GroupName,
GroupLen,
&(pGroup->Status),
pGroup->pMembers,
pGroup->pMemberOf,
&nGroupCount
);
if ( (Win32rc != ERROR_SUCCESS) &&
(pGroup->Status & SCE_GROUP_STATUS_DONE_IN_DS) ) {
ScepLogOutput3(1, Win32rc, SCEDLL_SAP_ERROR_ANALYZE, pGroup->GroupName);
rc32 = Win32rc;
if ( Win32rc == ERROR_FILE_NOT_FOUND ||
Win32rc == ERROR_SHARING_VIOLATION ||
Win32rc == ERROR_ACCESS_DENIED ) {
ScepRaiseErrorString(
NULL,
KeyName ? KeyName : pGroup->GroupName,
szMembers
);
Win32rc = ERROR_SUCCESS;
} else
break;
}
}
if ( rc32 != NO_ERROR ) {
rc = ScepDosErrorToSceStatus(rc32);
}
//
// free pRoots
//
ScepFreeObjectList(pRoots);
}
}
if ( KeyName ) {
LocalFree(KeyName);
}
if ( pGrpLDAP ) {
ScepLdapClose(&pGrpLDAP);
pGrpLDAP = NULL;
}
ScepCrackClose(&hDS);
hDS = NULL;
/*
// this will be handled in the analysis into SAM
//
// raise groups that are errored
//
for ( PSCE_GROUP_MEMBERSHIP pTmpGrp=pGroup;
pTmpGrp != NULL; pTmpGrp = pTmpGrp->Next ) {
if ( pTmpGrp->GroupName == NULL )
continue;
if ( wcschr(pGroup->GroupName, L'\\') ) {
ScepConvertNameToSidString(
PolicyHandle,
pGroup->GroupName,
FALSE,
&KeyName,
&GroupLen
);
}
ScepRaiseErrorString(
NULL,
KeyName ? KeyName : pTmpGrp->GroupName,
szMembers
);
if ( KeyName ) {
LocalFree(KeyName);
KeyName = NULL;
}
}
if ( rc != SCESTATUS_SERVICE_NOT_SUPPORT &&
nGroupCount < TICKS_GROUPS ) {
ScepPostProgress(TICKS_GROUPS-nGroupCount,
AREA_GROUP_MEMBERSHIP,
NULL);
}
*/
if ( PolicyHandle ) {
LsaClose(PolicyHandle);
}
return(rc);
#endif
}
#if _WIN32_WINNT>=0x0500
DWORD
ScepDsAnalyzeGroupMembers(
IN LSA_HANDLE LsaPolicy,
IN PSCE_OBJECT_LIST pRoots,
IN PWSTR GroupName,
IN PWSTR KeyName,
IN DWORD KeyLen,
IN OUT DWORD *pStatus,
IN PSCE_NAME_LIST pMembers,
IN PSCE_NAME_LIST pMemberOf,
IN DWORD *nGroupCount
)
{
if ( GroupName == NULL ) {
return(ERROR_SUCCESS);
}
if ( pRoots == NULL ) {
return(ERROR_INVALID_PARAMETER);
}
DWORD retErr=ERROR_SUCCESS;
//
// search for the name, if find it, get members and memberof
//
LDAPMessage *Message = NULL;
PWSTR Attribs[4];
Attribs[0] = L"distinguishedName";
Attribs[1] = L"member";
Attribs[2] = L"memberOf";
Attribs[3] = NULL;
WCHAR tmpBuf[128];
// wcscpy(tmpBuf, L"( &(|(objectClass=localGroup)(objectClass=group))(cn=");
// wcscpy(tmpBuf, L"( &(|(objectClass=localGroup)(objectClass=group))(samAccountName=");
wcscpy(tmpBuf, L"( &(&(|");
swprintf(tmpBuf+wcslen(L"( &(&(|"), L"(groupType=%d)(groupType=%d))(objectClass=group))(samAccountName=\0",
GROUP_TYPE_ACCOUNT_GROUP | GROUP_TYPE_SECURITY_ENABLED, GROUP_TYPE_UNIVERSAL_GROUP | GROUP_TYPE_SECURITY_ENABLED);
PWSTR Filter;
DWORD Len=wcslen(GroupName);
Filter = (PWSTR)LocalAlloc(LMEM_ZEROINIT, (wcslen(tmpBuf)+Len+4)*sizeof(WCHAR));
if ( Filter == NULL ) {
return(SCESTATUS_NOT_ENOUGH_RESOURCE);
}
swprintf(Filter, L"%s%s) )", tmpBuf, GroupName);
pGrpLDAP->ld_options = 0; // no chased referrel
retErr = ldap_search_s(
pGrpLDAP,
pRoots->Name,
LDAP_SCOPE_SUBTREE,
Filter,
Attribs,
0,
&Message);
retErr = LdapMapErrorToWin32(retErr);
if(retErr == ERROR_SUCCESS) {
//
// find the group
//
LDAPMessage *Entry = NULL;
//
// should only have one entry, unless there are duplicate groups
// within the domain, in which case, we only care the first entry anyway
//
//
// get the first one.
//
Entry = ldap_first_entry(pGrpLDAP, Message);
if(Entry != NULL) {
PWSTR *Values;
Values = ldap_get_values(pGrpLDAP, Entry, Attribs[0]);
if(Values != NULL) {
ScepLogOutput3(1,0, SCEDLL_SAP_ANALYZE, GroupName);
if ( *nGroupCount < TICKS_GROUPS ) {
ScepPostProgress(1,
AREA_GROUP_MEMBERSHIP,
GroupName);
*nGroupCount++;
}
ScepLogOutput2(3,0, L"\t\t%s", Values[0]);
ldap_value_free(Values);
PSCE_NAME_LIST pRealNames=NULL;
PSCE_NAME_LIST pCurrentList=NULL;
BOOL bDifferent;
DWORD retErr2, rc;
//
// translate each name in the pMembers list to real ds names (search)
//
retErr = ScepDsGetDsNameList(pMembers, &pRealNames);
if ( ERROR_SUCCESS == retErr ||
ERROR_FILE_NOT_FOUND == retErr ) {
//
// analyze members
//
Values = ldap_get_values(pGrpLDAP, Entry, Attribs[1]);
rc = ScepDsMembersDifferent(SCEGRP_MEMBERS,
Values,
&pRealNames,
&pCurrentList,
&bDifferent);
if ( Values != NULL )
ldap_value_free(Values);
//
// if there are some names unresolvable, this should be
// treated as mismatch
//
if ( ERROR_FILE_NOT_FOUND == retErr )
bDifferent = TRUE;
retErr = rc;
if ( ( ERROR_SUCCESS == retErr ) &&
( bDifferent ||
(*pStatus & SCE_GROUP_STATUS_NC_MEMBERS) ) ) {
//
// save to the database
//
retErr = ScepDsConvertDsNameList(pCurrentList);
if ( retErr == NO_ERROR ) {
retErr = ScepSaveMemberMembershipList(
LsaPolicy,
szMembers,
KeyName,
KeyLen,
pCurrentList,
(*pStatus & SCE_GROUP_STATUS_NC_MEMBERS) ? 2: 1);
}
if ( retErr != ERROR_SUCCESS ) {
ScepLogOutput3(1,retErr, SCEDLL_SAP_ERROR_SAVE, GroupName);
}
}
ScepFreeNameList(pCurrentList);
pCurrentList = NULL;
ScepFreeNameList(pRealNames);
pRealNames = NULL;
}
retErr2 = ScepDsGetDsNameList(pMemberOf, &pRealNames);
if ( ( ERROR_SUCCESS == retErr2 ||
ERROR_FILE_NOT_FOUND == retErr2 ) && pRealNames ) {
//
// analyze membership
//
Values = ldap_get_values(pGrpLDAP, Entry, Attribs[2]);
rc = ScepDsMembersDifferent(SCEGRP_MEMBERSHIP,
Values,
&pRealNames,
&pCurrentList,
&bDifferent);
if ( Values != NULL )
ldap_value_free(Values);
//
// if there are some names unresolvable, this should be
// treated as mismatch
//
if ( ERROR_FILE_NOT_FOUND == retErr )
bDifferent = TRUE;
retErr2 = rc;
if ( (retErr2 == NO_ERROR) &&
( bDifferent ||
(*pStatus & SCE_GROUP_STATUS_NC_MEMBEROF) ) ) {
//
// save to the database
//
retErr2 = ScepDsConvertDsNameList(pCurrentList);
if ( retErr2 == NO_ERROR ) {
retErr2 = ScepSaveMemberMembershipList(
LsaPolicy,
szMemberof,
KeyName,
KeyLen,
pCurrentList,
(*pStatus & SCE_GROUP_STATUS_NC_MEMBEROF) ? 2 : 1);
}
if ( retErr2 != ERROR_SUCCESS ) {
ScepLogOutput3(1,retErr2, SCEDLL_SAP_ERROR_SAVE, GroupName);
}
}
ScepFreeNameList(pCurrentList);
pCurrentList = NULL;
ScepFreeNameList(pRealNames);
pRealNames = NULL;
}
*pStatus |= SCE_GROUP_STATUS_DONE_IN_DS;
//
// remember the error
//
if ( retErr == NO_ERROR ) {
retErr = retErr2;
}
} else {
//
// Value[0] (group name) may not be empty
//
retErr = LdapMapErrorToWin32(pGrpLDAP->ld_errno);
ScepLogOutput3(3,retErr, SCEDLL_CANNOT_FIND, GroupName);
}
} else {
retErr = ERROR_FILE_NOT_FOUND; // the group is not found
ScepLogOutput3(3,retErr, SCEDLL_CANNOT_FIND, GroupName);
}
} else {
ScepLogOutput3(3,retErr, SCEDLL_CANNOT_FIND, Filter);
}
if ( Message )
ldap_msgfree(Message);
//
// free Filter
//
LocalFree(Filter);
return(retErr);
}
DWORD
ScepDsMembersDifferent(
IN ULONG Flag,
IN PWSTR *Values,
IN OUT PSCE_NAME_LIST *pNameList,
OUT PSCE_NAME_LIST *pCurrentList,
OUT PBOOL pbDifferent
)
{
if ( pCurrentList == NULL || pbDifferent == NULL ) {
return(ERROR_INVALID_PARAMETER);
}
if ( Values == NULL ) {
if ( pNameList == NULL || *pNameList == NULL )
*pbDifferent = FALSE;
else
*pbDifferent = TRUE;
return(ERROR_SUCCESS);
}
ULONG ValCount = ldap_count_values(Values);
DWORD rc=NO_ERROR;
*pbDifferent = FALSE;
for(ULONG index = 0; index < ValCount; index++) {
if ( Values[index] == NULL ) {
continue;
}
if ( !(*pbDifferent) ) {
PSCE_NAME_LIST pTemp = *pNameList, pTemp2;
PSCE_NAME_LIST pParent = NULL;
INT i;
while ( pTemp != NULL ) {
if ( (i = _wcsicmp(Values[index], pTemp->Name)) == 0 ) {
//
// find this member
//
if ( pParent == NULL ) {
*pNameList = pTemp->Next;
} else
pParent->Next = pTemp->Next;
pTemp2 = pTemp;
pTemp = pTemp->Next;
pTemp2->Next = NULL;
ScepFreeNameList(pTemp2);
break;
} else {
pParent = pTemp;
pTemp = pTemp->Next;
}
}
if ( pTemp == NULL && i != 0 )
*pbDifferent = TRUE;
}
//
// build the current list
//
rc = ScepAddToNameList(pCurrentList, Values[index], 0);
if ( rc != NO_ERROR ) {
ScepLogOutput3(1,rc, SCEDLL_SCP_ERROR_ADD, Values[index]);
break;
}
}
if ( rc == NO_ERROR && Flag == SCEGRP_MEMBERS &&
*pbDifferent == FALSE ) {
//
// still same so far, only continue to compare for members
// because membership is not one to one configuring
//
if ( *pNameList != NULL )
*pbDifferent = TRUE;
} // pCurrentList will be freed outside
return(rc);
}
PWSTR
ScepGetLocalAdminsName()
{
NTSTATUS NtStatus;
SAM_HANDLE AccountDomain=NULL;
SAM_HANDLE AliasHandle=NULL;
SAM_HANDLE ServerHandle=NULL;
PSID DomainSid=NULL;
SAM_HANDLE theBuiltinHandle=NULL;
PSID theBuiltinSid=NULL;
ALIAS_NAME_INFORMATION *BufName=NULL;
PWSTR pAdminsName=NULL;
//
// open the sam account domain
//
NtStatus = ScepOpenSamDomain(
SAM_SERVER_ALL_ACCESS,
MAXIMUM_ALLOWED,
&ServerHandle,
&AccountDomain,
&DomainSid,
&theBuiltinHandle,
&theBuiltinSid
);
if ( !NT_SUCCESS(NtStatus) ) {
ScepLogOutput3(1, RtlNtStatusToDosError(NtStatus),
SCEDLL_ERROR_OPEN, L"SAM");
return(NULL);
}
NtStatus = SamOpenAlias(
theBuiltinHandle,
MAXIMUM_ALLOWED,
DOMAIN_ALIAS_RID_ADMINS,
&AliasHandle
);
if ( NT_SUCCESS( NtStatus ) ) {
NtStatus = SamQueryInformationAlias(
AliasHandle,
AliasNameInformation,
(PVOID *)&BufName
);
if ( NT_SUCCESS( NtStatus ) && BufName &&
BufName->Name.Length > 0 && BufName->Name.Buffer ) {
//
// allocate buffer to return
//
pAdminsName = (PWSTR)ScepAlloc(0, BufName->Name.Length+2);
if ( pAdminsName ) {
wcsncpy(pAdminsName, BufName->Name.Buffer,
BufName->Name.Length/2);
pAdminsName[BufName->Name.Length/2] = L'\0';
} else {
NtStatus = STATUS_NO_MEMORY;
}
}
if ( BufName ) {
SamFreeMemory(BufName);
BufName = NULL;
}
//
// close the user handle
//
SamCloseHandle(AliasHandle);
}
SamCloseHandle(AccountDomain);
SamCloseHandle( ServerHandle );
if ( DomainSid != NULL )
SamFreeMemory(DomainSid);
SamCloseHandle( theBuiltinHandle );
if ( theBuiltinSid != NULL )
SamFreeMemory(theBuiltinSid);
return pAdminsName;
}
DWORD
ScepDsConvertDsNameList(
IN OUT PSCE_NAME_LIST pDsNameList
)
/*
Routine:
The input list is in the LDAP format (CN=<>,...DC=<>, ...). When the routine
returns, the list will be in NT4 account name format (domain\account)
*/
{
if ( pDsNameList == NULL ) {
return(ERROR_SUCCESS);
}
PFNDSCRACKNAMES pfnDsCrackNames=NULL;
PFNDSFREENAMERESULT pfnDsFreeNameResult=NULL;
if ( hNtdsApi ) {
#if defined(UNICODE)
pfnDsCrackNames = (PFNDSCRACKNAMES)GetProcAddress(hNtdsApi, "DsCrackNamesW");
pfnDsFreeNameResult = (PFNDSFREENAMERESULT)GetProcAddress(hNtdsApi, "DsFreeNameResultW");
#else
pfnDsCrackNames = (PFNDSCRACKNAMES)GetProcAddress(hNtdsApi, "DsCrackNamesA");
pfnDsFreeNameResult = (PFNDSFREENAMERESULT)GetProcAddress(hNtdsApi, "DsFreeNameResultA");
#endif
}
if ( pfnDsCrackNames == NULL || pfnDsFreeNameResult == NULL ) {
return(ERROR_PROC_NOT_FOUND);
}
DWORD retErr=ERROR_SUCCESS;
PWSTR pTemp;
DS_NAME_RESULT *pDsResult=NULL;
DWORD DomLen;
DWORD SidLen;
CHAR SidBuf[MAX_PATH];
PWSTR RefDom[MAX_PATH];
SID_NAME_USE SidUse;
for ( PSCE_NAME_LIST pName = pDsNameList; pName != NULL; pName = pName->Next ) {
if ( pName->Name == NULL ) {
continue;
}
retErr = (*pfnDsCrackNames) (
hDS, // in
DS_NAME_NO_FLAGS, // in
DS_FQDN_1779_NAME, // in
DS_NT4_ACCOUNT_NAME,// in
1, // in
&(pName->Name), // in
&pDsResult); // out
if(retErr == ERROR_SUCCESS && pDsResult && pDsResult->rItems &&
pDsResult->rItems[0].pName ) {
//
// NT4 account name format is returned, should check if the
// domain is not a acccount domain
//
pTemp = wcschr(pDsResult->rItems[0].pName, L'\\');
if ( pTemp ) {
DomLen=MAX_PATH;
SidLen=MAX_PATH;
if ( LookupAccountName(
NULL,
pDsResult->rItems[0].pName,
(PSID)SidBuf,
&SidLen,
(PWSTR)RefDom,
&DomLen,
&SidUse
) ) {
if ( !ScepIsSidFromAccountDomain( (PSID)SidBuf) ) {
//
// add name only
//
pTemp++;
} else {
pTemp = pDsResult->rItems[0].pName;
}
} else {
pTemp = pDsResult->rItems[0].pName;
}
} else {
pTemp = pDsResult->rItems[0].pName;
}
PWSTR pNewName = (PWSTR)ScepAlloc(0, (wcslen(pTemp)+1)*sizeof(WCHAR));
if ( pNewName ) {
wcscpy(pNewName, pTemp);
ScepFree(pName->Name);
pName->Name = pNewName;
} else {
retErr = ERROR_NOT_ENOUGH_MEMORY;
}
} else {
// no match is found
retErr = ERROR_FILE_NOT_FOUND;
ScepLogOutput3(1,retErr, SCEDLL_CANNOT_FIND, pName->Name);
}
if ( pDsResult ) {
(*pfnDsFreeNameResult) (pDsResult);
pDsResult = NULL;
}
if ( retErr != ERROR_SUCCESS ) {
break;
}
}
return(retErr);
}
#endif