![]() |
![]() |
![]() |
Evolution Connector for Microsoft Exchange Programmer’s Reference Manual | ![]() |
---|
Folder Permissions |
Every object in the Exchange database has a security descriptor that controls access to it. (This includes both folders and items, but security descriptors for items are virtually always inherited from their parent folders in well-defined ways, so we don't bother thinking about them.)
The Exchange 2000 Web Storage System has a nifty sytem for translating Windows security descriptors into XML and back, which unfortunately we cannot use, because it's buggy. So we have to generate binary format security descriptors, as described below.
When considering folder permissions, it is important to remember that while the Exchange database looks like a file system when accessed via WebDAV, it does not behave like a filesystem internally. In particular, access to an object is controlled only by its own security descriptor; you do not need to have "folder visible" permission on an object's parent in order to be able to access the object.
This information mostly comes from WinNT.h
(though I've changed the code samples to use
GNOME types rather than
Windows ones).
A SID (Security IDentifier) looks like:
1 2 3 4 5 6 7 8 |
typedef struct { guint8 Revision; guint8 SubauthorityCount; guint8 IdentifierAuthority[6]; guint32 Subauthority[]; } SID; #define SID_REVISION 1 |
1 2 3 4 5 6 7 8 9 10 11 |
typedef struct { guint8 Revision; guint8 Sbz1; guint16 Control; guint32 Owner; guint32 Group; guint32 Sacl; guint32 Dacl; } SECURITY_DESCRIPTOR_RELATIVE; #define SECURITY_DESCRIPTOR_REVISION 1 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#define SE_OWNER_DEFAULTED 0x0001 #define SE_GROUP_DEFAULTED 0x0002 #define SE_DACL_PRESENT 0x0004 #define SE_DACL_DEFAULTED 0x0008 #define SE_SACL_PRESENT 0x0010 #define SE_SACL_DEFAULTED 0x0020 #define SE_DACL_AUTO_INHERIT_REQ 0x0100 #define SE_SACL_AUTO_INHERIT_REQ 0x0200 #define SE_DACL_AUTO_INHERITED 0x0400 #define SE_SACL_AUTO_INHERITED 0x0800 #define SE_DACL_PROTECTED 0x1000 #define SE_SACL_PROTECTED 0x2000 #define SE_RM_CONTROL_VALID 0x4000 #define SE_SELF_RELATIVE 0x8000 |
|
The Owner / Group was provided by a defaulting mechanism rather than explicitly provided by the original provider of the security descriptor. |
|
The Discretionary/Security ACL is present. |
|
The DACL/SACL was provided by a defaulting mechanism rather than explicitly provided by the original provider of the security descriptor. |
|
Always set when using this interface. Indicates that the SD occupies contiguous memory and contains offsets rather than pointers. |
Outlook-generated descriptors usually have
0x8c04
(SE_SELF_RELATIVE
,
SE_SACL
/DACL_AUTO_INHERITED
, and
SE_DACL_PRESENT
).
Each ACL header looks like:
1 2 3 4 5 6 7 8 9 10 |
typedef struct { guint8 AclRevision; guint8 Sbz1; guint16 AclSize; guint16 AceCount; guint16 Sbz2; } ACL; #define ACL_REVISION (2) #define ACL_REVISION_DS (4) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
typedef struct { guint8 AceType; guint8 AceFlags; guint16 AceSize; guint32 Mask; SID Sid; } ACE; /* type */ #define ACCESS_ALLOWED_ACE_TYPE (0x00) #define ACCESS_DENIED_ACE_TYPE (0x01) /* flags */ #define OBJECT_INHERIT_ACE (0x01) #define CONTAINER_INHERIT_ACE (0x02) #define NO_PROPAGATE_INHERIT_ACE (0x04) #define INHERIT_ONLY_ACE (0x08) #define INHERITED_ACE (0x10) |
1 2 3 4 5 6 |
for each ACE in the ACL { if ((ACE.Mask covers the attempted_action) && (ACE.Sid is in CurrentUserSidList)) return (ACE.AceType == ACCESS_ALLOWED_ACE); } return FALSE; |
Default |
Editor |
Bob |
Reviewer |
Bob&Jane |
Contributor |
Jane&Ted |
delete items |
then Bob has "Reviewer", Ted has "delete items", Jane has "Contributor" and "delete items", and everyone else has "Editor". To make that work in the Windows security model, we have to put the ACEs in a specific order, and add explicit "deny" ACEs to prevent fallthrough.
So, the ordering is:
While this is what we implement in e2k-security-descriptor.c, it is known to be wrong. Some folders' ACLs (especially public folders) don't obey this order. Further investigation is needed.
Object-level ACEs for real users, in allow/deny pairs
Container-level ACEs for real users, in allow/deny pairs
Object-level ACEs for Anonymous, in allow/deny pairs
Container-level ACEs for Anonymous, in allow/deny pairs
Container-level ACEs for groups, first all the allows, then all denies
Container-level ACEs for Default (allow only)
Object-level ACEs for groups, first allows, then denies
Object-level ACEs for Default (allow only)
"Default" is never explicitly denied anything.
The ordering above isn't explained completely by the preceding description (in particular, there doesn't seem to be any reason to want to put object- and container-level ACEs in a particular order). I'm not sure if there are additional unknown constraints that force exactly that ordering or if it's just an artifact of how Outlook creates them. At any rate, we do it the same way.