mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1700 lines
50 KiB
1700 lines
50 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
delegate.c
|
|
|
|
Abstract:
|
|
|
|
This Module implements the delegation tool, which allows for the management
|
|
of access to DS objects
|
|
|
|
Author:
|
|
|
|
Mac McLain (MacM) 10-02-96
|
|
|
|
Environment:
|
|
|
|
User Mode
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <delegate.h>
|
|
|
|
|
|
|
|
__cdecl main(
|
|
IN INT argc,
|
|
IN CHAR *argv[]
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The MAIN for this executable
|
|
|
|
|
|
Arguments:
|
|
|
|
argc - The count of arguments
|
|
argv - The list of arguments
|
|
|
|
Return Value:
|
|
|
|
0 - Success
|
|
1 - Failure
|
|
|
|
--*/
|
|
{
|
|
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
PWSTR pwszObjPath = NULL;
|
|
ULONG fAccessFlags = 0;
|
|
PWSTR rgwszObjIds[UNKNOWN_ID];
|
|
PACTRL_ACCESSW rgpDefObjAccess[MAX_DEF_ACCESS_ID + 1];
|
|
PACTRL_ACCESSW pCurrentAccess = NULL;
|
|
PACTRL_ACCESSW pAccess = NULL;
|
|
DWORD i;
|
|
DWORD cUsed;
|
|
|
|
memset(rgwszObjIds, 0, sizeof(rgwszObjIds));
|
|
memset(rgpDefObjAccess, 0, sizeof(rgpDefObjAccess));
|
|
|
|
//
|
|
// Temporary inclusion, until the new ADVAPI32.DLL is built
|
|
//
|
|
AccProvInit(dwErr);
|
|
if(dwErr != ERROR_SUCCESS)
|
|
{
|
|
fprintf(stderr,
|
|
"Failed to initialize the security apis: %lu\n",
|
|
dwErr);
|
|
}
|
|
|
|
|
|
//
|
|
// Ok, parse the command line
|
|
//
|
|
if(argc < 2)
|
|
{
|
|
Usage();
|
|
exit(1);
|
|
}
|
|
|
|
//
|
|
// See if we need help
|
|
//
|
|
if(strlen(argv[1]) == 2 && IS_ARG_SWITCH(argv[1]) && argv[1][1] == '?')
|
|
{
|
|
Usage();
|
|
exit(1);
|
|
}
|
|
|
|
//
|
|
// Ok, convert our OU parameter into a WIDE string, so we can do what we
|
|
// have to
|
|
//
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
dwErr = ConvertStringAToStringW(argv[1], &pwszObjPath);
|
|
}
|
|
|
|
//
|
|
// Ok, first, we initialize our ID list from the DS schema
|
|
//
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
dwErr = InitializeIdAndAccessLists(pwszObjPath,
|
|
rgwszObjIds,
|
|
rgpDefObjAccess);
|
|
}
|
|
|
|
//
|
|
// Make sure we're actually dealing with an OU
|
|
//
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
BOOL fIsOU = FALSE;
|
|
|
|
dwErr = IsPathOU(pwszObjPath,
|
|
&fIsOU);
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
if(fIsOU == FALSE)
|
|
{
|
|
fprintf(stderr,
|
|
"%ws is not an Organizational Unit\n",
|
|
pwszObjPath);
|
|
dwErr = ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr,
|
|
"Failed to determine the status of %ws\n",
|
|
pwszObjPath);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
fprintf(stderr,"Initialization failed\n");
|
|
}
|
|
|
|
//
|
|
// First pass through the command line. We'll read off our flags. We
|
|
// need this to determine whether to do the initial read or not
|
|
//
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// First, go through and look for all of our flags
|
|
//
|
|
for(i = 2; i < (DWORD)argc; i++)
|
|
{
|
|
if(IS_ARG_SWITCH(argv[i]))
|
|
{
|
|
if(_stricmp(argv[i] + 1, "T") == 0 ||
|
|
_stricmp(argv[i] + 1, "reseT") == 0)
|
|
{
|
|
fAccessFlags |= D_REPLACE;
|
|
}
|
|
else if(_stricmp(argv[i] + 1, "I") == 0 ||
|
|
_stricmp(argv[i] + 1, "Inherit") == 0)
|
|
{
|
|
fAccessFlags |= D_INHERIT;
|
|
}
|
|
else if(_stricmp(argv[i] + 1, "P") == 0 ||
|
|
_stricmp(argv[i] + 1, "Protected") == 0)
|
|
{
|
|
fAccessFlags |= D_PROTECT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// See if we need to read the current access, which is if we are simply
|
|
// displaying the current security, or editing the existing security
|
|
//
|
|
if(dwErr == ERROR_SUCCESS && (argc == 2 ||
|
|
(fAccessFlags & D_REPLACE) == 0))
|
|
{
|
|
//
|
|
// GetNamedSecurityInfoEx is a NT 5 API
|
|
//
|
|
dwErr = GetNamedSecurityInfoEx(pwszObjPath,
|
|
SE_DS_OBJECT_ALL,
|
|
DACL_SECURITY_INFORMATION,
|
|
L"Windows NT Access Provider",
|
|
NULL,
|
|
&pCurrentAccess,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// See if we were supposed to display it
|
|
//
|
|
if(argc == 2)
|
|
{
|
|
DumpAccess(pwszObjPath,
|
|
pCurrentAccess,
|
|
rgwszObjIds);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr,
|
|
"Failed to read the current security from %ws\n",
|
|
pwszObjPath);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Ok, now process the command line again, and do the necessary operations
|
|
//
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// First, go through and look for all of our flags
|
|
//
|
|
i = 2;
|
|
|
|
while(dwErr == ERROR_SUCCESS && i < (DWORD)argc)
|
|
{
|
|
if(IS_ARG_SWITCH(argv[i]))
|
|
{
|
|
if(_stricmp(argv[i] + 1, "T") == 0 ||
|
|
_stricmp(argv[i] + 1, "reseT") == 0)
|
|
{
|
|
//
|
|
// already processed above
|
|
//
|
|
}
|
|
else if(_stricmp(argv[i] + 1, "I") == 0 ||
|
|
_stricmp(argv[i] + 1, "Inherit") == 0)
|
|
{
|
|
//
|
|
// already processed above
|
|
//
|
|
}
|
|
else if(_stricmp(argv[i] + 1, "P") == 0 ||
|
|
_stricmp(argv[i] + 1, "Protected") == 0)
|
|
{
|
|
//
|
|
// already processed above
|
|
//
|
|
}
|
|
else if(_stricmp(argv[i] + 1, "R") == 0 ||
|
|
_stricmp(argv[i] + 1, "Revoke") == 0)
|
|
{
|
|
dwErr = ProcessCmdlineUsers(pCurrentAccess,
|
|
argv,
|
|
argc,
|
|
i,
|
|
REVOKE,
|
|
fAccessFlags,
|
|
rgwszObjIds,
|
|
rgpDefObjAccess,
|
|
&cUsed,
|
|
&pAccess);
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
LocalFree(pCurrentAccess);
|
|
pCurrentAccess = pAccess;
|
|
|
|
i += cUsed;
|
|
}
|
|
}
|
|
else if(_stricmp(argv[i] + 1, "G") == 0 ||
|
|
_stricmp(argv[i] + 1, "Grant") == 0)
|
|
{
|
|
dwErr = ProcessCmdlineUsers(pCurrentAccess,
|
|
argv,
|
|
argc,
|
|
i,
|
|
GRANT,
|
|
fAccessFlags,
|
|
rgwszObjIds,
|
|
rgpDefObjAccess,
|
|
&cUsed,
|
|
&pAccess);
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
LocalFree(pCurrentAccess);
|
|
pCurrentAccess = pAccess;
|
|
|
|
i += cUsed;
|
|
}
|
|
}
|
|
else if(_stricmp(argv[i] + 1, "D") == 0 ||
|
|
_stricmp(argv[i] + 1, "Deny") == 0)
|
|
{
|
|
dwErr = ProcessCmdlineUsers(pCurrentAccess,
|
|
argv,
|
|
argc,
|
|
i,
|
|
DENY,
|
|
fAccessFlags,
|
|
rgwszObjIds,
|
|
rgpDefObjAccess,
|
|
&cUsed,
|
|
&pAccess);
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
LocalFree(pCurrentAccess);
|
|
pCurrentAccess = pAccess;
|
|
|
|
i += cUsed;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Some unknown command line parameter
|
|
//
|
|
fprintf(stderr,
|
|
"Unrecognized command line parameter: %s\n",
|
|
argv[i]);
|
|
dwErr = ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Finally, set the access as requested
|
|
//
|
|
if(dwErr == ERROR_SUCCESS && pAccess != NULL)
|
|
{
|
|
//
|
|
// SetNamedSecurityInfoEx is a NT 5 API
|
|
//
|
|
dwErr = SetNamedSecurityInfoEx(pwszObjPath,
|
|
SE_DS_OBJECT_ALL,
|
|
DACL_SECURITY_INFORMATION,
|
|
L"Windows NT Access Provider",
|
|
pCurrentAccess,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
if(dwErr != ERROR_SUCCESS)
|
|
{
|
|
fprintf(stderr,
|
|
"Delegate failed to write the new access to %ws\n",
|
|
pwszObjPath);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Last little informative message...
|
|
//
|
|
if(dwErr == ERROR_PATH_NOT_FOUND)
|
|
{
|
|
fprintf(stderr,
|
|
"DELEGATE did not recognize %ws as a DS path\n",
|
|
pwszObjPath);
|
|
}
|
|
|
|
//
|
|
// Free all our allocated memory
|
|
//
|
|
FreeIdAndAccessList(rgwszObjIds,
|
|
rgpDefObjAccess);
|
|
LocalFree(pwszObjPath);
|
|
LocalFree(pCurrentAccess);
|
|
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
fprintf(stdout,
|
|
"The command completed successfully.\n");
|
|
}
|
|
|
|
return(dwErr == ERROR_SUCCESS ? 0 : 1);
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
DumpAccess (
|
|
IN PWSTR pwszObject,
|
|
IN PACTRL_ACCESSW pAccess,
|
|
IN PWSTR *ppwszIDs
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will display the given actrl_access list to stdout
|
|
|
|
|
|
Arguments:
|
|
|
|
pwszObject - The path to the object being displayed
|
|
pAccess - The access list to display
|
|
ppwszIDs - The list of property/control ids read from the schema. Used
|
|
to assign a name to the property list.
|
|
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
ULONG iProp, iEnt, i;
|
|
ULONG Inherit;
|
|
ACCESS_RIGHTS Access;
|
|
PWSTR pwszTag = NULL;
|
|
PWSTR pwszPropertyTag = L"Object or Property:";
|
|
PWSTR rgwszInheritTags[] = {L"None",
|
|
L"Object",
|
|
L"Container",
|
|
L"Inherit, no propagate",
|
|
L"Inherit only",
|
|
L"Inherited"};
|
|
|
|
PWSTR rgwszAccessTags[] = {L"None",
|
|
L"Delete",
|
|
L"Read Security Information",
|
|
L"Change Security Information",
|
|
L"Change owner",
|
|
L"Synchronize",
|
|
L"Open Object",
|
|
L"Create Child",
|
|
L"Delete Child",
|
|
L"List contents",
|
|
L"Write Self",
|
|
L"Read Property",
|
|
L"Write Property"};
|
|
|
|
ACCESS_RIGHTS rgAccess[] = {0,
|
|
ACTRL_DELETE,
|
|
ACTRL_READ_CONTROL,
|
|
ACTRL_CHANGE_ACCESS,
|
|
ACTRL_CHANGE_OWNER,
|
|
ACTRL_SYNCHRONIZE,
|
|
ACTRL_DS_OPEN,
|
|
ACTRL_DS_CREATE_CHILD,
|
|
ACTRL_DS_DELETE_CHILD,
|
|
ACTRL_DS_LIST,
|
|
ACTRL_DS_SELF,
|
|
ACTRL_DS_READ_PROP,
|
|
ACTRL_DS_WRITE_PROP};
|
|
|
|
PWSTR rgwszPropTags[] = {L"User object",
|
|
L"Group object",
|
|
L"Printer object",
|
|
L"Volume object",
|
|
L"Organizational Unit object",
|
|
L"Change group membership property",
|
|
L"Change password property",
|
|
L"Account control property",
|
|
L"Local Group object"};
|
|
|
|
//
|
|
// These [currently string versions of valid] IDs are currently planned to
|
|
// be publicly defined for the product. They are included below only due
|
|
// to the fact that is no current public definition (as it is not
|
|
// necessary for anyone else to need them), and the delegate tool needs
|
|
// to be able to display a friendly name for it. DO NOT RELY ON THE
|
|
// FOLLOWING DEFINITIONS REMAINING CONSTANT.
|
|
//
|
|
PWSTR rgwszDSControlIds[] = {
|
|
L"ab721a50-1e2f-11d0-9819-00aa0040529b",
|
|
L"ab721a51-1e2f-11d0-9819-00aa0040529b"};
|
|
|
|
PWSTR rgwszDSControlTrags[] = {
|
|
L"List Domain Accounts",
|
|
L"Lookup Domains"
|
|
};
|
|
|
|
|
|
|
|
//
|
|
// Don't dump something that doesn't exist...
|
|
//
|
|
if(pAccess == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
fprintf(stdout, "Displaying access list for object %ws\n", pwszObject);
|
|
fprintf(stdout, "\tNumber of property lists: %lu\n", pAccess->cEntries);
|
|
for(iProp = 0; iProp < pAccess->cEntries; iProp++)
|
|
{
|
|
if(pAccess->pPropertyAccessList[iProp].lpProperty != NULL)
|
|
{
|
|
pwszTag = NULL;
|
|
//
|
|
// Find it in our list, so we can display the right value
|
|
//
|
|
for(i = 0; i < UNKNOWN_ID; i++)
|
|
{
|
|
if(_wcsicmp(pAccess->pPropertyAccessList[iProp].lpProperty,
|
|
ppwszIDs[i]) == 0)
|
|
{
|
|
pwszTag = rgwszPropTags[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Look up the list of DS control rights
|
|
//
|
|
for(i = 0;
|
|
i < sizeof(rgwszDSControlIds) / sizeof(PWSTR) &&
|
|
pwszTag == NULL;
|
|
i++)
|
|
{
|
|
if(_wcsicmp(pAccess->pPropertyAccessList[iProp].lpProperty,
|
|
rgwszDSControlIds[i]) == 0)
|
|
{
|
|
pwszTag = rgwszDSControlTrags[i];
|
|
pwszPropertyTag = L"DS Control right id:";
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(pwszTag == NULL)
|
|
{
|
|
fprintf(stdout,
|
|
"\t\tUnrecognized property whose id is %ws\n",
|
|
pAccess->pPropertyAccessList[iProp].lpProperty);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stdout, "\t\t%ws %ws\n", pwszPropertyTag, pwszTag);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fprintf(stdout, "\t\tObject: %ws\n", pwszObject);
|
|
}
|
|
|
|
//
|
|
// Is it protected?
|
|
//
|
|
if(pAccess->pPropertyAccessList[iProp].fListFlags != 0)
|
|
{
|
|
if((pAccess->pPropertyAccessList[iProp].fListFlags &
|
|
ACTRL_ACCESS_PROTECTED) != 0)
|
|
{
|
|
fprintf(stdout,"\t\tAccess list is protected\n");
|
|
}
|
|
}
|
|
|
|
if(pAccess->pPropertyAccessList[iProp].pAccessEntryList == NULL)
|
|
{
|
|
fprintf(stdout,"\t\tpAccessEntryList: NULL\n");
|
|
}
|
|
else
|
|
{
|
|
PACTRL_ACCESS_ENTRYW pAE= pAccess->pPropertyAccessList[iProp].
|
|
pAccessEntryList->pAccessList;
|
|
fprintf(stdout,
|
|
"\t\t\t%lu Access Entries for this object or property\n",
|
|
pAccess->pPropertyAccessList[iProp].pAccessEntryList->
|
|
cEntries);
|
|
|
|
for(iEnt = 0;
|
|
iEnt < pAccess->pPropertyAccessList[iProp].
|
|
pAccessEntryList->cEntries;
|
|
iEnt++)
|
|
{
|
|
//
|
|
// Type of entry
|
|
//
|
|
if(pAE[iEnt].fAccessFlags == ACTRL_ACCESS_ALLOWED)
|
|
{
|
|
fprintf(stdout,
|
|
"\t\t\t[%lu] Access Allowed entry\n",
|
|
iEnt);
|
|
}
|
|
else if(pAE[iEnt].fAccessFlags == ACTRL_ACCESS_DENIED)
|
|
{
|
|
fprintf(stdout,
|
|
"\t\t\t[%lu] Access Denied entry\n",
|
|
iEnt);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stdout,"\t\t\t[%lu]", iEnt);
|
|
if((pAE[iEnt].fAccessFlags & ACTRL_AUDIT_SUCCESS) != 0)
|
|
{
|
|
fprintf(stdout,"Success Audit");
|
|
}
|
|
if((pAE[iEnt].fAccessFlags & ACTRL_AUDIT_FAILURE) != 0)
|
|
{
|
|
if((pAE[iEnt].fAccessFlags & ACTRL_AUDIT_SUCCESS) != 0)
|
|
{
|
|
fprintf(stdout," | ");
|
|
}
|
|
fprintf(stdout,"Failure Audit");
|
|
}
|
|
fprintf(stdout," entry\n");
|
|
}
|
|
|
|
//
|
|
// User name
|
|
//
|
|
fprintf(stdout,"\t\t\t\tUser: %ws\n",
|
|
pAE[iEnt].Trustee.ptstrName);
|
|
|
|
//
|
|
// Access rights
|
|
//
|
|
fprintf(stdout,"\t\t\t\tAccess: ");
|
|
Access = pAE[iEnt].Access;
|
|
if(Access == 0)
|
|
{
|
|
fprintf(stdout,"%ws\n", rgwszAccessTags[0]);
|
|
}
|
|
else
|
|
{
|
|
for(i = 1;
|
|
i < sizeof(rgwszAccessTags) / sizeof(PWSTR);
|
|
i++)
|
|
{
|
|
if((Access & rgAccess[i]) != 0)
|
|
{
|
|
fprintf(stdout,"%ws", rgwszAccessTags[i]);
|
|
Access &= ~(rgAccess[i]);
|
|
|
|
if(Access != 0)
|
|
{
|
|
fprintf(stdout,
|
|
" |\n\t\t\t\t ");
|
|
}
|
|
}
|
|
}
|
|
|
|
if(Access != 0)
|
|
{
|
|
fprintf(stdout,
|
|
"Unrecognized rights: 0x%lx\n",
|
|
Access);
|
|
}
|
|
|
|
fprintf(stdout,"\n");
|
|
}
|
|
|
|
//
|
|
// Inheritance
|
|
//
|
|
fprintf(stdout,"\t\t\t\tInheritance: ");
|
|
Inherit = pAE[iEnt].Inheritance;
|
|
if(Inherit == 0)
|
|
{
|
|
fprintf(stdout,"%ws\n", rgwszInheritTags[0]);
|
|
}
|
|
else
|
|
{
|
|
for(i = 0;
|
|
i < sizeof(rgwszInheritTags) / sizeof(PWSTR);
|
|
i++)
|
|
{
|
|
if((Inherit & 1 << i) != 0)
|
|
{
|
|
fprintf(stdout,"%ws", rgwszInheritTags[i + 1]);
|
|
Inherit &= ~(1 << i);
|
|
|
|
if(Inherit == 0)
|
|
{
|
|
fprintf(stdout,"\n");
|
|
}
|
|
else
|
|
{
|
|
fprintf(stdout,
|
|
" |\n\t\t\t\t ");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(pAE[iEnt].lpInheritProperty != NULL)
|
|
{
|
|
pwszTag = NULL;
|
|
//
|
|
// Find it in our list, so we can display the right value
|
|
//
|
|
for(i = 0; i < UNKNOWN_ID; i++)
|
|
{
|
|
if(_wcsicmp(pAE[iEnt].lpInheritProperty,
|
|
ppwszIDs[i]) == 0)
|
|
{
|
|
pwszTag = rgwszPropTags[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(pwszTag == NULL)
|
|
{
|
|
fprintf(stdout,
|
|
"\t\t\t\tUnrecognized inherit to object "
|
|
"whose id is %ws\n",
|
|
pAE[iEnt].lpInheritProperty);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stdout,
|
|
"\t\t\t\tObject to inherit to: %ws\n",
|
|
pwszTag);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
Usage (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will display the expected command line usage
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
|
|
fprintf(stdout,
|
|
"Delegates administrative privileges on a directory OU\n");
|
|
fprintf(stdout, "\n");
|
|
fprintf(stdout,
|
|
"DELEGATE <ou> [/T] [/I] [/P] [/G user:perm] [/D user:perm [...]] "
|
|
"[/R user [...]]\n");
|
|
fprintf(stdout, "\n");
|
|
fprintf(stdout," <ou>\tOU to modify or display the rights for\n");
|
|
fprintf(stdout," /T\tReplace the access instead of editing it.\n");
|
|
fprintf(stdout," /I\tInherit to all subcontainers in the directory.\n");
|
|
fprintf(stdout," /P\tMark the object as protected following the operation\n");
|
|
fprintf(stdout," /G user:perm\tGrant specified user admin access rights.\n");
|
|
fprintf(stdout," /D user:perm\tDeny specified user admin access rights.\n");
|
|
fprintf(stdout," \tPerm can be:\n");
|
|
fprintf(stdout," \t\tAbility to create/manage objects in this container\n");
|
|
fprintf(stdout," \t\t\t%2s Create/Manage All object types\n",D_ALL);
|
|
fprintf(stdout," \t\t\t%2s Create/Manage Users\n", D_USER);
|
|
fprintf(stdout," \t\t\t%2s Create/Manage Groups\n", D_GROUP);
|
|
fprintf(stdout," \t\t\t%2s Create/Manage Printers\n", D_PRINT);
|
|
fprintf(stdout," \t\t\t%2s Create/Manage Volumes\n", D_VOL);
|
|
fprintf(stdout," \t\t\t%2s Create/Manage OUs\n", D_OU);
|
|
fprintf(stdout," \t\tAbility to modify specific user or group "
|
|
"properties\n");
|
|
fprintf(stdout," \t\t\t%2s Change Group membership for "
|
|
"all groups\n", D_MEMBERS);
|
|
fprintf(stdout," \t\t\t%2s Set User Passwords\n", D_PASSWD);
|
|
fprintf(stdout," \t\t\t%2s Enable/Disable user accounts\n", D_ENABLE);
|
|
fprintf(stdout, "\n");
|
|
fprintf(stdout," /R user Revoke\tSpecified user's access rights (only valid "
|
|
"without /E).\n");
|
|
fprintf(stdout, "\n");
|
|
fprintf(stdout,"You can specify more than one user in a command and "
|
|
"more than one perm per user, seperated by a , (comma).\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
ConvertStringAToStringW (
|
|
IN PSTR pszString,
|
|
OUT PWSTR *ppwszString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will convert an ASCII string to a UNICODE string.
|
|
|
|
The returned string buffer must be freed via a call to LocalFree
|
|
|
|
|
|
Arguments:
|
|
|
|
pszString - The string to convert
|
|
ppwszString - Where the converted string is returned
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - Success
|
|
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
|
|
|
|
--*/
|
|
{
|
|
|
|
if(pszString == NULL)
|
|
{
|
|
*ppwszString = NULL;
|
|
}
|
|
else
|
|
{
|
|
ULONG cLen = strlen(pszString);
|
|
*ppwszString = (PWSTR)LocalAlloc(LMEM_FIXED,sizeof(WCHAR) *
|
|
(mbstowcs(NULL, pszString, cLen + 1) + 1));
|
|
if(*ppwszString != NULL)
|
|
{
|
|
mbstowcs(*ppwszString,
|
|
pszString,
|
|
cLen + 1);
|
|
}
|
|
else
|
|
{
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
}
|
|
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
ConvertStringWToStringA (
|
|
IN PWSTR pwszString,
|
|
OUT PSTR *ppszString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will convert a UNICODE string to an ANSI string.
|
|
|
|
The returned string buffer must be freed via a call to LocalFree
|
|
|
|
|
|
Arguments:
|
|
|
|
pwszString - The string to convert
|
|
ppszString - Where the converted string is returned
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - Success
|
|
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
|
|
|
|
--*/
|
|
{
|
|
|
|
if(pwszString == NULL)
|
|
{
|
|
*ppszString = NULL;
|
|
}
|
|
else
|
|
{
|
|
ULONG cLen = wcslen(pwszString);
|
|
*ppszString = (PSTR)LocalAlloc(LMEM_FIXED,sizeof(CHAR) *
|
|
(wcstombs(NULL, pwszString, cLen + 1) + 1));
|
|
if(*ppszString != NULL)
|
|
{
|
|
wcstombs(*ppszString,
|
|
pwszString,
|
|
cLen + 1);
|
|
}
|
|
else
|
|
{
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
}
|
|
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
InitializeIdAndAccessLists (
|
|
IN PWSTR pwszOU,
|
|
IN PWSTR *ppwszObjIdList,
|
|
IN PACTRL_ACCESS *ppDefObjAccessList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will read the list of object ids from the schema for the
|
|
object types as indicated by DELEGATE_OBJ_ID enumeration.
|
|
|
|
The returned access list needs to be processed by FreeIdList.
|
|
|
|
Arguments:
|
|
|
|
pwszOU - Information on the domain for which to query the schema
|
|
ppwszObjIdList - The list of object ids to initialize. The list must
|
|
already exist and must of the proper size
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - Success
|
|
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
|
|
ERROR_INVALID_PARAMETER - The OU given was not correct
|
|
|
|
--*/
|
|
{
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
DWORD i;
|
|
PSTR pszSchemaPath = NULL;
|
|
PLDAP pLDAP;
|
|
|
|
//
|
|
// Build a list of attributes to read
|
|
//
|
|
PSTR pszAttribs[] = {"User", // USER_ID
|
|
"Group", // GROUP_ID
|
|
"Print-Queue", // PRINT_ID
|
|
"Volume", // VOLUME_ID
|
|
"Organizational-Unit", // OU_ID
|
|
"Member", // MEMBER_ID
|
|
"User-Password", // PASSWD_ID
|
|
"User-Account-Control", // ACCTCTRL_ID
|
|
"LocalGroup" // LOCALGRP_ID
|
|
};
|
|
|
|
//
|
|
// Get the path to the schema
|
|
//
|
|
dwErr = LDAPReadSchemaPath(pwszOU,
|
|
&pszSchemaPath,
|
|
&pLDAP);
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Ok, now, we need to query the schema for the information
|
|
//
|
|
for(i = 0; i < UNKNOWN_ID && dwErr == ERROR_SUCCESS; i++)
|
|
{
|
|
//
|
|
// Get the info from the schema
|
|
//
|
|
dwErr = LDAPReadSecAndObjIdAsString(pLDAP,
|
|
pszSchemaPath,
|
|
pszAttribs[i],
|
|
&(ppwszObjIdList[i]),
|
|
i > MAX_DEF_ACCESS_ID ?
|
|
NULL :
|
|
&(ppDefObjAccessList[i]));
|
|
}
|
|
|
|
|
|
LocalFree(pszSchemaPath);
|
|
LDAPUnbind(pLDAP);
|
|
}
|
|
|
|
return(dwErr);
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
FreeIdAndAccessList (
|
|
IN PWSTR *ppwszObjIdList,
|
|
IN PACTRL_ACCESS *ppDefObjAccessList
|
|
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will process the list of Ids and determine if any of them
|
|
have been converted to strings. If so, it deallocates the memory
|
|
|
|
Arguments:
|
|
|
|
pObjIdList - The list of object ids to free
|
|
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
DWORD i;
|
|
|
|
for(i = 0; i < UNKNOWN_ID; i++)
|
|
{
|
|
RpcStringFree(&(ppwszObjIdList[i]));
|
|
|
|
if(i <= MAX_DEF_ACCESS_ID)
|
|
{
|
|
LocalFree(ppDefObjAccessList[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
ProcessCmdlineUsers (
|
|
IN PACTRL_ACCESSW pAccessList,
|
|
IN CHAR *argv[],
|
|
IN INT argc,
|
|
IN DWORD iStart,
|
|
IN DELEGATE_OP Op,
|
|
IN ULONG fFlags,
|
|
IN PWSTR *ppwszIDs,
|
|
IN PACTRL_ACCESS *ppDefObjAccessList,
|
|
OUT PULONG pcUsed,
|
|
OUT PACTRL_ACCESSW *ppNewAccess
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will process the command line for any users to have
|
|
access added/denied. If any entries are found, the access list will be
|
|
appropriately updated.
|
|
|
|
The returned access list must be freed via a call to LocalFree
|
|
|
|
|
|
Arguments:
|
|
|
|
pAccessList - The current access list
|
|
argv - List of command line arguments
|
|
argc - count of command line arguments
|
|
iStart - Where in the command line does the current argument start
|
|
Op - Type of operation (grant, revoke, etc) to perform
|
|
fInherit - Whether to do inheritance or not
|
|
fProtected - Whether to mark the entries as protected
|
|
ppwszIDs - List of supported IDs
|
|
pcUsed - Number of items command line items used
|
|
ppNewAccess - Where the new access list is returned. Only valid if
|
|
returned count of revoked items is non-0
|
|
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - Success
|
|
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
|
|
|
|
--*/
|
|
{
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
DWORD i;
|
|
PACTRL_ACCESSW pListToFree = NULL;
|
|
|
|
*pcUsed = 0;
|
|
iStart++;
|
|
|
|
//
|
|
// Process all the entries until we find the next seperator or the end of
|
|
// the list
|
|
//
|
|
while(iStart + *pcUsed < (DWORD)argc &&
|
|
!IS_ARG_SWITCH(argv[iStart + *pcUsed]) &&
|
|
dwErr == ERROR_SUCCESS)
|
|
{
|
|
PWSTR pwszUser = NULL;
|
|
PSTR pszAccess;
|
|
PSTR pszAccessStart;
|
|
|
|
//
|
|
// Get the user name and the list of arguments, if it exists
|
|
//
|
|
dwErr = GetUserInfoFromCmdlineString(argv[iStart + *pcUsed],
|
|
&pwszUser,
|
|
&pszAccessStart);
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
pszAccess = pszAccessStart;
|
|
//
|
|
// Should we have arguments? All except for the revoke case, we
|
|
// should
|
|
//
|
|
if(pszAccess == NULL && Op != REVOKE)
|
|
{
|
|
fprintf(stderr,
|
|
"Missing permissions for %ws\n",
|
|
pwszUser);
|
|
dwErr = ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Ok, now we'll have to process the list, and actually build the
|
|
// access entries
|
|
//
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
DWORD iIndex = 0;
|
|
|
|
//
|
|
// Reset our list of entries...
|
|
//
|
|
pszAccess = pszAccessStart;
|
|
while(dwErr == ERROR_SUCCESS)
|
|
{
|
|
PSTR pszNext = NULL;
|
|
|
|
if(pszAccess != NULL)
|
|
{
|
|
pszNext = strchr(pszAccess, ',');
|
|
|
|
if(pszNext != NULL)
|
|
{
|
|
*pszNext = '\0';
|
|
}
|
|
}
|
|
|
|
dwErr = AddAccessEntry(pAccessList,
|
|
pszAccess,
|
|
pwszUser,
|
|
Op,
|
|
ppwszIDs,
|
|
ppDefObjAccessList,
|
|
fFlags,
|
|
ppNewAccess);
|
|
//
|
|
// Restore our string
|
|
//
|
|
if(pszNext != NULL)
|
|
{
|
|
*pszNext = ',';
|
|
pszNext++;
|
|
}
|
|
|
|
pszAccess = pszNext;
|
|
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// We don't want to free the original list, since that
|
|
// is what we were given to start with...
|
|
//
|
|
LocalFree(pListToFree);
|
|
pAccessList = *ppNewAccess;
|
|
pListToFree = pAccessList;
|
|
}
|
|
else
|
|
{
|
|
if(dwErr == ERROR_NONE_MAPPED)
|
|
{
|
|
fprintf(stderr,"Unknown user %ws specified\n",
|
|
pwszUser);
|
|
}
|
|
}
|
|
|
|
if(Op == REVOKE || pszAccess == NULL)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
(*pcUsed)++;
|
|
}
|
|
|
|
if(*pcUsed == 0)
|
|
{
|
|
dwErr = ERROR_INVALID_PARAMETER;
|
|
fprintf(stderr,"No user information was supplied!\n");
|
|
}
|
|
|
|
return(dwErr);
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
GetUserInfoFromCmdlineString (
|
|
IN PSTR pszUserInfo,
|
|
OUT PWSTR *ppwszUser,
|
|
OUT PSTR *ppszAccessStart
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will process the command line for any user to convert the
|
|
user name to a wide string, and optionally get the access, if it exists
|
|
|
|
The returned user must be freed via a call to LocalFree
|
|
|
|
|
|
Arguments:
|
|
|
|
pszUserInfo - The user info to convert. In the form of username or
|
|
username:access
|
|
ppwszUser - Where to return the user name
|
|
pAccess - Where the access is returned
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - Success
|
|
|
|
--*/
|
|
{
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
|
|
//
|
|
// First, find the seperator, if it exists
|
|
//
|
|
PSTR pszSep = strchr(pszUserInfo, ':');
|
|
if(pszSep != NULL)
|
|
{
|
|
*pszSep = '\0';
|
|
}
|
|
|
|
//
|
|
// Convert our user name
|
|
//
|
|
dwErr = ConvertStringAToStringW(pszUserInfo,
|
|
ppwszUser);
|
|
|
|
if(pszSep != NULL)
|
|
{
|
|
*pszSep = ':';
|
|
pszSep++;
|
|
}
|
|
|
|
*ppszAccessStart = pszSep;
|
|
|
|
return(dwErr);
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
AddAccessEntry (
|
|
IN PACTRL_ACCESSW pAccessList,
|
|
IN PSTR pszAccess,
|
|
IN PWSTR pwszTrustee,
|
|
IN DELEGATE_OP Op,
|
|
IN PWSTR *ppwszIDs,
|
|
IN PACTRL_ACCESS *ppDefObjAccessList,
|
|
IN ULONG fFlags,
|
|
OUT PACTRL_ACCESSW *ppNewAccess
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will add a new access entry to the list based upon the access
|
|
action string and the operation. The pointer to the index variable will
|
|
indicate where in the list it goes, and will be updated to point to the
|
|
next entry on return.
|
|
|
|
|
|
Arguments:
|
|
|
|
pAccessList - The current access list. Can be NULL.
|
|
pszAccess - User access string to add
|
|
pwszTrustee - The user for which an entry is being created
|
|
Op - Type of operation (grant, revoke, etc) to perform
|
|
ppwszIDs - List of object IDs from the DS Schema
|
|
fFlags - Whether to do inheritance, protection, etc
|
|
ppNewAccess - Where the new access list is returned.
|
|
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - Success
|
|
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
|
|
|
|
--*/
|
|
{
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
DWORD i,j,k,iIndex = 0;
|
|
PWSTR pwszProperty = NULL;
|
|
ULONG cEntries = 0;
|
|
BOOL fInherit;
|
|
ACCESS_MODE Access[] = {REVOKE_ACCESS,
|
|
GRANT_ACCESS,
|
|
GRANT_ACCESS};
|
|
ULONG Flags[] = {0,
|
|
ACTRL_ACCESS_ALLOWED,
|
|
ACTRL_ACCESS_DENIED};
|
|
//
|
|
// The most we add is 3 entries at a time... (2 per items, 1 inheritable)
|
|
//
|
|
ACTRL_ACCESS_ENTRYW AccList[3];
|
|
memset(&AccList, 0, sizeof(AccList));
|
|
|
|
fInherit = (BOOL)(fFlags & D_INHERIT);
|
|
if(Op == REVOKE)
|
|
{
|
|
BuildTrusteeWithName(&(AccList[cEntries].Trustee),
|
|
pwszTrustee);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// GroupMembership
|
|
//
|
|
if(_stricmp(pszAccess, D_MEMBERS) == 0)
|
|
{
|
|
//
|
|
// This gets 1 access entry: WriteProp
|
|
//
|
|
AccList[cEntries].lpInheritProperty = ppwszIDs[GROUP_ID];
|
|
AccList[cEntries].Inheritance = INHERIT_ONLY | fInherit ?
|
|
SUB_CONTAINERS_AND_OBJECTS_INHERIT :
|
|
0;
|
|
BuildTrusteeWithName(&(AccList[cEntries].Trustee),
|
|
pwszTrustee);
|
|
|
|
AccList[cEntries].fAccessFlags = Flags[Op];
|
|
AccList[cEntries].Access = ACTRL_DS_WRITE_PROP;
|
|
pwszProperty = ppwszIDs[MEMBER_ID];
|
|
iIndex = MEMBER_ID;
|
|
fprintf(stderr,
|
|
"Sorry... delegation for changing Group membership is "
|
|
"not supported in this alpha release\n");
|
|
dwErr = ERROR_INVALID_PARAMETER;
|
|
|
|
}
|
|
//
|
|
// SetPassword
|
|
//
|
|
else if(_stricmp(pszAccess, D_PASSWD) == 0)
|
|
{
|
|
//
|
|
// This gets 1 access entry: WriteProp
|
|
//
|
|
AccList[cEntries].lpInheritProperty = ppwszIDs[USER_ID];
|
|
AccList[cEntries].Inheritance = INHERIT_ONLY | fInherit ?
|
|
SUB_CONTAINERS_AND_OBJECTS_INHERIT :
|
|
0;
|
|
BuildTrusteeWithName(&(AccList[cEntries].Trustee),
|
|
pwszTrustee);
|
|
|
|
AccList[cEntries].fAccessFlags = Flags[Op];
|
|
AccList[cEntries].Access = ACTRL_DS_WRITE_PROP;
|
|
pwszProperty = ppwszIDs[PASSWD_ID];
|
|
iIndex = PASSWD_ID;
|
|
fprintf(stderr,
|
|
"Sorry... delegation for Set Password is "
|
|
"not supported in this alpha release\n");
|
|
dwErr = ERROR_INVALID_PARAMETER;
|
|
}
|
|
//
|
|
// Enable/Disable accounts
|
|
//
|
|
else if(_stricmp(pszAccess, D_ENABLE) == 0)
|
|
{
|
|
//
|
|
// This gets 1 access entry: WriteProp
|
|
//
|
|
AccList[cEntries].lpInheritProperty = ppwszIDs[USER_ID];
|
|
AccList[cEntries].Inheritance = INHERIT_ONLY | fInherit ?
|
|
SUB_CONTAINERS_AND_OBJECTS_INHERIT :
|
|
0;
|
|
BuildTrusteeWithName(&(AccList[cEntries].Trustee),
|
|
pwszTrustee);
|
|
|
|
AccList[cEntries].fAccessFlags = Flags[Op];
|
|
AccList[cEntries].Access = ACTRL_DS_WRITE_PROP;
|
|
pwszProperty = ppwszIDs[ACCTCTRL_ID];
|
|
iIndex = ACCTCTRL_ID;
|
|
fprintf(stderr,
|
|
"Sorry... delegation for Enabling and Disabling accounts "
|
|
" is not supported in this alpha release\n");
|
|
dwErr = ERROR_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Some object type...
|
|
//
|
|
if(_stricmp(pszAccess, D_ALL) == 0) // All
|
|
{
|
|
pwszProperty = NULL;
|
|
}
|
|
else if(_stricmp(pszAccess, D_USER) == 0) // User
|
|
{
|
|
pwszProperty = ppwszIDs[USER_ID];
|
|
iIndex = USER_ID;
|
|
fprintf(stderr,
|
|
"Sorry... delegation for user objects is "
|
|
"not supported in this alpha release\n");
|
|
dwErr = ERROR_INVALID_PARAMETER;
|
|
}
|
|
else if(_stricmp(pszAccess, D_GROUP) == 0) // Group
|
|
{
|
|
pwszProperty = ppwszIDs[USER_ID];
|
|
iIndex = GROUP_ID;
|
|
fprintf(stderr,
|
|
"Sorry... delegation for group objects is "
|
|
"not supported in this alpha release\n");
|
|
dwErr = ERROR_INVALID_PARAMETER;
|
|
}
|
|
else if(_stricmp(pszAccess, D_PRINT) == 0) // Printers
|
|
{
|
|
pwszProperty = ppwszIDs[PRINT_ID];
|
|
iIndex = PRINT_ID;
|
|
}
|
|
else if(_stricmp(pszAccess, D_VOL) == 0) // Volumes
|
|
{
|
|
pwszProperty = ppwszIDs[VOLUME_ID];
|
|
iIndex = VOLUME_ID;
|
|
}
|
|
else if(_stricmp(pszAccess, D_OU) == 0) // OUs
|
|
{
|
|
pwszProperty = ppwszIDs[OU_ID];
|
|
iIndex = OU_ID;
|
|
}
|
|
else
|
|
{
|
|
dwErr = ERROR_INVALID_PARAMETER;
|
|
fprintf(stderr,
|
|
"Unexpected delegation permission %s given for "
|
|
"user %ws\n",
|
|
pszAccess,
|
|
pwszTrustee);
|
|
}
|
|
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Add the create/delete for the user
|
|
//
|
|
BuildTrusteeWithName(&(AccList[cEntries].Trustee),
|
|
pwszTrustee);
|
|
|
|
AccList[cEntries].fAccessFlags = Flags[Op];
|
|
AccList[cEntries].Access = ACTRL_DS_CREATE_CHILD |
|
|
ACTRL_DS_DELETE_CHILD;
|
|
AccList[cEntries].Inheritance = fInherit ?
|
|
SUB_CONTAINERS_AND_OBJECTS_INHERIT :
|
|
0;
|
|
//
|
|
// If we are inheriting, make sure we inherit only to the
|
|
// proper property
|
|
//
|
|
if(fInherit == TRUE)
|
|
{
|
|
AccList[cEntries].lpInheritProperty = pwszProperty;
|
|
}
|
|
|
|
//
|
|
// Then the inherit on the child object
|
|
//
|
|
cEntries++;
|
|
AccList[cEntries].Inheritance = INHERIT_ONLY |
|
|
(fInherit ?
|
|
SUB_CONTAINERS_AND_OBJECTS_INHERIT :
|
|
0);
|
|
BuildTrusteeWithName(&(AccList[cEntries].Trustee),
|
|
pwszTrustee);
|
|
|
|
AccList[cEntries].fAccessFlags = Flags[Op];
|
|
AccList[cEntries].Access = ACTRL_DS_WRITE_PROP |
|
|
ACTRL_DS_READ_PROP |
|
|
ACTRL_DS_LIST |
|
|
ACTRL_DS_SELF;
|
|
|
|
AccList[cEntries].lpInheritProperty = pwszProperty;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// SetEntriesInAccessList is a NT5 API
|
|
//
|
|
dwErr = SetEntriesInAccessList(cEntries + 1,
|
|
AccList,
|
|
Access[Op],
|
|
pwszProperty,
|
|
pAccessList,
|
|
ppNewAccess);
|
|
//
|
|
// Mark it as protected if we were so asked
|
|
//
|
|
if(dwErr == ERROR_SUCCESS && (fFlags & D_PROTECT) != 0)
|
|
{
|
|
(*ppNewAccess)->pPropertyAccessList[0].fListFlags =
|
|
ACTRL_ACCESS_PROTECTED;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Finally, if this was the first entry we were asked to add for this
|
|
// property, we'll have to go get the default security information
|
|
// from the schema, so we can figure out what inherited entries should
|
|
// be on the object, and apply them as object inherit entries for the
|
|
// property
|
|
//
|
|
if(dwErr == ERROR_SUCCESS && iIndex <= MAX_DEF_ACCESS_ID && Op != REVOKE)
|
|
{
|
|
PACTRL_ACCESS pOldAccess = pAccessList;
|
|
|
|
//
|
|
// First, find the property in our list of access entries we
|
|
// created above
|
|
//
|
|
for(i = 0; i <= (*ppNewAccess)->cEntries; i++)
|
|
{
|
|
//
|
|
// We'll do this based on property... In this case, the only
|
|
// entries we'll be adding will have a property, so we don't have
|
|
// to protect against that...
|
|
//
|
|
if(pwszProperty != NULL &&
|
|
(*ppNewAccess)->pPropertyAccessList[i].lpProperty != NULL &&
|
|
_wcsicmp((*ppNewAccess)->pPropertyAccessList[i].lpProperty,
|
|
pwszProperty) == 0)
|
|
{
|
|
//
|
|
// If it has more entries that we added, we won't have to
|
|
// worry about it, since the information will already
|
|
// have been added. Note that in this case, we don't have
|
|
// to worry about pAccessEntryList being null, since we know
|
|
// we have added some valid entries.
|
|
//
|
|
if((*ppNewAccess)->pPropertyAccessList[i].
|
|
pAccessEntryList->cEntries ==
|
|
cEntries + 1)
|
|
{
|
|
PACTRL_ACCESS pAddAccess = ppDefObjAccessList[iIndex];
|
|
pAccessList = *ppNewAccess;
|
|
|
|
//
|
|
// Ok, we'll have to add them...
|
|
//
|
|
for(j = 0;
|
|
j < (DWORD)(pAddAccess->cEntries) &&
|
|
dwErr == ERROR_SUCCESS;
|
|
j++)
|
|
{
|
|
PACTRL_PROPERTY_ENTRY pPPE =
|
|
&(pAddAccess->pPropertyAccessList[j]);
|
|
dwErr = SetEntriesInAccessList(
|
|
pPPE->pAccessEntryList->cEntries,
|
|
pPPE->pAccessEntryList->pAccessList,
|
|
GRANT_ACCESS,
|
|
pPPE->lpProperty,
|
|
pAccessList,
|
|
ppNewAccess);
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
pAccessList = *ppNewAccess;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We don't want to run through the loop anymore
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(dwErr);
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
IsPathOU (
|
|
IN PWSTR pwszOU,
|
|
OUT PBOOL pfIsOU
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will determine whether the given path is an OU or not.
|
|
|
|
|
|
Arguments:
|
|
|
|
pwszOU - The path into the DS to check on
|
|
ppwszIDs - List of string representations of known IDs
|
|
pfIsOU - Where the results of the test are returned
|
|
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - Success
|
|
|
|
--*/
|
|
{
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
PSTR pszOU = NULL;
|
|
HANDLE hDS = NULL;
|
|
PDS_NAME_RESULTA pNameRes;
|
|
|
|
|
|
dwErr = ConvertStringWToStringA(pwszOU,
|
|
&pszOU);
|
|
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
dwErr = DsBindA(NULL,
|
|
NULL,
|
|
&hDS);
|
|
}
|
|
|
|
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
dwErr = DsCrackNamesA(hDS,
|
|
DS_NAME_NO_FLAGS,
|
|
DS_UNKNOWN_NAME,
|
|
DS_FQDN_1779_NAME,
|
|
1,
|
|
&pszOU,
|
|
&pNameRes);
|
|
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
if(pNameRes->cItems == 0)
|
|
{
|
|
dwErr = ERROR_PATH_NOT_FOUND;
|
|
}
|
|
else
|
|
{
|
|
PSTR pszName = NULL;
|
|
PLDAP pLDAP;
|
|
|
|
//
|
|
// Now, we'll bind to the object, and then do the read
|
|
//
|
|
dwErr = LDAPBind(pNameRes->rItems[0].pDomain,
|
|
&pLDAP);
|
|
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
PSTR *ppszValues;
|
|
DWORD cValues;
|
|
dwErr = LDAPReadAttribute(pszOU,
|
|
"objectclass",
|
|
pLDAP,
|
|
&cValues,
|
|
&ppszValues);
|
|
LDAPUnbind(pLDAP);
|
|
|
|
if(dwErr == ERROR_SUCCESS)
|
|
{
|
|
ULONG i;
|
|
*pfIsOU = FALSE;
|
|
for(i = 0; i <cValues; i++)
|
|
{
|
|
if(_stricmp(ppszValues[i],
|
|
"organizationalUnit") == 0)
|
|
{
|
|
*pfIsOU = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
LDAPFreeValues(ppszValues);
|
|
}
|
|
}
|
|
}
|
|
|
|
DsFreeNameResultA(pNameRes);
|
|
}
|
|
|
|
}
|
|
|
|
if (NULL != pszOU)
|
|
{
|
|
LocalFree(pszOU);
|
|
}
|
|
if (NULL != hDS)
|
|
{
|
|
DsUnBindA(hDS);
|
|
}
|
|
|
|
return(dwErr);
|
|
}
|