xref: /illumos-gate/usr/src/lib/libshare/smb/libshare_smb.c (revision 051aabe6136ff13e81542a427e9693ffe1503525)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * SMB specific functions
31  */
32 #include <stdio.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <zone.h>
38 #include <errno.h>
39 #include <locale.h>
40 #include <fcntl.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <syslog.h>
44 #include "libshare.h"
45 #include "libshare_impl.h"
46 #include <pwd.h>
47 #include <limits.h>
48 #include <libscf.h>
49 #include <strings.h>
50 #include "libshare_smb.h"
51 #include <rpcsvc/daemon_utils.h>
52 #include <smbsrv/smb_share.h>
53 #include <smbsrv/smbinfo.h>
54 #include <smbsrv/libsmb.h>
55 
56 /* internal functions */
57 static int smb_share_init(void);
58 static void smb_share_fini(void);
59 static int smb_enable_share(sa_share_t);
60 static int smb_share_changed(sa_share_t);
61 static int smb_resource_changed(sa_resource_t);
62 static int smb_rename_resource(sa_handle_t, sa_resource_t, char *);
63 static int smb_disable_share(sa_share_t share, char *);
64 static int smb_validate_property(sa_handle_t, sa_property_t, sa_optionset_t);
65 static int smb_set_proto_prop(sa_property_t);
66 static sa_protocol_properties_t smb_get_proto_set(void);
67 static char *smb_get_status(void);
68 static int smb_parse_optstring(sa_group_t, char *);
69 static char *smb_format_options(sa_group_t, int);
70 
71 static int smb_enable_service(void);
72 
73 static int range_check_validator(int, char *);
74 static int range_check_validator_zero_ok(int, char *);
75 static int string_length_check_validator(int, char *);
76 static int true_false_validator(int, char *);
77 static int ip_address_validator_empty_ok(int, char *);
78 static int ip_address_csv_list_validator_empty_ok(int, char *);
79 static int path_validator(int, char *);
80 
81 static int smb_enable_resource(sa_resource_t);
82 static int smb_disable_resource(sa_resource_t);
83 static uint64_t smb_share_features(void);
84 static int smb_list_transient(sa_handle_t);
85 
86 /* size of basic format allocation */
87 #define	OPT_CHUNK	1024
88 
89 /* size of string for types - big enough to hold "dependency" */
90 #define	SCFTYPE_LEN	32
91 
92 /*
93  * Indexes of entries in smb_proto_options table.
94  * Changes to smb_proto_options table may require
95  * an update to these values.
96  */
97 #define	PROTO_OPT_WINS1			6
98 #define	PROTO_OPT_WINS_EXCLUDE		8
99 
100 
101 /*
102  * ops vector that provides the protocol specific info and operations
103  * for share management.
104  */
105 
106 struct sa_plugin_ops sa_plugin_ops = {
107 	SA_PLUGIN_VERSION,
108 	SMB_PROTOCOL_NAME,
109 	smb_share_init,
110 	smb_share_fini,
111 	smb_enable_share,
112 	smb_disable_share,
113 	smb_validate_property,
114 	NULL,	/* valid_space */
115 	NULL,	/* security_prop */
116 	smb_parse_optstring,
117 	smb_format_options,
118 	smb_set_proto_prop,
119 	smb_get_proto_set,
120 	smb_get_status,
121 	NULL,	/* space_alias */
122 	NULL,	/* update_legacy */
123 	NULL,	/* delete_legacy */
124 	smb_share_changed,
125 	smb_enable_resource,
126 	smb_disable_resource,
127 	smb_share_features,
128 	smb_list_transient,
129 	smb_resource_changed,
130 	smb_rename_resource,
131 	NULL,	/* run_command */
132 	NULL,	/* command_help */
133 	NULL	/* delete_proto_section */
134 };
135 
136 /*
137  * option definitions.  Make sure to keep the #define for the option
138  * index just before the entry it is the index for. Changing the order
139  * can cause breakage.
140  */
141 
142 struct option_defs optdefs[] = {
143 	{SMB_SHROPT_AD_CONTAINER, OPT_TYPE_STRING},
144 	{SMB_SHROPT_NAME, OPT_TYPE_NAME},
145 	{NULL, NULL},
146 };
147 
148 /*
149  * findopt(name)
150  *
151  * Lookup option "name" in the option table and return the table
152  * index.
153  */
154 
155 static int
156 findopt(char *name)
157 {
158 	int i;
159 	if (name != NULL) {
160 		for (i = 0; optdefs[i].tag != NULL; i++) {
161 			if (strcmp(optdefs[i].tag, name) == 0)
162 				return (i);
163 		}
164 	}
165 	return (-1);
166 }
167 
168 /*
169  * is_a_number(number)
170  *
171  * is the string a number in one of the forms we want to use?
172  */
173 
174 static int
175 is_a_number(char *number)
176 {
177 	int ret = 1;
178 	int hex = 0;
179 
180 	if (strncmp(number, "0x", 2) == 0) {
181 		number += 2;
182 		hex = 1;
183 	} else if (*number == '-') {
184 		number++; /* skip the minus */
185 	}
186 
187 	while (ret == 1 && *number != '\0') {
188 		if (hex) {
189 			ret = isxdigit(*number++);
190 		} else {
191 			ret = isdigit(*number++);
192 		}
193 	}
194 	return (ret);
195 }
196 
197 /*
198  * validresource(name)
199  *
200  * Check that name only has valid characters in it. The current valid
201  * set are the printable characters but not including:
202  *	" / \ [ ] : | < > + ; , ? * = \t
203  * Note that space is included and there is a maximum length.
204  */
205 static int
206 validresource(const char *name)
207 {
208 	const char *cp;
209 	size_t len;
210 
211 	if (name == NULL)
212 		return (B_FALSE);
213 
214 	len = strlen(name);
215 	if (len == 0 || len > SA_MAX_RESOURCE_NAME)
216 		return (B_FALSE);
217 
218 	if (strpbrk(name, "\"/\\[]:|<>+;,?*=\t") != NULL) {
219 		return (B_FALSE);
220 	}
221 
222 	for (cp = name; *cp != '\0'; cp++)
223 		if (iscntrl(*cp))
224 			return (B_FALSE);
225 
226 	return (B_TRUE);
227 }
228 
229 /*
230  * smb_isonline()
231  *
232  * Determine if the SMF service instance is in the online state or
233  * not. A number of operations depend on this state.
234  */
235 static boolean_t
236 smb_isonline(void)
237 {
238 	char *str;
239 	boolean_t ret = B_FALSE;
240 
241 	if ((str = smf_get_state(SMBD_DEFAULT_INSTANCE_FMRI)) != NULL) {
242 		ret = (strcmp(str, SCF_STATE_STRING_ONLINE) == 0);
243 		free(str);
244 	}
245 	return (ret);
246 }
247 
248 /*
249  * smb_isdisabled()
250  *
251  * Determine if the SMF service instance is in the disabled state or
252  * not. A number of operations depend on this state.
253  */
254 static boolean_t
255 smb_isdisabled(void)
256 {
257 	char *str;
258 	boolean_t ret = B_FALSE;
259 
260 	if ((str = smf_get_state(SMBD_DEFAULT_INSTANCE_FMRI)) != NULL) {
261 		ret = (strcmp(str, SCF_STATE_STRING_DISABLED) == 0);
262 		free(str);
263 	}
264 	return (ret);
265 }
266 
267 /*
268  * smb_isautoenable()
269  *
270  * Determine if the SMF service instance auto_enabled set or not. A
271  * number of operations depend on this state.  The property not being
272  * set or being set to true means autoenable.  Only being set to false
273  * is not autoenabled.
274  */
275 static boolean_t
276 smb_isautoenable(void)
277 {
278 	boolean_t ret = B_TRUE;
279 	scf_simple_prop_t *prop;
280 	uint8_t *retstr;
281 
282 	prop = scf_simple_prop_get(NULL, SMBD_DEFAULT_INSTANCE_FMRI,
283 	    "application", "auto_enable");
284 	if (prop != NULL) {
285 		retstr = scf_simple_prop_next_boolean(prop);
286 		ret = *retstr != 0;
287 		scf_simple_prop_free(prop);
288 	}
289 	return (ret);
290 }
291 
292 /*
293  * smb_ismaint()
294  *
295  * Determine if the SMF service instance is in the disabled state or
296  * not. A number of operations depend on this state.
297  */
298 static boolean_t
299 smb_ismaint(void)
300 {
301 	char *str;
302 	boolean_t ret = B_FALSE;
303 
304 	if ((str = smf_get_state(SMBD_DEFAULT_INSTANCE_FMRI)) != NULL) {
305 		ret = (strcmp(str, SCF_STATE_STRING_MAINT) == 0);
306 		free(str);
307 	}
308 	return (ret);
309 }
310 
311 /*
312  * smb_enable_share tells the implementation that it is to enable the share.
313  * This entails converting the path and options into the appropriate ioctl
314  * calls. It is assumed that all error checking of paths, etc. were
315  * done earlier.
316  */
317 static int
318 smb_enable_share(sa_share_t share)
319 {
320 	char *path;
321 	char *rname;
322 	smb_share_t si;
323 	sa_resource_t resource;
324 	boolean_t iszfs;
325 	boolean_t privileged;
326 	int err = SA_OK;
327 	priv_set_t *priv_effective;
328 	boolean_t online;
329 
330 	/*
331 	 * We only start in the global zone and only run if we aren't
332 	 * running Trusted Extensions.
333 	 */
334 	if (getzoneid() != GLOBAL_ZONEID) {
335 		(void) printf(dgettext(TEXT_DOMAIN,
336 		    "SMB: service not supported in local zone\n"));
337 		return (SA_NOT_SUPPORTED);
338 	}
339 	if (is_system_labeled()) {
340 		(void) printf(dgettext(TEXT_DOMAIN,
341 		    "SMB: service not supported with Trusted Extensions\n"));
342 		return (SA_NOT_SUPPORTED);
343 	}
344 
345 	priv_effective = priv_allocset();
346 	(void) getppriv(PRIV_EFFECTIVE, priv_effective);
347 	privileged = (priv_isfullset(priv_effective) == B_TRUE);
348 	priv_freeset(priv_effective);
349 
350 	/* get the path since it is important in several places */
351 	path = sa_get_share_attr(share, "path");
352 	if (path == NULL)
353 		return (SA_NO_SUCH_PATH);
354 
355 	/*
356 	 * If administratively disabled, don't try to start anything.
357 	 */
358 	online = smb_isonline();
359 	if (!online && !smb_isautoenable() && smb_isdisabled())
360 		goto done;
361 
362 	iszfs = sa_path_is_zfs(path);
363 
364 	if (iszfs) {
365 
366 		if (privileged == B_FALSE && !online) {
367 
368 			if (!online) {
369 				(void) printf(dgettext(TEXT_DOMAIN,
370 				    "SMB: Cannot share remove "
371 				    "file system: %s\n"), path);
372 				(void) printf(dgettext(TEXT_DOMAIN,
373 				    "SMB: Service needs to be enabled "
374 				    "by a privileged user\n"));
375 				err = SA_NO_PERMISSION;
376 				errno = EPERM;
377 			}
378 			if (err) {
379 				sa_free_attr_string(path);
380 				return (err);
381 			}
382 
383 		}
384 	}
385 
386 	if (privileged == B_TRUE && !online) {
387 		err = smb_enable_service();
388 		if (err != SA_OK) {
389 			(void) printf(dgettext(TEXT_DOMAIN,
390 			    "SMB: Unable to enable service\n"));
391 			/*
392 			 * For now, it is OK to not be able to enable
393 			 * the service.
394 			 */
395 			if (err == SA_BUSY || err == SA_SYSTEM_ERR)
396 				err = SA_OK;
397 		} else {
398 			online = B_TRUE;
399 		}
400 	}
401 
402 	/*
403 	 * Don't bother trying to start shares if the service isn't
404 	 * running.
405 	 */
406 	if (!online)
407 		goto done;
408 
409 	/* Each share can have multiple resources */
410 	for (resource = sa_get_share_resource(share, NULL);
411 	    resource != NULL;
412 	    resource = sa_get_next_resource(resource)) {
413 		bzero(&si, sizeof (smb_share_t));
414 		rname = sa_get_resource_attr(resource, "name");
415 		if (rname == NULL) {
416 			sa_free_attr_string(path);
417 			return (SA_NO_SUCH_RESOURCE);
418 		}
419 
420 		smb_build_lmshare_info(rname, path, resource, &si);
421 		sa_free_attr_string(rname);
422 
423 		if (!iszfs) {
424 			err = smb_share_add(&si);
425 		} else {
426 			share_t sh;
427 
428 			sa_sharetab_fill_zfs(share, &sh, "smb");
429 			err = sa_share_zfs(share, (char *)path, &sh,
430 			    &si, ZFS_SHARE_SMB);
431 
432 			sa_emptyshare(&sh);
433 		}
434 	}
435 	if (!iszfs)
436 		(void) sa_update_sharetab(share, "smb");
437 done:
438 	sa_free_attr_string(path);
439 
440 	return (err == NERR_DuplicateShare ? 0 : err);
441 }
442 
443 /*
444  * This is the share for CIFS all shares have resource names.
445  * Enable tells the smb server to update its hash. If it fails
446  * because smb server is down, we just ignore as smb server loads
447  * the resources from sharemanager at startup.
448  */
449 
450 static int
451 smb_enable_resource(sa_resource_t resource)
452 {
453 	char *path;
454 	char *rname;
455 	sa_share_t share;
456 	smb_share_t si;
457 	int ret = SA_OK;
458 	int err;
459 	boolean_t isonline;
460 
461 	share = sa_get_resource_parent(resource);
462 	if (share == NULL)
463 		return (SA_NO_SUCH_PATH);
464 
465 	/*
466 	 * If administratively disabled, don't try to start anything.
467 	 */
468 	isonline = smb_isonline();
469 	if (!isonline && !smb_isautoenable() && smb_isdisabled())
470 		goto done;
471 
472 	if (!isonline)
473 		ret = smb_enable_service();
474 	if (!smb_isonline()) {
475 		ret = SA_OK;
476 		goto done;
477 	}
478 
479 	path = sa_get_share_attr(share, "path");
480 	if (path == NULL)
481 		return (SA_SYSTEM_ERR);
482 	rname = sa_get_resource_attr(resource, "name");
483 	if (rname == NULL) {
484 		sa_free_attr_string(path);
485 		return (SA_NO_SUCH_RESOURCE);
486 	}
487 
488 	smb_build_lmshare_info(rname, path, resource, &si);
489 	sa_free_attr_string(path);
490 	sa_free_attr_string(rname);
491 
492 	/*
493 	 * Attempt to add the share. Any error that occurs if it was
494 	 * online is an error but don't count NERR_DuplicateName if
495 	 * smb/server had to be brought online since bringing the
496 	 * service up will enable the share that was just added prior
497 	 * to the attempt to enable.
498 	 */
499 
500 	err = smb_share_add(&si);
501 	if (err == NERR_Success || !(!isonline && err == NERR_DuplicateName))
502 		(void) sa_update_sharetab(share, "smb");
503 	else
504 		return (SA_NOT_SHARED);
505 
506 done:
507 	return (ret);
508 }
509 
510 /*
511  * Remove it from smb server hash.
512  */
513 static int
514 smb_disable_resource(sa_resource_t resource)
515 {
516 	char *rname;
517 	uint32_t res;
518 	sa_share_t share;
519 
520 	rname = sa_get_resource_attr(resource, "name");
521 	if (rname == NULL)
522 		return (SA_NO_SUCH_RESOURCE);
523 
524 	if (smb_isonline()) {
525 		res = smb_share_del(rname);
526 		if (res != NERR_Success) {
527 			sa_free_attr_string(rname);
528 			return (SA_CONFIG_ERR);
529 		}
530 	}
531 
532 	sa_free_attr_string(rname);
533 
534 	share = sa_get_resource_parent(resource);
535 	if (share != NULL) {
536 		rname = sa_get_share_attr(share, "path");
537 		if (rname != NULL) {
538 			sa_handle_t handle;
539 
540 			handle = sa_find_group_handle((sa_group_t)resource);
541 			(void) sa_delete_sharetab(handle, rname, "smb");
542 			sa_free_attr_string(rname);
543 		}
544 	}
545 	/*
546 	 * Always return OK as smb/server may be down and
547 	 * Shares will be picked up when loaded.
548 	 */
549 	return (SA_OK);
550 }
551 
552 /*
553  * smb_share_changed(sa_share_t share)
554  *
555  * The specified share has changed.
556  */
557 static int
558 smb_share_changed(sa_share_t share)
559 {
560 	char *path;
561 	sa_resource_t resource;
562 
563 	/* get the path since it is important in several places */
564 	path = sa_get_share_attr(share, "path");
565 	if (path == NULL)
566 		return (SA_NO_SUCH_PATH);
567 	for (resource = sa_get_share_resource(share, NULL);
568 	    resource != NULL;
569 	    resource = sa_get_next_resource(resource))
570 		(void) smb_resource_changed(resource);
571 
572 	sa_free_attr_string(path);
573 
574 	return (SA_OK);
575 }
576 
577 /*
578  * smb_resource_changed(sa_resource_t resource)
579  *
580  * The specified resource has changed.
581  */
582 static int
583 smb_resource_changed(sa_resource_t resource)
584 {
585 	uint32_t res;
586 	smb_share_t si;
587 	smb_share_t new_si;
588 	char *rname, *path;
589 	sa_share_t share;
590 
591 	rname = sa_get_resource_attr(resource, "name");
592 	if (rname == NULL)
593 		return (SA_NO_SUCH_RESOURCE);
594 
595 	share = sa_get_resource_parent(resource);
596 	if (share == NULL) {
597 		sa_free_attr_string(rname);
598 		return (SA_CONFIG_ERR);
599 	}
600 
601 	path = sa_get_share_attr(share, "path");
602 	if (path == NULL) {
603 		sa_free_attr_string(rname);
604 		return (SA_NO_SUCH_PATH);
605 	}
606 
607 	if (!smb_isonline()) {
608 		sa_free_attr_string(rname);
609 		return (SA_OK);
610 	}
611 
612 	/* Update the share cache in smb/server */
613 	res = smb_share_get(rname, &si);
614 	if (res != NERR_Success) {
615 		sa_free_attr_string(path);
616 		sa_free_attr_string(rname);
617 		return (SA_CONFIG_ERR);
618 	}
619 
620 	smb_build_lmshare_info(rname, path, resource, &new_si);
621 	sa_free_attr_string(path);
622 	sa_free_attr_string(rname);
623 
624 	/*
625 	 * Update all fields from sa_share_t
626 	 * Get derived values.
627 	 */
628 	if (smb_share_set(&new_si) != SMB_SHARE_DSUCCESS)
629 		return (SA_CONFIG_ERR);
630 	return (smb_enable_service());
631 }
632 
633 /*
634  * smb_disable_share(sa_share_t share, char *path)
635  *
636  * Unshare the specified share. Note that "path" is the same
637  * path as what is in the "share" object. It is passed in to avoid an
638  * additional lookup. A missing "path" value makes this a no-op
639  * function.
640  */
641 static int
642 smb_disable_share(sa_share_t share, char *path)
643 {
644 	char *rname;
645 	sa_resource_t resource;
646 	sa_group_t parent;
647 	boolean_t iszfs;
648 	int err = SA_OK;
649 	sa_handle_t handle;
650 
651 	if (path == NULL)
652 		return (err);
653 
654 	/*
655 	 * If the share is in a ZFS group we need to handle it
656 	 * differently.  Just being on a ZFS file system isn't
657 	 * enough since we may be in a legacy share case.
658 	 */
659 	parent = sa_get_parent_group(share);
660 	iszfs = sa_group_is_zfs(parent);
661 
662 	if (!smb_isonline())
663 		goto done;
664 
665 	for (resource = sa_get_share_resource(share, NULL);
666 	    resource != NULL;
667 	    resource = sa_get_next_resource(resource)) {
668 		rname = sa_get_resource_attr(resource, "name");
669 		if (rname == NULL) {
670 			continue;
671 		}
672 		if (!iszfs) {
673 			err = smb_share_del(rname);
674 			switch (err) {
675 			case NERR_NetNameNotFound:
676 			case NERR_Success:
677 				err = SA_OK;
678 				break;
679 			default:
680 				err = SA_CONFIG_ERR;
681 				break;
682 			}
683 		} else {
684 			share_t sh;
685 
686 			sa_sharetab_fill_zfs(share, &sh, "smb");
687 			err = sa_share_zfs(share, (char *)path, &sh,
688 			    rname, ZFS_UNSHARE_SMB);
689 			sa_emptyshare(&sh);
690 		}
691 		sa_free_attr_string(rname);
692 	}
693 done:
694 	if (!iszfs) {
695 		handle = sa_find_group_handle((sa_group_t)share);
696 		if (handle != NULL)
697 			(void) sa_delete_sharetab(handle, path, "smb");
698 		else
699 			err = SA_SYSTEM_ERR;
700 	}
701 	return (err);
702 }
703 
704 /*
705  * smb_validate_property(handle, property, parent)
706  *
707  * Check that the property has a legitimate value for its type.
708  * Handle isn't currently used but may need to be in the future.
709  */
710 
711 /*ARGSUSED*/
712 static int
713 smb_validate_property(sa_handle_t handle, sa_property_t property,
714     sa_optionset_t parent)
715 {
716 	int ret = SA_OK;
717 	char *propname;
718 	int optindex;
719 	sa_group_t parent_group;
720 	char *value;
721 
722 	propname = sa_get_property_attr(property, "type");
723 
724 	if ((optindex = findopt(propname)) < 0)
725 		ret = SA_NO_SUCH_PROP;
726 
727 	/* need to validate value range here as well */
728 	if (ret == SA_OK) {
729 		parent_group = sa_get_parent_group((sa_share_t)parent);
730 		if (optdefs[optindex].share && !sa_is_share(parent_group))
731 			ret = SA_PROP_SHARE_ONLY;
732 	}
733 	if (ret != SA_OK) {
734 		if (propname != NULL)
735 			sa_free_attr_string(propname);
736 		return (ret);
737 	}
738 
739 	value = sa_get_property_attr(property, "value");
740 	if (value != NULL) {
741 		/* first basic type checking */
742 		switch (optdefs[optindex].type) {
743 		case OPT_TYPE_NUMBER:
744 			/* check that the value is all digits */
745 			if (!is_a_number(value))
746 				ret = SA_BAD_VALUE;
747 			break;
748 		case OPT_TYPE_BOOLEAN:
749 			if (strlen(value) == 0 ||
750 			    strcasecmp(value, "true") == 0 ||
751 			    strcmp(value, "1") == 0 ||
752 			    strcasecmp(value, "false") == 0 ||
753 			    strcmp(value, "0") == 0) {
754 				ret = SA_OK;
755 			} else {
756 				ret = SA_BAD_VALUE;
757 			}
758 			break;
759 		case OPT_TYPE_NAME:
760 			/*
761 			 * Make sure no invalid characters
762 			 */
763 			if (validresource(value) == B_FALSE)
764 				ret = SA_BAD_VALUE;
765 			break;
766 		case OPT_TYPE_STRING:
767 			/* whatever is here should be ok */
768 			break;
769 		default:
770 			break;
771 		}
772 	}
773 
774 	if (value != NULL)
775 		sa_free_attr_string(value);
776 	if (ret == SA_OK && optdefs[optindex].check != NULL)
777 		/* do the property specific check */
778 		ret = optdefs[optindex].check(property);
779 
780 	if (propname != NULL)
781 		sa_free_attr_string(propname);
782 	return (ret);
783 }
784 
785 /*
786  * Protocol management functions
787  *
788  * properties defined in the default files are defined in
789  * proto_option_defs for parsing and validation.
790  */
791 
792 struct smb_proto_option_defs {
793 	int smb_index;
794 	int32_t minval;
795 	int32_t maxval; /* In case of length of string this should be max */
796 	int (*validator)(int, char *);
797 	int32_t	refresh;
798 } smb_proto_options[] = {
799 	{ SMB_CI_SYS_CMNT, 0, MAX_VALUE_BUFLEN,
800 	    string_length_check_validator, SMB_REFRESH_REFRESH },
801 	{ SMB_CI_MAX_WORKERS, 64, 1024, range_check_validator,
802 	    SMB_REFRESH_REFRESH },
803 	{ SMB_CI_NBSCOPE, 0, MAX_VALUE_BUFLEN,
804 	    string_length_check_validator, 0 },
805 	{ SMB_CI_LM_LEVEL, 2, 5, range_check_validator, 0 },
806 	{ SMB_CI_KEEPALIVE, 20, 5400, range_check_validator_zero_ok,
807 	    SMB_REFRESH_REFRESH },
808 	{ SMB_CI_WINS_SRV1, 0, MAX_VALUE_BUFLEN,
809 	    ip_address_validator_empty_ok, SMB_REFRESH_REFRESH },
810 	{ SMB_CI_WINS_SRV2, 0, MAX_VALUE_BUFLEN,
811 	    ip_address_validator_empty_ok, SMB_REFRESH_REFRESH },
812 	{ SMB_CI_WINS_EXCL, 0, MAX_VALUE_BUFLEN,
813 	    ip_address_csv_list_validator_empty_ok, SMB_REFRESH_REFRESH },
814 	{ SMB_CI_SIGNING_ENABLE, 0, 0, true_false_validator,
815 	    SMB_REFRESH_REFRESH },
816 	{ SMB_CI_SIGNING_REQD, 0, 0, true_false_validator,
817 	    SMB_REFRESH_REFRESH },
818 	{ SMB_CI_RESTRICT_ANON, 0, 0, true_false_validator,
819 	    SMB_REFRESH_REFRESH },
820 	{ SMB_CI_DOMAIN_SRV, 0, MAX_VALUE_BUFLEN,
821 	    ip_address_validator_empty_ok, 0 },
822 	{ SMB_CI_ADS_SITE, 0, MAX_VALUE_BUFLEN,
823 	    string_length_check_validator, SMB_REFRESH_REFRESH },
824 	{ SMB_CI_DYNDNS_ENABLE, 0, 0, true_false_validator, 0 },
825 	{ SMB_CI_AUTOHOME_MAP, 0, MAX_VALUE_BUFLEN, path_validator, 0 },
826 };
827 
828 #define	SMB_OPT_NUM \
829 	(sizeof (smb_proto_options) / sizeof (smb_proto_options[0]))
830 
831 /*
832  * Check the range of value as int range.
833  */
834 static int
835 range_check_validator(int index, char *value)
836 {
837 	int ret = SA_OK;
838 
839 	if (!is_a_number(value)) {
840 		ret = SA_BAD_VALUE;
841 	} else {
842 		int val;
843 		val = strtoul(value, NULL, 0);
844 		if (val < smb_proto_options[index].minval ||
845 		    val > smb_proto_options[index].maxval)
846 			ret = SA_BAD_VALUE;
847 	}
848 	return (ret);
849 }
850 
851 /*
852  * Check the range of value as int range.
853  */
854 static int
855 range_check_validator_zero_ok(int index, char *value)
856 {
857 	int ret = SA_OK;
858 
859 	if (!is_a_number(value)) {
860 		ret = SA_BAD_VALUE;
861 	} else {
862 		int val;
863 		val = strtoul(value, NULL, 0);
864 		if (val == 0)
865 			ret = SA_OK;
866 		else {
867 			if (val < smb_proto_options[index].minval ||
868 			    val > smb_proto_options[index].maxval)
869 			ret = SA_BAD_VALUE;
870 		}
871 	}
872 	return (ret);
873 }
874 
875 /*
876  * Check the length of the string
877  */
878 static int
879 string_length_check_validator(int index, char *value)
880 {
881 	int ret = SA_OK;
882 
883 	if (value == NULL)
884 		return (SA_BAD_VALUE);
885 	if (strlen(value) > smb_proto_options[index].maxval)
886 		ret = SA_BAD_VALUE;
887 	return (ret);
888 }
889 
890 /*
891  * Check yes/no
892  */
893 /*ARGSUSED*/
894 static int
895 true_false_validator(int index, char *value)
896 {
897 	if (value == NULL)
898 		return (SA_BAD_VALUE);
899 	if ((strcasecmp(value, "true") == 0) ||
900 	    (strcasecmp(value, "false") == 0))
901 		return (SA_OK);
902 	return (SA_BAD_VALUE);
903 }
904 
905 /*
906  * Check IP address.
907  */
908 /*ARGSUSED*/
909 static int
910 ip_address_validator_empty_ok(int index, char *value)
911 {
912 	char sbytes[16];
913 	int len;
914 
915 	if (value == NULL)
916 		return (SA_OK);
917 	len = strlen(value);
918 	if (len == 0)
919 		return (SA_OK);
920 	if (inet_pton(AF_INET, value, (void *)sbytes) != 1)
921 		return (SA_BAD_VALUE);
922 
923 	return (SA_OK);
924 }
925 
926 /*
927  * Check IP address list
928  */
929 /*ARGSUSED*/
930 static int
931 ip_address_csv_list_validator_empty_ok(int index, char *value)
932 {
933 	char sbytes[16];
934 	char *ip, *tmp, *ctx;
935 
936 	if (value == NULL || *value == '\0')
937 		return (SA_OK);
938 
939 	if (strlen(value) > MAX_VALUE_BUFLEN)
940 		return (SA_BAD_VALUE);
941 
942 	if ((tmp = strdup(value)) == NULL)
943 		return (SA_NO_MEMORY);
944 
945 	ip = strtok_r(tmp, ",", &ctx);
946 	while (ip) {
947 		if (strlen(ip) == 0) {
948 			free(tmp);
949 			return (SA_BAD_VALUE);
950 		}
951 		if (*ip != 0) {
952 			if (inet_pton(AF_INET, ip,
953 			    (void *)sbytes) != 1) {
954 				free(tmp);
955 				return (SA_BAD_VALUE);
956 			}
957 		}
958 		ip = strtok_r(0, ",", &ctx);
959 	}
960 
961 	free(tmp);
962 	return (SA_OK);
963 }
964 
965 /*
966  * Check path
967  */
968 /*ARGSUSED*/
969 static int
970 path_validator(int index, char *value)
971 {
972 	struct stat buffer;
973 	int fd, status;
974 
975 	if (value == NULL)
976 		return (SA_BAD_VALUE);
977 
978 	fd = open(value, O_RDONLY);
979 	if (fd < 0)
980 		return (SA_BAD_VALUE);
981 
982 	status = fstat(fd, &buffer);
983 	(void) close(fd);
984 
985 	if (status < 0)
986 		return (SA_BAD_VALUE);
987 
988 	if (buffer.st_mode & S_IFDIR)
989 		return (SA_OK);
990 	return (SA_BAD_VALUE);
991 }
992 
993 /*
994  * the protoset holds the defined options so we don't have to read
995  * them multiple times
996  */
997 static sa_protocol_properties_t protoset;
998 
999 static int
1000 findprotoopt(char *name)
1001 {
1002 	int i;
1003 	char *sc_name;
1004 
1005 	for (i = 0; i < SMB_OPT_NUM; i++) {
1006 		sc_name = smb_config_getname(smb_proto_options[i].smb_index);
1007 		if (strcasecmp(sc_name, name) == 0)
1008 			return (i);
1009 	}
1010 
1011 	return (-1);
1012 }
1013 
1014 /*
1015  * smb_load_proto_properties()
1016  *
1017  * read the smb config values from SMF.
1018  */
1019 
1020 static int
1021 smb_load_proto_properties()
1022 {
1023 	sa_property_t prop;
1024 	char value[MAX_VALUE_BUFLEN];
1025 	char *name;
1026 	int index;
1027 	int ret = SA_OK;
1028 	int rc;
1029 
1030 	protoset = sa_create_protocol_properties(SMB_PROTOCOL_NAME);
1031 	if (protoset == NULL)
1032 		return (SA_NO_MEMORY);
1033 
1034 	for (index = 0; index < SMB_OPT_NUM && ret == SA_OK; index++) {
1035 		rc = smb_config_get(smb_proto_options[index].smb_index,
1036 		    value, sizeof (value));
1037 		if (rc != SMBD_SMF_OK)
1038 			continue;
1039 		name = smb_config_getname(smb_proto_options[index].smb_index);
1040 		prop = sa_create_property(name, value);
1041 		if (prop != NULL)
1042 			ret = sa_add_protocol_property(protoset, prop);
1043 		else
1044 			ret = SA_NO_MEMORY;
1045 	}
1046 	return (ret);
1047 }
1048 
1049 /*
1050  * smb_share_init()
1051  *
1052  * Initialize the smb plugin.
1053  */
1054 
1055 static int
1056 smb_share_init(void)
1057 {
1058 	int ret = SA_OK;
1059 
1060 	if (sa_plugin_ops.sa_init != smb_share_init)
1061 		return (SA_SYSTEM_ERR);
1062 
1063 	ret = smb_load_proto_properties();
1064 
1065 	return (ret);
1066 }
1067 
1068 /*
1069  * smb_share_fini()
1070  *
1071  */
1072 static void
1073 smb_share_fini(void)
1074 {
1075 	xmlFreeNode(protoset);
1076 	protoset = NULL;
1077 
1078 	(void) smb_share_dclose();
1079 }
1080 
1081 /*
1082  * smb_get_proto_set()
1083  *
1084  * Return an optionset with all the protocol specific properties in
1085  * it.
1086  */
1087 static sa_protocol_properties_t
1088 smb_get_proto_set(void)
1089 {
1090 	return (protoset);
1091 }
1092 
1093 /*
1094  * smb_enable_dependencies()
1095  *
1096  * SMBD_DEFAULT_INSTANCE_FMRI may have some dependencies that aren't
1097  * enabled. This will attempt to enable all of them.
1098  */
1099 static void
1100 smb_enable_dependencies(const char *fmri)
1101 {
1102 	scf_handle_t *handle;
1103 	scf_service_t *service;
1104 	scf_instance_t *inst = NULL;
1105 	scf_iter_t *iter;
1106 	scf_property_t *prop;
1107 	scf_value_t *value;
1108 	scf_propertygroup_t *pg;
1109 	scf_scope_t *scope;
1110 	char type[SCFTYPE_LEN];
1111 	char *dependency;
1112 	char *servname;
1113 	int maxlen;
1114 
1115 	/*
1116 	 * Get all required handles and storage.
1117 	 */
1118 	handle = scf_handle_create(SCF_VERSION);
1119 	if (handle == NULL)
1120 		return;
1121 
1122 	if (scf_handle_bind(handle) != 0) {
1123 		scf_handle_destroy(handle);
1124 		return;
1125 	}
1126 
1127 	maxlen = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
1128 	if (maxlen == (ssize_t)-1)
1129 		maxlen = MAXPATHLEN;
1130 
1131 	dependency = malloc(maxlen);
1132 
1133 	service = scf_service_create(handle);
1134 
1135 	iter = scf_iter_create(handle);
1136 
1137 	pg = scf_pg_create(handle);
1138 
1139 	prop = scf_property_create(handle);
1140 
1141 	value = scf_value_create(handle);
1142 
1143 	scope = scf_scope_create(handle);
1144 
1145 	if (service == NULL || iter == NULL || pg == NULL || prop == NULL ||
1146 	    value == NULL || scope == NULL || dependency == NULL)
1147 		goto done;
1148 
1149 	/*
1150 	 *  We passed in the FMRI for the default instance but for
1151 	 *  some things we need the simple form so construct it. Since
1152 	 *  we reuse the storage that dependency points to, we need to
1153 	 *  use the servname early.
1154 	 */
1155 	(void) snprintf(dependency, maxlen, "%s", fmri + sizeof ("svc:"));
1156 	servname = strrchr(dependency, ':');
1157 	if (servname == NULL)
1158 		goto done;
1159 	*servname = '\0';
1160 	servname = dependency;
1161 
1162 	/*
1163 	 * Setup to iterate over the service property groups, only
1164 	 * looking at those that are "dependency" types. The "entity"
1165 	 * property will have the FMRI of the service we are dependent
1166 	 * on.
1167 	 */
1168 	if (scf_handle_get_scope(handle, SCF_SCOPE_LOCAL, scope) != 0)
1169 		goto done;
1170 
1171 	if (scf_scope_get_service(scope, servname, service) != 0)
1172 		goto done;
1173 
1174 	if (scf_iter_service_pgs(iter, service) != 0)
1175 		goto done;
1176 
1177 	while (scf_iter_next_pg(iter, pg) > 0) {
1178 		char *services[2];
1179 		/*
1180 		 * Have a property group for the service. See if it is
1181 		 * a dependency pg and only do operations on those.
1182 		 */
1183 		if (scf_pg_get_type(pg, type, SCFTYPE_LEN) <= 0)
1184 			continue;
1185 
1186 		if (strncmp(type, SCF_GROUP_DEPENDENCY, SCFTYPE_LEN) != 0)
1187 			continue;
1188 		/*
1189 		 * Have a dependency.  Attempt to enable it.
1190 		 */
1191 		if (scf_pg_get_property(pg, SCF_PROPERTY_ENTITIES, prop) != 0)
1192 			continue;
1193 
1194 		if (scf_property_get_value(prop, value) != 0)
1195 			continue;
1196 
1197 		services[1] = NULL;
1198 
1199 		if (scf_value_get_as_string(value, dependency, maxlen) > 0) {
1200 			services[0] = dependency;
1201 			_check_services(services);
1202 		}
1203 	}
1204 
1205 done:
1206 	if (dependency != NULL)
1207 		free(dependency);
1208 	if (value != NULL)
1209 		scf_value_destroy(value);
1210 	if (prop != NULL)
1211 		scf_property_destroy(prop);
1212 	if (pg != NULL)
1213 		scf_pg_destroy(pg);
1214 	if (iter != NULL)
1215 		scf_iter_destroy(iter);
1216 	if (scope != NULL)
1217 		scf_scope_destroy(scope);
1218 	if (inst != NULL)
1219 		scf_instance_destroy(inst);
1220 	if (service != NULL)
1221 		scf_service_destroy(service);
1222 
1223 	(void) scf_handle_unbind(handle);
1224 	scf_handle_destroy(handle);
1225 }
1226 
1227 /*
1228  * How long to wait for service to come online
1229  */
1230 #define	WAIT_FOR_SERVICE	15
1231 
1232 /*
1233  * smb_enable_service()
1234  *
1235  */
1236 static int
1237 smb_enable_service(void)
1238 {
1239 	int i;
1240 	int ret = SA_OK;
1241 	char *service[] = { SMBD_DEFAULT_INSTANCE_FMRI, NULL };
1242 
1243 	if (!smb_isonline()) {
1244 		/*
1245 		 * Attempt to start the idmap, and other dependent
1246 		 * services, first.  If it fails, the SMB service will
1247 		 * ultimately fail so we use that as the error.  If we
1248 		 * don't try to enable idmap, smb won't start the
1249 		 * first time unless the admin has done it
1250 		 * manually. The service could be administratively
1251 		 * disabled so we won't always get started.
1252 		 */
1253 		smb_enable_dependencies(SMBD_DEFAULT_INSTANCE_FMRI);
1254 		_check_services(service);
1255 
1256 		/* Wait for service to come online */
1257 		for (i = 0; i < WAIT_FOR_SERVICE; i++) {
1258 			if (smb_isonline()) {
1259 				ret =  SA_OK;
1260 				break;
1261 			} else if (smb_ismaint()) {
1262 				/* maintenance requires help */
1263 				ret = SA_SYSTEM_ERR;
1264 				break;
1265 			} else if (smb_isdisabled()) {
1266 				/* disabled is ok */
1267 				ret = SA_OK;
1268 				break;
1269 			} else {
1270 				/* try another time */
1271 				ret = SA_BUSY;
1272 				(void) sleep(1);
1273 			}
1274 		}
1275 	}
1276 	return (ret);
1277 }
1278 
1279 /*
1280  * smb_validate_proto_prop(index, name, value)
1281  *
1282  * Verify that the property specified by name can take the new
1283  * value. This is a sanity check to prevent bad values getting into
1284  * the default files.
1285  */
1286 static int
1287 smb_validate_proto_prop(int index, char *name, char *value)
1288 {
1289 	if ((name == NULL) || (index < 0))
1290 		return (SA_BAD_VALUE);
1291 
1292 	if (smb_proto_options[index].validator == NULL)
1293 		return (SA_OK);
1294 
1295 	if (smb_proto_options[index].validator(index, value) == SA_OK)
1296 		return (SA_OK);
1297 	return (SA_BAD_VALUE);
1298 }
1299 
1300 /*
1301  * smb_set_proto_prop(prop)
1302  *
1303  * check that prop is valid.
1304  */
1305 /*ARGSUSED*/
1306 static int
1307 smb_set_proto_prop(sa_property_t prop)
1308 {
1309 	int ret = SA_OK;
1310 	char *name;
1311 	char *value;
1312 	int index = -1;
1313 	struct smb_proto_option_defs *opt;
1314 
1315 	name = sa_get_property_attr(prop, "type");
1316 	value = sa_get_property_attr(prop, "value");
1317 	if (name != NULL && value != NULL) {
1318 		index = findprotoopt(name);
1319 		if (index >= 0) {
1320 			/* should test for valid value */
1321 			ret = smb_validate_proto_prop(index, name, value);
1322 			if (ret == SA_OK) {
1323 				opt = &smb_proto_options[index];
1324 
1325 				/* Save to SMF */
1326 				(void) smb_config_set(opt->smb_index, value);
1327 				/*
1328 				 * Specialized refresh mechanisms can
1329 				 * be flagged in the proto_options and
1330 				 * processed here.
1331 				 */
1332 				if (opt->refresh & SMB_REFRESH_REFRESH)
1333 					(void) smf_refresh_instance(
1334 					    SMBD_DEFAULT_INSTANCE_FMRI);
1335 				else if (opt->refresh & SMB_REFRESH_RESTART)
1336 					(void) smf_restart_instance(
1337 					    SMBD_DEFAULT_INSTANCE_FMRI);
1338 			}
1339 		}
1340 	}
1341 
1342 	if (name != NULL)
1343 		sa_free_attr_string(name);
1344 	if (value != NULL)
1345 		sa_free_attr_string(value);
1346 
1347 	return (ret);
1348 }
1349 
1350 /*
1351  * smb_get_status()
1352  *
1353  * What is the current status of the smbd? We use the SMF state here.
1354  * Caller must free the returned value.
1355  */
1356 
1357 static char *
1358 smb_get_status(void)
1359 {
1360 	char *state = NULL;
1361 	state = smf_get_state(SMBD_DEFAULT_INSTANCE_FMRI);
1362 	return (state != NULL ? state : "-");
1363 }
1364 
1365 /*
1366  * This protocol plugin require resource names
1367  */
1368 static uint64_t
1369 smb_share_features(void)
1370 {
1371 	return (SA_FEATURE_RESOURCE | SA_FEATURE_ALLOWSUBDIRS |
1372 	    SA_FEATURE_ALLOWPARDIRS | SA_FEATURE_SERVER);
1373 }
1374 
1375 /*
1376  * This should be used to convert lmshare_info to sa_resource_t
1377  * Should only be needed to build temp shares/resources to be
1378  * supplied to sharemanager to display temp shares.
1379  */
1380 static int
1381 smb_build_tmp_sa_resource(sa_handle_t handle, smb_share_t *si)
1382 {
1383 	int err;
1384 	sa_share_t share;
1385 	sa_group_t group;
1386 	sa_resource_t resource;
1387 
1388 	if (si == NULL)
1389 		return (SA_INVALID_NAME);
1390 
1391 	/*
1392 	 * First determine if the "share path" is already shared
1393 	 * somewhere. If it is, we have to use it as the authority on
1394 	 * where the transient share lives so will use it's parent
1395 	 * group. If it doesn't exist, it needs to land in "smb".
1396 	 */
1397 
1398 	share = sa_find_share(handle, si->shr_path);
1399 	if (share != NULL) {
1400 		group = sa_get_parent_group(share);
1401 	} else {
1402 		group = smb_get_smb_share_group(handle);
1403 		if (group == NULL)
1404 			return (SA_NO_SUCH_GROUP);
1405 		share = sa_get_share(group, si->shr_path);
1406 		if (share == NULL) {
1407 			share = sa_add_share(group, si->shr_path,
1408 			    SA_SHARE_TRANSIENT, &err);
1409 			if (share == NULL)
1410 				return (SA_NO_SUCH_PATH);
1411 		}
1412 	}
1413 
1414 	/*
1415 	 * Now handle the resource. Make sure that the resource is
1416 	 * transient and added to the share.
1417 	 */
1418 	resource = sa_get_share_resource(share, si->shr_name);
1419 	if (resource == NULL) {
1420 		resource = sa_add_resource(share,
1421 		    si->shr_name, SA_SHARE_TRANSIENT, &err);
1422 		if (resource == NULL)
1423 			return (SA_NO_SUCH_RESOURCE);
1424 	}
1425 
1426 	/* set resource attributes now */
1427 	(void) sa_set_resource_attr(resource, "description", si->shr_cmnt);
1428 	(void) sa_set_resource_attr(resource, SMB_SHROPT_AD_CONTAINER,
1429 	    si->shr_container);
1430 
1431 	return (SA_OK);
1432 }
1433 
1434 /*
1435  * Return smb transient shares.  Note that we really want to look at
1436  * all current shares from SMB in order to determine this. Transient
1437  * shares should be those that don't appear in either the SMF or ZFS
1438  * configurations.  Those that are in the repositories will be
1439  * filtered out by smb_build_tmp_sa_resource.
1440  */
1441 static int
1442 smb_list_transient(sa_handle_t handle)
1443 {
1444 	int i, offset, num;
1445 	smb_shrlist_t list;
1446 	int res;
1447 
1448 	num = smb_share_count();
1449 	if (num <= 0)
1450 		return (SA_OK);
1451 	offset = 0;
1452 	while (smb_share_list(offset, &list) != NERR_InternalError) {
1453 		if (list.no == 0)
1454 			break;
1455 		for (i = 0; i < list.no; i++) {
1456 			res = smb_build_tmp_sa_resource(handle,
1457 			    &(list.smbshr[i]));
1458 			if (res != SA_OK)
1459 				return (res);
1460 		}
1461 		offset += list.no;
1462 	}
1463 
1464 	return (SA_OK);
1465 }
1466 
1467 /*
1468  * fix_resource_name(share, name,  prefix)
1469  *
1470  * Construct a name where the ZFS dataset has the prefix replaced with "name".
1471  */
1472 static char *
1473 fix_resource_name(sa_share_t share, char *name, char *prefix)
1474 {
1475 	char *dataset = NULL;
1476 	char *newname = NULL;
1477 	size_t psize;
1478 	size_t nsize;
1479 
1480 	dataset = sa_get_share_attr(share, "dataset");
1481 
1482 	if (dataset != NULL && strcmp(dataset, prefix) != 0) {
1483 		psize = strlen(prefix);
1484 		if (strncmp(dataset, prefix, psize) == 0) {
1485 			/* need string plus ',' and NULL */
1486 			nsize = (strlen(dataset) - psize) + strlen(name) + 2;
1487 			newname = calloc(nsize, 1);
1488 			if (newname != NULL) {
1489 				(void) snprintf(newname, nsize, "%s%s", name,
1490 				    dataset + psize);
1491 				sa_fix_resource_name(newname);
1492 			}
1493 			sa_free_attr_string(dataset);
1494 			return (newname);
1495 		}
1496 	}
1497 	if (dataset != NULL)
1498 		sa_free_attr_string(dataset);
1499 	return (strdup(name));
1500 }
1501 
1502 /*
1503  * smb_parse_optstring(group, options)
1504  *
1505  * parse a compact option string into individual options. This allows
1506  * ZFS sharesmb and sharemgr "share" command to work.  group can be a
1507  * group, a share or a resource.
1508  */
1509 static int
1510 smb_parse_optstring(sa_group_t group, char *options)
1511 {
1512 	char *dup;
1513 	char *base;
1514 	char *lasts;
1515 	char *token;
1516 	sa_optionset_t optionset;
1517 	sa_group_t parent = NULL;
1518 	sa_resource_t resource = NULL;
1519 	int iszfs = 0;
1520 	int persist = 0;
1521 	int need_optionset = 0;
1522 	int ret = SA_OK;
1523 	sa_property_t prop;
1524 
1525 	/*
1526 	 * In order to not attempt to change ZFS properties unless
1527 	 * absolutely necessary, we never do it in the legacy parsing
1528 	 * so we need to keep track of this.
1529 	 */
1530 	if (sa_is_share(group)) {
1531 		char *zfs;
1532 
1533 		parent = sa_get_parent_group(group);
1534 		if (parent != NULL) {
1535 			zfs = sa_get_group_attr(parent, "zfs");
1536 			if (zfs != NULL) {
1537 				sa_free_attr_string(zfs);
1538 				iszfs = 1;
1539 			}
1540 		}
1541 	} else {
1542 		iszfs = sa_group_is_zfs(group);
1543 		/*
1544 		 * If a ZFS group, then we need to see if a resource
1545 		 * name is being set. If so, bail with
1546 		 * SA_PROP_SHARE_ONLY, so we come back in with a share
1547 		 * instead of a group.
1548 		 */
1549 		if (strncmp(options, "name=", sizeof ("name=") - 1) == 0 ||
1550 		    strstr(options, ",name=") != NULL) {
1551 			return (SA_PROP_SHARE_ONLY);
1552 		}
1553 	}
1554 
1555 	/* do we have an existing optionset? */
1556 	optionset = sa_get_optionset(group, "smb");
1557 	if (optionset == NULL) {
1558 		/* didn't find existing optionset so create one */
1559 		optionset = sa_create_optionset(group, "smb");
1560 		if (optionset == NULL)
1561 			return (SA_NO_MEMORY);
1562 	} else {
1563 		/*
1564 		 * If an optionset already exists, we've come through
1565 		 * twice so ignore the second time.
1566 		 */
1567 		return (ret);
1568 	}
1569 
1570 	/* We need a copy of options for the next part. */
1571 	dup = strdup(options);
1572 	if (dup == NULL)
1573 		return (SA_NO_MEMORY);
1574 
1575 	/*
1576 	 * SMB properties are straightforward and are strings,
1577 	 * integers or booleans.  Properties are separated by
1578 	 * commas. It will be necessary to parse quotes due to some
1579 	 * strings not having a restricted characters set.
1580 	 *
1581 	 * Note that names will create a resource. For now, if there
1582 	 * is a set of properties "before" the first name="", those
1583 	 * properties will be placed on the group.
1584 	 */
1585 	persist = sa_is_persistent(group);
1586 	base = dup;
1587 	token = dup;
1588 	lasts = NULL;
1589 	while (token != NULL && ret == SA_OK) {
1590 		ret = SA_OK;
1591 		token = strtok_r(base, ",", &lasts);
1592 		base = NULL;
1593 		if (token != NULL) {
1594 			char *value;
1595 			/*
1596 			 * All SMB properties have values so there
1597 			 * MUST be an '=' character.  If it doesn't,
1598 			 * it is a syntax error.
1599 			 */
1600 			value = strchr(token, '=');
1601 			if (value != NULL) {
1602 				*value++ = '\0';
1603 			} else {
1604 				ret = SA_SYNTAX_ERR;
1605 				break;
1606 			}
1607 			/*
1608 			 * We may need to handle a "name" property
1609 			 * that is a ZFS imposed resource name. Each
1610 			 * name would trigger getting a new "resource"
1611 			 * to put properties on. For now, assume no
1612 			 * "name" property for special handling.
1613 			 */
1614 
1615 			if (strcmp(token, "name") == 0) {
1616 				char *prefix;
1617 				char *name = NULL;
1618 				/*
1619 				 * We have a name, so now work on the
1620 				 * resource level. We have a "share"
1621 				 * in "group" due to the caller having
1622 				 * added it. If we are called with a
1623 				 * group, the check for group/share
1624 				 * at the beginning of this function
1625 				 * will bail out the parse if there is a
1626 				 * "name" but no share.
1627 				 */
1628 				if (!iszfs) {
1629 					ret = SA_SYNTAX_ERR;
1630 					break;
1631 				}
1632 				/*
1633 				 * Make sure the parent group has the
1634 				 * "prefix" property since we will
1635 				 * need to use this for constructing
1636 				 * inherited name= values.
1637 				 */
1638 				prefix = sa_get_group_attr(parent, "prefix");
1639 				if (prefix == NULL) {
1640 					prefix = sa_get_group_attr(parent,
1641 					    "name");
1642 					if (prefix != NULL) {
1643 						(void) sa_set_group_attr(parent,
1644 						    "prefix", prefix);
1645 					}
1646 				}
1647 				name = fix_resource_name((sa_share_t)group,
1648 				    value, prefix);
1649 				if (name != NULL) {
1650 					resource = sa_add_resource(
1651 					    (sa_share_t)group, name,
1652 					    SA_SHARE_TRANSIENT, &ret);
1653 					sa_free_attr_string(name);
1654 				} else {
1655 					ret = SA_NO_MEMORY;
1656 				}
1657 				if (prefix != NULL)
1658 					sa_free_attr_string(prefix);
1659 
1660 				/* A resource level optionset is needed */
1661 
1662 				need_optionset = 1;
1663 				if (resource == NULL) {
1664 					ret = SA_NO_MEMORY;
1665 					break;
1666 				}
1667 				continue;
1668 			}
1669 
1670 			if (need_optionset) {
1671 				optionset = sa_create_optionset(resource,
1672 				    "smb");
1673 				need_optionset = 0;
1674 			}
1675 
1676 			prop = sa_create_property(token, value);
1677 			if (prop == NULL)
1678 				ret = SA_NO_MEMORY;
1679 			else
1680 				ret = sa_add_property(optionset, prop);
1681 			if (ret != SA_OK)
1682 				break;
1683 			if (!iszfs)
1684 				ret = sa_commit_properties(optionset, !persist);
1685 		}
1686 	}
1687 	free(dup);
1688 	return (ret);
1689 }
1690 
1691 /*
1692  * smb_sprint_option(rbuff, rbuffsize, incr, prop, sep)
1693  *
1694  * provides a mechanism to format SMB properties into legacy output
1695  * format. If the buffer would overflow, it is reallocated and grown
1696  * as appropriate. Special cases of converting internal form of values
1697  * to those used by "share" are done. this function does one property
1698  * at a time.
1699  */
1700 
1701 static void
1702 smb_sprint_option(char **rbuff, size_t *rbuffsize, size_t incr,
1703 			sa_property_t prop, int sep)
1704 {
1705 	char *name;
1706 	char *value;
1707 	int curlen;
1708 	char *buff = *rbuff;
1709 	size_t buffsize = *rbuffsize;
1710 
1711 	name = sa_get_property_attr(prop, "type");
1712 	value = sa_get_property_attr(prop, "value");
1713 	if (buff != NULL)
1714 		curlen = strlen(buff);
1715 	else
1716 		curlen = 0;
1717 	if (name != NULL) {
1718 		int len;
1719 		len = strlen(name) + sep;
1720 
1721 		/*
1722 		 * A future RFE would be to replace this with more
1723 		 * generic code and to possibly handle more types.
1724 		 *
1725 		 * For now, everything else is treated as a string. If
1726 		 * we get any properties that aren't exactly
1727 		 * name/value pairs, we may need to
1728 		 * interpret/transform.
1729 		 */
1730 		if (value != NULL)
1731 			len += 1 + strlen(value);
1732 
1733 		while (buffsize <= (curlen + len)) {
1734 			/* need more room */
1735 			buffsize += incr;
1736 			buff = realloc(buff, buffsize);
1737 			*rbuff = buff;
1738 			*rbuffsize = buffsize;
1739 			if (buff == NULL) {
1740 				/* realloc failed so free everything */
1741 				if (*rbuff != NULL)
1742 					free(*rbuff);
1743 				goto err;
1744 			}
1745 		}
1746 		if (buff == NULL)
1747 			goto err;
1748 		(void) snprintf(buff + curlen, buffsize - curlen,
1749 		    "%s%s=%s", sep ? "," : "",
1750 		    name, value != NULL ? value : "\"\"");
1751 
1752 	}
1753 err:
1754 	if (name != NULL)
1755 		sa_free_attr_string(name);
1756 	if (value != NULL)
1757 		sa_free_attr_string(value);
1758 }
1759 
1760 /*
1761  * smb_format_resource_options(resource, hier)
1762  *
1763  * format all the options on the group into a flattened option
1764  * string. If hier is non-zero, walk up the tree to get inherited
1765  * options.
1766  */
1767 
1768 static char *
1769 smb_format_options(sa_group_t group, int hier)
1770 {
1771 	sa_optionset_t options = NULL;
1772 	sa_property_t prop;
1773 	int sep = 0;
1774 	char *buff;
1775 	size_t buffsize;
1776 
1777 
1778 	buff = malloc(OPT_CHUNK);
1779 	if (buff == NULL)
1780 		return (NULL);
1781 
1782 	buff[0] = '\0';
1783 	buffsize = OPT_CHUNK;
1784 
1785 	/*
1786 	 * We may have a an optionset relative to this item. format
1787 	 * these if we find them and then add any security definitions.
1788 	 */
1789 
1790 	options = sa_get_derived_optionset(group, "smb", hier);
1791 
1792 	/*
1793 	 * do the default set first but skip any option that is also
1794 	 * in the protocol specific optionset.
1795 	 */
1796 	if (options != NULL) {
1797 		for (prop = sa_get_property(options, NULL);
1798 		    prop != NULL; prop = sa_get_next_property(prop)) {
1799 			/*
1800 			 * use this one since we skipped any
1801 			 * of these that were also in
1802 			 * optdefault
1803 			 */
1804 			smb_sprint_option(&buff, &buffsize, OPT_CHUNK,
1805 			    prop, sep);
1806 			if (buff == NULL) {
1807 				/*
1808 				 * buff could become NULL if there
1809 				 * isn't enough memory for
1810 				 * smb_sprint_option to realloc()
1811 				 * as necessary. We can't really
1812 				 * do anything about it at this
1813 				 * point so we return NULL.  The
1814 				 * caller should handle the
1815 				 * failure.
1816 				 */
1817 				if (options != NULL)
1818 					sa_free_derived_optionset(
1819 					    options);
1820 				return (buff);
1821 			}
1822 			sep = 1;
1823 		}
1824 	}
1825 
1826 	if (options != NULL)
1827 		sa_free_derived_optionset(options);
1828 	return (buff);
1829 }
1830 
1831 /*
1832  * smb_rename_resource(resource, newname)
1833  *
1834  * Change the current exported name of the resource to newname.
1835  */
1836 /*ARGSUSED*/
1837 int
1838 smb_rename_resource(sa_handle_t handle, sa_resource_t resource, char *newname)
1839 {
1840 	int ret = SA_OK;
1841 	int err;
1842 	char *oldname;
1843 
1844 	oldname = sa_get_resource_attr(resource, "name");
1845 	if (oldname == NULL)
1846 		return (SA_NO_SUCH_RESOURCE);
1847 
1848 	err = smb_share_ren(oldname, newname);
1849 
1850 	/* improve error values somewhat */
1851 	switch (err) {
1852 	case NERR_Success:
1853 		break;
1854 	case NERR_InternalError:
1855 		ret = SA_SYSTEM_ERR;
1856 		break;
1857 	case NERR_DuplicateShare:
1858 		ret = SA_DUPLICATE_NAME;
1859 		break;
1860 	default:
1861 		ret = SA_CONFIG_ERR;
1862 		break;
1863 	}
1864 
1865 	return (ret);
1866 }
1867