xref: /titanic_51/usr/src/lib/libshare/smbfs/libshare_smbfs.c (revision f770199a45a8893d2f1615ff4e2d13e041992dc3)
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 /*
28  * SMB specific functions
29  */
30 #include <stdio.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <zone.h>
36 #include <errno.h>
37 #include <locale.h>
38 #include <signal.h>
39 #include <fcntl.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <syslog.h>
43 #include "libshare.h"
44 #include "libshare_impl.h"
45 #include <pwd.h>
46 #include <limits.h>
47 #include <libscf.h>
48 #include <strings.h>
49 #include "libshare_smbfs.h"
50 #include <rpcsvc/daemon_utils.h>
51 #include <arpa/inet.h>
52 #include <uuid/uuid.h>
53 #include <netsmb/smb_lib.h>
54 
55 #define	SMBFS_PROTOCOL_NAME	"smbfs"
56 
57 /* internal functions */
58 static uint64_t smbfs_features();
59 static int smbfs_init();
60 static void smbfs_fini();
61 static int smbfs_set_proto_prop(sa_property_t);
62 static sa_protocol_properties_t smbfs_get_proto_set();
63 static char *smbfs_get_status();
64 static int smbfs_delete_section(char *);
65 static int smbfs_delete_property_group(char *);
66 
67 static int range_check_validator(int, char *, char *);
68 static int string_length_check_validator(int, char *, char *);
69 static int yes_no_validator(int, char *, char *);
70 static int ip_address_validator(int, char *, char *);
71 static int minauth_validator(int, char *, char *);
72 static int password_validator(int, char *, char *);
73 static int signing_validator(int, char *, char *);
74 
75 int propset_changed = 0;
76 
77 /*
78  * ops vector that provides the protocol specific info and operations
79  * for share management.
80  */
81 
82 struct sa_plugin_ops sa_plugin_ops = {
83 	SA_PLUGIN_VERSION,
84 	SMBFS_PROTOCOL_NAME,
85 	smbfs_init,
86 	smbfs_fini,
87 	NULL,	/* share */
88 	NULL,	/* unshare */
89 	NULL,	/* valid_prop */
90 	NULL,	/* valid_space */
91 	NULL,	/* security_prop */
92 	NULL,	/* legacy_opts */
93 	NULL,	/* legacy_format */
94 	smbfs_set_proto_prop,
95 	smbfs_get_proto_set,
96 	smbfs_get_status,
97 	NULL,	/* space_alias */
98 	NULL,	/* update_legacy */
99 	NULL,	/* delete_legacy */
100 	NULL,	/* change_notify */
101 	NULL,	/* enable_resource */
102 	NULL,	/* disable_resource */
103 	smbfs_features,
104 	NULL,	/* get_transient_shares */
105 	NULL,	/* notify_resource */
106 	NULL,	/* rename_resource */
107 	NULL,	/* run_command */
108 	NULL,	/* command_help */
109 	smbfs_delete_section,
110 };
111 
112 /*
113  * is_a_number(number)
114  *
115  * is the string a number in one of the forms we want to use?
116  */
117 
118 static int
119 is_a_number(char *number)
120 {
121 	int ret = 1;
122 	int hex = 0;
123 
124 	if (strncmp(number, "0x", 2) == 0) {
125 		number += 2;
126 		hex = 1;
127 	} else if (*number == '-') {
128 		number++; /* skip the minus */
129 	}
130 
131 	while (ret == 1 && *number != '\0') {
132 		if (hex) {
133 			ret = isxdigit(*number++);
134 		} else {
135 			ret = isdigit(*number++);
136 		}
137 	}
138 	return (ret);
139 }
140 
141 /*
142  * Protocol management functions
143  *
144  * properties defined in the default files are defined in
145  * proto_option_defs for parsing and validation.
146  */
147 
148 struct smbclnt_proto_option_defs smbclnt_proto_options[] = {
149 	{ "section", NULL, PROTO_OPT_SECTION,
150 	    0, 0, MAX_VALUE_BUFLEN,
151 	    string_length_check_validator},
152 	{ "addr", NULL, PROTO_OPT_ADDR,
153 	    0, 0, MAX_VALUE_BUFLEN,
154 	    ip_address_validator},
155 	{ "minauth", NULL, PROTO_OPT_MINAUTH,
156 	    0, 0, MAX_VALUE_BUFLEN,
157 	    minauth_validator},
158 	{ "nbns_broadcast", NULL, PROTO_OPT_NBNS_BROADCAST,
159 	    0, 0, 0,
160 	    yes_no_validator},
161 	{ "nbns_enable", NULL, PROTO_OPT_NBNS_ENABLE,
162 	    0, 0, 0,
163 	    yes_no_validator},
164 	{ "nbns", NULL, PROTO_OPT_NBNSADDR,
165 	    0, 0, MAX_VALUE_BUFLEN,
166 	    ip_address_validator},
167 	{ "password", NULL, PROTO_OPT_PASSWORD,
168 	    0, 0, MAX_VALUE_BUFLEN,
169 	    password_validator},
170 	{ "timeout", NULL, PROTO_OPT_TIMEOUT,
171 	    0, 0, 60,
172 	    range_check_validator},
173 	{ "user", NULL, PROTO_OPT_USER,
174 	    0, 0, MAX_VALUE_BUFLEN,
175 	    string_length_check_validator},
176 	{ "domain", NULL, PROTO_OPT_DOMAIN,
177 	    0, 0, MAX_VALUE_BUFLEN,
178 	    string_length_check_validator},
179 	{ "workgroup", NULL, PROTO_OPT_WORKGROUP,
180 	    0, 0, MAX_VALUE_BUFLEN,
181 	    string_length_check_validator},
182 	{ "signing", NULL, PROTO_OPT_SIGNING,
183 	    0, 0, MAX_VALUE_BUFLEN,
184 	    signing_validator},
185 	{NULL}
186 };
187 
188 /*
189  * Check the range of value as int range.
190  */
191 /*ARGSUSED*/
192 static int
193 range_check_validator(int index, char *section, char *value)
194 {
195 	int ret = SA_OK;
196 
197 	if (value == NULL)
198 		return (SA_BAD_VALUE);
199 	if (strlen(value) == 0)
200 		return (SA_OK);
201 	if (!is_a_number(value)) {
202 		ret = SA_BAD_VALUE;
203 	} else {
204 		int val;
205 		val = strtoul(value, NULL, 0);
206 		if (val < smbclnt_proto_options[index].minval ||
207 		    val > smbclnt_proto_options[index].maxval)
208 			ret = SA_BAD_VALUE;
209 	}
210 	return (ret);
211 }
212 
213 /*
214  * Check the length of the string
215  */
216 /*ARGSUSED*/
217 static int
218 string_length_check_validator(int index, char *section, char *value)
219 {
220 	int ret = SA_OK;
221 
222 	if (value == NULL)
223 		return (SA_BAD_VALUE);
224 	if (strlen(value) == 0)
225 		return (SA_OK);
226 	if (strlen(value) > smbclnt_proto_options[index].maxval)
227 		ret = SA_BAD_VALUE;
228 	return (ret);
229 }
230 
231 /*
232  * Check yes/no
233  */
234 /*ARGSUSED*/
235 static int
236 yes_no_validator(int index, char *section, char *value)
237 {
238 	if (value == NULL)
239 		return (SA_BAD_VALUE);
240 	if (strlen(value) == 0)
241 		return (SA_OK);
242 	if ((strcasecmp(value, "yes") == 0) ||
243 	    (strcasecmp(value, "no") == 0) ||
244 	    (strcasecmp(value, "true") == 0) ||
245 	    (strcasecmp(value, "false") == 0))
246 		return (SA_OK);
247 	return (SA_BAD_VALUE);
248 }
249 
250 /*
251  * Check IP address.
252  */
253 /*ARGSUSED*/
254 static int
255 ip_address_validator(int index, char *section, char *value)
256 {
257 	int len;
258 
259 	if (value == NULL)
260 		return (SA_BAD_VALUE);
261 	len = strlen(value);
262 	if (len == 0)
263 		return (SA_OK);
264 	if (len > MAX_VALUE_BUFLEN)
265 		return (SA_BAD_VALUE);
266 	return (SA_OK);
267 }
268 
269 /*ARGSUSED*/
270 static int
271 minauth_validator(int index, char *section, char *value)
272 {
273 	if (value == NULL)
274 		return (SA_BAD_VALUE);
275 	if (strlen(value) == 0)
276 		return (SA_OK);
277 	if (strcmp(value, "kerberos") == 0 ||
278 	    strcmp(value, "ntlmv2") == 0 ||
279 	    strcmp(value, "ntlm") == 0 ||
280 	    strcmp(value, "lm") == 0 ||
281 	    strcmp(value, "none") == 0)
282 		return (SA_OK);
283 	else
284 		return (SA_BAD_VALUE);
285 }
286 
287 /*ARGSUSED*/
288 static int
289 signing_validator(int index, char *section, char *value)
290 {
291 	if (value == NULL)
292 		return (SA_BAD_VALUE);
293 	if (strlen(value) == 0)
294 		return (SA_OK);
295 	if (strcmp(value, "disabled") == 0 ||
296 	    strcmp(value, "enabled") == 0 ||
297 	    strcmp(value, "required") == 0)
298 		return (SA_OK);
299 	else
300 		return (SA_BAD_VALUE);
301 }
302 
303 /*ARGSUSED*/
304 static int
305 password_validator(int index, char *section, char *value)
306 {
307 	char buffer[100];
308 
309 	/* mangled passwords will start with this pattern */
310 	if (strlen(value) == 0)
311 		return (SA_OK);
312 	if (strncmp(value, "$$1", 3) != 0)
313 		return (SA_PASSWORD_ENC);
314 	if (smb_simpledecrypt(buffer, value) != 0)
315 		return (SA_BAD_VALUE);
316 	return (SA_OK);
317 }
318 
319 
320 /*
321  * the protoset holds the defined options so we don't have to read
322  * them multiple times
323  */
324 sa_protocol_properties_t protoset;
325 
326 static int
327 findprotoopt(char *name)
328 {
329 	int i;
330 	for (i = 0; smbclnt_proto_options[i].name != NULL; i++) {
331 		if (strcasecmp(smbclnt_proto_options[i].name, name) == 0)
332 			return (i);
333 	}
334 	return (-1);
335 }
336 
337 /*
338  * Load the persistent settings from SMF.  Each section is an SMF
339  * property group with an "S-" prefix and a UUID, and the section
340  * is itself a property which can have a more flexible name than
341  * a property group name can have.  The section name need not be
342  * the first property, so we have to be a little flexible, but
343  * the change of name of the property groups is a reliable way
344  * to know that we're seeing a different section.
345  */
346 int
347 smbclnt_config_load()
348 {
349 	scf_simple_app_props_t *props = NULL;
350 	scf_simple_prop_t *prop = NULL, *lastprop = NULL;
351 	char *lastpgname = NULL, *pgname = NULL;
352 	char *name = NULL, *value = NULL;
353 	sa_property_t sect, node;
354 
355 	props = scf_simple_app_props_get(NULL, SMBC_DEFAULT_INSTANCE_FMRI);
356 	if (props == NULL)
357 		return (-1);
358 
359 	for (;;) {
360 		lastprop = prop;
361 		prop = (scf_simple_prop_t *)
362 		    scf_simple_app_props_next(props, lastprop);
363 		if (prop == NULL)
364 			break;
365 
366 		/* Ignore properties that don't have our prefix */
367 		pgname = scf_simple_prop_pgname(prop);
368 		if (strncmp("S-", pgname, 2) != 0)
369 			continue;
370 
371 		/*
372 		 * Note property group name changes, which mark sections
373 		 *
374 		 * The memory allocated by sa_create_section is
375 		 * linked into the list of children under protoset,
376 		 * and will eventually be freed via that list.
377 		 */
378 		if (lastpgname == NULL || strcmp(lastpgname, pgname) != 0) {
379 			sect = sa_create_section(NULL, pgname+2);
380 			(void) xmlSetProp(sect, (xmlChar *)"type",
381 			    (xmlChar *)SMBFS_PROTOCOL_NAME);
382 			(void) sa_add_protocol_property(protoset, sect);
383 			if (lastpgname)
384 				free(lastpgname);
385 			lastpgname = strdup(pgname);
386 		}
387 		name = scf_simple_prop_name(prop);
388 		value = scf_simple_prop_next_astring(prop);
389 
390 		/* If we get a section name, apply it and consume it */
391 		if (strncmp("section", name, 7) == 0 && value != NULL) {
392 			(void) xmlSetProp(sect, (xmlChar *)"name",
393 			    (xmlChar *)value);
394 			continue;
395 		}
396 
397 		/*
398 		 * We have an ordinary property.  Add to the section.
399 		 *
400 		 * The memory allocated by sa_create_property is
401 		 * linked into the list of children under "sect",
402 		 * and will eventually be freed via that list.
403 		 */
404 		node = sa_create_property(name, value);
405 		(void) sa_add_protocol_property(sect, node);
406 	}
407 	scf_simple_app_props_free(props);
408 
409 	if (lastpgname)
410 		free(lastpgname);
411 	return (0);
412 }
413 
414 /*
415  * Save the set of properties for a particular section, which is
416  * stored as a single property group.  Properties will have been
417  * changed earlier by one or more calls to smbfs_save_property(),
418  * which only set the value in our array and marked them as
419  * SMBC_MODIFIED.
420  */
421 int
422 smbfs_save_propset()
423 {
424 	smb_scfhandle_t *handle = NULL;
425 	char propgroup[256];
426 	char *section = smbclnt_proto_options[PROTO_OPT_SECTION].value;
427 	char *uu = NULL;
428 	uuid_t uuid;
429 	int i, ret = 0;
430 	sa_property_t propset;
431 	int new = 0, nonnull = 0;
432 
433 	propset = sa_get_protocol_section(protoset, section);
434 	(void) strlcpy(propgroup, SMBC_PG_PREFIX, sizeof (propgroup));
435 	propgroup[SMBC_PG_PREFIX_LEN] = '\0';
436 	uu = sa_get_property_attr(propset, "extra");
437 	if (uu != NULL) {
438 		(void) strlcat(propgroup, uu, sizeof (propgroup));
439 		free(uu);
440 	} else {
441 		new = 1;
442 		smbclnt_proto_options[PROTO_OPT_SECTION].flags |= SMBC_MODIFIED;
443 		uuid_generate(uuid);
444 		uuid_unparse(uuid, &propgroup[SMBC_PG_PREFIX_LEN]);
445 	}
446 
447 	handle = smb_smf_scf_init(SMBC_FMRI_PREFIX);
448 	if (handle == NULL) {
449 		return (1);
450 	}
451 
452 	if ((ret = smb_smf_instance_create(handle, SMBC_FMRI_PREFIX,
453 	    SMBC_PG_INSTANCE)) != SMBC_SMF_OK) {
454 		goto out;
455 	}
456 
457 	if ((ret = smb_smf_create_instance_pgroup(handle, propgroup))
458 	    != SMBC_SMF_OK) {
459 		goto out;
460 	}
461 
462 	if ((ret = smb_smf_start_transaction(handle)) != SMBC_SMF_OK) {
463 		goto out;
464 	}
465 
466 	for (i = PROTO_OPT_SECTION+1; i <= SMBC_OPT_MAX; i++) {
467 		if ((smbclnt_proto_options[i].flags & SMBC_MODIFIED) == 0)
468 			continue;
469 		if (strcmp(smbclnt_proto_options[i].value, "") == 0)
470 			ret = smb_smf_delete_property(handle,
471 			    smbclnt_proto_options[i].name);
472 		else {
473 			ret = smb_smf_set_string_property(handle,
474 			    smbclnt_proto_options[i].name,
475 			    smbclnt_proto_options[i].value);
476 			nonnull = 1;
477 		}
478 		free(smbclnt_proto_options[i].value);
479 		smbclnt_proto_options[i].value = NULL;
480 		smbclnt_proto_options[i].flags &= ~SMBC_MODIFIED;
481 		if (ret != SMBC_SMF_OK)
482 			goto outtrans;
483 	}
484 	/*
485 	 * Suppress new, null entries by not saving the section name.
486 	 */
487 	if (!new || nonnull) {
488 		ret = smb_smf_set_string_property(handle,
489 		    smbclnt_proto_options[PROTO_OPT_SECTION].name,
490 		    smbclnt_proto_options[PROTO_OPT_SECTION].value);
491 		free(smbclnt_proto_options[PROTO_OPT_SECTION].value);
492 		smbclnt_proto_options[PROTO_OPT_SECTION].value = NULL;
493 		smbclnt_proto_options[PROTO_OPT_SECTION].flags &=
494 		    ~SMBC_MODIFIED;
495 	}
496 	propset_changed = 0;
497 
498 outtrans:
499 	ret = smb_smf_end_transaction(handle);
500 out:
501 	smb_smf_scf_fini(handle);
502 	return (ret);
503 }
504 
505 /*
506  * initprotofromdefault()
507  *
508  * read the default file(s) and add the defined values to the
509  * protoset.  Note that default values are known from the built in
510  * table in case the file doesn't have a definition.
511  */
512 
513 static int
514 initprotofromdefault()
515 {
516 	protoset = sa_create_protocol_properties(SMBFS_PROTOCOL_NAME);
517 	if (protoset == NULL)
518 		return (SA_NO_MEMORY);
519 	if (smbclnt_config_load() != 0)
520 		return (SA_OK);
521 
522 	return (SA_OK);
523 }
524 
525 /*
526  *
527  * smbfs_features()
528  *
529  * Report the plugin's features
530  */
531 static uint64_t
532 smbfs_features()
533 {
534 	return (SA_FEATURE_HAS_SECTIONS | SA_FEATURE_ADD_PROPERTIES);
535 }
536 
537 /*
538  * smbfs_init()
539  *
540  * Initialize the smb plugin.
541  */
542 
543 static int
544 smbfs_init()
545 {
546 	int ret = SA_OK;
547 
548 	if (sa_plugin_ops.sa_init != smbfs_init) {
549 		return (SA_SYSTEM_ERR);
550 	}
551 
552 	if (initprotofromdefault() != SA_OK) {
553 		return (SA_SYSTEM_ERR);
554 	}
555 
556 	return (ret);
557 }
558 
559 /*
560  * smbfs_fini()
561  *
562  * uninitialize the smb plugin. Want to avoid memory leaks.
563  */
564 
565 static void
566 smbfs_fini()
567 {
568 	if (propset_changed)
569 		(void) smbfs_save_propset();
570 	xmlFreeNode(protoset);
571 	protoset = NULL;
572 }
573 
574 /*
575  * smbfs_get_proto_set()
576  *
577  * Return an optionset with all the protocol specific properties in
578  * it.
579  */
580 
581 static sa_protocol_properties_t
582 smbfs_get_proto_set()
583 {
584 	return (protoset);
585 }
586 
587 /*
588  * smbfs_validate_proto_prop(index, name, value)
589  *
590  * Verify that the property specifed by name can take the new
591  * value. This is a sanity check to prevent bad values getting into
592  * the default files.
593  */
594 static int
595 smbfs_validate_proto_prop(int index, char *section, char *name, char *value)
596 {
597 	if ((section == NULL) || (name == NULL) || (index < 0))
598 		return (SA_BAD_VALUE);
599 
600 	if (smbclnt_proto_options[index].validator == NULL)
601 		return (SA_OK);
602 
603 	return (smbclnt_proto_options[index].validator(index, section, value));
604 }
605 
606 /*
607  * Save a property to our array; it will be stored to SMF later by
608  * smbfs_save_propset().
609  */
610 int
611 smbfs_save_property(int index, char *section, char *value)
612 {
613 	char *s;
614 
615 	if (index == PROTO_OPT_WORKGROUP) {
616 		index = PROTO_OPT_DOMAIN;
617 	}
618 	propset_changed = 1;
619 	s = strdup(section);
620 	if (s == NULL)
621 		return (-1);
622 	smbclnt_proto_options[PROTO_OPT_SECTION].value = s;
623 	s = strdup(value);
624 	if (s == NULL)
625 		return (-1);
626 	smbclnt_proto_options[index].value = s;
627 	smbclnt_proto_options[index].flags |= SMBC_MODIFIED;
628 	return (0);
629 }
630 
631 /*
632  * smbfs_set_proto_prop(prop)
633  *
634  * check that prop is valid.
635  */
636 /*ARGSUSED*/
637 static int
638 smbfs_set_proto_prop(sa_property_t prop)
639 {
640 	int ret = SA_OK;
641 	char *name;
642 	char *value;
643 	char *section;
644 	int i = -1;
645 
646 	section = sa_get_property_attr(prop, "section");
647 	if (section == NULL)
648 		return (SA_NO_SECTION);
649 	name = sa_get_property_attr(prop, "type");
650 	value = sa_get_property_attr(prop, "value");
651 	if (name != NULL && value != NULL) {
652 		i = findprotoopt(name);
653 		if (i >= 0) {
654 			ret = smbfs_validate_proto_prop(i, section,
655 			    name, value);
656 			if (ret == SA_OK) {
657 				if (smbfs_save_property(i, section,
658 				    value) != 0) {
659 					ret = SA_SYSTEM_ERR;
660 					errno = EIO;
661 				}
662 			}
663 		} else
664 			ret = SA_INVALID_NAME;
665 	}
666 	if (name != NULL)
667 		sa_free_attr_string(name);
668 	if (value != NULL)
669 		sa_free_attr_string(value);
670 	if (section != NULL)
671 		sa_free_attr_string(section);
672 
673 	return (ret);
674 }
675 
676 /*
677  * smbfs_get_status()
678  *
679  * What is the current status of the smbd? We use the SMF state here.
680  * Caller must free the returned value.
681  */
682 
683 static char *
684 smbfs_get_status()
685 {
686 	return (smf_get_state(SMBC_DEFAULT_INSTANCE_FMRI));
687 }
688 
689 /*
690  * Delete a section by its name, which we will have read into an
691  * XML optionset above.  We need to find it and find its UUID to
692  * be able to generate the property group name in order to call
693  * smbfs_delete_property_group().
694  */
695 static int
696 smbfs_delete_section(char *section)
697 {
698 	char propgroup[256];
699 	char *uu = NULL;
700 	sa_property_t propset;
701 	int ret = SA_SYSTEM_ERR;
702 
703 	propset = sa_get_protocol_section(protoset, section);
704 	(void) strlcpy(propgroup, SMBC_PG_PREFIX, sizeof (propgroup));
705 	propgroup[SMBC_PG_PREFIX_LEN] = '\0';
706 	uu = sa_get_property_attr(propset, "extra");
707 	if (uu == NULL)
708 		goto out;
709 	(void) strlcat(propgroup, uu, sizeof (propgroup));
710 	free(uu);
711 	if ((ret = smbfs_delete_property_group(propgroup)) != SMBC_SMF_OK)
712 		goto out;
713 	ret = SA_OK;
714 out:
715 	return (ret);
716 }
717 
718 /*
719  * Delete a property group by its name.  Called to do a 'delsect'
720  * or called when smbclnt_config_load() notices an empty section
721  * at the end of the properties.
722  */
723 static int
724 smbfs_delete_property_group(char *propgroup)
725 {
726 	smb_scfhandle_t *handle = NULL;
727 	int ret = SA_SYSTEM_ERR;
728 
729 	handle = smb_smf_scf_init(SMBC_FMRI_PREFIX);
730 	if (handle == NULL)
731 		goto out;
732 
733 	if ((ret = smb_smf_instance_create(handle, SMBC_FMRI_PREFIX,
734 	    SMBC_PG_INSTANCE)) != SMBC_SMF_OK)
735 		goto out;
736 
737 	if ((ret = smb_smf_delete_instance_pgroup(handle, propgroup))
738 	    != SMBC_SMF_OK)
739 		goto out;
740 	ret = SA_OK;
741 out:
742 	smb_smf_scf_fini(handle);
743 	return (ret);
744 }
745