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