Mapping between POSIX and NT ACLs

Andreas Gruenbacher agruen at suse.de
Sun Sep 8 22:16:01 GMT 2002


Hi Jeremy and all,

as recently discussed on #samba-techical the mapping between POSIX ACLs and 
Windows NT ACLs is not ideal. A perfect mapping probably cannot be achieved, 
but the current mapping can still be improved. Attached is a patch that I 
post with request for comment. (Apologies for the unusually long posting.)

I have tested the changes on Windows NT as well as Windows 2000; it seems to 
work as expected.

The previous version of the mapping had to return ACL entries for the file 
owner, group, and others no matter if these entries contained any permissions 
or not. The reason for this (if I understand correctly) was that there was no 
way to add an ACL entry for Everyone as such an entity could be selected. The 
user database browsing functionality has been implemented/improved since, so 
this now works. So it is now possible to leave out ACL entries.

It's (still) important to support Windows NT as well as newer Windows systems. 
Windows NT refuses to display ACL entries that have no permissions in them 
(while Windows 2000 accepts them happily). Therefore the old mapping abused 
the Take Ownership (O) permission to mark ACL entries which have neither 
read, write nor execute permission. This probably was the best we could do 
when the current mapping was designed/implemented.

Windows also knows DENY ACL entries in addition to ALLOW entries. We could 
also use DENY ACL entries to indicate that a user has no access. 
Unfortunately DENY ACL entries take precedence over all other ACL entries in 
the Windows ACL semantics (if I understand this correctly), so the 
ALLOW/Everyone/NONE entry, which would become DENY/Everyone/ALL, would 
indicate no access for anybody. Since we can now simply leave out empty ACL 
entries we can drop the ALLOW/Everyone/NONE entry, and don't need a hack for 
this common case.

