From 2664c997587416a2c8c911a75158485a5c98b70b Mon Sep 17 00:00:00 2001
From: John Hixon <john@ixsystems.com>
Date: Sat, 20 May 2017 04:39:37 +0200
Subject: [PATCH] Zfs provision (#1)

Cherry-pick ZFS provisioning code by iXsystems Inc.

* Check if sysvol is on filesystem with NFSv4 ACL's
(cherry picked from commit ca86f52b78a7b6e7537454a69cf93e7b96210cba)

* Only check targetdir if it is defined (I had assumed it was)
(cherry picked from commit a29050cb2978ce23e3c04a859340dc2664c77a8a)

* Kick samba a little bit into understanding NFSv4 ACL's
(cherry picked from commit 1c7542ff4904b729e311e17464ee76582760c219)

Signed-off-by: Timur I. Bakeyev <timur@iXsystems.com>
---
 python/samba/provision/__init__.py |  25 ++++--
 source3/lib/sysacls.c              |  10 +++
 source3/param/loadparm.c           |   7 ++
 source3/smbd/pysmbd.c              | 156 ++++++++++++++++++++++++++++++++++++-
 4 files changed, 191 insertions(+), 7 deletions(-)

diff --git a/python/samba/provision/__init__.py b/python/samba/provision/__init__.py
index 5de986463a5..cd3b91f41b9 100644
--- a/python/samba/provision/__init__.py
+++ b/python/samba/provision/__init__.py
@@ -1695,19 +1695,25 @@ def setsysvolacl(samdb, netlogon, sysvol, uid, gid, do
         s3conf = s3param.get_context()
         s3conf.load(lp.configfile)
 
-        file = tempfile.NamedTemporaryFile(dir=os.path.abspath(sysvol))
+        sysvol_dir = os.path.abspath(sysvol)
+
+        set_simple_acl = smbd.set_simple_acl
+        if smbd.has_nfsv4_acls(sysvol_dir):
+            set_simple_acl = smbd.set_simple_nfsv4_acl
+
+        file = tempfile.NamedTemporaryFile(dir=sysvol_dir)
         try:
             try:
-                smbd.set_simple_acl(file.name, 0o755, system_session_unix(), gid)
+                set_simple_acl(file.name, 0o755, system_session_unix(), gid)
             except OSError:
-                if not smbd.have_posix_acls():
+                if not smbd.have_posix_acls() and not smbd.have_nfsv4_acls():
                     # This clue is only strictly correct for RPM and
                     # Debian-like Linux systems, but hopefully other users
                     # will get enough clue from it.
-                    raise ProvisioningError("Samba was compiled without the posix ACL support that s3fs requires.  "
+                    raise ProvisioningError("Samba was compiled without the ACL support that s3fs requires.  "
                                             "Try installing libacl1-dev or libacl-devel, then re-run configure and make.")
 
-                raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires.  "
+                raise ProvisioningError("Your filesystem or build does not support ACLs, which s3fs requires.  "
                                         "Try the mounting the filesystem with the 'acl' option.")
             try:
                 smbd.chown(file.name, uid, gid, system_session_unix())
@@ -1984,6 +1990,9 @@ def provision_fill(samdb, secrets_ldb, logger, names, 
         samdb.transaction_commit()
 
     if serverrole == "active directory domain controller":
+        if targetdir and smbd.have_nfsv4_acls() and smbd.has_nfsv4_acls(targetdir):
+            smbd.set_nfsv4_defaults()
+
         # Continue setting up sysvol for GPO. This appears to require being
         # outside a transaction.
         if not skip_sysvolacl:
@@ -2340,6 +2349,9 @@ def provision(logger, session_info, smbconf=None,
 
             if not os.path.isdir(paths.netlogon):
                 os.makedirs(paths.netlogon, 0o755)
+
+            if smbd.have_nfsv4_acls() and smbd.has_nfsv4_acls(paths.sysvol):
+                smbd.set_nfsv4_defaults()
 
         if adminpass is None:
             adminpass = samba.generate_random_password(12, 32)
diff --git a/source3/lib/sysacls.c b/source3/lib/sysacls.c
index 0bf3c37edfa..786cd39b5bc 100644
--- a/source3/lib/sysacls.c
+++ b/source3/lib/sysacls.c
@@ -38,6 +38,16 @@
 #include "modules/vfs_hpuxacl.h"
 #endif
 
+/*
+ * NFSv4 ACL's should be understood and a first class citizen. Work
+ * needs to be done in librpc/idl/smb_acl.idl for this to occur.
+ */
+#if defined(HAVE_LIBSUNACL) && defined(FREEBSD)
+#if 0
+#include "modules/nfs4_acls.h"
+#endif
+#endif
+
 #undef  DBGC_CLASS
 #define DBGC_CLASS DBGC_ACLS
 
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index a2fcc4246c9..4b676897fc1 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -2801,9 +2801,29 @@ static void init_locals(void)
 		} else {
 			if (lp_parm_const_string(-1, "xattr_tdb", "file", NULL)) {
 				lp_do_parameter(-1, "vfs objects", "dfs_samba4 acl_xattr xattr_tdb");
+			/*
+			 * By default, the samba sysvol is located in the statedir. Provisioning will fail in setntacl
+			 * unless we have zfacl enabled. Unfortunately, at this point the smb.conf has not been generated.
+			 * This workaround is freebsd-specific.
+			 */
+#if defined(_PC_ACL_EXTENDED)
+			} else if (pathconf(lp_state_directory(), _PC_ACL_EXTENDED) == 1) {
+				lp_do_parameter(-1, "vfs objects", "dfs_samba4 freebsd");
+#endif
+#if defined(_PC_ACL_NFS4)
+			} else if (pathconf(lp_state_directory(), _PC_ACL_NFS4) == 1) {
+				lp_do_parameter(-1, "vfs objects", "dfs_samba4 zfsacl");
+#endif
 			} else if (lp_parm_const_string(-1, "posix", "eadb", NULL)) {
 				lp_do_parameter(-1, "vfs objects", "dfs_samba4 acl_xattr posix_eadb");
 			} else {
+				/*
+				 * This should only set dfs_samba4 and leave acl_xattr
+				 * to be set later (or zfsacl). The only reason the decision
+				 * can't be made here to load acl_xattr or zfsacl is
+				 * that we don't have access to what the target
+				 * directory is.
+				 */
 				lp_do_parameter(-1, "vfs objects", "dfs_samba4 acl_xattr");
 			}
 		}
diff --git a/source3/smbd/pysmbd.c b/source3/smbd/pysmbd.c
index 63fc5d68c33..f5a536ee186 100644
--- a/source3/smbd/pysmbd.c
+++ b/source3/smbd/pysmbd.c
@@ -419,6 +419,20 @@ static SMB_ACL_T make_simple_acl(TALLOC_CTX *mem_ctx,
 	return acl;
 }
 
+static SMB_ACL_T make_simple_nfsv4_acl(TALLOC_CTX *mem_ctx,
+					gid_t gid,
+					mode_t chmod_mode)
+{
+	/*
+	 * This function needs to create an NFSv4 ACL. Currently, the only way
+	 * to do so is to use the operating system interface, or to use the
+	 * functions in source3/modules/nfs4_acls.c. These seems ugly and
+	 * hacky. NFSv4 ACL's should be a first class citizen and
+	 * librpc/idl/smb_acl.idl should be modified accordingly.
+	 */
+	return NULL;
+}
+
 /*
   set a simple ACL on a file, as a test
  */
@@ -491,7 +505,85 @@ static PyObject *py_smbd_set_simple_acl(PyObject *self
 	Py_RETURN_NONE;
 }
 
+
 /*
+  set a simple NFSv4 ACL on a file, as a test
+ */
+static PyObject *py_smbd_set_simple_nfsv4_acl(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+	const char * const kwnames[] = {
+		"fname",
+		"mode",
+		"session_info",
+		"gid",
+		"service",
+		NULL
+	};
+	char *fname, *service = NULL;
+	PyObject *py_session = Py_None;
+	struct auth_session_info *session_info = NULL;
+	int ret;
+	int mode, gid = -1;
+	SMB_ACL_T acl;
+	TALLOC_CTX *frame;
+	connection_struct *conn;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siO|iz",
+					 discard_const_p(char *, kwnames),
+					 &fname,
+					 &mode,
+					 &py_session,
+					 &gid,
+					 &service))
+		return NULL;
+
+	if (!py_check_dcerpc_type(py_session,
+				  "samba.dcerpc.auth",
+				  "session_info")) {
+		return NULL;
+	}
+	session_info = pytalloc_get_type(py_session,
+					 struct auth_session_info);
+	if (session_info == NULL) {
+		PyErr_Format(PyExc_TypeError,
+			     "Expected auth_session_info for session_info argument got %s",
+			     pytalloc_get_name(py_session));
+		return NULL;
+	}
+
+	frame = talloc_stackframe();
+
+	acl = make_simple_nfsv4_acl(frame, gid, mode);
+	if (acl == NULL) {
+		TALLOC_FREE(frame);
+		Py_RETURN_NONE;
+	}
+
+	conn = get_conn_tos(service, session_info);
+	if (!conn) {
+		TALLOC_FREE(frame);
+		Py_RETURN_NONE;
+	}
+
+	/*
+	 * SMB_ACL_TYPE_ACCESS -> ACL_TYPE_ACCESS -> Not valid for NFSv4 ACL
+	 */
+	ret = 0;
+
+	/* ret = set_sys_acl_conn(fname, SMB_ACL_TYPE_ACCESS, acl, conn); */
+
+	if (ret != 0) {
+		TALLOC_FREE(frame);
+		errno = ret;
+		return PyErr_SetFromErrno(PyExc_OSError);
+	}
+
+	TALLOC_FREE(frame);
+
+	Py_RETURN_NONE;
+}
+
+/*
   chown a file
  */
 static PyObject *py_smbd_chown(PyObject *self, PyObject *args, PyObject *kwargs)
@@ -665,7 +757,7 @@ static PyObject *py_smbd_unlink(PyObject *self, PyObje
 }
 
 /*
-  check if we have ACL support
+  check if we have POSIX.1e ACL support
  */
 static PyObject *py_smbd_have_posix_acls(PyObject *self,
 		PyObject *Py_UNUSED(ignored))
@@ -677,7 +769,84 @@ static PyObject *py_smbd_have_posix_acls(PyObject *sel
 #endif
 }
 
+static PyObject *py_smbd_has_posix_acls(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+	const char * const kwnames[] = { "path", NULL };
+	char *path = NULL;
+	TALLOC_CTX *frame;
+	struct statfs fs;
+	int ret = false;
+
+	frame = talloc_stackframe();
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|z",
+					 discard_const_p(char *, kwnames), &path)) {
+		TALLOC_FREE(frame);
+		return NULL;
+	}
+
+	if (statfs(path, &fs) != 0) {
+		TALLOC_FREE(frame);
+		return NULL;
+	}
+
+	if (fs.f_flags & MNT_ACLS)
+		ret = true;
+
+	TALLOC_FREE(frame);
+	return PyBool_FromLong(ret);
+}
+
 /*
+  check if we have NFSv4 ACL support
+ */
+static PyObject *py_smbd_have_nfsv4_acls(PyObject *self)
+{
+#ifdef HAVE_LIBSUNACL
+	return PyBool_FromLong(true);
+#else
+	return PyBool_FromLong(false);
+#endif
+}
+
+static PyObject *py_smbd_has_nfsv4_acls(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+	const char * const kwnames[] = { "path", NULL };
+	char *path = NULL;
+	TALLOC_CTX *frame;
+	struct statfs fs;
+	int ret = false;
+
+	frame = talloc_stackframe();
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|z",
+					 discard_const_p(char *, kwnames), &path)) {
+		TALLOC_FREE(frame);
+		return NULL;
+	}
+
+	if (statfs(path, &fs) != 0) {
+		TALLOC_FREE(frame);
+		return NULL;
+	}
+
+	if (fs.f_flags & MNT_NFS4ACLS)
+		ret = true;
+
+	TALLOC_FREE(frame);
+	return PyBool_FromLong(ret);
+}
+
+
+static PyObject *py_smbd_set_nfsv4_defaults(PyObject *self)
+{
+	/*
+	 * It is really be done in source3/param/loadparm.c
+	 */
+	Py_RETURN_NONE;
+}
+
+/*
   set the NT ACL on a file
  */
 static PyObject *py_smbd_set_nt_acl(PyObject *self, PyObject *args, PyObject *kwargs)
