Are you a world traveler? ZoneTick is a cool utility that'll help you stay in touch over multiple time zones!
 
Modifying Access to Directory Objects  
Nik Okuntseff  MS Exchange Server Programming 

Modifying Access to Directory Objects

How can we modify access rights to a Directory object? Obviously, we need to change the security descriptor associated with the object or its parent. Let's suppose that we want to allow everyone access to a mailbox. The program below (Security/Modify) does it.

#include <afxwin.h>
#include <dapi.h>
#include <edk.h>
 

int SetDirObjectSD(char * pszServer, char * pszDirectoryName, void * pSelfRelSD)
{
    /*
     * Algorithm:
     *  1. Do DAPIStart.
     *  2. Do DAPIWrite to set security descriptor.
     *  3. Do DAPIEnd.
     */

    DAPI_PARMS parms = {0};
    parms.dwDAPISignature = DAPI_SIGNATURE;
    parms.pszDSAName = pszServer;
    parms.pszContainer = "Recipients";
 
    PDAPI_EVENT pDAPIEvent = NULL;
    DAPI_HANDLE hDAPISession = NULL;

    // Initialize DAPI
    pDAPIEvent = ::DAPIStart(&hDAPISession, &parms);
    if (pDAPIEvent)
    {
        DAPIFreeMemory(pDAPIEvent);
        return -1;
    }
 
    // We have DAPI session. Use it now for read.
    DAPI_ENTRY * pdeValues = NULL;
    DAPI_ENTRY * pdeAttributes = NULL;

 
    ATT_VALUE avAttrName[5];
    ATT_VALUE avAttrValue[5];

    avAttrName[0].DapiType = DAPI_STRING8;
    avAttrName[0].Value.pszA = "Obj-Class";
    avAttrName[0].size = 9;
    avAttrName[0].pNextValue = NULL;

    avAttrName[1].DapiType = DAPI_STRING8;
    avAttrName[1].Value.pszA = "Directory Name";
    avAttrName[1].size = 14;
    avAttrName[1].pNextValue = NULL;

    avAttrName[2].DapiType = DAPI_STRING8;
    avAttrName[2].Value.pszA = "Home-Server";
    avAttrName[2].size = 11;
    avAttrName[2].pNextValue = NULL;
 
    avAttrName[3].DapiType = DAPI_STRING8;
    avAttrName[3].Value.pszA = "NT-Security-Descriptor";
    avAttrName[3].size = 22;
    avAttrName[3].pNextValue = NULL;

    avAttrValue[0].DapiType = DAPI_STRING8;
    avAttrValue[0].Value.pszA = "Mailbox";
    avAttrValue[0].size = 8;
    avAttrValue[0].pNextValue = NULL;

    avAttrValue[1].DapiType = DAPI_STRING8;
    avAttrValue[1].Value.pszA = pszDirectoryName;
    avAttrValue[1].size = strlen(pszDirectoryName);
    avAttrValue[1].pNextValue = NULL;
 
    avAttrValue[2].DapiType = DAPI_STRING8;
    avAttrValue[2].Value.pszA = "~SERVER"; // Or use explicit server name here
    avAttrValue[2].size = 7;
    avAttrValue[2].pNextValue = NULL;

    avAttrValue[3].DapiType = DAPI_BINARY;
    avAttrValue[3].Value.lpBinary = (BYTE *) pSelfRelSD;
    avAttrValue[3].size = GetSecurityDescriptorLength(pSelfRelSD);
    avAttrValue[3].pNextValue = NULL;

    // Set terminators
    ZeroMemory(&avAttrName[4], sizeof(ATT_VALUE));
    ZeroMemory(&avAttrValue[4], sizeof(ATT_VALUE));

    DAPI_ENTRY deAttributes;
    DAPI_ENTRY deValues;

    deAttributes.unAttributes = 4;
    deAttributes.ulEvalTag = TEXT_VALUE_ARRAY;
    deAttributes.rgEntryValues = &avAttrName[0];
 
    deValues.unAttributes = 4;
    deValues.ulEvalTag = VALUE_ARRAY;
    deValues.rgEntryValues = &avAttrValue[0];
 
    char * pAccount = NULL;
    char * pPassword = NULL;
    ULONG ulUSN = 0;
    pDAPIEvent = ::DAPIWrite(hDAPISession,
        DAPI_WRITE_UPDATE,
        &deAttributes,
        &deValues,
        &ulUSN,
        &pAccount,  // Account
        &pPassword); // Password
    if (pDAPIEvent)
    {
        DAPIFreeMemory(pDAPIEvent);
        return -1;
    }

    // Terminate DAPI session
    DAPIEnd(&hDAPISession);
    return 0;
}
 