There are still situations in which we would like to indicate that a party has 
no access (e.g., the owning group for a file with `rw----r--' permissions, 
something rather obscure to have). We could either still map this to 
`ALLOW/Group/O', or to `DENY/Group/ALL'. The latter now seems preferable (and 
is also displayed correctly in Windows NT/2000), with one minor drawback: 
When such an ACL is read back in by Samba, Samba follows Windows ACL 
semantics as closely as possible, so if the file owner is also member in the 
owning group (which is the usual case) Samba removes the owner's read and 
write permissions. *Ouch!*

I think it would make more sense to make Samba not follow Windows ACL 
semantics that closely to avoid such effects. What do you think?


There are still a few problems to overcome:

(1) The ACL_MASK POSIX ACL entry is not mapped at all. I currently have no 
good idea.

(2) POSIX ACLs have the following form (textual representation):
	u::rw-
	u:joe:rw-
	g::r--
	m::rw-
	o::---
If the file has a different owner, the u:joe:rw- entry applies for joe. If a 
file is owned by user joe, the u::rw- entry applies for joe, and the 
u:joe:rw- entry has no effect. The same can happen with a named group/owning 
group.

Windows only allows one ACL entry for each identity, so we cannot represent 
this in Access ACLs.

For directories there is a special identity CREATOR_OWNER, so we could map the 
u::rwx entry of a Default ACL to the CREATOR_OWNER identity. Unfortunately 
there is nothing similar for the owning group (Windows doesn't have this 
concept). Also CREATOR_OWNER cannot be used for files, so the merging of ACL 
entries from a file's Access ACL and Default ACL, which Windows requires us 
to do to correctly map the ACL entry inheritance flags, would be tricky.


Regards,
Andreas.

------------------------------------------------------------------
 Andreas Gruenbacher                                SuSE Linux AG
 mailto:agruen at suse.de                     Deutschherrnstr. 15-19
 http://www.suse.de/                   D-90429 Nuernberg, Germany


--- samba-2.2.5.orig/source/smbd/posix_acls.c	2002-06-19 03:13:48.000000000 
+0200
+++ samba-2.2.5/source/smbd/posix_acls.c	2002-09-08 23:17:18.000000000 +0200
@@ -346,7 +346,23 @@
 	if ((ace->perms & ALL_ACE_PERMS) == ALL_ACE_PERMS) {
 			nt_mask = UNIX_ACCESS_RWX;
 	} else if ((ace->perms & ALL_ACE_PERMS) == (mode_t)0) {
+#ifdef OLD_MAPPING
+		/* Windows NT refuses to display ACEs with no permissions in them (but
+		   they are perfectly legal with Windows 2000). If the ACE has empty
+		   permissions we cannot use 0, so we map UNIX_ACCESS_NONE to WRITE_OWNER
+		   which we ignore when set an ACL. */
 		nt_mask = UNIX_ACCESS_NONE;
+#else
+		/* With the new mapping it has become feasible to convert the entry to
+		   DENY/.../FULL, which isn't so wrong anymore since we drop
+		   ALLOW/Everone/NONE ACEs anyway. The only problem: This will mask off
+		   permissions from the owner if he is member of the group with no
+		   permissions. Perhaps it's better to fix the code that does that
+		   masking off? */
+		*pacl_type = SEC_ACE_TYPE_ACCESS_DENIED;
+		nt_mask = UNIX_ACCESS_RWX | GENERIC_ALL_ACCESS;
+		
+#endif
 	} else {
 		nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
 		nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
@@ -1399,11 +1415,10 @@
 
  Note that this doesn't exactly match the NT semantics for an ACL. As POSIX 
entries
  are not ordered, and match on the most specific entry rather than walking a 
list,
- then a simple POSIX permission of rw-r--r-- should really map to 6 entries,
+ then a simple POSIX permission of rw-r--r-- should really map to 5 entries,
 
  Entry 0: owner : deny all except read and write.
  Entry 1: group : deny all except read.
- Entry 2: Everyone : deny all except read.
  Entry 3: owner : allow read and write.
  Entry 4: group : allow read.
  Entry 5: Everyone : allow read.
@@ -1953,6 +1968,7 @@
 		return 0;
 	}
 
+#if OLD_MAPPING
 	if (fsp->is_directory) { 
 		/*
 		 * If we have to fake a default ACL then this is the mode to use.
@@ -1962,6 +1978,13 @@
 		dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf, &owner_sid, &group_sid);
 		num_dir_acls = count_canon_ace_list(dir_ace);
 	}
+#else
+	if (fsp->is_directory && dir_acl) {
+		sbuf.st_mode = 0;
+		dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf, &owner_sid, &group_sid);
+		num_dir_acls = count_canon_ace_list(dir_ace);
+	}
+#endif
 
 	/* Allocate the ace list. */
 	if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_dir_acls)* 
sizeof(SEC_ACE))) == NULL) {
@@ -1978,19 +2001,57 @@
 	{
 		canon_ace *ace;
 		int nt_acl_type;
+#ifndef OLD_MAPPING
+		mode_t other_perms;
+#endif
 		int i;
 
+#ifndef OLD_MAPPING
+		/* Find the SMB_ACL_OTHER entry: We will only remove entries with no 
permissions
+		   if the permissions for others are also zero. */
+		other_perms = 0;
+		for (ace = file_ace, i = 0; i < num_acls; i++, ace = ace->next) {
+			if (ace->type == SMB_ACL_OTHER) {
+				other_perms = ace->perms;
+				break;
+			}
+		}
+#endif
+
 		ace = file_ace;
 
 		for (i = 0; i < num_acls; i++, ace = ace->next) {
-			SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
+			SEC_ACCESS acc;
+#ifndef OLD_MAPPING
+			if (!ace->perms && !other_perms)
+				continue;
+#endif
+			acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
 			init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, 
0);
 		}
 
+#ifndef OLD_MAPPING
+		/* Find the SMB_ACL_OTHER entry: We will only remove entries with no 
permissions
+		   if the permissions for others are also zero. */
+		other_perms = 0;
+		for (ace = dir_ace, i = 0; i < num_dir_acls; i++, ace = ace->next) {
+			if (ace->type == SMB_ACL_OTHER) {
+				other_perms = ace->perms;
+				break;
+			}
+		}
+#endif
+
+
 		ace = dir_ace;
 
 		for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
-			SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
+			SEC_ACCESS acc;
+#ifndef OLD_MAPPING
+			if (!ace->perms && !other_perms)
+				continue;
+#endif
+			acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
 			init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, 
 					
SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
 		}
@@ -2015,6 +2076,18 @@
 	if(!*ppdesc) {
 		DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
 		sd_size = 0;
+#ifndef OLD_MAPPING
+	} else {
+		/* Windows 2000: The DACL_PROTECTED flag in the security
+		   descriptor marks the ACL as non-inheriting, i.e., no
+		   ACEs from higher level directories propagate to this
+		   ACL. In the POSIX ACL model permissions are only
+		   inherited at file create time, so ACLs never contain
+		   any ACEs that are inherited dynamically. The DACL_PROTECTED
+		   flag doesn't seem to bother Windows NT.
+		*/
+		(*ppdesc)->type |= SE_DESC_DACL_PROTECTED;
+#endif
 	}
 
   done:





More information about the samba-technical mailing list