//+-------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1995.
//
//  File:        cacl.cxx
//
//  Contents:    class providing merging of an access list and an ACL
//
//  Classes:     CAclBuilder
//
//  History:     Feb-94        Created         DaveMont
//
//--------------------------------------------------------------------
#include <aclpch.hxx>
#pragma hdrstop

#define NAME_NOT_FOUND L"Name Not Found"    
//----------------------------------------------------------------------------
//
//  This is rather an odd object.  It's output is either an ACL or a list
//  (array)  of AccessEntries.  As input it takes an (optional) ACL, and
//  (optionally) multiple lists of AccessEntries.  The object maintains the
//  SID, user name, mask, etc. in AccountAccess object.
//
//  The following 3 methods are used to input access information:
//
//      SetAcl           -- can only be called once
//      ClearAll         -- used when a call to replace all access rights is made
//      AddAccessEntries -- adds a list of access entries to the object
//
//  The following method generates an ACL from the information within the object:
//
//      GetAcl           -- allocates and generates a new ACL
//
//  The following method generates a list of access entries from the information
//  within the object.  This list will match an ACL generated by GetAcl
//
//      GetAccessEntries -- allocates and generates a list of AccessEntries
//
//  The following method calculates the effective rights for a trustee based
//  on the access control information within the object (but not on privileges
//  the trustee may have):
//
//      GetEffectiveRights-- returns the effective access rights for trustee
//
//----------------------------------------------------------------------------
//+---------------------------------------------------------------------------
//
//  Member:    ctor, public
//
//  Synopsis:   initializes class member variables
//
//  Arguments: IN - [system] - the machine where the object is
//             IN - [fSaveNamesAndSids] - TRUE if this object is to be transacted
//             IN - [fUsedByProviderIndependentApi] - if TRUE, SYNCHRONIZE and
//                  READ_CONTROL are appended to any allowed masks, thus allowing
//                  PROV_OBJECT_READ, ...WRITE and ...EXECUTE to be non-overlapping.
//
//----------------------------------------------------------------------------
CAcl::CAcl(LPWSTR system,
           IS_CONTAINER fdir,
           BOOL fSaveNamesAndSids,
           BOOL fUsedByProviderIndependentApi)
   :_fused_by_provider_independent_api(fUsedByProviderIndependentApi),
    _aclrevision(ACL_REVISION2),
    _capabilities(0),
    _pcaaacl(NULL),
    _pcaaaes(NULL),
    _pcaaaclindex(0),
    _pcaaaesindex(0),
    _pcacli(NULL),
    _pcaeli(NULL),
    _system(system),
    _fdir(fdir),
    _fsave_names_and_sids(fSaveNamesAndSids)
{
}

