Win32 Security in Managed C++

An access token helps track users as they log on and determine what operations the user is allowed to perform. Here's an AccessToken class in Managed C++, which is used to create a Security Explorer that displays a standard tree/list view, where the tree contains composite or collection entities, and the list contains the values or attributes.


September 01, 2003
URL:http://www.drdobbs.com/security/win32-security-in-managed-c/184416685

Win32 Security in Managed C++

Download the code for this issue

Surviving the Win32 Security API
Strings in Managed C++
Managed C++ Hasn't Got Any Friends!


Although .NET provides its own security model (in the System::Security namespace) for CLR security, it doesn't address the lurking behemoth that is the Win32 security subsystem. For those who have programmed with Win32 security, you'll know that while it's conceptually straightforward, it's a real bear to program. For example, when writing a program that requires certain rights or privileges to work, it can often be a very intensive process to determine what privileges one has, or what rights are required.

In this article, I'll demonstrate how this coding headache can be simplified as I develop a .NET library, in Managed C++, that encapsulates the programming details in a simple object model. Along the way, we'll have a quick refresher on Win32 security, demonstrate some techniques for simplifying coding with the Win32 security APIs, and learn some of the restrictions and gotchas of Managed C++.

The article will be based on the first version of the .NET Win32 Security component I'm developing, and will describe the implementation of the AccessToken class and supporting types. It will also show how this object model can be utilized in a UI client, Security Explorer, written in C#.

Win32 Security Model Whistlestop

