Mapping between POSIX and NT ACLs

Andreas Gruenbacher agruen at suse.de
Mon Sep 9 14:44:32 GMT 2002


Hello,

here is an improved version of the patch.

--Andreas.

--- 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-09 16:23:54.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 );
@@ -678,6 +694,43 @@
 	return True;
 }
 
+static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, 
DOM_SID *pfile_grp_sid)
+{
+	BOOL got_user_obj, got_group_obj;
+	canon_ace *current_ace;
+	int i, entries;
+	
+	entries = count_canon_ace_list(ace);
+	got_user_obj = False;
+	got_group_obj = False;
+
+	for (i=0, current_ace = ace; i < entries; i++, current_ace = 
current_ace->next) {
+		if (current_ace->type == SMB_ACL_USER_OBJ)
+			got_user_obj = True;
+		else if (current_ace->type == SMB_ACL_GROUP_OBJ)
+			got_group_obj = True;
+	}
+	if (got_user_obj && got_group_obj) {
+		print_canon_ace_list( "ACL had owning user/group entries", ace);
+		return;
+	} else {
+		print_canon_ace_list( "Faking owning user/group entries", ace);
+	}
+
+	for (i=0, current_ace = ace; i < entries; i++, current_ace = 
current_ace->next) {
+		if (!got_user_obj && current_ace->owner_type == UID_ACE &&
+		    sid_equal(&current_ace->trustee, pfile_owner_sid)) {
+			current_ace->type = SMB_ACL_USER_OBJ;
+			got_user_obj = True;
+		}
+		if (!got_group_obj && current_ace->owner_type == GID_ACE &&
+		    sid_equal(&current_ace->trustee, pfile_grp_sid)) {
+			current_ace->type = SMB_ACL_GROUP_OBJ;
+			got_group_obj = True;
+		}
+	}
+}
+
 /****************************************************************************
  Unpack a SEC_DESC into two canonical ace lists.
 ****************************************************************************/
@@ -688,6 +741,8 @@
 							canon_ace **ppfile_ace, canon_ace **ppdir_ace,
 							SEC_ACL *dacl)
 {
+	extern DOM_SID global_sid_Creator_Owner;
+	extern DOM_SID global_sid_Creator_Group;
 	extern DOM_SID global_sid_World;
 	extern struct generic_mapping file_generic_mapping;
 	BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
@@ -805,15 +860,27 @@
 		/*
 		 * Try and work out if the SID is a user or group
 		 * as we need to flag these differently for POSIX.
+		 * Note what kind of a POSIX ACL this should map to.
 		 */
 
 		if( sid_equal(&current_ace->trustee, &global_sid_World)) {
 			current_ace->owner_type = WORLD_ACE;
 			current_ace->unix_ug.world = -1;
+			current_ace->type = SMB_ACL_OTHER;
+		} else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Owner)) {
+			current_ace->owner_type = UID_ACE;
+			current_ace->unix_ug.world = -1;
+			current_ace->type = SMB_ACL_USER_OBJ;
+		} else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Group)) {
+			current_ace->owner_type = GID_ACE;
+			current_ace->unix_ug.world = -1;
+			current_ace->type = SMB_ACL_GROUP_OBJ;
 		} else if (sid_to_uid( &current_ace->trustee, &current_ace->unix_ug.uid, 
&sid_type)) {
 			current_ace->owner_type = UID_ACE;
+			current_ace->type = SMB_ACL_USER;
 		} else if (sid_to_gid( &current_ace->trustee, &current_ace->unix_ug.gid, 
&sid_type)) {
 			current_ace->owner_type = GID_ACE;
+			current_ace->type = SMB_ACL_GROUP;
 		} else {
 			fstring str;
 
@@ -834,31 +901,6 @@
 		current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE 
: DENY_ACE;
 
 		/*
-		 * Now note what kind of a POSIX ACL this should map to.
-		 */
-
-		if(sid_equal(&current_ace->trustee, pfile_owner_sid)) {
-
-			current_ace->type = SMB_ACL_USER_OBJ;
-
-		} else if( sid_equal(&current_ace->trustee, pfile_grp_sid)) {
-
-			current_ace->type = SMB_ACL_GROUP_OBJ;
-
-		} else if( sid_equal(&current_ace->trustee, &global_sid_World)) {
-
-			current_ace->type = SMB_ACL_OTHER;
-
-		} else {
-			/*
-			 * Could be a SMB_ACL_USER or SMB_ACL_GROUP. Check by
-			 * looking at owner_type.
-			 */
-
-			current_ace->type = (current_ace->owner_type == UID_ACE) ? SMB_ACL_USER : 
SMB_ACL_GROUP;
-		}
-
-		/*
 		 * Now add the created ace to either the file list, the directory
 		 * list, or both. We *MUST* preserve the order here (hence we use
 		 * DLIST_ADD_END) as NT ACLs are order dependent.
@@ -971,6 +1013,14 @@
 		free_canon_ace_list(dir_ace);
 		file_ace = NULL;
 		dir_ace = NULL;
+	} else {
+		/*
+		 * Check if we have SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries in each
+		 * ACL. If we don't have them, check if any SMB_ACL_USER/SMB_ACL_GROUP
+		 * entries can be converted to *_OBJ.
+		 */
+		check_owning_objs(file_ace, pfile_owner_sid, pfile_grp_sid);
+		check_owning_objs(dir_ace, pfile_owner_sid, pfile_grp_sid);
 	}
 
 	*ppfile_ace = file_ace;
