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 }