1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2014, 2016 by Delphix. All rights reserved.
25 * Copyright 2018 Nexenta Systems, Inc.
26 */
27
28/*
29 * NFS specific functions
30 */
31
32#include <stdio.h>
33#include <string.h>
34#include <ctype.h>
35#include <stdlib.h>
36#include <unistd.h>
37#include <errno.h>
38#include <locale.h>
39#include <signal.h>
40#include <strings.h>
41#include "libshare.h"
42#include "libshare_impl.h"
43#include <nfs/export.h>
44#include <pwd.h>
45#include <grp.h>
46#include <limits.h>
47#include <libscf.h>
48#include <syslog.h>
49#include <rpcsvc/daemon_utils.h>
50#include "nfslog_config.h"
51#include "nfslogtab.h"
52#include "libshare_nfs.h"
53#include <nfs/nfs.h>
54#include <nfs/nfssys.h>
55#include <netconfig.h>
56#include "smfcfg.h"
57
58/* should really be in some global place */
59#define	DEF_WIN	30000
60#define	OPT_CHUNK	1024
61
62int debug = 0;
63
64#define	NFS_SERVER_SVC	"svc:/network/nfs/server:default"
65#define	NFS_CLIENT_SVC	(char *)"svc:/network/nfs/client:default"
66
67/* internal functions */
68static int nfs_init();
69static void nfs_fini();
70static int nfs_enable_share(sa_share_t);
71static int nfs_disable_share(sa_share_t, char *);
72static int nfs_validate_property(sa_handle_t, sa_property_t, sa_optionset_t);
73static int nfs_validate_security_mode(char *);
74static int nfs_is_security_opt(char *);
75static int nfs_parse_legacy_options(sa_group_t, char *);
76static char *nfs_format_options(sa_group_t, int);
77static int nfs_set_proto_prop(sa_property_t);
78static sa_protocol_properties_t nfs_get_proto_set();
79static char *nfs_get_status();
80static char *nfs_space_alias(char *);
81static uint64_t nfs_features();
82
83/*
84 * ops vector that provides the protocol specific info and operations
85 * for share management.
86 */
87
88struct sa_plugin_ops sa_plugin_ops = {
89	SA_PLUGIN_VERSION,
90	"nfs",
91	nfs_init,
92	nfs_fini,
93	nfs_enable_share,
94	nfs_disable_share,
95	nfs_validate_property,
96	nfs_validate_security_mode,
97	nfs_is_security_opt,
98	nfs_parse_legacy_options,
99	nfs_format_options,
100	nfs_set_proto_prop,
101	nfs_get_proto_set,
102	nfs_get_status,
103	nfs_space_alias,
104	NULL,	/* update_legacy */
105	NULL,	/* delete_legacy */
106	NULL,	/* change_notify */
107	NULL,	/* enable_resource */
108	NULL,	/* disable_resource */
109	nfs_features,
110	NULL,	/* transient shares */
111	NULL,	/* notify resource */
112	NULL,	/* rename_resource */
113	NULL,	/* run_command */
114	NULL,	/* command_help */
115	NULL	/* delete_proto_section */
116};
117
118/*
119 * list of support services needed
120 * defines should come from head/rpcsvc/daemon_utils.h
121 */
122
123static char *service_list_default[] =
124	{ STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, REPARSED, NULL };
125static char *service_list_logging[] =
126	{ STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NFSLOGD, REPARSED,
127	    NULL };
128
129/*
130 * option definitions.  Make sure to keep the #define for the option
131 * index just before the entry it is the index for. Changing the order
132 * can cause breakage.  E.g OPT_RW is index 1 and must precede the
133 * line that includes the SHOPT_RW and OPT_RW entries.
134 */
135
136struct option_defs optdefs[] = {
137#define	OPT_RO		0
138	{SHOPT_RO, OPT_RO, OPT_TYPE_ACCLIST},
139#define	OPT_RW		1
140	{SHOPT_RW, OPT_RW, OPT_TYPE_ACCLIST},
141#define	OPT_ROOT	2
142	{SHOPT_ROOT, OPT_ROOT, OPT_TYPE_ACCLIST},
143#define	OPT_SECURE	3
144	{SHOPT_SECURE, OPT_SECURE, OPT_TYPE_DEPRECATED},
145#define	OPT_ANON	4
146	{SHOPT_ANON, OPT_ANON, OPT_TYPE_USER},
147#define	OPT_WINDOW	5
148	{SHOPT_WINDOW, OPT_WINDOW, OPT_TYPE_NUMBER},
149#define	OPT_NOSUID	6
150	{SHOPT_NOSUID, OPT_NOSUID, OPT_TYPE_BOOLEAN},
151#define	OPT_ACLOK	7
152	{SHOPT_ACLOK, OPT_ACLOK, OPT_TYPE_BOOLEAN},
153#define	OPT_NOSUB	8
154	{SHOPT_NOSUB, OPT_NOSUB, OPT_TYPE_BOOLEAN},
155#define	OPT_SEC		9
156	{SHOPT_SEC, OPT_SEC, OPT_TYPE_SECURITY},
157#define	OPT_PUBLIC	10
158	{SHOPT_PUBLIC, OPT_PUBLIC, OPT_TYPE_BOOLEAN, OPT_SHARE_ONLY},
159#define	OPT_INDEX	11
160	{SHOPT_INDEX, OPT_INDEX, OPT_TYPE_FILE},
161#define	OPT_LOG		12
162	{SHOPT_LOG, OPT_LOG, OPT_TYPE_LOGTAG},
163#define	OPT_CKSUM	13
164	{SHOPT_CKSUM, OPT_CKSUM, OPT_TYPE_STRINGSET},
165#define	OPT_NONE	14
166	{SHOPT_NONE, OPT_NONE, OPT_TYPE_ACCLIST},
167#define	OPT_ROOT_MAPPING	15
168	{SHOPT_ROOT_MAPPING, OPT_ROOT_MAPPING, OPT_TYPE_USER},
169#define	OPT_CHARSET_MAP	16
170	{"", OPT_CHARSET_MAP, OPT_TYPE_ACCLIST},
171#define	OPT_NOACLFAB	17
172	{SHOPT_NOACLFAB, OPT_NOACLFAB, OPT_TYPE_BOOLEAN},
173#define	OPT_UIDMAP	18
174	{SHOPT_UIDMAP, OPT_UIDMAP, OPT_TYPE_MAPPING},
175#define	OPT_GIDMAP	19
176	{SHOPT_GIDMAP, OPT_GIDMAP, OPT_TYPE_MAPPING},
177#define	OPT_NOHIDE	20
178	{SHOPT_NOHIDE, OPT_NOHIDE, OPT_TYPE_BOOLEAN},
179#ifdef VOLATILE_FH_TEST	/* XXX added for testing volatile fh's only */
180#define	OPT_VOLFH	21
181	{SHOPT_VOLFH, OPT_VOLFH},
182#endif /* VOLATILE_FH_TEST */
183	NULL
184};
185
186/*
187 * Codesets that may need to be converted to UTF-8 for file paths.
188 * Add new names here to add new property support. If we ever get a
189 * way to query the kernel for character sets, this should become
190 * dynamically loaded. Make sure changes here are reflected in
191 * cmd/fs.d/nfs/mountd/nfscmd.c
192 */
193
194static char *legal_conv[] = {
195	"euc-cn",
196	"euc-jp",
197	"euc-jpms",
198	"euc-kr",
199	"euc-tw",
200	"iso8859-1",
201	"iso8859-2",
202	"iso8859-5",
203	"iso8859-6",
204	"iso8859-7",
205	"iso8859-8",
206	"iso8859-9",
207	"iso8859-13",
208	"iso8859-15",
209	"koi8-r",
210	NULL
211};
212
213/*
214 * list of properties that are related to security flavors.
215 */
216static char *seclist[] = {
217	SHOPT_RO,
218	SHOPT_RW,
219	SHOPT_ROOT,
220	SHOPT_WINDOW,
221	SHOPT_NONE,
222	SHOPT_ROOT_MAPPING,
223	SHOPT_UIDMAP,
224	SHOPT_GIDMAP,
225	NULL
226};
227
228/* structure for list of securities */
229struct securities {
230	sa_security_t security;
231	struct securities *next;
232};
233
234/*
235 * findcharset(charset)
236 *
237 * Returns B_TRUE if the charset is a legal conversion otherwise
238 * B_FALSE. This will need to be rewritten to be more efficient when
239 * we have a dynamic list of legal conversions.
240 */
241
242static boolean_t
243findcharset(char *charset)
244{
245	int i;
246
247	for (i = 0; legal_conv[i] != NULL; i++)
248		if (strcmp(charset, legal_conv[i]) == 0)
249			return (B_TRUE);
250	return (B_FALSE);
251}
252
253/*
254 * findopt(name)
255 *
256 * Lookup option "name" in the option table and return the table
257 * index.
258 */
259
260static int
261findopt(char *name)
262{
263	int i;
264	if (name != NULL) {
265		for (i = 0; optdefs[i].tag != NULL; i++) {
266			if (strcmp(optdefs[i].tag, name) == 0)
267				return (optdefs[i].index);
268		}
269		if (findcharset(name))
270			return (OPT_CHARSET_MAP);
271	}
272	return (-1);
273}
274
275/*
276 * gettype(name)
277 *
278 * Return the type of option "name".
279 */
280
281static int
282gettype(char *name)
283{
284	int optdef;
285
286	optdef = findopt(name);
287	if (optdef != -1)
288		return (optdefs[optdef].type);
289	return (OPT_TYPE_ANY);
290}
291
292/*
293 * nfs_validate_security_mode(mode)
294 *
295 * is the specified mode string a valid one for use with NFS?
296 */
297
298static int
299nfs_validate_security_mode(char *mode)
300{
301	seconfig_t secinfo;
302	int err;
303
304	(void) memset(&secinfo, '\0', sizeof (secinfo));
305	err = nfs_getseconfig_byname(mode, &secinfo);
306	if (err == SC_NOERROR)
307		return (1);
308	return (0);
309}
310
311/*
312 * nfs_is_security_opt(tok)
313 *
314 * check to see if tok represents an option that is only valid in some
315 * security flavor.
316 */
317
318static int
319nfs_is_security_opt(char *tok)
320{
321	int i;
322
323	for (i = 0; seclist[i] != NULL; i++) {
324		if (strcmp(tok, seclist[i]) == 0)
325			return (1);
326	}
327	return (0);
328}
329
330/*
331 * find_security(seclist, sec)
332 *
333 * Walk the current list of security flavors and return true if it is
334 * present, else return false.
335 */
336
337static int
338find_security(struct securities *seclist, sa_security_t sec)
339{
340	while (seclist != NULL) {
341		if (seclist->security == sec)
342			return (1);
343		seclist = seclist->next;
344	}
345	return (0);
346}
347
348/*
349 * make_security_list(group, securitymodes, proto)
350 *	go through the list of securitymodes and add them to the
351 *	group's list of security optionsets. We also keep a list of
352 *	those optionsets so we don't have to find them later. All of
353 *	these will get copies of the same properties.
354 */
355
356static struct securities *
357make_security_list(sa_group_t group, char *securitymodes, char *proto)
358{
359	char *tok, *next = NULL;
360	struct securities *curp, *headp = NULL, *prev;
361	sa_security_t check;
362	int freetok = 0;
363
364	for (tok = securitymodes; tok != NULL; tok = next) {
365		next = strchr(tok, ':');
366		if (next != NULL)
367			*next++ = '\0';
368		if (strcmp(tok, "default") == 0) {
369			/* resolve default into the real type */
370			tok = nfs_space_alias(tok);
371			freetok = 1;
372		}
373		check = sa_get_security(group, tok, proto);
374
375		/* add to the security list if it isn't there already */
376		if (check == NULL || !find_security(headp, check)) {
377			curp = (struct securities *)calloc(1,
378			    sizeof (struct securities));
379			if (curp != NULL) {
380				if (check == NULL) {
381					curp->security = sa_create_security(
382					    group, tok, proto);
383				} else {
384					curp->security = check;
385				}
386				/*
387				 * note that the first time through the loop,
388				 * headp will be NULL and prev will be
389				 * undefined.  Since headp is NULL, we set
390				 * both it and prev to the curp (first
391				 * structure to be allocated).
392				 *
393				 * later passes through the loop will have
394				 * headp not being NULL and prev will be used
395				 * to allocate at the end of the list.
396				 */
397				if (headp == NULL) {
398					headp = curp;
399					prev = curp;
400				} else {
401					prev->next = curp;
402					prev = curp;
403				}
404			}
405		}
406
407		if (freetok) {
408			freetok = 0;
409			sa_free_attr_string(tok);
410		}
411	}
412	return (headp);
413}
414
415static void
416free_security_list(struct securities *sec)
417{
418	struct securities *next;
419	if (sec != NULL) {
420		for (next = sec->next; sec != NULL; sec = next) {
421			next = sec->next;
422			free(sec);
423		}
424	}
425}
426
427/*
428 * nfs_alistcat(str1, str2, sep)
429 *
430 * concatenate str1 and str2 into a new string using sep as a separate
431 * character. If memory allocation fails, return NULL;
432 */
433
434static char *
435nfs_alistcat(char *str1, char *str2, char sep)
436{
437	char *newstr;
438	size_t len;
439
440	len = strlen(str1) + strlen(str2) + 2;
441	newstr = (char *)malloc(len);
442	if (newstr != NULL)
443		(void) snprintf(newstr, len, "%s%c%s", str1, sep, str2);
444	return (newstr);
445}
446
447/*
448 * add_security_prop(sec, name, value, persist, iszfs)
449 *
450 * Add the property to the securities structure. This accumulates
451 * properties for as part of parsing legacy options.
452 */
453
454static int
455add_security_prop(struct securities *sec, char *name, char *value,
456    int persist, int iszfs)
457{
458	sa_property_t prop;
459	int ret = SA_OK;
460
461	for (; sec != NULL; sec = sec->next) {
462		if (value == NULL) {
463			if (strcmp(name, SHOPT_RW) == 0 ||
464			    strcmp(name, SHOPT_RO) == 0)
465				value = "*";
466			else
467				value = "true";
468		}
469
470		/*
471		 * Get the existing property, if it exists, so we can
472		 * determine what to do with it. The ro/rw/root
473		 * properties can be merged if multiple instances of
474		 * these properies are given. For example, if "rw"
475		 * exists with a value "host1" and a later token of
476		 * rw="host2" is seen, the values are merged into a
477		 * single rw="host1:host2".
478		 */
479		prop = sa_get_property(sec->security, name);
480
481		if (prop != NULL) {
482			char *oldvalue;
483			char *newvalue;
484
485			/*
486			 * The security options of ro/rw/root/uidmap/gidmap
487			 * might appear multiple times.  If they do, the values
488			 * need to be merged.  If it was previously empty, the
489			 * new value alone is added.
490			 */
491			oldvalue = sa_get_property_attr(prop, "value");
492			if (oldvalue != NULL) {
493				char sep = ':';
494
495				if (strcmp(name, SHOPT_UIDMAP) == 0 ||
496				    strcmp(name, SHOPT_GIDMAP) == 0)
497					sep = '~';
498
499				/*
500				 * The general case is to concatenate the new
501				 * value onto the old value for multiple
502				 * rw(ro/root/uidmap/gidmap) properties.  For
503				 * rw/ro/root a special case exists when either
504				 * the old or new is the "all" case.  In the
505				 * special case, if both are "all", then it is
506				 * "all", else if one is an access-list, that
507				 * replaces the "all".
508				 */
509				if (strcmp(oldvalue, "*") == 0) {
510					/* Replace old value with new value. */
511					newvalue = strdup(value);
512				} else if (strcmp(value, "*") == 0 ||
513				    strcmp(oldvalue, value) == 0) {
514					/*
515					 * Keep old value and ignore
516					 * the new value.
517					 */
518					newvalue = NULL;
519				} else {
520					/*
521					 * Make a new list of old plus new
522					 * access-list.
523					 */
524					newvalue = nfs_alistcat(oldvalue,
525					    value, sep);
526				}
527
528				if (newvalue != NULL) {
529					(void) sa_remove_property(prop);
530					prop = sa_create_property(name,
531					    newvalue);
532					ret = sa_add_property(sec->security,
533					    prop);
534					free(newvalue);
535				}
536
537				sa_free_attr_string(oldvalue);
538			}
539		} else {
540			prop = sa_create_property(name, value);
541			ret = sa_add_property(sec->security, prop);
542		}
543		if (ret == SA_OK && !iszfs) {
544			ret = sa_commit_properties(sec->security, !persist);
545		}
546	}
547	return (ret);
548}
549
550/*
551 * check to see if group/share is persistent.
552 */
553static int
554is_persistent(sa_group_t group)
555{
556	char *type;
557	int persist = 1;
558
559	type = sa_get_group_attr(group, "type");
560	if (type != NULL && strcmp(type, "persist") != 0)
561		persist = 0;
562	if (type != NULL)
563		sa_free_attr_string(type);
564	return (persist);
565}
566
567/*
568 * invalid_security(options)
569 *
570 * search option string for any invalid sec= type.
571 * return true (1) if any are not valid else false (0)
572 */
573static int
574invalid_security(char *options)
575{
576	char *copy, *base, *token, *value;
577	int ret = 0;
578
579	copy = strdup(options);
580	token = base = copy;
581	while (token != NULL && ret == 0) {
582		token = strtok(base, ",");
583		base = NULL;
584		if (token != NULL) {
585			value = strchr(token, '=');
586			if (value != NULL)
587				*value++ = '\0';
588			if (strcmp(token, SHOPT_SEC) == 0) {
589				/* HAVE security flavors so check them */
590				char *tok, *next;
591				for (next = NULL, tok = value; tok != NULL;
592				    tok = next) {
593					next = strchr(tok, ':');
594					if (next != NULL)
595						*next++ = '\0';
596					ret = !nfs_validate_security_mode(tok);
597					if (ret)
598						break;
599				}
600			}
601		}
602	}
603	if (copy != NULL)
604		free(copy);
605	return (ret);
606}
607
608/*
609 * nfs_parse_legacy_options(group, options)
610 *
611 * Parse the old style options into internal format and store on the
612 * specified group.  Group could be a share for full legacy support.
613 */
614
615static int
616nfs_parse_legacy_options(sa_group_t group, char *options)
617{
618	char *dup;
619	char *base;
620	char *token;
621	sa_optionset_t optionset;
622	struct securities *security_list = NULL;
623	sa_property_t prop;
624	int ret = SA_OK;
625	int iszfs = 0;
626	sa_group_t parent;
627	int persist = 0;
628	char *lasts;
629
630	/* do we have an existing optionset? */
631	optionset = sa_get_optionset(group, "nfs");
632	if (optionset == NULL) {
633		/* didn't find existing optionset so create one */
634		optionset = sa_create_optionset(group, "nfs");
635	} else {
636		/*
637		 * Have an existing optionset . Ideally, we would need
638		 * to compare options in order to detect errors. For
639		 * now, we assume that the first optionset is the
640		 * correct one and the others will be the same. An
641		 * empty optionset is the same as no optionset so we
642		 * don't want to exit in that case. Getting an empty
643		 * optionset can occur with ZFS property checking.
644		 */
645		if (sa_get_property(optionset, NULL) != NULL)
646			return (ret);
647	}
648
649	if (strcmp(options, SHOPT_RW) == 0) {
650		/*
651		 * there is a special case of only the option "rw"
652		 * being the default option. We don't have to do
653		 * anything.
654		 */
655		return (ret);
656	}
657
658	/*
659	 * check if security types are present and validate them. If
660	 * any are not legal, fail.
661	 */
662
663	if (invalid_security(options)) {
664		return (SA_INVALID_SECURITY);
665	}
666
667	/*
668	 * in order to not attempt to change ZFS properties unless
669	 * absolutely necessary, we never do it in the legacy parsing.
670	 */
671	if (sa_is_share(group)) {
672		char *zfs;
673		parent = sa_get_parent_group(group);
674		if (parent != NULL) {
675			zfs = sa_get_group_attr(parent, "zfs");
676			if (zfs != NULL) {
677				sa_free_attr_string(zfs);
678				iszfs++;
679			}
680		}
681	} else {
682		iszfs = sa_group_is_zfs(group);
683	}
684
685	/* We need a copy of options for the next part. */
686	dup = strdup(options);
687	if (dup == NULL)
688		return (SA_NO_MEMORY);
689
690	/*
691	 * we need to step through each option in the string and then
692	 * add either the option or the security option as needed. If
693	 * this is not a persistent share, don't commit to the
694	 * repository. If there is an error, we also want to abort the
695	 * processing and report it.
696	 */
697	persist = is_persistent(group);
698	base = dup;
699	token = dup;
700	lasts = NULL;
701	while (token != NULL && ret == SA_OK) {
702		token = strtok_r(base, ",", &lasts);
703		base = NULL;
704		if (token != NULL) {
705			char *value;
706			/*
707			 * if the option has a value, it will have an '=' to
708			 * separate the name from the value. The following
709			 * code will result in value != NULL and token
710			 * pointing to just the name if there is a value.
711			 */
712			value = strchr(token, '=');
713			if (value != NULL) {
714				*value++ = '\0';
715			}
716			if (strcmp(token, SHOPT_SEC) == 0 ||
717			    strcmp(token, SHOPT_SECURE) == 0) {
718				/*
719				 * Once in security parsing, we only
720				 * do security. We do need to move
721				 * between the security node and the
722				 * toplevel. The security tag goes on
723				 * the root while the following ones
724				 * go on the security.
725				 */
726				if (security_list != NULL) {
727					/*
728					 * have an old list so close it and
729					 * start the new
730					 */
731					free_security_list(security_list);
732				}
733				if (strcmp(token, SHOPT_SECURE) == 0) {
734					value = "dh";
735				} else {
736					if (value == NULL) {
737						ret = SA_SYNTAX_ERR;
738						break;
739					}
740				}
741				security_list = make_security_list(group,
742				    value, "nfs");
743			} else {
744				/*
745				 * Note that the "old" syntax allowed a
746				 * default security model.  This must be
747				 * accounted for and internally converted to
748				 * "standard" security structure.
749				 */
750				if (nfs_is_security_opt(token)) {
751					if (security_list == NULL) {
752						/*
753						 * need to have a
754						 * security
755						 * option. This will
756						 * be "closed" when a
757						 * defined "sec="
758						 * option is
759						 * seen. This is
760						 * technically an
761						 * error but will be
762						 * allowed with
763						 * warning.
764						 */
765						security_list =
766						    make_security_list(group,
767						    "default",
768						    "nfs");
769					}
770					if (security_list != NULL) {
771						ret = add_security_prop(
772						    security_list, token,
773						    value, persist, iszfs);
774					} else {
775						ret = SA_NO_MEMORY;
776					}
777				} else {
778					/* regular options */
779					if (value == NULL) {
780						if (strcmp(token, SHOPT_RW) ==
781						    0 || strcmp(token,
782						    SHOPT_RO) == 0) {
783							value = "*";
784						} else {
785							value = "global";
786							if (strcmp(token,
787							    SHOPT_LOG) != 0) {
788								value = "true";
789							}
790						}
791					}
792					/*
793					 * In all cases, create the
794					 * property specified. If the
795					 * value was NULL, the default
796					 * value will have been
797					 * substituted.
798					 */
799					prop = sa_create_property(token, value);
800					ret =  sa_add_property(optionset, prop);
801					if (ret != SA_OK)
802						break;
803
804					if (!iszfs) {
805						ret = sa_commit_properties(
806						    optionset, !persist);
807					}
808				}
809			}
810		}
811	}
812	if (security_list != NULL)
813		free_security_list(security_list);
814
815	free(dup);
816	return (ret);
817}
818
819/*
820 * is_a_number(number)
821 *
822 * is the string a number in one of the forms we want to use?
823 */
824
825static int
826is_a_number(char *number)
827{
828	int ret = 1;
829	int hex = 0;
830
831	if (strncmp(number, "0x", 2) == 0) {
832		number += 2;
833		hex = 1;
834	} else if (*number == '-') {
835		number++; /* skip the minus */
836	}
837	while (ret == 1 && *number != '\0') {
838		if (hex) {
839			ret = isxdigit(*number++);
840		} else {
841			ret = isdigit(*number++);
842		}
843	}
844	return (ret);
845}
846
847/*
848 * Look for the specified tag in the configuration file. If it is found,
849 * enable logging and set the logging configuration information for exp.
850 */
851static void
852configlog(struct exportdata *exp, char *tag)
853{
854	nfsl_config_t *configlist = NULL, *configp;
855	int error = 0;
856	char globaltag[] = DEFAULTTAG;
857
858	/*
859	 * Sends config errors to stderr
860	 */
861	nfsl_errs_to_syslog = B_FALSE;
862
863	/*
864	 * get the list of configuration settings
865	 */
866	error = nfsl_getconfig_list(&configlist);
867	if (error) {
868		(void) fprintf(stderr,
869		    dgettext(TEXT_DOMAIN, "Cannot get log configuration: %s\n"),
870		    strerror(error));
871	}
872
873	if (tag == NULL)
874		tag = globaltag;
875	if ((configp = nfsl_findconfig(configlist, tag, &error)) == NULL) {
876		nfsl_freeconfig_list(&configlist);
877		(void) fprintf(stderr,
878		    dgettext(TEXT_DOMAIN, "No tags matching \"%s\"\n"), tag);
879		/* bad configuration */
880		error = ENOENT;
881		goto err;
882	}
883
884	if ((exp->ex_tag = strdup(tag)) == NULL) {
885		error = ENOMEM;
886		goto out;
887	}
888	if ((exp->ex_log_buffer = strdup(configp->nc_bufferpath)) == NULL) {
889		error = ENOMEM;
890		goto out;
891	}
892	exp->ex_flags |= EX_LOG;
893	if (configp->nc_rpclogpath != NULL)
894		exp->ex_flags |= EX_LOG_ALLOPS;
895out:
896	if (configlist != NULL)
897		nfsl_freeconfig_list(&configlist);
898
899err:
900	if (error != 0) {
901		free(exp->ex_tag);
902		free(exp->ex_log_buffer);
903		(void) fprintf(stderr,
904		    dgettext(TEXT_DOMAIN, "Cannot set log configuration: %s\n"),
905		    strerror(error));
906	}
907}
908
909/*
910 * fill_export_from_optionset(export, optionset)
911 *
912 * In order to share, we need to set all the possible general options
913 * into the export structure. Share info will be filled in by the
914 * caller. Various property values get turned into structure specific
915 * values.
916 */
917
918static int
919fill_export_from_optionset(struct exportdata *export, sa_optionset_t optionset)
920{
921	sa_property_t option;
922	int ret = SA_OK;
923
924	for (option = sa_get_property(optionset, NULL);
925	    option != NULL; option = sa_get_next_property(option)) {
926		char *name;
927		char *value;
928		uint32_t val;
929
930		/*
931		 * since options may be set/reset multiple times, always do an
932		 * explicit set or clear of the option. This allows defaults
933		 * to be set and then the protocol specific to override.
934		 */
935
936		name = sa_get_property_attr(option, "type");
937		value = sa_get_property_attr(option, "value");
938		switch (findopt(name)) {
939		case OPT_ANON:
940			if (value != NULL && is_a_number(value)) {
941				val = strtoul(value, NULL, 0);
942			} else {
943				struct passwd *pw;
944				pw = getpwnam(value != NULL ? value : "nobody");
945				if (pw != NULL) {
946					val = pw->pw_uid;
947				} else {
948					val = UID_NOBODY;
949				}
950				endpwent();
951			}
952			export->ex_anon = val;
953			break;
954		case OPT_NOSUID:
955			if (value != NULL && (strcasecmp(value, "true") == 0 ||
956			    strcmp(value, "1") == 0))
957				export->ex_flags |= EX_NOSUID;
958			else
959				export->ex_flags &= ~EX_NOSUID;
960			break;
961		case OPT_ACLOK:
962			if (value != NULL && (strcasecmp(value, "true") == 0 ||
963			    strcmp(value, "1") == 0))
964				export->ex_flags |= EX_ACLOK;
965			else
966				export->ex_flags &= ~EX_ACLOK;
967			break;
968		case OPT_NOSUB:
969			if (value != NULL && (strcasecmp(value, "true") == 0 ||
970			    strcmp(value, "1") == 0))
971				export->ex_flags |= EX_NOSUB;
972			else
973				export->ex_flags &= ~EX_NOSUB;
974			break;
975		case OPT_PUBLIC:
976			if (value != NULL && (strcasecmp(value, "true") == 0 ||
977			    strcmp(value, "1") == 0))
978				export->ex_flags |= EX_PUBLIC;
979			else
980				export->ex_flags &= ~EX_PUBLIC;
981			break;
982		case OPT_INDEX:
983			if (value != NULL && (strcmp(value, "..") == 0 ||
984			    strchr(value, '/') != NULL)) {
985				/* this is an error */
986				(void) printf(dgettext(TEXT_DOMAIN,
987				    "NFS: index=\"%s\" not valid;"
988				    "must be a filename.\n"),
989				    value);
990				break;
991			}
992			if (value != NULL && *value != '\0' &&
993			    strcmp(value, ".") != 0) {
994				/* valid index file string */
995				if (export->ex_index != NULL) {
996					/* left over from "default" */
997					free(export->ex_index);
998				}
999				/* remember to free */
1000				export->ex_index = strdup(value);
1001				if (export->ex_index == NULL) {
1002					(void) printf(dgettext(TEXT_DOMAIN,
1003					    "NFS: out of memory setting "
1004					    "index property\n"));
1005					break;
1006				}
1007				export->ex_flags |= EX_INDEX;
1008			}
1009			break;
1010		case OPT_LOG:
1011			if (value == NULL)
1012				value = strdup("global");
1013			if (value != NULL)
1014				configlog(export,
1015				    strlen(value) ? value : "global");
1016			break;
1017		case OPT_CHARSET_MAP:
1018			/*
1019			 * Set EX_CHARMAP when there is at least one
1020			 * charmap conversion property. This will get
1021			 * checked by the nfs server when it needs to.
1022			 */
1023			export->ex_flags |= EX_CHARMAP;
1024			break;
1025		case OPT_NOACLFAB:
1026			if (value != NULL && (strcasecmp(value, "true") == 0 ||
1027			    strcmp(value, "1") == 0))
1028				export->ex_flags |= EX_NOACLFAB;
1029			else
1030				export->ex_flags &= ~EX_NOACLFAB;
1031			break;
1032		case OPT_NOHIDE:
1033			if (value != NULL && (strcasecmp(value, "true") == 0 ||
1034			    strcmp(value, "1") == 0))
1035				export->ex_flags |= EX_NOHIDE;
1036			else
1037				export->ex_flags &= ~EX_NOHIDE;
1038
1039			break;
1040		default:
1041			/* have a syntactic error */
1042			(void) printf(dgettext(TEXT_DOMAIN,
1043			    "NFS: unrecognized option %s=%s\n"),
1044			    name != NULL ? name : "",
1045			    value != NULL ? value : "");
1046			break;
1047		}
1048		if (name != NULL)
1049			sa_free_attr_string(name);
1050		if (value != NULL)
1051			sa_free_attr_string(value);
1052	}
1053	return (ret);
1054}
1055
1056/*
1057 * cleanup_export(export)
1058 *
1059 * Cleanup the allocated areas so we don't leak memory
1060 */
1061
1062static void
1063cleanup_export(struct exportdata *export)
1064{
1065	int i;
1066
1067	free(export->ex_index);
1068
1069	for (i = 0; i < export->ex_seccnt; i++) {
1070		struct secinfo *s = &export->ex_secinfo[i];
1071
1072		while (s->s_rootcnt > 0)
1073			free(s->s_rootnames[--s->s_rootcnt]);
1074
1075		free(s->s_rootnames);
1076	}
1077	free(export->ex_secinfo);
1078}
1079
1080/*
1081 * Given a seconfig entry and a colon-separated
1082 * list of names, allocate an array big enough
1083 * to hold the root list, then convert each name to
1084 * a principal name according to the security
1085 * info and assign it to an array element.
1086 * Return the array and its size.
1087 */
1088static caddr_t *
1089get_rootnames(seconfig_t *sec, char *list, int *count)
1090{
1091	caddr_t *a;
1092	int c, i;
1093	char *host, *p;
1094
1095	/*
1096	 * Count the number of strings in the list.
1097	 * This is the number of colon separators + 1.
1098	 */
1099	c = 1;
1100	for (p = list; *p; p++)
1101		if (*p == ':')
1102			c++;
1103	*count = c;
1104
1105	a = (caddr_t *)malloc(c * sizeof (char *));
1106	if (a == NULL) {
1107		(void) printf(dgettext(TEXT_DOMAIN,
1108		    "get_rootnames: no memory\n"));
1109		*count = 0;
1110	} else {
1111		for (i = 0; i < c; i++) {
1112			host = strtok(list, ":");
1113			if (!nfs_get_root_principal(sec, host, &a[i])) {
1114				while (i > 0)
1115					free(a[--i]);
1116				free(a);
1117				a = NULL;
1118				*count = 0;
1119				break;
1120			}
1121			list = NULL;
1122		}
1123	}
1124
1125	return (a);
1126}
1127
1128/*
1129 * fill_security_from_secopts(sp, secopts)
1130 *
1131 * Fill the secinfo structure from the secopts optionset.
1132 */
1133
1134static int
1135fill_security_from_secopts(struct secinfo *sp, sa_security_t secopts)
1136{
1137	sa_property_t prop;
1138	char *type;
1139	int longform;
1140	int err = SC_NOERROR;
1141	uint32_t val;
1142
1143	type = sa_get_security_attr(secopts, "sectype");
1144	if (type != NULL) {
1145		/* named security type needs secinfo to be filled in */
1146		err = nfs_getseconfig_byname(type, &sp->s_secinfo);
1147		sa_free_attr_string(type);
1148		if (err != SC_NOERROR)
1149			return (err);
1150	} else {
1151		/* default case */
1152		err = nfs_getseconfig_default(&sp->s_secinfo);
1153		if (err != SC_NOERROR)
1154			return (err);
1155	}
1156
1157	err = SA_OK;
1158	for (prop = sa_get_property(secopts, NULL);
1159	    prop != NULL && err == SA_OK;
1160	    prop = sa_get_next_property(prop)) {
1161		char *name;
1162		char *value;
1163
1164		name = sa_get_property_attr(prop, "type");
1165		value = sa_get_property_attr(prop, "value");
1166
1167		longform = value != NULL && strcmp(value, "*") != 0;
1168
1169		switch (findopt(name)) {
1170		case OPT_RO:
1171			sp->s_flags |= longform ? M_ROL : M_RO;
1172			break;
1173		case OPT_RW:
1174			sp->s_flags |= longform ? M_RWL : M_RW;
1175			break;
1176		case OPT_ROOT:
1177			sp->s_flags |= M_ROOT;
1178			/*
1179			 * if we are using AUTH_UNIX, handle like other things
1180			 * such as RO/RW
1181			 */
1182			if (sp->s_secinfo.sc_rpcnum == AUTH_UNIX)
1183				break;
1184			/* not AUTH_UNIX */
1185			if (value != NULL) {
1186				sp->s_rootnames = get_rootnames(&sp->s_secinfo,
1187				    value, &sp->s_rootcnt);
1188				if (sp->s_rootnames == NULL) {
1189					err = SA_BAD_VALUE;
1190					(void) fprintf(stderr,
1191					    dgettext(TEXT_DOMAIN,
1192					    "Bad root list\n"));
1193				}
1194			}
1195			break;
1196		case OPT_NONE:
1197			sp->s_flags |= M_NONE;
1198			break;
1199		case OPT_WINDOW:
1200			if (value != NULL) {
1201				sp->s_window = atoi(value);
1202				/* just in case */
1203				if (sp->s_window < 0)
1204					sp->s_window = DEF_WIN;
1205			}
1206			break;
1207		case OPT_ROOT_MAPPING:
1208			if (value != NULL && is_a_number(value)) {
1209				val = strtoul(value, NULL, 0);
1210			} else {
1211				struct passwd *pw;
1212				pw = getpwnam(value != NULL ? value : "nobody");
1213				if (pw != NULL) {
1214					val = pw->pw_uid;
1215				} else {
1216					val = UID_NOBODY;
1217				}
1218				endpwent();
1219			}
1220			sp->s_rootid = val;
1221			break;
1222		case OPT_UIDMAP:
1223		case OPT_GIDMAP:
1224			sp->s_flags |= M_MAP;
1225			break;
1226		default:
1227			break;
1228		}
1229		if (name != NULL)
1230			sa_free_attr_string(name);
1231		if (value != NULL)
1232			sa_free_attr_string(value);
1233	}
1234	/* if rw/ro options not set, use default of RW */
1235	if ((sp->s_flags & NFS_RWMODES) == 0)
1236		sp->s_flags |= M_RW;
1237	return (err);
1238}
1239
1240/*
1241 * This is for testing only
1242 * It displays the export structure that
1243 * goes into the kernel.
1244 */
1245static void
1246printarg(char *path, struct exportdata *ep)
1247{
1248	int i, j;
1249	struct secinfo *sp;
1250
1251	if (debug == 0)
1252		return;
1253
1254	(void) printf("%s:\n", path);
1255	(void) printf("\tex_version = %d\n", ep->ex_version);
1256	(void) printf("\tex_path = %s\n", ep->ex_path);
1257	(void) printf("\tex_pathlen = %ld\n", (ulong_t)ep->ex_pathlen);
1258	(void) printf("\tex_flags: (0x%02x) ", ep->ex_flags);
1259	if (ep->ex_flags & EX_NOSUID)
1260		(void) printf("NOSUID ");
1261	if (ep->ex_flags & EX_ACLOK)
1262		(void) printf("ACLOK ");
1263	if (ep->ex_flags & EX_PUBLIC)
1264		(void) printf("PUBLIC ");
1265	if (ep->ex_flags & EX_NOSUB)
1266		(void) printf("NOSUB ");
1267	if (ep->ex_flags & EX_LOG)
1268		(void) printf("LOG ");
1269	if (ep->ex_flags & EX_CHARMAP)
1270		(void) printf("CHARMAP ");
1271	if (ep->ex_flags & EX_LOG_ALLOPS)
1272		(void) printf("LOG_ALLOPS ");
1273	if (ep->ex_flags == 0)
1274		(void) printf("(none)");
1275	(void) printf("\n");
1276	if (ep->ex_flags & EX_LOG) {
1277		(void) printf("\tex_log_buffer = %s\n",
1278		    (ep->ex_log_buffer ? ep->ex_log_buffer : "(NULL)"));
1279		(void) printf("\tex_tag = %s\n",
1280		    (ep->ex_tag ? ep->ex_tag : "(NULL)"));
1281	}
1282	(void) printf("\tex_anon = %d\n", ep->ex_anon);
1283	(void) printf("\tex_seccnt = %d\n", ep->ex_seccnt);
1284	(void) printf("\n");
1285	for (i = 0; i < ep->ex_seccnt; i++) {
1286		sp = &ep->ex_secinfo[i];
1287		(void) printf("\t\ts_secinfo = %s\n", sp->s_secinfo.sc_name);
1288		(void) printf("\t\ts_flags: (0x%02x) ", sp->s_flags);
1289		if (sp->s_flags & M_ROOT) (void) printf("M_ROOT ");
1290		if (sp->s_flags & M_RO) (void) printf("M_RO ");
1291		if (sp->s_flags & M_ROL) (void) printf("M_ROL ");
1292		if (sp->s_flags & M_RW) (void) printf("M_RW ");
1293		if (sp->s_flags & M_RWL) (void) printf("M_RWL ");
1294		if (sp->s_flags & M_NONE) (void) printf("M_NONE ");
1295		if (sp->s_flags & M_MAP) (void) printf("M_MAP ");
1296		if (sp->s_flags == 0) (void) printf("(none)");
1297		(void) printf("\n");
1298		(void) printf("\t\ts_window = %d\n", sp->s_window);
1299		(void) printf("\t\ts_rootid = %d\n", sp->s_rootid);
1300		(void) printf("\t\ts_rootcnt = %d ", sp->s_rootcnt);
1301		(void) fflush(stdout);
1302		for (j = 0; j < sp->s_rootcnt; j++)
1303			(void) printf("%s ", sp->s_rootnames[j] ?
1304			    sp->s_rootnames[j] : "<null>");
1305		(void) printf("\n\n");
1306	}
1307}
1308
1309/*
1310 * count_security(opts)
1311 *
1312 * Count the number of security types (flavors). The optionset has
1313 * been populated with the security flavors as a holding mechanism.
1314 * We later use this number to allocate data structures.
1315 */
1316
1317static int
1318count_security(sa_optionset_t opts)
1319{
1320	int count = 0;
1321	sa_property_t prop;
1322	if (opts != NULL) {
1323		for (prop = sa_get_property(opts, NULL); prop != NULL;
1324		    prop = sa_get_next_property(prop)) {
1325			count++;
1326		}
1327	}
1328	return (count);
1329}
1330
1331/*
1332 * nfs_sprint_option(rbuff, rbuffsize, incr, prop, sep)
1333 *
1334 * provides a mechanism to format NFS properties into legacy output
1335 * format. If the buffer would overflow, it is reallocated and grown
1336 * as appropriate. Special cases of converting internal form of values
1337 * to those used by "share" are done. this function does one property
1338 * at a time.
1339 */
1340
1341static int
1342nfs_sprint_option(char **rbuff, size_t *rbuffsize, size_t incr,
1343    sa_property_t prop, int sep)
1344{
1345	char *name;
1346	char *value;
1347	int curlen;
1348	char *buff = *rbuff;
1349	size_t buffsize = *rbuffsize;
1350	int printed = B_FALSE;
1351
1352	name = sa_get_property_attr(prop, "type");
1353	value = sa_get_property_attr(prop, "value");
1354	if (buff != NULL)
1355		curlen = strlen(buff);
1356	else
1357		curlen = 0;
1358	if (name != NULL) {
1359		int len;
1360		len = strlen(name) + sep;
1361
1362		/*
1363		 * A future RFE would be to replace this with more
1364		 * generic code and to possibly handle more types.
1365		 */
1366		switch (gettype(name)) {
1367		case OPT_TYPE_BOOLEAN:
1368			/*
1369			 * For NFS, boolean value of FALSE means it
1370			 * doesn't show up in the option list at all.
1371			 */
1372			if (value != NULL && strcasecmp(value, "false") == 0)
1373				goto skip;
1374			if (value != NULL) {
1375				sa_free_attr_string(value);
1376				value = NULL;
1377			}
1378			break;
1379		case OPT_TYPE_ACCLIST:
1380			if (value != NULL && strcmp(value, "*") == 0) {
1381				sa_free_attr_string(value);
1382				value = NULL;
1383			} else {
1384				if (value != NULL)
1385					len += 1 + strlen(value);
1386			}
1387			break;
1388		case OPT_TYPE_LOGTAG:
1389			if (value != NULL && strlen(value) == 0) {
1390				sa_free_attr_string(value);
1391				value = NULL;
1392			} else {
1393				if (value != NULL)
1394					len += 1 + strlen(value);
1395			}
1396			break;
1397		default:
1398			if (value != NULL)
1399				len += 1 + strlen(value);
1400			break;
1401		}
1402		while (buffsize <= (curlen + len)) {
1403			/* need more room */
1404			buffsize += incr;
1405			buff = realloc(buff, buffsize);
1406			if (buff == NULL) {
1407				/* realloc failed so free everything */
1408				if (*rbuff != NULL)
1409					free(*rbuff);
1410			}
1411			*rbuff = buff;
1412			*rbuffsize = buffsize;
1413			if (buff == NULL)
1414				goto skip;
1415
1416		}
1417
1418		if (buff == NULL)
1419			goto skip;
1420
1421		if (value == NULL) {
1422			(void) snprintf(buff + curlen, buffsize - curlen,
1423			    "%s%s", sep ? "," : "", name);
1424		} else {
1425			(void) snprintf(buff + curlen, buffsize - curlen,
1426			    "%s%s=%s", sep ? "," : "",
1427			    name, value != NULL ? value : "");
1428		}
1429		printed = B_TRUE;
1430	}
1431skip:
1432	if (name != NULL)
1433		sa_free_attr_string(name);
1434	if (value != NULL)
1435		sa_free_attr_string(value);
1436	return (printed);
1437}
1438
1439/*
1440 * nfs_format_options(group, hier)
1441 *
1442 * format all the options on the group into an old-style option
1443 * string. If hier is non-zero, walk up the tree to get inherited
1444 * options.
1445 */
1446
1447static char *
1448nfs_format_options(sa_group_t group, int hier)
1449{
1450	sa_optionset_t options = NULL;
1451	sa_optionset_t secoptions = NULL;
1452	sa_property_t prop, secprop;
1453	sa_security_t security = NULL;
1454	char *buff;
1455	size_t buffsize;
1456	char *sectype = NULL;
1457	int sep = 0;
1458
1459
1460	buff = malloc(OPT_CHUNK);
1461	if (buff == NULL) {
1462		return (NULL);
1463	}
1464
1465	buff[0] = '\0';
1466	buffsize = OPT_CHUNK;
1467
1468	/*
1469	 * We may have a an optionset relative to this item. format
1470	 * these if we find them and then add any security definitions.
1471	 */
1472
1473	options = sa_get_derived_optionset(group, "nfs", hier);
1474
1475	/*
1476	 * do the default set first but skip any option that is also
1477	 * in the protocol specific optionset.
1478	 */
1479	if (options != NULL) {
1480		for (prop = sa_get_property(options, NULL);
1481		    prop != NULL; prop = sa_get_next_property(prop)) {
1482			/*
1483			 * use this one since we skipped any
1484			 * of these that were also in
1485			 * optdefault
1486			 */
1487			if (nfs_sprint_option(&buff, &buffsize, OPT_CHUNK,
1488			    prop, sep))
1489				sep = 1;
1490			if (buff == NULL) {
1491				/*
1492				 * buff could become NULL if there
1493				 * isn't enough memory for
1494				 * nfs_sprint_option to realloc()
1495				 * as necessary. We can't really
1496				 * do anything about it at this
1497				 * point so we return NULL.  The
1498				 * caller should handle the
1499				 * failure.
1500				 */
1501				if (options != NULL)
1502					sa_free_derived_optionset(
1503					    options);
1504				return (buff);
1505			}
1506		}
1507	}
1508	secoptions = (sa_optionset_t)sa_get_all_security_types(group,
1509	    "nfs", hier);
1510	if (secoptions != NULL) {
1511		for (secprop = sa_get_property(secoptions, NULL);
1512		    secprop != NULL;
1513		    secprop = sa_get_next_property(secprop)) {
1514			sectype = sa_get_property_attr(secprop, "type");
1515			security =
1516			    (sa_security_t)sa_get_derived_security(
1517			    group, sectype, "nfs", hier);
1518			if (security != NULL) {
1519				if (sectype != NULL) {
1520					prop = sa_create_property(
1521					    "sec", sectype);
1522					if (prop == NULL)
1523						goto err;
1524					if (nfs_sprint_option(&buff,
1525					    &buffsize, OPT_CHUNK, prop, sep))
1526						sep = 1;
1527					(void) sa_remove_property(prop);
1528					if (buff == NULL)
1529						goto err;
1530				}
1531				for (prop = sa_get_property(security,
1532				    NULL); prop != NULL;
1533				    prop = sa_get_next_property(prop)) {
1534					if (nfs_sprint_option(&buff,
1535					    &buffsize, OPT_CHUNK, prop, sep))
1536						sep = 1;
1537					if (buff == NULL)
1538						goto err;
1539				}
1540				sa_free_derived_optionset(security);
1541			}
1542			if (sectype != NULL)
1543				sa_free_attr_string(sectype);
1544		}
1545		sa_free_derived_optionset(secoptions);
1546	}
1547
1548	if (options != NULL)
1549		sa_free_derived_optionset(options);
1550	return (buff);
1551
1552err:
1553	/*
1554	 * If we couldn't allocate memory for option printing, we need
1555	 * to break out of the nested loops, cleanup and return NULL.
1556	 */
1557	if (secoptions != NULL)
1558		sa_free_derived_optionset(secoptions);
1559	if (security != NULL)
1560		sa_free_derived_optionset(security);
1561	if (sectype != NULL)
1562		sa_free_attr_string(sectype);
1563	if (options != NULL)
1564		sa_free_derived_optionset(options);
1565	return (buff);
1566}
1567
1568/*
1569 * Append an entry to the nfslogtab file
1570 */
1571static int
1572nfslogtab_add(char *dir, char *buffer, char *tag)
1573{
1574	FILE *f;
1575	struct logtab_ent lep;
1576	int error = 0;
1577
1578	/*
1579	 * Open the file for update and create it if necessary.
1580	 * This may leave the I/O offset at the end of the file,
1581	 * so rewind back to the beginning of the file.
1582	 */
1583	f = fopen(NFSLOGTAB, "a+");
1584	if (f == NULL) {
1585		error = errno;
1586		goto out;
1587	}
1588	rewind(f);
1589
1590	if (lockf(fileno(f), F_LOCK, 0L) < 0) {
1591		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1592		    "share complete, however failed to lock %s "
1593		    "for update: %s\n"), NFSLOGTAB, strerror(errno));
1594		error = -1;
1595		goto out;
1596	}
1597
1598	if (logtab_deactivate_after_boot(f) == -1) {
1599		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1600		    "share complete, however could not deactivate "
1601		    "entries in %s\n"), NFSLOGTAB);
1602		error = -1;
1603		goto out;
1604	}
1605
1606	/*
1607	 * Remove entries matching buffer and sharepoint since we're
1608	 * going to replace it with perhaps an entry with a new tag.
1609	 */
1610	if (logtab_rement(f, buffer, dir, NULL, -1)) {
1611		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1612		    "share complete, however could not remove matching "
1613		    "entries in %s\n"), NFSLOGTAB);
1614		error = -1;
1615		goto out;
1616	}
1617
1618	/*
1619	 * Deactivate all active entries matching this sharepoint
1620	 */
1621	if (logtab_deactivate(f, NULL, dir, NULL)) {
1622		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1623		    "share complete, however could not deactivate matching "
1624		    "entries in %s\n"), NFSLOGTAB);
1625		error = -1;
1626		goto out;
1627	}
1628
1629	lep.le_buffer = buffer;
1630	lep.le_path = dir;
1631	lep.le_tag = tag;
1632	lep.le_state = LES_ACTIVE;
1633
1634	/*
1635	 * Add new sharepoint / buffer location to nfslogtab
1636	 */
1637	if (logtab_putent(f, &lep) < 0) {
1638		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1639		    "share complete, however could not add %s to %s\n"),
1640		    dir, NFSLOGTAB);
1641		error = -1;
1642	}
1643
1644out:
1645	if (f != NULL)
1646		(void) fclose(f);
1647	return (error);
1648}
1649
1650/*
1651 * Deactivate an entry from the nfslogtab file
1652 */
1653static int
1654nfslogtab_deactivate(char *path)
1655{
1656	FILE *f;
1657	int error = 0;
1658
1659	f = fopen(NFSLOGTAB, "r+");
1660	if (f == NULL) {
1661		error = errno;
1662		goto out;
1663	}
1664	if (lockf(fileno(f), F_LOCK, 0L) < 0) {
1665		error = errno;
1666		(void)  fprintf(stderr, dgettext(TEXT_DOMAIN,
1667		    "share complete, however could not lock %s for "
1668		    "update: %s\n"), NFSLOGTAB, strerror(error));
1669		goto out;
1670	}
1671	if (logtab_deactivate(f, NULL, path, NULL) == -1) {
1672		error = -1;
1673		(void) fprintf(stderr,
1674		    dgettext(TEXT_DOMAIN,
1675		    "share complete, however could not "
1676		    "deactivate %s in %s\n"), path, NFSLOGTAB);
1677		goto out;
1678	}
1679
1680out:	if (f != NULL)
1681		(void) fclose(f);
1682
1683	return (error);
1684}
1685
1686/*
1687 * check_public(group, skipshare)
1688 *
1689 * Check the group for any shares that have the public property
1690 * enabled. We skip "skipshare" since that is the one we are
1691 * working with. This is a separate function to make handling
1692 * subgroups simpler. Returns true if there is a share with public.
1693 */
1694static int
1695check_public(sa_group_t group, sa_share_t skipshare)
1696{
1697	int exists = B_FALSE;
1698	sa_share_t share;
1699	sa_optionset_t opt;
1700	sa_property_t prop;
1701	char *shared;
1702
1703	for (share = sa_get_share(group, NULL); share != NULL;
1704	    share = sa_get_next_share(share)) {
1705		if (share == skipshare)
1706			continue;
1707
1708		opt = sa_get_optionset(share, "nfs");
1709		if (opt == NULL)
1710			continue;
1711		prop = sa_get_property(opt, "public");
1712		if (prop == NULL)
1713			continue;
1714		shared = sa_get_share_attr(share, "shared");
1715		if (shared != NULL) {
1716			exists = strcmp(shared, "true") == 0;
1717			sa_free_attr_string(shared);
1718			if (exists == B_TRUE)
1719				break;
1720		}
1721	}
1722
1723	return (exists);
1724}
1725
1726/*
1727 * public_exists(handle, skipshare)
1728 *
1729 * check to see if public option is set on any other share than the
1730 * one specified. Need to check zfs sub-groups as well as the top
1731 * level groups.
1732 */
1733static int
1734public_exists(sa_handle_t handle, sa_share_t skipshare)
1735{
1736	sa_group_t group = NULL;
1737
1738	/*
1739	 * If we don't have a handle, we can only do syntax check. We
1740	 * can't check against other shares so we assume OK and will
1741	 * catch the problem only when we actually try to apply it.
1742	 */
1743	if (handle == NULL)
1744		return (SA_OK);
1745
1746	if (skipshare != NULL) {
1747		group = sa_get_parent_group(skipshare);
1748		if (group == NULL)
1749			return (SA_NO_SUCH_GROUP);
1750	}
1751
1752	for (group = sa_get_group(handle, NULL); group != NULL;
1753	    group = sa_get_next_group(group)) {
1754		/* Walk any ZFS subgroups as well as all standard groups */
1755		if (sa_group_is_zfs(group)) {
1756			sa_group_t subgroup;
1757			for (subgroup = sa_get_sub_group(group);
1758			    subgroup != NULL;
1759			    subgroup = sa_get_next_group(subgroup)) {
1760				if (check_public(subgroup, skipshare))
1761					return (B_TRUE);
1762			}
1763		} else {
1764			if (check_public(group, skipshare))
1765				return (B_TRUE);
1766		}
1767	}
1768	return (B_FALSE);
1769}
1770
1771/*
1772 * sa_enable_share at the protocol level, enable_share must tell the
1773 * implementation that it is to enable the share. This entails
1774 * converting the path and options into the appropriate ioctl
1775 * calls. It is assumed that all error checking of paths, etc. were
1776 * done earlier.
1777 */
1778static int
1779nfs_enable_share(sa_share_t share)
1780{
1781	struct exportdata export;
1782	sa_optionset_t secoptlist;
1783	struct secinfo *sp;
1784	int num_secinfo;
1785	sa_optionset_t opt;
1786	sa_security_t sec;
1787	sa_property_t prop;
1788	char *path;
1789	int err = SA_OK;
1790	int i;
1791	int iszfs;
1792	sa_handle_t handle;
1793
1794	/* Don't drop core if the NFS module isn't loaded. */
1795	(void) signal(SIGSYS, SIG_IGN);
1796
1797	/* get the path since it is important in several places */
1798	path = sa_get_share_attr(share, "path");
1799	if (path == NULL)
1800		return (SA_NO_SUCH_PATH);
1801
1802	iszfs = sa_path_is_zfs(path);
1803	/*
1804	 * find the optionsets and security sets.  There may not be
1805	 * any or there could be one or two for each of optionset and
1806	 * security may have multiple, one per security type per
1807	 * protocol type.
1808	 */
1809	opt = sa_get_derived_optionset(share, "nfs", 1);
1810	secoptlist = (sa_optionset_t)sa_get_all_security_types(share, "nfs", 1);
1811	if (secoptlist != NULL)
1812		num_secinfo = MAX(1, count_security(secoptlist));
1813	else
1814		num_secinfo = 1;
1815
1816	/*
1817	 * walk through the options and fill in the structure
1818	 * appropriately.
1819	 */
1820
1821	(void) memset(&export, '\0', sizeof (export));
1822
1823	/*
1824	 * do non-security options first since there is only one after
1825	 * the derived group is constructed.
1826	 */
1827	export.ex_version = EX_CURRENT_VERSION;
1828	export.ex_anon = UID_NOBODY; /* this is our default value */
1829	export.ex_index = NULL;
1830	export.ex_path = path;
1831	export.ex_pathlen = strlen(path) + 1;
1832
1833	if (opt != NULL)
1834		err = fill_export_from_optionset(&export, opt);
1835
1836	/*
1837	 * check to see if "public" is set. If it is, then make sure
1838	 * no other share has it set. If it is already used, fail.
1839	 */
1840
1841	handle = sa_find_group_handle((sa_group_t)share);
1842	if (export.ex_flags & EX_PUBLIC && public_exists(handle, share)) {
1843		(void) printf(dgettext(TEXT_DOMAIN,
1844		    "NFS: Cannot share more than one file "
1845		    "system with 'public' property\n"));
1846		err = SA_NOT_ALLOWED;
1847		goto out;
1848	}
1849
1850	sp = calloc(num_secinfo, sizeof (struct secinfo));
1851	if (sp == NULL) {
1852		err = SA_NO_MEMORY;
1853		(void) printf(dgettext(TEXT_DOMAIN,
1854		    "NFS: NFS: no memory for security\n"));
1855		goto out;
1856	}
1857	export.ex_secinfo = sp;
1858	/* get default secinfo */
1859	export.ex_seccnt = num_secinfo;
1860	/*
1861	 * since we must have one security option defined, we
1862	 * init to the default and then override as we find
1863	 * defined security options. This handles the case
1864	 * where we have no defined options but we need to set
1865	 * up one.
1866	 */
1867	sp[0].s_window = DEF_WIN;
1868	sp[0].s_rootnames = NULL;
1869	/* setup a default in case no properties defined */
1870	if (nfs_getseconfig_default(&sp[0].s_secinfo)) {
1871		(void) printf(dgettext(TEXT_DOMAIN,
1872		    "NFS: nfs_getseconfig_default: failed to "
1873		    "get default security mode\n"));
1874		err = SA_CONFIG_ERR;
1875	}
1876	if (secoptlist != NULL) {
1877		for (i = 0, prop = sa_get_property(secoptlist, NULL);
1878		    prop != NULL && i < num_secinfo;
1879		    prop = sa_get_next_property(prop), i++) {
1880			char *sectype;
1881			sectype = sa_get_property_attr(prop, "type");
1882			/*
1883			 * if sectype is NULL, we probably
1884			 * have a memory problem and can't get
1885			 * the correct values. Rather than
1886			 * exporting with incorrect security,
1887			 * don't share it.
1888			 */
1889			if (sectype == NULL) {
1890				err = SA_NO_MEMORY;
1891				(void) printf(dgettext(TEXT_DOMAIN,
1892				    "NFS: Cannot share %s: "
1893				    "no memory\n"), path);
1894				goto out;
1895			}
1896			sec = (sa_security_t)sa_get_derived_security(
1897			    share, sectype, "nfs", 1);
1898			sp[i].s_window = DEF_WIN;
1899			sp[i].s_rootcnt = 0;
1900			sp[i].s_rootnames = NULL;
1901			(void) fill_security_from_secopts(&sp[i], sec);
1902			if (sec != NULL)
1903				sa_free_derived_security(sec);
1904			if (sectype != NULL)
1905				sa_free_attr_string(sectype);
1906		}
1907	}
1908
1909	/* now add the share to the internal tables */
1910	printarg(path, &export);
1911	/*
1912	 * call the exportfs system call which is implemented
1913	 * via the nfssys() call as the EXPORTFS subfunction.
1914	 */
1915	if (iszfs) {
1916		struct exportfs_args ea;
1917		share_t sh;
1918
1919		ea.dname = path;
1920		ea.uex = &export;
1921
1922		(void) sa_sharetab_fill_zfs(share, &sh, "nfs");
1923		err = sa_share_zfs(share, NULL, path, &sh, &ea, ZFS_SHARE_NFS);
1924		if (err != SA_OK) {
1925			errno = err;
1926			err = -1;
1927		}
1928		sa_emptyshare(&sh);
1929	} else {
1930		err = exportfs(path, &export);
1931	}
1932
1933	if (err < 0) {
1934		err = SA_SYSTEM_ERR;
1935		switch (errno) {
1936		case EPERM:
1937			err = SA_NO_PERMISSION;
1938			break;
1939		case EEXIST:
1940			err = SA_SHARE_EXISTS;
1941			break;
1942		default:
1943			break;
1944		}
1945	} else {
1946		/* update sharetab with an add/modify */
1947		if (!iszfs) {
1948			(void) sa_update_sharetab(share, "nfs");
1949		}
1950	}
1951
1952	if (err == SA_OK) {
1953		/*
1954		 * enable services as needed. This should probably be
1955		 * done elsewhere in order to minimize the calls to
1956		 * check services.
1957		 */
1958		/*
1959		 * check to see if logging and other services need to
1960		 * be triggered, but only if there wasn't an
1961		 * error. This is probably where sharetab should be
1962		 * updated with the NFS specific entry.
1963		 */
1964		if (export.ex_flags & EX_LOG) {
1965			/* enable logging */
1966			if (nfslogtab_add(path, export.ex_log_buffer,
1967			    export.ex_tag) != 0) {
1968				(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1969				    "Could not enable logging for %s\n"),
1970				    path);
1971			}
1972			_check_services(service_list_logging);
1973		} else {
1974			/*
1975			 * don't have logging so remove it from file. It might
1976			 * not be thre, but that doesn't matter.
1977			 */
1978			(void) nfslogtab_deactivate(path);
1979			_check_services(service_list_default);
1980		}
1981	}
1982
1983out:
1984	if (path != NULL)
1985		free(path);
1986
1987	cleanup_export(&export);
1988	if (opt != NULL)
1989		sa_free_derived_optionset(opt);
1990	if (secoptlist != NULL)
1991		(void) sa_destroy_optionset(secoptlist);
1992	return (err);
1993}
1994
1995/*
1996 * nfs_disable_share(share, path)
1997 *
1998 * Unshare the specified share. Note that "path" is the same path as
1999 * what is in the "share" object. It is passed in to avoid an
2000 * additional lookup. A missing "path" value makes this a no-op
2001 * function.
2002 */
2003static int
2004nfs_disable_share(sa_share_t share, char *path)
2005{
2006	int err;
2007	int ret = SA_OK;
2008	int iszfs;
2009	sa_group_t parent;
2010	sa_handle_t handle;
2011
2012	if (path == NULL)
2013		return (ret);
2014
2015	/*
2016	 * If the share is in a ZFS group we need to handle it
2017	 * differently.  Just being on a ZFS file system isn't
2018	 * enough since we may be in a legacy share case.
2019	 */
2020	parent = sa_get_parent_group(share);
2021	iszfs = sa_group_is_zfs(parent);
2022	if (iszfs) {
2023		struct exportfs_args ea;
2024		share_t sh = { 0 };
2025		ea.dname = path;
2026		ea.uex = NULL;
2027		sh.sh_path = path;
2028		sh.sh_fstype = "nfs";
2029
2030		err = sa_share_zfs(share, NULL, path, &sh,
2031		    &ea, ZFS_UNSHARE_NFS);
2032		if (err != SA_OK) {
2033			errno = err;
2034			err = -1;
2035		}
2036	} else {
2037		err = exportfs(path, NULL);
2038	}
2039	if (err < 0) {
2040		/*
2041		 * TBD: only an error in some
2042		 * cases - need better analysis
2043		 */
2044		switch (errno) {
2045		case EPERM:
2046		case EACCES:
2047			ret = SA_NO_PERMISSION;
2048			break;
2049		case EINVAL:
2050		case ENOENT:
2051			ret = SA_NO_SUCH_PATH;
2052			break;
2053		default:
2054			ret = SA_SYSTEM_ERR;
2055			break;
2056		}
2057	}
2058	if (ret == SA_OK || ret == SA_NO_SUCH_PATH) {
2059		handle = sa_find_group_handle((sa_group_t)share);
2060		if (!iszfs)
2061			(void) sa_delete_sharetab(handle, path, "nfs");
2062		/* just in case it was logged */
2063		(void) nfslogtab_deactivate(path);
2064	}
2065	return (ret);
2066}
2067
2068static int
2069check_user(char *value)
2070{
2071	int ret = SA_OK;
2072
2073	if (!is_a_number(value)) {
2074		struct passwd *pw;
2075		/*
2076		 * in this case it would have to be a
2077		 * user name
2078		 */
2079		pw = getpwnam(value);
2080		if (pw == NULL)
2081			ret = SA_BAD_VALUE;
2082		endpwent();
2083	} else {
2084		uint64_t intval;
2085		intval = strtoull(value, NULL, 0);
2086		if (intval > UID_MAX && intval != -1)
2087			ret = SA_BAD_VALUE;
2088	}
2089
2090	return (ret);
2091}
2092
2093static int
2094check_group(char *value)
2095{
2096	int ret = SA_OK;
2097
2098	if (!is_a_number(value)) {
2099		struct group *gr;
2100		/*
2101		 * in this case it would have to be a
2102		 * group name
2103		 */
2104		gr = getgrnam(value);
2105		if (gr == NULL)
2106			ret = SA_BAD_VALUE;
2107		endgrent();
2108	} else {
2109		uint64_t intval;
2110		intval = strtoull(value, NULL, 0);
2111		if (intval > UID_MAX && intval != -1)
2112			ret = SA_BAD_VALUE;
2113	}
2114
2115	return (ret);
2116}
2117
2118/*
2119 * check_rorwnone(v1, v2, v3)
2120 *
2121 * check ro vs rw vs none values.  Over time this may get beefed up.
2122 * for now it just does simple checks. v1 is never NULL but v2 or v3
2123 * could be.
2124 */
2125
2126static int
2127check_rorwnone(char *v1, char *v2, char *v3)
2128{
2129	int ret = SA_OK;
2130	if (v2 != NULL && strcmp(v1, v2) == 0)
2131		ret = SA_VALUE_CONFLICT;
2132	else if (v3 != NULL && strcmp(v1, v3) == 0)
2133		ret = SA_VALUE_CONFLICT;
2134
2135	return (ret);
2136}
2137
2138/*
2139 * nfs_validate_property(handle, property, parent)
2140 *
2141 * Check that the property has a legitimate value for its type.
2142 */
2143
2144static int
2145nfs_validate_property(sa_handle_t handle, sa_property_t property,
2146    sa_optionset_t parent)
2147{
2148	int ret = SA_OK;
2149	char *propname;
2150	char *other1;
2151	char *other2;
2152	int optindex;
2153	nfsl_config_t *configlist;
2154	sa_group_t parent_group;
2155	char *value;
2156
2157	propname = sa_get_property_attr(property, "type");
2158
2159	if ((optindex = findopt(propname)) < 0)
2160		ret = SA_NO_SUCH_PROP;
2161
2162	/* need to validate value range here as well */
2163
2164	if (ret == SA_OK) {
2165		parent_group = sa_get_parent_group((sa_share_t)parent);
2166		if (optdefs[optindex].share && parent_group != NULL &&
2167		    !sa_is_share(parent_group))
2168			ret = SA_PROP_SHARE_ONLY;
2169	}
2170	if (ret == SA_OK) {
2171		if (optdefs[optindex].index == OPT_PUBLIC) {
2172			/*
2173			 * Public is special in that only one instance can
2174			 * be in the repository at the same time.
2175			 */
2176			if (public_exists(handle, parent_group)) {
2177				sa_free_attr_string(propname);
2178				return (SA_VALUE_CONFLICT);
2179			}
2180		}
2181		value = sa_get_property_attr(property, "value");
2182		if (value != NULL) {
2183			/* first basic type checking */
2184			switch (optdefs[optindex].type) {
2185
2186			case OPT_TYPE_NUMBER:
2187				/* check that the value is all digits */
2188				if (!is_a_number(value))
2189					ret = SA_BAD_VALUE;
2190				break;
2191
2192			case OPT_TYPE_BOOLEAN:
2193				if (strlen(value) == 0 ||
2194				    strcasecmp(value, "true") == 0 ||
2195				    strcmp(value, "1") == 0 ||
2196				    strcasecmp(value, "false") == 0 ||
2197				    strcmp(value, "0") == 0) {
2198					ret = SA_OK;
2199				} else {
2200					ret = SA_BAD_VALUE;
2201				}
2202				break;
2203
2204			case OPT_TYPE_USER:
2205				ret = check_user(value);
2206				break;
2207
2208			case OPT_TYPE_FILE:
2209				if (strcmp(value, "..") == 0 ||
2210				    strchr(value, '/') != NULL) {
2211					ret = SA_BAD_VALUE;
2212				}
2213				break;
2214
2215			case OPT_TYPE_ACCLIST: {
2216				sa_property_t oprop1;
2217				sa_property_t oprop2;
2218				char *ovalue1 = NULL;
2219				char *ovalue2 = NULL;
2220
2221				if (parent == NULL)
2222					break;
2223				/*
2224				 * access list handling. Should eventually
2225				 * validate that all the values make sense.
2226				 * Also, ro and rw may have cross value
2227				 * conflicts.
2228				 */
2229				if (strcmp(propname, SHOPT_RO) == 0) {
2230					other1 = SHOPT_RW;
2231					other2 = SHOPT_NONE;
2232				} else if (strcmp(propname, SHOPT_RW) == 0) {
2233					other1 = SHOPT_RO;
2234					other2 = SHOPT_NONE;
2235				} else if (strcmp(propname, SHOPT_NONE) == 0) {
2236					other1 = SHOPT_RO;
2237					other2 = SHOPT_RW;
2238				} else {
2239					other1 = NULL;
2240					other2 = NULL;
2241				}
2242				if (other1 == NULL && other2 == NULL)
2243					break;
2244
2245				/* compare rw(ro) with ro(rw) */
2246
2247				oprop1 = sa_get_property(parent, other1);
2248				oprop2 = sa_get_property(parent, other2);
2249				if (oprop1 == NULL && oprop2 == NULL)
2250					break;
2251				/*
2252				 * Only potential confusion if other1
2253				 * or other2 exists. Check the values
2254				 * and run the check if there is a
2255				 * value other than the one we are
2256				 * explicitly looking at.
2257				 */
2258				ovalue1 = sa_get_property_attr(oprop1, "value");
2259				ovalue2 = sa_get_property_attr(oprop2, "value");
2260				if (ovalue1 != NULL || ovalue2 != NULL)
2261					ret = check_rorwnone(value, ovalue1,
2262					    ovalue2);
2263
2264				if (ovalue1 != NULL)
2265					sa_free_attr_string(ovalue1);
2266				if (ovalue2 != NULL)
2267					sa_free_attr_string(ovalue2);
2268				break;
2269			}
2270
2271			case OPT_TYPE_LOGTAG:
2272				if (nfsl_getconfig_list(&configlist) == 0) {
2273					int error;
2274					if (value == NULL ||
2275					    strlen(value) == 0) {
2276						if (value != NULL)
2277							sa_free_attr_string(
2278							    value);
2279						value = strdup("global");
2280					}
2281					if (value != NULL &&
2282					    nfsl_findconfig(configlist, value,
2283					    &error) == NULL) {
2284						ret = SA_BAD_VALUE;
2285					}
2286					/* Must always free when done */
2287					nfsl_freeconfig_list(&configlist);
2288				} else {
2289					ret = SA_CONFIG_ERR;
2290				}
2291				break;
2292
2293			case OPT_TYPE_STRING:
2294				/* whatever is here should be ok */
2295				break;
2296
2297			case OPT_TYPE_SECURITY:
2298				/*
2299				 * The "sec" property isn't used in the
2300				 * non-legacy parts of sharemgr. We need to
2301				 * reject it here. For legacy, it is pulled
2302				 * out well before we get here.
2303				 */
2304				ret = SA_NO_SUCH_PROP;
2305				break;
2306
2307			case OPT_TYPE_MAPPING: {
2308				char *p;
2309				char *n;
2310				char *c;
2311				int (*f)(char *);
2312
2313				sa_security_t security;
2314
2315				/*
2316				 * mapping is only supported for sec=sys
2317				 */
2318				ret = SA_CONFIG_ERR;
2319				if (parent_group == NULL)
2320					break;
2321
2322				for (security = sa_get_security(parent_group,
2323				    NULL, NULL); security != NULL;
2324				    security = sa_get_next_security(security)) {
2325					char *type;
2326					char *sectype;
2327
2328					type = sa_get_security_attr(security,
2329					    "type");
2330					if (type == NULL)
2331						continue;
2332
2333					if (strcmp(type, "nfs") != 0) {
2334						sa_free_attr_string(type);
2335						continue;
2336					}
2337					sa_free_attr_string(type);
2338
2339					sectype = sa_get_security_attr(security,
2340					    "sectype");
2341					if (sectype == NULL)
2342						continue;
2343
2344					if (strcmp(sectype, "sys") != 0) {
2345						sa_free_attr_string(sectype);
2346						ret = SA_CONFIG_ERR;
2347						break;
2348					}
2349					sa_free_attr_string(sectype);
2350					ret = SA_OK;
2351				}
2352
2353				if (ret != SA_OK)
2354					break;
2355
2356				assert(optindex == OPT_UIDMAP ||
2357				    optindex == OPT_GIDMAP);
2358				f = optindex == OPT_UIDMAP ? check_user :
2359				    check_group;
2360
2361
2362				p = strdup(value);
2363				if (p == NULL)
2364					ret = SA_BAD_VALUE;
2365
2366				for (c = p; ret == SA_OK && c != NULL; c = n) {
2367					char *s;
2368					char *t;
2369
2370					n = strchr(c, '~');
2371					if (n != NULL)
2372						*n++ = '\0';
2373
2374					s = strchr(c, ':');
2375					if (s != NULL) {
2376						*s++ = '\0';
2377						t = strchr(s, ':');
2378						if (t != NULL)
2379							*t = '\0';
2380					}
2381
2382					if (s == NULL || t == NULL)
2383						ret = SA_BAD_VALUE;
2384
2385					if (ret == SA_OK && *c != '\0' &&
2386					    strcmp(c, "*") != 0)
2387						ret = f(c);
2388
2389					if (ret == SA_OK && *s != '\0' &&
2390					    strcmp(s, "-1") != 0)
2391						ret = f(s);
2392				}
2393
2394				free(p);
2395
2396				break;
2397			}
2398
2399			default:
2400				break;
2401			}
2402
2403			if (value != NULL)
2404				sa_free_attr_string(value);
2405
2406			if (ret == SA_OK && optdefs[optindex].check != NULL) {
2407				/* do the property specific check */
2408				ret = optdefs[optindex].check(handle, property);
2409			}
2410		}
2411	}
2412
2413	if (propname != NULL)
2414		sa_free_attr_string(propname);
2415	return (ret);
2416}
2417
2418/*
2419 * Protocol management functions
2420 *
2421 * Properties defined in the default files are defined in
2422 * proto_option_defs for parsing and validation. If "other" and
2423 * "compare" are set, then the value for this property should be
2424 * compared against the property specified in "other" using the
2425 * "compare" check (either <= or >=) in order to ensure that the
2426 * values are in the correct range.  E.g. setting server_versmin
2427 * higher than server_versmax should not be allowed.
2428 */
2429
2430struct proto_option_defs {
2431	char *tag;
2432	char *name;	/* display name -- remove protocol identifier */
2433	int index;
2434	int type;
2435	union {
2436	    int intval;
2437	    char *string;
2438	} defvalue;
2439	uint32_t svcs;
2440	int32_t minval;
2441	int32_t maxval;
2442	char *other;
2443	int compare;
2444#define	OPT_CMP_GE	0
2445#define	OPT_CMP_LE	1
2446	int (*check)(char *);
2447} proto_options[] = {
2448#define	PROTO_OPT_NFSD_SERVERS			0
2449	{"nfsd_servers",
2450	    "servers", PROTO_OPT_NFSD_SERVERS, OPT_TYPE_NUMBER, 1024, SVC_NFSD,
2451	    1, INT32_MAX},
2452#define	PROTO_OPT_LOCKD_LISTEN_BACKLOG		1
2453	{"lockd_listen_backlog",
2454	    "lockd_listen_backlog", PROTO_OPT_LOCKD_LISTEN_BACKLOG,
2455	    OPT_TYPE_NUMBER, 32, SVC_LOCKD, 32, INT32_MAX},
2456#define	PROTO_OPT_LOCKD_SERVERS			2
2457	{"lockd_servers",
2458	    "lockd_servers", PROTO_OPT_LOCKD_SERVERS, OPT_TYPE_NUMBER, 256,
2459	    SVC_LOCKD, 1, INT32_MAX},
2460#define	PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT	3
2461	{"lockd_retransmit_timeout",
2462	    "lockd_retransmit_timeout", PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT,
2463	    OPT_TYPE_NUMBER, 5, SVC_LOCKD, 0, INT32_MAX},
2464#define	PROTO_OPT_GRACE_PERIOD			4
2465	{"grace_period",
2466	    "grace_period", PROTO_OPT_GRACE_PERIOD, OPT_TYPE_NUMBER, 90,
2467	    SVC_LOCKD, 0, INT32_MAX},
2468#define	PROTO_OPT_NFS_SERVER_VERSMIN		5
2469	{"nfs_server_versmin",
2470	    "server_versmin", PROTO_OPT_NFS_SERVER_VERSMIN, OPT_TYPE_NUMBER,
2471	    (int)NFS_VERSMIN_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN,
2472	    NFS_VERSMAX, "server_versmax", OPT_CMP_LE},
2473#define	PROTO_OPT_NFS_SERVER_VERSMAX		6
2474	{"nfs_server_versmax",
2475	    "server_versmax", PROTO_OPT_NFS_SERVER_VERSMAX, OPT_TYPE_NUMBER,
2476	    (int)NFS_VERSMAX_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN,
2477	    NFS_VERSMAX, "server_versmin", OPT_CMP_GE},
2478#define	PROTO_OPT_NFS_CLIENT_VERSMIN		7
2479	{"nfs_client_versmin",
2480	    "client_versmin", PROTO_OPT_NFS_CLIENT_VERSMIN, OPT_TYPE_NUMBER,
2481	    (int)NFS_VERSMIN_DEFAULT, SVC_CLIENT, NFS_VERSMIN, NFS_VERSMAX,
2482	    "client_versmax", OPT_CMP_LE},
2483#define	PROTO_OPT_NFS_CLIENT_VERSMAX		8
2484	{"nfs_client_versmax",
2485	    "client_versmax", PROTO_OPT_NFS_CLIENT_VERSMAX, OPT_TYPE_NUMBER,
2486	    (int)NFS_VERSMAX_DEFAULT, SVC_CLIENT, NFS_VERSMIN, NFS_VERSMAX,
2487	    "client_versmin", OPT_CMP_GE},
2488#define	PROTO_OPT_NFS_SERVER_DELEGATION		9
2489	{"nfs_server_delegation",
2490	    "server_delegation", PROTO_OPT_NFS_SERVER_DELEGATION,
2491	    OPT_TYPE_ONOFF, NFS_SERVER_DELEGATION_DEFAULT, SVC_NFSD, 0, 0},
2492#define	PROTO_OPT_NFSMAPID_DOMAIN		10
2493	{"nfsmapid_domain",
2494	    "nfsmapid_domain", PROTO_OPT_NFSMAPID_DOMAIN, OPT_TYPE_DOMAIN,
2495	    0, SVC_NFSMAPID, 0, 0},
2496#define	PROTO_OPT_NFSD_MAX_CONNECTIONS		11
2497	{"nfsd_max_connections",
2498	    "max_connections", PROTO_OPT_NFSD_MAX_CONNECTIONS,
2499	    OPT_TYPE_NUMBER, -1, SVC_NFSD, -1, INT32_MAX},
2500#define	PROTO_OPT_NFSD_PROTOCOL			12
2501	{"nfsd_protocol",
2502	    "protocol", PROTO_OPT_NFSD_PROTOCOL, OPT_TYPE_PROTOCOL, 0,
2503	    SVC_NFSD, 0, 0},
2504#define	PROTO_OPT_NFSD_LISTEN_BACKLOG		13
2505	{"nfsd_listen_backlog",
2506	    "listen_backlog", PROTO_OPT_NFSD_LISTEN_BACKLOG,
2507	    OPT_TYPE_NUMBER, 0, SVC_NFSD, 0, INT32_MAX},
2508#define	PROTO_OPT_NFSD_DEVICE			14
2509	{"nfsd_device",
2510	    "device", PROTO_OPT_NFSD_DEVICE,
2511	    OPT_TYPE_STRING, 0, SVC_NFSD, 0, 0},
2512#define	PROTO_OPT_MOUNTD_LISTEN_BACKLOG		15
2513	{"mountd_listen_backlog",
2514	    "mountd_listen_backlog", PROTO_OPT_MOUNTD_LISTEN_BACKLOG,
2515	    OPT_TYPE_NUMBER, 64, SVC_NFSD|SVC_MOUNTD, 1, INT32_MAX},
2516#define	PROTO_OPT_MOUNTD_MAX_THREADS		16
2517	{"mountd_max_threads",
2518	    "mountd_max_threads", PROTO_OPT_MOUNTD_MAX_THREADS,
2519	    OPT_TYPE_NUMBER, 16, SVC_NFSD|SVC_MOUNTD, 1, INT32_MAX},
2520#define	PROTO_OPT_MOUNTD_PORT			17
2521	{"mountd_port",
2522	    "mountd_port", PROTO_OPT_MOUNTD_PORT,
2523	    OPT_TYPE_NUMBER, 0, SVC_MOUNTD, 1, UINT16_MAX},
2524#define	PROTO_OPT_STATD_PORT			18
2525	{"statd_port",
2526	    "statd_port", PROTO_OPT_STATD_PORT,
2527	    OPT_TYPE_NUMBER, 0, SVC_STATD, 1, UINT16_MAX},
2528	{NULL}
2529};
2530
2531/*
2532 * the protoset holds the defined options so we don't have to read
2533 * them multiple times
2534 */
2535static sa_protocol_properties_t protoset;
2536
2537static int
2538findprotoopt(char *name, int whichname)
2539{
2540	int i;
2541	for (i = 0; proto_options[i].tag != NULL; i++) {
2542		if (whichname == 1) {
2543			if (strcasecmp(proto_options[i].name, name) == 0)
2544			return (i);
2545		} else {
2546			if (strcasecmp(proto_options[i].tag, name) == 0)
2547				return (i);
2548		}
2549	}
2550	return (-1);
2551}
2552
2553/*
2554 * fixcaselower(str)
2555 *
2556 * convert a string to lower case (inplace).
2557 */
2558
2559static void
2560fixcaselower(char *str)
2561{
2562	while (*str) {
2563		*str = tolower(*str);
2564		str++;
2565	}
2566}
2567
2568/*
2569 * skipwhitespace(str)
2570 *
2571 * Skip leading white space. It is assumed that it is called with a
2572 * valid pointer.
2573 */
2574
2575static char *
2576skipwhitespace(char *str)
2577{
2578	while (*str && isspace(*str))
2579		str++;
2580
2581	return (str);
2582}
2583
2584/*
2585 * extractprop()
2586 *
2587 * Extract the property and value out of the line and create the
2588 * property in the optionset.
2589 */
2590static int
2591extractprop(char *name, char *value)
2592{
2593	sa_property_t prop;
2594	int index;
2595	int ret = SA_OK;
2596	/*
2597	 * Remove any leading
2598	 * white space.
2599	 */
2600	name = skipwhitespace(name);
2601
2602	index = findprotoopt(name, 1);
2603	if (index >= 0) {
2604		fixcaselower(name);
2605		prop = sa_create_property(proto_options[index].name, value);
2606		if (prop != NULL)
2607			ret = sa_add_protocol_property(protoset, prop);
2608		else
2609			ret = SA_NO_MEMORY;
2610	}
2611	return (ret);
2612}
2613
2614scf_type_t
2615getscftype(int type)
2616{
2617	scf_type_t ret;
2618
2619	switch (type) {
2620	case OPT_TYPE_NUMBER:
2621		ret = SCF_TYPE_INTEGER;
2622	break;
2623	case OPT_TYPE_BOOLEAN:
2624		ret = SCF_TYPE_BOOLEAN;
2625	break;
2626	default:
2627		ret = SCF_TYPE_ASTRING;
2628	}
2629	return (ret);
2630}
2631
2632char *
2633getsvcname(uint32_t svcs)
2634{
2635	char *service;
2636	switch (svcs) {
2637		case SVC_LOCKD:
2638			service = LOCKD;
2639			break;
2640		case SVC_STATD:
2641			service = STATD;
2642			break;
2643		case SVC_NFSD:
2644			service = NFSD;
2645			break;
2646		case SVC_CLIENT:
2647			service = NFS_CLIENT_SVC;
2648			break;
2649		case SVC_NFS4CBD:
2650			service = NFS4CBD;
2651			break;
2652		case SVC_NFSMAPID:
2653			service = NFSMAPID;
2654			break;
2655		case SVC_RQUOTAD:
2656			service = RQUOTAD;
2657			break;
2658		case SVC_NFSLOGD:
2659			service = NFSLOGD;
2660			break;
2661		case SVC_REPARSED:
2662			service = REPARSED;
2663			break;
2664		default:
2665			service = NFSD;
2666	}
2667	return (service);
2668}
2669
2670/*
2671 * initprotofromsmf()
2672 *
2673 * Read NFS SMF properties and add the defined values to the
2674 * protoset.  Note that default values are known from the built in
2675 * table in case SMF doesn't have a definition. Not having
2676 * SMF properties is OK since we have builtin default
2677 * values.
2678 */
2679static int
2680initprotofromsmf()
2681{
2682	char name[PATH_MAX];
2683	char value[PATH_MAX];
2684	int ret = SA_OK, bufsz = 0, i;
2685
2686	protoset = sa_create_protocol_properties("nfs");
2687	if (protoset != NULL) {
2688		for (i = 0; proto_options[i].tag != NULL; i++) {
2689			scf_type_t ptype;
2690			char *svc_name;
2691
2692			bzero(value, PATH_MAX);
2693			(void) strncpy(name, proto_options[i].name, PATH_MAX);
2694			/* Replace NULL with the correct instance */
2695			ptype = getscftype(proto_options[i].type);
2696			svc_name = getsvcname(proto_options[i].svcs);
2697			bufsz = PATH_MAX;
2698			ret = nfs_smf_get_prop(name, value,
2699			    (char *)DEFAULT_INSTANCE, ptype,
2700			    svc_name, &bufsz);
2701			if (ret == SA_OK) {
2702				ret = extractprop(name, value);
2703			}
2704		}
2705	} else {
2706		ret = SA_NO_MEMORY;
2707	}
2708
2709	return (ret);
2710}
2711
2712/*
2713 * add_defaults()
2714 *
2715 * Add the default values for any property not defined
2716 * in NFS SMF repository.
2717 * Values are set according to their defined types.
2718 */
2719
2720static void
2721add_defaults()
2722{
2723	int i;
2724	char number[MAXDIGITS];
2725
2726	for (i = 0; proto_options[i].tag != NULL; i++) {
2727		sa_property_t prop;
2728		prop = sa_get_protocol_property(protoset,
2729		    proto_options[i].name);
2730		if (prop == NULL) {
2731			/* add the default value */
2732			switch (proto_options[i].type) {
2733			case OPT_TYPE_NUMBER:
2734				(void) snprintf(number, sizeof (number), "%d",
2735				    proto_options[i].defvalue.intval);
2736				prop = sa_create_property(proto_options[i].name,
2737				    number);
2738				break;
2739
2740			case OPT_TYPE_BOOLEAN:
2741				prop = sa_create_property(proto_options[i].name,
2742				    proto_options[i].defvalue.intval ?
2743				    "true" : "false");
2744				break;
2745
2746			case OPT_TYPE_ONOFF:
2747				prop = sa_create_property(proto_options[i].name,
2748				    proto_options[i].defvalue.intval ?
2749				    "on" : "off");
2750				break;
2751
2752			default:
2753				/* treat as strings of zero length */
2754				prop = sa_create_property(proto_options[i].name,
2755				    "");
2756				break;
2757			}
2758			if (prop != NULL)
2759				(void) sa_add_protocol_property(protoset, prop);
2760		}
2761	}
2762}
2763
2764static void
2765free_protoprops()
2766{
2767	if (protoset != NULL) {
2768		xmlFreeNode(protoset);
2769		protoset = NULL;
2770	}
2771}
2772
2773/*
2774 * nfs_init()
2775 *
2776 * Initialize the NFS plugin.
2777 */
2778
2779static int
2780nfs_init()
2781{
2782	int ret = SA_OK;
2783
2784	if (sa_plugin_ops.sa_init != nfs_init) {
2785		(void) printf(dgettext(TEXT_DOMAIN,
2786		    "NFS plugin not properly initialized\n"));
2787		return (SA_CONFIG_ERR);
2788	}
2789
2790	ret = initprotofromsmf();
2791	if (ret != SA_OK) {
2792		(void) printf(dgettext(TEXT_DOMAIN,
2793		    "NFS plugin problem with SMF repository: %s\n"),
2794		    sa_errorstr(ret));
2795		ret = SA_OK;
2796	}
2797	add_defaults();
2798
2799	return (ret);
2800}
2801
2802/*
2803 * nfs_fini()
2804 *
2805 * uninitialize the NFS plugin. Want to avoid memory leaks.
2806 */
2807
2808static void
2809nfs_fini()
2810{
2811	free_protoprops();
2812}
2813
2814/*
2815 * nfs_get_proto_set()
2816 *
2817 * Return an optionset with all the protocol specific properties in
2818 * it.
2819 */
2820
2821static sa_protocol_properties_t
2822nfs_get_proto_set()
2823{
2824	return (protoset);
2825}
2826
2827/*
2828 * service_in_state(service, chkstate)
2829 *
2830 * Want to know if the specified service is in the desired state
2831 * (chkstate) or not. Return true (1) if it is and false (0) if it
2832 * isn't.
2833 */
2834static int
2835service_in_state(char *service, const char *chkstate)
2836{
2837	char *state;
2838	int ret = B_FALSE;
2839
2840	state = smf_get_state(service);
2841	if (state != NULL) {
2842		/* got the state so get the equality for the return value */
2843		ret = strcmp(state, chkstate) == 0 ? B_TRUE : B_FALSE;
2844		free(state);
2845	}
2846	return (ret);
2847}
2848
2849/*
2850 * restart_service(svcs)
2851 *
2852 * Walk through the bit mask of services that need to be restarted in
2853 * order to use the new property values. Some properties affect
2854 * multiple daemons. Should only restart a service if it is currently
2855 * enabled (online).
2856 */
2857
2858static void
2859restart_service(uint32_t svcs)
2860{
2861	uint32_t mask;
2862	int ret;
2863	char *service;
2864
2865	for (mask = 1; svcs != 0; mask <<= 1) {
2866		switch (svcs & mask) {
2867		case SVC_LOCKD:
2868			service = LOCKD;
2869			break;
2870		case SVC_STATD:
2871			service = STATD;
2872			break;
2873		case SVC_NFSD:
2874			service = NFSD;
2875			break;
2876		case SVC_MOUNTD:
2877			service = MOUNTD;
2878			break;
2879		case SVC_NFS4CBD:
2880			service = NFS4CBD;
2881			break;
2882		case SVC_NFSMAPID:
2883			service = NFSMAPID;
2884			break;
2885		case SVC_RQUOTAD:
2886			service = RQUOTAD;
2887			break;
2888		case SVC_NFSLOGD:
2889			service = NFSLOGD;
2890			break;
2891		case SVC_REPARSED:
2892			service = REPARSED;
2893			break;
2894		case SVC_CLIENT:
2895			service = NFS_CLIENT_SVC;
2896			break;
2897		default:
2898			continue;
2899		}
2900
2901		/*
2902		 * Only attempt to restart the service if it is
2903		 * currently running. In the future, it may be
2904		 * desirable to use smf_refresh_instance if the NFS
2905		 * services ever implement the refresh method.
2906		 */
2907		if (service_in_state(service, SCF_STATE_STRING_ONLINE)) {
2908			ret = smf_restart_instance(service);
2909			/*
2910			 * There are only a few SMF errors at this point, but
2911			 * it is also possible that a bad value may have put
2912			 * the service into maintenance if there wasn't an
2913			 * SMF level error.
2914			 */
2915			if (ret != 0) {
2916				(void) fprintf(stderr,
2917				    dgettext(TEXT_DOMAIN,
2918				    "%s failed to restart: %s\n"),
2919				    service, scf_strerror(scf_error()));
2920			} else {
2921				/*
2922				 * Check whether it has gone to "maintenance"
2923				 * mode or not. Maintenance implies something
2924				 * went wrong.
2925				 */
2926				if (service_in_state(service,
2927				    SCF_STATE_STRING_MAINT)) {
2928					(void) fprintf(stderr,
2929					    dgettext(TEXT_DOMAIN,
2930					    "%s failed to restart\n"),
2931					    service);
2932				}
2933			}
2934		}
2935		svcs &= ~mask;
2936	}
2937}
2938
2939/*
2940 * nfs_minmax_check(name, value)
2941 *
2942 * Verify that the value for the property specified by index is valid
2943 * relative to the opposite value in the case of a min/max variable.
2944 * Currently, server_minvers/server_maxvers and
2945 * client_minvers/client_maxvers are the only ones to check.
2946 */
2947
2948static int
2949nfs_minmax_check(int index, int value)
2950{
2951	int val;
2952	char *pval;
2953	sa_property_t prop;
2954	sa_optionset_t opts;
2955	int ret = B_TRUE;
2956
2957	if (proto_options[index].other != NULL) {
2958		/* have a property to compare against */
2959		opts = nfs_get_proto_set();
2960		prop = sa_get_property(opts, proto_options[index].other);
2961		/*
2962		 * If we don't find the property, assume default
2963		 * values which will work since the max will be at the
2964		 * max and the min at the min.
2965		 */
2966		if (prop != NULL) {
2967			pval = sa_get_property_attr(prop, "value");
2968			if (pval != NULL) {
2969				val = strtoul(pval, NULL, 0);
2970				if (proto_options[index].compare ==
2971				    OPT_CMP_LE) {
2972					ret = value <= val ? B_TRUE : B_FALSE;
2973				} else if (proto_options[index].compare ==
2974				    OPT_CMP_GE) {
2975					ret = value >= val ? B_TRUE : B_FALSE;
2976				}
2977				sa_free_attr_string(pval);
2978			}
2979		}
2980	}
2981	return (ret);
2982}
2983
2984/*
2985 * nfs_validate_proto_prop(index, name, value)
2986 *
2987 * Verify that the property specified by name can take the new
2988 * value. This is a sanity check to prevent bad values getting into
2989 * the default files. All values need to be checked against what is
2990 * allowed by their defined type. If a type isn't explicitly defined
2991 * here, it is treated as a string.
2992 *
2993 * Note that OPT_TYPE_NUMBER will additionally check that the value is
2994 * within the range specified and potentially against another property
2995 * value as well as specified in the proto_options members other and
2996 * compare.
2997 */
2998
2999static int
3000nfs_validate_proto_prop(int index, char *name, char *value)
3001{
3002	int ret = SA_OK;
3003	char *cp;
3004#ifdef lint
3005	name = name;
3006#endif
3007	switch (proto_options[index].type) {
3008	case OPT_TYPE_NUMBER:
3009		if (!is_a_number(value))
3010			ret = SA_BAD_VALUE;
3011		else {
3012			int val;
3013			val = strtoul(value, NULL, 0);
3014			if (val < proto_options[index].minval ||
3015			    val > proto_options[index].maxval)
3016				ret = SA_BAD_VALUE;
3017			/*
3018			 * For server_versmin/server_versmax and
3019			 * client_versmin/client_versmax, the value of the
3020			 * min(max) should be checked to be correct relative
3021			 * to the current max(min).
3022			 */
3023			if (!nfs_minmax_check(index, val)) {
3024				ret = SA_BAD_VALUE;
3025			}
3026		}
3027		break;
3028
3029	case OPT_TYPE_DOMAIN:
3030		/*
3031		 * needs to be a qualified domain so will have at
3032		 * least one period and other characters on either
3033		 * side of it.  A zero length string is also allowed
3034		 * and is the way to turn off the override.
3035		 */
3036		if (strlen(value) == 0)
3037			break;
3038		cp = strchr(value, '.');
3039		if (cp == NULL || cp == value || strchr(value, '@') != NULL)
3040			ret = SA_BAD_VALUE;
3041		break;
3042
3043	case OPT_TYPE_BOOLEAN:
3044		if (strlen(value) == 0 ||
3045		    strcasecmp(value, "true") == 0 ||
3046		    strcmp(value, "1") == 0 ||
3047		    strcasecmp(value, "false") == 0 ||
3048		    strcmp(value, "0") == 0) {
3049			ret = SA_OK;
3050		} else {
3051			ret = SA_BAD_VALUE;
3052		}
3053		break;
3054
3055	case OPT_TYPE_ONOFF:
3056		if (strcasecmp(value, "on") != 0 &&
3057		    strcasecmp(value, "off") != 0) {
3058			ret = SA_BAD_VALUE;
3059		}
3060		break;
3061
3062	case OPT_TYPE_PROTOCOL: {
3063		struct netconfig *nconf;
3064		void *nc;
3065		boolean_t pfound = B_FALSE;
3066
3067		if (strcasecmp(value, "all") == 0)
3068			break;
3069
3070		if ((nc = setnetconfig()) == NULL) {
3071			(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
3072			    "setnetconfig failed: %s\n"), strerror(errno));
3073		} else {
3074			while ((nconf = getnetconfig(nc)) != NULL) {
3075				if (strcmp(nconf->nc_proto, value) == 0) {
3076					pfound = B_TRUE;
3077					break;
3078				}
3079			}
3080			(void) endnetconfig(nc);
3081		}
3082
3083		if (!pfound)
3084			ret = SA_BAD_VALUE;
3085		break;
3086	}
3087
3088	default:
3089		/* treat as a string */
3090		break;
3091	}
3092	return (ret);
3093}
3094
3095/*
3096 * nfs_set_proto_prop(prop)
3097 *
3098 * check that prop is valid.
3099 */
3100
3101static int
3102nfs_set_proto_prop(sa_property_t prop)
3103{
3104	int ret = SA_OK;
3105	char *name;
3106	char *value;
3107
3108	name = sa_get_property_attr(prop, "type");
3109	value = sa_get_property_attr(prop, "value");
3110	if (name != NULL && value != NULL) {
3111		scf_type_t sctype;
3112		char *svc_name;
3113		char *instance = NULL;
3114		int index = findprotoopt(name, 1);
3115
3116		ret = nfs_validate_proto_prop(index, name, value);
3117		if (ret == SA_OK) {
3118			sctype = getscftype(proto_options[index].type);
3119			svc_name = getsvcname(proto_options[index].svcs);
3120			if (sctype == SCF_TYPE_BOOLEAN) {
3121				if (value != NULL)
3122					sa_free_attr_string(value);
3123				if (string_to_boolean(value) == 0)
3124					value = strdup("0");
3125				else
3126					value = strdup("1");
3127			}
3128			ret = nfs_smf_set_prop(name, value, instance, sctype,
3129			    svc_name);
3130			if (ret == SA_OK) {
3131				restart_service(proto_options[index].svcs);
3132			} else {
3133				(void) printf(dgettext(TEXT_DOMAIN,
3134				    "Cannot restart NFS services : %s\n"),
3135				    sa_errorstr(ret));
3136			}
3137		}
3138	}
3139	if (name != NULL)
3140		sa_free_attr_string(name);
3141	if (value != NULL)
3142		sa_free_attr_string(value);
3143	return (ret);
3144}
3145
3146/*
3147 * nfs_get_status()
3148 *
3149 * What is the current status of the nfsd? We use the SMF state here.
3150 * Caller must free the returned value.
3151 */
3152
3153static char *
3154nfs_get_status()
3155{
3156	return (smf_get_state(NFSD));
3157}
3158
3159/*
3160 * nfs_space_alias(alias)
3161 *
3162 * Lookup the space (security) name. If it is default, convert to the
3163 * real name.
3164 */
3165
3166static char *
3167nfs_space_alias(char *space)
3168{
3169	char *name = space;
3170	seconfig_t secconf;
3171
3172	/*
3173	 * Only the space named "default" is special. If it is used,
3174	 * the default needs to be looked up and the real name used.
3175	 * This is normally "sys" but could be changed.  We always
3176	 * change default to the real name.
3177	 */
3178	if (strcmp(space, "default") == 0 &&
3179	    nfs_getseconfig_default(&secconf) == 0) {
3180		if (nfs_getseconfig_bynumber(secconf.sc_nfsnum, &secconf) == 0)
3181			name = secconf.sc_name;
3182	}
3183	return (strdup(name));
3184}
3185
3186/*
3187 * nfs_features()
3188 *
3189 * Return a mask of the features required.
3190 */
3191
3192static uint64_t
3193nfs_features()
3194{
3195	return ((uint64_t)SA_FEATURE_DFSTAB | SA_FEATURE_SERVER);
3196}
3197