Win32 security is a big topic, which would require many articles to do it justice. (For those who want to learn more, I recommend Nik Okuntseff's book Windows NT Security as a good place to start.) I'm going to restrict myself in this article to a discussion of access tokens. An access token is created for each user when the user logs on, and persists for the duration of that user's session. Every process that the user executes receives a copy of that access token, which is then used by the security subsystem to determine what operations the process (i.e., the user) is allowed to perform.

An access token contains, among other things, the following items:

AccessToken

The SynSoft::Security::AccessToken class is defined, in Managed C++, as shown in Listing 1. The first thing you'll note is that it derives from the interface System::IDisposable. This interface marks the class as implementing the Dispose pattern, which means that the class can be used in a C# using expression (providing Resource Acquisition Is Initialization, albeit user-requested rather than class-mandated) and also is amenable to generalized closure: One simply dynamically casts (using dynamic_cast in C++ or as in C#) to IDisposable, then calls Dispose() on a (nonnull) resultant reference. AccessToken, like any class that owns/manages a native resource (in this case the access token) provides this facility so that well-behaved clients can cause it to relinquish its resources in a timely fashion, rather than waiting for garbage collection. Furthermore, it is my practice to provide a Close() method whenever implementing IDisposable, which performs the same task as Dispose(). You'll see this in other classes in this library.

Next, it is clear that one cannot construct an instance of an AccessToken. This makes sense, since we cannot create an access token in Win32 land — we can only open one via the Win32 functions OpenProcessToken() and OpenThreadToken(). AccessToken has two corresponding static methods that do exactly the same job, and then return an AccessToken instance representing the (successfully opened) token. So we can open a token and we can close/dispose of it. Now what we're interested in are its attributes.

The current version of this library contains read-only components. (Create/edit functionality is planned in a future release; visit http:// synsoft.org/dotnet.html to obtain updates.) We can access, as .NET properties, the user and primary group, the groups, the privileges, the default DACL, and a variety of other attributes associated with an access token. The types returned (e.g., SID, GroupList, PrivilegeList) are also classes in the same namespace (and assembly). Most of these classes are also required to implement the IDisposable pattern; this is one of the hassles of .NET's not supporting C++'s deterministic destructors. Consider the GroupList class (Listing 2). This class does not contain any unmanaged resources. In fact, its only member is an ArrayList. However, because the list is populated with Group instances in the GroupList constructor, GroupList must implement the IDisposable interface, and in its Dispose() method must iterate through the list — ArrayList does not implement IDisposable — and call Dispose() on each contained object (in this case, Group instance). This stuff is tedious, and also carries a bit of a catch. Sometimes a class will implement IDisposable but will make its Dispose() method nonpublic; this is known as Explicit Interface Member Implementation. Hence, you need to cast the contained elements to IDisposable before attempting to call Dispose(). Because this is a common scenario, I've used the .netSTL (.netSTL is a subproject of STLSoft; http://dotnetstl.org/) algorithm dispose_contents<>() (shown in Listing 3, with its supporting function dispose_set_null<>()).

The picture doesn't end there, alas. Group also does not contain any unmanaged resources, but it has an instance of class SID, which does — a Win32 SID blob, in fact (see Listing 4). Thus, we had to add Dispose() (and Close()) functionality to at least two other types because a composed type requires it. Of course, you may think why bother, the Win32 SID is only a blob of memory (albeit from the native rather than the managed heap)? Well, as a rule, I don't feel comfortable having any unmanaged resources hanging around for an indefinite time, and one might ponder the case where the resource is not a block of memory but a file handle. This is a fundamental problem of garbage-collected systems; sure, they handle memory fine (often better than deterministically managed systems), but what about all the other resource types (files, pipes, and heaven-forefend, synchronization objects!)?

Having had my C++-biased rant, let's see what's good about this library. Looking into the implementation of many of the property accessor methods of AccessToken (Listing 5), we can see one nice feature. Since all managed types must be held by pointer, the implementation dilemma common in object models — do I use composition to reduce memory usage and increase execution speed, or use pimpl to reduce memory usage and increase execution and compilation speed? — is moot. We have to use pointers, and it's often best to choose lazy evaluation. This may or may not be the "best" thing from a pure programming perspective, but it simplifies the design, coding, and maintenance of modules, so it has a lot going for it. A side effect is that it will help to ameliorate the dangling unmanaged resource issue; in cases where they are not used, they're not created, and so not dangling.

So how do we get information out of an access token? GetTokenInformation(), of course. This very powerful function is prototyped as follows:

BOOL WINAPI GetTokenInformation (

HANDLE TokenHandle,

TOKEN_INFORMATION_CLASS TokenInfClass,

LPVOID TokenInformation,

DWORD TokenInfLength,

PDWORD ReturnLength);

You pass a token handle, the type of information you want (a member of the TOKEN_INFORMATION_CLASS enumeration), a destination buffer and its length (in bytes), and an address to receive the number of bytes of information retrieved. Seems straightforward, but like most Win32 Security API functions, it can be tricky. Some of the access token information is of a fixed size, and so one simply passes the appropriate parameters, as can be seen in the implementation of AccessToken::get_Type(). In most cases, however, the information is of variable length, so one must call GetTokenInformation() as described in the sidebar titled "Surviving the Win32 Security API." This approach is repetitive and error-prone — what if there is an exception thrown between the allocation and deallocation of the resource? A lot of careful boilerplate is required (see AccessToken::_get_groups() in Listing 5; this implementation is chosen because both TokenGroups and TokenRestrictedSids return information in a TOKEN_GROUPS structure). Mercifully, there is a simple and elegant solution in the form of the WinSTL (another STLSoft subproject; http://winstl.org/) component token_information<> (see Listing 6). You parameterize the template using the appropriate TOKEN_INFORMATION_CLASS enumeration member, and the traits mechanism automatically deduces the correct data structure type and instantiates a class that wraps the allocation, acting as a smart pointer for the requisite type. The get_User() and get_Privileges() methods in Listing 5 demonstrate how much this simplifies matters. (Note that the template is flexible in allowing you to specify an exception policy. The default is stlsoft::null_exception, which does not throw, in which case the implementation ensures that GetLastError() will reflect the failing condition, rather than being overwritten during deallocation of the memory buffer.)

The difficulty of processing the information returned depends on the type of information retrieved. Retrieving SECURITY_IMPERSONATION_LEVEL, which is an enumeration (as is TOKEN_TYPE), is very simple. Just retrieve it and convert it to the appropriate managed enumeration type. Note: It's convenient, but somewhat unsafe, that Managed C++ lets you cast, in the form of a conversion constructor, from a C-type to a managed enumeration type; see AccessToken::get_Type(). Although I've borrowed the values for the managed enumerations TokenType, TokenImpersonationLevel, and SidType from their native equivalents, and am confident I've transcribed them correctly, what if I got it wrong?)

For some variable sized entities, it is also reasonably easy to process the retrieved information. Retrieving groups or privileges returns counted arrays of SID_AND_ATTRIBUTES and LUID_AND_ATTRIBUTES, respectively, so one need simply walk the array, as in the GroupList constructor (Listing 2).

Access Control Lists and Entries

The retrieval of the default DACL from an access token is an altogether more complex matter. As well as retrieving a variable length structure, the items within the structure are polymorphic (can be one of the three ACE types), and are themselves of variable length. Nasty.

The answer comes from a combination of another useful class from WinSTL — the acl_sequence<> template (not shown; available from http://winstl.org/downloads.html) — and a hacky assumption. Since all the ACE structures have the same essential structure (which can't be a coincidence — I bet the designers of the Win32 Security API decided in this one small way to give themselves a break), we can treat them as sharing a pseudotype (wherein ACE_HEADER) to treat them all equally. Listing 7 shows the solution in the ACL constructor. acl_sequence<> takes care of defining the range [begin, end), and ensuring the appropriate pointer-arithmetic when moving from one ACE to the next. Inside the loop, we switch on the header type, and then call the appropriate ACE constructor. AccessAllowedACE, AccessDeniedACE, and SystemAuditACE share a common base class, ACE, which handles the retrieval of the ACE structure contents into the class in a generalized (based on their common layouts) fashion. As I said, it's a hack, but since it would be impossible to change the sizes of the extant ACE types without breaking every Win32 program out there that uses security, I think we can say it is an informed hack and sleep soundly.

Security Explorer

That's pretty much all the ugly stuff in the library; the rest is very straightforward and I'll leave it to you to download the archive and look through it. Let's see how we can use it. The Security Explorer program is shown in Figure 1. As you can see, it has a standard tree/list format, where the tree contains composite or collection entities, and the list contains the values or attributes. This first version allows exploration of the process access token. The root-most node, when selected, displays the user, owner, primary group, and the like, and then the five subnodes show the contents of each of their collections.

The Security Explorer works by opening the access token for the process, by calling AccessToken::OpenProcessToken. It stores this along with an identifying type in an instance of a NodeItem type, which is then stored as the Tag value (LPARAM in Win32 API parlance) of the tree node. The collection properties (Groups, Privileges, etc.) are similarly stored in subnodes. When the user clicks on a node, the handler loads the stored object (and its type), and processes it according to the type. It's not particularly elegant — a more professional solution would be to use polymorphic display-handler classes — but it serves to exercise the security object model. I'm already finding this useful, since it's far and away the quickest mechanism I have to tell me what privileges I have, and whether they're enabled, on a given system.

Summary

This article has given a very brief introduction to the Win32 security subsystem, discussing in some detail the contents of access tokens and how to read them. Hopefully, this has whetted your appetite and shown you that, in combination with suitable helper code, manipulating the security API is not as scary as you might have thought. We all avoid it like the plague as a rule, but when you need to secure your application objects, you have no choice but to dive in. Now, perhaps, you'll have a bit of background info to help.

We've also discussed some of the implications of development in Managed C++, and highlighted some of the issues you encounter with this new language. Finally, I hope the point is not lost that we've tamed a daunting C API by applying some very modern C++ (STL) techniques. Over this, we've layered a Managed C++ object model, which was then used in a C# application. How's that for integration?

Notes & References

Applied .NET Framework Programming. Jeffrey Richter, Microsoft Press, 2002.

Windows NT Security. Nik Okuntseff, R & D Books, 1997.

STLSoft is an open-source organization applying STL techniques to a variety of technologies and operating systems. It is located at http://stlsoft.org/. WinSTL and .netSTL are subprojects, and are located at http://winstl.org/ and http://dotnetstl.org/, respectively.

SynSoft is the nonprofit imprint of my company, Synesis Software, and is located at http://synsoft.org/. It provides D, .NET, Java, Perl, and Python code/components free for use without royalty.w::d


Matthew Wilson holds a degree in Information Technology and a Ph.D. in Electrical Engineering, and is a software development consultant for Synesis Software. Matthew's work interests are in writing bullet-proof real-time, GUI and software-analysis software in C, C++, C#, and Java. He has been working with C++ for over 10 years, and is currently bringing the STLSoft project into the public domain. Matthew can be contacted via [email protected] or at http://stlsoft.org/.

Win32 Security in Managed C++

Figure 1 Security Explorer Interface

Win32 Security in Managed C++

Listing 1 The AccessToken class declaration


namespace SynSoft
{
  namespace Security
  {
    /// Represents a security AccessToken
    public __gc class AccessToken
      : public IDisposable
    {
    private:
      typedef void  *HANDLE;

    private:
      /// Create an instance managing the given security handle
      AccessToken(HANDLE hToken);
      /// Finaliser
      ~AccessToken();

    // IDisposable
    public:
      /// Provides facility for 
      void  Close();
      void  Dispose();

    // Creation
    public:
      /// Open the access token for the current process
      static AccessToken *OpenProcessToken();
      /// Open the access token for the current thread
      static AccessToken *OpenThreadToken();

    // Attributes
    public:
      /// The token's user account
      __property SID *get_User();
      /// The groups accounts associated with the access token
      __property GroupList *get_Groups();
      /// Privileges
      __property PrivilegeList *get_Privileges();
      /// The security identifier of the owner
      __property SID *get_Owner();
      /// The primary groups of any objects created with the access token
      __property SID *get_PrimaryGroup();
      /// Default DACL
      __property ACL *get_DefaultDACL();
      /// Source
      __property TokenSource *get_Source();
      /// Type
      __property TokenType get_Type();
      /// Impersonation Level
      __property TokenImpersonationLevel get_ImpersonationLevel();
      /// Statistics
      __property TokenStatistics *get_Statistics();
      /// Restricted SIDs
      __property GroupList *get_RestrictedSIDs();
      /// Session Id
      __property Int32 get_SessionId();
      /// Sandbox Inert
      __property bool get_SandboxInert();

    // Standard members
    public:
      // Returns a string representation of the AccessToken
      String  *ToString();

    // Implementation
    private:
      GroupList *_get_groups(TOKEN_INFORMATION_CLASS tic);

    // Members
    private:
      HANDLE          m_hToken;
      SID             *m_user;
      GroupList       *m_groups;
      PrivilegeList   *m_privileges;
      SID             *m_owner;
      SID             *m_primaryGroup;
      ACL             *m_defaultDACL;
      TokenSource     *m_source;
      TokenStatistics *m_statistics;
      GroupList       *m_restrictedSIDs;
    };
  }
}

Win32 Security in Managed C++

Listing 2 The GroupList class



namespace SynSoft
{
  namespace Security
  {
// GroupList.h

    /// Represents a list of groups
      public __gc class GroupList
      : public IEnumerable
      , public IDisposable
    {
    public:
      GroupList(int cGroups, SID_AND_ATTRIBUTES *groups);

    // IDisposable
    public:
      void  Close();
      void  Dispose();

    // IEnumerable
    public:
      IEnumerator *GetEnumerator();

    // Members
    private:
      ArrayList *m_groups;
    };

// GroupList.cpp

    GroupList::GroupList(int cGroups, SID_AND_ATTRIBUTES *groups)
      : m_groups(new ArrayList(cGroups))
    {
      for(int i = 0; i < cGroups; ++i)
      {
        m_groups->Add(new Group(new SID(groups[i].Sid), groups[i].Attributes));
      }
    }

    void GroupList::Close()
    {
      if(0 != m_groups)
      {
        dispose_contents(m_groups);
        m_groups->Clear();

        m_groups = 0;
      }
    }
  }
}

Win32 Security in Managed C++

Listing 3 IDisposable helper functions from .netSTL


/* /////////////////////////////////////////////////////////////
 * ...
 *
 * Extract from dotnetstl_dispose_functions.h
 *
 * Copyright (C) 2003, Synesis Software Pty Ltd.
 * (Licensed under the Synesis Software Standard Source License:
 *  http://www.synesis.com.au/licenses/ssssl.html)
 *
 * ...
 * ////////////////////////////////////////////////////////// */

namespace dotnetstl
{
  /// Disposes the managed type, and resets the pointer
  template <ds_typename_param_k T>
  inline void dispose_set_null(T *&pt)
  {
    if(0 != pt)
    {
      System::IDisposable *disposable = pt;

      disposable->Dispose();
      pt = 0;
    }
  }

  /// Disposes all the items in a container
  template <ds_typename_param_k C>
  inline void dispose_contents(C *pc)
  {
    for(int i = 0, count = pc->get_Count(); i < count; ++i)
    {
      System::IDisposable *o  = dynamic_cast<System::IDisposable*>(pc->get_Item(i));

      dispose_set_null(o);
    }
  }

} // namespace dotnetstl

Win32 Security in Managed C++

Listing 4 Extracts from SID.h & SID.cpp


namespace SynSoft
{
  namespace Security
  {
// SID.h

    public __gc class SID
      : public IDisposable
    {
    public:
      SID(void *psid);
      ~SID();

    // IDisposable
    public:
      void  Close();
      void  Dispose();

    // Properties
    public:
      // Returns a string representation of the SID
      String *ToString();

      // The type of the SID
      __property SidType get_Type();
      // The name of the user associated with the SID
      __property String *get_UserName();
      // The name of the domain associated with the SID
      __property String *get_DomainName();

    // Members
    private:
      void    *m_psid;
      SidType m_type;
      String  *m_userName;
      String  *m_domainName;
      String  *m_stringForm;
    };

// SID.cpp

    SID::SID(void *psid)
      : m_psid(Sec_SidDup(psid))
      , m_type(SidType::Unknown)
      , m_userName(0)
      , m_domainName(0)
      , m_stringForm(0)
    {
      // Now we get the user and domain names

      if(0 == psid)
      {
        throw new SecurityException(E_INVALIDARG, "NULL SID");
      }
      else if(!::IsValidSid(psid))
      {
        throw new SecurityException(::GetLastError(), "Invalid SID");
      }
      else
      {
          . . . 
      }
    }

    SID::~SID()
    {
      if(0 != m_psid)
      {
        Sec_Free(m_psid);
      }
    }

    void SID::Close()
    {
      if(0 != m_psid)
      {
        Sec_Free(m_psid);
        m_psid = 0;
      }
    }

    void SID::Dispose()
    {
      Close();
    }

    . . .
  }
}

Win32 Security in Managed C++

Listing 5 Extract from AccessToken.cpp


SID *AccessToken::get_User()
{
  if(0 == m_user)
  {
    token_information<TokenUser>  user(m_hToken);

    if(!user)
    {
      throw new SecurityException(::GetLastError());
    }
    else
    {
      m_user = new SID(user->User.Sid);
    }
  }

  return m_user;
}

GroupList *AccessToken::get_Groups()
{
  if(0 == m_groups)
  {
    m_groups = _get_groups(TokenGroups);
  }

  return m_groups;
}

GroupList *AccessToken::get_RestrictedSIDs()
{
  if(0 == m_restrictedSIDs)
  {
    m_restrictedSIDs = _get_groups(TokenRestrictedSids);
  }

  return m_restrictedSIDs;
}

PrivilegeList *AccessToken::get_Privileges()
{
  if(0 == m_privileges)
  {
    winstl::token_information<TokenPrivileges>  privileges(m_hToken);

    if(!privileges)
    {
      throw new SecurityException(::GetLastError());
    }
    else
    {
      m_privileges = new PrivilegeList(privileges->PrivilegeCount, 		   privileges->Privileges);
    }

    return m_privileges;
  }

  return m_privileges;
}

TokenType AccessToken::get_Type()
{
  TOKEN_TYPE  tt;
  DWORD       chRequired;

  if(!::GetTokenInformation(m_hToken, ::TokenType, 	           &tt, sizeof(tt), &chRequired))
  {
    throw new SecurityException(::GetLastError(), 	           "Could not elicit type from token");
  }

  return TokenType(tt);
}

GroupList *AccessToken::_get_groups(TOKEN_INFORMATION_CLASS tic)
{
  GroupList *grouplist;
  DWORD     cbRequired;
  DWORD     dwErr;

  stlsoft_assert(tic == TokenGroups || tic == TokenRestrictedSids);

  ::GetTokenInformation(m_hToken, tic, NULL, 0, &cbRequired);
  dwErr = ::GetLastError();

  if(ERROR_INSUFFICIENT_BUFFER != dwErr)
  {
    throw new SecurityException(dwErr);
  }
  else
  {
    TOKEN_GROUPS  *groups = static_cast<TOKEN_GROUPS*>(Sec_Alloc(cbRequired));

    if(!::GetTokenInformation(m_hToken, tic, groups, cbRequired, &cbRequired))
    {
      throw new SecurityException(::GetLastError());
    }
    else
    {
      try
      {
        grouplist = new GroupList(groups->GroupCount, groups->Groups);
      }
      catch(Exception *x)
      {
        Sec_Free(groups);

        throw x;
      }
    }
  }

  return grouplist;
}

Win32 Security in Managed C++

Listing 6 WinSTL's token_information helper template


/* /////////////////////////////////////////////////////////////
 * ...
 *
 * Extract from winstl_token_information.h
 *
 * Copyright (C) 2003, Synesis Software Pty Ltd.
 * (Licensed under the Synesis Software Standard Source License:
 *  http://www.synesis.com.au/licenses/ssssl.html)
 *
 * ...
 * ////////////////////////////////////////////////////////// */

namespace winstl
{
  template <TOKEN_INFORMATION_CLASS C>
  struct token_information_traits;


  template <>
  struct token_information_traits<TokenUser>
  {
    typedef TOKEN_USER  data_type;
  };

  ... // More specialisations of token_information_traits

  template< TOKEN_INFORMATION_CLASS C
          , ws_typename_param_k     X = stlsoft::null_exception
          , ws_typename_param_k     D = token_information_traits<C>::data_type
          , ws_typename_param_k     A = processheap_allocator<ss_byte_t>
          >
  class token_information
  {
  public:
    typedef token_information<C, X, D, A> class_type;
    typedef token_information_traits<C>   traits_type;
    typedef X                             exception_thrower_type;
    typedef D                             data_type;
    typedef A                             allocator_type;

  // Construction
  public:
    /// Constructs an instance from the given access token
    ws_explicit_k token_information(HANDLE hToken)
      : m_data(0)
    {
      DWORD cbRequired;
      DWORD dwError;

      ::GetTokenInformation(hToken, C, NULL, 0, &cbRequired);
      dwError = ::GetLastError();
      if(ERROR_INSUFFICIENT_BUFFER != dwError)
      {
        // Report error
        exception_thrower_type()(dwError);
      }
      else
      {
        data_type *data = (data_type*)allocator_type().allocate(cbRequired);

        if(NULL == data)
        {
          // Report error
          exception_thrower_type()(ERROR_NOT_ENOUGH_MEMORY);

          // Set the last error, in case the client code is not           // using exception reporting
          ::SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        }
        else
        {
          if(!::GetTokenInformation(hToken, C, data, cbRequired, &cbRequired))
          {
            // Scope the last error, in case the client code is not             // using exception reporting
            dwError = ::GetLastError();

            allocator_type().deallocate((ss_byte_t*)data);

            // Report error
            ::SetLastError(dwError);
            exception_thrower_type()(dwError);
          }
          else
          {
            // Success
            m_data = data;

            ::SetLastError(ERROR_SUCCESS);
          }
        }
      }
    }
    ~token_information()
    {
      allocator_type().deallocate((ss_byte_t*)m_data);
    }

  // Conversion
  public:
    operator data_type *();
    operator data_type const *() const;

    data_type *operator ->();
    data_type const *operator ->() const;

    ws_bool_t operator !() const;

  /// Members
  private:
    data_type *m_data;

  /// Not to be implemented
  private:
    token_information(token_information const &);
    token_information &operator =(token_information const &);
  };

} // namespace winstl

Win32 Security in Managed C++

Listing 7 Enumerating an ACL


// from ACL.h

namespace SynSoft
{
  namespace Security
  {
    public __gc class ACL
      : public IEnumerable
      , public IDisposable
    {
    public:
      ACL(PACL pacl);

    // IDisposable
    public:
      void  Close();
      void  Dispose();

    // IEnumerable
    public:
      IEnumerator* GetEnumerator();

    // Properties
    public:
      __property Int32 get_Count();

      String  *ToString();

    // Members
    private:
      UInt32    m_revision;
      ArrayList *m_aces;
    };
  }
}

// GroupList.cpp

ACL::ACL(PACL pacl)
  : m_aces(new ArrayList(pacl->AceCount))
  , m_revision(pacl->AclRevision)
{
using winstl::acl_sequence;

acl_sequence           aces(pacl);
acl_sequence::const_iterator  begin = aces.begin();
acl_sequence::const_iterator  end   = aces.end();

  for(; begin != end; ++begin)
  {
    PACE_HEADER header  = *begin;

    switch(header->AceType)
    {
      case  ACCESS_ALLOWED_ACE_TYPE:
        m_aces->Add(new AccessAllowedACE(header));
        break;
      case  ACCESS_DENIED_ACE_TYPE:
        m_aces->Add(new AccessDeniedACE(header));
        break;
      case  SYSTEM_AUDIT_ACE_TYPE:
        m_aces->Add(new SystemAuditACE(header));
        break;
    }
  }
}

Surviving the Win32 Security API

Surviving the Win32 Security API

If there are two things you should remember above all others when working with the Win32 Security API, they are:

1. Nearly everything is variable length. Not only does this mean you should make no assumptions about being able to iterate through composite/collection types in an integral fashion, but you should also never assume that things are of a predeterminable size.

2. The common paradigm is Size-Allocate-Retrieve, as we saw with GetTokenInformation(). You call a function with a null destination pointer and give an initial size of 0. It will return into your size parameter the required size (and set the last error to ERROR_INSUFFICIENT_BUFFER). You allocate an appropriately sized buffer and call again, and the function will fill your buffer with the desired information.

— M.W.

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.