Source code of Windows XP (NT5)
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

/*++
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);
}