[PATCHES] add an option to inherit only the UNIX owner

Uri Simchoni uri at samba.org
Mon Aug 8 05:15:19 UTC 2016


Hi,

The attached patch (and associated test) add granularity to the "inherit
owner" parameter - in addition to today's behavior of inheriting both
the UNIX and the Windows owner of the file, it adds the option of
inheriting only the UNIX owner. This facilitates emulation of something
very similar to folder quotas.

With the use of acl_xattr and acl_xattr:ignore system acls, the security
of the file access can be governed only by the Windows security
descriptor and enforced only by smbd, thus freeing the unix owner for
other purposes.

One such use is for quota enforcement - the unix owner can be set on a
parent directory, and with the help of unix-only inheritance, propagated
to subdirectories and files, thereby causing all files created in that
directory to consume that unix user's quota.

Review appreciated,
Uri.
-------------- next part --------------
From f54ba96370b80688d44285c6ee0b213b08b3b0d9 Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Tue, 2 Aug 2016 09:37:00 +0300
Subject: [PATCH 1/2] smbd: add an option to inherit only the UNIX owner

This can be used to emulate folder quotas, as explained in the
modified manpage.

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 docs-xml/smbdotconf/security/inheritowner.xml | 37 +++++++++++++++++++++++++--
 lib/param/loadparm.h                          |  7 +++++
 lib/param/param_table.c                       |  5 ++++
 source3/smbd/open.c                           |  7 ++---
 source3/smbd/posix_acls.c                     |  8 ++++++
 5 files changed, 59 insertions(+), 5 deletions(-)

diff --git a/docs-xml/smbdotconf/security/inheritowner.xml b/docs-xml/smbdotconf/security/inheritowner.xml
index ab7da57..e2dbadc 100644
--- a/docs-xml/smbdotconf/security/inheritowner.xml
+++ b/docs-xml/smbdotconf/security/inheritowner.xml
@@ -1,6 +1,7 @@
 <samba:parameter name="inherit owner"
                  context="S"
-                 type="boolean"
+                 type="enum"
+		 enumlist="enum_inherit_owner_vals"
                  xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
 <description>
 	<para>The ownership of new files and directories 
@@ -8,11 +9,43 @@
 	This option allows the Samba administrator to specify that
 	the ownership for new files and directories should be controlled
 	by the ownership of the parent directory.</para>
-	
+
+	<para>Valid options are:</para>
+	<itemizedlist>
+	<listitem><para><constant>no</constant> -
+	Both the Windows (SID) owner and the UNIX (uid) owner of the file are
+	governed by the identity of the user that created the file.
+	</para></listitem>
+
+	<listitem><para><constant>windows and unix</constant> -
+	The Windows (SID) owner and the UNIX (uid) owner of new files and
+	directories are set to the respective owner of the parent directory.
+	</para></listitem>
+
+	<listitem><para><constant>unix only</constant> -
+	Only the UNIX owner is set to the UNIX owner of the parent directory.
+	</para></listitem>
+	</itemizedlist>
+
 	<para>Common scenarios where this behavior is useful is in 
 	implementing drop-boxes, where users can create and edit files but
 	not delete them and ensuring that newly created files in a user's
 	roaming profile directory are actually owned by the user.</para>
+
+	<para>The <constant>unix only</constant> option effectively
+	breaks the tie between the Windows owner of a file and the
+	UNIX owner. As a logical consequence, in this mode,
+	setting the the Windows owner of a file does not modify the UNIX
+	owner. Using this mode should typically be combined with a
+	backing store that can emulate the full NT ACL model without
+	affecting the POSIX permissions, such as the acl_xattr
+	VFS module, coupled with
+	<smbconfoption name="acl_xattr:ignore system acls">yes</smbconfoption>.
+	This can be used to emulate folder quotas, when files are
+	exposed only via SMB (without UNIX extensions).
+	The UNIX owner of a directory is locally set
+	and inherited by all subdirectories and files, and they all
+	consume the same quota.</para>
 </description>
 
 <related>inherit permissions</related>