int main( void )
{
    char * pszServer = "MIG";   // Change this
    char * pszDirectoryName = "NikO"; // Change this

    /*
     * Algorithm:
     *  1. Create a security descriptor that allows access for everyone.
     *  2. Convert it to self-relative format.
     *  3. Call the SetDirObjectSD function.
     */
 
    // Start with security descriptor in absolute format
    SECURITY_DESCRIPTOR sd;
    BOOL b = InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
    assert(b);

    // This is our SID for group "Everyone"
    BYTE sidEveryone[12] = {1,1,0,0,0,0,0,1,0,0,0,0};

    // Determine size for DACL, allocate and initialize it.
    DWORD dwSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + sizeof(sidEveryone) - sizeof(DWORD);
    PACL pDacl = (PACL) malloc(dwSize);
    b = InitializeAcl(pDacl, dwSize, ACL_REVISION);
    assert(b);

    // Allow access for everyone.
    // The mask 0x000100FF defines Service Account Admin. access.
    b = AddAccessAllowedAce(pDacl, ACL_REVISION, 0x000100FF, sidEveryone);
    assert(b);

    // Insert DACL into security descriptor
    b = SetSecurityDescriptorDacl(&sd, TRUE, pDacl, FALSE);
    assert(b);

    // Set up owner and group. Exchange needs them.
    b = SetSecurityDescriptorOwner(&sd, sidEveryone, FALSE);
    assert(b);

    b = SetSecurityDescriptorGroup(&sd, sidEveryone, FALSE);
    assert(b);

    // We need to convert it to self-relative format
 
    // Determine size first
    BYTE * pSelfRelSD = NULL;
    DWORD dwSelfRelSize = 0;
    b = MakeSelfRelativeSD(&sd, pSelfRelSD, &dwSelfRelSize);
    if (dwSelfRelSize)
        pSelfRelSD = (BYTE *) malloc(dwSelfRelSize);
    b = MakeSelfRelativeSD(&sd, pSelfRelSD, &dwSelfRelSize);
    assert(b);

    // Set our security descriptor into the directory object
    int i = SetDirObjectSD(pszServer, pszDirectoryName, pSelfRelSD);
    assert(S_OK == i);

    // Deallocate memory
    if (pSelfRelSD)
        free(pSelfRelSD);
    if (pDacl)
        free(pDacl);

    return 0;
}

A few notes about this code.

  • First, the format of the security descriptor is important - it needs to be self-relative. Otherwise the DAPIWrite function fails. Also, you need to set up owner and primary group in the security descriptor. I use the well-known security identifier assigned to "Everyone" group. This makes code shorter and more illustrative.
  •  Second, notice that I have used the pszContainer member in DAPI_PARMS structure to make it work properly. If you don't supply it, the DAPIWrite may create another mailbox object in the site container instead of the Recipients container.
  • Third, the Directory Name attribute that is set up before the DAPIWrite call is the last part of mailbox distinguished name. Examine raw properties of the mailbox  to get this value and change accordingly. At first I was thinking that I could use mailbox DN (similar to the way I used when retrieving security descriptors). I abandoned further attempts to make the DN working after a few unsuccessful attempts.
  • You may need to add some error output when pDAPIEvent is not NULL (or GetLastError() for Win32 API function calls). All error handling is stripped to deliver readability. The topic "How to Work with DAPI_EVENT Structure" describes how to handle DAPI_EVENT.
If you modify mailbox access as this code does, then anyone will be able to use the mailbox. Also, if you examine Application Event Log on a computer against which you run this code, you should normally see MSExchangeDS service event ID 1175 recorded there with the following description: "The security attributes on object Object_Distinguished_Name were modified." User name who had performed the operation is also listed.
 
[ Contents | Home ]

Send comments and suggestions to niko@wrconsulting.com
Copyright © 1997-1998 by Nik Okuntseff