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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2016 Nexenta Systems, Inc.
  25  * Copyright (c) 2014, 2016 by Delphix. All rights reserved.
  26  */
  27 
  28 /*
  29  * NFS specific functions
  30  */
  31 #include <stdio.h>
  32 #include <string.h>
  33 #include <ctype.h>
  34 #include <stdlib.h>
  35 #include <unistd.h>
  36 #include <zone.h>
  37 #include <errno.h>
  38 #include <locale.h>
  39 #include <signal.h>
  40 #include <strings.h>
  41 #include "libshare.h"
  42 #include "libshare_impl.h"
  43 #include <nfs/export.h>
  44 #include <pwd.h>
  45 #include <grp.h>
  46 #include <limits.h>
  47 #include <libscf.h>
  48 #include <syslog.h>
  49 #include <rpcsvc/daemon_utils.h>
  50 #include "nfslog_config.h"
  51 #include "nfslogtab.h"
  52 #include "libshare_nfs.h"
  53 #include <nfs/nfs.h>
  54 #include <nfs/nfssys.h>
  55 #include <netconfig.h>
  56 #include "smfcfg.h"
  57 
  58 /* should really be in some global place */
  59 #define DEF_WIN 30000
  60 #define OPT_CHUNK       1024
  61 
  62 int debug = 0;
  63 
  64 #define NFS_SERVER_SVC  "svc:/network/nfs/server:default"
  65 #define NFS_CLIENT_SVC  (char *)"svc:/network/nfs/client:default"
  66 
  67 /* internal functions */
  68 static int nfs_init();
  69 static void nfs_fini();
  70 static int nfs_enable_share(sa_share_t);
  71 static int nfs_disable_share(sa_share_t, char *);
  72 static int nfs_validate_property(sa_handle_t, sa_property_t, sa_optionset_t);
  73 static int nfs_validate_security_mode(char *);
  74 static int nfs_is_security_opt(char *);
  75 static int nfs_parse_legacy_options(sa_group_t, char *);
  76 static char *nfs_format_options(sa_group_t, int);
  77 static int nfs_set_proto_prop(sa_property_t);
  78 static sa_protocol_properties_t nfs_get_proto_set();
  79 static char *nfs_get_status();
  80 static char *nfs_space_alias(char *);
  81 static uint64_t nfs_features();
  82 
  83 /*
  84  * ops vector that provides the protocol specific info and operations
  85  * for share management.
  86  */
  87 
  88 struct sa_plugin_ops sa_plugin_ops = {
  89         SA_PLUGIN_VERSION,
  90         "nfs",
  91         nfs_init,
  92         nfs_fini,
  93         nfs_enable_share,
  94         nfs_disable_share,
  95         nfs_validate_property,
  96         nfs_validate_security_mode,
  97         nfs_is_security_opt,
  98         nfs_parse_legacy_options,
  99         nfs_format_options,
 100         nfs_set_proto_prop,
 101         nfs_get_proto_set,
 102         nfs_get_status,
 103         nfs_space_alias,
 104         NULL,   /* update_legacy */
 105         NULL,   /* delete_legacy */
 106         NULL,   /* change_notify */
 107         NULL,   /* enable_resource */
 108         NULL,   /* disable_resource */
 109         nfs_features,
 110         NULL,   /* transient shares */
 111         NULL,   /* notify resource */
 112         NULL,   /* rename_resource */
 113         NULL,   /* run_command */
 114         NULL,   /* command_help */
 115         NULL    /* delete_proto_section */
 116 };
 117 
 118 /*
 119  * list of support services needed
 120  * defines should come from head/rpcsvc/daemon_utils.h
 121  */
 122 
 123 static char *service_list_default[] =
 124         { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, REPARSED, NULL };
 125 static char *service_list_logging[] =
 126         { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NFSLOGD, REPARSED,
 127             NULL };
 128 
 129 /*
 130  * option definitions.  Make sure to keep the #define for the option
 131  * index just before the entry it is the index for. Changing the order
 132  * can cause breakage.  E.g OPT_RW is index 1 and must precede the
 133  * line that includes the SHOPT_RW and OPT_RW entries.
 134  */
 135 
 136 struct option_defs optdefs[] = {
 137 #define OPT_RO          0
 138         {SHOPT_RO, OPT_RO, OPT_TYPE_ACCLIST},
 139 #define OPT_RW          1
 140         {SHOPT_RW, OPT_RW, OPT_TYPE_ACCLIST},
 141 #define OPT_ROOT        2
 142         {SHOPT_ROOT, OPT_ROOT, OPT_TYPE_ACCLIST},
 143 #define OPT_SECURE      3
 144         {SHOPT_SECURE, OPT_SECURE, OPT_TYPE_DEPRECATED},
 145 #define OPT_ANON        4
 146         {SHOPT_ANON, OPT_ANON, OPT_TYPE_USER},
 147 #define OPT_WINDOW      5
 148         {SHOPT_WINDOW, OPT_WINDOW, OPT_TYPE_NUMBER},
 149 #define OPT_NOSUID      6
 150         {SHOPT_NOSUID, OPT_NOSUID, OPT_TYPE_BOOLEAN},
 151 #define OPT_ACLOK       7
 152         {SHOPT_ACLOK, OPT_ACLOK, OPT_TYPE_BOOLEAN},
 153 #define OPT_NOSUB       8
 154         {SHOPT_NOSUB, OPT_NOSUB, OPT_TYPE_BOOLEAN},
 155 #define OPT_SEC         9
 156         {SHOPT_SEC, OPT_SEC, OPT_TYPE_SECURITY},
 157 #define OPT_PUBLIC      10
 158         {SHOPT_PUBLIC, OPT_PUBLIC, OPT_TYPE_BOOLEAN, OPT_SHARE_ONLY},
 159 #define OPT_INDEX       11
 160         {SHOPT_INDEX, OPT_INDEX, OPT_TYPE_FILE},
 161 #define OPT_LOG         12
 162         {SHOPT_LOG, OPT_LOG, OPT_TYPE_LOGTAG},
 163 #define OPT_CKSUM       13
 164         {SHOPT_CKSUM, OPT_CKSUM, OPT_TYPE_STRINGSET},
 165 #define OPT_NONE        14
 166         {SHOPT_NONE, OPT_NONE, OPT_TYPE_ACCLIST},
 167 #define OPT_ROOT_MAPPING        15
 168         {SHOPT_ROOT_MAPPING, OPT_ROOT_MAPPING, OPT_TYPE_USER},
 169 #define OPT_CHARSET_MAP 16
 170         {"", OPT_CHARSET_MAP, OPT_TYPE_ACCLIST},
 171 #define OPT_NOACLFAB    17
 172         {SHOPT_NOACLFAB, OPT_NOACLFAB, OPT_TYPE_BOOLEAN},
 173 #define OPT_UIDMAP      18
 174         {SHOPT_UIDMAP, OPT_UIDMAP, OPT_TYPE_MAPPING},
 175 #define OPT_GIDMAP      19
 176         {SHOPT_GIDMAP, OPT_GIDMAP, OPT_TYPE_MAPPING},
 177 #define OPT_NOHIDE      20
 178         {SHOPT_NOHIDE, OPT_NOHIDE, OPT_TYPE_BOOLEAN},
 179 #ifdef VOLATILE_FH_TEST /* XXX added for testing volatile fh's only */
 180 #define OPT_VOLFH       21
 181         {SHOPT_VOLFH, OPT_VOLFH},
 182 #endif /* VOLATILE_FH_TEST */
 183         NULL
 184 };
 185 
 186 /*
 187  * Codesets that may need to be converted to UTF-8 for file paths.
 188  * Add new names here to add new property support. If we ever get a
 189  * way to query the kernel for character sets, this should become
 190  * dynamically loaded. Make sure changes here are reflected in
 191  * cmd/fs.d/nfs/mountd/nfscmd.c
 192  */
 193 
 194 static char *legal_conv[] = {
 195         "euc-cn",
 196         "euc-jp",
 197         "euc-jpms",
 198         "euc-kr",
 199         "euc-tw",
 200         "iso8859-1",
 201         "iso8859-2",
 202         "iso8859-5",
 203         "iso8859-6",
 204         "iso8859-7",
 205         "iso8859-8",
 206         "iso8859-9",
 207         "iso8859-13",
 208         "iso8859-15",
 209         "koi8-r",
 210         NULL
 211 };
 212 
 213 /*
 214  * list of properties that are related to security flavors.
 215  */
 216 static char *seclist[] = {
 217         SHOPT_RO,
 218         SHOPT_RW,
 219         SHOPT_ROOT,
 220         SHOPT_WINDOW,
 221         SHOPT_NONE,
 222         SHOPT_ROOT_MAPPING,
 223         SHOPT_UIDMAP,
 224         SHOPT_GIDMAP,
 225         NULL
 226 };
 227 
 228 /* structure for list of securities */
 229 struct securities {
 230         sa_security_t security;
 231         struct securities *next;
 232 };
 233 
 234 /*
 235  * findcharset(charset)
 236  *
 237  * Returns B_TRUE if the charset is a legal conversion otherwise
 238  * B_FALSE. This will need to be rewritten to be more efficient when
 239  * we have a dynamic list of legal conversions.
 240  */
 241 
 242 static boolean_t
 243 findcharset(char *charset)
 244 {
 245         int i;
 246 
 247         for (i = 0; legal_conv[i] != NULL; i++)
 248                 if (strcmp(charset, legal_conv[i]) == 0)
 249                         return (B_TRUE);
 250         return (B_FALSE);
 251 }
 252 
 253 /*
 254  * findopt(name)
 255  *
 256  * Lookup option "name" in the option table and return the table
 257  * index.
 258  */
 259 
 260 static int
 261 findopt(char *name)
 262 {
 263         int i;
 264         if (name != NULL) {
 265                 for (i = 0; optdefs[i].tag != NULL; i++) {
 266                         if (strcmp(optdefs[i].tag, name) == 0)
 267                                 return (optdefs[i].index);
 268                 }
 269                 if (findcharset(name))
 270                         return (OPT_CHARSET_MAP);
 271         }
 272         return (-1);
 273 }
 274 
 275 /*
 276  * gettype(name)
 277  *
 278  * Return the type of option "name".
 279  */
 280 
 281 static int
 282 gettype(char *name)
 283 {
 284         int optdef;
 285 
 286         optdef = findopt(name);
 287         if (optdef != -1)
 288                 return (optdefs[optdef].type);
 289         return (OPT_TYPE_ANY);
 290 }
 291 
 292 /*
 293  * nfs_validate_security_mode(mode)
 294  *
 295  * is the specified mode string a valid one for use with NFS?
 296  */
 297 
 298 static int
 299 nfs_validate_security_mode(char *mode)
 300 {
 301         seconfig_t secinfo;
 302         int err;
 303 
 304         (void) memset(&secinfo, '\0', sizeof (secinfo));
 305         err = nfs_getseconfig_byname(mode, &secinfo);
 306         if (err == SC_NOERROR)
 307                 return (1);
 308         return (0);
 309 }
 310 
 311 /*
 312  * nfs_is_security_opt(tok)
 313  *
 314  * check to see if tok represents an option that is only valid in some
 315  * security flavor.
 316  */
 317 
 318 static int
 319 nfs_is_security_opt(char *tok)
 320 {
 321         int i;
 322 
 323         for (i = 0; seclist[i] != NULL; i++) {
 324                 if (strcmp(tok, seclist[i]) == 0)
 325                         return (1);
 326         }
 327         return (0);
 328 }
 329 
 330 /*
 331  * find_security(seclist, sec)
 332  *
 333  * Walk the current list of security flavors and return true if it is
 334  * present, else return false.
 335  */
 336 
 337 static int
 338 find_security(struct securities *seclist, sa_security_t sec)
 339 {
 340         while (seclist != NULL) {
 341                 if (seclist->security == sec)
 342                         return (1);
 343                 seclist = seclist->next;
 344         }
 345         return (0);
 346 }
 347 
 348 /*
 349  * make_security_list(group, securitymodes, proto)
 350  *      go through the list of securitymodes and add them to the
 351  *      group's list of security optionsets. We also keep a list of
 352  *      those optionsets so we don't have to find them later. All of
 353  *      these will get copies of the same properties.
 354  */
 355 
 356 static struct securities *
 357 make_security_list(sa_group_t group, char *securitymodes, char *proto)
 358 {
 359         char *tok, *next = NULL;
 360         struct securities *curp, *headp = NULL, *prev;
 361         sa_security_t check;
 362         int freetok = 0;
 363 
 364         for (tok = securitymodes; tok != NULL; tok = next) {
 365                 next = strchr(tok, ':');
 366                 if (next != NULL)
 367                         *next++ = '\0';
 368                 if (strcmp(tok, "default") == 0) {
 369                         /* resolve default into the real type */
 370                         tok = nfs_space_alias(tok);
 371                         freetok = 1;
 372                 }
 373                 check = sa_get_security(group, tok, proto);
 374 
 375                 /* add to the security list if it isn't there already */
 376                 if (check == NULL || !find_security(headp, check)) {
 377                         curp = (struct securities *)calloc(1,
 378                             sizeof (struct securities));
 379                         if (curp != NULL) {
 380                                 if (check == NULL) {
 381                                         curp->security = sa_create_security(
 382                                             group, tok, proto);
 383                                 } else {
 384                                         curp->security = check;
 385                                 }
 386                                 /*
 387                                  * note that the first time through the loop,
 388                                  * headp will be NULL and prev will be
 389                                  * undefined.  Since headp is NULL, we set
 390                                  * both it and prev to the curp (first
 391                                  * structure to be allocated).
 392                                  *
 393                                  * later passes through the loop will have
 394                                  * headp not being NULL and prev will be used
 395                                  * to allocate at the end of the list.
 396                                  */
 397                                 if (headp == NULL) {
 398                                         headp = curp;
 399                                         prev = curp;
 400                                 } else {
 401                                         prev->next = curp;
 402                                         prev = curp;
 403                                 }
 404                         }
 405                 }
 406 
 407                 if (freetok) {
 408                         freetok = 0;
 409                         sa_free_attr_string(tok);
 410                 }
 411         }
 412         return (headp);
 413 }
 414 
 415 static void
 416 free_security_list(struct securities *sec)
 417 {
 418         struct securities *next;
 419         if (sec != NULL) {
 420                 for (next = sec->next; sec != NULL; sec = next) {
 421                         next = sec->next;
 422                         free(sec);
 423                 }
 424         }
 425 }
 426 
 427 /*
 428  * nfs_alistcat(str1, str2, sep)
 429  *
 430  * concatenate str1 and str2 into a new string using sep as a separate
 431  * character. If memory allocation fails, return NULL;
 432  */
 433 
 434 static char *
 435 nfs_alistcat(char *str1, char *str2, char sep)
 436 {
 437         char *newstr;
 438         size_t len;
 439 
 440         len = strlen(str1) + strlen(str2) + 2;
 441         newstr = (char *)malloc(len);
 442         if (newstr != NULL)
 443                 (void) snprintf(newstr, len, "%s%c%s", str1, sep, str2);
 444         return (newstr);
 445 }
 446 
 447 /*
 448  * add_security_prop(sec, name, value, persist, iszfs)
 449  *
 450  * Add the property to the securities structure. This accumulates
 451  * properties for as part of parsing legacy options.
 452  */
 453 
 454 static int
 455 add_security_prop(struct securities *sec, char *name, char *value,
 456     int persist, int iszfs)
 457 {
 458         sa_property_t prop;
 459         int ret = SA_OK;
 460 
 461         for (; sec != NULL; sec = sec->next) {
 462                 if (value == NULL) {
 463                         if (strcmp(name, SHOPT_RW) == 0 ||
 464                             strcmp(name, SHOPT_RO) == 0)
 465                                 value = "*";
 466                         else
 467                                 value = "true";
 468                 }
 469 
 470                 /*
 471                  * Get the existing property, if it exists, so we can
 472                  * determine what to do with it. The ro/rw/root
 473                  * properties can be merged if multiple instances of
 474                  * these properies are given. For example, if "rw"
 475                  * exists with a value "host1" and a later token of
 476                  * rw="host2" is seen, the values are merged into a
 477                  * single rw="host1:host2".
 478                  */
 479                 prop = sa_get_property(sec->security, name);
 480 
 481                 if (prop != NULL) {
 482                         char *oldvalue;
 483                         char *newvalue;
 484 
 485                         /*
 486                          * The security options of ro/rw/root/uidmap/gidmap
 487                          * might appear multiple times.  If they do, the values
 488                          * need to be merged.  If it was previously empty, the
 489                          * new value alone is added.
 490                          */
 491                         oldvalue = sa_get_property_attr(prop, "value");
 492                         if (oldvalue != NULL) {
 493                                 char sep = ':';
 494 
 495                                 if (strcmp(name, SHOPT_UIDMAP) == 0 ||
 496                                     strcmp(name, SHOPT_GIDMAP) == 0)
 497                                         sep = '~';
 498 
 499                                 /*
 500                                  * The general case is to concatenate the new
 501                                  * value onto the old value for multiple
 502                                  * rw(ro/root/uidmap/gidmap) properties.  For
 503                                  * rw/ro/root a special case exists when either
 504                                  * the old or new is the "all" case.  In the
 505                                  * special case, if both are "all", then it is
 506                                  * "all", else if one is an access-list, that
 507                                  * replaces the "all".
 508                                  */
 509                                 if (strcmp(oldvalue, "*") == 0) {
 510                                         /* Replace old value with new value. */
 511                                         newvalue = strdup(value);
 512                                 } else if (strcmp(value, "*") == 0 ||
 513                                     strcmp(oldvalue, value) == 0) {
 514                                         /*
 515                                          * Keep old value and ignore
 516                                          * the new value.
 517                                          */
 518                                         newvalue = NULL;
 519                                 } else {
 520                                         /*
 521                                          * Make a new list of old plus new
 522                                          * access-list.
 523                                          */
 524                                         newvalue = nfs_alistcat(oldvalue,
 525                                             value, sep);
 526                                 }
 527 
 528                                 if (newvalue != NULL) {
 529                                         (void) sa_remove_property(prop);
 530                                         prop = sa_create_property(name,
 531                                             newvalue);
 532                                         ret = sa_add_property(sec->security,
 533                                             prop);
 534                                         free(newvalue);
 535                                 }
 536 
 537                                 sa_free_attr_string(oldvalue);
 538                         }
 539                 } else {
 540                         prop = sa_create_property(name, value);
 541                         ret = sa_add_property(sec->security, prop);
 542                 }
 543                 if (ret == SA_OK && !iszfs) {
 544                         ret = sa_commit_properties(sec->security, !persist);
 545                 }
 546         }
 547         return (ret);
 548 }
 549 
 550 /*
 551  * check to see if group/share is persistent.
 552  */
 553 static int
 554 is_persistent(sa_group_t group)
 555 {
 556         char *type;
 557         int persist = 1;
 558 
 559         type = sa_get_group_attr(group, "type");
 560         if (type != NULL && strcmp(type, "persist") != 0)
 561                 persist = 0;
 562         if (type != NULL)
 563                 sa_free_attr_string(type);
 564         return (persist);
 565 }
 566 
 567 /*
 568  * invalid_security(options)
 569  *
 570  * search option string for any invalid sec= type.
 571  * return true (1) if any are not valid else false (0)
 572  */
 573 static int
 574 invalid_security(char *options)
 575 {
 576         char *copy, *base, *token, *value;
 577         int ret = 0;
 578 
 579         copy = strdup(options);
 580         token = base = copy;
 581         while (token != NULL && ret == 0) {
 582                 token = strtok(base, ",");
 583                 base = NULL;
 584                 if (token != NULL) {
 585                         value = strchr(token, '=');
 586                         if (value != NULL)
 587                                 *value++ = '\0';
 588                         if (strcmp(token, SHOPT_SEC) == 0) {
 589                                 /* HAVE security flavors so check them */
 590                                 char *tok, *next;
 591                                 for (next = NULL, tok = value; tok != NULL;
 592                                     tok = next) {
 593                                         next = strchr(tok, ':');
 594                                         if (next != NULL)
 595                                                 *next++ = '\0';
 596                                         ret = !nfs_validate_security_mode(tok);
 597                                         if (ret)
 598                                                 break;
 599                                 }
 600                         }
 601                 }
 602         }
 603         if (copy != NULL)
 604                 free(copy);
 605         return (ret);
 606 }
 607 
 608 /*
 609  * nfs_parse_legacy_options(group, options)
 610  *
 611  * Parse the old style options into internal format and store on the
 612  * specified group.  Group could be a share for full legacy support.
 613  */
 614 
 615 static int
 616 nfs_parse_legacy_options(sa_group_t group, char *options)
 617 {
 618         char *dup;
 619         char *base;
 620         char *token;
 621         sa_optionset_t optionset;
 622         struct securities *security_list = NULL;
 623         sa_property_t prop;
 624         int ret = SA_OK;
 625         int iszfs = 0;
 626         sa_group_t parent;
 627         int persist = 0;
 628         char *lasts;
 629 
 630         /* do we have an existing optionset? */
 631         optionset = sa_get_optionset(group, "nfs");
 632         if (optionset == NULL) {
 633                 /* didn't find existing optionset so create one */
 634                 optionset = sa_create_optionset(group, "nfs");
 635         } else {
 636                 /*
 637                  * Have an existing optionset . Ideally, we would need
 638                  * to compare options in order to detect errors. For
 639                  * now, we assume that the first optionset is the
 640                  * correct one and the others will be the same. An
 641                  * empty optionset is the same as no optionset so we
 642                  * don't want to exit in that case. Getting an empty
 643                  * optionset can occur with ZFS property checking.
 644                  */
 645                 if (sa_get_property(optionset, NULL) != NULL)
 646                         return (ret);
 647         }
 648 
 649         if (strcmp(options, SHOPT_RW) == 0) {
 650                 /*
 651                  * there is a special case of only the option "rw"
 652                  * being the default option. We don't have to do
 653                  * anything.
 654                  */
 655                 return (ret);
 656         }
 657 
 658         /*
 659          * check if security types are present and validate them. If
 660          * any are not legal, fail.
 661          */
 662 
 663         if (invalid_security(options)) {
 664                 return (SA_INVALID_SECURITY);
 665         }
 666 
 667         /*
 668          * in order to not attempt to change ZFS properties unless
 669          * absolutely necessary, we never do it in the legacy parsing.
 670          */
 671         if (sa_is_share(group)) {
 672                 char *zfs;
 673                 parent = sa_get_parent_group(group);
 674                 if (parent != NULL) {
 675                         zfs = sa_get_group_attr(parent, "zfs");
 676                         if (zfs != NULL) {
 677                                 sa_free_attr_string(zfs);
 678                                 iszfs++;
 679                         }
 680                 }
 681         } else {
 682                 iszfs = sa_group_is_zfs(group);
 683         }
 684 
 685         /* We need a copy of options for the next part. */
 686         dup = strdup(options);
 687         if (dup == NULL)
 688                 return (SA_NO_MEMORY);
 689 
 690         /*
 691          * we need to step through each option in the string and then
 692          * add either the option or the security option as needed. If
 693          * this is not a persistent share, don't commit to the
 694          * repository. If there is an error, we also want to abort the
 695          * processing and report it.
 696          */
 697         persist = is_persistent(group);
 698         base = dup;
 699         token = dup;
 700         lasts = NULL;
 701         while (token != NULL && ret == SA_OK) {
 702                 token = strtok_r(base, ",", &lasts);
 703                 base = NULL;
 704                 if (token != NULL) {
 705                         char *value;
 706                         /*
 707                          * if the option has a value, it will have an '=' to
 708                          * separate the name from the value. The following
 709                          * code will result in value != NULL and token
 710                          * pointing to just the name if there is a value.
 711                          */
 712                         value = strchr(token, '=');
 713                         if (value != NULL) {
 714                                 *value++ = '\0';
 715                         }
 716                         if (strcmp(token, SHOPT_SEC) == 0 ||
 717                             strcmp(token, SHOPT_SECURE) == 0) {
 718                                 /*
 719                                  * Once in security parsing, we only
 720                                  * do security. We do need to move
 721                                  * between the security node and the
 722                                  * toplevel. The security tag goes on
 723                                  * the root while the following ones
 724                                  * go on the security.
 725                                  */
 726                                 if (security_list != NULL) {
 727                                         /*
 728                                          * have an old list so close it and
 729                                          * start the new
 730                                          */
 731                                         free_security_list(security_list);
 732                                 }
 733                                 if (strcmp(token, SHOPT_SECURE) == 0) {
 734                                         value = "dh";
 735                                 } else {
 736                                         if (value == NULL) {
 737                                                 ret = SA_SYNTAX_ERR;
 738                                                 break;
 739                                         }
 740                                 }
 741                                 security_list = make_security_list(group,
 742                                     value, "nfs");
 743                         } else {
 744                                 /*
 745                                  * Note that the "old" syntax allowed a
 746                                  * default security model.  This must be
 747                                  * accounted for and internally converted to
 748                                  * "standard" security structure.
 749                                  */
 750                                 if (nfs_is_security_opt(token)) {
 751                                         if (security_list == NULL) {
 752                                                 /*
 753                                                  * need to have a
 754                                                  * security
 755                                                  * option. This will
 756                                                  * be "closed" when a
 757                                                  * defined "sec="
 758                                                  * option is
 759                                                  * seen. This is
 760                                                  * technically an
 761                                                  * error but will be
 762                                                  * allowed with
 763                                                  * warning.
 764                                                  */
 765                                                 security_list =
 766                                                     make_security_list(group,
 767                                                     "default",
 768                                                     "nfs");
 769                                         }
 770                                         if (security_list != NULL) {
 771                                                 ret = add_security_prop(
 772                                                     security_list, token,
 773                                                     value, persist, iszfs);
 774                                         } else {
 775                                                 ret = SA_NO_MEMORY;
 776                                         }
 777                                 } else {
 778                                         /* regular options */
 779                                         if (value == NULL) {
 780                                                 if (strcmp(token, SHOPT_RW) ==
 781                                                     0 || strcmp(token,
 782                                                     SHOPT_RO) == 0) {
 783                                                         value = "*";
 784                                                 } else {
 785                                                         value = "global";
 786                                                         if (strcmp(token,
 787                                                             SHOPT_LOG) != 0) {
 788                                                                 value = "true";
 789                                                         }
 790                                                 }
 791                                         }
 792                                         /*
 793                                          * In all cases, create the
 794                                          * property specified. If the
 795                                          * value was NULL, the default
 796                                          * value will have been
 797                                          * substituted.
 798                                          */
 799                                         prop = sa_create_property(token, value);
 800                                         ret =  sa_add_property(optionset, prop);
 801                                         if (ret != SA_OK)
 802                                                 break;
 803 
 804                                         if (!iszfs) {
 805                                                 ret = sa_commit_properties(
 806                                                     optionset, !persist);
 807                                         }
 808                                 }
 809                         }
 810                 }
 811         }
 812         if (security_list != NULL)
 813                 free_security_list(security_list);
 814 
 815         free(dup);
 816         return (ret);
 817 }
 818 
 819 /*
 820  * is_a_number(number)
 821  *
 822  * is the string a number in one of the forms we want to use?
 823  */
 824 
 825 static int
 826 is_a_number(char *number)
 827 {
 828         int ret = 1;
 829         int hex = 0;
 830 
 831         if (strncmp(number, "0x", 2) == 0) {
 832                 number += 2;
 833                 hex = 1;
 834         } else if (*number == '-') {
 835                 number++; /* skip the minus */
 836         }
 837         while (ret == 1 && *number != '\0') {
 838                 if (hex) {
 839                         ret = isxdigit(*number++);
 840                 } else {
 841                         ret = isdigit(*number++);
 842                 }
 843         }
 844         return (ret);
 845 }
 846 
 847 /*
 848  * Look for the specified tag in the configuration file. If it is found,
 849  * enable logging and set the logging configuration information for exp.
 850  */
 851 static void
 852 configlog(struct exportdata *exp, char *tag)
 853 {
 854         nfsl_config_t *configlist = NULL, *configp;
 855         int error = 0;
 856         char globaltag[] = DEFAULTTAG;
 857 
 858         /*
 859          * Sends config errors to stderr
 860          */
 861         nfsl_errs_to_syslog = B_FALSE;
 862 
 863         /*
 864          * get the list of configuration settings
 865          */
 866         error = nfsl_getconfig_list(&configlist);
 867         if (error) {
 868                 (void) fprintf(stderr,
 869                     dgettext(TEXT_DOMAIN, "Cannot get log configuration: %s\n"),
 870                     strerror(error));
 871         }
 872 
 873         if (tag == NULL)
 874                 tag = globaltag;
 875         if ((configp = nfsl_findconfig(configlist, tag, &error)) == NULL) {
 876                 nfsl_freeconfig_list(&configlist);
 877                 (void) fprintf(stderr,
 878                     dgettext(TEXT_DOMAIN, "No tags matching \"%s\"\n"), tag);
 879                 /* bad configuration */
 880                 error = ENOENT;
 881                 goto err;
 882         }
 883 
 884         if ((exp->ex_tag = strdup(tag)) == NULL) {
 885                 error = ENOMEM;
 886                 goto out;
 887         }
 888         if ((exp->ex_log_buffer = strdup(configp->nc_bufferpath)) == NULL) {
 889                 error = ENOMEM;
 890                 goto out;
 891         }
 892         exp->ex_flags |= EX_LOG;
 893         if (configp->nc_rpclogpath != NULL)
 894                 exp->ex_flags |= EX_LOG_ALLOPS;
 895 out:
 896         if (configlist != NULL)
 897                 nfsl_freeconfig_list(&configlist);
 898 
 899 err:
 900         if (error != 0) {
 901                 free(exp->ex_tag);
 902                 free(exp->ex_log_buffer);
 903                 (void) fprintf(stderr,
 904                     dgettext(TEXT_DOMAIN, "Cannot set log configuration: %s\n"),
 905                     strerror(error));
 906         }
 907 }
 908 
 909 /*
 910  * fill_export_from_optionset(export, optionset)
 911  *
 912  * In order to share, we need to set all the possible general options
 913  * into the export structure. Share info will be filled in by the
 914  * caller. Various property values get turned into structure specific
 915  * values.
 916  */
 917 
 918 static int
 919 fill_export_from_optionset(struct exportdata *export, sa_optionset_t optionset)
 920 {
 921         sa_property_t option;
 922         int ret = SA_OK;
 923 
 924         for (option = sa_get_property(optionset, NULL);
 925             option != NULL; option = sa_get_next_property(option)) {
 926                 char *name;
 927                 char *value;
 928                 uint32_t val;
 929 
 930                 /*
 931                  * since options may be set/reset multiple times, always do an
 932                  * explicit set or clear of the option. This allows defaults
 933                  * to be set and then the protocol specific to override.
 934                  */
 935 
 936                 name = sa_get_property_attr(option, "type");
 937                 value = sa_get_property_attr(option, "value");
 938                 switch (findopt(name)) {
 939                 case OPT_ANON:
 940                         if (value != NULL && is_a_number(value)) {
 941                                 val = strtoul(value, NULL, 0);
 942                         } else {
 943                                 struct passwd *pw;
 944                                 pw = getpwnam(value != NULL ? value : "nobody");
 945                                 if (pw != NULL) {
 946                                         val = pw->pw_uid;
 947                                 } else {
 948                                         val = UID_NOBODY;
 949                                 }
 950                                 endpwent();
 951                         }
 952                         export->ex_anon = val;
 953                         break;
 954                 case OPT_NOSUID:
 955                         if (value != NULL && (strcasecmp(value, "true") == 0 ||
 956                             strcmp(value, "1") == 0))
 957                                 export->ex_flags |= EX_NOSUID;
 958                         else
 959                                 export->ex_flags &= ~EX_NOSUID;
 960                         break;
 961                 case OPT_ACLOK:
 962                         if (value != NULL && (strcasecmp(value, "true") == 0 ||
 963                             strcmp(value, "1") == 0))
 964                                 export->ex_flags |= EX_ACLOK;
 965                         else
 966                                 export->ex_flags &= ~EX_ACLOK;
 967                         break;
 968                 case OPT_NOSUB:
 969                         if (value != NULL && (strcasecmp(value, "true") == 0 ||
 970                             strcmp(value, "1") == 0))
 971                                 export->ex_flags |= EX_NOSUB;
 972                         else
 973                                 export->ex_flags &= ~EX_NOSUB;
 974                         break;
 975                 case OPT_PUBLIC:
 976                         if (value != NULL && (strcasecmp(value, "true") == 0 ||
 977                             strcmp(value, "1") == 0))
 978                                 export->ex_flags |= EX_PUBLIC;
 979                         else
 980                                 export->ex_flags &= ~EX_PUBLIC;
 981                         break;
 982                 case OPT_INDEX:
 983                         if (value != NULL && (strcmp(value, "..") == 0 ||
 984                             strchr(value, '/') != NULL)) {
 985                                 /* this is an error */
 986                                 (void) printf(dgettext(TEXT_DOMAIN,
 987                                     "NFS: index=\"%s\" not valid;"
 988                                     "must be a filename.\n"),
 989                                     value);
 990                                 break;
 991                         }
 992                         if (value != NULL && *value != '\0' &&
 993                             strcmp(value, ".") != 0) {
 994                                 /* valid index file string */
 995                                 if (export->ex_index != NULL) {
 996                                         /* left over from "default" */
 997                                         free(export->ex_index);
 998                                 }
 999                                 /* remember to free */
1000                                 export->ex_index = strdup(value);
1001                                 if (export->ex_index == NULL) {
1002                                         (void) printf(dgettext(TEXT_DOMAIN,
1003                                             "NFS: out of memory setting "
1004                                             "index property\n"));
1005                                         break;
1006                                 }
1007                                 export->ex_flags |= EX_INDEX;
1008                         }
1009                         break;
1010                 case OPT_LOG:
1011                         if (value == NULL)
1012                                 value = strdup("global");
1013                         if (value != NULL)
1014                                 configlog(export,
1015                                     strlen(value) ? value : "global");
1016                         break;
1017                 case OPT_CHARSET_MAP:
1018                         /*
1019                          * Set EX_CHARMAP when there is at least one
1020                          * charmap conversion property. This will get
1021                          * checked by the nfs server when it needs to.
1022                          */
1023                         export->ex_flags |= EX_CHARMAP;
1024                         break;
1025                 case OPT_NOACLFAB:
1026                         if (value != NULL && (strcasecmp(value, "true") == 0 ||
1027                             strcmp(value, "1") == 0))
1028                                 export->ex_flags |= EX_NOACLFAB;
1029                         else
1030                                 export->ex_flags &= ~EX_NOACLFAB;
1031                         break;
1032                 case OPT_NOHIDE:
1033                         if (value != NULL && (strcasecmp(value, "true") == 0 ||
1034                             strcmp(value, "1") == 0))
1035                                 export->ex_flags |= EX_NOHIDE;
1036                         else
1037                                 export->ex_flags &= ~EX_NOHIDE;
1038 
1039                         break;
1040                 default:
1041                         /* have a syntactic error */
1042                         (void) printf(dgettext(TEXT_DOMAIN,
1043                             "NFS: unrecognized option %s=%s\n"),
1044                             name != NULL ? name : "",
1045                             value != NULL ? value : "");
1046                         break;
1047                 }
1048                 if (name != NULL)
1049                         sa_free_attr_string(name);
1050                 if (value != NULL)
1051                         sa_free_attr_string(value);
1052         }
1053         return (ret);
1054 }
1055 
1056 /*
1057  * cleanup_export(export)
1058  *
1059  * Cleanup the allocated areas so we don't leak memory
1060  */
1061 
1062 static void
1063 cleanup_export(struct exportdata *export)
1064 {
1065         int i;
1066 
1067         free(export->ex_index);
1068 
1069         for (i = 0; i < export->ex_seccnt; i++) {
1070                 struct secinfo *s = &export->ex_secinfo[i];
1071 
1072                 while (s->s_rootcnt > 0)
1073                         free(s->s_rootnames[--s->s_rootcnt]);
1074 
1075                 free(s->s_rootnames);
1076         }
1077         free(export->ex_secinfo);
1078 }
1079 
1080 /*
1081  * Given a seconfig entry and a colon-separated
1082  * list of names, allocate an array big enough
1083  * to hold the root list, then convert each name to
1084  * a principal name according to the security
1085  * info and assign it to an array element.
1086  * Return the array and its size.
1087  */
1088 static caddr_t *
1089 get_rootnames(seconfig_t *sec, char *list, int *count)
1090 {
1091         caddr_t *a;
1092         int c, i;
1093         char *host, *p;
1094 
1095         /*
1096          * Count the number of strings in the list.
1097          * This is the number of colon separators + 1.
1098          */
1099         c = 1;
1100         for (p = list; *p; p++)
1101                 if (*p == ':')
1102                         c++;
1103         *count = c;
1104 
1105         a = (caddr_t *)malloc(c * sizeof (char *));
1106         if (a == NULL) {
1107                 (void) printf(dgettext(TEXT_DOMAIN,
1108                     "get_rootnames: no memory\n"));
1109                 *count = 0;
1110         } else {
1111                 for (i = 0; i < c; i++) {
1112                         host = strtok(list, ":");
1113                         if (!nfs_get_root_principal(sec, host, &a[i])) {
1114                                 while (i > 0)
1115                                         free(a[--i]);
1116                                 free(a);
1117                                 a = NULL;
1118                                 *count = 0;
1119                                 break;
1120                         }
1121                         list = NULL;
1122                 }
1123         }
1124 
1125         return (a);
1126 }
1127 
1128 /*
1129  * fill_security_from_secopts(sp, secopts)
1130  *
1131  * Fill the secinfo structure from the secopts optionset.
1132  */
1133 
1134 static int
1135 fill_security_from_secopts(struct secinfo *sp, sa_security_t secopts)
1136 {
1137         sa_property_t prop;
1138         char *type;
1139         int longform;
1140         int err = SC_NOERROR;
1141         uint32_t val;
1142 
1143         type = sa_get_security_attr(secopts, "sectype");
1144         if (type != NULL) {
1145                 /* named security type needs secinfo to be filled in */
1146                 err = nfs_getseconfig_byname(type, &sp->s_secinfo);
1147                 sa_free_attr_string(type);
1148                 if (err != SC_NOERROR)
1149                         return (err);
1150         } else {
1151                 /* default case */
1152                 err = nfs_getseconfig_default(&sp->s_secinfo);
1153                 if (err != SC_NOERROR)
1154                         return (err);
1155         }
1156 
1157         err = SA_OK;
1158         for (prop = sa_get_property(secopts, NULL);
1159             prop != NULL && err == SA_OK;
1160             prop = sa_get_next_property(prop)) {
1161                 char *name;
1162                 char *value;
1163 
1164                 name = sa_get_property_attr(prop, "type");
1165                 value = sa_get_property_attr(prop, "value");
1166 
1167                 longform = value != NULL && strcmp(value, "*") != 0;
1168 
1169                 switch (findopt(name)) {
1170                 case OPT_RO:
1171                         sp->s_flags |= longform ? M_ROL : M_RO;
1172                         break;
1173                 case OPT_RW:
1174                         sp->s_flags |= longform ? M_RWL : M_RW;
1175                         break;
1176                 case OPT_ROOT:
1177                         sp->s_flags |= M_ROOT;
1178                         /*
1179                          * if we are using AUTH_UNIX, handle like other things
1180                          * such as RO/RW
1181                          */
1182                         if (sp->s_secinfo.sc_rpcnum == AUTH_UNIX)
1183                                 break;
1184                         /* not AUTH_UNIX */
1185                         if (value != NULL) {
1186                                 sp->s_rootnames = get_rootnames(&sp->s_secinfo,
1187                                     value, &sp->s_rootcnt);
1188                                 if (sp->s_rootnames == NULL) {
1189                                         err = SA_BAD_VALUE;
1190                                         (void) fprintf(stderr,
1191                                             dgettext(TEXT_DOMAIN,
1192                                             "Bad root list\n"));
1193                                 }
1194                         }
1195                         break;
1196                 case OPT_NONE:
1197                         sp->s_flags |= M_NONE;
1198                         break;
1199                 case OPT_WINDOW:
1200                         if (value != NULL) {
1201                                 sp->s_window = atoi(value);
1202                                 /* just in case */
1203                                 if (sp->s_window < 0)
1204                                         sp->s_window = DEF_WIN;
1205                         }
1206                         break;
1207                 case OPT_ROOT_MAPPING:
1208                         if (value != NULL && is_a_number(value)) {
1209                                 val = strtoul(value, NULL, 0);
1210                         } else {
1211                                 struct passwd *pw;
1212                                 pw = getpwnam(value != NULL ? value : "nobody");
1213                                 if (pw != NULL) {
1214                                         val = pw->pw_uid;
1215                                 } else {
1216                                         val = UID_NOBODY;
1217                                 }
1218                                 endpwent();
1219                         }
1220                         sp->s_rootid = val;
1221                         break;
1222                 case OPT_UIDMAP:
1223                 case OPT_GIDMAP:
1224                         sp->s_flags |= M_MAP;
1225                         break;
1226                 default:
1227                         break;
1228                 }
1229                 if (name != NULL)
1230                         sa_free_attr_string(name);
1231                 if (value != NULL)
1232                         sa_free_attr_string(value);
1233         }
1234         /* if rw/ro options not set, use default of RW */
1235         if ((sp->s_flags & NFS_RWMODES) == 0)
1236                 sp->s_flags |= M_RW;
1237         return (err);
1238 }
1239 
1240 /*
1241  * This is for testing only
1242  * It displays the export structure that
1243  * goes into the kernel.
1244  */
1245 static void
1246 printarg(char *path, struct exportdata *ep)
1247 {
1248         int i, j;
1249         struct secinfo *sp;
1250 
1251         if (debug == 0)
1252                 return;
1253 
1254         (void) printf("%s:\n", path);
1255         (void) printf("\tex_version = %d\n", ep->ex_version);
1256         (void) printf("\tex_path = %s\n", ep->ex_path);
1257         (void) printf("\tex_pathlen = %ld\n", (ulong_t)ep->ex_pathlen);
1258         (void) printf("\tex_flags: (0x%02x) ", ep->ex_flags);
1259         if (ep->ex_flags & EX_NOSUID)
1260                 (void) printf("NOSUID ");
1261         if (ep->ex_flags & EX_ACLOK)
1262                 (void) printf("ACLOK ");
1263         if (ep->ex_flags & EX_PUBLIC)
1264                 (void) printf("PUBLIC ");
1265         if (ep->ex_flags & EX_NOSUB)
1266                 (void) printf("NOSUB ");
1267         if (ep->ex_flags & EX_LOG)
1268                 (void) printf("LOG ");
1269         if (ep->ex_flags & EX_CHARMAP)
1270                 (void) printf("CHARMAP ");
1271         if (ep->ex_flags & EX_LOG_ALLOPS)
1272                 (void) printf("LOG_ALLOPS ");
1273         if (ep->ex_flags == 0)
1274                 (void) printf("(none)");
1275         (void) printf("\n");
1276         if (ep->ex_flags & EX_LOG) {
1277                 (void) printf("\tex_log_buffer = %s\n",
1278                     (ep->ex_log_buffer ? ep->ex_log_buffer : "(NULL)"));
1279                 (void) printf("\tex_tag = %s\n",
1280                     (ep->ex_tag ? ep->ex_tag : "(NULL)"));
1281         }
1282         (void) printf("\tex_anon = %d\n", ep->ex_anon);
1283         (void) printf("\tex_seccnt = %d\n", ep->ex_seccnt);
1284         (void) printf("\n");
1285         for (i = 0; i < ep->ex_seccnt; i++) {
1286                 sp = &ep->ex_secinfo[i];
1287                 (void) printf("\t\ts_secinfo = %s\n", sp->s_secinfo.sc_name);
1288                 (void) printf("\t\ts_flags: (0x%02x) ", sp->s_flags);
1289                 if (sp->s_flags & M_ROOT) (void) printf("M_ROOT ");
1290                 if (sp->s_flags & M_RO) (void) printf("M_RO ");
1291                 if (sp->s_flags & M_ROL) (void) printf("M_ROL ");
1292                 if (sp->s_flags & M_RW) (void) printf("M_RW ");
1293                 if (sp->s_flags & M_RWL) (void) printf("M_RWL ");
1294                 if (sp->s_flags & M_NONE) (void) printf("M_NONE ");
1295                 if (sp->s_flags & M_MAP) (void) printf("M_MAP ");
1296                 if (sp->s_flags == 0) (void) printf("(none)");
1297                 (void) printf("\n");
1298                 (void) printf("\t\ts_window = %d\n", sp->s_window);
1299                 (void) printf("\t\ts_rootid = %d\n", sp->s_rootid);
1300                 (void) printf("\t\ts_rootcnt = %d ", sp->s_rootcnt);
1301                 (void) fflush(stdout);
1302                 for (j = 0; j < sp->s_rootcnt; j++)
1303                         (void) printf("%s ", sp->s_rootnames[j] ?
1304                             sp->s_rootnames[j] : "<null>");
1305                 (void) printf("\n\n");
1306         }
1307 }
1308 
1309 /*
1310  * count_security(opts)
1311  *
1312  * Count the number of security types (flavors). The optionset has
1313  * been populated with the security flavors as a holding mechanism.
1314  * We later use this number to allocate data structures.
1315  */
1316 
1317 static int
1318 count_security(sa_optionset_t opts)
1319 {
1320         int count = 0;
1321         sa_property_t prop;
1322         if (opts != NULL) {
1323                 for (prop = sa_get_property(opts, NULL); prop != NULL;
1324                     prop = sa_get_next_property(prop)) {
1325                         count++;
1326                 }
1327         }
1328         return (count);
1329 }
1330 
1331 /*
1332  * nfs_sprint_option(rbuff, rbuffsize, incr, prop, sep)
1333  *
1334  * provides a mechanism to format NFS properties into legacy output
1335  * format. If the buffer would overflow, it is reallocated and grown
1336  * as appropriate. Special cases of converting internal form of values
1337  * to those used by "share" are done. this function does one property
1338  * at a time.
1339  */
1340 
1341 static int
1342 nfs_sprint_option(char **rbuff, size_t *rbuffsize, size_t incr,
1343     sa_property_t prop, int sep)
1344 {
1345         char *name;
1346         char *value;
1347         int curlen;
1348         char *buff = *rbuff;
1349         size_t buffsize = *rbuffsize;
1350         int printed = B_FALSE;
1351 
1352         name = sa_get_property_attr(prop, "type");
1353         value = sa_get_property_attr(prop, "value");
1354         if (buff != NULL)
1355                 curlen = strlen(buff);
1356         else
1357                 curlen = 0;
1358         if (name != NULL) {
1359                 int len;
1360                 len = strlen(name) + sep;
1361 
1362                 /*
1363                  * A future RFE would be to replace this with more
1364                  * generic code and to possibly handle more types.
1365                  */
1366                 switch (gettype(name)) {
1367                 case OPT_TYPE_BOOLEAN:
1368                         /*
1369                          * For NFS, boolean value of FALSE means it
1370                          * doesn't show up in the option list at all.
1371                          */
1372                         if (value != NULL && strcasecmp(value, "false") == 0)
1373                                 goto skip;
1374                         if (value != NULL) {
1375                                 sa_free_attr_string(value);
1376                                 value = NULL;
1377                         }
1378                         break;
1379                 case OPT_TYPE_ACCLIST:
1380                         if (value != NULL && strcmp(value, "*") == 0) {
1381                                 sa_free_attr_string(value);
1382                                 value = NULL;
1383                         } else {
1384                                 if (value != NULL)
1385                                         len += 1 + strlen(value);
1386                         }
1387                         break;
1388                 case OPT_TYPE_LOGTAG:
1389                         if (value != NULL && strlen(value) == 0) {
1390                                 sa_free_attr_string(value);
1391                                 value = NULL;
1392                         } else {
1393                                 if (value != NULL)
1394                                         len += 1 + strlen(value);
1395                         }
1396                         break;
1397                 default:
1398                         if (value != NULL)
1399                                 len += 1 + strlen(value);
1400                         break;
1401                 }
1402                 while (buffsize <= (curlen + len)) {
1403                         /* need more room */
1404                         buffsize += incr;
1405                         buff = realloc(buff, buffsize);
1406                         if (buff == NULL) {
1407                                 /* realloc failed so free everything */
1408                                 if (*rbuff != NULL)
1409                                         free(*rbuff);
1410                         }
1411                         *rbuff = buff;
1412                         *rbuffsize = buffsize;
1413                         if (buff == NULL)
1414                                 goto skip;
1415 
1416                 }
1417 
1418                 if (buff == NULL)
1419                         goto skip;
1420 
1421                 if (value == NULL) {
1422                         (void) snprintf(buff + curlen, buffsize - curlen,
1423                             "%s%s", sep ? "," : "", name);
1424                 } else {
1425                         (void) snprintf(buff + curlen, buffsize - curlen,
1426                             "%s%s=%s", sep ? "," : "",
1427                             name, value != NULL ? value : "");
1428                 }
1429                 printed = B_TRUE;
1430         }
1431 skip:
1432         if (name != NULL)
1433                 sa_free_attr_string(name);
1434         if (value != NULL)
1435                 sa_free_attr_string(value);
1436         return (printed);
1437 }
1438 
1439 /*
1440  * nfs_format_options(group, hier)
1441  *
1442  * format all the options on the group into an old-style option
1443  * string. If hier is non-zero, walk up the tree to get inherited
1444  * options.
1445  */
1446 
1447 static char *
1448 nfs_format_options(sa_group_t group, int hier)
1449 {
1450         sa_optionset_t options = NULL;
1451         sa_optionset_t secoptions = NULL;
1452         sa_property_t prop, secprop;
1453         sa_security_t security = NULL;
1454         char *buff;
1455         size_t buffsize;
1456         char *sectype = NULL;
1457         int sep = 0;
1458 
1459 
1460         buff = malloc(OPT_CHUNK);
1461         if (buff == NULL) {
1462                 return (NULL);
1463         }
1464 
1465         buff[0] = '\0';
1466         buffsize = OPT_CHUNK;
1467 
1468         /*
1469          * We may have a an optionset relative to this item. format
1470          * these if we find them and then add any security definitions.
1471          */
1472 
1473         options = sa_get_derived_optionset(group, "nfs", hier);
1474 
1475         /*
1476          * do the default set first but skip any option that is also
1477          * in the protocol specific optionset.
1478          */
1479         if (options != NULL) {
1480                 for (prop = sa_get_property(options, NULL);
1481                     prop != NULL; prop = sa_get_next_property(prop)) {
1482                         /*
1483                          * use this one since we skipped any
1484                          * of these that were also in
1485                          * optdefault
1486                          */
1487                         if (nfs_sprint_option(&buff, &buffsize, OPT_CHUNK,
1488                             prop, sep))
1489                                 sep = 1;
1490                         if (buff == NULL) {
1491                                 /*
1492                                  * buff could become NULL if there
1493                                  * isn't enough memory for
1494                                  * nfs_sprint_option to realloc()
1495                                  * as necessary. We can't really
1496                                  * do anything about it at this
1497                                  * point so we return NULL.  The
1498                                  * caller should handle the
1499                                  * failure.
1500                                  */
1501                                 if (options != NULL)
1502                                         sa_free_derived_optionset(
1503                                             options);
1504                                 return (buff);
1505                         }
1506                 }
1507         }
1508         secoptions = (sa_optionset_t)sa_get_all_security_types(group,
1509             "nfs", hier);
1510         if (secoptions != NULL) {
1511                 for (secprop = sa_get_property(secoptions, NULL);
1512                     secprop != NULL;
1513                     secprop = sa_get_next_property(secprop)) {
1514                         sectype = sa_get_property_attr(secprop, "type");
1515                         security =
1516                             (sa_security_t)sa_get_derived_security(
1517                             group, sectype, "nfs", hier);
1518                         if (security != NULL) {
1519                                 if (sectype != NULL) {
1520                                         prop = sa_create_property(
1521                                             "sec", sectype);
1522                                         if (prop == NULL)
1523                                                 goto err;
1524                                         if (nfs_sprint_option(&buff,
1525                                             &buffsize, OPT_CHUNK, prop, sep))
1526                                                 sep = 1;
1527                                         (void) sa_remove_property(prop);
1528                                         if (buff == NULL)
1529                                                 goto err;
1530                                 }
1531                                 for (prop = sa_get_property(security,
1532                                     NULL); prop != NULL;
1533                                     prop = sa_get_next_property(prop)) {
1534                                         if (nfs_sprint_option(&buff,
1535                                             &buffsize, OPT_CHUNK, prop, sep))
1536                                                 sep = 1;
1537                                         if (buff == NULL)
1538                                                 goto err;
1539                                 }
1540                                 sa_free_derived_optionset(security);
1541                         }
1542                         if (sectype != NULL)
1543                                 sa_free_attr_string(sectype);
1544                 }
1545                 sa_free_derived_optionset(secoptions);
1546         }
1547 
1548         if (options != NULL)
1549                 sa_free_derived_optionset(options);
1550         return (buff);
1551 
1552 err:
1553         /*
1554          * If we couldn't allocate memory for option printing, we need
1555          * to break out of the nested loops, cleanup and return NULL.
1556          */
1557         if (secoptions != NULL)
1558                 sa_free_derived_optionset(secoptions);
1559         if (security != NULL)
1560                 sa_free_derived_optionset(security);
1561         if (sectype != NULL)
1562                 sa_free_attr_string(sectype);
1563         if (options != NULL)
1564                 sa_free_derived_optionset(options);
1565         return (buff);
1566 }
1567 
1568 /*
1569  * Append an entry to the nfslogtab file
1570  */
1571 static int
1572 nfslogtab_add(char *dir, char *buffer, char *tag)
1573 {
1574         FILE *f;
1575         struct logtab_ent lep;
1576         int error = 0;
1577 
1578         /*
1579          * Open the file for update and create it if necessary.
1580          * This may leave the I/O offset at the end of the file,
1581          * so rewind back to the beginning of the file.
1582          */
1583         f = fopen(NFSLOGTAB, "a+");
1584         if (f == NULL) {
1585                 error = errno;
1586                 goto out;
1587         }
1588         rewind(f);
1589 
1590         if (lockf(fileno(f), F_LOCK, 0L) < 0) {
1591                 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1592                     "share complete, however failed to lock %s "
1593                     "for update: %s\n"), NFSLOGTAB, strerror(errno));
1594                 error = -1;
1595                 goto out;
1596         }
1597 
1598         if (logtab_deactivate_after_boot(f) == -1) {
1599                 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1600                     "share complete, however could not deactivate "
1601                     "entries in %s\n"), NFSLOGTAB);
1602                 error = -1;
1603                 goto out;
1604         }
1605 
1606         /*
1607          * Remove entries matching buffer and sharepoint since we're
1608          * going to replace it with perhaps an entry with a new tag.
1609          */
1610         if (logtab_rement(f, buffer, dir, NULL, -1)) {
1611                 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1612                     "share complete, however could not remove matching "
1613                     "entries in %s\n"), NFSLOGTAB);
1614                 error = -1;
1615                 goto out;
1616         }
1617 
1618         /*
1619          * Deactivate all active entries matching this sharepoint
1620          */
1621         if (logtab_deactivate(f, NULL, dir, NULL)) {
1622                 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1623                     "share complete, however could not deactivate matching "
1624                     "entries in %s\n"), NFSLOGTAB);
1625                 error = -1;
1626                 goto out;
1627         }
1628 
1629         lep.le_buffer = buffer;
1630         lep.le_path = dir;
1631         lep.le_tag = tag;
1632         lep.le_state = LES_ACTIVE;
1633 
1634         /*
1635          * Add new sharepoint / buffer location to nfslogtab
1636          */
1637         if (logtab_putent(f, &lep) < 0) {
1638                 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1639                     "share complete, however could not add %s to %s\n"),
1640                     dir, NFSLOGTAB);
1641                 error = -1;
1642         }
1643 
1644 out:
1645         if (f != NULL)
1646                 (void) fclose(f);
1647         return (error);
1648 }
1649 
1650 /*
1651  * Deactivate an entry from the nfslogtab file
1652  */
1653 static int
1654 nfslogtab_deactivate(char *path)
1655 {
1656         FILE *f;
1657         int error = 0;
1658 
1659         f = fopen(NFSLOGTAB, "r+");
1660         if (f == NULL) {
1661                 error = errno;
1662                 goto out;
1663         }
1664         if (lockf(fileno(f), F_LOCK, 0L) < 0) {
1665                 error = errno;
1666                 (void)  fprintf(stderr, dgettext(TEXT_DOMAIN,
1667                     "share complete, however could not lock %s for "
1668                     "update: %s\n"), NFSLOGTAB, strerror(error));
1669                 goto out;
1670         }
1671         if (logtab_deactivate(f, NULL, path, NULL) == -1) {
1672                 error = -1;
1673                 (void) fprintf(stderr,
1674                     dgettext(TEXT_DOMAIN,
1675                     "share complete, however could not "
1676                     "deactivate %s in %s\n"), path, NFSLOGTAB);
1677                 goto out;
1678         }
1679 
1680 out:    if (f != NULL)
1681                 (void) fclose(f);
1682 
1683         return (error);
1684 }
1685 
1686 /*
1687  * check_public(group, skipshare)
1688  *
1689  * Check the group for any shares that have the public property
1690  * enabled. We skip "skipshare" since that is the one we are
1691  * working with. This is a separate function to make handling
1692  * subgroups simpler. Returns true if there is a share with public.
1693  */
1694 static int
1695 check_public(sa_group_t group, sa_share_t skipshare)
1696 {
1697         int exists = B_FALSE;
1698         sa_share_t share;
1699         sa_optionset_t opt;
1700         sa_property_t prop;
1701         char *shared;
1702 
1703         for (share = sa_get_share(group, NULL); share != NULL;
1704             share = sa_get_next_share(share)) {
1705                 if (share == skipshare)
1706                         continue;
1707 
1708                 opt = sa_get_optionset(share, "nfs");
1709                 if (opt == NULL)
1710                         continue;
1711                 prop = sa_get_property(opt, "public");
1712                 if (prop == NULL)
1713                         continue;
1714                 shared = sa_get_share_attr(share, "shared");
1715                 if (shared != NULL) {
1716                         exists = strcmp(shared, "true") == 0;
1717                         sa_free_attr_string(shared);
1718                         if (exists == B_TRUE)
1719                                 break;
1720                 }
1721         }
1722 
1723         return (exists);
1724 }
1725 
1726 /*
1727  * public_exists(handle, skipshare)
1728  *
1729  * check to see if public option is set on any other share than the
1730  * one specified. Need to check zfs sub-groups as well as the top
1731  * level groups.
1732  */
1733 static int
1734 public_exists(sa_handle_t handle, sa_share_t skipshare)
1735 {
1736         sa_group_t group = NULL;
1737 
1738         /*
1739          * If we don't have a handle, we can only do syntax check. We
1740          * can't check against other shares so we assume OK and will
1741          * catch the problem only when we actually try to apply it.
1742          */
1743         if (handle == NULL)
1744                 return (SA_OK);
1745 
1746         if (skipshare != NULL) {
1747                 group = sa_get_parent_group(skipshare);
1748                 if (group == NULL)
1749                         return (SA_NO_SUCH_GROUP);
1750         }
1751 
1752         for (group = sa_get_group(handle, NULL); group != NULL;
1753             group = sa_get_next_group(group)) {
1754                 /* Walk any ZFS subgroups as well as all standard groups */
1755                 if (sa_group_is_zfs(group)) {
1756                         sa_group_t subgroup;
1757                         for (subgroup = sa_get_sub_group(group);
1758                             subgroup != NULL;
1759                             subgroup = sa_get_next_group(subgroup)) {
1760                                 if (check_public(subgroup, skipshare))
1761                                         return (B_TRUE);
1762                         }
1763                 } else {
1764                         if (check_public(group, skipshare))
1765                                 return (B_TRUE);
1766                 }
1767         }
1768         return (B_FALSE);
1769 }
1770 
1771 /*
1772  * sa_enable_share at the protocol level, enable_share must tell the
1773  * implementation that it is to enable the share. This entails
1774  * converting the path and options into the appropriate ioctl
1775  * calls. It is assumed that all error checking of paths, etc. were
1776  * done earlier.
1777  */
1778 static int
1779 nfs_enable_share(sa_share_t share)
1780 {
1781         struct exportdata export;
1782         sa_optionset_t secoptlist;
1783         struct secinfo *sp;
1784         int num_secinfo;
1785         sa_optionset_t opt;
1786         sa_security_t sec;
1787         sa_property_t prop;
1788         char *path;
1789         int err = SA_OK;
1790         int i;
1791         int iszfs;
1792         sa_handle_t handle;
1793 
1794         /* Don't drop core if the NFS module isn't loaded. */
1795         (void) signal(SIGSYS, SIG_IGN);
1796 
1797         /* get the path since it is important in several places */
1798         path = sa_get_share_attr(share, "path");
1799         if (path == NULL)
1800                 return (SA_NO_SUCH_PATH);
1801 
1802         iszfs = sa_path_is_zfs(path);
1803         /*
1804          * find the optionsets and security sets.  There may not be
1805          * any or there could be one or two for each of optionset and
1806          * security may have multiple, one per security type per
1807          * protocol type.
1808          */
1809         opt = sa_get_derived_optionset(share, "nfs", 1);
1810         secoptlist = (sa_optionset_t)sa_get_all_security_types(share, "nfs", 1);
1811         if (secoptlist != NULL)
1812                 num_secinfo = MAX(1, count_security(secoptlist));
1813         else
1814                 num_secinfo = 1;
1815 
1816         /*
1817          * walk through the options and fill in the structure
1818          * appropriately.
1819          */
1820 
1821         (void) memset(&export, '\0', sizeof (export));
1822 
1823         /*
1824          * do non-security options first since there is only one after
1825          * the derived group is constructed.
1826          */
1827         export.ex_version = EX_CURRENT_VERSION;
1828         export.ex_anon = UID_NOBODY; /* this is our default value */
1829         export.ex_index = NULL;
1830         export.ex_path = path;
1831         export.ex_pathlen = strlen(path) + 1;
1832 
1833         if (opt != NULL)
1834                 err = fill_export_from_optionset(&export, opt);
1835 
1836         /*
1837          * check to see if "public" is set. If it is, then make sure
1838          * no other share has it set. If it is already used, fail.
1839          */
1840 
1841         handle = sa_find_group_handle((sa_group_t)share);
1842         if (export.ex_flags & EX_PUBLIC && public_exists(handle, share)) {
1843                 (void) printf(dgettext(TEXT_DOMAIN,
1844                     "NFS: Cannot share more than one file "
1845                     "system with 'public' property\n"));
1846                 err = SA_NOT_ALLOWED;
1847                 goto out;
1848         }
1849 
1850         sp = calloc(num_secinfo, sizeof (struct secinfo));
1851         if (sp == NULL) {
1852                 err = SA_NO_MEMORY;
1853                 (void) printf(dgettext(TEXT_DOMAIN,
1854                     "NFS: NFS: no memory for security\n"));
1855                 goto out;
1856         }
1857         export.ex_secinfo = sp;
1858         /* get default secinfo */
1859         export.ex_seccnt = num_secinfo;
1860         /*
1861          * since we must have one security option defined, we
1862          * init to the default and then override as we find
1863          * defined security options. This handles the case
1864          * where we have no defined options but we need to set
1865          * up one.
1866          */
1867         sp[0].s_window = DEF_WIN;
1868         sp[0].s_rootnames = NULL;
1869         /* setup a default in case no properties defined */
1870         if (nfs_getseconfig_default(&sp[0].s_secinfo)) {
1871                 (void) printf(dgettext(TEXT_DOMAIN,
1872                     "NFS: nfs_getseconfig_default: failed to "
1873                     "get default security mode\n"));
1874                 err = SA_CONFIG_ERR;
1875         }
1876         if (secoptlist != NULL) {
1877                 for (i = 0, prop = sa_get_property(secoptlist, NULL);
1878                     prop != NULL && i < num_secinfo;
1879                     prop = sa_get_next_property(prop), i++) {
1880                         char *sectype;
1881                         sectype = sa_get_property_attr(prop, "type");
1882                         /*
1883                          * if sectype is NULL, we probably
1884                          * have a memory problem and can't get
1885                          * the correct values. Rather than
1886                          * exporting with incorrect security,
1887                          * don't share it.
1888                          */
1889                         if (sectype == NULL) {
1890                                 err = SA_NO_MEMORY;
1891                                 (void) printf(dgettext(TEXT_DOMAIN,
1892                                     "NFS: Cannot share %s: "
1893                                     "no memory\n"), path);
1894                                 goto out;
1895                         }
1896                         sec = (sa_security_t)sa_get_derived_security(
1897                             share, sectype, "nfs", 1);
1898                         sp[i].s_window = DEF_WIN;
1899                         sp[i].s_rootcnt = 0;
1900                         sp[i].s_rootnames = NULL;
1901                         (void) fill_security_from_secopts(&sp[i], sec);
1902                         if (sec != NULL)
1903                                 sa_free_derived_security(sec);
1904                         if (sectype != NULL)
1905                                 sa_free_attr_string(sectype);
1906                 }
1907         }
1908         /*
1909          * when we get here, we can do the exportfs system call and
1910          * initiate things. We probably want to enable the
1911          * svc:/network/nfs/server service first if it isn't running.
1912          */
1913         /* check svc:/network/nfs/server status and start if needed */
1914         /* now add the share to the internal tables */
1915         printarg(path, &export);
1916         /*
1917          * call the exportfs system call which is implemented
1918          * via the nfssys() call as the EXPORTFS subfunction.
1919          */
1920         if (iszfs) {
1921                 struct exportfs_args ea;
1922                 share_t sh;
1923                 char *str;
1924                 priv_set_t *priv_effective;
1925                 int privileged;
1926 
1927                 /*
1928                  * If we aren't a privileged user
1929                  * and NFS server service isn't running
1930                  * then print out an error message
1931                  * and return EPERM
1932                  */
1933 
1934                 priv_effective = priv_allocset();
1935                 (void) getppriv(PRIV_EFFECTIVE, priv_effective);
1936 
1937                 privileged = (priv_isfullset(priv_effective) == B_TRUE);
1938                 priv_freeset(priv_effective);
1939 
1940                 if (!privileged &&
1941                     (str = smf_get_state(NFS_SERVER_SVC)) != NULL) {
1942                         err = 0;
1943                         if (strcmp(str, SCF_STATE_STRING_ONLINE) != 0) {
1944                                 (void) printf(dgettext(TEXT_DOMAIN,
1945                                     "NFS: Cannot share remote "
1946                                     "filesystem: %s\n"), path);
1947                                 (void) printf(dgettext(TEXT_DOMAIN,
1948                                     "NFS: Service needs to be enabled "
1949                                     "by a privileged user\n"));
1950                                 err = SA_SYSTEM_ERR;
1951                                 errno = EPERM;
1952                         }
1953                         free(str);
1954                 }
1955 
1956                 if (err == 0) {
1957                         ea.dname = path;
1958                         ea.uex = &export;
1959 
1960                         (void) sa_sharetab_fill_zfs(share, &sh, "nfs");
1961                         err = sa_share_zfs(share, NULL, path, &sh,
1962                             &ea, ZFS_SHARE_NFS);
1963                         if (err != SA_OK) {
1964                                 errno = err;
1965                                 err = -1;
1966                         }
1967                         sa_emptyshare(&sh);
1968                 }
1969         } else {
1970                 err = exportfs(path, &export);
1971         }
1972 
1973         if (err < 0) {
1974                 err = SA_SYSTEM_ERR;
1975                 switch (errno) {
1976                 case EREMOTE:
1977                         (void) printf(dgettext(TEXT_DOMAIN,
1978                             "NFS: Cannot share filesystems "
1979                             "in non-global zones: %s\n"), path);
1980                         err = SA_NOT_SUPPORTED;
1981                         break;
1982                 case EPERM:
1983                         if (getzoneid() != GLOBAL_ZONEID) {
1984                                 (void) printf(dgettext(TEXT_DOMAIN,
1985                                     "NFS: Cannot share file systems "
1986                                     "in non-global zones: %s\n"), path);
1987                                 err = SA_NOT_SUPPORTED;
1988                                 break;
1989                         }
1990                         err = SA_NO_PERMISSION;
1991                         break;
1992                 case EEXIST:
1993                         err = SA_SHARE_EXISTS;
1994                         break;
1995                 default:
1996                         break;
1997                 }
1998         } else {
1999                 /* update sharetab with an add/modify */
2000                 if (!iszfs) {
2001                         (void) sa_update_sharetab(share, "nfs");
2002                 }
2003         }
2004 
2005         if (err == SA_OK) {
2006                 /*
2007                  * enable services as needed. This should probably be
2008                  * done elsewhere in order to minimize the calls to
2009                  * check services.
2010                  */
2011                 /*
2012                  * check to see if logging and other services need to
2013                  * be triggered, but only if there wasn't an
2014                  * error. This is probably where sharetab should be
2015                  * updated with the NFS specific entry.
2016                  */
2017                 if (export.ex_flags & EX_LOG) {
2018                         /* enable logging */
2019                         if (nfslogtab_add(path, export.ex_log_buffer,
2020                             export.ex_tag) != 0) {
2021                                 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2022                                     "Could not enable logging for %s\n"),
2023                                     path);
2024                         }
2025                         _check_services(service_list_logging);
2026                 } else {
2027                         /*
2028                          * don't have logging so remove it from file. It might
2029                          * not be thre, but that doesn't matter.
2030                          */
2031                         (void) nfslogtab_deactivate(path);
2032                         _check_services(service_list_default);
2033                 }
2034         }
2035 
2036 out:
2037         if (path != NULL)
2038                 free(path);
2039 
2040         cleanup_export(&export);
2041         if (opt != NULL)
2042                 sa_free_derived_optionset(opt);
2043         if (secoptlist != NULL)
2044                 (void) sa_destroy_optionset(secoptlist);
2045         return (err);
2046 }
2047 
2048 /*
2049  * nfs_disable_share(share, path)
2050  *
2051  * Unshare the specified share. Note that "path" is the same path as
2052  * what is in the "share" object. It is passed in to avoid an
2053  * additional lookup. A missing "path" value makes this a no-op
2054  * function.
2055  */
2056 static int
2057 nfs_disable_share(sa_share_t share, char *path)
2058 {
2059         int err;
2060         int ret = SA_OK;
2061         int iszfs;
2062         sa_group_t parent;
2063         sa_handle_t handle;
2064 
2065         if (path == NULL)
2066                 return (ret);
2067 
2068         /*
2069          * If the share is in a ZFS group we need to handle it
2070          * differently.  Just being on a ZFS file system isn't
2071          * enough since we may be in a legacy share case.
2072          */
2073         parent = sa_get_parent_group(share);
2074         iszfs = sa_group_is_zfs(parent);
2075         if (iszfs) {
2076                 struct exportfs_args ea;
2077                 share_t sh = { 0 };
2078                 ea.dname = path;
2079                 ea.uex = NULL;
2080                 sh.sh_path = path;
2081                 sh.sh_fstype = "nfs";
2082 
2083                 err = sa_share_zfs(share, NULL, path, &sh,
2084                     &ea, ZFS_UNSHARE_NFS);
2085                 if (err != SA_OK) {
2086                         errno = err;
2087                         err = -1;
2088                 }
2089         } else {
2090                 err = exportfs(path, NULL);
2091         }
2092         if (err < 0) {
2093                 /*
2094                  * TBD: only an error in some
2095                  * cases - need better analysis
2096                  */
2097                 switch (errno) {
2098                 case EPERM:
2099                 case EACCES:
2100                         ret = SA_NO_PERMISSION;
2101                         if (getzoneid() != GLOBAL_ZONEID) {
2102                                 ret = SA_NOT_SUPPORTED;
2103                         }
2104                         break;
2105                 case EINVAL:
2106                 case ENOENT:
2107                         ret = SA_NO_SUCH_PATH;
2108                         break;
2109                 default:
2110                         ret = SA_SYSTEM_ERR;
2111                         break;
2112                 }
2113         }
2114         if (ret == SA_OK || ret == SA_NO_SUCH_PATH) {
2115                 handle = sa_find_group_handle((sa_group_t)share);
2116                 if (!iszfs)
2117                         (void) sa_delete_sharetab(handle, path, "nfs");
2118                 /* just in case it was logged */
2119                 (void) nfslogtab_deactivate(path);
2120         }
2121         return (ret);
2122 }
2123 
2124 static int
2125 check_user(char *value)
2126 {
2127         int ret = SA_OK;
2128 
2129         if (!is_a_number(value)) {
2130                 struct passwd *pw;
2131                 /*
2132                  * in this case it would have to be a
2133                  * user name
2134                  */
2135                 pw = getpwnam(value);
2136                 if (pw == NULL)
2137                         ret = SA_BAD_VALUE;
2138                 endpwent();
2139         } else {
2140                 uint64_t intval;
2141                 intval = strtoull(value, NULL, 0);
2142                 if (intval > UID_MAX && intval != -1)
2143                         ret = SA_BAD_VALUE;
2144         }
2145 
2146         return (ret);
2147 }
2148 
2149 static int
2150 check_group(char *value)
2151 {
2152         int ret = SA_OK;
2153 
2154         if (!is_a_number(value)) {
2155                 struct group *gr;
2156                 /*
2157                  * in this case it would have to be a
2158                  * group name
2159                  */
2160                 gr = getgrnam(value);
2161                 if (gr == NULL)
2162                         ret = SA_BAD_VALUE;
2163                 endgrent();
2164         } else {
2165                 uint64_t intval;
2166                 intval = strtoull(value, NULL, 0);
2167                 if (intval > UID_MAX && intval != -1)
2168                         ret = SA_BAD_VALUE;
2169         }
2170 
2171         return (ret);
2172 }
2173 
2174 /*
2175  * check_rorwnone(v1, v2, v3)
2176  *
2177  * check ro vs rw vs none values.  Over time this may get beefed up.
2178  * for now it just does simple checks. v1 is never NULL but v2 or v3
2179  * could be.
2180  */
2181 
2182 static int
2183 check_rorwnone(char *v1, char *v2, char *v3)
2184 {
2185         int ret = SA_OK;
2186         if (v2 != NULL && strcmp(v1, v2) == 0)
2187                 ret = SA_VALUE_CONFLICT;
2188         else if (v3 != NULL && strcmp(v1, v3) == 0)
2189                 ret = SA_VALUE_CONFLICT;
2190 
2191         return (ret);
2192 }
2193 
2194 /*
2195  * nfs_validate_property(handle, property, parent)
2196  *
2197  * Check that the property has a legitimate value for its type.
2198  */
2199 
2200 static int
2201 nfs_validate_property(sa_handle_t handle, sa_property_t property,
2202     sa_optionset_t parent)
2203 {
2204         int ret = SA_OK;
2205         char *propname;
2206         char *other1;
2207         char *other2;
2208         int optindex;
2209         nfsl_config_t *configlist;
2210         sa_group_t parent_group;
2211         char *value;
2212 
2213         propname = sa_get_property_attr(property, "type");
2214 
2215         if ((optindex = findopt(propname)) < 0)
2216                 ret = SA_NO_SUCH_PROP;
2217 
2218         /* need to validate value range here as well */
2219 
2220         if (ret == SA_OK) {
2221                 parent_group = sa_get_parent_group((sa_share_t)parent);
2222                 if (optdefs[optindex].share && parent_group != NULL &&
2223                     !sa_is_share(parent_group))
2224                         ret = SA_PROP_SHARE_ONLY;
2225         }
2226         if (ret == SA_OK) {
2227                 if (optdefs[optindex].index == OPT_PUBLIC) {
2228                         /*
2229                          * Public is special in that only one instance can
2230                          * be in the repository at the same time.
2231                          */
2232                         if (public_exists(handle, parent_group)) {
2233                                 sa_free_attr_string(propname);
2234                                 return (SA_VALUE_CONFLICT);
2235                         }
2236                 }
2237                 value = sa_get_property_attr(property, "value");
2238                 if (value != NULL) {
2239                         /* first basic type checking */
2240                         switch (optdefs[optindex].type) {
2241 
2242                         case OPT_TYPE_NUMBER:
2243                                 /* check that the value is all digits */
2244                                 if (!is_a_number(value))
2245                                         ret = SA_BAD_VALUE;
2246                                 break;
2247 
2248                         case OPT_TYPE_BOOLEAN:
2249                                 if (strlen(value) == 0 ||
2250                                     strcasecmp(value, "true") == 0 ||
2251                                     strcmp(value, "1") == 0 ||
2252                                     strcasecmp(value, "false") == 0 ||
2253                                     strcmp(value, "0") == 0) {
2254                                         ret = SA_OK;
2255                                 } else {
2256                                         ret = SA_BAD_VALUE;
2257                                 }
2258                                 break;
2259 
2260                         case OPT_TYPE_USER:
2261                                 ret = check_user(value);
2262                                 break;
2263 
2264                         case OPT_TYPE_FILE:
2265                                 if (strcmp(value, "..") == 0 ||
2266                                     strchr(value, '/') != NULL) {
2267                                         ret = SA_BAD_VALUE;
2268                                 }
2269                                 break;
2270 
2271                         case OPT_TYPE_ACCLIST: {
2272                                 sa_property_t oprop1;
2273                                 sa_property_t oprop2;
2274                                 char *ovalue1 = NULL;
2275                                 char *ovalue2 = NULL;
2276 
2277                                 if (parent == NULL)
2278                                         break;
2279                                 /*
2280                                  * access list handling. Should eventually
2281                                  * validate that all the values make sense.
2282                                  * Also, ro and rw may have cross value
2283                                  * conflicts.
2284                                  */
2285                                 if (strcmp(propname, SHOPT_RO) == 0) {
2286                                         other1 = SHOPT_RW;
2287                                         other2 = SHOPT_NONE;
2288                                 } else if (strcmp(propname, SHOPT_RW) == 0) {
2289                                         other1 = SHOPT_RO;
2290                                         other2 = SHOPT_NONE;
2291                                 } else if (strcmp(propname, SHOPT_NONE) == 0) {
2292                                         other1 = SHOPT_RO;
2293                                         other2 = SHOPT_RW;
2294                                 } else {
2295                                         other1 = NULL;
2296                                         other2 = NULL;
2297                                 }
2298                                 if (other1 == NULL && other2 == NULL)
2299                                         break;
2300 
2301                                 /* compare rw(ro) with ro(rw) */
2302 
2303                                 oprop1 = sa_get_property(parent, other1);
2304                                 oprop2 = sa_get_property(parent, other2);
2305                                 if (oprop1 == NULL && oprop2 == NULL)
2306                                         break;
2307                                 /*
2308                                  * Only potential confusion if other1
2309                                  * or other2 exists. Check the values
2310                                  * and run the check if there is a
2311                                  * value other than the one we are
2312                                  * explicitly looking at.
2313                                  */
2314                                 ovalue1 = sa_get_property_attr(oprop1, "value");
2315                                 ovalue2 = sa_get_property_attr(oprop2, "value");
2316                                 if (ovalue1 != NULL || ovalue2 != NULL)
2317                                         ret = check_rorwnone(value, ovalue1,
2318                                             ovalue2);
2319 
2320                                 if (ovalue1 != NULL)
2321                                         sa_free_attr_string(ovalue1);
2322                                 if (ovalue2 != NULL)
2323                                         sa_free_attr_string(ovalue2);
2324                                 break;
2325                         }
2326 
2327                         case OPT_TYPE_LOGTAG:
2328                                 if (nfsl_getconfig_list(&configlist) == 0) {
2329                                         int error;
2330                                         if (value == NULL ||
2331                                             strlen(value) == 0) {
2332                                                 if (value != NULL)
2333                                                         sa_free_attr_string(
2334                                                             value);
2335                                                 value = strdup("global");
2336                                         }
2337                                         if (value != NULL &&
2338                                             nfsl_findconfig(configlist, value,
2339                                             &error) == NULL) {
2340                                                 ret = SA_BAD_VALUE;
2341                                         }
2342                                         /* Must always free when done */
2343                                         nfsl_freeconfig_list(&configlist);
2344                                 } else {
2345                                         ret = SA_CONFIG_ERR;
2346                                 }
2347                                 break;
2348 
2349                         case OPT_TYPE_STRING:
2350                                 /* whatever is here should be ok */
2351                                 break;
2352 
2353                         case OPT_TYPE_SECURITY:
2354                                 /*
2355                                  * The "sec" property isn't used in the
2356                                  * non-legacy parts of sharemgr. We need to
2357                                  * reject it here. For legacy, it is pulled
2358                                  * out well before we get here.
2359                                  */
2360                                 ret = SA_NO_SUCH_PROP;
2361                                 break;
2362 
2363                         case OPT_TYPE_MAPPING: {
2364                                 char *p;
2365                                 char *n;
2366                                 char *c;
2367                                 int (*f)(char *);
2368 
2369                                 sa_security_t security;
2370 
2371                                 /*
2372                                  * mapping is only supported for sec=sys
2373                                  */
2374                                 ret = SA_CONFIG_ERR;
2375                                 if (parent_group == NULL)
2376                                         break;
2377 
2378                                 for (security = sa_get_security(parent_group,
2379                                     NULL, NULL); security != NULL;
2380                                     security = sa_get_next_security(security)) {
2381                                         char *type;
2382                                         char *sectype;
2383 
2384                                         type = sa_get_security_attr(security,
2385                                             "type");
2386                                         if (type == NULL)
2387                                                 continue;
2388 
2389                                         if (strcmp(type, "nfs") != 0) {
2390                                                 sa_free_attr_string(type);
2391                                                 continue;
2392                                         }
2393                                         sa_free_attr_string(type);
2394 
2395                                         sectype = sa_get_security_attr(security,
2396                                             "sectype");
2397                                         if (sectype == NULL)
2398                                                 continue;
2399 
2400                                         if (strcmp(sectype, "sys") != 0) {
2401                                                 sa_free_attr_string(sectype);
2402                                                 ret = SA_CONFIG_ERR;
2403                                                 break;
2404                                         }
2405                                         sa_free_attr_string(sectype);
2406                                         ret = SA_OK;
2407                                 }
2408 
2409                                 if (ret != SA_OK)
2410                                         break;
2411 
2412                                 assert(optindex == OPT_UIDMAP ||
2413                                     optindex == OPT_GIDMAP);
2414                                 f = optindex == OPT_UIDMAP ? check_user :
2415                                     check_group;
2416 
2417 
2418                                 p = strdup(value);
2419                                 if (p == NULL)
2420                                         ret = SA_BAD_VALUE;
2421 
2422                                 for (c = p; ret == SA_OK && c != NULL; c = n) {
2423                                         char *s;
2424                                         char *t;
2425 
2426                                         n = strchr(c, '~');
2427                                         if (n != NULL)
2428                                                 *n++ = '\0';
2429 
2430                                         s = strchr(c, ':');
2431                                         if (s != NULL) {
2432                                                 *s++ = '\0';
2433                                                 t = strchr(s, ':');
2434                                                 if (t != NULL)
2435                                                         *t = '\0';
2436                                         }
2437 
2438                                         if (s == NULL || t == NULL)
2439                                                 ret = SA_BAD_VALUE;
2440 
2441                                         if (ret == SA_OK && *c != '\0' &&
2442                                             strcmp(c, "*") != 0)
2443                                                 ret = f(c);
2444 
2445                                         if (ret == SA_OK && *s != '\0' &&
2446                                             strcmp(s, "-1") != 0)
2447                                                 ret = f(s);
2448                                 }
2449 
2450                                 free(p);
2451 
2452                                 break;
2453                         }
2454 
2455                         default:
2456                                 break;
2457                         }
2458 
2459                         if (value != NULL)
2460                                 sa_free_attr_string(value);
2461 
2462                         if (ret == SA_OK && optdefs[optindex].check != NULL) {
2463                                 /* do the property specific check */
2464                                 ret = optdefs[optindex].check(handle, property);
2465                         }
2466                 }
2467         }
2468 
2469         if (propname != NULL)
2470                 sa_free_attr_string(propname);
2471         return (ret);
2472 }
2473 
2474 /*
2475  * Protocol management functions
2476  *
2477  * Properties defined in the default files are defined in
2478  * proto_option_defs for parsing and validation. If "other" and
2479  * "compare" are set, then the value for this property should be
2480  * compared against the property specified in "other" using the
2481  * "compare" check (either <= or >=) in order to ensure that the
2482  * values are in the correct range.  E.g. setting server_versmin
2483  * higher than server_versmax should not be allowed.
2484  */
2485 
2486 struct proto_option_defs {
2487         char *tag;
2488         char *name;     /* display name -- remove protocol identifier */
2489         int index;
2490         int type;
2491         union {
2492             int intval;
2493             char *string;
2494         } defvalue;
2495         uint32_t svcs;
2496         int32_t minval;
2497         int32_t maxval;
2498         char *other;
2499         int compare;
2500 #define OPT_CMP_GE      0
2501 #define OPT_CMP_LE      1
2502         int (*check)(char *);
2503 } proto_options[] = {
2504 #define PROTO_OPT_NFSD_SERVERS                  0
2505         {"nfsd_servers",
2506             "servers", PROTO_OPT_NFSD_SERVERS, OPT_TYPE_NUMBER, 1024, SVC_NFSD,
2507             1, INT32_MAX},
2508 #define PROTO_OPT_LOCKD_LISTEN_BACKLOG          1
2509         {"lockd_listen_backlog",
2510             "lockd_listen_backlog", PROTO_OPT_LOCKD_LISTEN_BACKLOG,
2511             OPT_TYPE_NUMBER, 32, SVC_LOCKD, 32, INT32_MAX},
2512 #define PROTO_OPT_LOCKD_SERVERS                 2
2513         {"lockd_servers",
2514             "lockd_servers", PROTO_OPT_LOCKD_SERVERS, OPT_TYPE_NUMBER, 256,
2515             SVC_LOCKD, 1, INT32_MAX},
2516 #define PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT      3
2517         {"lockd_retransmit_timeout",
2518             "lockd_retransmit_timeout", PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT,
2519             OPT_TYPE_NUMBER, 5, SVC_LOCKD, 0, INT32_MAX},
2520 #define PROTO_OPT_GRACE_PERIOD                  4
2521         {"grace_period",
2522             "grace_period", PROTO_OPT_GRACE_PERIOD, OPT_TYPE_NUMBER, 90,
2523             SVC_LOCKD, 0, INT32_MAX},
2524 #define PROTO_OPT_NFS_SERVER_VERSMIN            5
2525         {"nfs_server_versmin",
2526             "server_versmin", PROTO_OPT_NFS_SERVER_VERSMIN, OPT_TYPE_NUMBER,
2527             (int)NFS_VERSMIN_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN,
2528             NFS_VERSMAX, "server_versmax", OPT_CMP_LE},
2529 #define PROTO_OPT_NFS_SERVER_VERSMAX            6
2530         {"nfs_server_versmax",
2531             "server_versmax", PROTO_OPT_NFS_SERVER_VERSMAX, OPT_TYPE_NUMBER,
2532             (int)NFS_VERSMAX_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN,
2533             NFS_VERSMAX, "server_versmin", OPT_CMP_GE},
2534 #define PROTO_OPT_NFS_CLIENT_VERSMIN            7
2535         {"nfs_client_versmin",
2536             "client_versmin", PROTO_OPT_NFS_CLIENT_VERSMIN, OPT_TYPE_NUMBER,
2537             (int)NFS_VERSMIN_DEFAULT, SVC_CLIENT, NFS_VERSMIN, NFS_VERSMAX,
2538             "client_versmax", OPT_CMP_LE},
2539 #define PROTO_OPT_NFS_CLIENT_VERSMAX            8
2540         {"nfs_client_versmax",
2541             "client_versmax", PROTO_OPT_NFS_CLIENT_VERSMAX, OPT_TYPE_NUMBER,
2542             (int)NFS_VERSMAX_DEFAULT, SVC_CLIENT, NFS_VERSMIN, NFS_VERSMAX,
2543             "client_versmin", OPT_CMP_GE},
2544 #define PROTO_OPT_NFS_SERVER_DELEGATION         9
2545         {"nfs_server_delegation",
2546             "server_delegation", PROTO_OPT_NFS_SERVER_DELEGATION,
2547             OPT_TYPE_ONOFF, NFS_SERVER_DELEGATION_DEFAULT, SVC_NFSD, 0, 0},
2548 #define PROTO_OPT_NFSMAPID_DOMAIN               10
2549         {"nfsmapid_domain",
2550             "nfsmapid_domain", PROTO_OPT_NFSMAPID_DOMAIN, OPT_TYPE_DOMAIN,
2551             0, SVC_NFSMAPID, 0, 0},
2552 #define PROTO_OPT_NFSD_MAX_CONNECTIONS          11
2553         {"nfsd_max_connections",
2554             "max_connections", PROTO_OPT_NFSD_MAX_CONNECTIONS,
2555             OPT_TYPE_NUMBER, -1, SVC_NFSD, -1, INT32_MAX},
2556 #define PROTO_OPT_NFSD_PROTOCOL                 12
2557         {"nfsd_protocol",
2558             "protocol", PROTO_OPT_NFSD_PROTOCOL, OPT_TYPE_PROTOCOL, 0,
2559             SVC_NFSD, 0, 0},
2560 #define PROTO_OPT_NFSD_LISTEN_BACKLOG           13
2561         {"nfsd_listen_backlog",
2562             "listen_backlog", PROTO_OPT_NFSD_LISTEN_BACKLOG,
2563             OPT_TYPE_NUMBER, 0, SVC_NFSD, 0, INT32_MAX},
2564 #define PROTO_OPT_NFSD_DEVICE                   14
2565         {"nfsd_device",
2566             "device", PROTO_OPT_NFSD_DEVICE,
2567             OPT_TYPE_STRING, 0, SVC_NFSD, 0, 0},
2568 #define PROTO_OPT_MOUNTD_LISTEN_BACKLOG         15
2569         {"mountd_listen_backlog",
2570             "mountd_listen_backlog", PROTO_OPT_MOUNTD_LISTEN_BACKLOG,
2571             OPT_TYPE_NUMBER, 64, SVC_NFSD|SVC_MOUNTD, 1, INT32_MAX},
2572 #define PROTO_OPT_MOUNTD_MAX_THREADS            16
2573         {"mountd_max_threads",
2574             "mountd_max_threads", PROTO_OPT_MOUNTD_MAX_THREADS,
2575             OPT_TYPE_NUMBER, 16, SVC_NFSD|SVC_MOUNTD, 1, INT32_MAX},
2576 #define PROTO_OPT_MOUNTD_PORT                   17
2577         {"mountd_port",
2578             "mountd_port", PROTO_OPT_MOUNTD_PORT,
2579             OPT_TYPE_NUMBER, 0, SVC_MOUNTD, 1, UINT16_MAX},
2580 #define PROTO_OPT_STATD_PORT                    18
2581         {"statd_port",
2582             "statd_port", PROTO_OPT_STATD_PORT,
2583             OPT_TYPE_NUMBER, 0, SVC_STATD, 1, UINT16_MAX},
2584         {NULL}
2585 };
2586 
2587 /*
2588  * the protoset holds the defined options so we don't have to read
2589  * them multiple times
2590  */
2591 static sa_protocol_properties_t protoset;
2592 
2593 static int
2594 findprotoopt(char *name, int whichname)
2595 {
2596         int i;
2597         for (i = 0; proto_options[i].tag != NULL; i++) {
2598                 if (whichname == 1) {
2599                         if (strcasecmp(proto_options[i].name, name) == 0)
2600                         return (i);
2601                 } else {
2602                         if (strcasecmp(proto_options[i].tag, name) == 0)
2603                                 return (i);
2604                 }
2605         }
2606         return (-1);
2607 }
2608 
2609 /*
2610  * fixcaselower(str)
2611  *
2612  * convert a string to lower case (inplace).
2613  */
2614 
2615 static void
2616 fixcaselower(char *str)
2617 {
2618         while (*str) {
2619                 *str = tolower(*str);
2620                 str++;
2621         }
2622 }
2623 
2624 /*
2625  * skipwhitespace(str)
2626  *
2627  * Skip leading white space. It is assumed that it is called with a
2628  * valid pointer.
2629  */
2630 
2631 static char *
2632 skipwhitespace(char *str)
2633 {
2634         while (*str && isspace(*str))
2635                 str++;
2636 
2637         return (str);
2638 }
2639 
2640 /*
2641  * extractprop()
2642  *
2643  * Extract the property and value out of the line and create the
2644  * property in the optionset.
2645  */
2646 static int
2647 extractprop(char *name, char *value)
2648 {
2649         sa_property_t prop;
2650         int index;
2651         int ret = SA_OK;
2652         /*
2653          * Remove any leading
2654          * white space.
2655          */
2656         name = skipwhitespace(name);
2657 
2658         index = findprotoopt(name, 1);
2659         if (index >= 0) {
2660                 fixcaselower(name);
2661                 prop = sa_create_property(proto_options[index].name, value);
2662                 if (prop != NULL)
2663                         ret = sa_add_protocol_property(protoset, prop);
2664                 else
2665                         ret = SA_NO_MEMORY;
2666         }
2667         return (ret);
2668 }
2669 
2670 scf_type_t
2671 getscftype(int type)
2672 {
2673         scf_type_t ret;
2674 
2675         switch (type) {
2676         case OPT_TYPE_NUMBER:
2677                 ret = SCF_TYPE_INTEGER;
2678         break;
2679         case OPT_TYPE_BOOLEAN:
2680                 ret = SCF_TYPE_BOOLEAN;
2681         break;
2682         default:
2683                 ret = SCF_TYPE_ASTRING;
2684         }
2685         return (ret);
2686 }
2687 
2688 char *
2689 getsvcname(uint32_t svcs)
2690 {
2691         char *service;
2692         switch (svcs) {
2693                 case SVC_LOCKD:
2694                         service = LOCKD;
2695                         break;
2696                 case SVC_STATD:
2697                         service = STATD;
2698                         break;
2699                 case SVC_NFSD:
2700                         service = NFSD;
2701                         break;
2702                 case SVC_CLIENT:
2703                         service = NFS_CLIENT_SVC;
2704                         break;
2705                 case SVC_NFS4CBD:
2706                         service = NFS4CBD;
2707                         break;
2708                 case SVC_NFSMAPID:
2709                         service = NFSMAPID;
2710                         break;
2711                 case SVC_RQUOTAD:
2712                         service = RQUOTAD;
2713                         break;
2714                 case SVC_NFSLOGD:
2715                         service = NFSLOGD;
2716                         break;
2717                 case SVC_REPARSED:
2718                         service = REPARSED;
2719                         break;
2720                 default:
2721                         service = NFSD;
2722         }
2723         return (service);
2724 }
2725 
2726 /*
2727  * initprotofromsmf()
2728  *
2729  * Read NFS SMF properties and add the defined values to the
2730  * protoset.  Note that default values are known from the built in
2731  * table in case SMF doesn't have a definition. Not having
2732  * SMF properties is OK since we have builtin default
2733  * values.
2734  */
2735 static int
2736 initprotofromsmf()
2737 {
2738         char name[PATH_MAX];
2739         char value[PATH_MAX];
2740         int ret = SA_OK, bufsz = 0, i;
2741 
2742         protoset = sa_create_protocol_properties("nfs");
2743         if (protoset != NULL) {
2744                 for (i = 0; proto_options[i].tag != NULL; i++) {
2745                         scf_type_t ptype;
2746                         char *svc_name;
2747 
2748                         bzero(value, PATH_MAX);
2749                         (void) strncpy(name, proto_options[i].name, PATH_MAX);
2750                         /* Replace NULL with the correct instance */
2751                         ptype = getscftype(proto_options[i].type);
2752                         svc_name = getsvcname(proto_options[i].svcs);
2753                         bufsz = PATH_MAX;
2754                         ret = nfs_smf_get_prop(name, value,
2755                             (char *)DEFAULT_INSTANCE, ptype,
2756                             svc_name, &bufsz);
2757                         if (ret == SA_OK) {
2758                                 ret = extractprop(name, value);
2759                         }
2760                 }
2761         } else {
2762                 ret = SA_NO_MEMORY;
2763         }
2764 
2765         return (ret);
2766 }
2767 
2768 /*
2769  * add_defaults()
2770  *
2771  * Add the default values for any property not defined
2772  * in NFS SMF repository.
2773  * Values are set according to their defined types.
2774  */
2775 
2776 static void
2777 add_defaults()
2778 {
2779         int i;
2780         char number[MAXDIGITS];
2781 
2782         for (i = 0; proto_options[i].tag != NULL; i++) {
2783                 sa_property_t prop;
2784                 prop = sa_get_protocol_property(protoset,
2785                     proto_options[i].name);
2786                 if (prop == NULL) {
2787                         /* add the default value */
2788                         switch (proto_options[i].type) {
2789                         case OPT_TYPE_NUMBER:
2790                                 (void) snprintf(number, sizeof (number), "%d",
2791                                     proto_options[i].defvalue.intval);
2792                                 prop = sa_create_property(proto_options[i].name,
2793                                     number);
2794                                 break;
2795 
2796                         case OPT_TYPE_BOOLEAN:
2797                                 prop = sa_create_property(proto_options[i].name,
2798                                     proto_options[i].defvalue.intval ?
2799                                     "true" : "false");
2800                                 break;
2801 
2802                         case OPT_TYPE_ONOFF:
2803                                 prop = sa_create_property(proto_options[i].name,
2804                                     proto_options[i].defvalue.intval ?
2805                                     "on" : "off");
2806                                 break;
2807 
2808                         default:
2809                                 /* treat as strings of zero length */
2810                                 prop = sa_create_property(proto_options[i].name,
2811                                     "");
2812                                 break;
2813                         }
2814                         if (prop != NULL)
2815                                 (void) sa_add_protocol_property(protoset, prop);
2816                 }
2817         }
2818 }
2819 
2820 static void
2821 free_protoprops()
2822 {
2823         if (protoset != NULL) {
2824                 xmlFreeNode(protoset);
2825                 protoset = NULL;
2826         }
2827 }
2828 
2829 /*
2830  * nfs_init()
2831  *
2832  * Initialize the NFS plugin.
2833  */
2834 
2835 static int
2836 nfs_init()
2837 {
2838         int ret = SA_OK;
2839 
2840         if (sa_plugin_ops.sa_init != nfs_init) {
2841                 (void) printf(dgettext(TEXT_DOMAIN,
2842                     "NFS plugin not properly initialized\n"));
2843                 return (SA_CONFIG_ERR);
2844         }
2845 
2846         ret = initprotofromsmf();
2847         if (ret != SA_OK) {
2848                 (void) printf(dgettext(TEXT_DOMAIN,
2849                     "NFS plugin problem with SMF repository: %s\n"),
2850                     sa_errorstr(ret));
2851                 ret = SA_OK;
2852         }
2853         add_defaults();
2854 
2855         return (ret);
2856 }
2857 
2858 /*
2859  * nfs_fini()
2860  *
2861  * uninitialize the NFS plugin. Want to avoid memory leaks.
2862  */
2863 
2864 static void
2865 nfs_fini()
2866 {
2867         free_protoprops();
2868 }
2869 
2870 /*
2871  * nfs_get_proto_set()
2872  *
2873  * Return an optionset with all the protocol specific properties in
2874  * it.
2875  */
2876 
2877 static sa_protocol_properties_t
2878 nfs_get_proto_set()
2879 {
2880         return (protoset);
2881 }
2882 
2883 /*
2884  * service_in_state(service, chkstate)
2885  *
2886  * Want to know if the specified service is in the desired state
2887  * (chkstate) or not. Return true (1) if it is and false (0) if it
2888  * isn't.
2889  */
2890 static int
2891 service_in_state(char *service, const char *chkstate)
2892 {
2893         char *state;
2894         int ret = B_FALSE;
2895 
2896         state = smf_get_state(service);
2897         if (state != NULL) {
2898                 /* got the state so get the equality for the return value */
2899                 ret = strcmp(state, chkstate) == 0 ? B_TRUE : B_FALSE;
2900                 free(state);
2901         }
2902         return (ret);
2903 }
2904 
2905 /*
2906  * restart_service(svcs)
2907  *
2908  * Walk through the bit mask of services that need to be restarted in
2909  * order to use the new property values. Some properties affect
2910  * multiple daemons. Should only restart a service if it is currently
2911  * enabled (online).
2912  */
2913 
2914 static void
2915 restart_service(uint32_t svcs)
2916 {
2917         uint32_t mask;
2918         int ret;
2919         char *service;
2920 
2921         for (mask = 1; svcs != 0; mask <<= 1) {
2922                 switch (svcs & mask) {
2923                 case SVC_LOCKD:
2924                         service = LOCKD;
2925                         break;
2926                 case SVC_STATD:
2927                         service = STATD;
2928                         break;
2929                 case SVC_NFSD:
2930                         service = NFSD;
2931                         break;
2932                 case SVC_MOUNTD:
2933                         service = MOUNTD;
2934                         break;
2935                 case SVC_NFS4CBD:
2936                         service = NFS4CBD;
2937                         break;
2938                 case SVC_NFSMAPID:
2939                         service = NFSMAPID;
2940                         break;
2941                 case SVC_RQUOTAD:
2942                         service = RQUOTAD;
2943                         break;
2944                 case SVC_NFSLOGD:
2945                         service = NFSLOGD;
2946                         break;
2947                 case SVC_REPARSED:
2948                         service = REPARSED;
2949                         break;
2950                 case SVC_CLIENT:
2951                         service = NFS_CLIENT_SVC;
2952                         break;
2953                 default:
2954                         continue;
2955                 }
2956 
2957                 /*
2958                  * Only attempt to restart the service if it is
2959                  * currently running. In the future, it may be
2960                  * desirable to use smf_refresh_instance if the NFS
2961                  * services ever implement the refresh method.
2962                  */
2963                 if (service_in_state(service, SCF_STATE_STRING_ONLINE)) {
2964                         ret = smf_restart_instance(service);
2965                         /*
2966                          * There are only a few SMF errors at this point, but
2967                          * it is also possible that a bad value may have put
2968                          * the service into maintenance if there wasn't an
2969                          * SMF level error.
2970                          */
2971                         if (ret != 0) {
2972                                 (void) fprintf(stderr,
2973                                     dgettext(TEXT_DOMAIN,
2974                                     "%s failed to restart: %s\n"),
2975                                     service, scf_strerror(scf_error()));
2976                         } else {
2977                                 /*
2978                                  * Check whether it has gone to "maintenance"
2979                                  * mode or not. Maintenance implies something
2980                                  * went wrong.
2981                                  */
2982                                 if (service_in_state(service,
2983                                     SCF_STATE_STRING_MAINT)) {
2984                                         (void) fprintf(stderr,
2985                                             dgettext(TEXT_DOMAIN,
2986                                             "%s failed to restart\n"),
2987                                             service);
2988                                 }
2989                         }
2990                 }
2991                 svcs &= ~mask;
2992         }
2993 }
2994 
2995 /*
2996  * nfs_minmax_check(name, value)
2997  *
2998  * Verify that the value for the property specified by index is valid
2999  * relative to the opposite value in the case of a min/max variable.
3000  * Currently, server_minvers/server_maxvers and
3001  * client_minvers/client_maxvers are the only ones to check.
3002  */
3003 
3004 static int
3005 nfs_minmax_check(int index, int value)
3006 {
3007         int val;
3008         char *pval;
3009         sa_property_t prop;
3010         sa_optionset_t opts;
3011         int ret = B_TRUE;
3012 
3013         if (proto_options[index].other != NULL) {
3014                 /* have a property to compare against */
3015                 opts = nfs_get_proto_set();
3016                 prop = sa_get_property(opts, proto_options[index].other);
3017                 /*
3018                  * If we don't find the property, assume default
3019                  * values which will work since the max will be at the
3020                  * max and the min at the min.
3021                  */
3022                 if (prop != NULL) {
3023                         pval = sa_get_property_attr(prop, "value");
3024                         if (pval != NULL) {
3025                                 val = strtoul(pval, NULL, 0);
3026                                 if (proto_options[index].compare ==
3027                                     OPT_CMP_LE) {
3028                                         ret = value <= val ? B_TRUE : B_FALSE;
3029                                 } else if (proto_options[index].compare ==
3030                                     OPT_CMP_GE) {
3031                                         ret = value >= val ? B_TRUE : B_FALSE;
3032                                 }
3033                                 sa_free_attr_string(pval);
3034                         }
3035                 }
3036         }
3037         return (ret);
3038 }
3039 
3040 /*
3041  * nfs_validate_proto_prop(index, name, value)
3042  *
3043  * Verify that the property specified by name can take the new
3044  * value. This is a sanity check to prevent bad values getting into
3045  * the default files. All values need to be checked against what is
3046  * allowed by their defined type. If a type isn't explicitly defined
3047  * here, it is treated as a string.
3048  *
3049  * Note that OPT_TYPE_NUMBER will additionally check that the value is
3050  * within the range specified and potentially against another property
3051  * value as well as specified in the proto_options members other and
3052  * compare.
3053  */
3054 
3055 static int
3056 nfs_validate_proto_prop(int index, char *name, char *value)
3057 {
3058         int ret = SA_OK;
3059         char *cp;
3060 #ifdef lint
3061         name = name;
3062 #endif
3063         switch (proto_options[index].type) {
3064         case OPT_TYPE_NUMBER:
3065                 if (!is_a_number(value))
3066                         ret = SA_BAD_VALUE;
3067                 else {
3068                         int val;
3069                         val = strtoul(value, NULL, 0);
3070                         if (val < proto_options[index].minval ||
3071                             val > proto_options[index].maxval)
3072                                 ret = SA_BAD_VALUE;
3073                         /*
3074                          * For server_versmin/server_versmax and
3075                          * client_versmin/client_versmax, the value of the
3076                          * min(max) should be checked to be correct relative
3077                          * to the current max(min).
3078                          */
3079                         if (!nfs_minmax_check(index, val)) {
3080                                 ret = SA_BAD_VALUE;
3081                         }
3082                 }
3083                 break;
3084 
3085         case OPT_TYPE_DOMAIN:
3086                 /*
3087                  * needs to be a qualified domain so will have at
3088                  * least one period and other characters on either
3089                  * side of it.  A zero length string is also allowed
3090                  * and is the way to turn off the override.
3091                  */
3092                 if (strlen(value) == 0)
3093                         break;
3094                 cp = strchr(value, '.');
3095                 if (cp == NULL || cp == value || strchr(value, '@') != NULL)
3096                         ret = SA_BAD_VALUE;
3097                 break;
3098 
3099         case OPT_TYPE_BOOLEAN:
3100                 if (strlen(value) == 0 ||
3101                     strcasecmp(value, "true") == 0 ||
3102                     strcmp(value, "1") == 0 ||
3103                     strcasecmp(value, "false") == 0 ||
3104                     strcmp(value, "0") == 0) {
3105                         ret = SA_OK;
3106                 } else {
3107                         ret = SA_BAD_VALUE;
3108                 }
3109                 break;
3110 
3111         case OPT_TYPE_ONOFF:
3112                 if (strcasecmp(value, "on") != 0 &&
3113                     strcasecmp(value, "off") != 0) {
3114                         ret = SA_BAD_VALUE;
3115                 }
3116                 break;
3117 
3118         case OPT_TYPE_PROTOCOL: {
3119                 struct netconfig *nconf;
3120                 void *nc;
3121                 boolean_t pfound = B_FALSE;
3122 
3123                 if (strcasecmp(value, "all") == 0)
3124                         break;
3125 
3126                 if ((nc = setnetconfig()) == NULL) {
3127                         (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
3128                             "setnetconfig failed: %s\n"), strerror(errno));
3129                 } else {
3130                         while ((nconf = getnetconfig(nc)) != NULL) {
3131                                 if (strcmp(nconf->nc_proto, value) == 0) {
3132                                         pfound = B_TRUE;
3133                                         break;
3134                                 }
3135                         }
3136                         (void) endnetconfig(nc);
3137                 }
3138 
3139                 if (!pfound)
3140                         ret = SA_BAD_VALUE;
3141                 break;
3142         }
3143 
3144         default:
3145                 /* treat as a string */
3146                 break;
3147         }
3148         return (ret);
3149 }
3150 
3151 /*
3152  * nfs_set_proto_prop(prop)
3153  *
3154  * check that prop is valid.
3155  */
3156 
3157 static int
3158 nfs_set_proto_prop(sa_property_t prop)
3159 {
3160         int ret = SA_OK;
3161         char *name;
3162         char *value;
3163 
3164         name = sa_get_property_attr(prop, "type");
3165         value = sa_get_property_attr(prop, "value");
3166         if (name != NULL && value != NULL) {
3167                 scf_type_t sctype;
3168                 char *svc_name;
3169                 char *instance = NULL;
3170                 int index = findprotoopt(name, 1);
3171 
3172                 ret = nfs_validate_proto_prop(index, name, value);
3173                 if (ret == SA_OK) {
3174                         sctype = getscftype(proto_options[index].type);
3175                         svc_name = getsvcname(proto_options[index].svcs);
3176                         if (sctype == SCF_TYPE_BOOLEAN) {
3177                                 if (value != NULL)
3178                                         sa_free_attr_string(value);
3179                                 if (string_to_boolean(value) == 0)
3180                                         value = strdup("0");
3181                                 else
3182                                         value = strdup("1");
3183                         }
3184                         ret = nfs_smf_set_prop(name, value, instance, sctype,
3185                             svc_name);
3186                         if (ret == SA_OK) {
3187                                 restart_service(proto_options[index].svcs);
3188                         } else {
3189                                 (void) printf(dgettext(TEXT_DOMAIN,
3190                                     "Cannot restart NFS services : %s\n"),
3191                                     sa_errorstr(ret));
3192                         }
3193                 }
3194         }
3195         if (name != NULL)
3196                 sa_free_attr_string(name);
3197         if (value != NULL)
3198                 sa_free_attr_string(value);
3199         return (ret);
3200 }
3201 
3202 /*
3203  * nfs_get_status()
3204  *
3205  * What is the current status of the nfsd? We use the SMF state here.
3206  * Caller must free the returned value.
3207  */
3208 
3209 static char *
3210 nfs_get_status()
3211 {
3212         return (smf_get_state(NFSD));
3213 }
3214 
3215 /*
3216  * nfs_space_alias(alias)
3217  *
3218  * Lookup the space (security) name. If it is default, convert to the
3219  * real name.
3220  */
3221 
3222 static char *
3223 nfs_space_alias(char *space)
3224 {
3225         char *name = space;
3226         seconfig_t secconf;
3227 
3228         /*
3229          * Only the space named "default" is special. If it is used,
3230          * the default needs to be looked up and the real name used.
3231          * This is normally "sys" but could be changed.  We always
3232          * change default to the real name.
3233          */
3234         if (strcmp(space, "default") == 0 &&
3235             nfs_getseconfig_default(&secconf) == 0) {
3236                 if (nfs_getseconfig_bynumber(secconf.sc_nfsnum, &secconf) == 0)
3237                         name = secconf.sc_name;
3238         }
3239         return (strdup(name));
3240 }
3241 
3242 /*
3243  * nfs_features()
3244  *
3245  * Return a mask of the features required.
3246  */
3247 
3248 static uint64_t
3249 nfs_features()
3250 {
3251         return ((uint64_t)SA_FEATURE_DFSTAB | SA_FEATURE_SERVER);
3252 }