SID is the short form of Security Identifier. It is used to uniquely name a user (like Alice or Bob), a group (like Users or Administrators) or a system account (like SYSTEM or Network Service). Important to understand is that not only human users get a SID but also system parts like the System or Network Service and even computers itself. An inhuman principal does not have a password so it cannot be get logged on in the traditional sense. They just exist as soon as the system is booted up. If you read about a principal in this context, it is either a human user, a group or a system user. Because there is often no need to tell them apart, we just refer to it as a principal.

A principal is not recognized by its name, but by its unique SID which is a dynamic structure in the C world. It is made of three parts:

  • A revision level that defines the sid version and currently is set to one (1).
  • A 48 bit number which defines the authority that created the SID. (e.g. Windows NT defines 5)
  • An array of numbers which uniquely identify the principal within the authority. The last number is sometimes called a relative identifier (RID).

A template string of a SID may look like this. (Brackets [ ] define optional parameters):

S – R – I [- S1 [- ... - Sn - [RID]]]

  • S declares the string as a SID.
  • R defines the revision number.
  • S1-…-Sn defines the sub-authority. It is an array of n numbers that identifies a domain or machine.
  • RID is the relative principal identifier. It is an unique, sequential and increasing number for a principal assigned by the authority. In Windows, known RIDs start with 500 which defines the Administrator account. RIDs of 1000 and above are used for the usual users and groups. The RID is not an independant part of a SID. In fact it is really the last part of the sub-authority array. Thus the Windows API and JWSCL do not have functions to read or alter it directly. To do so you must access the sub-authority array.

Windows defines some well known SIDs and some well known RIDs. Well knowns SIDs look the same on every computer because they have no domain or machine identifier. On the other hand there are well known RIDs which are system or domain relative. I write system as in operating system, because even on a multi boot computer the SIDs vary between different operating systems if they are not cloned. Several identical SIDs on different machines may lead to security problems. In this case there is a little helper called NewSID that helps changing the SID.

You can find a list of well known SIDs and RIDs in MSDN. Some important well known SIDs are shown here:

  • S-1-1-0 : The Everyone group
  • S-1-5-4 : The Interactive identifier which allows interactive logon.
  • S-1-5-5-#-# : The Session Logon SID (more about it here ) “#” is a placeholder for the high and low value of the logon session ID called LUID. It can be retrieved from the principal’s token.

Here are some important well known RIDs. The ellipsis <…> represent the system or domain.

  • S-1-5-<…>-500 : The Administrator account
  • S-1-5-<…>-501 : The Guest account
  • S-1-5-32 : The Builtin Local groups which identifies a member of a Builtin database

Some well known RIDs are appended to the Builtin SID to define them as special accounts and that they are hard-coded into the system.

  • S-1-5-32-544 : The Administrators group
  • S-1-5-32-545 : The Users group
  • S-1-5-32-546 : The Guest group

JWSCL provides an easy way to access all these elements.
Let start with the base element TJwSecurityID which resides in unit JwsclSid. It encapsulates the C style structure SID and allows to use it in a more object-oriented way. In this way we get rid of manipulating memory directly.
There are many different ways to create a SID instance in JWSCL:

  1. Create a copy of an existing TJwSecurityID instance
  2. Create a copy of an existing SID structure
  3. Create a copy from a nested SID structure within a SidAndAttributes structure
  4. Create a SID from an authority and an identifier
  5. Create a well known SID from known constants
  6. Create a SID from a string that represents the SID
  7. Create a SID from the combination of a system and user name

These ways are accomplished by the TJwSecurity constructors:

  1. constructor Create(const SecurityID: TJwSecurityId);
  2. constructor Create(const SID: PSID);
  3. constructor Create(const SID: PSidAndAttributes);
  4. constructor Create(const Authorities: TJwSubAuthorityArray; Identifier: TSidIdentifierAuthority);
    constructor Create(const Authorities: array of Cardinal; Identifier: TSidIdentifierAuthority);
  5. constructor CreateWellKnownSid(WellKnownSidType: TWellKnownSidType; DomainSid: TJwSecurityId = nil);
  6. constructor Create(const SIDString: TJwString);
  7. constructor Create(const SystemName, AccountName: TJwString);

