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