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

1912 lines
51 KiB
C++
Raw 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:
dsobject.cpp
Abstract:
Routines to configure/analyze security of DS objects
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>
#include <ntldap.h>
#pragma hdrstop
//#define SCEDS_DBG 1
//
// NT-Security-Descriptor attribute's LDAP name.
//
#define ACTRL_SD_PROP_NAME L"nTSecurityDescriptor"
//
// LDAP handle
//
PLDAP Thread pLDAP = NULL;
BOOL Thread StartDsCheck=FALSE;
DWORD
ScepConvertObjectTreeToLdap(
IN PSCE_OBJECT_TREE pObject
);
SCESTATUS
ScepConfigureDsObjectTree(
IN PSCE_OBJECT_TREE ThisNode
);
DWORD
ScepSetDsSecurityOverwrite(
PWSTR ObjectName,
PSECURITY_DESCRIPTOR pSecurityDescriptor OPTIONAL,
SECURITY_INFORMATION SeInfo,
PSCE_OBJECT_CHILD_LIST pNextLevel OPTIONAL
);
BOOL
ScepIsMatchingSchemaObject(
PWSTR Class,
PWSTR ClassDn
);
DWORD
ScepAnalyzeDsObjectTree(
IN PSCE_OBJECT_TREE ThisNode
);
DWORD
ScepAnalyzeDsObject(
IN PWSTR ObjectFullName,
IN PSECURITY_DESCRIPTOR ProfileSD,
IN SECURITY_INFORMATION ProfileSeInfo
);
DWORD
ScepAnalyzeDsObjectAndChildren(
IN PWSTR ObjectName,
IN BYTE Status,
IN SECURITY_INFORMATION SeInfo,
IN PSCE_OBJECT_CHILD_LIST pNextLevel
);
PSECURITY_DESCRIPTOR
ScepMakeNullSD();
DWORD
ScepChangeSecurityOnObject(
PWSTR ObjectName,
PSECURITY_DESCRIPTOR pSecurityDescriptor,
SECURITY_INFORMATION SeInfo
);
DWORD
ScepReadDsObjSecurity(
IN PWSTR pwszObject,
IN SECURITY_INFORMATION SeInfo,
OUT PSECURITY_DESCRIPTOR *ppSD
);
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// Functions to configure DS object security
//
//
//
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
SCESTATUS
ScepConfigureDsSecurity(
IN PSCE_OBJECT_TREE pObject
)
/* ++
Routine Description:
Configure the ds object security as specified in pObject tree.
This routine should only be executed on a domain controller.
Arguments:
pObject - The ds object tree. The objects in the tree are in
the format of Jet index (o=,dc=,...cn=), need to convert before
calls to ldap
Return value:
SCESTATUS error codes
++ */
{
SCESTATUS rc;
DWORD Win32rc;
//
// open the Ldap server
//
rc = ScepLdapOpen(NULL);
if ( rc == SCESTATUS_SUCCESS ) {
//
// process the tree node format to ldap format
//
Win32rc = ScepConvertObjectTreeToLdap(pObject);
if ( Win32rc == ERROR_SUCCESS ) {
//
// do not need bind because ConvertObjectTreeToLadp already does that
//
//
// configure the object tree
//
rc = ScepConfigureDsObjectTree(pObject);
} else {
ScepLogOutput3(1, Win32rc,
SCEDLL_ERROR_CONVERT_LDAP,
pObject->ObjectFullName);
rc = ScepDosErrorToSceStatus(Win32rc);
}
ScepLdapClose(NULL);
}
return(rc);
}
SCESTATUS
ScepConfigureDsObjectTree(
IN PSCE_OBJECT_TREE ThisNode
)
/* ++
Routine Description:
This routine set security information to each DS object in the tree. DS
objects are configured separately from file/registry objects because the
logic behind ds objects is different.
Arguments:
ThisNode - one node in the tree
Return value:
SCESTATUS
-- */
{
if ( ThisNode == NULL )
return(SCESTATUS_SUCCESS);
SCESTATUS rc=SCESTATUS_SUCCESS;
//
// if IGNORE is set, skip this node
//
if ( ThisNode->Status != SCE_STATUS_CHECK &&
ThisNode->Status != SCE_STATUS_OVERWRITE &&
ThisNode->Status != SCE_STATUS_NO_AUTO_INHERIT )
goto SkipNode;
if ( ThisNode->pSecurityDescriptor != NULL ) {
ScepLogOutput3(2, 0, SCEDLL_SCP_CONFIGURE, ThisNode->ObjectFullName);
//
// notify the progress bar if there is any
//
ScepPostProgress(1, AREA_DS_OBJECTS, ThisNode->ObjectFullName);
}
//
// Process this node first
//
if ( ThisNode->pSecurityDescriptor != NULL ||
ThisNode->Status == SCE_STATUS_OVERWRITE ) {
ScepLogOutput3(1, 0, SCEDLL_SCP_CONFIGURE, ThisNode->ObjectFullName);
DWORD Win32Rc;
//
// set security to the ds object and all children
// because the OVERWRITE flag.
//
if ( ThisNode->Status == SCE_STATUS_OVERWRITE ) {
//
// prepare for next level nodes
//
for ( PSCE_OBJECT_CHILD_LIST pTemp = ThisNode->ChildList;
pTemp != NULL;
pTemp = pTemp->Next ) {
if ( pTemp->Node->pSecurityDescriptor == NULL &&
pTemp->Node->Status != SCE_STATUS_IGNORE )
pTemp->Node->Status = SCE_STATUS_OVERWRITE;
}
//
// recursive set objects under the node, exclude nodes in the tree
//
Win32Rc = ScepSetDsSecurityOverwrite(
ThisNode->ObjectFullName,
ThisNode->pSecurityDescriptor,
ThisNode->SeInfo,
ThisNode->ChildList
);
} else {
Win32Rc = ScepChangeSecurityOnObject(
ThisNode->ObjectFullName,
ThisNode->pSecurityDescriptor,
ThisNode->SeInfo
);
}
//
// ignore the following error codes
//
if ( Win32Rc == ERROR_FILE_NOT_FOUND ||
Win32Rc == ERROR_PATH_NOT_FOUND ||
Win32Rc == ERROR_ACCESS_DENIED ||
Win32Rc == ERROR_SHARING_VIOLATION ||
Win32Rc == ERROR_INVALID_OWNER ||
Win32Rc == ERROR_INVALID_PRIMARY_GROUP) {
gWarningCode = Win32Rc;
rc = SCESTATUS_SUCCESS;
goto SkipNode;
}
if ( Win32Rc != ERROR_SUCCESS )
return(ScepDosErrorToSceStatus(Win32Rc));
}
//
// then process children
//
for ( PSCE_OBJECT_CHILD_LIST pTemp = ThisNode->ChildList;
pTemp != NULL;
pTemp = pTemp->Next ) {
if ( pTemp->Node == NULL ) continue;
rc = ScepConfigureDsObjectTree(
pTemp->Node
);
}
SkipNode:
return(rc);
}
DWORD
ScepSetDsSecurityOverwrite(
PWSTR ObjectName,
PSECURITY_DESCRIPTOR pSecurityDescriptor OPTIONAL,
SECURITY_INFORMATION SeInfo,
PSCE_OBJECT_CHILD_LIST pNextLevel OPTIONAL
)
{
DWORD retErr=ERROR_SUCCESS;
//
// set security on the object first
//
/*
retErr = ScepSetSecurityWin32(
ObjectName,
SeInfo,
pSecurityDescriptor,
SE_DS_OBJECT
);
*/
retErr = ScepChangeSecurityOnObject(
ObjectName,
pSecurityDescriptor,
SeInfo
);
if ( retErr == ERROR_SUCCESS ) {
//
// enumerate one level nodes under the current object
//
LDAPMessage *Message = NULL;
PWSTR Attribs[2];
WCHAR dn[] = L"distinguishedName";
Attribs[0] = dn;
Attribs[1] = NULL;
retErr = ldap_search_s( pLDAP,
ObjectName,
LDAP_SCOPE_ONELEVEL,
L"(objectClass=*)",
Attribs,
0,
&Message);
if( Message ) {
retErr = ERROR_SUCCESS;
LDAPMessage *Entry = NULL;
//
// How many entries ?
//
ULONG nChildren = ldap_count_entries(pLDAP, Message);
//
// get the first one.
//
Entry = ldap_first_entry(pLDAP, Message);
//
// now loop through the entries and recursively fix the
// security on the subtree.
//
PWSTR *Values;
PWSTR SubObjectName;
INT cmpFlag;
PSCE_OBJECT_CHILD_LIST pTemp;
PSECURITY_DESCRIPTOR pNullSD = ScepMakeNullSD();
for(ULONG i = 0; i<nChildren; i++) {
if(Entry != NULL) {
Values = ldap_get_values(pLDAP, Entry, Attribs[0]);
if(Values != NULL) {
//
// Save the sub object DN for recursion.
//
SubObjectName = (PWSTR)LocalAlloc(0,(wcslen(Values[0]) + 1)*sizeof(WCHAR));
if ( SubObjectName != NULL ) {
wcscpy(SubObjectName, Values[0]);
#ifdef SCEDS_DBG
printf("%ws\n", SubObjectName);
#endif
ldap_value_free(Values);
//
// check if the SubObjectName is in the object tree already
// SubObjectName should not contain extra spaces and comma is used as the delimiter
// if not, need a convert routine to handle it.
//
for ( pTemp = pNextLevel; pTemp != NULL; pTemp=pTemp->Next ) {
cmpFlag = _wcsicmp(pTemp->Node->ObjectFullName, SubObjectName);
if ( cmpFlag >= 0 )
break;
}
if ( pTemp == NULL || cmpFlag > 0 ) {
//
// did not find in the object tree, so resurse it
//
retErr = ScepSetDsSecurityOverwrite(
SubObjectName,
pNullSD,
(SeInfo & ( DACL_SECURITY_INFORMATION |
SACL_SECURITY_INFORMATION)),
NULL
);
} // else find it, skip the subnode
LocalFree(SubObjectName);
} else {
ldap_value_free(Values);
retErr = ERROR_NOT_ENOUGH_MEMORY;
}
} else {
retErr = LdapMapErrorToWin32(pLDAP->ld_errno);
}
} else {
retErr = LdapMapErrorToWin32(pLDAP->ld_errno);
}
if ( retErr != ERROR_SUCCESS ) {
break;
}
if ( i < nChildren-1 ) {
Entry = ldap_next_entry(pLDAP, Entry);
}
} // end for loop
//
// free the NULL security descriptor
//
if ( pNullSD ) {
ScepFree(pNullSD);
}
ldap_msgfree(Message);
}
}
return(retErr);
}
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// Functions to analyze DS object security
//
//
//
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
BOOL
ScepIsMatchingSchemaObject(
PWSTR Class,
PWSTR ClassDn
)
{
//
// Note: Class and ClassDn can't be NULL
//
ULONG len = lstrlen(Class);
ULONG i;
//
// if the first component is not CN=, then no point continuing
//
if(*ClassDn != L'C') return FALSE;
//
// we need to match the name exactly.
//
for(i=0;i<len;i++)
{
if(ClassDn[i+3] != Class[i]) return FALSE;
}
//
// things are good, but ensure that this is not just a prefix match!
//
if(ClassDn[i+3] == L',' || ClassDn[i+3] == L';')
return TRUE;
else
return FALSE;
}
DWORD
ScepAnalyzeDsSecurity(
IN PSCE_OBJECT_TREE pObject
)
/* ++
Routine Description:
Analyze the ds object security as specified in pObject tree.
This routine should only be executed on a domain controller.
Arguments:
pObject - The ds object tree
Return value:
SCESTATUS error codes
++ */
{
DWORD Win32rc;
//
// open the Ldap server
//
Win32rc = ScepSceStatusToDosError( ScepLdapOpen(NULL) );
if( Win32rc == ERROR_SUCCESS ) {
//
// process the tree node format to ldap format
//
Win32rc = ScepConvertObjectTreeToLdap(pObject);
if ( Win32rc == ERROR_SUCCESS ) {
//
// analyze all ds objects to the level that NOT_CONFIGURED
// status is raised for the node
// no matter if the node is specified in the tree
//
StartDsCheck=FALSE;
Win32rc = ScepAnalyzeDsObjectTree(pObject);
} else {
ScepLogOutput3(1, Win32rc,
SCEDLL_ERROR_CONVERT_LDAP, pObject->ObjectFullName);
}
ScepLdapClose(NULL);
}
return(Win32rc);
}
DWORD
ScepAnalyzeDsObjectTree(
IN PSCE_OBJECT_TREE ThisNode
)
/* ++
Routine Description:
This routine analyze security information of each DS object in the tree. DS
objects are analyzed separately from file/registry objects because the
logic behind ds objects is different.
Arguments:
ThisNode - one node in the tree
Return value:
Win32 error codes
-- */
{
if ( ThisNode == NULL )
return(ERROR_SUCCESS);
DWORD Win32Rc=ERROR_SUCCESS;
//
// if IGNORE is set, log a SAP and skip this node
//
if ( ThisNode->Status != SCE_STATUS_CHECK &&
ThisNode->Status != SCE_STATUS_OVERWRITE &&
ThisNode->Status != SCE_STATUS_NO_AUTO_INHERIT ) {
//
// Log a point in SAP
//
Win32Rc = ScepSaveDsStatusToSection(
ThisNode->ObjectFullName,
ThisNode->IsContainer,
SCE_STATUS_NOT_CONFIGURED,
NULL,
0
);
goto SkipNode;
}
if ( NULL != ThisNode->pSecurityDescriptor ) {
//
// notify the progress bar if there is any
//
ScepPostProgress(1, AREA_DS_OBJECTS, ThisNode->ObjectFullName);
StartDsCheck = TRUE;
ScepLogOutput3(1, 0, SCEDLL_SAP_ANALYZE, ThisNode->ObjectFullName);
//
// only analyze objects with explicit aces specified
//
Win32Rc = ScepAnalyzeDsObject(
ThisNode->ObjectFullName,
ThisNode->pSecurityDescriptor,
ThisNode->SeInfo
);
//
// if the object denies access, skip it.
//
if ( Win32Rc == ERROR_ACCESS_DENIED ||
Win32Rc == ERROR_SHARING_VIOLATION) {
//
// log a point in SAP for skipping
//
Win32Rc = ScepSaveDsStatusToSection(
ThisNode->ObjectFullName,
ThisNode->IsContainer,
SCE_STATUS_ERROR_NOT_AVAILABLE,
NULL,
0
);
if ( Win32Rc == ERROR_SUCCESS)
goto ProcChild;
}
//
// if the object specified in the profile does not exist, skip it and children
//
if ( Win32Rc == ERROR_FILE_NOT_FOUND ||
Win32Rc == ERROR_PATH_NOT_FOUND ) {
gWarningCode = Win32Rc;
Win32Rc = ERROR_SUCCESS;
goto SkipNode;
}
} else {
//
// log a point in SAP for not analyzing
//
Win32Rc = ScepSaveDsStatusToSection(
ThisNode->ObjectFullName,
ThisNode->IsContainer,
SCE_STATUS_CHILDREN_CONFIGURED,
NULL,
0
);
}
if ( Win32Rc != ERROR_SUCCESS )
return(Win32Rc);
//
// if the status is NO_AUTO_INHERIT then all children except specified are N.C.ed
// if status is overwrite, analyze everyone under
// if status is check (auto inherit), everyone except specified should be "good" so don't go down
//
if ( (StartDsCheck && ThisNode->Status != SCE_STATUS_CHECK) ||
(!StartDsCheck && NULL != ThisNode->ChildList ) ) {
if ( ThisNode->Status == SCE_STATUS_OVERWRITE ) {
//
// prepare for next level nodes
//
for ( PSCE_OBJECT_CHILD_LIST pTemp = ThisNode->ChildList;
pTemp != NULL;
pTemp = pTemp->Next ) {
if ( pTemp->Node->pSecurityDescriptor == NULL &&
pTemp->Node->Status != SCE_STATUS_IGNORE )
pTemp->Node->Status = SCE_STATUS_OVERWRITE;
}
}
//
// make a SD which represents a NULL DACL and SACL
//
Win32Rc = ScepAnalyzeDsObjectAndChildren(
ThisNode->ObjectFullName,
ThisNode->Status,
(ThisNode->SeInfo &
(DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION)),
ThisNode->ChildList
);
//
// ignore the following errors
//
if ( Win32Rc == ERROR_FILE_NOT_FOUND ||
Win32Rc == ERROR_PATH_NOT_FOUND ||
Win32Rc == ERROR_ACCESS_DENIED ||
Win32Rc == ERROR_SHARING_VIOLATION ||
Win32Rc == ERROR_INVALID_OWNER ||
Win32Rc == ERROR_INVALID_PRIMARY_GROUP) {
gWarningCode = Win32Rc;
Win32Rc = ERROR_SUCCESS;
}
if ( Win32Rc != ERROR_SUCCESS )
return(Win32Rc);
}
ProcChild:
//
// then process children
//
for (PSCE_OBJECT_CHILD_LIST pTemp = ThisNode->ChildList;
pTemp != NULL; pTemp = pTemp->Next ) {
Win32Rc = ScepAnalyzeDsObjectTree(
pTemp->Node
);
if ( Win32Rc != ERROR_SUCCESS ) {
break;
}
}
SkipNode:
return(Win32Rc);
}
DWORD
ScepAnalyzeDsObject(
IN PWSTR ObjectFullName,
IN PSECURITY_DESCRIPTOR ProfileSD,
IN SECURITY_INFORMATION ProfileSeInfo
)
/* ++
Routine Description:
Get security setting for the current object and compare it with the profile
setting. This routine analyzes the current object only. If there is
difference in the security setting, the object will be added to the
analysis database
Arguments:
ObjectFullName - The object's full path name
ProfileSD - security descriptor specified in the INF profile
ProfileSeInfo - security information specified in the INF profile
Return value:
SCESTATUS error codes
++ */
{
DWORD Win32rc=NO_ERROR;
PSECURITY_DESCRIPTOR pSecurityDescriptor=NULL;
//
// get security information for this object
//
/*
Win32rc = GetNamedSecurityInfo(
ObjectFullName,
SE_DS_OBJECT,
ProfileSeInfo,
NULL,
NULL,
NULL,
NULL,
&pSecurityDescriptor
);
*/
Win32rc = ScepReadDsObjSecurity(
ObjectFullName,
ProfileSeInfo,
&pSecurityDescriptor
);
if ( Win32rc != ERROR_SUCCESS ) {
ScepLogOutput3(1, Win32rc, SCEDLL_ERROR_QUERY_SECURITY, ObjectFullName );
return(Win32rc);
}
// printf("\n\n\nDs Obj Sec. for %ws\n", ObjectFullName);
// ScepPrintSecurityDescriptor(pSecurityDescriptor, TRUE);
//
// Compare the analysis security descriptor with the profile
//
Win32rc = ScepCompareAndAddObject(
ObjectFullName,
SE_DS_OBJECT,
TRUE,
pSecurityDescriptor,
ProfileSD,
ProfileSeInfo,
TRUE,
NULL
);
if ( Win32rc != ERROR_SUCCESS ) {
ScepLogOutput3(1, Win32rc, SCEDLL_SAP_ERROR_ANALYZE, ObjectFullName);
}
ScepFree(pSecurityDescriptor);
return(Win32rc);
}
DWORD
ScepAnalyzeDsObjectAndChildren(
IN PWSTR ObjectName,
IN BYTE Status,
IN SECURITY_INFORMATION SeInfo,
IN PSCE_OBJECT_CHILD_LIST pNextLevel
)
/* ++
Routine Description:
Analyze current object and all subkeys/files/directories under the object.
If there is difference in security setting for any object, the object will
be added to the analysis database.
Arguments:
ObjectFullName - The object's full path name
ProfileSD - security descriptor specified in the INF profile
ProfileSeInfo - security information specified in the INF profile
Return value:
SCESTATUS error codes
++ */
{
DWORD retErr=ERROR_SUCCESS;
//
// enumerate one level nodes under the current object
//
LDAPMessage *Message = NULL;
PWSTR Attribs[2];
WCHAR dn[] = L"distinguishedName";
Attribs[0] = dn;
Attribs[1] = NULL;
retErr = ldap_search_s( pLDAP,
ObjectName,
LDAP_SCOPE_ONELEVEL,
L"(objectClass=*)",
Attribs,
0,
&Message);
if( Message ) {
retErr = ERROR_SUCCESS;
LDAPMessage *Entry = NULL;
//
// How many entries ?
//
ULONG nChildren = ldap_count_entries(pLDAP, Message);
//
// get the first one.
//
Entry = ldap_first_entry(pLDAP, Message);
//
// now loop through the entries and recursively fix the
// security on the subtree.
//
PWSTR *Values;
PWSTR SubObjectName;
INT cmpFlag;
PSCE_OBJECT_CHILD_LIST pTemp;
for(ULONG i = 0; i<nChildren; i++) {
if(Entry != NULL) {
Values = ldap_get_values(pLDAP, Entry, Attribs[0]);
if(Values != NULL) {
//
// Save the sub object DN for recursion.
//
SubObjectName = (PWSTR)LocalAlloc(0,(wcslen(Values[0]) + 1)*sizeof(WCHAR));
if ( SubObjectName != NULL ) {
wcscpy(SubObjectName, Values[0]);
ldap_value_free(Values);
#ifdef SCEDS_DBG
printf("%ws\n", SubObjectName);
#endif
//
// check if the SubObjectName is in the object tree already
//
for ( pTemp = pNextLevel; pTemp != NULL; pTemp=pTemp->Next ) {
cmpFlag = _wcsicmp(pTemp->Node->ObjectFullName, SubObjectName);
if ( cmpFlag >= 0 )
break;
}
if ( pTemp == NULL || cmpFlag > 0 ) {
//
// did not find in the object tree, so anayze it or recursive it
//
if ( Status == SCE_STATUS_OVERWRITE ) {
//
// analyze this file/key first
//
retErr = ScepAnalyzeDsObject(
SubObjectName,
NULL,
SeInfo
);
//
// if the object does not exist (impossible), skip all children
//
if ( retErr == ERROR_ACCESS_DENIED ||
retErr == ERROR_SHARING_VIOLATION ) {
gWarningCode = retErr;
retErr = ScepSaveDsStatusToSection(
SubObjectName,
TRUE,
SCE_STATUS_ERROR_NOT_AVAILABLE,
NULL,
0
);
retErr = ERROR_SUCCESS;
}
if ( retErr == ERROR_FILE_NOT_FOUND ||
retErr == ERROR_PATH_NOT_FOUND ) {
gWarningCode = retErr;
retErr = ERROR_SUCCESS;
} else if ( retErr == SCESTATUS_SUCCESS ) {
//
// recursive to next level
//
retErr = ScepAnalyzeDsObjectAndChildren(
SubObjectName,
Status,
SeInfo,
NULL
);
}
} else {
//
// status is check, just raise a NOT_CONFIGURED status
//
retErr = ScepSaveDsStatusToSection(
SubObjectName,
TRUE,
SCE_STATUS_NOT_CONFIGURED,
NULL,
0
);
}
} // else find it, skip the subnode
LocalFree(SubObjectName);
} else {
ldap_value_free(Values);
retErr = ERROR_NOT_ENOUGH_MEMORY;
}
} else {
retErr = LdapMapErrorToWin32(pLDAP->ld_errno);
}
} else {
retErr = LdapMapErrorToWin32(pLDAP->ld_errno);
}
if ( retErr != ERROR_SUCCESS ) {
break;
}
if ( i < nChildren-1 ) {
Entry = ldap_next_entry(pLDAP, Entry);
}
} // end for loop
ldap_msgfree(Message);
}
return(retErr);
}
DWORD
ScepConvertObjectTreeToLdap(
IN PSCE_OBJECT_TREE pObject
)
{
DWORD Win32rc;
PWSTR NewName=NULL;
if ( pObject == NULL ) {
return(ERROR_SUCCESS);
}
//
// this node
//
Win32rc = ScepConvertJetNameToLdapCase(
pObject->ObjectFullName,
FALSE,
SCE_CASE_DONT_CARE,
&NewName
);
if ( Win32rc == ERROR_SUCCESS && NewName != NULL ) {
ScepFree(pObject->ObjectFullName);
pObject->ObjectFullName = NewName;
//
// child
//
for ( PSCE_OBJECT_CHILD_LIST pTemp = pObject->ChildList;
pTemp != NULL; pTemp = pTemp->Next ) {
Win32rc = ScepConvertObjectTreeToLdap(
pTemp->Node
);
if ( Win32rc != ERROR_SUCCESS ) {
break;
}
}
}
return(Win32rc);
}
DWORD
ScepConvertJetNameToLdapCase(
IN PWSTR JetName,
IN BOOL bLastComponent,
IN BYTE bCase,
OUT PWSTR *LdapName
)
{
if ( JetName == NULL || LdapName == NULL ) {
return(ERROR_INVALID_PARAMETER);
}
DWORD retErr;
PWSTR pTempName=NULL;
//
// reserve the components
//
retErr = ScepSceStatusToDosError(
ScepConvertLdapToJetIndexName(
JetName,
&pTempName
) );
if ( retErr == ERROR_SUCCESS && pTempName == NULL ) {
return(ERROR_INVALID_PARAMETER);
}
if ( retErr == ERROR_SUCCESS ) {
if ( bCase == SCE_CASE_REQUIRED ||
bCase == SCE_CASE_PREFERED ) {
if ( pLDAP == NULL ) {
//
// ldap is not available
//
retErr = ERROR_NOT_SUPPORTED;
} else {
//
// go search in the DS tree
//
LDAPMessage *Message = NULL; // for LDAP calls.
PWSTR Attribs[2]; // for LDAP calls.
Attribs[0] = L"distinguishedName";
Attribs[1] = NULL;
retErr = ldap_search_s( pLDAP,
pTempName,
LDAP_SCOPE_BASE,
L"(objectClass=*)",
Attribs,
0,
&Message);
if( Message ) {
retErr = ERROR_SUCCESS;
LDAPMessage *Entry = NULL;
Entry = ldap_first_entry(pLDAP, Message);
if(Entry != NULL) {
//
// Values here is a new scope pointer
//
PWSTR *Values = ldap_get_values(pLDAP, Entry, Attribs[0]);
if(Values != NULL) {
//
// Values[0] is the DN.
// save it in pTempName
//
PWSTR pTemp2 = (PWSTR)ScepAlloc(0, (wcslen(Values[0])+1)*sizeof(WCHAR));
if ( pTemp2 != NULL ) {
wcscpy(pTemp2, Values[0]);
ScepFree(pTempName);
pTempName = pTemp2;
} else
retErr = ERROR_NOT_ENOUGH_MEMORY;
ldap_value_free(Values);
} else
retErr = LdapMapErrorToWin32(pLDAP->ld_errno);
} else
retErr = LdapMapErrorToWin32(pLDAP->ld_errno);
ldap_msgfree(Message);
}
}
if ( (retErr != ERROR_SUCCESS && bCase == SCE_CASE_REQUIRED) ||
retErr == ERROR_NOT_ENOUGH_MEMORY ) {
ScepFree(pTempName);
return(retErr);
}
}
if ( pTempName == NULL ) {
// ???
return(ERROR_NOT_ENOUGH_MEMORY);
}
//
// ignore other errors for CASE_PREFERED
//
retErr = ERROR_SUCCESS;
if ( bLastComponent ) {
//
// only return the first component
// pTempName must not be NULL. it shouldn't be NULL
//
PWSTR pStart = wcschr(pTempName, L',');
if ( pStart == NULL ) {
*LdapName = pTempName;
} else {
*LdapName = (PWSTR)ScepAlloc(0, ((UINT)(pStart-pTempName+1))*sizeof(WCHAR));
if ( *LdapName == NULL ) {
retErr = ERROR_NOT_ENOUGH_MEMORY;
} else {
wcsncpy(*LdapName, pTempName, (size_t)(pStart-pTempName));
*(*LdapName+(pStart-pTempName)) = L'\0';
}
ScepFree(pTempName);
}
} else {
//
// return the whole name
//
*LdapName = pTempName;
}
}
return(retErr);
}
SCESTATUS
ScepDsObjectExist(
IN PWSTR ObjectName
)
// ObjectName must be in Ldap format
{
DWORD retErr;
LDAPMessage *Message = NULL; // for LDAP calls.
PWSTR Attribs[2]; // for LDAP calls.
Attribs[0] = L"distinguishedName";
Attribs[1] = NULL;
retErr = ldap_search_s( pLDAP,
ObjectName,
LDAP_SCOPE_BASE,
L"(objectClass=*)",
Attribs,
0,
&Message);
if( Message ) {
retErr = ERROR_SUCCESS;
LDAPMessage *Entry = NULL;
Entry = ldap_first_entry(pLDAP, Message);
if(Entry != NULL) {
//
// Values here is a new scope pointer
//
PWSTR *Values = ldap_get_values(pLDAP, Entry, Attribs[0]);
if(Values != NULL) {
ldap_value_free(Values);
} else
retErr = LdapMapErrorToWin32(pLDAP->ld_errno);
} else
retErr = LdapMapErrorToWin32(pLDAP->ld_errno);
ldap_msgfree(Message);
}
return(ScepDosErrorToSceStatus(retErr));
}
SCESTATUS
ScepEnumerateDsOneLevel(
IN PWSTR ObjectName,
OUT PSCE_NAME_LIST *pNameList
)
{
if ( ObjectName == NULL || pNameList == NULL ) {
return(SCESTATUS_INVALID_PARAMETER);
}
DWORD retErr=ERROR_SUCCESS;
//
// enumerate one level nodes under the current object
//
LDAPMessage *Message = NULL;
PWSTR Attribs[2];
WCHAR dn[] = L"distinguishedName";
Attribs[0] = dn;
Attribs[1] = NULL;
retErr = ldap_search_s( pLDAP,
ObjectName,
LDAP_SCOPE_ONELEVEL,
L"(objectClass=*)",
Attribs,
0,
&Message);
if( Message ) {
retErr = ERROR_SUCCESS;
LDAPMessage *Entry = NULL;
//
// How many entries ?
//
ULONG nChildren = ldap_count_entries(pLDAP, Message);
//
// get the first one.
//
Entry = ldap_first_entry(pLDAP, Message);
//
// now loop through the entries and recursively fix the
// security on the subtree.
//
PWSTR *Values;
for(ULONG i = 0; i<nChildren; i++) {
if(Entry != NULL) {
Values = ldap_get_values(pLDAP, Entry, Attribs[0]);
if(Values != NULL) {
//
// Save the sub object DN for recursion.
//
retErr = ScepAddToNameList(
pNameList,
Values[0],
wcslen(Values[0])
);
ldap_value_free(Values);
} else {
retErr = LdapMapErrorToWin32(pLDAP->ld_errno);
}
} else {
retErr = LdapMapErrorToWin32(pLDAP->ld_errno);
}
if ( retErr != ERROR_SUCCESS ) {
break;
}
if ( i < nChildren-1 ) {
Entry = ldap_next_entry(pLDAP, Entry);
}
} // end for loop
ldap_msgfree(Message);
}
if ( retErr != ERROR_SUCCESS ) {
//
// free the object list
//
ScepFreeNameList(*pNameList);
*pNameList = NULL;
}
return(ScepDosErrorToSceStatus(retErr));
}
DWORD
ScepChangeSecurityOnObject(
PWSTR ObjectName,
PSECURITY_DESCRIPTOR pSecurityDescriptor,
SECURITY_INFORMATION SeInfo
)
{
PLDAPMod rgMods[2];
PLDAP_BERVAL pBVals[2];
LDAPMod Mod;
LDAP_BERVAL BVal;
DWORD retErr;
BYTE berValue[8];
//
// JohnsonA The BER encoding is current hardcoded. Change this to use
// AndyHe's BER_printf package once it's done.
//
berValue[0] = 0x30;
berValue[1] = 0x03;
berValue[2] = 0x02;
berValue[3] = 0x01;
berValue[4] = (BYTE)((ULONG)SeInfo & 0xF);
LDAPControl SeInfoControl =
{
LDAP_SERVER_SD_FLAGS_OID_W,
{
5, (PCHAR)berValue
},
TRUE
};
PLDAPControl ServerControls[2] =
{
&SeInfoControl,
NULL
};
rgMods[0] = &Mod;
rgMods[1] = NULL;
pBVals[0] = &BVal;
pBVals[1] = NULL;
//
// lets set object security (whack NT-Security-Descriptor)
//
Mod.mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
Mod.mod_type = ACTRL_SD_PROP_NAME;
Mod.mod_values = (PWSTR *)pBVals;
//
// calculate the length of the security descriptor
//
if ( pSecurityDescriptor == NULL )
BVal.bv_len = 0;
else {
BVal.bv_len = RtlLengthSecurityDescriptor(pSecurityDescriptor);
}
BVal.bv_val = (PCHAR)(pSecurityDescriptor);
//
// Now, we'll do the write...
//
retErr = ldap_modify_ext_s(pLDAP,
ObjectName,
rgMods,
(PLDAPControl *)&ServerControls,
NULL);
return(retErr);
}
DWORD
ScepReadDsObjSecurity(
IN PWSTR pwszObject,
IN SECURITY_INFORMATION SeInfo,
OUT PSECURITY_DESCRIPTOR *ppSD
)
{
DWORD dwErr;
PLDAPMessage pMessage = NULL;
PWSTR rgAttribs[2];
BYTE berValue[8];
//
// JohnsonA The BER encoding is current hardcoded. Change this to use
// AndyHe's BER_printf package once it's done.
//
berValue[0] = 0x30;
berValue[1] = 0x03;
berValue[2] = 0x02;
berValue[3] = 0x01;
berValue[4] = (BYTE)((ULONG)SeInfo & 0xF);
LDAPControl SeInfoControl =
{
LDAP_SERVER_SD_FLAGS_OID_W,
{
5, (PCHAR)berValue
},
TRUE
};
PLDAPControl ServerControls[2] =
{
&SeInfoControl,
NULL
};
rgAttribs[0] = ACTRL_SD_PROP_NAME;
rgAttribs[1] = NULL;
dwErr = ldap_search_ext_s(pLDAP,
pwszObject,
LDAP_SCOPE_BASE,
L"(objectClass=*)",
rgAttribs,
0,
(PLDAPControl *)&ServerControls,
NULL,
NULL,
10000,
&pMessage);
if( pMessage ) {
dwErr = ERROR_SUCCESS;
LDAPMessage *pEntry = NULL;
pEntry = ldap_first_entry(pLDAP,
pMessage);
if(pEntry == NULL) {
dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
} else {
//
// Now, we'll have to get the values
//
PWSTR *ppwszValues = ldap_get_values(pLDAP,
pEntry,
rgAttribs[0]);
if(ppwszValues == NULL) {
dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
} else {
PLDAP_BERVAL *pSize = ldap_get_values_len(pLDAP,
pMessage,
rgAttribs[0]);
if(pSize == NULL) {
dwErr = LdapMapErrorToWin32( pLDAP->ld_errno );
} else {
//
// Allocate the security descriptor to return
//
*ppSD = (PSECURITY_DESCRIPTOR)ScepAlloc(0, (*pSize)->bv_len);
if(*ppSD == NULL) {
dwErr = ERROR_NOT_ENOUGH_MEMORY;
} else {
memcpy(*ppSD,
(PBYTE)(*pSize)->bv_val,
(*pSize)->bv_len);
}
ldap_value_free_len(pSize);
}
ldap_value_free(ppwszValues);
}
}
ldap_msgfree(pMessage);
}
return(dwErr);
}
PSECURITY_DESCRIPTOR
ScepMakeNullSD()
{
PSECURITY_DESCRIPTOR pNullSD;
DWORD dwErr=ERROR_SUCCESS;
pNullSD = (PSECURITY_DESCRIPTOR)ScepAlloc(0, sizeof(SECURITY_DESCRIPTOR));
if(pNullSD == NULL) {
dwErr = ERROR_NOT_ENOUGH_MEMORY;
SetLastError(dwErr);
} else {
//
// build the SD
//
if(InitializeSecurityDescriptor(pNullSD,
SECURITY_DESCRIPTOR_REVISION
) == FALSE ) {
dwErr = GetLastError();
} else {
if(SetSecurityDescriptorDacl(pNullSD,
TRUE,
NULL,
FALSE) == FALSE) {
dwErr = GetLastError();
} else {
if(SetSecurityDescriptorSacl(pNullSD,
TRUE,
NULL,
FALSE) == FALSE) {
dwErr = GetLastError();
}
}
}
if ( dwErr != ERROR_SUCCESS ) {
ScepFree(pNullSD);
pNullSD = NULL;
SetLastError(dwErr);
}
}
return(pNullSD);
}
SCESTATUS
ScepEnumerateDsObjectRoots(
IN PLDAP pLdap OPTIONAL,
OUT PSCE_OBJECT_LIST *pRoots
)
{
DWORD retErr;
SCESTATUS rc=SCESTATUS_SUCCESS;
LDAPMessage *Message = NULL; // for LDAP calls.
PWSTR Attribs[2]; // for LDAP calls.
Attribs[0] = LDAP_OPATT_DEFAULT_NAMING_CONTEXT_W; // ntldap.h
Attribs[1] = NULL;
PLDAP pTempLdap;
if ( pLdap == NULL )
pTempLdap = pLDAP;
else
pTempLdap = pLdap;
retErr = ldap_search_s(pTempLdap,
L"",
LDAP_SCOPE_BASE,
L"(objectClass=*)",
Attribs,
0,
&Message);
if( Message ) { // should not check for error code
retErr = ERROR_SUCCESS;
LDAPMessage *Entry = NULL;
//
// read the first entry.
// we did base level search, we have only one entry.
// Entry does not need to be freed (it is freed with the message)
//
Entry = ldap_first_entry(pTempLdap, Message);
if(Entry != NULL) {
PWSTR *Values = ldap_get_values(pTempLdap, Entry, Attribs[0]);
if(Values != NULL) {
//
// should only get one value for the default naming context
// Values[0] here is the DN.
//
if ( Values[0] == NULL ) {
//
// unknown error.
//
rc = SCESTATUS_OTHER_ERROR;
} else {
//
// add the full name to the object list
// search for base, only one value should be returned
//
rc = ScepAddToObjectList(
pRoots,
Values[0],
wcslen(Values[0]),
TRUE,
SCE_STATUS_IGNORE,
0,
SCE_CHECK_DUP //TRUE // check for duplicate
);
}
ldap_value_free(Values);
} else
retErr = LdapMapErrorToWin32(pTempLdap->ld_errno);
} else
retErr = LdapMapErrorToWin32(pTempLdap->ld_errno);
ldap_msgfree(Message);
Message = NULL;
}
if ( retErr != ERROR_SUCCESS ) {
rc = ScepDosErrorToSceStatus(retErr);
}
return(rc);
}
/*
SCESTATUS
ScepEnumerateDsObjectRoots(
IN PLDAP pLdap OPTIONAL,
OUT PSCE_OBJECT_LIST *pRoots
)
{
DWORD retErr;
SCESTATUS rc;
LDAPMessage *Message = NULL; // for LDAP calls.
PWSTR Attribs[2]; // for LDAP calls.
Attribs[0] = LDAP_OPATT_NAMING_CONTEXTS_W;
Attribs[1] = NULL;
PLDAP pTempLdap;
if ( pLdap == NULL )
pTempLdap = pLDAP;
else
pTempLdap = pLdap;
retErr = ldap_search_s(pTempLdap,
L"",
LDAP_SCOPE_BASE,
L"(objectClass=*)",
Attribs,
0,
&Message);
if(retErr == ERROR_SUCCESS) {
LDAPMessage *Entry = NULL;
//
// read the first entry.
// we did base level search, we have only one entry.
// Entry does not need to be freed (it is freed with the message)
//
Entry = ldap_first_entry(pTempLdap, Message);
if(Entry != NULL) {
PWSTR *Values = ldap_get_values(pTempLdap, Entry, Attribs[0]);
if(Values != NULL) {
ULONG ValCount = ldap_count_values(Values);
ULONG index;
PWSTR ObjectName;
Attribs[0] = L"distinguishedName";
Attribs[1] = NULL;
//
// process each NC
//
for(index = 0; index < ValCount; index++) {
if ( Values[index] == NULL ) {
continue;
}
if( ScepIsMatchingSchemaObject(L"Configuration", Values[index]) ||
ScepIsMatchingSchemaObject(L"Schema", Values[index]) ) {
//
// If it is the Configuration or Schema, skip it
// because it is under the domain node
// only the domain node is returned
//
continue;
}
//
// free the message so it can be reused
//
ldap_msgfree(Message);
Message = NULL;
//
// The root object of the NC
//
retErr = ldap_search_s( pTempLdap,
Values[index],
LDAP_SCOPE_BASE,
L"(objectClass=*)",
Attribs,
0,
&Message);
if(retErr == ERROR_SUCCESS) {
Entry = ldap_first_entry(pTempLdap, Message);
if(Entry != NULL) {
//
// Values here is a new scope pointer
//
PWSTR *Values = ldap_get_values(pTempLdap, Entry, Attribs[0]);
if(Values != NULL) {
//
// Values[0] is the DN.
//
if ( Values[0] == NULL ) {
//
// unknown error.
//
rc = SCESTATUS_OTHER_ERROR;
} else {
//
// add the full name to the object list
// search for base, only one value should be returned
//
rc = ScepAddToObjectList(
pRoots,
Values[0],
wcslen(Values[0]),
TRUE,
SCE_STATUS_IGNORE,
0,
SCE_CHECK_DUP //TRUE // check for duplicate
);
}
ldap_value_free(Values);
} else
retErr = LdapMapErrorToWin32(pTempLdap->ld_errno);
} else
retErr = LdapMapErrorToWin32(pTempLdap->ld_errno);
if ( retErr != ERROR_SUCCESS ) {
break;
}
}
} // end for loop
//
// outer scope Values
//
ldap_value_free(Values);
} else
retErr = LdapMapErrorToWin32(pTempLdap->ld_errno);
} else
retErr = LdapMapErrorToWin32(pTempLdap->ld_errno);
ldap_msgfree(Message);
Message = NULL;
}
if ( retErr != ERROR_SUCCESS ) {
rc = ScepDosErrorToSceStatus(retErr);
}
return(rc);
}
*/
SCESTATUS
ScepLdapOpen(
OUT PLDAP *pLdap OPTIONAL
)
{
#if _WIN32_WINNT<0x0500
return SCESTATUS_SERVICE_NOT_SUPPORT;
#else
DWORD Win32rc;
//
// bind to ldap
//
PLDAP pTempLdap;
pTempLdap = ldap_open(NULL, LDAP_PORT);
if ( pTempLdap == NULL ) {
Win32rc = ERROR_FILE_NOT_FOUND;
} else {
Win32rc = ldap_bind_s(pTempLdap,
NULL,
NULL,
LDAP_AUTH_SSPI);
}
if ( pLdap == NULL ) {
pLDAP = pTempLdap;
} else {
*pLdap = pTempLdap;
}
pTempLdap = NULL;
if ( Win32rc != ERROR_SUCCESS ) {
ScepLogOutput3(0, Win32rc, SCEDLL_ERROR_OPEN, L"Ldap server.");
}
return(ScepDosErrorToSceStatus(Win32rc));
#endif
}
SCESTATUS
ScepLdapClose(
IN PLDAP *pLdap OPTIONAL
)
{
if ( pLdap == NULL ) {
if ( pLDAP != NULL )
ldap_unbind(pLDAP);
pLDAP = NULL;
return(SCESTATUS_SUCCESS );
}
//
// unbind pLDAP
//
if ( *pLdap != NULL )
ldap_unbind(*pLdap);
*pLdap = NULL;
return(SCESTATUS_SUCCESS);
}