--- old/usr/src/lib/smbsrv/libmlsvc/common/smb_share.c	Mon Aug  5 17:17:12 2019
+++ new/usr/src/lib/smbsrv/libmlsvc/common/smb_share.c	Mon Aug  5 17:17:12 2019
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  *
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
  */
 
 /*
@@ -389,6 +389,7 @@
 	struct stat st;
 	smb_share_t *cached_si;
 	nvlist_t *shrlist;
+	boolean_t created_zfs = B_FALSE;
 	uint32_t status;
 	int rc;
 
@@ -416,8 +417,34 @@
 		/*
 		 * If share type is STYPE_DISKTREE then the path to the
 		 * share should exist so that we can add the share to cache.
+		 * If path is ZFS, add the .zfs/shares/<share> entry.
+		 *
+		 * Both actions may require privileges that main dropped,
+		 * so we need to temporarily make those effective.
 		 */
-		rc = stat(si->shr_path, &st);
+		if (smb_proc_takesem() == 0) {
+
+			(void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
+			    PRIV_FILE_DAC_READ,
+			    PRIV_FILE_DAC_SEARCH,
+			    PRIV_FILE_DAC_WRITE,
+			    NULL);
+
+			rc = stat(si->shr_path, &st);
+			if (rc == 0) {
+				smb_shr_zfs_add(si);
+				created_zfs = B_TRUE;
+			}
+
+			(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
+			    PRIV_FILE_DAC_READ,
+			    PRIV_FILE_DAC_SEARCH,
+			    PRIV_FILE_DAC_WRITE,
+			    NULL);
+			smb_proc_givesem();
+		} else {
+			rc = NERR_InternalError;
+		}
 		if (rc != 0) {
 			smb_shr_cache_unlock();
 			return (NERR_ItemNotFound);
@@ -425,6 +452,7 @@
 	}
 
 	if ((status = smb_shr_cache_addent(si)) != NERR_Success) {
+		/* This error should be impossible after findent above. */
 		smb_shr_cache_unlock();
 		return (status);
 	}
@@ -440,9 +468,6 @@
 		if (rc == 0) {
 			smb_shr_publish(si->shr_name, si->shr_container);
 
-			/* If path is ZFS, add the .zfs/shares/<share> entry. */
-			smb_shr_zfs_add(si);
-
 			if ((si->shr_flags & SMB_SHRF_DFSROOT) != 0)
 				dfs_namespace_load(si->shr_name);
 
@@ -450,11 +475,34 @@
 		}
 	}
 
+	/*
+	 * Error code path, i.e. when the kernel could not accept
+	 * the new share for some reason.
+	 */
 	if (smb_shr_cache_lock(SMB_SHR_CACHE_WRLOCK) == NERR_Success) {
 		smb_shr_cache_delent(si->shr_name);
 		smb_shr_cache_unlock();
 	}
 
+	if (created_zfs && smb_proc_takesem() == 0) {
+
+		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
+		    PRIV_FILE_DAC_READ,
+		    PRIV_FILE_DAC_SEARCH,
+		    PRIV_FILE_DAC_WRITE,
+		    NULL);
+
+		smb_shr_zfs_remove(si);
+
+		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
+		    PRIV_FILE_DAC_READ,
+		    PRIV_FILE_DAC_SEARCH,
+		    PRIV_FILE_DAC_WRITE,
+		    NULL);
+
+		smb_proc_givesem();
+	}
+
 	/*
 	 * rc == ENOENT means the shared directory doesn't exist
 	 */
@@ -505,9 +553,28 @@
 
 	/*
 	 * If path is ZFS, remove the .zfs/shares/<share> entry.  Need
-	 * to remove before cleanup of cache occurs.
+	 * to remove before cleanup of cache occurs.  These actions
+	 * require temporary elevation of privileges.
 	 */