//+---------------------------------------------------------------------------
//
//  Member:    dtor, public
//
//  Synopsis:   frees class member variables
//
//----------------------------------------------------------------------------
CAcl::~CAcl()
{
    //
    // have to delete the individual account accesses
    //
    if (_pcaaacl)
    {
        for (ULONG idx = 0; idx < _pcaaaclindex; idx++ )
        {
            delete _pcaaacl[idx];
        }
        AccFree(_pcaaacl);
    }
    if (_pcaaaes)
    {
        for (ULONG idx = 0; idx < _pcaaaesindex; idx++ )
        {
            delete _pcaaaes[idx];
        }
        AccFree(_pcaaaes);
    }
    if (_pcacli)
    {
        delete _pcacli;
    }
    if (_pcaeli)
    {
        delete _pcaeli;
    }
}
//+---------------------------------------------------------------------------
//
//  Member:    new, public
//
//  Synopsis:
//
//----------------------------------------------------------------------------
void * CAcl::operator new(size_t size)
{
    return(RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, (ULONG)size));
}
//+---------------------------------------------------------------------------
//
//  Member:    delete, public
//
//  Synopsis:
//
//----------------------------------------------------------------------------
void CAcl::operator delete(void *p, size_t size)
{
    RtlFreeHeap(RtlProcessHeap(), 0, p);
}
//+---------------------------------------------------------------------------
//
//  Member:   SetAcl, public
//
//  Synopsis: Takes an input ACL and converts it into AccountAccess objects.
//
//  Arguments: IN - [pacl] - the acl from the object
//
//----------------------------------------------------------------------------
DWORD CAcl::SetAcl(PACL pacl)
{
    DWORD status;

    //
    // new the iterators if required
    //
    status = _InitIterators();
    if (status != NO_ERROR)
    {
        return(status);
    }
    //
    // initialize the ACL, if it exists
    //
    _pcacli->Init(pacl);

    //
    // allocate space for the ACL's array of account accesses
    // (if there are any ACEs)
    //

    if (_pcacli->NumberEntries() > 0)
    {
        //
        // allocate for the account accesses for the acl
        //
        if ( NULL != (_pcaaacl = (CAccountAccess **)AccAlloc(
                              (_pcacli->NumberEntries() )* sizeof(void *))))
        {
            //
            // initialize the ACLs AccountAccesses (old AAs)
            //

            for (_pcacli->FirstAce(); _pcacli->MoreAces(); _pcacli->NextAce() )
            {
                status = _AddEntry( _pcacli,
                                    &(_pcaaacl[_pcaaaclindex]),
                                    &_pcaaaclindex );
                if (status != NO_ERROR)
                {
                    break;
                }
            }
        } else
        {
            status = ERROR_NOT_ENOUGH_MEMORY;
        }
        //
        // all done with the acl iterator now, clear it.
        //
        _pcacli->Init(NULL);
    }
    return(status);
}
//+---------------------------------------------------------------------------
//
//  Member:   ClearAll, public
//
//  Synopsis: clears the account accesses for the ACL and the access entries
//            (preparatory to doing a replace all API call)
//
//  Arguments:  none
//
//----------------------------------------------------------------------------
DWORD CAcl::ClearAll()
{
    DWORD status;

    //
    // new the iterators if required
    //
    status = _InitIterators();
    if (status != NO_ERROR)
    {
        return(status);
    }
    //
    // clear out the acl, if it exists
    //
    _pcacli->Init(NULL);

    if (_pcaaacl)
    {
        for (ULONG idx = 0; idx < _pcaaaclindex; idx++ )
        {
            delete _pcaaacl[idx];
        }
        AccFree(_pcaaacl);
        _pcaaacl = NULL;
        _pcaaaclindex = 0;
    }
    status = ClearAccessEntries();

    return(status);
}
//+---------------------------------------------------------------------------
//
//  Member:   ClearAccessEntries, public
//
//  Synopsis: clears the access entries AccountAccess objects. (revert)
//
//  Arguments:  none
//
//----------------------------------------------------------------------------
DWORD CAcl::ClearAccessEntries()
{
    if (_pcaaaes)
    {
        for (ULONG idx = 0; idx < _pcaaaesindex; idx++ )
        {
            delete _pcaaaes[idx];
        }
        AccFree(_pcaaaes);
        _pcaaaes = NULL;
        _pcaaaesindex = 0;
    }
    return(NO_ERROR);
}
//+---------------------------------------------------------------------------
//
//  Member:   AddAccessEntries, public
//
//  Synopsis: converts the access entries and ACL into AccountAccess objects,
//
//  Arguments: IN - [ccount] - the number of access entries
//             IN - [pae] - the array of access entries
//
//----------------------------------------------------------------------------
DWORD CAcl::AddAccessEntries( ULONG ccount,
                              PACCESS_ENTRY pae)
{

    DWORD status = NO_ERROR;

    //
    // new the iterators if required
    //
    status = _InitIterators();
    if (status != NO_ERROR)
    {
        return(status);
    }
    //
    // initialize the iterator thru the access entries
    //
    _pcaeli->Init(ccount, pae);

    //
    // do something if there are any access entries
    //
    if (_pcaeli->NumberEntries() > 0)
    {
        //
        // if we have already processed some access entries into
        // AccountAccesses, then we need to make room for more
        // ie. do a re-alloc
        //
        if (_pcaaaes != NULL)
        {
            CAccountAccess **pcaaaestmp;

            if (NULL != (pcaaaestmp = (CAccountAccess **)AccAlloc(
                              (_pcaaaesindex +
                               _pcaeli->NumberEntries() )* sizeof(void *)) ))
            {
                //
                // after allocating enough space for the old and new access
                // entries, move the old AccountAccesses into the new space,
                //
                for (ULONG aeindex = 0; aeindex < _pcaaaesindex; aeindex++)
                {
                    status = _pcaaaes[aeindex]->Clone(&(pcaaaestmp[aeindex]));
                    if (status != NO_ERROR)
                    {
                        break;
                    }
                }
                //
                // free the old list and its contents (AccountAccesses)
                //
                if (status == NO_ERROR)
                {
                    for (ULONG idx = 0; idx < _pcaaaesindex; idx++ )
                    {
                       delete _pcaaaes[idx];
                    }
                    AccFree(_pcaaaes);
                    _pcaaaes = pcaaaestmp;
                } else
                //
                // if something failed, free the new list and what ever contents
                // got initialized
                //
                {
                    for (ULONG idx = 0; idx < aeindex; idx++ )
                    {
                       delete pcaaaestmp[idx];
                    }
                    AccFree(pcaaaestmp);
                }
            }else
            {
                status = ERROR_NOT_ENOUGH_MEMORY;
            }
        } else
        {
            //
            // allocate memory for the account accesses for the access entries
            //
            if (NULL == (_pcaaaes = (CAccountAccess **)AccAlloc(
                              (_pcaeli->NumberEntries() )* sizeof(void *)) ))
            {
                status = ERROR_NOT_ENOUGH_MEMORY;
            }

        }
        if (status == NO_ERROR)
        {
            //
            // save the number of old AccountAccesses so any that are for the
            // same trustee as a new access entry can be overridden (marked
            // unused).
            //
            ULONG countold = _pcaaaesindex;
            //
            // create AccountAccesses for the new access entries
            //
            for (_pcaeli->FirstAe(); _pcaeli->MoreAes(); _pcaeli->NextAe() )
            {
                status = _AddEntry( _pcaeli,
                                    &(_pcaaaes[_pcaaaesindex]),
                                    &_pcaaaesindex );
                if (status != NO_ERROR)
                {
                    break;
                }
            }
        }
    } else
    {
        status = ERROR_INVALID_PARAMETER;
    }
    return(status);
}
//+---------------------------------------------------------------------------
//
//  Member:     BuildAcl, public
//
//  Synopsis:   converts AccountAccess objects into ACEs and builds an ACL
//              this is a two pass operation, the first pass calculates
//              the size required for the ACL, and resolves any conficts
//              between the ACL and access entries.  The second pass
//              builds the ACL.
//
//  Arguments:  OUT - [pacl]  - the returned, built acl
//
//----------------------------------------------------------------------------
DWORD CAcl::BuildAcl(PACL *pacl)
{
    DWORD status = NO_ERROR;
    NTSTATUS ntstatus;
    ULONG cacl_size, cace_count;
    ULONG aceindex = 0;
    ULONG newidx, oldidx;

    //
    // pass1 calculates the size and number of entries for an ACL or list of
    // access entries based on the current contents of this class
    //
    status = _Pass1(&cacl_size, &cace_count, TRUE); // TRUE = build ACL

    //
    // now for pass2, building the ACL.  The first thing to do is to
    // allocate space for the ACL, then add any access entries denies
    // then add any ACL denies, then add any access entries allows,
    // then add any ACL allows.  If any ACL denies are found after the
    // first ACL allow, an error is returned.  In this case the caller must
    // use a replace all option to clear out the old ACL.
    //
    if (status == NO_ERROR)
    {
        *pacl = (PACL)AccAlloc( cacl_size);
        if (*pacl == NULL)
        {
            status = ERROR_NOT_ENOUGH_MEMORY;
        }
    }

    if (status == NO_ERROR)
    {
        ntstatus = RtlCreateAcl( *pacl,
                                 cacl_size,
                                 ACL_REVISION2 );
        if (!NT_SUCCESS(ntstatus))
        {
            status = RtlNtStatusToDosError(ntstatus);
        }
    }

    if (status == NO_ERROR)
    {
        //
        // Add any access entries denies
        //
        for (ULONG newidx = 0; newidx < _pcaaaesindex ; newidx++ )
        {
            if (_pcaaaes[newidx]->AccessMode() == DENY_ACCESS)
            {
                if (_pcaaaes[newidx]->MultipleTrusteeOperation() ==
                              TRUSTEE_IS_IMPERSONATE)
                {
                    status = ERROR_INVALID_PARAMETER;
                    break;
                }
                if (!NT_SUCCESS(ntstatus = RtlAddAccessDeniedAce(*pacl,
                                           ACL_REVISION2,
                                          _pcaaaes[newidx]->AccessMask(),
                                          _pcaaaes[newidx]->Sid())))
                {
                    status = RtlNtStatusToDosError(ntstatus);
                    break;
                }
                else
                {
                    //
                    // set the inheritance
                    //
                    status = _SetAceFlags( aceindex,
                                           *pacl,
                                           _pcaaaes[newidx] );
                    if (status != NO_ERROR)
                    {
                        break;
                    }
                }
                //
                // increment the count of added aces (so can set any
                // inheritance)
                //
                aceindex++;
            }
        }
    }

    if (status == NO_ERROR)
    {
        //
        // loop thru the ACL AccountAccesses until the first allow
        //
        for (oldidx = 0; oldidx < _pcaaaclindex ; oldidx++ )
        {
            //
            //    add any denys found
            //
            if (_pcaaacl[oldidx]->AccessMode() == DENY_ACCESS)
            {
                if (!NT_SUCCESS(ntstatus = RtlAddAccessDeniedAce(
                                            *pacl,
                                            ACL_REVISION2,
                                            _pcaaacl[oldidx]->AccessMask(),
                                            _pcaaacl[oldidx]->Sid())))
                {
                    status = RtlNtStatusToDosError(ntstatus);
                    break;
                }
                else
                {
                    //
                    // set the inheritance
                    //
                    status = _SetAceFlags( aceindex,
                                           *pacl,
                                           _pcaaacl[oldidx] );
                    if (status != NO_ERROR)
                    {
                        break;
                    }
                }
                //
                // increment the count of added aces (so can set any
                // inheritance)
                //
                aceindex++;

            //
            // accessmode will be set to SE_AUDIT_BOTH in the case
            // where some other utility has put an an ACE to
            // audit both success and failure; this dll will
            // insert two aces when a request is made to set
            // both success and failure auditing
            //
            }
            else if ( (_pcaaacl[oldidx]->AccessMode() ==
                         SET_ACCESS) ||
                        (_pcaaacl[oldidx]->AccessMode() ==
                         SET_AUDIT_SUCCESS) ||
                        (_pcaaacl[oldidx]->AccessMode() ==
                         SET_AUDIT_FAILURE) ||
                        (_pcaaacl[oldidx]->AccessMode() ==
                         SE_AUDIT_BOTH))
            {
                //
                // break at the first allowed ACE
                //
                break;
            }
        } // for
    }

    if (status == NO_ERROR)
    {
        //
        // loop thru the access entries AccountAccesses
        //
        for (newidx = 0; newidx < _pcaaaesindex ; newidx++ )
        {
            //
            //    add any grants or sets found
            //
            if ( (_pcaaaes[newidx]->AccessMode() ==
                  SET_ACCESS) ||
                 (_pcaaaes[newidx]->AccessMode() ==
                  GRANT_ACCESS) )
            {
                if (_pcaaaes[newidx]->MultipleTrusteeOperation() ==
                    TRUSTEE_IS_IMPERSONATE)
                {
                    //
                    // note that no mask translations additions are
                    // done on compound ace masks
                    //
                    if (!NT_SUCCESS(ntstatus = RtlAddCompoundAce(
                                               *pacl,
                                               ACL_REVISION3,
                             COMPOUND_ACE_IMPERSONATION,
                            _pcaaaes[newidx]->AccessMask(),
                            _pcaaaes[newidx]->ImpersonateSid(),
                            _pcaaaes[newidx]->Sid())))
                    {
                        status = RtlNtStatusToDosError(ntstatus);
                        break;
                    }
                }
                else
                {
                    if (!NT_SUCCESS(ntstatus = RtlAddAccessAllowedAce(
                                                *pacl,
                                                ACL_REVISION2,
                                                _pcaaaes[newidx]->AccessMask(),
                                                _pcaaaes[newidx]->Sid())))
                    {
                        status = RtlNtStatusToDosError(ntstatus);
                        break;
                    }
                }

                //
                // set the inheritance
                //
                status = _SetAceFlags( aceindex,
                                       *pacl,
                                       _pcaaaes[newidx] );

                if (status != NO_ERROR)
                {
                    break;
                }
                //
                // increment the count of added aces (so can set
                // any inheritance)
                //
                aceindex++;
            }

            //
            // else add any audit entries
            //
            else if ( (_pcaaaes[newidx]->AccessMode() ==
                         SET_AUDIT_SUCCESS) ||
                        (_pcaaaes[newidx]->AccessMode() ==
                         SET_AUDIT_FAILURE) )
            {
                //
                // allow synchronize and read control if
                // used by provider independent API
                //
                ACCESS_MASK tmpmask;

                if (_fused_by_provider_independent_api)
                {
                    tmpmask = _pcaaaes[newidx]->AccessMask() |
                              SYNCHRONIZE | READ_CONTROL;
                } else
                {
                    tmpmask = _pcaaaes[newidx]->AccessMask();
                }

                if (!NT_SUCCESS(ntstatus = RtlAddAuditAccessAce(
                                           *pacl,
                                           ACL_REVISION2,
                                           tmpmask,
                                          _pcaaaes[newidx]->Sid(),
                                          (_pcaaaes[newidx]->AccessMode() ==
                                              SET_AUDIT_SUCCESS),
                                          (_pcaaaes[newidx]->AccessMode() ==
                                              SET_AUDIT_FAILURE))))
                {
                    status = RtlNtStatusToDosError(ntstatus);
                }
                else
                {
                    //
                    // set the inheritance
                    //
                    status = _SetAceFlags( aceindex,
                                           *pacl,
                                           _pcaaaes[newidx] );
                }

                if (status != NO_ERROR)
                {
                    break;
                }

                //
                // increment the count of added aces (so can set
                // any inheritance)
                //
                aceindex++;
            }

        } // for
    }

    if (status == NO_ERROR)
    {
        //
        // loop thru the rest of the ACLs AccountAccesses
        //
        for (; oldidx < _pcaaaclindex ; oldidx++ )
        {
            //
            //    add any sets found, error if any denys found
            //
            if (_pcaaacl[oldidx]->AccessMode() == SET_ACCESS)
            {
                if (_pcaaacl[oldidx]->MultipleTrusteeOperation() ==
                    TRUSTEE_IS_IMPERSONATE)
                {
                    //
                    // add any impersonate aces
                    //
                    if (!NT_SUCCESS(ntstatus = RtlAddCompoundAce( *pacl,
                                                ACL_REVISION3,
                                                COMPOUND_ACE_IMPERSONATION,
                                             _pcaaacl[oldidx]->AccessMask(),
                                          _pcaaacl[oldidx]->ImpersonateSid(),
                                                    _pcaaacl[oldidx]->Sid())))
                    {
                        status = RtlNtStatusToDosError(ntstatus);
                        break;
                    }
                }
                else
                {
                    //
                    // add an access allowed ACE
                    //
                    if (!NT_SUCCESS(ntstatus = RtlAddAccessAllowedAce(
                                                 *pacl,
                                                 ACL_REVISION2,
                                                 _pcaaacl[oldidx]->AccessMask(),
                                                 _pcaaacl[oldidx]->Sid())))
                    {
                        status = RtlNtStatusToDosError(ntstatus);
                        break;
                    }
                }

                //
                // set the inheritance
                //
                status = _SetAceFlags( aceindex,
                                       *pacl,
                                       _pcaaacl[oldidx] );
                if (status != NO_ERROR)
                {
                    break;
                }

                //
                // increment the count of added aces (so can
                // set any inheritance)
                //
                aceindex++;

            }
            //
            // add any audit aces
            //
            else if ( (_pcaaacl[oldidx]->AccessMode() == SET_AUDIT_SUCCESS) ||
                      (_pcaaacl[oldidx]->AccessMode() == SET_AUDIT_FAILURE) ||
                      (_pcaaacl[oldidx]->AccessMode() == SE_AUDIT_BOTH))
            {
                //
                // allow synchronize and read control if
                // used by provider independent API
                // also note that if an audit both ace
                // existed before, and is not being modified,
                // no changes are made to the ace
                //

                ACCESS_MASK tmpmask;

                if (_fused_by_provider_independent_api)
                {
                    tmpmask = _pcaaacl[oldidx]->AccessMask() |
                              SYNCHRONIZE | READ_CONTROL;
                } else
                {
                    tmpmask = _pcaaacl[oldidx]->AccessMask();
                }

                //
                // add an audit ace
                //
                if (!NT_SUCCESS(ntstatus = RtlAddAuditAccessAce(
                                             *pacl,
                                             ACL_REVISION2,
                                             tmpmask,
                                             _pcaaacl[oldidx]->Sid(),
                                             (_pcaaacl[oldidx]->AccessMode() ==
                                                 SET_AUDIT_SUCCESS),
                                             (_pcaaacl[oldidx]->AccessMode() ==
                                                 SET_AUDIT_FAILURE))))
                {
                    status = RtlNtStatusToDosError(ntstatus);
                    break;
                }
                else
                {
                    //
                    // set the inheritance
                    //
                    status = _SetAceFlags( aceindex,
                                           *pacl,
                                           _pcaaacl[oldidx] );
                    if (status != NO_ERROR)
                    {
                        break;
                    }
                }

                //
                // increment the count of added aces (so can
                // set any inheritance)
                //
                aceindex++;
            }

            //
            // unless the entry was marked as not used, it is
            // out of order
            //
            else if (_pcaaacl[oldidx]->AccessMode() !=
                       NOT_USED_ACCESS)

            {
               status = ERROR_INVALID_ACL;
               break;
            }
        } // for

    }

    if (status != NO_ERROR)
    {
        if (*pacl != NULL)
        {
            AccFree(*pacl);
        }
    }

    return(status);
}
//+---------------------------------------------------------------------------
//
//  Member:     BuildAccessEntries, public
//
//  Synopsis:  converts the ACL and access entries AccountAccesses into
//             access entries.  This is also a 2 pass operation, the first
//             pass calculates the size and the second pass resolves conflicts,
//             allocates memory and builds the access entries to return
//
//  Arguments:  OUT - [csize]  - the returned size of the access entries
//              OUT - [ccount]  - the returned count of access entries
//              OUT - [pae]  - the returned list of access entries, this buffer
//                             and each trustee name in it must be freed using
//                             AccFree (or see below)
//              IN - [fAbsolute]  - If TRUE the returned access entries are in a
//                                  single buffer and must be freed with a single
//                                  call to AccFree
//
//----------------------------------------------------------------------------
DWORD CAcl::BuildAccessEntries(PULONG csize,
                                 PULONG ccount,
                                 PACCESS_ENTRY *pae,
                                 BOOL fAbsolute)
{
    DWORD status = NO_ERROR;

    //
    // pass1 resolve conflicts and get the size of the access entries based on
    // the current contents of this class
    //
    status = _Pass1(csize, ccount, FALSE); // FALSE:build access entries
    if (status == NO_ERROR)
    {
        //
        // allocate space for the new entries
        //
        if ( NULL != (*pae = (PACCESS_ENTRY)AccAlloc(*csize) ) )
        {
            //
            // get a pointer to where the names will be put
            //
            LPWSTR nameptr = (LPWSTR)((PBYTE)(*pae) + sizeof(ACCESS_ENTRY) *
                                                *ccount);

            ULONG cintcount = 0;
            //
            // loop thru the access entries AccountAccesses
            //
            for (ULONG newidx = 0; newidx < _pcaaaesindex ; newidx++ )
            {
                //
                //    add any denys found
                //
                if (_pcaaaes[newidx]->AccessMode() == DENY_ACCESS)
                {
                    _BuildAccessEntry(_pcaaaes[newidx],
                                      &nameptr,
                                      &((*pae)[cintcount++]),
                                      fAbsolute);
                }
            }

            //
            // loop thru the ACL AccountAccesses until the first allow
            //
            for (ULONG oldidx = 0; oldidx < _pcaaaclindex ; oldidx++ )
            {
                //
                //    add any denys found
                //
                if (_pcaaacl[oldidx]->AccessMode() == DENY_ACCESS)
                {
                    _BuildAccessEntry(_pcaaacl[oldidx],
                                      &nameptr,
                                      &((*pae)[cintcount++]),
                                      fAbsolute);

                } else if ( (_pcaaacl[oldidx]->AccessMode() ==
                             SET_ACCESS)  ||
                            (_pcaaacl[oldidx]->AccessMode() ==
                             SET_AUDIT_SUCCESS) ||
                            (_pcaaacl[oldidx]->AccessMode() ==
                             SET_AUDIT_FAILURE) ||
                            (_pcaaacl[oldidx]->AccessMode() ==
                             SE_AUDIT_BOTH))
                {
                    //
                    // break at the first allowed ACE
                    //
                    break;
                }
            }
            //
            // loop thru the access entries AccountAccesses
            //
            for (newidx = 0; newidx < _pcaaaesindex ; newidx++)
            {
                //
                //    add any grants or sets found
                //
                if (  (_pcaaaes[newidx]->AccessMode() ==
                       SET_ACCESS) ||
                      (_pcaaaes[newidx]->AccessMode() ==
                       GRANT_ACCESS) ||
                      (_pcaaaes[newidx]->AccessMode() ==
                       SET_AUDIT_SUCCESS) ||
                      (_pcaaaes[newidx]->AccessMode() ==
                       SET_AUDIT_FAILURE) )
                {
                    _BuildAccessEntry(_pcaaaes[newidx],
                                      &nameptr,
                                      &((*pae)[cintcount++]),
                                      fAbsolute);
                }
            }
            //
            // loop thru the rest of the ACLs AccountAccesses
            //
            for (; oldidx < _pcaaaclindex ; oldidx++)
            {
                //
                //    add any sets found, error if any denys found
                //
                if ( (_pcaaacl[oldidx]->AccessMode() ==
                      SET_ACCESS)  ||
                     (_pcaaacl[oldidx]->AccessMode() ==
                      SET_AUDIT_SUCCESS) ||
                     (_pcaaacl[oldidx]->AccessMode() ==
                      SET_AUDIT_FAILURE))
                {
                    _BuildAccessEntry(_pcaaacl[oldidx],
                                      &nameptr,
                                      &((*pae)[cintcount++]),
                                      fAbsolute);
                //
                // unless the entry was marked as not used, it is
                // out of order
                //
                } else if (_pcaaacl[oldidx]->AccessMode() ==
                           SE_AUDIT_BOTH)
                {
                    _BuildDualAuditEntries(_pcaaacl[oldidx],
                                           &nameptr,
                                           *pae,
                                           &cintcount,
                                           fAbsolute);

                } else if (_pcaaacl[oldidx]->AccessMode() !=
                           NOT_USED_ACCESS)

                {
                   status = ERROR_INVALID_ACL;
                   AccFree(*pae);
                   break;
                }
            }
            ASSERT(*ccount == cintcount);
        } else
        {
            status = ERROR_NOT_ENOUGH_MEMORY;
        }
    }
    return(status);
}
#if 0
//+---------------------------------------------------------------------------
//
//  Member:     GetEffectiveRights, public
//
//  Synopsis:   grovels the CACLs AccountAccess objects to determine the trustees
//              effective access rights
//
//  Arguments:  IN - [ptrustee]  - the name of the trustee to get effective right
//              OUT - [accessmask] - effective rights for the trustee
//
//----------------------------------------------------------------------------
DWORD CAcl::GetEffectiveRights( PTRUSTEE ptrustee,
                                PACCESS_MASK accessmask)
{
    DWORD status = NO_ERROR;
    CAccountAccess caa;

    //
    // initialize the account access for the trustee depending on type
    // of trustee
    //
    if (ptrustee->TrusteeForm == TRUSTEE_IS_SID)
    {
        status = caa.Init( (PSID)ptrustee->ptstrName,
                           _system,
                           SET_ACCESS,
                           0,
                           0,
                           FALSE);
    }
    else if (ptrustee->TrusteeForm == TRUSTEE_IS_NAME)
    {
        status = caa.Init( ptrustee->ptstrName,
                           _system,
                           SET_ACCESS,
                           0,
                           0,
                           FALSE);
    }
    else
    {
        status = ERROR_NOT_SUPPORTED;
    }

    if (status == NO_ERROR)
    {
        //
        // a class to do the member check
        //
        CMemberCheck cmc(&caa);

        //
        // initialized the class
        //
        status = cmc.Init();
        if (status == NO_ERROR)
        {
            ACCESS_MASK allowmask = 0, denymask = 0;

            for (ULONG newidx = 0; newidx < _pcaaaesindex ; newidx++ )
            {
                //
                //    add any denys found
                //
                if (_pcaaaes[newidx]->AccessMode() == DENY_ACCESS)
                {
                    status = _ComputeEffective( _pcaaaes[newidx],
                                                &cmc,
                                                &allowmask,
                                                &denymask);
                    if (status != NO_ERROR)
                    {
                        break;
                    }
                }
            }
            if (status == NO_ERROR)
            {
                //
                // loop thru the ACL AccountAccesses until the first allow
                //
                for (ULONG oldidx = 0; oldidx < _pcaaaclindex ; oldidx++ )
                {
                    //
                    //    add any denys found
                    //
                    if (_pcaaacl[oldidx]->AccessMode() == DENY_ACCESS)
                    {
                        status = _ComputeEffective( _pcaaacl[oldidx],
                                                    &cmc,
                                                    &allowmask,
                                                    &denymask );
                        if (status != NO_ERROR)
                        {
                            break;
                        }
                    }
                    else if (_pcaaacl[oldidx]->AccessMode() == SET_ACCESS)
                    {
                        //
                        // break at the first allowed ACE
                        //
                        break;
                    }
                    else if ( (_pcaaacl[oldidx]->AccessMode() ==
                                  SET_AUDIT_SUCCESS) ||
                              (_pcaaacl[oldidx]->AccessMode() ==
                                  SET_AUDIT_FAILURE) ||
                              (_pcaaacl[oldidx]->AccessMode() ==
                                  SE_AUDIT_BOTH) )
                    {
                        //
                        // we should not encounter any audit aces when
                        // we are checking for effective access rights
                        //
                        status = ERROR_INVALID_ACL;
                        break;
                    }
                }
                if (status == NO_ERROR)
                {
                    //
                    // loop thru the access entries AccountAccesses
                    //
                    for (newidx = 0; newidx < _pcaaaesindex ; newidx++ )
                    {
                        //
                        //    add any grants, or sets found
                        //
                        if ( (_pcaaaes[newidx]->AccessMode() ==
                              SET_ACCESS) ||
                             (_pcaaaes[newidx]->AccessMode() ==
                              GRANT_ACCESS) )
                        {
                            status = _ComputeEffective( _pcaaaes[newidx],
                                                        &cmc,
                                                        &allowmask,
                                                        &denymask );
                            if (status != NO_ERROR)
                            {
                                break;
                            }
                        }
                        else if ( (_pcaaaes[newidx]->AccessMode() ==
                                      SET_AUDIT_SUCCESS) ||
                                  (_pcaaaes[newidx]->AccessMode() ==
                                      SET_AUDIT_FAILURE) ||
                                  (_pcaaaes[newidx]->AccessMode() ==
                                      SE_AUDIT_BOTH) )
                        {
                            //
                            // we should not encounter any audit aces when
                            // we are checking for effective access rights
                            //
                            status = ERROR_INVALID_ACL;
                            break;
                        }
                    }
                    if (status == NO_ERROR)
                    {
                        //
                        // loop thru the rest of the ACLs AccountAccesses
                        //
                        for (; oldidx < _pcaaaclindex ; oldidx++ )
                        {
                            //
                            //    add any sets or audits found, error if any
                            //    denys found
                            //
                            if ( (_pcaaacl[oldidx]->AccessMode() ==
                                  SET_ACCESS) )
                            {
                                status = _ComputeEffective( _pcaaacl[oldidx],
                                                            &cmc,
                                                            &allowmask,
                                                            &denymask );
                                if (status != NO_ERROR)
                                {
                                    break;
                                }
                            }
                            else if (_pcaaacl[oldidx]->AccessMode() !=
                                        NOT_USED_ACCESS)

                            {
                                //
                                // unless the entry was marked as not used, it is
                                // out of order
                                //
                                status = ERROR_INVALID_ACL;
                                break;
                            }
                        }
                    }
                }
            }
            if (status == NO_ERROR)
            {
                //
                // set the effective rights to be the alloweds minus the denieds
                // (because of required ordering)
                //
                *accessmask = allowmask & ~denymask;
            }
        }
    }


    return(status);
}
//+---------------------------------------------------------------------------
//
//  Member:     GetAuditedRights, public
//
//  Synopsis:   grovels the CACLs AccountAccess objects to determine the trustees
//              audited rights
//
//  Arguments:  IN - [ptrustee]  - the name of the trustee to get effective right
//              OUT - [successmask] - success audited rights for the trustee
//              OUT - [failuremask] - failure audited rights for the trustee
//
//----------------------------------------------------------------------------
DWORD CAcl::GetAuditedRights(PTRUSTEE ptrustee,
                               PACCESS_MASK successmask,
                               PACCESS_MASK failuremask)
{
    DWORD status;
    CAccountAccess caa;


    *successmask = 0;
    *failuremask = 0;

    //
    // initialize the account access for the trustee depending on form of
    // trustee
    //
    if (ptrustee->TrusteeForm == TRUSTEE_IS_SID)
    {
        status = caa.Init( (PSID)ptrustee->ptstrName,
                           _system,
                           SET_ACCESS,
                           0,
                           0,
                           FALSE );
    }
    else if (ptrustee->TrusteeForm == TRUSTEE_IS_NAME)
    {
        status = caa.Init( ptrustee->ptstrName,
                           _system,
                           SET_ACCESS,
                           0,
                           0,
                           FALSE );
    }
    else
    {
        status = ERROR_NOT_SUPPORTED;
    }

    if (status == NO_ERROR)
    {
        //
        // a class to do the member check
        //
        CMemberCheck cmc(&caa);

        //
        // initialized the class
        //
        status = cmc.Init();
        if (status == NO_ERROR)
        {
            //
            // loop thru the access entry accesses
            //
            for (ULONG newidx = 0; newidx < _pcaaaesindex ; newidx++ )
            {
                //
                // no access convered by ace with inherit only flag on
                //
                if (0 == (_pcaaaes[newidx]->AceFlags() & INHERIT_ONLY_ACE ) )
                {
                    BOOL fresult;

                    //
                    // check if the trustee is a member of the account access id
                    //
                    status = cmc.IsMemberOf( _pcaaaes[newidx],
                                             &fresult );
                    if (status == NO_ERROR)
                    {
                        if (fresult == TRUE)
                        {
                            //
                            // then decide which type to add it to
                            //
                            if ( (_pcaaaes[newidx]->AccessMode() ==
                                  SET_AUDIT_SUCCESS) ||
                                 (_pcaaaes[newidx]->AccessMode() ==
                                  SE_AUDIT_BOTH) )
                            {
                                *successmask |=_pcaaaes[newidx]->AccessMask();
                            }

                            if ( (_pcaaaes[newidx]->AccessMode() ==
                                  SET_AUDIT_FAILURE) ||
                                 (_pcaaaes[newidx]->AccessMode() ==
                                  SE_AUDIT_BOTH) )
                            {
                                *failuremask |=_pcaaaes[newidx]->AccessMask();
                            }
                        }
                     }
                     else
                     {
                         break;
                     }
                }
            }

            if (status == NO_ERROR)
            {
                //
                // loop thru the ACL AccountAccesses
                //
                for (ULONG oldidx = 0; oldidx < _pcaaaclindex ; oldidx++ )
                {
                    //
                    // no access convered by ace with inherit only flag on
                    //
                    if (0 == (_pcaaacl[oldidx]->AceFlags() & INHERIT_ONLY_ACE ) )
                    {
                        BOOL fresult;

                        //
                        // check if the trustee is a member of the account
                        //  access id
                        //
                        status = cmc.IsMemberOf( _pcaaacl[oldidx],
                                                 &fresult );
                        if (status == NO_ERROR)
                        {
                            if (fresult == TRUE)
                            {
                                //
                                // then decide which type to add it to
                                //
                                if ( (_pcaaacl[oldidx]->AccessMode() ==
                                                             SET_AUDIT_SUCCESS) ||
                                     (_pcaaacl[oldidx]->AccessMode() ==
                                                             SE_AUDIT_BOTH) )
                                {
                                    *successmask |=_pcaaacl[oldidx]->AccessMask();
                                }
                                if ( (_pcaaacl[oldidx]->AccessMode() ==
                                                             SET_AUDIT_FAILURE) ||
                                     (_pcaaacl[oldidx]->AccessMode() ==
                                                             SE_AUDIT_BOTH) )
                                {
                                    *failuremask |=_pcaaacl[oldidx]->AccessMask();
                                }
                            }
                         }
                         else
                         {
                             break;
                         }
                    }
                }
            }
        }
    }

    return(status);
}
#endif
//+---------------------------------------------------------------------------
//
//  Member:     _InitIterators, private
//
//  Synopsis:   resolves any conficts between the ACL and access entries,
//              and calculates the size required for an ACL or access entries
//              to return
//
//  Arguments:  none
//
//----------------------------------------------------------------------------
DWORD CAcl::_InitIterators()
{
    DWORD status = NO_ERROR;
    //
    // new the iterators if required
    //
    if (_pcacli == NULL)
    {
        if (NULL == (_pcacli = new CAclIterator()))
        {
            status = ERROR_NOT_ENOUGH_MEMORY;
        }
    }

    if (status == NO_ERROR)
    {
        if (_pcaeli == NULL)
        {
            if (NULL == (_pcaeli = new CAesIterator()))
            {
                status = ERROR_NOT_ENOUGH_MEMORY;
            }
        }
    }
    return(status);
}
//+---------------------------------------------------------------------------
//
//  Member:     _Pass1, private
//
//  Synopsis:   resolves any conficts between the ACL and access entries,
//              and calculates the size required for an ACL or access entries
//              to return
//
//  Arguments:  [OUT] cSize - the size required for an ACL or access entries
//              [OUT] cCount - the count of ACEs or access entries
//              [IN] fBuildAcl - if FALSE, calculate size for access entries
//
//----------------------------------------------------------------------------
DWORD CAcl::_Pass1(PULONG cSize, PULONG cCount, BOOL fBuildAcl)
{

    DWORD status = NO_ERROR;
    BOOL useentry;

    //
    // initialize the size.
    //
    *cSize = fBuildAcl ? sizeof(ACL) : 0;
    *cCount = 0;

    //
    // assume that we are using all the (remaining) ACL entries
    // (ie. whats one more loop)
    //
    for (ULONG aclindex = 0; aclindex < _pcaaaclindex; aclindex++)
    {
        //
        // don't process any entries marked not used
        //
        if (_pcaaacl[aclindex]->AccessMode() != NOT_USED_ACCESS)
        {
            status = _UseEntry( _pcaaacl[aclindex],
                                cSize,
                                cCount,
                                fBuildAcl );
            if (status != NO_ERROR)
            {
                break;
            }
        }
    }
    //
    // if there are any access entries, resolve any conflicts
    //
    for (ULONG aeindex = 0; aeindex < _pcaaaesindex; aeindex++)
    {
        //
        // don't process any entries already marked not used.
        // (perhaps from a previous pass)
        //
        if (_pcaaaes[aeindex]->AccessMode() != NOT_USED_ACCESS)
        {
            //
            // first check for matching SIDs in the ACL
            //
            status = _CheckEntryList( _pcaaaes[aeindex],
                                      _pcaaacl,
                                      _pcaaaclindex,
                                      cSize,
                                      cCount,
                                      fBuildAcl );
            if (status != NO_ERROR)
            {
                break;
            }
            //
            // if the entry is still needed (access not already provided
            // by a previous entry), check the access entries list for matches
            //
            if (_pcaaaes[aeindex]->AccessMode() != NOT_USED_ACCESS)
            {
                //
                // then check for matching SIDS in any previous access entries
                //
                status = _CheckEntryList( _pcaaaes[aeindex],
                                          _pcaaaes,
                                          aeindex,
                                          cSize,
                                          cCount,
                                          fBuildAcl );
                if (status != NO_ERROR)
                {
                    break;
                }
                //
                // all done checking, if we still want the entry, use it
                //

                if ( (_pcaaaes[aeindex]->AccessMode() != NOT_USED_ACCESS) &&
                     (_pcaaaes[aeindex]->AccessMode() != REVOKE_ACCESS) )
                {
                    status = _UseEntry( _pcaaaes[aeindex],
                                        cSize,
                                        cCount,
                                        fBuildAcl );
                    if (status != NO_ERROR)
                    {
                        break;
                    }
                }
            }
        }
    }
    return(status);
}
//+---------------------------------------------------------------------------
//
//  Member:     _CheckEntryList, private
//
//  Synopsis:   resolves any conficts between the ACL and access entries,
//              and calculates the size required for an ACL or access entries
//              to return
//
//  Arguments:  [IN] pCAA - the Account Access class to check for matches against
//                          the list of account accessses
//              [IN] plistCAA - the list of account accesses to search thru
//              [IN] clistlength - the number of entries in the list
//              [IN/OUT] cSize - the size required for an ACL or access entries
//              [IN/OUT] cCount - the size required for an ACL or access entries
//              [IN] fBuildAcl - if FALSE, calculate size for access entries
//
//----------------------------------------------------------------------------
DWORD CAcl::_CheckEntryList(CAccountAccess *pCAA,
                            CAccountAccess **plistCAA,
                            ULONG clistlength,
                            PULONG cSize,
                            PULONG cCount,
                            BOOL fBuildAcl)
{
    DWORD status = NO_ERROR;

    BOOL dontuseentry = FALSE;

    for (ULONG index = 0; index < clistlength; index++)
    {
        //
        // don't process any entries already marked not used.
        // (perhaps from a previous pass)
        //
        if (plistCAA[index]->AccessMode() != NOT_USED_ACCESS)
        {
            //
            // check for matching ids (and matching modes, see
            // accesscontrolcdd.doc for a full explaination of this)
            // this impersonate stuff is specifically for DS objects,
            // and the kind of ace management they do (specifically, see
            // notes on DS object specific permissions, and query access
            //
            if ( (RtlEqualSid( pCAA->Sid(), plistCAA[index]->Sid())) &&
                 ( ( (pCAA->AccessMode() == DENY_ACCESS) ||
                     (plistCAA[index]->AccessMode() == DENY_ACCESS) ) ||
                   ( (pCAA->MultipleTrusteeOperation() != TRUSTEE_IS_IMPERSONATE) &&
                     (plistCAA[index]->MultipleTrusteeOperation() != TRUSTEE_IS_IMPERSONATE) ||
                   ( (pCAA->MultipleTrusteeOperation() == TRUSTEE_IS_IMPERSONATE) &&
                     (plistCAA[index]->MultipleTrusteeOperation() == TRUSTEE_IS_IMPERSONATE) ))))
            {

                //
                // do things based on the type of the new entry
                //
                switch (pCAA->AccessMode())
                {
                case GRANT_ACCESS:
                case DENY_ACCESS:
                    //
                    // merge the entries
                    //
                    status = _MergeEntries(pCAA,
                                           plistCAA[index],
                                           cSize,
                                           cCount,
                                           fBuildAcl);
                    break;
                case SET_ACCESS:
                case REVOKE_ACCESS:
                case SET_AUDIT_SUCCESS:
                case SET_AUDIT_FAILURE:
                    //
                    // don't use the old entry (since the new one takes
                    // precedence)
                    //

                    status = _RemoveEntry(plistCAA[index],
                                          cSize,
                                          cCount,
                                          fBuildAcl);
                    break;
                default:
                    ASSERT( (pCAA->AccessMode() != GRANT_ACCESS  ) ||
                            (pCAA->AccessMode() != DENY_ACCESS   ) ||
                            (pCAA->AccessMode() != SET_ACCESS    ) ||
                            (pCAA->AccessMode() != REVOKE_ACCESS ) ||
                            (pCAA->AccessMode() != SET_AUDIT_SUCCESS ) ||
                            (pCAA->AccessMode() != SET_AUDIT_FAILURE ) );
                    break;
                }
            }
        }
    }
    return(status);
}
//+---------------------------------------------------------------------------
//
//  Member:     _UseEntry, private
//
//  Synopsis:   uses the entry for the ACL or access entries being built
//
//  Arguments:  [IN] pCAA - the Account Access class to use
//              [IN/OUT] cSize - the size required for an ACL or access entries
//              [IN/OUT] cCount - the size required for an ACL or access entries
//              [IN] fBuildAcl - if FALSE, calculate size for access entries
//
//----------------------------------------------------------------------------
DWORD CAcl::_UseEntry(CAccountAccess *pCAA,
                        PULONG cSize,
                        PULONG cCount,
                        BOOL fBuildAcl)
{
    DWORD status = NO_ERROR;

    if (fBuildAcl)
    {
        //
        // increment the count (of aces in this case)
        // and grow the size
        //
        (*cSize) += _GetAceSize(pCAA);

    } else
    {
        //
        // get the size of the access entry
        //
        ULONG centrysize;

        status = _GetAccessEntrySize( pCAA,
                                      &centrysize);
        if (status == NO_ERROR)
        {
            (*cSize) += centrysize;
            if (pCAA->AccessMode() == SE_AUDIT_BOTH)
            {
                //
                // if it is an audit both (success and failure) ACE,
                // add two access entries
                //
                (*cSize) += centrysize;
                (*cCount)++;
            }
        }
    }
    //
    // increment the count (of access entries)
    //
    (*cCount)++;
    return(status);
}
//+---------------------------------------------------------------------------
//
//  Member:     _RemoveEntry, private
//
//  Synopsis:   removes the entry from those to be used for the ACL or access
//              entries being built
//
//  Arguments:  [IN] pCAA - the Account Access class to not use
//              [IN/OUT] cSize - the size required for an ACL or access entries
//              [IN/OUT] cCount - the size required for an ACL or access entries
//              [IN] fBuildAcl - if FALSE, calculate size for access entries
//
//----------------------------------------------------------------------------
DWORD CAcl::_RemoveEntry(CAccountAccess *pCAA,
                           PULONG cSize,
                           PULONG cCount,
                           BOOL fBuildAcl)
{
    DWORD status = NO_ERROR;

    ASSERT(pCAA->AccessMode() != NOT_USED_ACCESS);

    if (fBuildAcl)
    {
        //
        // increment the count (of aces in this case)
        // and grow the size
        //
        (*cSize) -= _GetAceSize(pCAA);
    } else
    {
        //
        // get the size of the access entry
        //
        ULONG centrysize;

        status = _GetAccessEntrySize( pCAA,
                                      &centrysize );
        if (status == NO_ERROR)
        {
            (*cSize) -= centrysize;
        }
    }
    //
    // increment the count (of access entries)
    //
    (*cCount)--;
    pCAA->SetAccessMode(NOT_USED_ACCESS);
    return(status);
}
//+---------------------------------------------------------------------------
//
//  Member:     _MergeEntries, private
//
//  Synopsis:   this merge function checks two access entries with matching
//              SIDs for inheritance and mask intersections.  It is assumed that
//              the new access entry is either DENY_ACCESS or GRANT_ACCESS,
//              and the old entry is either DENY_ACCESS, GRANT_ACCESS, or SET_ACCESS
//
//  Arguments:  [IN] pnewCAA - the new Account Access class to merge with the
//                             old one
//              [IN] poldCAA - the old Account Access class
//              [IN/OUT] cSize - the size required for an ACL or access entries
//              [IN/OUT] cCount - the size required for an ACL or access entries
//              [IN] fBuildAcl - if FALSE, calculate size for access entries
//
//----------------------------------------------------------------------------
DWORD CAcl::_MergeEntries(CAccountAccess *pnewCAA,
                            CAccountAccess *poldCAA,
                            PULONG cSize,
                            PULONG cCount,
                            BOOL fBuildAcl)
{

    DWORD status = NO_ERROR;
    BOOL leave_old_ace = FALSE;

    //
    // bogus while loop for fake gotos
    //
    while(1)
    {
        //
        // can't set inheritance on objects
        //
        if ( ( (_fdir == ACCESS_TO_UNKNOWN ) ||
               (_fdir == ACCESS_TO_OBJECT ) ) &&
             (0 != pnewCAA->AceFlags()) )
        {
            status = ERROR_INVALID_PARAMETER;
            break;
        } else if (_fdir == ACCESS_TO_CONTAINER )
        {
            //
            // check for inheritance intersections
            //
            if (0 == (pnewCAA->AceFlags() & poldCAA->AceFlags()))
            {
                //
                // if disjoint inheritance, the entries really don't match
                //
                break;

            } else if (pnewCAA->AceFlags() != poldCAA->AceFlags())
            {
                //
                // if the entries inheritance intersects in some way, return an
                // error (can only set or revoke access in this case)
                //
                status = ERROR_INVALID_PARAMETER;
                break;
            }
        }
        //
        // now to merge the masks, switch on mask intersection type, and
        // then on entry types,
        // NOTE that SYNCHRONIZE and READ_CONTROL are always added on the
        // mask compares, however
        // it is only actually applied on the SET_ACCESS ACEs, so that, for
        // example, READ can be allowed, and WRITE can be denied (as opposed to
        // not granted).
        //
        if (0 == ((pnewCAA->AccessMask() | SYNCHRONIZE | READ_CONTROL) &
                  (poldCAA->AccessMask() | SYNCHRONIZE | READ_CONTROL)))
        {
            //
            // disjoint
            //
            if ( (pnewCAA->AccessMode() == poldCAA->AccessMode()) ||
                 ( (pnewCAA->AccessMode() != DENY_ACCESS) &&
                   (poldCAA->AccessMode() != DENY_ACCESS) ) )
            {
                //
                // equivalent modes
                //
                pnewCAA->SetAccessMask(pnewCAA->AccessMask() |
                                       poldCAA->AccessMask());
                status = _RemoveEntry(poldCAA,
                                      cSize,
                                      cCount,
                                      fBuildAcl);
            }
        } else if ((pnewCAA->AccessMask() | SYNCHRONIZE | READ_CONTROL) ==
                   (poldCAA->AccessMask() | SYNCHRONIZE | READ_CONTROL) )
        {
            //
            // equal
            //
            status = _RemoveEntry(poldCAA,
                                  cSize,
                                  cCount,
                                  fBuildAcl);

        } else if ( (poldCAA->AccessMask() | pnewCAA->AccessMask() |
                     SYNCHRONIZE | READ_CONTROL) ==
                    (poldCAA->AccessMask() | SYNCHRONIZE | READ_CONTROL))
        {
            //
            // new is subset of old
            //
            if ( (pnewCAA->AccessMode() == poldCAA->AccessMode()) ||
                 ( (pnewCAA->AccessMode() != DENY_ACCESS) &&
                   (poldCAA->AccessMode() != DENY_ACCESS) ) )
            {
                //
                // equivalent modes
                //
                pnewCAA->SetAccessMode(NOT_USED_ACCESS);
            } else
            {
                //
                // one is deny, one is allow
                //
                poldCAA->SetAccessMask(poldCAA->AccessMask() &
                                       ~(pnewCAA->AccessMask()));
            }
        } else if ( (poldCAA->AccessMask() | pnewCAA->AccessMask() |
                     SYNCHRONIZE | READ_CONTROL) ==
                    (pnewCAA->AccessMask() | SYNCHRONIZE | READ_CONTROL))
        {
            //
            // old is subset of new
            //
            status = _RemoveEntry(poldCAA,
                                  cSize,
                                  cCount,
                                  fBuildAcl);
        } else
        {
            //
            // overlapping
            //
            if ( ( (pnewCAA->AccessMode() | SYNCHRONIZE | READ_CONTROL) ==
                   (poldCAA->AccessMode() | SYNCHRONIZE | READ_CONTROL) ) ||
                 ( (pnewCAA->AccessMode() != DENY_ACCESS) &&
                   (poldCAA->AccessMode() != DENY_ACCESS) ) )
            {
                //
                // equivalent modes
                //
                pnewCAA->SetAccessMask(pnewCAA->AccessMask() |
                                       poldCAA->AccessMask());
                status = _RemoveEntry(poldCAA,
                                      cSize,
                                      cCount,
                                      fBuildAcl);
            } else
            {
                //
                // one is deny, one is allow
                //
                poldCAA->SetAccessMask(poldCAA->AccessMask() &
                                       ~(pnewCAA->AccessMask()));
            }
        }
        break;
    }
    return(status);
}
//+---------------------------------------------------------------------------
//
//  Member:     _BuildAccessEntry, private
//
//  Synopsis:   converts an AccountAccess into an access entry
//
//  Arguments:  [IN] pCAA - the AccountAccess being converted to an access entry
//              [IN] nameptr - where to put the trustee name
//              [OUT] pAccessEntry - access entry to be stuffed
//              [IN] fAbsolute - if TRUE, trustee names are pointers
//
//+---------------------------------------------------------------------------
void CAcl::_BuildAccessEntry(CAccountAccess *pCAA,
                             LPWSTR *nameptr,
                             PACCESS_ENTRY pAccessEntry,
                             BOOL fAbsolute)
{

    pAccessEntry->AccessMode = pCAA->AccessMode();
    pAccessEntry->InheritType = (pCAA->AceFlags() & ACLBUILD_VALID_ACE_FLAGS);
    pAccessEntry->AccessMask = pCAA->AccessMask();

    //
    // now to make the trustee, first the single one
    //
    if (NULL != pCAA->Name())
    {
        wcscpy(*nameptr, pCAA->Name());
    } else
    {
        wcscpy(*nameptr, NAME_NOT_FOUND);
    }
    BuildTrusteeWithName(&(pAccessEntry->Trustee), *nameptr);
    //
    // return the real (simplified) trustee type
    //
    switch (pCAA->SidType())
    {
    case SidTypeUser:
        pAccessEntry->Trustee.TrusteeType = TRUSTEE_IS_USER;
        break;
    case SidTypeGroup:
    case SidTypeDomain:
    case SidTypeAlias:
    case SidTypeWellKnownGroup:
        pAccessEntry->Trustee.TrusteeType = TRUSTEE_IS_GROUP;
        break;
    case SidTypeDeletedAccount:
    case SidTypeInvalid:
    case SidTypeUnknown:
    default:
        pAccessEntry->Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
        break;
    }
    //
    // increment to the next trustee
    //
    *nameptr = (LPWSTR)((PBYTE)(*nameptr) + (wcslen(*nameptr) + 1) * sizeof(WCHAR));

    //
    // now if there is a multiple trustee
    //
    if (pCAA->MultipleTrusteeOperation() == TRUSTEE_IS_IMPERSONATE)
    {
        //
        // make space for the impersonate trustee
        //
        PTRUSTEE pImpTrustee = (PTRUSTEE)*nameptr;
        (*nameptr) = (LPWSTR)((PBYTE)*nameptr  + sizeof(TRUSTEE));
        //
        // figure out the sid size
        // why not make the server a name based trustee??
        // answer: because it is not easier
        //
        PSID psid = pCAA->ImpersonateSid();
        ULONG sidsize = RtlLengthSid(psid);
        //
        // copy the sid, and increment the buffer pointer
        //
        RtlCopySid(sidsize, *nameptr,psid);
        //
        // connect the trustee together with the sid
        //
        BuildTrusteeWithSid(pImpTrustee, *nameptr);
        //
        // move the buffer pointer past the sid
        //
        (*nameptr) = (LPWSTR)((PBYTE)*nameptr + sidsize);
        //
        // finally turn the original trustee into an impersonate trustee
        // with the other one
        //
        BuildImpersonateTrustee(&(pAccessEntry->Trustee), pImpTrustee);
    }
}
//+---------------------------------------------------------------------------
//
//  Member:     _BuildDualAuditEntries, private
//
//  Synopsis:   converts an ACEs AccountAccess for auditing both success
//              and failed opens into two access entries
//
//  Arguments:  [IN] pCAA - the AccountAccess being converted to an access entry
//              [IN] nameptr - where to put the trustee name
//              [IN/OUT] pae - list of access entries to be stuffed
//              [IN/OUT] ccount - index into access entry list to start stuffing
//              [IN] fAbsolute - if TRUE, trustee names are pointers
//
//+---------------------------------------------------------------------------
void CAcl::_BuildDualAuditEntries(CAccountAccess *pCAA,
                             LPWSTR *nameptr,
                             PACCESS_ENTRY pae,
                             DWORD *ccount,
                             BOOL fAbsolute)
{
    //
    // first add an access entry for the successful open
    _BuildAccessEntry(pCAA,
                      nameptr,
                      &(pae[*ccount]),
                      fAbsolute);
    pae[(*ccount)++].AccessMode = SET_AUDIT_SUCCESS;

    //
    // then add an entry for the failed open
    //
    _BuildAccessEntry(pCAA,
                      nameptr,
                      &(pae[*ccount]),
                      fAbsolute);
    pae[(*ccount)++].AccessMode = SET_AUDIT_FAILURE;
}
#if 0
//+---------------------------------------------------------------------------
//
//  Member:     _ComputeEffective, private
//
//  Synopsis:   computes effective rights from an AccountAccess, including
//              doing a check for group membership
//
//  Arguments:  [IN] pCAA - the AccountAccess to test against
//              [IN] cMC - the member check class
//              [IN OUT] AllowedMask - the mask to add allowed accesses to
//              [IN OUT] DeniedMask - the mask to add denied accesses to
//
//+---------------------------------------------------------------------------
DWORD CAcl::_ComputeEffective(CAccountAccess *pCAA,
                        CMemberCheck *cMC,
                        PACCESS_MASK AllowMask,
                        PACCESS_MASK DenyMask)
{
    DWORD status = NO_ERROR;
    //
    // no access convered by ace with inherit only flag on
    //
    if (0 == (pCAA->AceFlags() & INHERIT_ONLY_ACE ) )
    {
        BOOL fresult;

        //
        // check of the trustee is a member of the account access id
        //
        status = cMC->IsMemberOf( pCAA,
                                  &fresult );
        if (status == NO_ERROR)
        {
            if (fresult == TRUE)
            {
                //
                // if a denied, then add to the deny mask
                //
                if (pCAA->AccessMode() == DENY_ACCESS)
                {
                    *DenyMask |= pCAA->AccessMask();
                //
                // if a set, then add to the allowed mask
                //
                } else if ( (pCAA->AccessMode() ==  SET_ACCESS) ||
                            (pCAA->AccessMode() ==  GRANT_ACCESS) )
                {
                    *AllowMask |= pCAA->AccessMask();
                }
            }
        }
    }
    return(status);
}
#endif

