Windows 2000 Pro Windows XP Pro Windows 2000 Adv Server
Windows .NET Ent Server (Build 3604)
Every now and then we face with a question on our web applications, “What kind
of permissions the a user has on a certain file or folder?” And answer to this
question helps in determining what kind of actions a user can or can’t perform
on that object. For example in your web site you want certain users to only
read contents of a file but don’t allow them to make any modifications to it.
So if you know before hand that the logged in user does not have write
permissions on the file, you can hide or not present them with the buttons or
controls that will lead them into that path. There is nothing worst than a
frustrated user who sees the error message that “You are not allowed to perform
this operation”. It is always the best practice not to present a feature that a
user can’t perform.
This article is an attempt to implement one such component that check if a
particular domain user has certain privileges on a file or folder or not. There
are some operations that can be performed through ADSI interfaces. It is fine
to use ADSI on a Domain Controller or network of servers. But when you are
dealing with only one server machine, then all calls get routed through Net API
calls. We have tried to implement this component using the low level Net API
calls. No ADSI interface has been made use of.
Security Background
Whenever an object (folder, file, handle, mutex, etc.) is created in Windows
environment, it is associated with a Discretionary Access Control List (DACL).
The DACL contains a list of Access Control Entries (ACE). Each ACE specifies
who is allowed to access this object and what kind of access certain user or
group has on the object.
Therefore the idea behind this implementation is to get hold of the DACL of an
object and then check if particular user’s access rights exists in the DACL or
not.
Implementation
This component is developed as an ATL/COM ASP component. Since we said ATL, the
implementation is done using C++. Since it is implemented as an ASP component,
which means that the component implements IDispatch interface so that late
binding clients like javascript, vbscript, VB, etc. can use it.
The following steps describe the algorithm used to accomplish our tasking of
checking a user’s rights on an object.
-
Given a user’s logon ID, get the corresponding SID. Make use of
LookupAccountName
API to accomplish this step.
bRetVal = ::LookupAccountName((strDCServer.length() == 0) ? NULL : strDCServer.c_str(),
strUser.c_str(),
pUserSID,
&cbUserSID,
pszDomain,
&cbDomain,
&snuType);
In the project, this step has been implemented in GetUserSID function.
Take a look at the source code for more implementation details.
-
Next you need to get the SD and DACL associated with the object. Platform SDK
provides the following APIs to get SD.
GetSecurityInfo
GetNamedSecurityInfo
We have used the earlier version of API. It returns SD and DACL associated with
the SD. Take a look at the SDK documentation to get more information their
usage and their limitations. In the project this step has been implemented in
GetFileSD function.
dwErr = ::GetSecurityInfo(hFile,
SE_FILE_OBJECT,
secInfo,
NULL,
NULL,
pACL,
NULL,
pFileSD);
-
The next step is to check if the specified access rights in the DACL that was
obtained in step 2. We have made use high-level
GetEffectiveRightsFromAcl
security API to get the mask describing the actual rights the user has on the
given object.
ACCESS_MASK mask;
dwRetVal = ::GetEffectiveRightsFromAcl( pACL,
pTrustee,
&mask);
The second parameter to this API is a pointer to TRUSTEE structure. This
structure specifies a user, group or logon session information. There are four
different flavors of API to build this structure.
BuildTruteeWithSid
BuildTrusteeWithName
BuildTrusteeWithObjectsAndName
BuildTrusteeWithObjectsAndSid
We have used the first flavor of constructing TRUSTEE structure with SID.
BuildTrusteeWithSid(pTrustee, pUserSID);
The GetEffectiveRightsFromAcl returns a DWORD value indicating
access mask. This mask can be used to check if a specified right exists or not.
This step has been implemented in GetUserRights function of the
class.
-
The last step is to check the mask obtained in step 3.
lRetMask = this->GetUserRights(pUserSID, pFileDACL);
if (lRetMask & lAccessToCheck)
{
*pbOK = TRUE;
}
else
{
*pbOK = FALSE;
}
Using Component
As stated earlier, the component has been implemented as an ASP COM component.
It contains ITrusteeUtil interface implementing couple of interfaces. For
checking the access rights, you need to call CheckPermissionsOnFile method. The
third parameter is the complete file pr folder path for which you want to check
the rights. And the fourth parameter is the access mask you want to check for.
This mask can be constructed using the same values as specified in WinNT.h
file. Some of the values are as follows.
#define GENERIC_READ (0x80000000L)
#define GENERIC_WRITE (0x40000000L)
#define GENERIC_EXECUTE (0x20000000L)
#define GENERIC_ALL (0x10000000L)
We have included a file named Masks.txt in the project that contains
some of the values. You can take a look at it to specify the access mask to
method.
For example to check if a user has write permissions on a folder, you can use
it as follows.
<%
var hasWriteAccess;
obNet = Server.CreateObject('Pardesi.TrusteeUtil');
hasWriteAccess = obNet.CheckPermissionsOnFile("foo", "bar", "C:\\DataFiles", 0x0002)
Response.Write(hasWriteAccess);
Response.End();
%>
Road Map
This component is still under development and being tested under various
environments. Feel free to use and take a look at the implement and improvise
as per your requirement. For suggestions and questions, please feel free to
contact us at softomatix@pardesiservices.com