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