//+---------------------------------------------------------------------------
//
//  Member:     _SetAceFlags, private
//
//  Synopsis:   sets the ACE (inheritance) flag on an ACE in an ACL being
//              constructed1
//
//  Arguments:  IN - [AceIndex]  - the index of the ace
//              IN/OUT - [pacl] - the acl
//              IN - [pcaa] - the account access for the ace index
//
//----------------------------------------------------------------------------
DWORD CAcl::_SetAceFlags(ULONG AceIndex, PACL pacl, CAccountAccess *pcaa)
{
    NTSTATUS ntstatus = STATUS_SUCCESS;
    if (0 != pcaa->AceFlags())
    {
        //
        // get the ACE and set the ace flags
        //
        PACE_HEADER pah;
        if (NT_SUCCESS(ntstatus = RtlGetAce(pacl,
                                            AceIndex,
                                            (void **)&pah)))
        {
            pah->AceFlags |= (BYTE)(pcaa->AceFlags() & VALID_INHERIT_FLAGS);
        } else
        {
            return(RtlNtStatusToDosError(ntstatus));
        }
    }
    return(ERROR_SUCCESS);
}
//+---------------------------------------------------------------------------
//
//  Member:     _GetAceSize, private
//
//  Synopsis:   gets the size required for an ACE
//
//  Arguments:  IN - [pcaa]  - returns the size required for the ACE
//
//----------------------------------------------------------------------------
ULONG CAcl::_GetAceSize(CAccountAccess *pcaa)
{
    if (pcaa->AccessMode() == REVOKE_ACCESS)
    {
        return(0);
    } else if (pcaa->MultipleTrusteeOperation() == TRUSTEE_IS_IMPERSONATE)
    {
        return(RtlLengthSid(pcaa->Sid()) +
                sizeof(ACE_HEADER) +
                sizeof(ACCESS_MASK) +
                sizeof(ULONG) + // sizeof compound ace type & reserved short
                RtlLengthSid(pcaa->ImpersonateSid()) );
    } else
    {
        return(RtlLengthSid(pcaa->Sid()) +
               sizeof(ACE_HEADER) +
               sizeof(ACCESS_MASK));
    }
}
//+---------------------------------------------------------------------------
//
//  Member:     _GetAccessEntrySize, private
//
//  Synopsis:   gets the size required for an access entry
//
//  Arguments:  IN - [pcaa]  - the size required for an access entry
//
//----------------------------------------------------------------------------
DWORD CAcl::_GetAccessEntrySize(CAccountAccess *pcaa, PULONG cAccessEntrySize)
{
    DWORD status;
    LPWSTR name;

    status = pcaa->LookupName(&name);
    if (status == NO_ERROR)
    {
        *cAccessEntrySize = (name == NULL ?
                           (wcslen(NAME_NOT_FOUND) + 1) * sizeof(WCHAR) +
                           sizeof(ACCESS_ENTRY) :
                           (wcslen(name) + 1) * sizeof(WCHAR) +
                           sizeof(ACCESS_ENTRY));

        if (pcaa->MultipleTrusteeOperation() == TRUSTEE_IS_IMPERSONATE)
        {
            (*cAccessEntrySize) += (sizeof(TRUSTEE) +
                                    RtlLengthSid(pcaa->ImpersonateSid()));
        }
    }
    return(status);
}
//+---------------------------------------------------------------------------
//
//  Member:     _AddEntry , private
//
//  Synopsis:   adds an entry (either an ACE or an access entry) to the CACL
//              objects list of AccountAccess objects
//
//  Arguments:  IN - [ci]  - the iterator class
//              OUT - [pcaa] - the account access class for the entry
//              OUT - [pcaaindex] the index of the entry
//
//----------------------------------------------------------------------------
DWORD CAcl::_AddEntry(CIterator *ci,
                        CAccountAccess **pcaa,
                        PULONG pcaaindex)
{

    DWORD status;


    //
    // allocate space for the account access class
    //
    if (NULL != (*pcaa = new CAccountAccess()))
    {
        //
        // initialize the account access
        //
        status = ci->InitAccountAccess( *pcaa,
                                        _system,
                                        _fdir,
                                        _fsave_names_and_sids );
        if (status == NO_ERROR)
        {
            (*pcaaindex)++;

        }
        else
        {
            delete *pcaa;
        }
    }
    else
    {
        status = ERROR_NOT_ENOUGH_MEMORY;
    }


    //
    // Map ERROR_BAD_INHERITANCE_ACL to NO_ERROR. That error is returned
    // when an ACE is inherit-only, so we don't want it in the ACL entry.
    // We do, however, want to skip the ACE so it is good that hte
    // index is not incremented.
    //
    //
    // JINHUANG: InitAccountAccess is changed so that INHERIT_ONLY ace
    // is also added to the list. So this error should never be returned
    //

    if (status == ERROR_BAD_INHERITANCE_ACL)
    {
        status = NO_ERROR;
    }

    return(status);
}
//+---------------------------------------------------------------------------
//
//  Member:     _CheckForDuplicateEntries, private
//
//  Synopsis:   checks for duplicate AccountAccess objects in the CACLs list of
//              AccountAccess objects.
//
//  Arguments:  IN - [pcaa]  - the list of account access objects
//              IN - [curindex] - the account access just added (at the end)
//              IN - [countold] - the number of account accesses in the list
//
//----------------------------------------------------------------------------
DWORD CAcl::_CheckForDuplicateEntries(CAccountAccess **pcaa,
                                        ULONG curindex,
                                        ULONG countold)
{

    DWORD status = NO_ERROR;


    //
    // loop thru the existing AccountAccesses to see if the latest matches
    //
    for (ULONG idx = 0; idx < countold; idx++)
    {
        if (RtlEqualSid(pcaa[idx]->Sid(), pcaa[curindex]->Sid()))
        {
            pcaa[idx]->SetAccessMode(NOT_USED_ACCESS);
        }
    }
    //
    // loop thru the new AccountAccesses to see if the latest matches
    // (they start at countold, which is where idx is left at after
    // the last loop)
    //
    for (; idx < curindex; idx++)
    {
        if (RtlEqualSid(pcaa[idx]->Sid(), pcaa[curindex]->Sid()))
        {
            status =ERROR_INVALID_PARAMETER;
            break;
        }
    }

    return(status);
}
