Windows 2000 file security: security descriptors

Security descriptors are data structures associated with system objects (eg files and directories) which describe one of the following:

I suppose you could think of security descriptors as being similar to Unix stat() buffers.

To obtain a security descriptor for a file or directory, we use the GetFileSecurity() function:

int GetFileSecurity(char *path, SECURITY_INFORMATION *si, SECURITY_DESCRIPTOR *sd, unsigned long size, unsigned long *reqsize);

Let's look at the arguments to this function.

char *path
The path to the file for which the security descriptor will be obtained.

SECURITY_INFORMATION *si
A parameter which specifies the type of security descriptor is required:

OWNER_SECURITY_INFORMATION
Retrieve a security descriptor with information about the object's owner.
GROUP_SECURITY_INFORMATION
Retrieve a security descriptor with information about the object's primary group.
DACL_SECURITY_INFORMATION
Retrieve a security descriptor with information about the object's discretionary access control list.
SACL_SECURITY_INFORMATION
Retrieve a security descriptor with information about the object's system access control list.

SECURITY_DESCRIPTOR *sd
Address of a buffer large enough to contain the security descriptor.

unsigned long size
Size of the buffer pointed to by sd above.

unsigned long *reqsize
Pointer to an int which will be set to the size of buffer needed to contain the security descriptor.

Like many of Microsoft's security functions, GetFileSecurity() expects a pointer to a buffer which is large enough to accomodate the information you are requesting. If, however, that buffer is too small, the function will fail gracefully and let you know how big your buffer neds to be.

Therefore, our strategy for using this function is to call it once with a buffer we know is too small, hence finding out how big it needs to be, allocate enough memory for it then finally call the function again, passing the new buffer.

Using the security descriptor

Once we have a security descriptor, we need to extract the information we want using one of the functions:

int GetSecurityDescriptorOwner(SECURITY_DESCRIPTOR *sd, SID **owner, int *defaulted);
int GetSecurityDescriptorGroup(SECURITY_DESCRIPTOR *sd, SID **group, int *defaulted);
int GetSecurityDescriptorDacl(SECURITY_DESCRIPTOR *sd, int *present, ACL **dacl, int *defaulted);
int GetSecurityDescriptorSacl(SECURITY_DESCRIPTOR *sd, int *present, ACL **sacl, int *defaulted);

Each of these functions behave in a similar way. They take a security descriptor as a first argument and fill in pointers to the information you want. Note that because they are extracting pointers from the security descriptor, you don't need to free the addresses which are returned.

The two ACL functions also fill in information related to whether an ACL is actually present. If present is non-zero, an ACL is contained in the descriptor. The ACL pointer might itself be null, which is different to no ACL at all.

Consult the SDK documentation for details on the defaulted flag.

Sample code

Finding a file's owner

#include <stdio.h>
#include <windows.h>

int main(int argc, char **argv) {
  SECURITY_DESCRIPTOR *sd;
  SID *owner;
  SID_NAME_USE snu;
  char *name, *domain;
  int defaulted, error;
  unsigned long size, domainsize;

  if (argc == 1) {
    fprintf(stderr, "Usage: %s pathspec\n", argv[1]);
    exit(1);
  }

  /* First find out how big the buffer needs to be */
  size = 0;
  GetFileSecurity(argv[1], OWNER_SECURITY_INFORMATION, 0, 0, &size);

  /* Should have failed with ERROR_INSUFFICIENT_BUFFER */
  error = GetLastError();
  if (error != ERROR_INSUFFICIENT_BUFFER) {
    fprintf(stderr, "Error getting size of owner SD: %d\n", error);
    exit(2);
  }

  /* Now size contains the size of buffer needed */
  sd = (SECURITY_DESCRIPTOR *) HeapAlloc(GetProcessHeap(), 0, size);
  if (! sd) {
    fprintf(stderr, "Out of memory for security descriptor!\n");
    exit(3);
  }

  /* Call GetFileSecurity() for real */
  if (! GetFileSecurity(argv[1], OWNER_SECURITY_INFORMATION, sd, size, &size)) {
    fprintf(stderr, "Error getting owner SD: %d\n", GetLastError());
    HeapFree(GetProcessHeap(), 0, sd);
    exit(4);
  }

  /* Extract the owner from the security descriptor */
  if (! GetSecurityDescriptorOwner(sd, &owner, &defaulted)) {
    fprintf(stderr, "Error getting owner from SD: %d\n", GetLastError());
    HeapFree(GetProcessHeap(), 0, sd);
    exit(5);
  }

  /* Get size of buffer needed to store account from SID */
  name = domain = 0;
  size = domainsize = 0;
  LookupAccountSid(0, owner, name, &size, domain, &domainsize, &snu);

  error = GetLastError();
  if (error != ERROR_INSUFFICIENT_BUFFER) {
    fprintf(stderr, "Error getting size of buffers for account and domain: %d\n", error);
    HeapFree(GetProcessHeap(), 0, sd);
    exit(6);
  }

  /* Allocate memory */
  name = (char *) HeapAlloc(GetProcessHeap(), 0, size);
  if (! name) {
    fprintf(stderr, "Out of memory for account name!\n");
    HeapFree(GetProcessHeap(), 0, sd);
    exit(7);
  }

  domain = (char *) HeapAlloc(GetProcessHeap(), 0, domainsize);
  if (! domain) {
    fprintf(stderr, "Out of memory for domain name!\n");
    HeapFree(GetProcessHeap(), 0, name);
    HeapFree(GetProcessHeap(), 0, sd);
    exit(8);
  }

  /* Extract name and domain */
  if (! LookupAccountSid(0, owner, name, &size, domain, &domainsize, &snu)) {
    fprintf(stderr, "Error getting account name from owner SID: %d\n", GetLastError());
    HeapFree(GetProcessHeap(), 0, domain);
    HeapFree(GetProcessHeap(), 0, name);
    HeapFree(GetProcessHeap(), 0, sd);
    exit(9);
  }

  /* Print them */
  if (*domain) printf("%s\\", domain);
  printf("%s\n", name);

  /* Clean up */
  HeapFree(GetProcessHeap(), 0, domain);
  HeapFree(GetProcessHeap(), 0, name);
  HeapFree(GetProcessHeap(), 0, sd);

  exit(0);
}

A full application, viewacls, is described in the final part of this article.