-	smb_shr_zfs_remove(si);
+	if (smb_proc_takesem() == 0) {
+
+		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
+		    PRIV_FILE_DAC_READ,
+		    PRIV_FILE_DAC_SEARCH,
+		    PRIV_FILE_DAC_WRITE,
+		    NULL);
+
+		smb_shr_zfs_remove(si);
+
+		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
+		    PRIV_FILE_DAC_READ,
+		    PRIV_FILE_DAC_SEARCH,
+		    PRIV_FILE_DAC_WRITE,
+		    NULL);
+
+		smb_proc_givesem();
+	}
+
 	(void) smb_shr_encode(si, &shrlist);
 
 	(void) strlcpy(container, si->shr_container, sizeof (container));
@@ -570,10 +637,26 @@
 	bcopy(from_si, &to_si, sizeof (smb_share_t));
 	(void) strlcpy(to_si.shr_name, to_name, sizeof (to_si.shr_name));
 
-
 	/* If path is ZFS, rename the .zfs/shares/<share> entry. */
-	smb_shr_zfs_rename(from_si, &to_si);
+	if (smb_proc_takesem() == 0) {
 
+		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
+		    PRIV_FILE_DAC_READ,
+		    PRIV_FILE_DAC_SEARCH,
+		    PRIV_FILE_DAC_WRITE,
+		    NULL);
+
+		smb_shr_zfs_rename(from_si, &to_si);
+
+		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
+		    PRIV_FILE_DAC_READ,
+		    PRIV_FILE_DAC_SEARCH,
+		    PRIV_FILE_DAC_WRITE,
+		    NULL);
+
+		smb_proc_givesem();
+	}
+
 	if ((status = smb_shr_cache_addent(&to_si)) != NERR_Success) {
 		smb_shr_cache_unlock();
 		return (status);
@@ -628,14 +711,15 @@
  *   o comment
  *   o AD container
  *   o host access
- *   o abe
+ *   o flags
  */
 uint32_t
 smb_shr_modify(smb_share_t *new_si)
 {
+	smb_share_t old_si;
 	smb_share_t *si;
 	boolean_t adc_changed = B_FALSE;
-	char old_container[MAXPATHLEN];
+	boolean_t quota_flag_changed = B_FALSE;
 	uint32_t access, flag;
 	nvlist_t *shrlist;
 
@@ -655,16 +739,20 @@
 		return (ERROR_ACCESS_DENIED);
 	}
 
+	/*
+	 * Keep a copy of what the share entry looks like before we
+	 * modify it.  We need this for things like unpublishing
+	 * from the old share container, removing the quota dir.
+	 */
+	bcopy(si, &old_si, sizeof (old_si));
+
+	/* Share comment */
 	(void) strlcpy(si->shr_cmnt, new_si->shr_cmnt, sizeof (si->shr_cmnt));
 
-	adc_changed = (strcmp(new_si->shr_container, si->shr_container) != 0);
-	if (adc_changed) {
-		/* save current container - needed for unpublishing */
-		(void) strlcpy(old_container, si->shr_container,
-		    sizeof (old_container));
-		(void) strlcpy(si->shr_container, new_si->shr_container,
-		    sizeof (si->shr_container));
-	}
+	/* Container */
+	(void) strlcpy(si->shr_container, new_si->shr_container,
+	    sizeof (si->shr_container));
+	adc_changed = (strcmp(old_si.shr_container, si->shr_container) != 0);
 
 	flag = (new_si->shr_flags & SMB_SHRF_ABE);
 	si->shr_flags &= ~SMB_SHRF_ABE;
@@ -682,6 +770,20 @@
 	si->shr_flags &= ~SMB_SHRF_DFSROOT;
 	si->shr_flags |= flag;
 
+	flag = (new_si->shr_flags & SMB_SHRF_CA);
+	si->shr_flags &= ~SMB_SHRF_CA;
+	si->shr_flags |= flag;
+
+	flag = (new_si->shr_flags & SMB_SHRF_FSO);
+	si->shr_flags &= ~SMB_SHRF_FSO;
+	si->shr_flags |= flag;
+
+	flag = (new_si->shr_flags & SMB_SHRF_QUOTAS);
+	si->shr_flags &= ~SMB_SHRF_QUOTAS;
+	si->shr_flags |= flag;
+	if ((old_si.shr_flags ^ si->shr_flags) & SMB_SHRF_QUOTAS)
+		quota_flag_changed = B_TRUE;
+
 	flag = (new_si->shr_flags & SMB_SHRF_CSC_MASK);
 	si->shr_flags &= ~SMB_SHRF_CSC_MASK;
 	si->shr_flags |= flag;
@@ -690,6 +792,8 @@
 	si->shr_flags &= ~SMB_SHRF_ACC_ALL;
 	si->shr_flags |= access;
 
+	si->shr_encrypt = new_si->shr_encrypt;
+
 	if (access & SMB_SHRF_ACC_NONE)
 		(void) strlcpy(si->shr_access_none, new_si->shr_access_none,
 		    sizeof (si->shr_access_none));
@@ -715,10 +819,31 @@
 	}
 
 	if (adc_changed) {
-		smb_shr_unpublish(new_si->shr_name, old_container);
+		smb_shr_unpublish(old_si.shr_name, old_si.shr_container);
 		smb_shr_publish(new_si->shr_name, new_si->shr_container);
 	}
 
