Help desks often are responsible for creating home directories and setting NTFS permissions. Here's a script that provides a Web form that your Help desk staff or a similar group can use to perform these common tasks. The Web form uses Active Server Pages (ASP), Windows Management Instrumentation (WMI), and the Scripting Runtime Library's FileSystemObject object. (If you're unfamiliar with these technologies, see the Web exclusive sidebar "Resources for the Script's Technologies.")
You can find the script HomeFolderPerms.asp and its style sheet, StyleSheet.css, in the Code Library on the Windows Scripting Solutions Web site (http://www.winscriptingsolutions.com). Here's a look at how HomeFolderPerms.asp works and how you customize the script and the scripting environment so that you can use the Web form in your Windows 2000 network.
Taking the Bird's-Eye View
The Web scripting environment needed to create home directories and set NTFS permissions from a Web form is fairly complex. The complexity results from ensuring that only authorized personnel can run the script. By default, any member of the Administrators group in the domain can run the script, but making all your Help desk staff members part of this group is unrealistic and unsafe. Thus, I designed the script and set up the scripting environment so that a small group of Help desk staff members have enough privileges to successfully run the script. I call this group the HelpDesk group.
When a HelpDesk group member enters the script's URL in a browser, HomeFolderPerms.asp runs and displays the Web form that Figure 1 shows. After that person enters data into the form and clicks Submit, the script checks the form for appropriate and complete entries.
If the entries are OK, the script establishes two WMI namespace connections. The first connection is to the CIMv2 namespace on the file server containing the home directories. The second connection is to the Lightweight Directory Access Protocol (LDAP) namespace on the domain controller (DC) that contains the user accounts. After establishing the connections, the script checks whether the user account exists in the LDAP namespace. If the user account doesn't exist or the HelpDesk group member doesn't have sufficient privileges to access the LDAP namespace, the script informs the HelpDesk group member of the problem, then exits.
When the user account exists and the HelpDesk group member has sufficient privileges, the script checks whether the parent directory exists. If it doesn't or if the HelpDesk group member doesn't have sufficient permissions to the file system, the script informs the HelpDesk group member of the problem, then exits. When the parent directory exists and the HelpDesk group member has sufficient privileges, the script checks whether the home directory exists. If it doesn't, the script creates it.
With the home directory in existence, the script prepares a Security Descriptor (SD) that will eventually contain the discretionary ACL (DACL) for the home directory. The DACL is simply an array of access control entries (ACEs). The script then prepares the ACE, which contains the user account (i.e., the trustee) and the home-directory permissions that you want to assign the trustee.
At this point, the script assigns to a home directory the SD that contains the ACE, then applies the SD to the home directory. The action of replacing the default SD with the new SD removes inheritance from the parent directory. You'll likely want the parent directory's rights to flow to the users' home directories so that the script updates the home directory's DACL to allow inheritance. If you don't want to allow inheritance from the parent directory to users' home directories, you can have the HelpDesk group members clear the Allow inheritable permissions from parent to propagate to this directory check box that appears at the bottom of the form in Figure 1.
Taking a Closer Look
Now that you have an overall picture of how HomeFolderPerms.asp works, we'll take a closer look at the script. Let's examine the user-defined functions and subroutines that the script uses to
- build the form
- validate and assign the form's data
- connect to the provider
- perform file-system operations
- create the SD and its ACE
- assign the SD to the home directory
- allow inheritance from the parent directory
Building the Form
To build the Web form, the script uses the Form subroutine. The Form subroutine uses a mixture of VBScript and HTML code to create ASP pages; %> and <% tags enclose the VBScript code. You'll see that the %> tag appears right before the HTML form begins. Although you could use ASP syntax to write the form, I find that it's simpler to exit the scripting language, use HTML syntax to build the form, then return to VBScript to end the procedure. I used the style sheet (i.e., StyleSheet.css) to enhance the form's appearance.
Validating and Assigning the Data
After a HelpDesk group member completes the form, he or she clicks Submit. The Submit event prompts the FeedbackForm_OnSubmit function into action. This function determines whether the person has filled in the three required text boxes (i.e., Local domain controller, User account name, and Path to parent directory). If a required text box is empty, the function displays a message box that details the information the person needs to provide. The FeedbackForm_OnSubmit function also determines whether the Path to parent directory text box contains a properly formatted path. If the path is incorrect, the function displays a message box that provides the proper format for the path. After the HelpDesk group member clicks OK in the message box, the FeedbackForm_OnSubmit function places the cursor in the appropriate text box in the form so that the person can enter or modify the information.
After validating the data, the script assigns the data to variables. Before assigning the string in the Path to parent directory text box to a variable, the script uses the AdjustPathSpec subroutine to remove any backslash at the end of the string. Although a backslash at the end of a path is syntactically legal, the script uses paths without them.
Connecting to the Provider
HomeFolderPerms.asp uses two WMI monikers, which Web Listing 1 shows, to establish connections to the WMI namespaces on the appropriate remote computers. Each moniker contains
- the mandatory prefix winmgmts:.
- an optional security setting that authenticates the namespace and sets specific permissions. In both strings, the security level is impersonate. This setting instructs the namespace connection string to use the name and password of the currently logged-on user to establish a connection with the namespace.
- an optional namespace path that contains the management data that the script uses. If you don't specify this path, the connection string chooses the default namespace configured on each computer running WMI. Win2K's default namespace is CIMv2. Because administrators might change a computer's default namespace, specifying the namespace path is safer than using the default. Specifying the path also helps anyone viewing the script understand to which namespace path the script connects.
The first WMI moniker connects to the CIMv2 namespace on the server containing the home directories. The sFileServer variable obtains its value from the Server name text box in the form. The second moniker connects to the LDAP namespace on the DC containing the user account that will be assigned permission to a home directory. The sDomainController variable obtains its value from the Local domain controller text box in the form.
Connecting to the LDAP namespace serves two purposes. First, this connection lets the script determine whether the user account exists. If the account doesn't exist, the script exits. When the user account exists, the script uses the QuerySid function to obtain the user's SID. You need the user's SID to assign the user as a trustee in an ACE.
The script sets the retrieved SID to the SidVal variable. If the QuerySid function doesn't return a SID, the script sets the value of 0 or vbEmpty to the SidVal variable, displays an error message, then exits. In this case, the error message informs the HelpDesk group member that this failure occurred because either the user account doesn't exist or the person doesn't have sufficient permissions to check the directory.
Performing File-System Operations
After obtaining the SID, the script uses the CheckForFolder and CheckOrCreateHomeFolder subroutines to perform file-system operations. Both subroutines use the FileSystemObject object.
The CheckForFolder subroutine uses the FileSystemObject object's FolderExists method to make sure the parent directory for the home directories exists. If the HelpDesk group member enters a nonexistent parent directory in the Path to parent directory text box or doesn't have sufficient permissions to access the parent directory, the script exits. If you don't want to give HelpDesk group members permission to access the parent directory, you can configure hidden shares. I explain how to create the hidden shares in the "Setting Up the Scripting Environment" section.
When the parent directory exists and is accessible, the CheckOrCreateHomeFolder subroutine uses the FileSystemObject object's FolderExists method to check for the existence of a home directory that has the same name as the user account. If that home directory doesn't exist, the CheckOrCreateHomeFolder subroutine uses the FileSystemObject object's CreateFolder method to create it.
Creating the SD and Its ACE
The script uses the WMI scripting API to create an SD and add an ACE to that SD. First, the script creates a Win32_SecurityDescriptor class object and prepares it to receive an ACE. Next, the script uses the SetACE function to add the ACE to the SD. You need to pass five parameters to the SetACE function. The first parameter is the WMI moniker that connects to the CIMv2 namespace, which I've already discussed. The other four parameters are AccessMask, AceFlags, AceType, and oTrustee.
The AccessMask value specifies the access rights, or access mask, that you want to assign to the ACE. In VBScript, an access mask is a bit-mask value in decimal notation. Web Table 1 lists the access mask constants that you can use. Although generic access rights are easier to use than object-specific access rights, you can't use generic access rights to set an access mask for an ACE. Consequently, the script uses object-specific access rights. To make the script easier to read and understand, I defined three custom constants to assign access rights. Each custom constant represents the sum of the object-specific access rights necessary to grant the permission. The custom constants and the access rights they represent are
- CUSTCON_READ—provides Read permission. The constant is the sum of the RIGHT_DS_CREATE_CHILD, RIGHT_DS_SELF, RIGHT_DS_LIST_OBJECT, RIGHT_READ_CONTROL, and RIGHT_SYNCHRONIZE access rights.
- CUSTCON_CHANGE—provides Change permission. The constant is the sum of all the object-specific access rights except RIGHT_WRITE_DAC and RIGHT_WRITE_OWNER.
- CUSTCON_FULL_CONTROL—provides Full Control permission. The constant is the sum of all the object-specific access rights.
By looking at the binary or decimal values, you can easily see how the custom constants represent the sum of the desired individual access rights.
The AceFlags value specifies how you want the ACE to behave when applied to the SD. Table 1 contains the ACE flag constants that you can use. Three flags are common to a SD on a directory that allows inheritance. Those constants are
- SE_DACL_PRESENT—the DACL is present in the SD. If you don't set this flag or if you set this flag but the DACL is NULL, the SD allows full access to everyone.
- SE_DACL_AUTO_INHERITED—the DACL in the SD supports automatic propagation of inheritable ACEs to existing child objects.
- SE_SELF_RELATIVE—the SD stores all of its security information in a continuous block of memory. An SD stored in this way is considered self-relative. Otherwise, an SD on an object is considered absolute when it contains pointers to its security information.
To make the script easier to read, I defined the custom constant of CUSTCON_ALLOW_INHERIT, whose sum represents these three flags. This custom constant configures the ACE so that the DACLs below the home directory inherit the ACE.
The AceType value specifies whether the ACE will have Allow or Deny type permissions. In the script, I use the ACETYPE_ACCESS_ALLOWED constant to specify Allow type permissions. In other words, the subdirectories below the user account's home directory will inherit the ACE and the user account will have Allow type permissions.
The oTrustee value specifies the trustee that you want to assign to the ACE. To assign a trustee to the ACE, the SetACE function needs the trustee's user-account name, domain name, and SID. The SetTrustee function neatly compartmentalizes the trustee information for the SetACE function.
The SetTrustee function takes four parameters: the moniker to the CIMv2 namespace and the trustee's domain name, user-account name, and SID. The script obtains the domain name from the Web form's User account domain text box and the user-account name from the form's User account name text box. The script obtains the SID from the QuerySid function that I described earlier.
Using the information that the SetTrustee function provides, the SetACE function creates an ACE, then assigns the ACE to the SD. More specifically, the script creates an array object (the ACE) and assigns it to the DACL property of the SD object.
Assigning the SD
At this point, the script uses WMI to assign the SD to the home directory. First, the script creates a Win32_Directory class object. This object receives the SD, which contains the ACE. Then, the script assigns the SD to the directory, thereby replacing the default SD. If you look at the local permissions assigned to the home directory, you'll see that the DACL contains one ACE and that inheritance is blocked. With this setup, the trustees assigned to higher folders in the directory hierarchy aren't assigned to this home directory.
In most network environments, the permissions assigned to the parent directory of home directories must flow to all subdirectories. This setup facilitates home directory administration tasks. The Web form lets the HelpDesk group members choose whether to allow inheritance from the parent directory. When the Allow inheritable permissions from parent to propagate to this directory check box is selected, the script configures the DACL to allow inheritance. To achieve this configuration, the script creates a Win32_LogicalFileSecuritySetting class object that contains the DACL of the home directory. After obtaining the DACL, the script assigns the CUSTCON_ALLOW_INHERIT constant to the Win32_LogicalFileSecuritySetting object's ControlFlags property to allow inheritance.
The script then calls the Win32_LogicalFileSecuritySetting object's SetSecurityDescriptor method to apply the change to the DACL of the home directory. Finally, the script informs the HelpDesk group member that it granted the permission, after which it removes any remaining objects it created from the system's memory.
Customizing the Script
Now that you know how HomeFolderPerms.asp works, let's look at how to customize the script and the scripting environment so that you can implement it in your Win2K Service Pack 1 (SP1) or later network. To use the script, you must have WMI installed. Here are the steps you need to take so that the script operates properly in your network:
- Customize the domain list. The PROCEDURES section of the script includes the Form procedure; Listing 1 shows an excerpt of this procedure. The code at callout A in Listing 1 forms the User account domain drop-down list in Figure 1. In this code, you need to replace DOMAIN01 and DOMAIN02 with the names of your domains. If you need to add domains to the form, you can add more <OPTION> tags. If you have only one domain, you can remove the code at callout A and set the name of the domain to the sDomain variable. In the script, go to the Data initialization code in the MAIN SCRIPT BODY section. In the code replace Request.Form("DomainName") with the name of your domain.sDomain=Request.Form("DomainName")
- Customize the default DC. In the Form subroutine, locate the code that callout B shows. This code causes the text SERVER1 to appear by default in the Local domain controller text box in Figure 1. Replace SERVER1 with the name of the local DC that's closest to the HelpDesk group members.
- Customize the server list. In the Form subroutine, find the code that callout C shows. This code forms the Server name drop-down list in Figure 1. In this code, you need to replace SERVER1 and SERVER2 with the names of your servers. If you need to add servers to the form, you can add more <OPTION> tags. If you have only one server, you can remove the code at callout C and set the name of the server to the sFileServer variable. In the Data initialization code, find the line Replace Request.Form("ServerName") with the name of your server.sFileServer=Request.Form("ServerName")
- Customize the default path. In the Form subroutine, locate the code that callout D shows. This code causes the text e:\home to appear by default in the Path to parent directory text box in Figure 1. Replace e:\home with the path to the home directory that contains most of your users' home directories.
Setting Up the Scripting Environment
You now need to grant the HelpDesk group the permissions it needs to successfully run the script. After you customize HomeFolderPerms.asp, place the script and StyleSheet.css in a folder on a DC. Restrict folder access by granting the HelpDesk group only Read permission to the folder.
On the DC containing your user accounts, create a virtual directory in IIS. Point the virtual directory to the physical directory that contains the script and configure the virtual directory for the access permissions of Read and Run scripts. Then, go to Virtual Directories Properties and select the Directory Security tab. Select Integrated Windows authentication only and clear the Anonymous check box. If the DC contains home directories, grant the HelpDesk group all NTFS permissions except Full Control to the parent directory of your home directories.
Create a hidden share named xdrive$, where x is the drive containing your home directories. For example, if your home directories are on the E drive, you can create a hidden share named edrive$. Another option is to assign a more generic name to the hidden share, such as Users$. That way, if you need to move your home directories to another drive, all you need to do is reassign the share to the new drive. If you take this approach, you must modify the script. Instead of pointing the sParentPath variable to the sDriveLetter variable and the drive$ string, point it to the Users$ string.
Next, give the HelpDesk and Domain Admins groups Change and Read share access, and remove the Everyone group from the share's DACL. Although a hidden administrative share exists on each drive on a fixed disk of a computer running Win2K, you can't directly add groups or users to the administrative share's DACL. Repeat the steps of setting the NTFS permissions, creating the hidden share, and setting share access on all servers containing home directories.
On member servers that contain home directories, you must grant the HelpDesk group the Take ownership of files or other objects user right. If you don't grant this right, the HelpDesk group members won't be able to apply permissions to home directories that they don't already have permission to access.
Because the HelpDesk group will access the WMI namespace remotely, you must configure WMI security. Open Microsoft Management Console (MMC) on the DC on which the script will run. In the tree, expand Services and Applications, then select the WMI Control node. Right-click the node and select Properties. From the WMI Control Properties dialog box, click the Security tab and expand the Root folder. Select the CIMV2 folder, then click the Security button. Add the HelpDesk group, grant it Remote Enable permission, and click OK.
Next, expand the directory folder, select the LDAP folder, and assign Remote Enable permission to the HelpDesk group. This permission applies to the CIMv2 and LDAP namespaces only. Granting this permission lets the HelpDesk group members connect to and use these two namespaces from the script. Don't remove the Everyone group from the default permission settings inherited from the Root folder of the WMI namespace.
The Finishing Touches
With the script customized and the scripting environment set, double-checking a few items is a good idea. Here's a quick checklist:
- Make sure that the home directory servers and the DCs are running Win2K SP1 or later.
- To protect other directories that are peers of the parent directory, adjust NTFS permissions so that the HelpDesk group members don't gain access to restricted directories.
- Make sure you use local groups for all permissions settings. You can add a global group containing your staff members to the local group.
In addition to running through this checklist, you might want to implement a few improvements to HomeFolderPerms.asp. Possible improvements include:
- Use Dynamic HTML (DHTML) to change the value in the Path to parent directory text box when the value in the Server name drop-down list changes. For example, if Server1 contains home directories on E:\home and Server2 contains home directories on F:\homedirs, you can make the Path to parent directory text box show E:\home when Server1 is selected and F:\homedirs when Server2 is selected. You might also modify the script to find the proper file server based on the value in the User account name text box.
- Modify the script so that it creates a list of all user accounts, then uses that information to populate a drop-down list that replaces the User account name text box. If you decide to implement this suggestion, you might want to limit the list of user accounts displayed to one organizational unit (OU) at a time. Otherwise, the script might be slow to generate the complete list of user accounts.
I'm sure some of you will come up with even more ways to improve the script. With or without improvements, the information I presented here should get you well on your way to facilitating network administration with HomeFolderPerms.asp.