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