+	/* The following required privileges we dropped. */
+	if (quota_flag_changed && smb_proc_takesem() == 0) {
+
+		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
+		    PRIV_FILE_DAC_READ,
+		    PRIV_FILE_DAC_SEARCH,
+		    PRIV_FILE_DAC_WRITE,
+		    NULL);
+
+		smb_shr_zfs_remove(&old_si);
+		smb_shr_zfs_add(si);
+
+		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
+		    PRIV_FILE_DAC_READ,
+		    PRIV_FILE_DAC_SEARCH,
+		    PRIV_FILE_DAC_WRITE,
+		    NULL);
+
+		smb_proc_givesem();
+	}
+
 	return (NERR_Success);
 }
 
@@ -1436,7 +1561,12 @@
  */
 
 /*
- * Load shares from sharemgr
+ * Loads the SMB shares, from sharemgr, then:
+ *     - calls smb_shr_add which:
+ *         - adds the share into the share cache
+ *         - adds the share into in-kernel kshare table
+ *         - publishes the share in ADS
+ *     - updates the share list in sharefs/sharetab
  */
 /*ARGSUSED*/
 void *
@@ -1447,10 +1577,7 @@
 	char *gstate;
 	boolean_t gdisabled;
 
-	(void) mutex_lock(&smb_shr_exec_mtx);
-	(void) smb_config_get_execinfo(smb_shr_exec_map, smb_shr_exec_unmap,
-	    MAXPATHLEN);
-	(void) mutex_unlock(&smb_shr_exec_mtx);
+	smb_shr_load_execinfo();
 
 	if ((handle = smb_shr_sa_enter()) == NULL) {
 		syslog(LOG_ERR, "smb_shr_load: load failed");
@@ -1481,7 +1608,51 @@
 	return (NULL);
 }
 
