Hi,
These were some suggestions from Clifford Heath regarding
win32-security and Windows security in general.
Regards,
Dan
PS - I''ll be on vacation until Jan, 2009. Merry Christmas and Happy
New Year everyone!
---------- Forwarded message ----------
From: Clifford Heath <clifford.heath at gmail.com>
Date: Fri, Dec 19, 2008 at 12:34 AM
Subject: Re: [ANN] win32-security 0.1.0
To: Daniel Berger <djberg96 at gmail.com>
On 19/12/2008, at 4:59 AM, Daniel Berger wrote:>
> Please take a look at the CVS repo.
Ok, making notes as I read through it now. I think the hardest thing with
the Microsoft APIs is getting an understanding of what''s going on under
the covers, so I''ll comment on the C++ API I created. Most of what I
write
is about our implementation rather than your prototype.
I note that you aren''t (yet?) targeting a NTSD (SecurityDescriptor)
yet.
This is a good thing to have, it contains the owner, group, DACL and
SACL - each of which is optional depending on LDAP server options
set before the LDAP query is made. The owner and group are just SIDs,
the DACL (Discretionary ACL) is what you normally look at, and the SACL
or System ACL is for audit (secret police) type features, normally visible
only to domain admins. If you want to read a DACL over LDAP, you need
an NTSD parser - and preferably the ability to set the LDAP server options
that say not to retrieve the SACL, otherwise you get nothing when not
domain admin.
SIDs.
The SID is made up of: version, top authority, and a number of subAuthorities.
The SID as you retrieve it over LDAP will *always* contain slots for
12 sub-auths,
so its important to use the count provided, not believe the size. I
forget whether
it''s important to encode all 12 (zero-padded) when composing an LDAP
command.
The SubAuthorities are encoded as 32-bit RIDs, relative IDs.
I found it was useful to provide a "SID Difference" string conversion,
so you can
pass a Domain SID and a domain name to a SID, and the returned string contains
<domain_name>-RID - nice when printing messages containing SIDs.
I also provided constant SIDs for:
Null = S-1-0-0
World = S-1-1-0
CreatorOwner = S-1-3-0
CreatorGroup = S-1-3-1
AuthenticatedUsers = S-1-5-11
SYSTEM = S-1-5-18
BuiltinAdministrators = S-1-5-32-0x220 (or S-1-5-32-544)
BuiltinUsers = S-1-5-32-0x221 (or S-1-5-32-545)
EnterpriseDomainControllers (S-1-5-9)
I also provided a constant RID for the DomainControllers group and there are
others of these. BTW, a new AD domain database is created by merely cloning
and tweaking a template one, but the fixed values are defined in Microsoft
documentation so should be stable.
The need for all these reflect my focus on ActiveDirectory, but they
(and probably
others I didn''t need!) have constant values, they''re nice to
have. They also
probably obviate the need for your SidType enumeration.
The "domain the SID is on" is just the SID with the last RID removed,
but
you probably knew that.
You should provided accessors for get/set using the string form and by
cloning a SID and adding or removing a RID.
A subset operator is useful (use case-equality === like Class.=== does.
Make sure your SID has a hash method - SIDs are useful as hash keys.
I don''t get why you have any reference to hostnames in the SID class.
You need a domain and a host lookup that returns SIDs for those names,
and that is done using an LDAP query - or call a system DLL to do it for
you... but it''s less reliable and slower... preferable to do the lookup
yourself
I believe. The ADSI DLL is an utter crock of shit collected from all the worst
corners of Microsoft, is slow, buggy and feature-lacking. Much better to just
do what it does and completely ignore it. In general, the conversions between
SIDs and machine & account names is *not* a security thing, its a
user-identity
thing that we layered under a Directory::Binding aspect.
ACLs.
We needed extreme performance in the C version, so implemented iterator
methods over the NTSD, rather than walking the whole ACL to build the ACL
and all ACEs as separate structures. In Ruby, I suspect walking is
better/easier.
ACEs.
You have most of the same stuff we had - except you forgot the SID!
My ACE had the following methods:
type() (allow, deny, audit, alarm - the last two are used in SACLs)
basicType() (I forget what this did that was different)
isAllow()
isDeny()
flags()
mask() (32 bit mask with most of the obvious things like read, write, etc)
size() - in bytes, used during unpacking.
who() - returns a SID
objectGUID()
inheritedGUID() - both for ACEs that include such, like custom access
rights (we were interested in the ApplyGroupPolicy right).
describe() = like ACE#to_s, useful for inspect.
flags&0x1 indicates there''s an object type GUID.
flags()&0x2 indicates there''s a inherited object type GUID.
You should have constants for all the mask() values - there are a lot
and not all are compatible with the others.
Also, although you can look up the GUIDs using LDAP queries on the
schemaContainer, they''re constants, and a little LDAP program to dump
them
and generate Ruby versions to include wouldn''t go amiss.
Good luck, and happy Xmas.
Clifford Heath, Data Constellation.