@@ -1399,11 +1449,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.
@@ -1490,7 +1539,8 @@
 		switch(tagtype) {
 			case SMB_ACL_USER_OBJ:
 				/* Get the SID from the owner. */
-				uid_to_sid( &sid, psbuf->st_uid );
+				/* uid_to_sid( &sid, psbuf->st_uid ); */
+				sid_copy(&sid, powner);
 				unix_ug.uid = psbuf->st_uid;
 				owner_type = UID_ACE;
 				break;
@@ -1509,7 +1559,8 @@
 				}
 			case SMB_ACL_GROUP_OBJ:
 				/* Get the SID from the owning group. */
-				gid_to_sid( &sid, psbuf->st_gid );
+				/* gid_to_sid( &sid, psbuf->st_gid ); */
+				sid_copy(&sid, pgroup);
 				unix_ug.gid = psbuf->st_gid;
 				owner_type = GID_ACE;
 				break;
@@ -1882,6 +1933,8 @@
 
 size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
 {
+	extern DOM_SID global_sid_Creator_Owner;
+	extern DOM_SID global_sid_Creator_Group;
 	connection_struct *conn = fsp->conn;
 	SMB_STRUCT_STAT sbuf;
 	SEC_ACE *nt_ace_list = NULL;
@@ -1936,14 +1989,15 @@
 			posix_acl ? "present" :  "absent",
 			dir_acl ? "present" :  "absent" ));
 
-	/*
-	 * Get the owner, group and world SIDs.
-	 */
+	/* Creator_Owner and Creator_Group should be used as owner and owning group 
in the
+	   Access ACL, but Windows NT/2000 seems to support them only for 
inherit-only ACEs.
+	   So we use the owner/owning group SID's instead. */
 
+	/* Get the owner, group and world SIDs. */
 	create_file_sids(&sbuf, &owner_sid, &group_sid);
 
 	/* Create the canon_ace lists. */
-	file_ace = canonicalise_acl( fsp, posix_acl, &sbuf,  &owner_sid, 
&group_sid);
+	file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid);
 	num_acls = count_canon_ace_list(file_ace);
 
 	/* We must have *some* ACLS. */
@@ -1953,6 +2007,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 +2017,15 @@
 		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); 
*/
+		dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf,
+			&global_sid_Creator_Owner, &global_sid_Creator_Group);
+		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 +2042,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 +2117,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