1. Create(const SecurityID: TJwSecurityId)
Sometimes it is necessary to get a second copy of an already existing SID instance. Use the copy constructor for that task. It makes a duplicate so you can use the second instance exactly like the first one. This comes really handy if you write a multiple threads environment where threads create and free SID instances. Be aware that you cannot change the SID content afterwards. Most properties do not allow to change their values. So you cannot change the identifier, sub-authorities or the well known SID type. This is because a change may lead to security problems. Consider that you could change a SID while it is used in another part of your application (a thread). If this part does a security access check using your SID instance while you change the SID content, the result are unpredictable or even risky even if you apply thread safety. This is because the AccessCheck could return true even if the original SID does not have access. It only may return access allowed because the SID was changed. If you want to change an existing SID instance, you have to create a copy using one of the constructors. For example get the SubAuthority property, alter it and pass it to one of the conststructors in #4. In this way the existing SID instances won’t be affected.

2./3. Create(const SID: PSID)/Create(const SID: PSidAndAttributes);
It is quite uncommon to create an instance by using a SID structure. However if you ever encounter such a task you must be aware that the assigned SID is copied (not refered to it) into the instance. The const key-word in front of the parameter name denotes that parameter SID will not be altered internally. Thus there is no other way than to copy it. Also be aware that the SID memory is checked for a correct SID structure; otherwise EJwsclInvalidSIDException will be raised.

var SID : PSID;
    SIDInstance : TJwSecurityID;

begin
  GetMem(SID, requestedSIDSize);
  try
    //obtain SID structure here
    SIDInstance := TJwSecurityID(SID);
  finally
    FreeMem(SID);
  end;
  try
    //do stuff with SIDInstance here…
  finally
    SIDInstance.Free;
  end;
end;

4. Create(const Authorities: TJwSubAuthorityArray; Identifier: TSidIdentifierAuthority);
It is easily possible to create a SID from scratch by using the internal parts of a SID. In the following example we create the well known group SID “Everybody”.

uses JwaWindows, JwsclSID;
var SIDInstance : TJwSecurityID;
begin
  SIDInstance := TJwSecurityID.Create([0], SECURITY_WORLD_SID_AUTHORITY);
  WriteLn(SIDInstance.GetText(true));
  SIDInstance.Free;
end;

The constant five bytes long array identifier SECURITY_WORLD_SID_AUTHORITY from JwaWindows is declared as follow:

SECURITY_WORLD_SID_AUTHORITY: TSidIdentifierAuthority = (Value: (0, 0, 0, 0, 0, 1));

We add a subauthority of zero and get the shown output. The emtpy brackets usually contains the SID’s attributes which we did not use.

Everybody (S-1-1-0) []

The second constructors receives an declared array which describes the sub-authoroties. With the help of it we can alter an existing instance. The example shown below demonstrates how to alter the Everybody group by changing the sub authority array.

uses JwaWindows, JwsclSID;

var SIDInstance,SIDInstance2 : TJwSecurityID;
    SubAuths : TJwSubAuthorityArray;
begin
  SIDInstance := TJwSecurityID.Create([0], SECURITY_WORLD_SID_AUTHORITY);
  writeln(SIDInstance.getText(true));  

  //get copy of this array
  SubAuths := SIDInstance.SubAuthorityArray;  //increase first array member

  Inc(SubAuths[0]);

  //create new instance and use the new authorities
  SIDInstance2 := TJwSecurityID.Create(SubAuths, SIDInstance.IdentifierAuthority);

  writeln(SIDInstance2.getText(true));

5. CreateWellKnownSid(WellKnownSidType: TWellKnownSidType;...);
The way to create a well known SID as shown in part #4 is very inconvenient. Thus the class TJwSecurityID contains a constructor that directly allows us to use one of the well known SID constants declared in JwaWindows or JwaVista (only if you need the new Vista definitions). The enumeration type WELL_KNOWN_SID_TYPE holds all known SIDs.

type
  WELL_KNOWN_SID_TYPE = (
    WinNullSid {= 0},
    WinWorldSid {= 1},
    WinLocalSid {= 2},
    WinCreatorOwnerSid {= 3},
    WinCreatorGroupSid {= 4},
    …
    //JwaVista
    WinLowLabelSid {= 66},
    WinMediumLabelSid {= 67},
    WinHighLabelSid {= 68},
    WinSystemLabelSid {= 69},
    …
);