@@ -1124,8 +1296,26 @@ static PyMethodDef py_smbd_methods[] = {
 	{ "have_posix_acls",
 		(PyCFunction)py_smbd_have_posix_acls, METH_NOARGS,
 		NULL },
+	{ "has_posix_acls",
+		PY_DISCARD_FUNC_SIG(PyCFunction, py_smbd_has_posix_acls),
+		METH_VARARGS|METH_KEYWORDS,
+		NULL },
+	{ "have_nfsv4_acls",
+		(PyCFunction)py_smbd_have_nfsv4_acls, METH_NOARGS,
+		NULL },
+	{ "has_nfsv4_acls",
+		PY_DISCARD_FUNC_SIG(PyCFunction, py_smbd_has_nfsv4_acls),
+		METH_VARARGS|METH_KEYWORDS,
+		NULL },
+	{ "set_nfsv4_defaults",
+		(PyCFunction)py_smbd_set_nfsv4_defaults, METH_NOARGS,
+		NULL },
 	{ "set_simple_acl",
 		PY_DISCARD_FUNC_SIG(PyCFunction, py_smbd_set_simple_acl),
+		METH_VARARGS|METH_KEYWORDS,
+		NULL },
+	{ "set_simple_nfsv4_acl",
+		PY_DISCARD_FUNC_SIG(PyCFunction, py_smbd_set_simple_nfsv4_acl),
 		METH_VARARGS|METH_KEYWORDS,
 		NULL },
 	{ "set_nt_acl",
-- 
2.14.2