diff --git a/lib/param/loadparm.h b/lib/param/loadparm.h
index aa256c1..e525ea7 100644
--- a/lib/param/loadparm.h
+++ b/lib/param/loadparm.h
@@ -225,6 +225,13 @@ enum mapreadonly_options {MAP_READONLY_NO, MAP_READONLY_YES, MAP_READONLY_PERMIS
 /* case handling */
 enum case_handling {CASE_LOWER,CASE_UPPER};
 
+/* inherit owner options */
+enum inheritowner_options {
+	INHERIT_OWNER_NO,
+	INHERIT_OWNER_WINDOWS_N_UNIX,
+	INHERIT_OWNER_UNIX_ONLY
+};
+
 /*
  * Default passwd chat script.
  */
diff --git a/lib/param/param_table.c b/lib/param/param_table.c
index d8d9144..0f408e4 100644
--- a/lib/param/param_table.c
+++ b/lib/param/param_table.c
@@ -299,6 +299,11 @@ static const struct enum_list enum_case[] = {
 	{-1, NULL}
 };
 
+static const struct enum_list enum_inherit_owner_vals[] = {
+    {INHERIT_OWNER_NO, "no"},
+    {INHERIT_OWNER_WINDOWS_N_UNIX, "windows and unix"},
+    {INHERIT_OWNER_UNIX_ONLY, "unix only"},
+    {-1, NULL}};
 
 /* Note: We do not initialise the defaults union - it is not allowed in ANSI C
  *
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 2ae6f83..d763ef2 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -930,7 +930,7 @@ static NTSTATUS open_file(files_struct *fsp,
 			}
 
 			/* Change the owner if required. */
-			if (lp_inherit_owner(SNUM(conn))) {
+			if (lp_inherit_owner(SNUM(conn)) != INHERIT_OWNER_NO) {
 				change_file_owner_to_parent(conn, parent_dir,
 							    fsp);
 				need_re_stat = true;
@@ -3375,7 +3375,7 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
 	}
 
 	/* Change the owner if required. */
-	if (lp_inherit_owner(SNUM(conn))) {
+	if (lp_inherit_owner(SNUM(conn)) != INHERIT_OWNER_NO) {
 		change_dir_owner_to_parent(conn, parent_dir,
 					   smb_dname->base_name,
 					   &smb_dname->st);
@@ -4017,7 +4017,8 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
 	const struct dom_sid *group_sid = NULL;
 	uint32_t security_info_sent = (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL);
 	struct security_token *token = fsp->conn->session_info->security_token;
-	bool inherit_owner = lp_inherit_owner(SNUM(fsp->conn));
+	bool inherit_owner =
+	    (lp_inherit_owner(SNUM(fsp->conn)) == INHERIT_OWNER_WINDOWS_N_UNIX);
 	bool inheritable_components = false;
 	bool try_builtin_administrators = false;
 	const struct dom_sid *BA_U_sid = NULL;
diff --git a/source3/smbd/posix_acls.c b/source3/smbd/posix_acls.c
index c575568..e8e819c 100644
--- a/source3/smbd/posix_acls.c
+++ b/source3/smbd/posix_acls.c
@@ -3754,6 +3754,14 @@ NTSTATUS set_nt_acl(files_struct *fsp, uint32_t security_info_sent, const struct
 		security_info_sent &= ~SECINFO_GROUP;
 	}
 
+	/* If UNIX owner is inherited and Windows isn't, then
+	 * setting the UNIX owner based on Windows owner conflicts
+	 * with the inheritance rule
+	 */
+	if (lp_inherit_owner(SNUM(conn)) == INHERIT_OWNER_UNIX_ONLY) {
+		security_info_sent &= ~SECINFO_OWNER;
+	}
+
 	status = unpack_nt_owners( conn, &user, &grp, security_info_sent, psd);
 	if (!NT_STATUS_IS_OK(status)) {
 		return status;
-- 
2.5.5


From 21fbeb5b89a10c4254c487d2ff662a872e25ecfa Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Sun, 7 Aug 2016 21:11:14 +0300
Subject: [PATCH 2/2] selftest: add tests to cover "inherit owner"

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 selftest/target/Samba3.pm                  |   9 ++
 source3/script/tests/test_inherit_owner.sh | 158 +++++++++++++++++++++++++++++
 source3/selftest/tests.py                  |   3 +
 3 files changed, 170 insertions(+)
 create mode 100755 source3/script/tests/test_inherit_owner.sh

diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index e8f35e3..0678f64 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -671,6 +671,15 @@ sub setup_fileserver($$)
 	path = $share_dir
 	comment = ignore system acls
 	acl_xattr:ignore system acls = yes
+[inherit_owner]
+	path = $share_dir
+	comment = inherit owner
+	inherit owner = windows and unix
+[inherit_owner_u]
+	path = $share_dir
+	comment = inherit only unix owner
+	inherit owner = unix only
+	acl_xattr:ignore system acls = yes
 ";
 
 	my $vars = $self->provision($path,
diff --git a/source3/script/tests/test_inherit_owner.sh b/source3/script/tests/test_inherit_owner.sh
new file mode 100755
index 0000000..4a7ed76
--- /dev/null
+++ b/source3/script/tests/test_inherit_owner.sh
@@ -0,0 +1,158 @@
+#!/bin/sh
+
+# this tests "inherit owner" config parameter
+# currently needs to run in SMB1 mode, because it uses UNIX
+# extensions to fetch the UNIX owner of a file.
+
+if [ $# -lt 9 ]; then
+cat <<EOF
+Usage: $0 SERVER USERNAME PASSWORD PREFIX SMBCLIENT SMBCACLS SHARE INH_WIN INH_UNIX <additional args>
+EOF
+exit 1;
+fi
+
+SERVER="$1"
+USERNAME="$2"
+PASSWORD="$3"
+PREFIX="$4"
+SMBCLIENT="$5"
+SMBCACLS="$6"
+SHARE="$7"
+INH_WIN="$8"
+INH_UNIX="$9"
+shift 9
+ADDARGS="$*"
+SMBCLIENT="$VALGRIND ${SMBCLIENT} ${ADDARGS}"
+SMBCACLS="$VALGRIND ${SMBCACLS} ${ADDARGS}"
+
+incdir=`dirname $0`/../../../testprogs/blackbox
+. $incdir/subunit.sh
+
+create_file() {
+    local share=$1
+    local fname=$2
+    local rem_dirname=$(dirname $fname)
+    local bname=$(basename $fname)
+    touch $PREFIX/$bname
+    $SMBCLIENT //$SERVER/$share -U $USERNAME%$PASSWORD -c "cd $rem_dirname; rm $bname" 2>/dev/null
+    $SMBCLIENT //$SERVER/$share -U $USERNAME%$PASSWORD -c "cd $rem_dirname; ls" 2>/dev/null | grep "$bname" && exit 1
+    $SMBCLIENT //$SERVER/$share -U $USERNAME%$PASSWORD -c "lcd $PREFIX; cd $rem_dirname; put $bname" 2>/dev/null || exit 1
+}
+
+create_dir() {
+    local share=$1
+    local dname=$2
+    local rem_dirname=$(dirname $dname)
+    local bname=$(basename $dname)
+    $SMBCLIENT //$SERVER/$share -U $USERNAME%$PASSWORD -c "cd $rem_dirname; rmdir $bname" 2>/dev/null
+    $SMBCLIENT //$SERVER/$share -U $USERNAME%$PASSWORD -c "cd $rem_dirname; ls" 2>/dev/null | grep "$dname" && exit 1
+    $SMBCLIENT //$SERVER/$share -U $USERNAME%$PASSWORD -c "cd $rem_dirname; mkdir $bname" 2>/dev/null || exit 1
+}
+
+cleanup_file() {
+    local share=$1
+    local fname=$2
+    local rem_dirname=$(dirname $fname)
+    local bname=$(basename $fname)
+    $SMBCLIENT //$SERVER/$share -U $USERNAME%$PASSWORD -c "cd $rem_dirname; rm $bname" 2>/dev/null || exit 1
+}
+
+cleanup_dir() {
+    local share=$1
+    local dname=$2
+    local rem_dirname=$(dirname $dname)
+    local bname=$(basename $dname)
+    $SMBCLIENT //$SERVER/$share -U $USERNAME%$PASSWORD -c "cd $rem_dirname; rmdir $bname" 2>/dev/null || exit 1
+}
+
+set_win_owner() {
+    local share=$1
+    local fname=$2
+    local owner=$3
+    $SMBCACLS //$SERVER/$share $fname -U $USERNAME%$PASSWORD -C $owner 2>/dev/null || exit 1
+}
+
+unix_owner_id_is() {
+    local share=$1
+    local fname=$2
+    local expected_id=$3
+    local actual_id
+    actual_id=$($SMBCLIENT //$SERVER/$share -U $USERNAME%$PASSWORD -c "getfacl $fname" 2>/dev/null | sed -rn 's/^# owner: (.*)/\1/p')
+    if ! test "x$actual_id" = "x$expected_id" ; then
+        echo "Actual uid of $share/$fname is [$actual_id] expected [$expected_id]"
+        exit 1
+    fi
+}
+
+get_unix_id() {
+    local user=$1
+    local ent
+    ent=$(getent passwd $user) || exit 1
+    echo "$ent" | awk -F: '{print $3}'
+}
+
+win_owner_is() {
+    local share=$1
+    local fname=$2
+    local expected_owner=$3
+    local actual_owner
+    actual_owner=$($SMBCACLS //$SERVER/$share $fname -U $USERNAME%$PASSWORD 2>/dev/null | sed -rn 's/^OWNER:(.*)/\1/p')
+    if ! test "x$actual_owner" = "x$expected_owner" ; then
+        echo "Actual owner of $share/$fname is [$actual_owner] expected [$expected_owner]"
+        exit 1
+    fi
+}
+
+default_uid=$(get_unix_id $USERNAME)
+alt_uid=$(get_unix_id force_user)
+
+if [ "$INH_WIN" == "0" ] && [ "$INH_UNIX" == "0" ] ; then
+    #default - file owned by creator, change-owner modifies both
+    WIN_OWNER_AFTER_CREATE="$SERVER/$USERNAME"
+    UNIX_OWNER_AFTER_CREATE=$(get_unix_id $USERNAME)
+    WIN_OWNER_AFTER_CHOWN="$SERVER/smbget_user"
+    UNIX_OWNER_AFTER_CHOWN=$(get_unix_id smbget_user)
+    TEST_LABEL="default"
+elif [ "$INH_WIN" == "1" ] && [ "$INH_UNIX" == "1" ] ; then
+    #inherit owner=windows and unix - file owned by parent
+    #owner, change-owner modifies both
+    WIN_OWNER_AFTER_CREATE="$SERVER/force_user"
+    UNIX_OWNER_AFTER_CREATE=$(get_unix_id force_user)
+    WIN_OWNER_AFTER_CHOWN="$SERVER/smbget_user"
+    UNIX_OWNER_AFTER_CHOWN=$(get_unix_id smbget_user)
+    TEST_LABEL="both"
+elif [ "$INH_WIN" == "0" ] && [ "$INH_UNIX" == "1" ] ; then
+    #inherit owner=unix only - windows owner is creator,
+    #unix owner inherited, upon change-owner only windows
+    #owner is changed
+    WIN_OWNER_AFTER_CREATE="$SERVER/$USERNAME"
+    UNIX_OWNER_AFTER_CREATE=$(get_unix_id force_user)
+    WIN_OWNER_AFTER_CHOWN="$SERVER/smbget_user"
+    UNIX_OWNER_AFTER_CHOWN=$(get_unix_id force_user)
+    TEST_LABEL="unix"
+else
+    echo "Unknown combination INH_WIN=$INH_WIN INH_UNIX=$INH_UNIX"
+    exit 1
+fi
+
+# SETUP
+testit "$TEST_LABEL - setup root dir" create_dir tmp tmp.$$
+testit "$TEST_LABEL - assign default ACL" $SMBCACLS //$SERVER/tmp tmp.$$ -U $USERNAME%$PASSWORD -S "REVISION:1,OWNER:$SERVER\force_user,GROUP:$SERVER\domusers,ACL:Everyone:ALLOWED/0x3/FULL" 2>/dev/null
+# END SETUP
+
+testit "$TEST_LABEL - create subdir under root" create_dir $SHARE tmp.$$/subdir
+testit "$TEST_LABEL - verify subdir win owner" win_owner_is $SHARE tmp.$$/subdir "$WIN_OWNER_AFTER_CREATE"
+testit "$TEST_LABEL - verify subdir unix owner" unix_owner_id_is $SHARE tmp.$$/subdir $UNIX_OWNER_AFTER_CREATE
+testit "$TEST_LABEL - create file under root" create_file $SHARE tmp.$$/afile
+testit "$TEST_LABEL - verify file win owner" win_owner_is $SHARE tmp.$$/afile "$WIN_OWNER_AFTER_CREATE"
+testit "$TEST_LABEL - verify file unix owner" unix_owner_id_is $SHARE tmp.$$/afile $UNIX_OWNER_AFTER_CREATE
+testit "$TEST_LABEL - change dir owner" set_win_owner $SHARE tmp.$$/subdir "$SERVER\smbget_user"
+testit "$TEST_LABEL - verify subdir win owner after change" win_owner_is $SHARE tmp.$$/subdir "$WIN_OWNER_AFTER_CHOWN"
+testit "$TEST_LABEL - verify subdir unix owner after change" unix_owner_id_is $SHARE tmp.$$/subdir $UNIX_OWNER_AFTER_CHOWN
+testit "$TEST_LABEL - change file owner" set_win_owner $SHARE tmp.$$/afile "$SERVER\smbget_user"
+testit "$TEST_LABEL - verify file win owner after change" win_owner_is $SHARE tmp.$$/afile "$WIN_OWNER_AFTER_CHOWN"
+testit "$TEST_LABEL - verify file unix owner after change" unix_owner_id_is $SHARE tmp.$$/afile $UNIX_OWNER_AFTER_CHOWN
+testit "$TEST_LABEL - cleanup subdir" cleanup_dir $SHARE tmp.$$/subdir
+testit "$TEST_LABEL - cleanup file" cleanup_file $SHARE tmp.$$/afile
+testit "$TEST_LABEL - cleanup root" cleanup_dir $SHARE tmp.$$
+
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index 4736ebc..23fb37d 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -193,6 +193,9 @@ for env in ["fileserver"]:
     plantestsuite("samba3.blackbox.netshareenum (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_shareenum.sh"), '$SERVER', '$USERNAME', '$PASSWORD', rpcclient])
     plantestsuite("samba3.blackbox.acl_xattr (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_acl_xattr.sh"), '$SERVER', '$USERNAME', '$PASSWORD', '$PREFIX', smbclient3, smbcacls])
     plantestsuite("samba3.blackbox.smb2.not_casesensitive (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_smb2_not_casesensitive.sh"), '//$SERVER/tmp', '$SERVER_IP', '$USERNAME', '$PASSWORD', '$LOCAL_PATH', smbclient3])
+    plantestsuite("samba3.blackbox.inherit_owner.default(%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_inherit_owner.sh"), '$SERVER', '$USERNAME', '$PASSWORD', '$PREFIX', smbclient3, smbcacls, 'tmp', '0', '0', '-m', 'NT1'])
+    plantestsuite("samba3.blackbox.inherit_owner.full (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_inherit_owner.sh"), '$SERVER', '$USERNAME', '$PASSWORD', '$PREFIX', smbclient3, smbcacls, 'inherit_owner', '1', '1', '-m', 'NT1'])
+    plantestsuite("samba3.blackbox.inherit_owner.unix (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_inherit_owner.sh"), '$SERVER', '$USERNAME', '$PASSWORD', '$PREFIX', smbclient3, smbcacls, 'inherit_owner_u', '0', '1', '-m', 'NT1'])
 
     #
     # tar command tests
-- 
2.5.5



More information about the samba-technical mailing list