Introduction
Every now and then we are faced with a question concerning our web applications which is, "What kind of permissions the a user has on a certain file or folder?". The answer to this question assists 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 be able to read the contents of a file but don�t allow them to make any modifications to it. So if you know beforehand 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 along that path. There is nothing worse than a frustrated user who sees the error message "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 a component that can 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 the 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 users or groups have 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, the component implements the 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 this project, this step has been implemented in the 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. The Platform SDK provides the following APIs to get SD.
GetSecurityInfo
GetNamedSecurityInfo
We have used the earlier version of the API. It returns SD and DACL associated with the SD. Take a look at the SDK documentation to get more information on their usage and their limitations. In the project this step has been implemented in the 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 exist in the DACL that was obtained in step 2. We have made use of the 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 user, group or logon session information. There are four different flavors of the API to build this structure.
BuildTruteeWithSid
BuildTrusteeWithName
BuildTrusteeWithObjectsAndName
BuildTrusteeWithObjectsAndSid
We have used the first flavor to construct a TRUSTEE
structure with a SID.
BuildTrusteeWithSid(pTrustee, pUserSID);
The GetEffectiveRightsFromAcl
function returns a DWORD
value indicating the access mask. This mask can be used to check if a specified right exists or not. This step has been implemented in the 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 The Component
As stated earlier, the component has been implemented as an ASP COM component. It contains the ITrusteeUtil
interface which implements a couple of interfaces. For checking the access rights, you need to call the CheckPermissionsOnFile
method. The third parameter is the complete file or folder path for which you want to check the rights. The fourth parameter is the access mask you want to check for. This mask can be constructed using the same values as specified in the 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 the 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(
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 implemention and improvise as per your requirements. For suggestions and questions, please feel free to contact us at softomatix@pardesiservices.com or visit us at Softomatix