+void
+smb_shr_load_execinfo()
+{
+	(void) mutex_lock(&smb_shr_exec_mtx);
+	(void) smb_config_get_execinfo(smb_shr_exec_map, smb_shr_exec_unmap,
+	    MAXPATHLEN);
+	(void) mutex_unlock(&smb_shr_exec_mtx);
+}
+
 /*
+ * Handles disabling shares in sharefs when stoping smbd
+ */
+void
+smb_shr_unload()
+{
+	smb_shriter_t iterator;
+	smb_share_t *si;
+	sa_handle_t handle;
+	int rc;
+
+	if ((handle = smb_shr_sa_enter()) == NULL) {
+		syslog(LOG_ERR, "smb_shr_unload: failed");
+		return;
+	}
+
+	smb_shr_iterinit(&iterator);
+
+	while ((si = smb_shr_iterate(&iterator)) != NULL) {
+
+		/* Skip transient shares, IPC$, ... */
+		if ((si->shr_flags & SMB_SHRF_TRANS) ||
+		    STYPE_ISIPC(si->shr_type))
+			continue;
+
+		rc = sa_delete_sharetab(handle, si->shr_path, "smb");
+		if (rc) {
+			syslog(LOG_ERR,
+			    "sharefs remove %s failed, rc=%d, err=%d",
+			    si->shr_path, rc, errno);
+		}
+	}
+	smb_shr_sa_exit();
+}
+
+/*
  * Load the shares contained in the specified group.
  *
  * Don't process groups on which the smb protocol is disabled.
@@ -1535,6 +1706,7 @@
 	char *sharename;
 	uint32_t status;
 	boolean_t loaded;
+	int rc;
 
 	if ((sharename = sa_get_resource_attr(resource, "name")) == NULL)
 		return (NERR_InternalError);
@@ -1558,6 +1730,12 @@
 		return (status);
 	}
 
+	rc = sa_update_sharetab(share, "smb");
+	if (rc) {
+		syslog(LOG_ERR, "sharefs add %s failed, rc=%d, err=%d",
+		    sharename, rc, errno);
+	}
+
 	return (NERR_Success);
 }
 
@@ -1648,6 +1826,34 @@
 		free(val);
 	}
 
+	val = smb_shr_sa_getprop(opts, SHOPT_CA);
+	if (val != NULL) {
+		smb_shr_sa_setflag(val, si, SMB_SHRF_CA);
+		free(val);
+	}
+
+	val = smb_shr_sa_getprop(opts, SHOPT_FSO);
+	if (val != NULL) {
+		smb_shr_sa_setflag(val, si, SMB_SHRF_FSO);
+		free(val);
+	}
+
+	val = smb_shr_sa_getprop(opts, SHOPT_QUOTAS);
+	if (val != NULL) {
+		/* Turn the flag on or off */
+		smb_shr_sa_setflag(val, si, SMB_SHRF_QUOTAS);
+		free(val);
+	} else {
+		/* Default for this is enabled. */
+		si->shr_flags |= SMB_SHRF_QUOTAS;
+	}
+
+	val = smb_shr_sa_getprop(opts, SHOPT_ENCRYPT);
+	if (val != NULL) {
+		smb_cfg_set_require(val, &si->shr_encrypt);
+		free(val);
+	}
+
 	val = smb_shr_sa_getprop(opts, SHOPT_CSC);
 	if (val != NULL) {
 		smb_shr_sa_csc_option(val, si);
@@ -2023,8 +2229,8 @@
 
 /*
  * If the share path refers to a ZFS file system, add the
- * .zfs/shares/<share> object and call smb_quota_add_fs()
- * to initialize quota support for the share.
+ * .zfs/shares/<share> object and add or remove the special
+ * directory and file telling clients about quota support.
  */
 static void
 smb_shr_zfs_add(smb_share_t *si)
@@ -2034,11 +2240,13 @@
 	int ret;
 	char buf[MAXPATHLEN];	/* dataset or mountpoint */
 
-	if (smb_getdataset(si->shr_path, buf, MAXPATHLEN) != 0)
+	if ((libhd = libzfs_init()) == NULL)
 		return;
 
-	if ((libhd = libzfs_init()) == NULL)
+	if (smb_getdataset(libhd, si->shr_path, buf, MAXPATHLEN) != 0) {
+		libzfs_fini(libhd);
 		return;
+	}
 
 	if ((zfshd = zfs_open(libhd, buf, ZFS_TYPE_FILESYSTEM)) == NULL) {
 		libzfs_fini(libhd);
@@ -2051,12 +2259,19 @@
 		syslog(LOG_INFO, "share: failed to add ACL object: %s: %s\n",
 		    si->shr_name, strerror(errno));
 
-	if (zfs_prop_get(zfshd, ZFS_PROP_MOUNTPOINT, buf, MAXPATHLEN,
-	    NULL, NULL, 0, B_FALSE) == 0) {
-		smb_quota_add_fs(buf);
+	ret = zfs_prop_get(zfshd, ZFS_PROP_MOUNTPOINT,
+	    buf, MAXPATHLEN, NULL, NULL, 0, B_FALSE);
+	if (ret != 0) {
+		syslog(LOG_INFO, "share: failed to get mountpoint: "
+		    "%s\n", si->shr_name);
+	} else {
+		if ((si->shr_flags & SMB_SHRF_QUOTAS) != 0) {
+			smb_quota_add_fs(buf);
+		} else {
+			smb_quota_remove_fs(buf);
+		}
 	}
 
-
 	zfs_close(zfshd);
 	libzfs_fini(libhd);
 }
@@ -2063,24 +2278,19 @@
 
 /*
  * If the share path refers to a ZFS file system, remove the
- * .zfs/shares/<share> object, and call smb_quota_remove_fs()
- * to end quota support for the share.
+ * .zfs/shares/<share> object.
  */
 static void
 smb_shr_zfs_remove(smb_share_t *si)
 {
 	libzfs_handle_t *libhd;
-	zfs_handle_t *zfshd;
 	int ret;
 	char buf[MAXPATHLEN];	/* dataset or mountpoint */
 
-	if (smb_getdataset(si->shr_path, buf, MAXPATHLEN) != 0)
-		return;
-
 	if ((libhd = libzfs_init()) == NULL)
 		return;
 
-	if ((zfshd = zfs_open(libhd, buf, ZFS_TYPE_FILESYSTEM)) == NULL) {
+	if (smb_getdataset(libhd, si->shr_path, buf, MAXPATHLEN) != 0) {
 		libzfs_fini(libhd);
 		return;
 	}
@@ -2091,12 +2301,12 @@
 		syslog(LOG_INFO, "share: failed to remove ACL object: %s: %s\n",
 		    si->shr_name, strerror(errno));
 
-	if (zfs_prop_get(zfshd, ZFS_PROP_MOUNTPOINT, buf, MAXPATHLEN,
-	    NULL, NULL, 0, B_FALSE) == 0) {
-		smb_quota_remove_fs(buf);
-	}
+	/*
+	 * We could remove the quotas directory here, but that adds
+	 * significantly to the time required for a zpool export,
+	 * so just leave it here and fixup when we share next.
+	 */
 
-	zfs_close(zfshd);
 	libzfs_fini(libhd);
 }
 
@@ -2112,11 +2322,13 @@
 	int ret;
 	char dataset[MAXPATHLEN];
 
-	if (smb_getdataset(from->shr_path, dataset, MAXPATHLEN) != 0)
+	if ((libhd = libzfs_init()) == NULL)
 		return;
 
-	if ((libhd = libzfs_init()) == NULL)
+	if (smb_getdataset(libhd, from->shr_path, dataset, MAXPATHLEN) != 0) {
+		libzfs_fini(libhd);
 		return;
+	}
 
 	if ((zfshd = zfs_open(libhd, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) {
 		libzfs_fini(libhd);
@@ -2415,9 +2627,22 @@
 		rc |= nvlist_add_string(smb, SHOPT_GUEST, "true");
 	if ((si->shr_flags & SMB_SHRF_DFSROOT) != 0)
 		rc |= nvlist_add_string(smb, SHOPT_DFSROOT, "true");
+	if ((si->shr_flags & SMB_SHRF_CA) != 0)
+		rc |= nvlist_add_string(smb, SHOPT_CA, "true");
+	if ((si->shr_flags & SMB_SHRF_FSO) != 0)
+		rc |= nvlist_add_string(smb, SHOPT_FSO, "true");
+	if ((si->shr_flags & SMB_SHRF_QUOTAS) != 0)
+		rc |= nvlist_add_string(smb, SHOPT_QUOTAS, "true");
 
+	if (si->shr_encrypt == SMB_CONFIG_REQUIRED)
+		rc |= nvlist_add_string(smb, SHOPT_ENCRYPT, "required");
+	else if (si->shr_encrypt == SMB_CONFIG_ENABLED)
+		rc |= nvlist_add_string(smb, SHOPT_ENCRYPT, "enabled");
+	else
+		rc |= nvlist_add_string(smb, SHOPT_ENCRYPT, "disabled");
+
 	if ((si->shr_flags & SMB_SHRF_AUTOHOME) != 0) {
-		rc |= nvlist_add_string(smb, "Autohome", "true");
+		rc |= nvlist_add_string(smb, SHOPT_AUTOHOME, "true");
 		rc |= nvlist_add_uint32(smb, "uid", si->shr_uid);
 		rc |= nvlist_add_uint32(smb, "gid", si->shr_gid);
 	}
