ID: Q171273
The information in this article applies to:
In the Client/Server model, it is often desirable to arbitrate access to the server's functions or data based on a client's privileges.
Given an application-derived Security Descriptor (SD) that specifies a certain set of rights for a particular group of users in its Discretionary Access Control List (DACL) and a client token representing a user that wants access to the protected server, you can call the AccessCheck() API to determine if the client should be granted that access.
This process is explained in detail in the section below. Sample code is provided at the end of this article.
NOTE: It is assumed that you already have an understanding of Windows NT Security in general, and Access Control Security in particular.
To build a simple secure server, you need to follow the following steps:
1. Build a SD and a DACL for it that defines what users\groups have rights
to the secured portion of the server. The DACL should contain, at the
minimum, an Access Allowed ACE for every user or group that should be
allowed access to the protected portion of the server. Each ACE will
specify an Access Privilege level that the server defines.
The SD will normally be reusable by the server and should be saved
so that it can be reused every time the server runs.
InitializeSecurityDescriptor returns a SD in absolute format, which is
unsuitable for storing in a file or in the registry. Use the
MakeSelfRelativeSD() API to convert the absolute SD to a self-relative
format.
NOTE: The Private Object Security functions might be useful if you have
a large number of objects to secure that are hierarchical in
relationship, for example, a directory tree.
CreatePrivateObjectSecurity() allows you to pass creator and parent SDs
that give the object security based on inheritable ACEs contained in
these SDs.
2. Get the client's token. For example, if client applications connect to
the server by way of Named Pipes, then you can use the
ImpersonateNamedPipeClient() API to impersonate the client. Once you are
impersonating, you can get the clients token by calling
OpenThreadToken(GetCurrentThread()..).
3. When a client requests access to a protected part of the server, call
AccessCheck() using the client token and the SD described above. Access
can then be granted or denied based on the results.
The sample code below shows how to construct an SD, build a simple DACL,
and how to call the AccessCheck() API.
/*
The following sample code demonstrates how to use the AccessCheck API
to determine if a client token has sufficient access to perform some
operation against an object protected by a Security Descriptor or
to determine what the client's maximum access is on that same object.
This code sample requires the following import library:
advapi32.lib
David Mowers (davemo) 15-May-97
*/
#include <windows.h>
#include <stdio.h>
//
// Make up some private access rights.
//
#define ACCESS_READ 1
#define ACCESS_WRITE 2
void main( int argc, char *argv[] )
{
PSECURITY_DESCRIPTOR psdSD;
// User\SID Variables:
#define BUF_SIZE 256 // should be dynamic
HANDLE hToken;
TOKEN_USER ptuUser[BUF_SIZE];
DWORD cbBuffer=BUF_SIZE;
PSID pUserSid;
// ACE variables:
DWORD dwAccessMask=ACCESS_READ | ACCESS_WRITE;
PACL pACL;
DWORD dwACLSize;
// AccessCheck variables:
DWORD dwAccessDesired;
PRIVILEGE_SET PrivilegeSet;
DWORD dwPrivSetSize;
DWORD dwAccessGranted;
BOOL fAccessGranted=FALSE;
GENERIC_MAPPING GenericMapping;
//
// Get a SID for later use. A real server would use an API
// like LookupAccountName() to get SIDs that you will use to
// build your access control list.
//
OpenProcessToken(GetCurrentProcess(),TOKEN_READ,&hToken);
GetTokenInformation(hToken,
TokenUser,
ptuUser,
cbBuffer,
&cbBuffer);
pUserSid = ptuUser->User.Sid;
CloseHandle(hToken);
//
// Build a Security Descriptor.
//
psdSD = LocalAlloc(LPTR,SECURITY_DESCRIPTOR_MIN_LENGTH);
if(!InitializeSecurityDescriptor(psdSD,SECURITY_DESCRIPTOR_REVISION))
{
printf("Error %d:InitializeSecurityDescriptor\n",GetLastError());
}
//
// Compute size needed for the ACL.
//
dwACLSize = sizeof(ACCESS_ALLOWED_ACE) + 8 +
GetLengthSid(pUserSid) - sizeof(DWORD);
//
// Allocate memory for ACL.
//
pACL = (PACL)LocalAlloc(LPTR, dwACLSize);
//
// Initialize the new ACL.
//
if(!InitializeAcl(pACL, dwACLSize, ACL_REVISION2))
{
printf("Error %d:InitializeAcl\n",GetLastError());
}
//
// Add the access-allowed ACE to the DACL.
//
if(!AddAccessAllowedAce(pACL,ACL_REVISION2,dwAccessMask, pUserSid))
{
printf("Error %d:AddAccessAllowedAce",GetLastError());
}
//
// Set our DACL to the SD.
//
if (!SetSecurityDescriptorDacl(psdSD,
TRUE,
pACL,
FALSE))
{
printf("Error %d:SetSecurityDescriptorDacl",GetLastError());
}
//
// AccessCheck is picky about what is in the SD. Set
// the group and owner using our convenient SID.
//
SetSecurityDescriptorGroup(psdSD,pUserSid,FALSE);
SetSecurityDescriptorOwner(psdSD,pUserSid,FALSE);
//
// AccessCheck requires an impersonation token.
// For demonstration purposes, we are going to impersonate
// ourselves.
// A real server would impersonate the client using
// ImpersonateNamedPipeClient(),RPCImpersonateClient(),
// ImpersonateLoggedOnUser()with a token obtained through
// LogonUser() or the SSPI API ImpersonateSecurityContext().
//
ImpersonateSelf(SecurityImpersonation);
OpenThreadToken(GetCurrentThread(),
TOKEN_ALL_ACCESS,
TRUE,
&hToken );
//
// Using AccessCheck, there are two different things we could
// do:
//
//
// 1. See if we have Read/Write access to the object.
//
dwAccessDesired = ACCESS_READ;
//
// Initialize GenericMapping structure to map all.
//
memset(&GenericMapping,0xff,sizeof(GENERIC_MAPPING));
GenericMapping.GenericRead = ACCESS_READ;
GenericMapping.GenericWrite = ACCESS_WRITE;
GenericMapping.GenericExecute = 0;
GenericMapping.GenericAll = ACCESS_READ | ACCESS_WRITE;
//
// This only does something if we want to use generic access rights,
// like GENERIC_ALL, in our call to AccessCheck. We are not.
//
MapGenericMask(&dwAccessDesired, &GenericMapping);
dwPrivSetSize = sizeof(PRIVILEGE_SET);
//
// Make the call.
//
if( !AccessCheck(psdSD,
hToken,
dwAccessDesired,
&GenericMapping,
&PrivilegeSet,
&dwPrivSetSize,
&dwAccessGranted,
&fAccessGranted ) )
printf("Error in AccessCheck : %lu\n", GetLastError());
else
{
if(fAccessGranted)
printf(" Access Was Granted Using Mask %lx\n",
dwAccessGranted);
else
printf("Access was NOT granted!\n");
}
//
// 2. See if what is the maximum access I am allowed.
//
dwAccessDesired = MAXIMUM_ALLOWED;
if( !AccessCheck(psdSD,
hToken,
dwAccessDesired,
&GenericMapping,
&PrivilegeSet,
&dwPrivSetSize,
&dwAccessGranted,
&fAccessGranted ) )
printf("Error in AccessCheck : %lu\n", GetLastError());
else
{
if(fAccessGranted)
printf(" Maximum Access Allowed = %lx\n", dwAccessGranted);
}
RevertToSelf();
LocalFree(pACL);
LocalFree(psdSD);
}
Keywords : kbcode kbprg kbAPI kbKernBase kbGrpKernBase
Version : 4.0
Platform : NT WINDOWS
Issue type : kbhowto
Last Reviewed: July 13, 1997