16185db85Sdougm /*
26185db85Sdougm  * CDDL HEADER START
36185db85Sdougm  *
46185db85Sdougm  * The contents of this file are subject to the terms of the
56185db85Sdougm  * Common Development and Distribution License (the "License").
66185db85Sdougm  * You may not use this file except in compliance with the License.
76185db85Sdougm  *
86185db85Sdougm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96185db85Sdougm  * or http://www.opensolaris.org/os/licensing.
106185db85Sdougm  * See the License for the specific language governing permissions
116185db85Sdougm  * and limitations under the License.
126185db85Sdougm  *
136185db85Sdougm  * When distributing Covered Code, include this CDDL HEADER in each
146185db85Sdougm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156185db85Sdougm  * If applicable, add the following below this CDDL HEADER, with the
166185db85Sdougm  * fields enclosed by brackets "[]" replaced with your own identifying
176185db85Sdougm  * information: Portions Copyright [yyyy] [name of copyright owner]
186185db85Sdougm  *
196185db85Sdougm  * CDDL HEADER END
206185db85Sdougm  */
216185db85Sdougm 
226185db85Sdougm /*
23822e6387SMarcel Telka  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24e72ff08dSGordon Ross  * Copyright (c) 2014, 2016 by Delphix. All rights reserved.
25*0dfe541eSEvan Layton  * Copyright 2018 Nexenta Systems, Inc.
2687da98afSVitaliy Gusev  */
276185db85Sdougm 
286185db85Sdougm /*
296185db85Sdougm  * NFS specific functions
306185db85Sdougm  */
31*0dfe541eSEvan Layton 
326185db85Sdougm #include <stdio.h>
336185db85Sdougm #include <string.h>
346185db85Sdougm #include <ctype.h>
356185db85Sdougm #include <stdlib.h>
366185db85Sdougm #include <unistd.h>
376185db85Sdougm #include <errno.h>
386185db85Sdougm #include <locale.h>
396185db85Sdougm #include <signal.h>
40dd51520eSPavan Mettu - Oracle Corporation - Menlo Park United States #include <strings.h>
416185db85Sdougm #include "libshare.h"
426185db85Sdougm #include "libshare_impl.h"
436185db85Sdougm #include <nfs/export.h>
446185db85Sdougm #include <pwd.h>
455cb0d679SMarcel Telka #include <grp.h>
466185db85Sdougm #include <limits.h>
476185db85Sdougm #include <libscf.h>
48dd51520eSPavan Mettu - Oracle Corporation - Menlo Park United States #include <syslog.h>
49dd51520eSPavan Mettu - Oracle Corporation - Menlo Park United States #include <rpcsvc/daemon_utils.h>
506185db85Sdougm #include "nfslog_config.h"
516185db85Sdougm #include "nfslogtab.h"
526185db85Sdougm #include "libshare_nfs.h"
536185db85Sdougm #include <nfs/nfs.h>
54ecd6cf80Smarks #include <nfs/nfssys.h>
55f091aea1SYuri Pankov #include <netconfig.h>
56dd51520eSPavan Mettu - Oracle Corporation - Menlo Park United States #include "smfcfg.h"
576185db85Sdougm 
586185db85Sdougm /* should really be in some global place */
596185db85Sdougm #define	DEF_WIN	30000
606185db85Sdougm #define	OPT_CHUNK	1024
616185db85Sdougm 
626185db85Sdougm int debug = 0;
636185db85Sdougm 
64ecd6cf80Smarks #define	NFS_SERVER_SVC	"svc:/network/nfs/server:default"
65dd51520eSPavan Mettu - Oracle Corporation - Menlo Park United States #define	NFS_CLIENT_SVC	(char *)"svc:/network/nfs/client:default"
666185db85Sdougm 
676185db85Sdougm /* internal functions */
686185db85Sdougm static int nfs_init();
696185db85Sdougm static void nfs_fini();
706185db85Sdougm static int nfs_enable_share(sa_share_t);
71ecd6cf80Smarks static int nfs_disable_share(sa_share_t, char *);
72687915e9Sdougm static int nfs_validate_property(sa_handle_t, sa_property_t, sa_optionset_t);
736185db85Sdougm static int nfs_validate_security_mode(char *);
746185db85Sdougm static int nfs_is_security_opt(char *);
756185db85Sdougm static int nfs_parse_legacy_options(sa_group_t, char *);
766185db85Sdougm static char *nfs_format_options(sa_group_t, int);
776185db85Sdougm static int nfs_set_proto_prop(sa_property_t);
786185db85Sdougm static sa_protocol_properties_t nfs_get_proto_set();
796185db85Sdougm static char *nfs_get_status();
806185db85Sdougm static char *nfs_space_alias(char *);
81da6c28aaSamw static uint64_t nfs_features();
826185db85Sdougm 
836185db85Sdougm /*
846185db85Sdougm  * ops vector that provides the protocol specific info and operations
856185db85Sdougm  * for share management.
866185db85Sdougm  */
876185db85Sdougm 
886185db85Sdougm struct sa_plugin_ops sa_plugin_ops = {
896185db85Sdougm 	SA_PLUGIN_VERSION,
906185db85Sdougm 	"nfs",
916185db85Sdougm 	nfs_init,
926185db85Sdougm 	nfs_fini,
936185db85Sdougm 	nfs_enable_share,
946185db85Sdougm 	nfs_disable_share,
956185db85Sdougm 	nfs_validate_property,
966185db85Sdougm 	nfs_validate_security_mode,
976185db85Sdougm 	nfs_is_security_opt,
986185db85Sdougm 	nfs_parse_legacy_options,
996185db85Sdougm 	nfs_format_options,
1006185db85Sdougm 	nfs_set_proto_prop,
1016185db85Sdougm 	nfs_get_proto_set,
1026185db85Sdougm 	nfs_get_status,
1036185db85Sdougm 	nfs_space_alias,
104da6c28aaSamw 	NULL,	/* update_legacy */
105da6c28aaSamw 	NULL,	/* delete_legacy */
106da6c28aaSamw 	NULL,	/* change_notify */
107da6c28aaSamw 	NULL,	/* enable_resource */
108da6c28aaSamw 	NULL,	/* disable_resource */
109da6c28aaSamw 	nfs_features,
110da6c28aaSamw 	NULL,	/* transient shares */
111da6c28aaSamw 	NULL,	/* notify resource */
1124bff34e3Sthurlow 	NULL,	/* rename_resource */
1134bff34e3Sthurlow 	NULL,	/* run_command */
1144bff34e3Sthurlow 	NULL,	/* command_help */
1154bff34e3Sthurlow 	NULL	/* delete_proto_section */
1166185db85Sdougm };
1176185db85Sdougm 
1186185db85Sdougm /*
1196185db85Sdougm  * list of support services needed
1206185db85Sdougm  * defines should come from head/rpcsvc/daemon_utils.h
1216185db85Sdougm  */
1226185db85Sdougm 
1236185db85Sdougm static char *service_list_default[] =
1242f172c55SRobert Thurlow 	{ STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, REPARSED, NULL };
1256185db85Sdougm static char *service_list_logging[] =
1262f172c55SRobert Thurlow 	{ STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NFSLOGD, REPARSED,
1272f172c55SRobert Thurlow 	    NULL };
1286185db85Sdougm 
1296185db85Sdougm /*
1306185db85Sdougm  * option definitions.  Make sure to keep the #define for the option
1316185db85Sdougm  * index just before the entry it is the index for. Changing the order
1326185db85Sdougm  * can cause breakage.  E.g OPT_RW is index 1 and must precede the
1336185db85Sdougm  * line that includes the SHOPT_RW and OPT_RW entries.
1346185db85Sdougm  */
1356185db85Sdougm 
1366185db85Sdougm struct option_defs optdefs[] = {
1376185db85Sdougm #define	OPT_RO		0
1386185db85Sdougm 	{SHOPT_RO, OPT_RO, OPT_TYPE_ACCLIST},
1396185db85Sdougm #define	OPT_RW		1
1406185db85Sdougm 	{SHOPT_RW, OPT_RW, OPT_TYPE_ACCLIST},
1416185db85Sdougm #define	OPT_ROOT	2
1426185db85Sdougm 	{SHOPT_ROOT, OPT_ROOT, OPT_TYPE_ACCLIST},
1436185db85Sdougm #define	OPT_SECURE	3
1446185db85Sdougm 	{SHOPT_SECURE, OPT_SECURE, OPT_TYPE_DEPRECATED},
1456185db85Sdougm #define	OPT_ANON	4
1466185db85Sdougm 	{SHOPT_ANON, OPT_ANON, OPT_TYPE_USER},
1476185db85Sdougm #define	OPT_WINDOW	5
1486185db85Sdougm 	{SHOPT_WINDOW, OPT_WINDOW, OPT_TYPE_NUMBER},
1496185db85Sdougm #define	OPT_NOSUID	6
1506185db85Sdougm 	{SHOPT_NOSUID, OPT_NOSUID, OPT_TYPE_BOOLEAN},
1516185db85Sdougm #define	OPT_ACLOK	7
1526185db85Sdougm 	{SHOPT_ACLOK, OPT_ACLOK, OPT_TYPE_BOOLEAN},
1536185db85Sdougm #define	OPT_NOSUB	8
1546185db85Sdougm 	{SHOPT_NOSUB, OPT_NOSUB, OPT_TYPE_BOOLEAN},
1556185db85Sdougm #define	OPT_SEC		9
1566185db85Sdougm 	{SHOPT_SEC, OPT_SEC, OPT_TYPE_SECURITY},
1576185db85Sdougm #define	OPT_PUBLIC	10
1586185db85Sdougm 	{SHOPT_PUBLIC, OPT_PUBLIC, OPT_TYPE_BOOLEAN, OPT_SHARE_ONLY},
1596185db85Sdougm #define	OPT_INDEX	11
1606185db85Sdougm 	{SHOPT_INDEX, OPT_INDEX, OPT_TYPE_FILE},
1616185db85Sdougm #define	OPT_LOG		12
1626185db85Sdougm 	{SHOPT_LOG, OPT_LOG, OPT_TYPE_LOGTAG},
1636185db85Sdougm #define	OPT_CKSUM	13
1646185db85Sdougm 	{SHOPT_CKSUM, OPT_CKSUM, OPT_TYPE_STRINGSET},
165b89a8333Snatalie li - Sun Microsystems - Irvine United States #define	OPT_NONE	14
166b89a8333Snatalie li - Sun Microsystems - Irvine United States 	{SHOPT_NONE, OPT_NONE, OPT_TYPE_ACCLIST},
167b89a8333Snatalie li - Sun Microsystems - Irvine United States #define	OPT_ROOT_MAPPING	15
168b89a8333Snatalie li - Sun Microsystems - Irvine United States 	{SHOPT_ROOT_MAPPING, OPT_ROOT_MAPPING, OPT_TYPE_USER},
169b89a8333Snatalie li - Sun Microsystems - Irvine United States #define	OPT_CHARSET_MAP	16
170b89a8333Snatalie li - Sun Microsystems - Irvine United States 	{"", OPT_CHARSET_MAP, OPT_TYPE_ACCLIST},
1716764e83bSVallish Vaidyeshwara #define	OPT_NOACLFAB	17
1726764e83bSVallish Vaidyeshwara 	{SHOPT_NOACLFAB, OPT_NOACLFAB, OPT_TYPE_BOOLEAN},
1735cb0d679SMarcel Telka #define	OPT_UIDMAP	18
1745cb0d679SMarcel Telka 	{SHOPT_UIDMAP, OPT_UIDMAP, OPT_TYPE_MAPPING},
1755cb0d679SMarcel Telka #define	OPT_GIDMAP	19
1765cb0d679SMarcel Telka 	{SHOPT_GIDMAP, OPT_GIDMAP, OPT_TYPE_MAPPING},
177f7db5903SVitaliy Gusev #define	OPT_NOHIDE	20
178f7db5903SVitaliy Gusev 	{SHOPT_NOHIDE, OPT_NOHIDE, OPT_TYPE_BOOLEAN},
1796185db85Sdougm #ifdef VOLATILE_FH_TEST	/* XXX added for testing volatile fh's only */
180f7db5903SVitaliy Gusev #define	OPT_VOLFH	21
1816185db85Sdougm 	{SHOPT_VOLFH, OPT_VOLFH},
1826185db85Sdougm #endif /* VOLATILE_FH_TEST */
1836185db85Sdougm 	NULL
1846185db85Sdougm };
1856185db85Sdougm 
186b89a8333Snatalie li - Sun Microsystems - Irvine United States /*
187b89a8333Snatalie li - Sun Microsystems - Irvine United States  * Codesets that may need to be converted to UTF-8 for file paths.
188b89a8333Snatalie li - Sun Microsystems - Irvine United States  * Add new names here to add new property support. If we ever get a
189b89a8333Snatalie li - Sun Microsystems - Irvine United States  * way to query the kernel for character sets, this should become
190b89a8333Snatalie li - Sun Microsystems - Irvine United States  * dynamically loaded. Make sure changes here are reflected in
191b89a8333Snatalie li - Sun Microsystems - Irvine United States  * cmd/fs.d/nfs/mountd/nfscmd.c
192b89a8333Snatalie li - Sun Microsystems - Irvine United States  */
193b89a8333Snatalie li - Sun Microsystems - Irvine United States 
194b89a8333Snatalie li - Sun Microsystems - Irvine United States static char *legal_conv[] = {
195b89a8333Snatalie li - Sun Microsystems - Irvine United States 	"euc-cn",
196b89a8333Snatalie li - Sun Microsystems - Irvine United States 	"euc-jp",
197b89a8333Snatalie li - Sun Microsystems - Irvine United States 	"euc-jpms",
198b89a8333Snatalie li - Sun Microsystems - Irvine United States 	"euc-kr",
199b89a8333Snatalie li - Sun Microsystems - Irvine United States 	"euc-tw",
200b89a8333Snatalie li - Sun Microsystems - Irvine United States 	"iso8859-1",
201b89a8333Snatalie li - Sun Microsystems - Irvine United States 	"iso8859-2",
202b89a8333Snatalie li - Sun Microsystems - Irvine United States 	"iso8859-5",
203b89a8333Snatalie li - Sun Microsystems - Irvine United States 	"iso8859-6",
204b89a8333Snatalie li - Sun Microsystems - Irvine United States 	"iso8859-7",
205b89a8333Snatalie li - Sun Microsystems - Irvine United States 	"iso8859-8",
206b89a8333Snatalie li - Sun Microsystems - Irvine United States 	"iso8859-9",
207b89a8333Snatalie li - Sun Microsystems - Irvine United States 	"iso8859-13",
208b89a8333Snatalie li - Sun Microsystems - Irvine United States 	"iso8859-15",
209b89a8333Snatalie li - Sun Microsystems - Irvine United States 	"koi8-r",
210b89a8333Snatalie li - Sun Microsystems - Irvine United States 	NULL
211b89a8333Snatalie li - Sun Microsystems - Irvine United States };
212b89a8333Snatalie li - Sun Microsystems - Irvine United States 
2136185db85Sdougm /*
214a99982a7Sdougm  * list of properties that are related to security flavors.
2156185db85Sdougm  */
2166185db85Sdougm static char *seclist[] = {
2176185db85Sdougm 	SHOPT_RO,
2186185db85Sdougm 	SHOPT_RW,
2196185db85Sdougm 	SHOPT_ROOT,
2206185db85Sdougm 	SHOPT_WINDOW,
221b89a8333Snatalie li - Sun Microsystems - Irvine United States 	SHOPT_NONE,
222b89a8333Snatalie li - Sun Microsystems - Irvine United States 	SHOPT_ROOT_MAPPING,
2235cb0d679SMarcel Telka 	SHOPT_UIDMAP,
2245cb0d679SMarcel Telka 	SHOPT_GIDMAP,
2256185db85Sdougm 	NULL
2266185db85Sdougm };
2276185db85Sdougm 
2286185db85Sdougm /* structure for list of securities */
2296185db85Sdougm struct securities {
230a99982a7Sdougm 	sa_security_t security;
231a99982a7Sdougm 	struct securities *next;
2326185db85Sdougm };
2336185db85Sdougm 
234b89a8333Snatalie li - Sun Microsystems - Irvine United States /*
235b89a8333Snatalie li - Sun Microsystems - Irvine United States  * findcharset(charset)
236b89a8333Snatalie li - Sun Microsystems - Irvine United States  *
237b89a8333Snatalie li - Sun Microsystems - Irvine United States  * Returns B_TRUE if the charset is a legal conversion otherwise
238b89a8333Snatalie li - Sun Microsystems - Irvine United States  * B_FALSE. This will need to be rewritten to be more efficient when
239b89a8333Snatalie li - Sun Microsystems - Irvine United States  * we have a dynamic list of legal conversions.
240b89a8333Snatalie li - Sun Microsystems - Irvine United States  */
241b89a8333Snatalie li - Sun Microsystems - Irvine United States 
242b89a8333Snatalie li - Sun Microsystems - Irvine United States static boolean_t
findcharset(char * charset)243b89a8333Snatalie li - Sun Microsystems - Irvine United States findcharset(char *charset)
244b89a8333Snatalie li - Sun Microsystems - Irvine United States {
245b89a8333Snatalie li - Sun Microsystems - Irvine United States 	int i;
246b89a8333Snatalie li - Sun Microsystems - Irvine United States 
247b89a8333Snatalie li - Sun Microsystems - Irvine United States 	for (i = 0; legal_conv[i] != NULL; i++)
248b89a8333Snatalie li - Sun Microsystems - Irvine United States 		if (strcmp(charset, legal_conv[i]) == 0)
249b89a8333Snatalie li - Sun Microsystems - Irvine United States 			return (B_TRUE);
250b89a8333Snatalie li - Sun Microsystems - Irvine United States 	return (B_FALSE);
251b89a8333Snatalie li - Sun Microsystems - Irvine United States }
252b89a8333Snatalie li - Sun Microsystems - Irvine United States 
2536185db85Sdougm /*
2546185db85Sdougm  * findopt(name)
2556185db85Sdougm  *
2566185db85Sdougm  * Lookup option "name" in the option table and return the table
2576185db85Sdougm  * index.
2586185db85Sdougm  */
2596185db85Sdougm 
2606185db85Sdougm static int
findopt(char * name)2616185db85Sdougm findopt(char *name)
2626185db85Sdougm {
2636185db85Sdougm 	int i;
2646185db85Sdougm 	if (name != NULL) {
265a3351425Sdougm 		for (i = 0; optdefs[i].tag != NULL; i++) {
266a3351425Sdougm 			if (strcmp(optdefs[i].tag, name) == 0)
26787da98afSVitaliy Gusev 				return (optdefs[i].index);
268a3351425Sdougm 		}
269b89a8333Snatalie li - Sun Microsystems - Irvine United States 		if (findcharset(name))
270b89a8333Snatalie li - Sun Microsystems - Irvine United States 			return (OPT_CHARSET_MAP);
2716185db85Sdougm 	}
2726185db85Sdougm 	return (-1);
2736185db85Sdougm }
2746185db85Sdougm 
2756185db85Sdougm /*
2766185db85Sdougm  * gettype(name)
2776185db85Sdougm  *
2786185db85Sdougm  * Return the type of option "name".
2796185db85Sdougm  */
2806185db85Sdougm 
2816185db85Sdougm static int
gettype(char * name)2826185db85Sdougm gettype(char *name)
2836185db85Sdougm {
2846185db85Sdougm 	int optdef;
2856185db85Sdougm 
2866185db85Sdougm 	optdef = findopt(name);
2876185db85Sdougm 	if (optdef != -1)
288a3351425Sdougm 		return (optdefs[optdef].type);
2896185db85Sdougm 	return (OPT_TYPE_ANY);
2906185db85Sdougm }
2916185db85Sdougm 
2926185db85Sdougm /*
2936185db85Sdougm  * nfs_validate_security_mode(mode)
2946185db85Sdougm  *
2956185db85Sdougm  * is the specified mode string a valid one for use with NFS?
2966185db85Sdougm  */
2976185db85Sdougm 
2986185db85Sdougm static int
nfs_validate_security_mode(char * mode)2996185db85Sdougm nfs_validate_security_mode(char *mode)
3006185db85Sdougm {
3016185db85Sdougm 	seconfig_t secinfo;
3026185db85Sdougm 	int err;
3036185db85Sdougm 
3046185db85Sdougm 	(void) memset(&secinfo, '\0', sizeof (secinfo));
3056185db85Sdougm 	err = nfs_getseconfig_byname(mode, &secinfo);
3066185db85Sdougm 	if (err == SC_NOERROR)
307a3351425Sdougm 		return (1);
3086185db85Sdougm 	return (0);
3096185db85Sdougm }
3106185db85Sdougm 
3116185db85Sdougm /*
3126185db85Sdougm  * nfs_is_security_opt(tok)
3136185db85Sdougm  *
3146185db85Sdougm  * check to see if tok represents an option that is only valid in some
3156185db85Sdougm  * security flavor.
3166185db85Sdougm  */
3176185db85Sdougm 
3186185db85Sdougm static int
nfs_is_security_opt(char * tok)3196185db85Sdougm nfs_is_security_opt(char *tok)
3206185db85Sdougm {
3216185db85Sdougm 	int i;
3226185db85Sdougm 
3236185db85Sdougm 	for (i = 0; seclist[i] != NULL; i++) {
324a3351425Sdougm 		if (strcmp(tok, seclist[i]) == 0)
325a3351425Sdougm 			return (1);
3266185db85Sdougm 	}
3276185db85Sdougm 	return (0);
3286185db85Sdougm }
3296185db85Sdougm 
3306185db85Sdougm /*
3316185db85Sdougm  * find_security(seclist, sec)
3326185db85Sdougm  *
3336185db85Sdougm  * Walk the current list of security flavors and return true if it is
3346185db85Sdougm  * present, else return false.
3356185db85Sdougm  */
3366185db85Sdougm 
3376185db85Sdougm static int
find_security(struct securities * seclist,sa_security_t sec)3386185db85Sdougm find_security(struct securities *seclist, sa_security_t sec)
3396185db85Sdougm {
3406185db85Sdougm 	while (seclist != NULL) {
341a3351425Sdougm 		if (seclist->security == sec)
342a3351425Sdougm 			return (1);
343a3351425Sdougm 		seclist = seclist->next;
3446185db85Sdougm 	}
3456185db85Sdougm 	return (0);
3466185db85Sdougm }
3476185db85Sdougm 
3486185db85Sdougm /*
3496185db85Sdougm  * make_security_list(group, securitymodes, proto)
3506185db85Sdougm  *	go through the list of securitymodes and add them to the
3516185db85Sdougm  *	group's list of security optionsets. We also keep a list of
3526185db85Sdougm  *	those optionsets so we don't have to find them later. All of
3536185db85Sdougm  *	these will get copies of the same properties.
3546185db85Sdougm  */
3556185db85Sdougm 
3566185db85Sdougm static struct securities *
make_security_list(sa_group_t group,char * securitymodes,char * proto)3576185db85Sdougm make_security_list(sa_group_t group, char *securitymodes, char *proto)
3586185db85Sdougm {
3596185db85Sdougm 	char *tok, *next = NULL;
3606185db85Sdougm 	struct securities *curp, *headp = NULL, *prev;
3616185db85Sdougm 	sa_security_t check;
3626185db85Sdougm 	int freetok = 0;
3636185db85Sdougm 
3646185db85Sdougm 	for (tok = securitymodes; tok != NULL; tok = next) {
365a3351425Sdougm 		next = strchr(tok, ':');
366a3351425Sdougm 		if (next != NULL)
367a3351425Sdougm 			*next++ = '\0';
368a3351425Sdougm 		if (strcmp(tok, "default") == 0) {
369a3351425Sdougm 			/* resolve default into the real type */
370a3351425Sdougm 			tok = nfs_space_alias(tok);
371a3351425Sdougm 			freetok = 1;
372a3351425Sdougm 		}
373a3351425Sdougm 		check = sa_get_security(group, tok, proto);
374a3351425Sdougm 
375a3351425Sdougm 		/* add to the security list if it isn't there already */
376a3351425Sdougm 		if (check == NULL || !find_security(headp, check)) {
377a3351425Sdougm 			curp = (struct securities *)calloc(1,
378a3351425Sdougm 			    sizeof (struct securities));
379a3351425Sdougm 			if (curp != NULL) {
380a3351425Sdougm 				if (check == NULL) {
381a3351425Sdougm 					curp->security = sa_create_security(
382a3351425Sdougm 					    group, tok, proto);
383a3351425Sdougm 				} else {
384a3351425Sdougm 					curp->security = check;
385a3351425Sdougm 				}
386a3351425Sdougm 				/*
387a3351425Sdougm 				 * note that the first time through the loop,
388a3351425Sdougm 				 * headp will be NULL and prev will be
389a3351425Sdougm 				 * undefined.  Since headp is NULL, we set
390a3351425Sdougm 				 * both it and prev to the curp (first
391a3351425Sdougm 				 * structure to be allocated).
392a3351425Sdougm 				 *
393a3351425Sdougm 				 * later passes through the loop will have
394a3351425Sdougm 				 * headp not being NULL and prev will be used
395a3351425Sdougm 				 * to allocate at the end of the list.
396a3351425Sdougm 				 */
397a3351425Sdougm 				if (headp == NULL) {
398a3351425Sdougm 					headp = curp;
399a3351425Sdougm 					prev = curp;
400a3351425Sdougm 				} else {
401a3351425Sdougm 					prev->next = curp;
402a3351425Sdougm 					prev = curp;
403a3351425Sdougm 				}
404a3351425Sdougm 			}
4056185db85Sdougm 		}
406a99982a7Sdougm 
407a3351425Sdougm 		if (freetok) {
408a3351425Sdougm 			freetok = 0;
409a3351425Sdougm 			sa_free_attr_string(tok);
410a3351425Sdougm 		}
4116185db85Sdougm 	}
4126185db85Sdougm 	return (headp);
4136185db85Sdougm }
4146185db85Sdougm 
4156185db85Sdougm static void
free_security_list(struct securities * sec)4166185db85Sdougm free_security_list(struct securities *sec)
4176185db85Sdougm {
4186185db85Sdougm 	struct securities *next;
4196185db85Sdougm 	if (sec != NULL) {
420a3351425Sdougm 		for (next = sec->next; sec != NULL; sec = next) {
421a3351425Sdougm 			next = sec->next;
422a3351425Sdougm 			free(sec);
423a3351425Sdougm 		}
4246185db85Sdougm 	}
4256185db85Sdougm }
4266185db85Sdougm 
4276185db85Sdougm /*
4286185db85Sdougm  * nfs_alistcat(str1, str2, sep)
4296185db85Sdougm  *
4306185db85Sdougm  * concatenate str1 and str2 into a new string using sep as a separate
4316185db85Sdougm  * character. If memory allocation fails, return NULL;
4326185db85Sdougm  */
4336185db85Sdougm 
4346185db85Sdougm static char *
nfs_alistcat(char * str1,char * str2,char sep)4356185db85Sdougm nfs_alistcat(char *str1, char *str2, char sep)
4366185db85Sdougm {
4376185db85Sdougm 	char *newstr;
4386185db85Sdougm 	size_t len;
4396185db85Sdougm 
4406185db85Sdougm 	len = strlen(str1) + strlen(str2) + 2;
4416185db85Sdougm 	newstr = (char *)malloc(len);
4426185db85Sdougm 	if (newstr != NULL)
443a3351425Sdougm 		(void) snprintf(newstr, len, "%s%c%s", str1, sep, str2);
4446185db85Sdougm 	return (newstr);
4456185db85Sdougm }
4466185db85Sdougm 
4476185db85Sdougm /*
4485cb0d679SMarcel Telka  * add_security_prop(sec, name, value, persist, iszfs)
4496185db85Sdougm  *
4506185db85Sdougm  * Add the property to the securities structure. This accumulates
4516185db85Sdougm  * properties for as part of parsing legacy options.
4526185db85Sdougm  */
4536185db85Sdougm 
4546185db85Sdougm static int
add_security_prop(struct securities * sec,char * name,char * value,int persist,int iszfs)4556185db85Sdougm add_security_prop(struct securities *sec, char *name, char *value,
456f091aea1SYuri Pankov     int persist, int iszfs)
4576185db85Sdougm {
4586185db85Sdougm 	sa_property_t prop;
4596185db85Sdougm 	int ret = SA_OK;
4606185db85Sdougm 
4616185db85Sdougm 	for (; sec != NULL; sec = sec->next) {
462a3351425Sdougm 		if (value == NULL) {
463a3351425Sdougm 			if (strcmp(name, SHOPT_RW) == 0 ||
464a3351425Sdougm 			    strcmp(name, SHOPT_RO) == 0)
465a3351425Sdougm 				value = "*";
466a3351425Sdougm 			else
467a3351425Sdougm 				value = "true";
468a3351425Sdougm 		}
469a99982a7Sdougm 
470a99982a7Sdougm 		/*
471a99982a7Sdougm 		 * Get the existing property, if it exists, so we can
472a99982a7Sdougm 		 * determine what to do with it. The ro/rw/root
473a99982a7Sdougm 		 * properties can be merged if multiple instances of
474a99982a7Sdougm 		 * these properies are given. For example, if "rw"
475a99982a7Sdougm 		 * exists with a value "host1" and a later token of
476a99982a7Sdougm 		 * rw="host2" is seen, the values are merged into a
477a99982a7Sdougm 		 * single rw="host1:host2".
478a99982a7Sdougm 		 */
479a3351425Sdougm 		prop = sa_get_property(sec->security, name);
480a99982a7Sdougm 
481a3351425Sdougm 		if (prop != NULL) {
482a3351425Sdougm 			char *oldvalue;
483a3351425Sdougm 			char *newvalue;
484a99982a7Sdougm 
485a99982a7Sdougm 			/*
4865cb0d679SMarcel Telka 			 * The security options of ro/rw/root/uidmap/gidmap
4875cb0d679SMarcel Telka 			 * might appear multiple times.  If they do, the values
4885cb0d679SMarcel Telka 			 * need to be merged.  If it was previously empty, the
4895cb0d679SMarcel Telka 			 * new value alone is added.
490a99982a7Sdougm 			 */
491a3351425Sdougm 			oldvalue = sa_get_property_attr(prop, "value");
492a3351425Sdougm 			if (oldvalue != NULL) {
4935cb0d679SMarcel Telka 				char sep = ':';
4945cb0d679SMarcel Telka 
4955cb0d679SMarcel Telka 				if (strcmp(name, SHOPT_UIDMAP) == 0 ||
4965cb0d679SMarcel Telka 				    strcmp(name, SHOPT_GIDMAP) == 0)
4975cb0d679SMarcel Telka 					sep = '~';
4985cb0d679SMarcel Telka 
499a3351425Sdougm 				/*
500a3351425Sdougm 				 * The general case is to concatenate the new
501a3351425Sdougm 				 * value onto the old value for multiple
5025cb0d679SMarcel Telka 				 * rw(ro/root/uidmap/gidmap) properties.  For
5035cb0d679SMarcel Telka 				 * rw/ro/root a special case exists when either
5045cb0d679SMarcel Telka 				 * the old or new is the "all" case.  In the
5055cb0d679SMarcel Telka 				 * special case, if both are "all", then it is
5065cb0d679SMarcel Telka 				 * "all", else if one is an access-list, that
5075cb0d679SMarcel Telka 				 * replaces the "all".
508a3351425Sdougm 				 */
509a3351425Sdougm 				if (strcmp(oldvalue, "*") == 0) {
510a3351425Sdougm 					/* Replace old value with new value. */
511a3351425Sdougm 					newvalue = strdup(value);
51297df5ac9Sdougm 				} else if (strcmp(value, "*") == 0 ||
51397df5ac9Sdougm 				    strcmp(oldvalue, value) == 0) {
514a3351425Sdougm 					/*
515a3351425Sdougm 					 * Keep old value and ignore
516a3351425Sdougm 					 * the new value.
517a3351425Sdougm 					 */
518a3351425Sdougm 					newvalue = NULL;
519a3351425Sdougm 				} else {
520a3351425Sdougm 					/*
521a3351425Sdougm 					 * Make a new list of old plus new
522a3351425Sdougm 					 * access-list.
523a3351425Sdougm 					 */
524a3351425Sdougm 					newvalue = nfs_alistcat(oldvalue,
5255cb0d679SMarcel Telka 					    value, sep);
526a3351425Sdougm 				}
527a3351425Sdougm 
528a3351425Sdougm 				if (newvalue != NULL) {
529a3351425Sdougm 					(void) sa_remove_property(prop);
530a3351425Sdougm 					prop = sa_create_property(name,
531a3351425Sdougm 					    newvalue);
532a3351425Sdougm 					ret = sa_add_property(sec->security,
533a3351425Sdougm 					    prop);
534a3351425Sdougm 					free(newvalue);
535a3351425Sdougm 				}
5365cb0d679SMarcel Telka 
5375cb0d679SMarcel Telka 				sa_free_attr_string(oldvalue);
538a3351425Sdougm 			}
539a3351425Sdougm 		} else {
540a3351425Sdougm 			prop = sa_create_property(name, value);
541a99982a7Sdougm 			ret = sa_add_property(sec->security, prop);
5426185db85Sdougm 		}
543a3351425Sdougm 		if (ret == SA_OK && !iszfs) {
544a3351425Sdougm 			ret = sa_commit_properties(sec->security, !persist);
545a3351425Sdougm 		}
5466185db85Sdougm 	}
5476185db85Sdougm 	return (ret);
5486185db85Sdougm }
5496185db85Sdougm 
5506185db85Sdougm /*
5516185db85Sdougm  * check to see if group/share is persistent.
5526185db85Sdougm  */
5536185db85Sdougm static int
is_persistent(sa_group_t group)5546185db85Sdougm is_persistent(sa_group_t group)
5556185db85Sdougm {
5566185db85Sdougm 	char *type;
5576185db85Sdougm 	int persist = 1;
5586185db85Sdougm 
5596185db85Sdougm 	type = sa_get_group_attr(group, "type");
5606185db85Sdougm 	if (type != NULL && strcmp(type, "persist") != 0)
561a3351425Sdougm 		persist = 0;
5626185db85Sdougm 	if (type != NULL)
563a3351425Sdougm 		sa_free_attr_string(type);
5646185db85Sdougm 	return (persist);
5656185db85Sdougm }
5666185db85Sdougm 
5676185db85Sdougm /*
5686185db85Sdougm  * invalid_security(options)
5696185db85Sdougm  *
5706185db85Sdougm  * search option string for any invalid sec= type.
5716185db85Sdougm  * return true (1) if any are not valid else false (0)
5726185db85Sdougm  */
5736185db85Sdougm static int
invalid_security(char * options)5746185db85Sdougm invalid_security(char *options)
5756185db85Sdougm {
5766185db85Sdougm 	char *copy, *base, *token, *value;
5776185db85Sdougm 	int ret = 0;
5786185db85Sdougm 
5796185db85Sdougm 	copy = strdup(options);
5806185db85Sdougm 	token = base = copy;
5816185db85Sdougm 	while (token != NULL && ret == 0) {
582a3351425Sdougm 		token = strtok(base, ",");
583a3351425Sdougm 		base = NULL;
584a3351425Sdougm 		if (token != NULL) {
585a3351425Sdougm 			value = strchr(token, '=');
586a3351425Sdougm 			if (value != NULL)
587a3351425Sdougm 				*value++ = '\0';
5885cb0d679SMarcel Telka 			if (strcmp(token, SHOPT_SEC) == 0) {
589a3351425Sdougm 				/* HAVE security flavors so check them */
590a3351425Sdougm 				char *tok, *next;
591a3351425Sdougm 				for (next = NULL, tok = value; tok != NULL;
592a3351425Sdougm 				    tok = next) {
593a3351425Sdougm 					next = strchr(tok, ':');
594a3351425Sdougm 					if (next != NULL)
595a3351425Sdougm 						*next++ = '\0';
596a3351425Sdougm 					ret = !nfs_validate_security_mode(tok);
597a3351425Sdougm 					if (ret)
598a3351425Sdougm 						break;
599a3351425Sdougm 				}
600a3351425Sdougm 			}
6016185db85Sdougm 		}
6026185db85Sdougm 	}
6036185db85Sdougm 	if (copy != NULL)
604a3351425Sdougm 		free(copy);
6056185db85Sdougm 	return (ret);
6066185db85Sdougm }
6076185db85Sdougm 
6086185db85Sdougm /*
6096185db85Sdougm  * nfs_parse_legacy_options(group, options)
6106185db85Sdougm  *
6116185db85Sdougm  * Parse the old style options into internal format and store on the
6126185db85Sdougm  * specified group.  Group could be a share for full legacy support.
6136185db85Sdougm  */
6146185db85Sdougm 
6156185db85Sdougm static int
nfs_parse_legacy_options(sa_group_t group,char * options)6166185db85Sdougm nfs_parse_legacy_options(sa_group_t group, char *options)
6176185db85Sdougm {
618aba7a40bSdougm 	char *dup;
6196185db85Sdougm 	char *base;
6206185db85Sdougm 	char *token;
6216185db85Sdougm 	sa_optionset_t optionset;
6226185db85Sdougm 	struct securities *security_list = NULL;
6236185db85Sdougm 	sa_property_t prop;
6246185db85Sdougm 	int ret = SA_OK;
6256185db85Sdougm 	int iszfs = 0;
6266185db85Sdougm 	sa_group_t parent;
6276185db85Sdougm 	int persist = 0;
6286185db85Sdougm 	char *lasts;
6296185db85Sdougm 
6306185db85Sdougm 	/* do we have an existing optionset? */
6316185db85Sdougm 	optionset = sa_get_optionset(group, "nfs");
6326185db85Sdougm 	if (optionset == NULL) {
633a3351425Sdougm 		/* didn't find existing optionset so create one */
634a3351425Sdougm 		optionset = sa_create_optionset(group, "nfs");
6356185db85Sdougm 	} else {
6366185db85Sdougm 		/*
637da6c28aaSamw 		 * Have an existing optionset . Ideally, we would need
638da6c28aaSamw 		 * to compare options in order to detect errors. For
639da6c28aaSamw 		 * now, we assume that the first optionset is the
640da6c28aaSamw 		 * correct one and the others will be the same. An
641da6c28aaSamw 		 * empty optionset is the same as no optionset so we
642da6c28aaSamw 		 * don't want to exit in that case. Getting an empty
643da6c28aaSamw 		 * optionset can occur with ZFS property checking.
6446185db85Sdougm 		 */
645da6c28aaSamw 		if (sa_get_property(optionset, NULL) != NULL)
646da6c28aaSamw 			return (ret);
6476185db85Sdougm 	}
6486185db85Sdougm 
6496185db85Sdougm 	if (strcmp(options, SHOPT_RW) == 0) {
6506185db85Sdougm 		/*
6516185db85Sdougm 		 * there is a special case of only the option "rw"
6526185db85Sdougm 		 * being the default option. We don't have to do
6536185db85Sdougm 		 * anything.
6546185db85Sdougm 		 */
655a3351425Sdougm 		return (ret);
6566185db85Sdougm 	}
6576185db85Sdougm 
6586185db85Sdougm 	/*
6596185db85Sdougm 	 * check if security types are present and validate them. If
6606185db85Sdougm 	 * any are not legal, fail.
6616185db85Sdougm 	 */
6626185db85Sdougm 
6636185db85Sdougm 	if (invalid_security(options)) {
664a3351425Sdougm 		return (SA_INVALID_SECURITY);
6656185db85Sdougm 	}
6666185db85Sdougm 
6676185db85Sdougm 	/*
6686185db85Sdougm 	 * in order to not attempt to change ZFS properties unless
6696185db85Sdougm 	 * absolutely necessary, we never do it in the legacy parsing.
6706185db85Sdougm 	 */
6716185db85Sdougm 	if (sa_is_share(group)) {
672a3351425Sdougm 		char *zfs;
673a3351425Sdougm 		parent = sa_get_parent_group(group);
674a3351425Sdougm 		if (parent != NULL) {
675a3351425Sdougm 			zfs = sa_get_group_attr(parent, "zfs");
676a3351425Sdougm 			if (zfs != NULL) {
677a3351425Sdougm 				sa_free_attr_string(zfs);
678a3351425Sdougm 				iszfs++;
679a3351425Sdougm 			}
6806185db85Sdougm 		}
6816185db85Sdougm 	} else {
682a3351425Sdougm 		iszfs = sa_group_is_zfs(group);
6836185db85Sdougm 	}
6846185db85Sdougm 
685aba7a40bSdougm 	/* We need a copy of options for the next part. */
686aba7a40bSdougm 	dup = strdup(options);
687aba7a40bSdougm 	if (dup == NULL)
688aba7a40bSdougm 		return (SA_NO_MEMORY);
689aba7a40bSdougm 
6906185db85Sdougm 	/*
6916185db85Sdougm 	 * we need to step through each option in the string and then
6926185db85Sdougm 	 * add either the option or the security option as needed. If
6936185db85Sdougm 	 * this is not a persistent share, don't commit to the
694a99982a7Sdougm 	 * repository. If there is an error, we also want to abort the
695a99982a7Sdougm 	 * processing and report it.
6966185db85Sdougm 	 */
6976185db85Sdougm 	persist = is_persistent(group);
6986185db85Sdougm 	base = dup;
6996185db85Sdougm 	token = dup;
7006185db85Sdougm 	lasts = NULL;
701a99982a7Sdougm 	while (token != NULL && ret == SA_OK) {
702a3351425Sdougm 		token = strtok_r(base, ",", &lasts);
703a3351425Sdougm 		base = NULL;
704a3351425Sdougm 		if (token != NULL) {
705a3351425Sdougm 			char *value;
7066185db85Sdougm 			/*
707a3351425Sdougm 			 * if the option has a value, it will have an '=' to
708a3351425Sdougm 			 * separate the name from the value. The following
709a3351425Sdougm 			 * code will result in value != NULL and token
710a3351425Sdougm 			 * pointing to just the name if there is a value.
7116185db85Sdougm 			 */
712a3351425Sdougm 			value = strchr(token, '=');
713a3351425Sdougm 			if (value != NULL) {
714a3351425Sdougm 				*value++ = '\0';
7156185db85Sdougm 			}
7165cb0d679SMarcel Telka 			if (strcmp(token, SHOPT_SEC) == 0 ||
7175cb0d679SMarcel Telka 			    strcmp(token, SHOPT_SECURE) == 0) {
7186185db85Sdougm 				/*
719a3351425Sdougm 				 * Once in security parsing, we only
720a3351425Sdougm 				 * do security. We do need to move
721a3351425Sdougm 				 * between the security node and the
722a3351425Sdougm 				 * toplevel. The security tag goes on
723a3351425Sdougm 				 * the root while the following ones
724a3351425Sdougm 				 * go on the security.
7256185db85Sdougm 				 */
726a3351425Sdougm 				if (security_list != NULL) {
727a3351425Sdougm 					/*
728a3351425Sdougm 					 * have an old list so close it and
729a3351425Sdougm 					 * start the new
730a3351425Sdougm 					 */
731a3351425Sdougm 					free_security_list(security_list);
732a3351425Sdougm 				}
7335cb0d679SMarcel Telka 				if (strcmp(token, SHOPT_SECURE) == 0) {
734a3351425Sdougm 					value = "dh";
735a3351425Sdougm 				} else {
736a3351425Sdougm 					if (value == NULL) {
737a3351425Sdougm 						ret = SA_SYNTAX_ERR;
738a3351425Sdougm 						break;
739a3351425Sdougm 					}
740a3351425Sdougm 				}
741a3351425Sdougm 				security_list = make_security_list(group,
742a3351425Sdougm 				    value, "nfs");
7436185db85Sdougm 			} else {
744a3351425Sdougm 				/*
745a3351425Sdougm 				 * Note that the "old" syntax allowed a
7465cb0d679SMarcel Telka 				 * default security model.  This must be
747a3351425Sdougm 				 * accounted for and internally converted to
748a3351425Sdougm 				 * "standard" security structure.
749a3351425Sdougm 				 */
750a3351425Sdougm 				if (nfs_is_security_opt(token)) {
751a3351425Sdougm 					if (security_list == NULL) {
752a3351425Sdougm 						/*
753a3351425Sdougm 						 * need to have a
754a3351425Sdougm 						 * security
755a3351425Sdougm 						 * option. This will
756a3351425Sdougm 						 * be "closed" when a
757a3351425Sdougm 						 * defined "sec="
758a3351425Sdougm 						 * option is
759a3351425Sdougm 						 * seen. This is
760a3351425Sdougm 						 * technically an
761a3351425Sdougm 						 * error but will be
762a3351425Sdougm 						 * allowed with
763a3351425Sdougm 						 * warning.
764a3351425Sdougm 						 */
765a3351425Sdougm 						security_list =
766a3351425Sdougm 						    make_security_list(group,
767a3351425Sdougm 						    "default",
768a3351425Sdougm 						    "nfs");
769a3351425Sdougm 					}
770a3351425Sdougm 					if (security_list != NULL) {
771a3351425Sdougm 						ret = add_security_prop(
772a3351425Sdougm 						    security_list, token,
773a3351425Sdougm 						    value, persist, iszfs);
774a3351425Sdougm 					} else {
775a3351425Sdougm 						ret = SA_NO_MEMORY;
776a3351425Sdougm 					}
777a3351425Sdougm 				} else {
778a3351425Sdougm 					/* regular options */
779a3351425Sdougm 					if (value == NULL) {
780a3351425Sdougm 						if (strcmp(token, SHOPT_RW) ==
781a3351425Sdougm 						    0 || strcmp(token,
782a3351425Sdougm 						    SHOPT_RO) == 0) {
783a3351425Sdougm 							value = "*";
784a3351425Sdougm 						} else {
785a3351425Sdougm 							value = "global";
786a3351425Sdougm 							if (strcmp(token,
787a3351425Sdougm 							    SHOPT_LOG) != 0) {
788a3351425Sdougm 								value = "true";
789a3351425Sdougm 							}
790a3351425Sdougm 						}
791a3351425Sdougm 					}
792d6405362Sdougm 					/*
793d6405362Sdougm 					 * In all cases, create the
794d6405362Sdougm 					 * property specified. If the
795d6405362Sdougm 					 * value was NULL, the default
796d6405362Sdougm 					 * value will have been
797d6405362Sdougm 					 * substituted.
798d6405362Sdougm 					 */
799d6405362Sdougm 					prop = sa_create_property(token, value);
800d6405362Sdougm 					ret =  sa_add_property(optionset, prop);
801d6405362Sdougm 					if (ret != SA_OK)
802d6405362Sdougm 						break;
803d6405362Sdougm 
804a3351425Sdougm 					if (!iszfs) {
805a3351425Sdougm 						ret = sa_commit_properties(
806a3351425Sdougm 						    optionset, !persist);
807a3351425Sdougm 					}
808a3351425Sdougm 				}
8096185db85Sdougm 			}
8106185db85Sdougm 		}
8116185db85Sdougm 	}
8126185db85Sdougm 	if (security_list != NULL)
813a3351425Sdougm 		free_security_list(security_list);
814aba7a40bSdougm 
815aba7a40bSdougm 	free(dup);
8166185db85Sdougm 	return (ret);
8176185db85Sdougm }
8186185db85Sdougm 
8196185db85Sdougm /*
8206185db85Sdougm  * is_a_number(number)
8216185db85Sdougm  *
8226185db85Sdougm  * is the string a number in one of the forms we want to use?
8236185db85Sdougm  */
8246185db85Sdougm 
8256185db85Sdougm static int
is_a_number(char * number)8266185db85Sdougm is_a_number(char *number)
8276185db85Sdougm {
8286185db85Sdougm 	int ret = 1;
8296185db85Sdougm 	int hex = 0;
8306185db85Sdougm 
8316185db85Sdougm 	if (strncmp(number, "0x", 2) == 0) {
832a3351425Sdougm 		number += 2;
833a3351425Sdougm 		hex = 1;
834a3351425Sdougm 	} else if (*number == '-') {
835