The CreateWellKnownSid constructor uses the definition from JwaVista. So if you do not use JwaVista in your use clause you have to add it now because the constructor uses the new version. In this case you should add JwaVista in front of JwaWindows since Delphi uses by default an identifier that is declared in the latest included unit.
The code below uses declarations introduced by JwaWindows but not JwaVista.

uses …, JwaVista, JwaWindows, …;

Of course it is possible to explicitly refer to JwaVista. This task is done by adding the unit name in front of the identifier seperated by a point operator.

uses …,
  JwaVista,
  JwaWindows, …;

var SidType : <code>JwaVista.TWellKnownSidType; </code>
begin
  SidType := JwaVista.WinNullSid;

Creating a well known SID in this way needs getting used to:

uses
  JwaVista,
  JwaWindows,
  JwsclSID;

var SIDInstance : TJwSecurityID;
begin
  SIDInstance := TJwSecurityID.CreateWellKnownSid(JwaVista.WinWorldSid);
  writeln(SIDInstance.getText(true));

If you start programming and want to use Vista stuff from the beginning, you should add JwaVista after JwaWindows. In this case you can easily create a well known SID.

uses
  JwaWindows,
  JwaVista,
  JwsclSID;

var SIDInstance : TJwSecurityID;
begin
  SIDInstance := TJwSecurityID.CreateWellKnownSid(WinWorldSid);
  writeln(SIDInstance.getText(true));

6. Create(const SIDString: TJwString);

A very convenient way to create a SID instance is to use the SID string format that was described at the very first part of this discussion.

uses
  JwaWindows,
  JwaVista,
  JwsclSID;

var SIDInstance : TJwSecurityID;
begin
  try
    SIDInstance := TJwSecurityID.Create(‘S-1-1-0′);
  except
    on E : EJwsclWinCallFailedException do
     //do error stuff here and exit.
  end;
  writeln(SIDInstance.getText(true));

Be warned that the constructor may raise an exception if you do not comply with the SID string format.

7. Create(const SystemName, AccountName: TJwString);

There is sometimes the necessity to get the SID of a user on a specific system. E.g. the user enters his name and the domain or machine name, in this case you can create a SID by a principal’s name. The following example creates a SID instance from the given user on the local system.

uses
 JwaWindows,
 JwaVista,
 JwsclSID;

var SIDInstance : TJwSecurityID;
    UserName  : String;
begin
  ReadLn(UserName);
  try
    SIDInstance := TJwSecurityID.Create(,UserName);
  except
    on E : EJwsclWinCallFailedException do
     //do error stuff here and exit.
  end;
  WriteLn(SIDInstance.getText(true));


In the end, I want to show you some very useful methods. Here they are :

function GetText(ignoreExceptions: boolean = False): TJwString;

Call this method if you need display information of the SID instance. It shows information about the domain and user name, the SID string and attributes if any. Set the parameter ignoreExceptions to false if you want to get an exception if a SID could not be translated into an name. Set it to true if you just want an empty string to be displayed instead. If you don’t want to use too many exception handling mechanisms, you should set the parameter value to true.

Everybody (S-1-1-0) []

I already used this method to show you the output earlier in this article.

property CachedSystemName : TJwString; writable;

GetText uses the system or domain name once assigned to the constructor. However sometimes it is necessary to change it. The property CachedSystemName gets or sets this system or domain name.

property StringSID: TJwString readonly;

This readonly property contains the SID’s string representation as shown in GetText.

property WellKnownSidType: TWellKnownSidType; readonly;

Use the WellKnownSidType property if you need to know of what well known type the SID consists of. Be aware that you should not rely on it if the return value is WinNullSid, because in this case the SID could also be any other SID. The property returns this value for a NULL SID and also for a unknown SID. Use the boolean property IsWellKnownSID to check for a well known SID.

property Attributes: Cardinal; writable;
property AttributesType: TJwSidAttributeSet; writable;

The attributes assigned to a SID can be used in different ways. Some WinAPI functions need them for example. However you can use two versions that are nearly equal. Either you set a bitmask using the property Attributes or you use a enumeration set with AttributesType.

Next time I’ll talk about more JWSCL classes that represent well known SIDs and are defined in the unit JwsclKnownSID.

Tell me how you liked this blog entry by adding a comment.