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