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 }