Windows 2000 file security: SIDs

A SID is a security identifier. It is somewhat analogous to a Unix UID. According to the SDK documentation, it is a "variable-length structure used to uniquely identify users or groups." Every account on a Windows 2000 system has its own SID.

Though they are stored as binary data, SIDs can be represented as strings. For example, the local Administrators group has the SID S-1-5-32-544. The API provides two functions to handle conversion to and from this string format:

int ConvertSidToStringSid(SID *sid, char **stringsid);
int ConvertStringSidToSid(char *stringsid, SID **sid);

ConvertSidToStringSid() takes a SID and a pointer to a char pointer as arguments. It returns 1 on successful conversion, in which case stringsid points to a null-terminated string representation of sid. Memory does not need to be allocated for stringsid prior to calling this function but it must be freed afterwards.

Similarly, ConvertStringSidToSid() takes a char pointer and a pointer to a SID pointer and returns 1 on successful conversion, in which case the buffer to which sid points must be freed.

The SDK also describes several other functions for manipulating SIDs. In particular, IsValidSid() is useful (and fairly self-explanatory).

int IsValidSid(SID *sid);

Sample code

Converting a SID to its string representation

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

SID *sid; /* Obtained from somewhere */
char *stringsid;

if (! ConvertSidToStringSid(sid, &stringsid)) /* Error */

printf("%s\n", stringsid);
HeapFree(GetProcessHeap(), 0, stringsid);

Converting a string representation to a SID

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

char *stringsid; /* Obtained from somewhere */
SID *sid;

if (! ConvertStringSidToSid(stringsid, &sid)) {
  switch GetLastError() {
    case ERROR_INVALID_SID:
      /* String doesn't represent a valid SID */
      break;
    case ERROR_INVALID_PARAMETER:
      /* Some other error */
      break;
  }
}

HeapFree(GetProcessHeap(), 0, sid);

Relationship between SIDs and accounts

As you would expect, it is a relatively painless process to find the account name associated with a SID or the SID for a given account.

int LookupAccountName(char *computer, char *account, SID **sid, unsigned long *size, char *domain, unsigned long *domainsize, SID_NAME_USE *snu);
int LookupAccountSid(char *computer, SID *sid, char *name, unsigned long *size, char *domain, unsigned long *domainsize, SID_NAME_USE *snu);

Upon successfully mapping a SID to account name or vice versa, these functions return 1. As with many other security functions, they expect to be passed the addresses of buffers large enough to receive the requested data, and will fill in the size parameters with the required size if those buffers are too small.

Note that contrary to what you might expect, LookupAccountName() does NOT accept a domain name as input. The domain is returned along with the SID.

Sample Code

Finding the SID of a local account

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

SID *sid;
SID_NAME_USE snu;
char *name; /* The name to look up */
char *domain;
char *stringsid;
int error;
unsigned long size, domainsize;

/* First find the size of buffers required for the SID and domain name */
sid = 0;
domain = 0;
size = domainsize = 0;
LookupAccountName(0, name, sid, &size, domain, &domainsize, &snu);

/* Should have failed with ERROR_INSUFFICIENT_BUFFER */
error = GetLastError();
if (error != ERROR_INSUFFICIENT_BUFFER) /* Error */

/* Allocate memory */
sid = (SID *) HeapAlloc(GetProcessHeap(), 0, size);
if (! sid) /* Error */
domain = (char *) HeapAlloc(GetProcessHeap(), 0, domainsize);
if (! domain) /* Error */

/* Get the SID */
if (! LookupAccountName(0, name, sid, &size, domain, &domainsize, &snu)) /* Error */
if (! ConvertSidToStringSid(sid, &stringsid)) /* Error */

/* Print the user name and SID */
if (*domain) printf("%s\\", domain);
printf("%s: %s\n", name, stringsid);

/* Cleanup */
HeapFree(GetProcessHeap(), 0, sid);
HeapFree(GetProcessHeap(), 0, stringsid);
HeapFree(GetProcessHeap(), 0, domain);