One feature that Exchange 2000 offers is the ability to set permissions at both the item and property level for documents or other sorts of objects contained in the Exchange 2000 database. This functionality is different from Exchange 5.5, which allows you to set permissions only at the folder level. Imagine what kind of impact this ability will have on your applications. Now you can secure your applications even further when data is stored in even a single folder. This section outlines how to use the new security features in Exchange 2000 and shows you a Web application that allows you to try out these new security features.
NOTE
If you set item level security in Exchange 2000 and the item is replicated to an Exchange 5.5. server, your security will not be enforced since Exchange 5.5 does not support item level security.
Exchange 2000 supports native Windows 2000 security descriptors. Using these descriptors, you can allow or deny access to an item or the item's properties, grant this access using Windows 2000 security identifiers, and access and set permissions by viewing and modifying the descriptor in an XML format from WebDAV or ADO/OLEDB. By providing you with an XML representation of the security descriptor, Exchange 2000 makes it very easy for you to work with security settings; you do not have to learn Windows API programming to change permissions. Furthermore, the technology in Exchange 2000 takes your XML descriptor and turns it into the correct binary representation of the descriptor in the Exchange database. You can access the XML formatted descriptor by querying for the property http://schemas.microsoft.com/exchange/security/descriptor . The following code is an example of what is returned on an item when you query for this property from ADO:
'ADO code Dim oRecord As New ADODB.Record oRecord.Open "file://./backofficestorage/domain/apps/items/exchange.eml" |
Following are the XML results:
<S:security_descriptor xmlns:S="http://schemas.microsoft.com/security/" xmlns:D="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" D:dt="microsoft.security_descriptor"> <S:revision>1</S:revision> <S:owner S:defaulted="0"> <S:sid> <S:string_sid>S-1-5-21-1659004503-152049171-1202660629-1110 </S:string_sid> <S:nt4_compatible_name>THOMRIZNT5DOM\thomriz</S:nt4_compatible_name> <S:user_principal_name>thomriz@thomriznt5dom.extest.microsoft.com</S:user_ principal_name> <S:display_name>Thomas Rizzo</S:display_name> </S:sid> </S:owner> <S:primary_group S:defaulted="0"> <S:sid> <S:string_sid>S-1-5-21-1659004503-152049171-1202660629-513 </S:string_sid> <S:nt4_compatible_name>THOMRIZNT5DOM\Domain Users </S:nt4_compatible_name> </S:sid> </S:primary_group> <S:dacl S:defaulted="1" S:protected="0" S:autoinherited="1"> <S:revision>2</S:revision> <S:effective_aces> <S:access_allowed_ace S:inherited="1"> <S:access_mask>1fcfff</S:access_mask> <S:sid> <S:string_sid>S-1-5-21-1659004503-152049171-1202660629-1110 </S:string_sid> <S:nt4_compatible_name>THOMRIZNT5DOM\thomriz</S:nt4_compatible_name> <S:user_principal_name>thomriz@thomriznt5dom.extest.microsoft.com </S:user_principal_name> <S:display_name>Thomas Rizzo</S:display_name> </S:sid> </S:access_allowed_ace> <S:access_allowed_ace S:inherited="1"> <S:access_mask>1fcfff</S:access_mask> <S:sid> <S:string_sid>S-1-5-21-1659004503-152049171-1202660629-1105 </S:string_sid> <S:nt4_compatible_name>THOMRIZNT5DOM\Domain EXServers </S:nt4_compatible_name> </S:sid> </S:access_allowed_ace> <S:access_allowed_ace S:inherited="1"> <S:access_mask>1fcfff</S:access_mask> <S:sid> <S:string_sid>S-1-5-21-1659004503-152049171-1202660629-500 </S:string_sid> <S:nt4_compatible_name>THOMRIZNT5DOM\Administrator</S:nt4_compatible_name> <S:display_name>Administrator</S:display_name> </S:sid> </S:access_allowed_ace> <S:access_allowed_ace S:inherited="1"> <S:access_mask>1fcfff</S:access_mask> <S:sid> <S:string_sid>S-1-5-21-1659004503-152049171-1202660629-519 </S:string_sid> <S:nt4_compatible_name>THOMRIZNT5DOM\Enterprise Admins </S:nt4_compatible_name> </S:sid> </S:access_allowed_ace> <S:access_allowed_ace S:inherited="1"> <S:access_mask>1fcfff</S:access_mask> <S:sid> <S:string_sid>S-1-5-21-1659004503-152049171-1202660629-512 </S:string_sid> <S:nt4_compatible_name>THOMRIZNT5DOM\Domain Admins </S:nt4_compatible_name> </S:sid> </S:access_allowed_ace> <S:access_allowed_ace S:inherited="1"> <S:access_mask>12088f</S:access_mask> <S:sid> <S:string_sid>S-1-1-0</S:string_sid> <S:nt4_compatible_name>\Everyone</S:nt4_compatible_name> </S:sid> </S:access_allowed_ace> </S:effective_aces> </S:dacl> </S:security_descriptor> |
This XML code is for a non-folder security descriptor—specifically, for an item. A folder security descriptor is a little bit different, but as you can see in the XML structure shown in the code above, a security descriptor is made up of a Discretionary Access Control List (DACL) which in turn is made up of Access Control Entries (ACE). Each ACE in the DACL either grants or denies a trustee a certain set of rights to the object. The access mask, which you see defined in the XML, describes the set of rights that are granted or denied to a user. Access masks are 32-bit numbers where the upper 16 bits describe generic rights and the lower 16 bits describe object specific rights.
Tables 19-12, 19-13, and 19-14 outline the different sets of access rights you can have: standard access rights, access rights on folders, and on non-folders (items), respectively. Values from each table can be combined and put into the access mask to create the correct security descriptor for the user.
Table 19-12. Standard rights (Non-Exchange, Windows 2000).
Name | Value (Hex) | Description |
---|---|---|
fsdrightDelete | 0x00010000 | Delete |
fsdrightReadControl | 0x00020000 | Read access to security descriptor and owner |
fsdrightWriteSD | 0x00040000 | Write DACL permissions |
fsdrightWriteOwner | 0x00080000 | Used to assign write owner |
fsdrightSynchronize | 0x00100000 | Used to synchronize access to the object |
If you were to create an access mask with all the properties in Table 19-12, the value would be 0x1F0000.
Table 19-13. Folder rights.
Name | Value (Hex) | Description |
---|---|---|
fsdrightListContents | 0x00000001 | Right to list contents of the directory. |
fsdrightCreateItem | 0x00000002 | Right to add a file to the folder. |
fsdrightCreateContainer | 0x00000004 | Right to add a subfolder |
fsdrightReadProperty | 0x00000008 | Right to read extended attributes |
fsdrightWriteProperty | 0x00000010 | Right to write extended attributes |
fsdrightReadAttributes | 0x00000080 | Right to read file attributes. Currently unused. |
fsdrightWriteAttributes | 0x00000100 | Right to change file attributes. Currently unused. |
fsdrightWriteOwnProperty | 0x00000200 | Right to modify own items (Exchange specific property) |
fsdrightDeleteOwnItem | 0x00000400 | Right to delete own items |
fsdrightViewItem | 0x00000800 | Right to view items (Exchange specific) |
fsdrightOwner | 0x00004000 | Owner of the folder. Provided for backwards compatability. |
fsdrightContact | 0x00008000 | Contact for the folder. Provided for backwards compatability. |
If a user has all rights in Table 19-13, the value of the mask would be 0xCFFF.
Table 19-14. Item rights.
Name | Value (Hex) | Description |
---|---|---|
fsdrightReadBody | 0x00000001 | Right to read data from a file. |
fsdrightWriteBody | 0x00000002 | Right to write data to a file. |
fsdrightAppendMessage | 0x00000004 | Same as fsdrightWriteBody. Not currently used. |
fsdrightReadProperty | 0x00000008 | Right to read extended attributes. |
fsdrightWriteProperty | 0x00000010 | Right to write extended attributes. |
fsdrightExecute | 0x00000020 | Right to execute a file. Currently not used. |
fsdrightReadAttributes | 0x00000080 | Right to read file attributes. |
fsdrightWriteAttributes | 0x00000100 | Right to change file attributes. |
fsdrightWriteOwnProperty | 0x00000200 | Right to modify own item (Exchange-specific). |
fsdrightDeleteOwnItem | 0x00000400 | Right to delete own item (Exchange-specific). |
fsdrightViewItem | 0x00000800 | Right to view item (Exchange-specific). |
If a user has all the rights in Table 19-14, the value would be 0x0FBF.
As you can see in the preceding XML code example, you can have an access_allowed_ACE, which contains the access mask for the rights you are going to allow for the user on the item or folder. You can also have an access_denied_ACE, which specifies an access mask that contains the rights you are going to deny for the user on the folder or item. If the user has all rights, as in the preceding XML example, the access mask will be 1FCFFF, which means all rights from all the tables above are granted to the user. Creating the access mask is just a matter of adding the hexidecimal values from the tables together and creating the ACE for the user. We'll see an example on how to create an ACE later in this chapter using the XMLDOM.
One fact you need to know even before we drill down into a sample application is that there are certain rights that the user needs to be able to access the security descriptor. These rights are fsdrightReadControl, fsdrightWriteSD, and fsdrightWriteOwner.
One of the sample applications included on the companion CD will, I know, make using the XML security descriptor much easier for you. The security sample application is a Web application that highly leverages the XMLDOM, XSL as well as WebDAV to show you how to work with and set the XML security descriptors in Exchange 2000. Figure 19-15 shows the main interface for the security application.
Figure 19-15. The main interface for the security application.
One task to try when working with the security application is restricting who can view items in an Exchange folder. The way to do this in the application is included in the next bit of code. As you can see in Figure 19-16, the user can view the document named ADO and Exchange 2000. However, after applying the setting that denies this user access to the document, as shown in Figure 19-17, the user can no longer see that specific document but can see all the other documents contained in the folder. Figure 19-17 shows what the user sees after applying the security change.
The application works by combining the XMLDOM, XSL, and WebDAV to get, display, and set both item and property_level security in Exchange. As you can see from the following code snippet, when you click the OK button to retrieve the security for an object in Exchange, the application does a PROPFIND on the http://schemas.microsoft.com/exchange/security/descriptor property. The application then uses XSL to display the HTML that you see in the browser.
Figure 19-16. Before applying the security settings, the user can view the full list of documents.
Figure 19-17. After applying the security settings, the user can no longer view the restricted files.
function cmdGetSec.onclick() { var strReqBody = ""; if(txtUrl.value == "") { alert("Please Enter or select a valid URL."); return; } strReqBody = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<propfind xmlns=\"DAV:\">" + "<prop xmlns:r=\"http://schemas.microsoft.com/exchange/security/\">" + " <r:descriptor/>" + "</prop>" + "</propfind>" DAVRequest.open("PROPFIND", txtUrl.value, false); DAVRequest.setRequestHeader("Content-Type", "text/xml"); DAVRequest.setRequestHeader("Depth", "0"); DAVRequest.setRequestHeader("Translate", "f"); DAVRequest.send(strReqBody); if(chkMultiStatusForErr(DAVRequest) >= 0) { xmlResponse = DAVRequest.responseXml; perm_entries_dest.innerHTML = xmlResponse.transformNode(xslPerm_Entries); ace_entries_dest.innerHTML = "<div/>"; ace_edit_dest.innerHTML = "<div/>"; add_dest.innerHTML = "<DIV/>"; xmlResponse.transformNodeToObject(xslAceEntries.documentElement, xmlAceEntries); HiLitePermsTable(); propfindForResources(); cmdAdd.disabled = false; cmdRemove.disabled = true; cmdAceApply.disabled = true; cmdAceCancel.disabled = true; strOwner = owner.innerText; } } |
To add a new Access Control Entry, all the application does is take your entered data and turn it into a new XML node that it adds to the XML security descriptor. To do this, the application fills in the required properties to correctly generate a new ACE such as NT4 compatible name, the access mask, as well as the ACE type. The code that does the XML work is shown here:
function addAce(strUserName, strMask, strPropName, strApplyTo, _ strRoleProp, strRoleScope, fbAllow) { // Basic variable declarations. var baseNode = null; var nodeFirstAce = null; var nodeSubNode = null; var nodeNewNode = null; var nOrder = 0; nOrder = getAceOrder(strUserName, null, strPropName, strApplyTo, strRoleProp, strRoleScope, fbAllow); while(nOrder > -1) { removeAce(nOrder); nOrder = getAceOrder(strUserName, strMask, strPropName, strApplyTo, strRoleProp, strRoleScope, fbAllow); } // Establish the base node. baseNode = xmlAceEntries.documentElement; // Create the ace node. nodeNewNode = xmlAceEntries.createNode(1, "ace", ""); // Add the NT4_Compatible_Name sub node. nodeSubNode = xmlAceEntries.createNode(1, "NT4_Compatible_Name", ""); nodeSubNode.text = strUserName; nodeNewNode.appendChild(nodeSubNode); // Add the Access_Mask sub node. nodeSubNode = xmlAceEntries.createNode(1, "Access_Mask", ""); nodeSubNode.text = strMask; nodeNewNode.appendChild(nodeSubNode); // Add the Ace_Type sub node. nodeSubNode = xmlAceEntries.createNode(1, "Ace_Type", ""); nodeSubNode.text = getAceType(strPropName, fbAllow); nodeNewNode.appendChild(nodeSubNode); // Add the Apply_To sub node. nodeSubNode = xmlAceEntries.createNode(1, "Apply_To", ""); nodeSubNode.text = strApplyTo; nodeNewNode.appendChild(nodeSubNode); // Add the Property_Name sub node. nodeSubNode = xmlAceEntries.createNode(1, "Property_Name", ""); nodeSubNode.text = strPropName; nodeNewNode.appendChild(nodeSubNode); if(strRoleScope != null && strRoleScope != "") { nodeSubNode = xmlAceEntries.createNode(1, "Role_Scope", ""); nodeSubNode.text = strRoleScope; nodeNewNode.appendChild(nodeSubNode); } if(strRoleProp != null && strRoleProp != "") { nodeSubNode = xmlAceEntries.createNode(1, "Role_Property", ""); nodeSubNode.text = strRoleProp; nodeNewNode.appendChild(nodeSubNode); } baseNode = xmlAceEntries.documentElement; baseNode.appendChild(nodeNewNode); refreshAceList(); } |
To set the security, the application does a PROPPATCH to the same property but with the new security descriptor formatted as XML and the new security added as XML nodes.
function cmdAceApply.onclick() { var xmldomAce = new ActiveXObject("Microsoft.XMLDOM"); var xmlAccessMaskNode = null; var xmlAceTypeNodes = null; var xmlAceTypeNode = null; var xmlUserNameNode = null; var xmlApplyToNode = null; var xmlPropNode = null; cmdAceApply.disabled = true; cmdAceCancel.disabled = true; addAcesOutstandingAcesToXML(); xmlAceEntries.transformNodeToObject(xslProppatch.documentElement, xmldomProppatch); DAVRequest.open("PROPPATCH", txtUrl.value, false); DAVRequest.setRequestHeader("Content-Type", "text/xml"); DAVRequest.setRequestHeader("Translate", "f"); DAVRequest.send(xmldomProppatch); if(chkMultiStatusForErr(DAVRequest) >= 0) { HiLitePermsTable(); if(nCurrentAce > -1) { xmldomAce.loadXML( xmlAceEntries.childNodes(0).childNodes(nCurrentAce).xml); ace_entries_dest.innerHTML = xmldomAce.transformNode(xslDips_ACE_Info); ace_edit_dest.innerHTML = xmldomAce.transformNode(xslACE_Edit.documentElement); perm_entries_dest.innerHTML = xmldomProppatch.transformNode(xslPerm_Entries); HiLiteRow(thetable, nCurrentAce+1); } } cmdGetSec.onclick(); return ""; } |