1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2016 Nexenta Systems, Inc.
  25  * Copyright (c) 2014, 2016 by Delphix. All rights reserved.
  26  */
  27 
  28 /*
  29  * NFS specific functions
  30  */
  31 #include <stdio.h>
  32 #include <string.h>
  33 #include <ctype.h>
  34 #include <stdlib.h>
  35 #include <unistd.h>
  36 #include <zone.h>
  37 #include <errno.h>
  38 #include <locale.h>
  39 #include <signal.h>
  40 #include <strings.h>
  41 #include "libshare.h"
  42 #include "libshare_impl.h"
  43 #include <nfs/export.h>
  44 #include <pwd.h>
  45 #include <grp.h>
  46 #include <limits.h>
  47 #include <libscf.h>
  48 #include <syslog.h>
  49 #include <rpcsvc/daemon_utils.h>
  50 #include "nfslog_config.h"
  51 #include "nfslogtab.h"
  52 #include "libshare_nfs.h"
  53 #include <nfs/nfs.h>
  54 #include <nfs/nfssys.h>
  55 #include <netconfig.h>
  56 #include "smfcfg.h"
  57 
  58 /* should really be in some global place */
  59 #define DEF_WIN 30000
  60 #define OPT_CHUNK       1024
  61 
  62 int debug = 0;
  63 
  64 #define NFS_SERVER_SVC  "svc:/network/nfs/server:default"
  65 #define NFS_CLIENT_SVC  (char *)"svc:/network/nfs/client:default"
  66 
  67 /* internal functions */
  68 static int nfs_init();
  69 static void nfs_fini();
  70 static int nfs_enable_share(sa_share_t);
  71 static int nfs_disable_share(sa_share_t, char *);
  72 static int nfs_validate_property(sa_handle_t, sa_property_t, sa_optionset_t);
  73 static int nfs_validate_security_mode(char *);
  74 static int nfs_is_security_opt(char *);
  75 static int nfs_parse_legacy_options(sa_group_t, char *);
  76 static char *nfs_format_options(sa_group_t, int);
  77 static int nfs_set_proto_prop(sa_property_t);
  78 static sa_protocol_properties_t nfs_get_proto_set();
  79 static char *nfs_get_status();
  80 static char *nfs_space_alias(char *);
  81 static uint64_t nfs_features();
  82 
  83 /*
  84  * ops vector that provides the protocol specific info and operations
  85  * for share management.
  86  */
  87 
  88 struct sa_plugin_ops sa_plugin_ops = {
  89         SA_PLUGIN_VERSION,
  90         "nfs",
  91         nfs_init,
  92         nfs_fini,
  93         nfs_enable_share,
  94         nfs_disable_share,
  95         nfs_validate_property,
  96         nfs_validate_security_mode,
  97         nfs_is_security_opt,
  98         nfs_parse_legacy_options,
  99         nfs_format_options,
 100         nfs_set_proto_prop,
 101         nfs_get_proto_set,
 102         nfs_get_status,
 103         nfs_space_alias,
 104         NULL,   /* update_legacy */
 105         NULL,   /* delete_legacy */
 106         NULL,   /* change_notify */
 107         NULL,   /* enable_resource */
 108         NULL,   /* disable_resource */
 109         nfs_features,
 110         NULL,   /* transient shares */
 111         NULL,   /* notify resource */
 112         NULL,   /* rename_resource */
 113         NULL,   /* run_command */
 114         NULL,   /* command_help */
 115         NULL    /* delete_proto_section */
 116 };
 117 
 118 /*
 119  * list of support services needed
 120  * defines should come from head/rpcsvc/daemon_utils.h
 121  */
 122 
 123 static char *service_list_default[] =
 124         { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, REPARSED, NULL };
 125 static char *service_list_logging[] =
 126         { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NFSLOGD, REPARSED,
 127             NULL };
 128 
 129 /*
 130  * option definitions.  Make sure to keep the #define for the option
 131  * index just before the entry it is the index for. Changing the order
 132  * can cause breakage.  E.g OPT_RW is index 1 and must precede the
 133  * line that includes the SHOPT_RW and OPT_RW entries.
 134  */
 135 
 136 struct option_defs optdefs[] = {
 137 #define OPT_RO          0
 138         {SHOPT_RO, OPT_RO, OPT_TYPE_ACCLIST},
 139 #define OPT_RW          1
 140         {SHOPT_RW, OPT_RW, OPT_TYPE_ACCLIST},
 141 #define OPT_ROOT        2
 142         {SHOPT_ROOT, OPT_ROOT, OPT_TYPE_ACCLIST},
 143 #define OPT_SECURE      3
 144         {SHOPT_SECURE, OPT_SECURE, OPT_TYPE_DEPRECATED},
 145 #define OPT_ANON        4
 146         {SHOPT_ANON, OPT_ANON, OPT_TYPE_USER},
 147 #define OPT_WINDOW      5
 148         {SHOPT_WINDOW, OPT_WINDOW, OPT_TYPE_NUMBER},
 149 #define OPT_NOSUID      6
 150         {SHOPT_NOSUID, OPT_NOSUID, OPT_TYPE_BOOLEAN},
 151 #define OPT_ACLOK       7
 152         {SHOPT_ACLOK, OPT_ACLOK, OPT_TYPE_BOOLEAN},
 153 #define OPT_NOSUB       8
 154         {SHOPT_NOSUB, OPT_NOSUB, OPT_TYPE_BOOLEAN},
 155 #define OPT_SEC         9
 156         {SHOPT_SEC, OPT_SEC, OPT_TYPE_SECURITY},
 157 #define OPT_PUBLIC      10
 158         {SHOPT_PUBLIC, OPT_PUBLIC, OPT_TYPE_BOOLEAN, OPT_SHARE_ONLY},
 159 #define OPT_INDEX       11
 160         {SHOPT_INDEX, OPT_INDEX, OPT_TYPE_FILE},
 161 #define OPT_LOG         12
 162         {SHOPT_LOG, OPT_LOG, OPT_TYPE_LOGTAG},
 163 #define OPT_CKSUM       13
 164         {SHOPT_CKSUM, OPT_CKSUM, OPT_TYPE_STRINGSET},
 165 #define OPT_NONE        14
 166         {SHOPT_NONE, OPT_NONE, OPT_TYPE_ACCLIST},
 167 #define OPT_ROOT_MAPPING        15
 168         {SHOPT_ROOT_MAPPING, OPT_ROOT_MAPPING, OPT_TYPE_USER},
 169 #define OPT_CHARSET_MAP 16
 170         {"", OPT_CHARSET_MAP, OPT_TYPE_ACCLIST},
 171 #define OPT_NOACLFAB    17
 172         {SHOPT_NOACLFAB, OPT_NOACLFAB, OPT_TYPE_BOOLEAN},
 173 #define OPT_UIDMAP      18
 174         {SHOPT_UIDMAP, OPT_UIDMAP, OPT_TYPE_MAPPING},
 175 #define OPT_GIDMAP      19
 176         {SHOPT_GIDMAP, OPT_GIDMAP, OPT_TYPE_MAPPING},
 177 #ifdef VOLATILE_FH_TEST /* XXX added for testing volatile fh's only */
 178 #define OPT_VOLFH       20
 179         {SHOPT_VOLFH, OPT_VOLFH},
 180 #endif /* VOLATILE_FH_TEST */
 181         NULL
 182 };
 183 
 184 /*
 185  * Codesets that may need to be converted to UTF-8 for file paths.
 186  * Add new names here to add new property support. If we ever get a
 187  * way to query the kernel for character sets, this should become
 188  * dynamically loaded. Make sure changes here are reflected in
 189  * cmd/fs.d/nfs/mountd/nfscmd.c
 190  */
 191 
 192 static char *legal_conv[] = {
 193         "euc-cn",
 194         "euc-jp",
 195         "euc-jpms",
 196         "euc-kr",
 197         "euc-tw",
 198         "iso8859-1",
 199         "iso8859-2",
 200         "iso8859-5",
 201         "iso8859-6",
 202         "iso8859-7",
 203         "iso8859-8",
 204         "iso8859-9",
 205         "iso8859-13",
 206         "iso8859-15",
 207         "koi8-r",
 208         NULL
 209 };
 210 
 211 /*
 212  * list of properties that are related to security flavors.
 213  */
 214 static char *seclist[] = {
 215         SHOPT_RO,
 216         SHOPT_RW,
 217         SHOPT_ROOT,
 218         SHOPT_WINDOW,
 219         SHOPT_NONE,
 220         SHOPT_ROOT_MAPPING,
 221         SHOPT_UIDMAP,
 222         SHOPT_GIDMAP,
 223         NULL
 224 };
 225 
 226 /* structure for list of securities */
 227 struct securities {
 228         sa_security_t security;
 229         struct securities *next;
 230 };
 231 
 232 /*
 233  * findcharset(charset)
 234  *
 235  * Returns B_TRUE if the charset is a legal conversion otherwise
 236  * B_FALSE. This will need to be rewritten to be more efficient when
 237  * we have a dynamic list of legal conversions.
 238  */
 239 
 240 static boolean_t
 241 findcharset(char *charset)
 242 {
 243         int i;
 244 
 245         for (i = 0; legal_conv[i] != NULL; i++)
 246                 if (strcmp(charset, legal_conv[i]) == 0)
 247                         return (B_TRUE);
 248         return (B_FALSE);
 249 }
 250 
 251 /*
 252  * findopt(name)
 253  *
 254  * Lookup option "name" in the option table and return the table
 255  * index.
 256  */
 257 
 258 static int
 259 findopt(char *name)
 260 {
 261         int i;
 262         if (name != NULL) {
 263                 for (i = 0; optdefs[i].tag != NULL; i++) {
 264                         if (strcmp(optdefs[i].tag, name) == 0)
 265                                 return (optdefs[i].index);
 266                 }
 267                 if (findcharset(name))
 268                         return (OPT_CHARSET_MAP);
 269         }
 270         return (-1);
 271 }
 272 
 273 /*
 274  * gettype(name)
 275  *
 276  * Return the type of option "name".
 277  */
 278 
 279 static int
 280 gettype(char *name)
 281 {
 282         int optdef;
 283 
 284         optdef = findopt(name);
 285         if (optdef != -1)
 286                 return (optdefs[optdef].type);
 287         return (OPT_TYPE_ANY);
 288 }
 289 
 290 /*
 291  * nfs_validate_security_mode(mode)
 292  *
 293  * is the specified mode string a valid one for use with NFS?
 294  */
 295 
 296 static int
 297 nfs_validate_security_mode(char *mode)
 298 {
 299         seconfig_t secinfo;
 300         int err;
 301 
 302         (void) memset(&secinfo, '\0', sizeof (secinfo));
 303         err = nfs_getseconfig_byname(mode, &secinfo);
 304         if (err == SC_NOERROR)
 305                 return (1);
 306         return (0);
 307 }
 308 
 309 /*
 310  * nfs_is_security_opt(tok)
 311  *
 312  * check to see if tok represents an option that is only valid in some
 313  * security flavor.
 314  */
 315 
 316 static int
 317 nfs_is_security_opt(char *tok)
 318 {
 319         int i;
 320 
 321         for (i = 0; seclist[i] != NULL; i++) {
 322                 if (strcmp(tok, seclist[i]) == 0)
 323                         return (1);
 324         }
 325         return (0);
 326 }
 327 
 328 /*
 329  * find_security(seclist, sec)
 330  *
 331  * Walk the current list of security flavors and return true if it is
 332  * present, else return false.
 333  */
 334 
 335 static int
 336 find_security(struct securities *seclist, sa_security_t sec)
 337 {
 338         while (seclist != NULL) {
 339                 if (seclist->security == sec)
 340                         return (1);
 341                 seclist = seclist->next;
 342         }
 343         return (0);
 344 }
 345 
 346 /*
 347  * make_security_list(group, securitymodes, proto)
 348  *      go through the list of securitymodes and add them to the
 349  *      group's list of security optionsets. We also keep a list of
 350  *      those optionsets so we don't have to find them later. All of
 351  *      these will get copies of the same properties.
 352  */
 353 
 354 static struct securities *
 355 make_security_list(sa_group_t group, char *securitymodes, char *proto)
 356 {
 357         char *tok, *next = NULL;
 358         struct securities *curp, *headp = NULL, *prev;
 359         sa_security_t check;
 360         int freetok = 0;
 361 
 362         for (tok = securitymodes; tok != NULL; tok = next) {
 363                 next = strchr(tok, ':');
 364                 if (next != NULL)
 365                         *next++ = '\0';
 366                 if (strcmp(tok, "default") == 0) {
 367                         /* resolve default into the real type */
 368                         tok = nfs_space_alias(tok);
 369                         freetok = 1;
 370                 }
 371                 check = sa_get_security(group, tok, proto);
 372 
 373                 /* add to the security list if it isn't there already */
 374                 if (check == NULL || !find_security(headp, check)) {
 375                         curp = (struct securities *)calloc(1,
 376                             sizeof (struct securities));
 377                         if (curp != NULL) {
 378                                 if (check == NULL) {
 379                                         curp->security = sa_create_security(
 380                                             group, tok, proto);
 381                                 } else {
 382                                         curp->security = check;
 383                                 }
 384                                 /*
 385                                  * note that the first time through the loop,
 386                                  * headp will be NULL and prev will be
 387                                  * undefined.  Since headp is NULL, we set
 388                                  * both it and prev to the curp (first
 389                                  * structure to be allocated).
 390                                  *
 391                                  * later passes through the loop will have
 392                                  * headp not being NULL and prev will be used
 393                                  * to allocate at the end of the list.
 394                                  */
 395                                 if (headp == NULL) {
 396                                         headp = curp;
 397                                         prev = curp;
 398                                 } else {
 399                                         prev->next = curp;
 400                                         prev = curp;
 401                                 }
 402                         }
 403                 }
 404 
 405                 if (freetok) {
 406                         freetok = 0;
 407                         sa_free_attr_string(tok);
 408                 }
 409         }
 410         return (headp);
 411 }
 412 
 413 static void
 414 free_security_list(struct securities *sec)
 415 {
 416         struct securities *next;
 417         if (sec != NULL) {
 418                 for (next = sec->next; sec != NULL; sec = next) {
 419                         next = sec->next;
 420                         free(sec);
 421                 }
 422         }
 423 }
 424 
 425 /*
 426  * nfs_alistcat(str1, str2, sep)
 427  *
 428  * concatenate str1 and str2 into a new string using sep as a separate
 429  * character. If memory allocation fails, return NULL;
 430  */
 431 
 432 static char *
 433 nfs_alistcat(char *str1, char *str2, char sep)
 434 {
 435         char *newstr;
 436         size_t len;
 437 
 438         len = strlen(str1) + strlen(str2) + 2;
 439         newstr = (char *)malloc(len);
 440         if (newstr != NULL)
 441                 (void) snprintf(newstr, len, "%s%c%s", str1, sep, str2);
 442         return (newstr);
 443 }
 444 
 445 /*
 446  * add_security_prop(sec, name, value, persist, iszfs)
 447  *
 448  * Add the property to the securities structure. This accumulates
 449  * properties for as part of parsing legacy options.
 450  */
 451 
 452 static int
 453 add_security_prop(struct securities *sec, char *name, char *value,
 454     int persist, int iszfs)
 455 {
 456         sa_property_t prop;
 457         int ret = SA_OK;
 458 
 459         for (; sec != NULL; sec = sec->next) {
 460                 if (value == NULL) {
 461                         if (strcmp(name, SHOPT_RW) == 0 ||
 462                             strcmp(name, SHOPT_RO) == 0)
 463                                 value = "*";
 464                         else
 465                                 value = "true";
 466                 }
 467 
 468                 /*
 469                  * Get the existing property, if it exists, so we can
 470                  * determine what to do with it. The ro/rw/root
 471                  * properties can be merged if multiple instances of
 472                  * these properies are given. For example, if "rw"
 473                  * exists with a value "host1" and a later token of
 474                  * rw="host2" is seen, the values are merged into a
 475                  * single rw="host1:host2".
 476                  */
 477                 prop = sa_get_property(sec->security, name);
 478 
 479                 if (prop != NULL) {
 480                         char *oldvalue;
 481                         char *newvalue;
 482 
 483                         /*
 484                          * The security options of ro/rw/root/uidmap/gidmap
 485                          * might appear multiple times.  If they do, the values
 486                          * need to be merged.  If it was previously empty, the
 487                          * new value alone is added.
 488                          */
 489                         oldvalue = sa_get_property_attr(prop, "value");
 490                         if (oldvalue != NULL) {
 491                                 char sep = ':';
 492 
 493                                 if (strcmp(name, SHOPT_UIDMAP) == 0 ||
 494                                     strcmp(name, SHOPT_GIDMAP) == 0)
 495                                         sep = '~';
 496 
 497                                 /*
 498                                  * The general case is to concatenate the new
 499                                  * value onto the old value for multiple
 500                                  * rw(ro/root/uidmap/gidmap) properties.  For
 501                                  * rw/ro/root a special case exists when either
 502                                  * the old or new is the "all" case.  In the
 503                                  * special case, if both are "all", then it is
 504                                  * "all", else if one is an access-list, that
 505                                  * replaces the "all".
 506                                  */
 507                                 if (strcmp(oldvalue, "*") == 0) {
 508                                         /* Replace old value with new value. */
 509                                         newvalue = strdup(value);
 510                                 } else if (strcmp(value, "*") == 0 ||
 511                                     strcmp(oldvalue, value) == 0) {
 512                                         /*
 513                                          * Keep old value and ignore
 514                                          * the new value.
 515                                          */
 516                                         newvalue = NULL;
 517                                 } else {
 518                                         /*
 519                                          * Make a new list of old plus new
 520                                          * access-list.
 521                                          */
 522                                         newvalue = nfs_alistcat(oldvalue,
 523                                             value, sep);
 524                                 }
 525 
 526                                 if (newvalue != NULL) {
 527                                         (void) sa_remove_property(prop);
 528                                         prop = sa_create_property(name,
 529                                             newvalue);
 530                                         ret = sa_add_property(sec->security,
 531                                             prop);
 532                                         free(newvalue);
 533                                 }
 534 
 535                                 sa_free_attr_string(oldvalue);
 536                         }
 537                 } else {
 538                         prop = sa_create_property(name, value);
 539                         ret = sa_add_property(sec->security, prop);
 540                 }
 541                 if (ret == SA_OK && !iszfs) {
 542                         ret = sa_commit_properties(sec->security, !persist);
 543                 }
 544         }
 545         return (ret);
 546 }
 547 
 548 /*
 549  * check to see if group/share is persistent.
 550  */
 551 static int
 552 is_persistent(sa_group_t group)
 553 {
 554         char *type;
 555         int persist = 1;
 556 
 557         type = sa_get_group_attr(group, "type");
 558         if (type != NULL && strcmp(type, "persist") != 0)
 559                 persist = 0;
 560         if (type != NULL)
 561                 sa_free_attr_string(type);
 562         return (persist);
 563 }
 564 
 565 /*
 566  * invalid_security(options)
 567  *
 568  * search option string for any invalid sec= type.
 569  * return true (1) if any are not valid else false (0)
 570  */
 571 static int
 572 invalid_security(char *options)
 573 {
 574         char *copy, *base, *token, *value;
 575         int ret = 0;
 576 
 577         copy = strdup(options);
 578         token = base = copy;
 579         while (token != NULL && ret == 0) {
 580                 token = strtok(base, ",");
 581                 base = NULL;
 582                 if (token != NULL) {
 583                         value = strchr(token, '=');
 584                         if (value != NULL)
 585                                 *value++ = '\0';
 586                         if (strcmp(token, SHOPT_SEC) == 0) {
 587                                 /* HAVE security flavors so check them */
 588                                 char *tok, *next;
 589                                 for (next = NULL, tok = value; tok != NULL;
 590                                     tok = next) {
 591                                         next = strchr(tok, ':');
 592                                         if (next != NULL)
 593                                                 *next++ = '\0';
 594                                         ret = !nfs_validate_security_mode(tok);
 595                                         if (ret)
 596                                                 break;
 597                                 }
 598                         }
 599                 }
 600         }
 601         if (copy != NULL)
 602                 free(copy);
 603         return (ret);
 604 }
 605 
 606 /*
 607  * nfs_parse_legacy_options(group, options)
 608  *
 609  * Parse the old style options into internal format and store on the
 610  * specified group.  Group could be a share for full legacy support.
 611  */
 612 
 613 static int
 614 nfs_parse_legacy_options(sa_group_t group, char *options)
 615 {
 616         char *dup;
 617         char *base;
 618         char *token;
 619         sa_optionset_t optionset;
 620         struct securities *security_list = NULL;
 621         sa_property_t prop;
 622         int ret = SA_OK;
 623         int iszfs = 0;
 624         sa_group_t parent;
 625         int persist = 0;
 626         char *lasts;
 627 
 628         /* do we have an existing optionset? */
 629         optionset = sa_get_optionset(group, "nfs");
 630         if (optionset == NULL) {
 631                 /* didn't find existing optionset so create one */
 632                 optionset = sa_create_optionset(group, "nfs");
 633         } else {
 634                 /*
 635                  * Have an existing optionset . Ideally, we would need
 636                  * to compare options in order to detect errors. For
 637                  * now, we assume that the first optionset is the
 638                  * correct one and the others will be the same. An
 639                  * empty optionset is the same as no optionset so we
 640                  * don't want to exit in that case. Getting an empty
 641                  * optionset can occur with ZFS property checking.
 642                  */
 643                 if (sa_get_property(optionset, NULL) != NULL)
 644                         return (ret);
 645         }
 646 
 647         if (strcmp(options, SHOPT_RW) == 0) {
 648                 /*
 649                  * there is a special case of only the option "rw"
 650                  * being the default option. We don't have to do
 651                  * anything.
 652                  */
 653                 return (ret);
 654         }
 655 
 656         /*
 657          * check if security types are present and validate them. If
 658          * any are not legal, fail.
 659          */
 660 
 661         if (invalid_security(options)) {
 662                 return (SA_INVALID_SECURITY);
 663         }
 664 
 665         /*
 666          * in order to not attempt to change ZFS properties unless
 667          * absolutely necessary, we never do it in the legacy parsing.
 668          */
 669         if (sa_is_share(group)) {
 670                 char *zfs;
 671                 parent = sa_get_parent_group(group);
 672                 if (parent != NULL) {
 673                         zfs = sa_get_group_attr(parent, "zfs");
 674                         if (zfs != NULL) {
 675                                 sa_free_attr_string(zfs);
 676                                 iszfs++;
 677                         }
 678                 }
 679         } else {
 680                 iszfs = sa_group_is_zfs(group);
 681         }
 682 
 683         /* We need a copy of options for the next part. */
 684         dup = strdup(options);
 685         if (dup == NULL)
 686                 return (SA_NO_MEMORY);
 687 
 688         /*
 689          * we need to step through each option in the string and then
 690          * add either the option or the security option as needed. If
 691          * this is not a persistent share, don't commit to the
 692          * repository. If there is an error, we also want to abort the
 693          * processing and report it.
 694          */
 695         persist = is_persistent(group);
 696         base = dup;
 697         token = dup;
 698         lasts = NULL;
 699         while (token != NULL && ret == SA_OK) {
 700                 token = strtok_r(base, ",", &lasts);
 701                 base = NULL;
 702                 if (token != NULL) {
 703                         char *value;
 704                         /*
 705                          * if the option has a value, it will have an '=' to
 706                          * separate the name from the value. The following
 707                          * code will result in value != NULL and token
 708                          * pointing to just the name if there is a value.
 709                          */
 710                         value = strchr(token, '=');
 711                         if (value != NULL) {
 712                                 *value++ = '\0';
 713                         }
 714                         if (strcmp(token, SHOPT_SEC) == 0 ||
 715                             strcmp(token, SHOPT_SECURE) == 0) {
 716                                 /*
 717                                  * Once in security parsing, we only
 718                                  * do security. We do need to move
 719                                  * between the security node and the
 720                                  * toplevel. The security tag goes on
 721                                  * the root while the following ones
 722                                  * go on the security.
 723                                  */
 724                                 if (security_list != NULL) {
 725                                         /*
 726                                          * have an old list so close it and
 727                                          * start the new
 728                                          */
 729                                         free_security_list(security_list);
 730                                 }
 731                                 if (strcmp(token, SHOPT_SECURE) == 0) {
 732                                         value = "dh";
 733                                 } else {
 734                                         if (value == NULL) {
 735                                                 ret = SA_SYNTAX_ERR;
 736                                                 break;
 737                                         }
 738                                 }
 739                                 security_list = make_security_list(group,
 740                                     value, "nfs");
 741                         } else {
 742                                 /*
 743                                  * Note that the "old" syntax allowed a
 744                                  * default security model.  This must be
 745                                  * accounted for and internally converted to
 746                                  * "standard" security structure.
 747                                  */
 748                                 if (nfs_is_security_opt(token)) {
 749                                         if (security_list == NULL) {
 750                                                 /*
 751                                                  * need to have a
 752                                                  * security
 753                                                  * option. This will
 754                                                  * be "closed" when a
 755                                                  * defined "sec="
 756                                                  * option is
 757                                                  * seen. This is
 758                                                  * technically an
 759                                                  * error but will be
 760                                                  * allowed with
 761                                                  * warning.
 762                                                  */
 763                                                 security_list =
 764                                                     make_security_list(group,
 765                                                     "default",
 766                                                     "nfs");
 767                                         }
 768                                         if (security_list != NULL) {
 769                                                 ret = add_security_prop(
 770                                                     security_list, token,
 771                                                     value, persist, iszfs);
 772                                         } else {
 773                                                 ret = SA_NO_MEMORY;
 774                                         }
 775                                 } else {
 776                                         /* regular options */
 777                                         if (value == NULL) {
 778                                                 if (strcmp(token, SHOPT_RW) ==
 779                                                     0 || strcmp(token,
 780                                                     SHOPT_RO) == 0) {
 781                                                         value = "*";
 782                                                 } else {
 783                                                         value = "global";
 784                                                         if (strcmp(token,
 785                                                             SHOPT_LOG) != 0) {
 786                                                                 value = "true";
 787                                                         }
 788                                                 }
 789                                         }
 790                                         /*
 791                                          * In all cases, create the
 792                                          * property specified. If the
 793                                          * value was NULL, the default
 794                                          * value will have been
 795                                          * substituted.
 796                                          */
 797                                         prop = sa_create_property(token, value);
 798                                         ret =  sa_add_property(optionset, prop);
 799                                         if (ret != SA_OK)
 800                                                 break;
 801 
 802                                         if (!iszfs) {
 803                                                 ret = sa_commit_properties(
 804                                                     optionset, !persist);
 805                                         }
 806                                 }
 807                         }
 808                 }
 809         }
 810         if (security_list != NULL)
 811                 free_security_list(security_list);
 812 
 813         free(dup);
 814         return (ret);
 815 }
 816 
 817 /*
 818  * is_a_number(number)
 819  *
 820  * is the string a number in one of the forms we want to use?
 821  */
 822 
 823 static int
 824 is_a_number(char *number)
 825 {
 826         int ret = 1;
 827         int hex = 0;
 828 
 829         if (strncmp(number, "0x", 2) == 0) {
 830                 number += 2;
 831                 hex = 1;
 832         } else if (*number == '-') {
 833                 number++; /* skip the minus */
 834         }
 835         while (ret == 1 && *number != '\0') {
 836                 if (hex) {
 837                         ret = isxdigit(*number++);
 838                 } else {
 839                         ret = isdigit(*number++);
 840                 }
 841         }
 842         return (ret);
 843 }
 844 
 845 /*
 846  * Look for the specified tag in the configuration file. If it is found,
 847  * enable logging and set the logging configuration information for exp.
 848  */
 849 static void
 850 configlog(struct exportdata *exp, char *tag)
 851 {
 852         nfsl_config_t *configlist = NULL, *configp;
 853         int error = 0;
 854         char globaltag[] = DEFAULTTAG;
 855 
 856         /*
 857          * Sends config errors to stderr
 858          */
 859         nfsl_errs_to_syslog = B_FALSE;
 860 
 861         /*
 862          * get the list of configuration settings
 863          */
 864         error = nfsl_getconfig_list(&configlist);
 865         if (error) {
 866                 (void) fprintf(stderr,
 867                     dgettext(TEXT_DOMAIN, "Cannot get log configuration: %s\n"),
 868                     strerror(error));
 869         }
 870 
 871         if (tag == NULL)
 872                 tag = globaltag;
 873         if ((configp = nfsl_findconfig(configlist, tag, &error)) == NULL) {
 874                 nfsl_freeconfig_list(&configlist);
 875                 (void) fprintf(stderr,
 876                     dgettext(TEXT_DOMAIN, "No tags matching \"%s\"\n"), tag);
 877                 /* bad configuration */
 878                 error = ENOENT;
 879                 goto err;
 880         }
 881 
 882         if ((exp->ex_tag = strdup(tag)) == NULL) {
 883                 error = ENOMEM;
 884                 goto out;
 885         }
 886         if ((exp->ex_log_buffer = strdup(configp->nc_bufferpath)) == NULL) {
 887                 error = ENOMEM;
 888                 goto out;
 889         }
 890         exp->ex_flags |= EX_LOG;
 891         if (configp->nc_rpclogpath != NULL)
 892                 exp->ex_flags |= EX_LOG_ALLOPS;
 893 out:
 894         if (configlist != NULL)
 895                 nfsl_freeconfig_list(&configlist);
 896 
 897 err:
 898         if (error != 0) {
 899                 free(exp->ex_tag);
 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                 *count = 0;
1100         } else {
1101                 for (i = 0; i < c; i++) {
1102                         host = strtok(list, ":");
1103                         if (!nfs_get_root_principal(sec, host, &a[i])) {
1104                                 while (i > 0)
1105                                         free(a[--i]);
1106                                 free(a);
1107                                 a = NULL;
1108                                 *count = 0;
1109                                 break;
1110                         }
1111                         list = NULL;
1112                 }
1113         }
1114 
1115         return (a);
1116 }
1117 
1118 /*
1119  * fill_security_from_secopts(sp, secopts)
1120  *
1121  * Fill the secinfo structure from the secopts optionset.
1122  */
1123 
1124 static int
1125 fill_security_from_secopts(struct secinfo *sp, sa_security_t secopts)
1126 {
1127         sa_property_t prop;
1128         char *type;
1129         int longform;
1130         int err = SC_NOERROR;
1131         uint32_t val;
1132 
1133         type = sa_get_security_attr(secopts, "sectype");
1134         if (type != NULL) {
1135                 /* named security type needs secinfo to be filled in */
1136                 err = nfs_getseconfig_byname(type, &sp->s_secinfo);
1137                 sa_free_attr_string(type);
1138                 if (err != SC_NOERROR)
1139                         return (err);
1140         } else {
1141                 /* default case */
1142                 err = nfs_getseconfig_default(&sp->s_secinfo);
1143                 if (err != SC_NOERROR)
1144                         return (err);
1145         }
1146 
1147         err = SA_OK;
1148         for (prop = sa_get_property(secopts, NULL);
1149             prop != NULL && err == SA_OK;
1150             prop = sa_get_next_property(prop)) {
1151                 char *name;
1152                 char *value;
1153 
1154                 name = sa_get_property_attr(prop, "type");
1155                 value = sa_get_property_attr(prop, "value");
1156 
1157                 longform = value != NULL && strcmp(value, "*") != 0;
1158 
1159                 switch (findopt(name)) {
1160                 case OPT_RO:
1161                         sp->s_flags |= longform ? M_ROL : M_RO;
1162                         break;
1163                 case OPT_RW:
1164                         sp->s_flags |= longform ? M_RWL : M_RW;
1165                         break;
1166                 case OPT_ROOT:
1167                         sp->s_flags |= M_ROOT;
1168                         /*
1169                          * if we are using AUTH_UNIX, handle like other things
1170                          * such as RO/RW
1171                          */
1172                         if (sp->s_secinfo.sc_rpcnum == AUTH_UNIX)
1173                                 break;
1174                         /* not AUTH_UNIX */
1175                         if (value != NULL) {
1176                                 sp->s_rootnames = get_rootnames(&sp->s_secinfo,
1177                                     value, &sp->s_rootcnt);
1178                                 if (sp->s_rootnames == NULL) {
1179                                         err = SA_BAD_VALUE;
1180                                         (void) fprintf(stderr,
1181                                             dgettext(TEXT_DOMAIN,
1182                                             "Bad root list\n"));
1183                                 }
1184                         }
1185                         break;
1186                 case OPT_NONE:
1187                         sp->s_flags |= M_NONE;
1188                         break;
1189                 case OPT_WINDOW:
1190                         if (value != NULL) {
1191                                 sp->s_window = atoi(value);
1192                                 /* just in case */
1193                                 if (sp->s_window < 0)
1194                                         sp->s_window = DEF_WIN;
1195                         }
1196                         break;
1197                 case OPT_ROOT_MAPPING:
1198                         if (value != NULL && is_a_number(value)) {
1199                                 val = strtoul(value, NULL, 0);
1200                         } else {
1201                                 struct passwd *pw;
1202                                 pw = getpwnam(value != NULL ? value : "nobody");
1203                                 if (pw != NULL) {
1204                                         val = pw->pw_uid;
1205                                 } else {
1206                                         val = UID_NOBODY;
1207                                 }
1208                                 endpwent();
1209                         }
1210                         sp->s_rootid = val;
1211                         break;
1212                 case OPT_UIDMAP:
1213                 case OPT_GIDMAP:
1214                         sp->s_flags |= M_MAP;
1215                         break;
1216                 default:
1217                         break;
1218                 }
1219                 if (name != NULL)
1220                         sa_free_attr_string(name);
1221                 if (value != NULL)
1222                         sa_free_attr_string(value);
1223         }
1224         /* if rw/ro options not set, use default of RW */
1225         if ((sp->s_flags & NFS_RWMODES) == 0)
1226                 sp->s_flags |= M_RW;
1227         return (err);
1228 }
1229 
1230 /*
1231  * This is for testing only
1232  * It displays the export structure that
1233  * goes into the kernel.
1234  */
1235 static void
1236 printarg(char *path, struct exportdata *ep)
1237 {
1238         int i, j;
1239         struct secinfo *sp;
1240 
1241         if (debug == 0)
1242                 return;
1243 
1244         (void) printf("%s:\n", path);
1245         (void) printf("\tex_version = %d\n", ep->ex_version);
1246         (void) printf("\tex_path = %s\n", ep->ex_path);
1247         (void) printf("\tex_pathlen = %ld\n", (ulong_t)ep->ex_pathlen);
1248         (void) printf("\tex_flags: (0x%02x) ", ep->ex_flags);
1249         if (ep->ex_flags & EX_NOSUID)
1250                 (void) printf("NOSUID ");
1251         if (ep->ex_flags & EX_ACLOK)
1252                 (void) printf("ACLOK ");
1253         if (ep->ex_flags & EX_PUBLIC)
1254                 (void) printf("PUBLIC ");
1255         if (ep->ex_flags & EX_NOSUB)
1256                 (void) printf("NOSUB ");
1257         if (ep->ex_flags & EX_LOG)
1258                 (void) printf("LOG ");
1259         if (ep->ex_flags & EX_CHARMAP)
1260                 (void) printf("CHARMAP ");
1261         if (ep->ex_flags & EX_LOG_ALLOPS)
1262                 (void) printf("LOG_ALLOPS ");
1263         if (ep->ex_flags == 0)
1264                 (void) printf("(none)");
1265         (void)  printf("\n");
1266         if (ep->ex_flags & EX_LOG) {
1267                 (void) printf("\tex_log_buffer = %s\n",
1268                     (ep->ex_log_buffer ? ep->ex_log_buffer : "(NULL)"));
1269                 (void) printf("\tex_tag = %s\n",
1270                     (ep->ex_tag ? ep->ex_tag : "(NULL)"));
1271         }
1272         (void) printf("\tex_anon = %d\n", ep->ex_anon);
1273         (void) printf("\tex_seccnt = %d\n", ep->ex_seccnt);
1274         (void) printf("\n");
1275         for (i = 0; i < ep->ex_seccnt; i++) {
1276                 sp = &ep->ex_secinfo[i];
1277                 (void) printf("\t\ts_secinfo = %s\n", sp->s_secinfo.sc_name);
1278                 (void) printf("\t\ts_flags: (0x%02x) ", sp->s_flags);
1279                 if (sp->s_flags & M_ROOT) (void) printf("M_ROOT ");
1280                 if (sp->s_flags & M_RO) (void) printf("M_RO ");
1281                 if (sp->s_flags & M_ROL) (void) printf("M_ROL ");
1282                 if (sp->s_flags & M_RW) (void) printf("M_RW ");
1283                 if (sp->s_flags & M_RWL) (void) printf("M_RWL ");
1284                 if (sp->s_flags & M_NONE) (void) printf("M_NONE ");
1285                 if (sp->s_flags & M_MAP) (void) printf("M_MAP ");
1286                 if (sp->s_flags == 0) (void) printf("(none)");
1287                 (void) printf("\n");
1288                 (void) printf("\t\ts_window = %d\n", sp->s_window);
1289                 (void) printf("\t\ts_rootid = %d\n", sp->s_rootid);
1290                 (void) printf("\t\ts_rootcnt = %d ", sp->s_rootcnt);
1291                 (void) fflush(stdout);
1292                 for (j = 0; j < sp->s_rootcnt; j++)
1293                         (void) printf("%s ", sp->s_rootnames[j] ?
1294                             sp->s_rootnames[j] : "<null>");
1295                 (void) printf("\n\n");
1296         }
1297 }
1298 
1299 /*
1300  * count_security(opts)
1301  *
1302  * Count the number of security types (flavors). The optionset has
1303  * been populated with the security flavors as a holding mechanism.
1304  * We later use this number to allocate data structures.
1305  */
1306 
1307 static int
1308 count_security(sa_optionset_t opts)
1309 {
1310         int count = 0;
1311         sa_property_t prop;
1312         if (opts != NULL) {
1313                 for (prop = sa_get_property(opts, NULL); prop != NULL;
1314                     prop = sa_get_next_property(prop)) {
1315                         count++;
1316                 }
1317         }
1318         return (count);
1319 }
1320 
1321 /*
1322  * nfs_sprint_option(rbuff, rbuffsize, incr, prop, sep)
1323  *
1324  * provides a mechanism to format NFS properties into legacy output
1325  * format. If the buffer would overflow, it is reallocated and grown
1326  * as appropriate. Special cases of converting internal form of values
1327  * to those used by "share" are done. this function does one property
1328  * at a time.
1329  */
1330 
1331 static int
1332 nfs_sprint_option(char **rbuff, size_t *rbuffsize, size_t incr,
1333     sa_property_t prop, int sep)
1334 {
1335         char *name;
1336         char *value;
1337         int curlen;
1338         char *buff = *rbuff;
1339         size_t buffsize = *rbuffsize;
1340         int printed = B_FALSE;
1341 
1342         name = sa_get_property_attr(prop, "type");
1343         value = sa_get_property_attr(prop, "value");
1344         if (buff != NULL)
1345                 curlen = strlen(buff);
1346         else
1347                 curlen = 0;
1348         if (name != NULL) {
1349                 int len;
1350                 len = strlen(name) + sep;
1351 
1352                 /*
1353                  * A future RFE would be to replace this with more
1354                  * generic code and to possibly handle more types.
1355                  */
1356                 switch (gettype(name)) {
1357                 case OPT_TYPE_BOOLEAN:
1358                         /*
1359                          * For NFS, boolean value of FALSE means it
1360                          * doesn't show up in the option list at all.
1361                          */
1362                         if (value != NULL && strcasecmp(value, "false") == 0)
1363                                 goto skip;
1364                         if (value != NULL) {
1365                                 sa_free_attr_string(value);
1366                                 value = NULL;
1367                         }
1368                         break;
1369                 case OPT_TYPE_ACCLIST:
1370                         if (value != NULL && strcmp(value, "*") == 0) {
1371                                 sa_free_attr_string(value);
1372                                 value = NULL;
1373                         } else {
1374                                 if (value != NULL)
1375                                         len += 1 + strlen(value);
1376                         }
1377                         break;
1378                 case OPT_TYPE_LOGTAG:
1379                         if (value != NULL && strlen(value) == 0) {
1380                                 sa_free_attr_string(value);
1381                                 value = NULL;
1382                         } else {
1383                                 if (value != NULL)
1384                                         len += 1 + strlen(value);
1385                         }
1386                         break;
1387                 default:
1388                         if (value != NULL)
1389                                 len += 1 + strlen(value);
1390                         break;
1391                 }
1392                 while (buffsize <= (curlen + len)) {
1393                         /* need more room */
1394                         buffsize += incr;
1395                         buff = realloc(buff, buffsize);
1396                         if (buff == NULL) {
1397                                 /* realloc failed so free everything */
1398                                 if (*rbuff != NULL)
1399                                         free(*rbuff);
1400                         }
1401                         *rbuff = buff;
1402                         *rbuffsize = buffsize;
1403                         if (buff == NULL)
1404                                 goto skip;
1405 
1406                 }
1407 
1408                 if (buff == NULL)
1409                         goto skip;
1410 
1411                 if (value == NULL) {
1412                         (void) snprintf(buff + curlen, buffsize - curlen,
1413                             "%s%s", sep ? "," : "", name);
1414                 } else {
1415                         (void) snprintf(buff + curlen, buffsize - curlen,
1416                             "%s%s=%s", sep ? "," : "",
1417                             name, value != NULL ? value : "");
1418                 }
1419                 printed = B_TRUE;
1420         }
1421 skip:
1422         if (name != NULL)
1423                 sa_free_attr_string(name);
1424         if (value != NULL)
1425                 sa_free_attr_string(value);
1426         return (printed);
1427 }
1428 
1429 /*
1430  * nfs_format_options(group, hier)
1431  *
1432  * format all the options on the group into an old-style option
1433  * string. If hier is non-zero, walk up the tree to get inherited
1434  * options.
1435  */
1436 
1437 static char *
1438 nfs_format_options(sa_group_t group, int hier)
1439 {
1440         sa_optionset_t options = NULL;
1441         sa_optionset_t secoptions = NULL;
1442         sa_property_t prop, secprop;
1443         sa_security_t security = NULL;
1444         char *buff;
1445         size_t buffsize;
1446         char *sectype = NULL;
1447         int sep = 0;
1448 
1449 
1450         buff = malloc(OPT_CHUNK);
1451         if (buff == NULL) {
1452                 return (NULL);
1453         }
1454 
1455         buff[0] = '\0';
1456         buffsize = OPT_CHUNK;
1457 
1458         /*
1459          * We may have a an optionset relative to this item. format
1460          * these if we find them and then add any security definitions.
1461          */
1462 
1463         options = sa_get_derived_optionset(group, "nfs", hier);
1464 
1465         /*
1466          * do the default set first but skip any option that is also
1467          * in the protocol specific optionset.
1468          */
1469         if (options != NULL) {
1470                 for (prop = sa_get_property(options, NULL);
1471                     prop != NULL; prop = sa_get_next_property(prop)) {
1472                         /*
1473                          * use this one since we skipped any
1474                          * of these that were also in
1475                          * optdefault
1476                          */
1477                         if (nfs_sprint_option(&buff, &buffsize, OPT_CHUNK,
1478                             prop, sep))
1479                                 sep = 1;
1480                         if (buff == NULL) {
1481                                 /*
1482                                  * buff could become NULL if there
1483                                  * isn't enough memory for
1484                                  * nfs_sprint_option to realloc()
1485                                  * as necessary. We can't really
1486                                  * do anything about it at this
1487                                  * point so we return NULL.  The
1488                                  * caller should handle the
1489                                  * failure.
1490                                  */
1491                                 if (options != NULL)
1492                                         sa_free_derived_optionset(
1493                                             options);
1494                                 return (buff);
1495                         }
1496                 }
1497         }
1498         secoptions = (sa_optionset_t)sa_get_all_security_types(group,
1499             "nfs", hier);
1500         if (secoptions != NULL) {
1501                 for (secprop = sa_get_property(secoptions, NULL);
1502                     secprop != NULL;
1503                     secprop = sa_get_next_property(secprop)) {
1504                         sectype = sa_get_property_attr(secprop, "type");
1505                         security =
1506                             (sa_security_t)sa_get_derived_security(
1507                             group, sectype, "nfs", hier);
1508                         if (security != NULL) {
1509                                 if (sectype != NULL) {
1510                                         prop = sa_create_property(
1511                                             "sec", sectype);
1512                                         if (prop == NULL)
1513                                                 goto err;
1514                                         if (nfs_sprint_option(&buff,
1515                                             &buffsize, OPT_CHUNK, prop, sep))
1516                                                 sep = 1;
1517                                         (void) sa_remove_property(prop);
1518                                         if (buff == NULL)
1519                                                 goto err;
1520                                 }
1521                                 for (prop = sa_get_property(security,
1522                                     NULL); prop != NULL;
1523                                     prop = sa_get_next_property(prop)) {
1524                                         if (nfs_sprint_option(&buff,
1525                                             &buffsize, OPT_CHUNK, prop, sep))
1526                                                 sep = 1;
1527                                         if (buff == NULL)
1528                                                 goto err;
1529                                 }
1530                                 sa_free_derived_optionset(security);
1531                         }
1532                         if (sectype != NULL)
1533                                 sa_free_attr_string(sectype);
1534                 }
1535                 sa_free_derived_optionset(secoptions);
1536         }
1537 
1538         if (options != NULL)
1539                 sa_free_derived_optionset(options);
1540         return (buff);
1541 
1542 err:
1543         /*
1544          * If we couldn't allocate memory for option printing, we need
1545          * to break out of the nested loops, cleanup and return NULL.
1546          */
1547         if (secoptions != NULL)
1548                 sa_free_derived_optionset(secoptions);
1549         if (security != NULL)
1550                 sa_free_derived_optionset(security);
1551         if (sectype != NULL)
1552                 sa_free_attr_string(sectype);
1553         if (options != NULL)
1554                 sa_free_derived_optionset(options);
1555         return (buff);
1556 }
1557 
1558 /*
1559  * Append an entry to the nfslogtab file
1560  */
1561 static int
1562 nfslogtab_add(char *dir, char *buffer, char *tag)
1563 {
1564         FILE *f;
1565         struct logtab_ent lep;
1566         int error = 0;
1567 
1568         /*
1569          * Open the file for update and create it if necessary.
1570          * This may leave the I/O offset at the end of the file,
1571          * so rewind back to the beginning of the file.
1572          */
1573         f = fopen(NFSLOGTAB, "a+");
1574         if (f == NULL) {
1575                 error = errno;
1576                 goto out;
1577         }
1578         rewind(f);
1579 
1580         if (lockf(fileno(f), F_LOCK, 0L) < 0) {
1581                 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1582                     "share complete, however failed to lock %s "
1583                     "for update: %s\n"), NFSLOGTAB, strerror(errno));
1584                 error = -1;
1585                 goto out;
1586         }
1587 
1588         if (logtab_deactivate_after_boot(f) == -1) {
1589                 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1590                     "share complete, however could not deactivate "
1591                     "entries in %s\n"), NFSLOGTAB);
1592                 error = -1;
1593                 goto out;
1594         }
1595 
1596         /*
1597          * Remove entries matching buffer and sharepoint since we're
1598          * going to replace it with perhaps an entry with a new tag.
1599          */
1600         if (logtab_rement(f, buffer, dir, NULL, -1)) {
1601                 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1602                     "share complete, however could not remove matching "
1603                     "entries in %s\n"), NFSLOGTAB);
1604                 error = -1;
1605                 goto out;
1606         }
1607 
1608         /*
1609          * Deactivate all active entries matching this sharepoint
1610          */
1611         if (logtab_deactivate(f, NULL, dir, NULL)) {
1612                 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1613                     "share complete, however could not deactivate matching "
1614                     "entries in %s\n"), NFSLOGTAB);
1615                 error = -1;
1616                 goto out;
1617         }
1618 
1619         lep.le_buffer = buffer;
1620         lep.le_path = dir;
1621         lep.le_tag = tag;
1622         lep.le_state = LES_ACTIVE;
1623 
1624         /*
1625          * Add new sharepoint / buffer location to nfslogtab
1626          */
1627         if (logtab_putent(f, &lep) < 0) {
1628                 (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1629                     "share complete, however could not add %s to %s\n"),
1630                     dir, NFSLOGTAB);
1631                 error = -1;
1632         }
1633 
1634 out:
1635         if (f != NULL)
1636                 (void) fclose(f);
1637         return (error);
1638 }
1639 
1640 /*
1641  * Deactivate an entry from the nfslogtab file
1642  */
1643 static int
1644 nfslogtab_deactivate(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
1901          * svc:/network/nfs/server service first if it isn't running.
1902          */
1903         /* check svc:/network/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, 1024, 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, 256,
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 #define PROTO_OPT_MOUNTD_PORT                   17
2567         {"mountd_port",
2568             "mountd_port", PROTO_OPT_MOUNTD_PORT,
2569             OPT_TYPE_NUMBER, 0, SVC_MOUNTD, 1, UINT16_MAX},
2570 #define PROTO_OPT_STATD_PORT                    18
2571         {"statd_port",
2572             "statd_port", PROTO_OPT_STATD_PORT,
2573             OPT_TYPE_NUMBER, 0, SVC_STATD, 1, UINT16_MAX},
2574         {NULL}
2575 };
2576 
2577 /*
2578  * the protoset holds the defined options so we don't have to read
2579  * them multiple times
2580  */
2581 static sa_protocol_properties_t protoset;
2582 
2583 static int
2584 findprotoopt(char *name, int whichname)
2585 {
2586         int i;
2587         for (i = 0; proto_options[i].tag != NULL; i++) {
2588                 if (whichname == 1) {
2589                         if (strcasecmp(proto_options[i].name, name) == 0)
2590                         return (i);
2591                 } else {
2592                         if (strcasecmp(proto_options[i].tag, name) == 0)
2593                                 return (i);
2594                 }
2595         }
2596         return (-1);
2597 }
2598 
2599 /*
2600  * fixcaselower(str)
2601  *
2602  * convert a string to lower case (inplace).
2603  */
2604 
2605 static void
2606 fixcaselower(char *str)
2607 {
2608         while (*str) {
2609                 *str = tolower(*str);
2610                 str++;
2611         }
2612 }
2613 
2614 /*
2615  * skipwhitespace(str)
2616  *
2617  * Skip leading white space. It is assumed that it is called with a
2618  * valid pointer.
2619  */
2620 
2621 static char *
2622 skipwhitespace(char *str)
2623 {
2624         while (*str && isspace(*str))
2625                 str++;
2626 
2627         return (str);
2628 }
2629 
2630 /*
2631  * extractprop()
2632  *
2633  * Extract the property and value out of the line and create the
2634  * property in the optionset.
2635  */
2636 static int
2637 extractprop(char *name, char *value)
2638 {
2639         sa_property_t prop;
2640         int index;
2641         int ret = SA_OK;
2642         /*
2643          * Remove any leading
2644          * white space.
2645          */
2646         name = skipwhitespace(name);
2647 
2648         index = findprotoopt(name, 1);
2649         if (index >= 0) {
2650                 fixcaselower(name);
2651                 prop = sa_create_property(proto_options[index].name, value);
2652                 if (prop != NULL)
2653                         ret = sa_add_protocol_property(protoset, prop);
2654                 else
2655                         ret = SA_NO_MEMORY;
2656         }
2657         return (ret);
2658 }
2659 
2660 scf_type_t
2661 getscftype(int type)
2662 {
2663         scf_type_t ret;
2664 
2665         switch (type) {
2666         case OPT_TYPE_NUMBER:
2667                 ret = SCF_TYPE_INTEGER;
2668         break;
2669         case OPT_TYPE_BOOLEAN:
2670                 ret = SCF_TYPE_BOOLEAN;
2671         break;
2672         default:
2673                 ret = SCF_TYPE_ASTRING;
2674         }
2675         return (ret);
2676 }
2677 
2678 char *
2679 getsvcname(uint32_t svcs)
2680 {
2681         char *service;
2682         switch (svcs) {
2683                 case SVC_LOCKD:
2684                         service = LOCKD;
2685                         break;
2686                 case SVC_STATD:
2687                         service = STATD;
2688                         break;
2689                 case SVC_NFSD:
2690                         service = NFSD;
2691                         break;
2692                 case SVC_CLIENT:
2693                         service = NFS_CLIENT_SVC;
2694                         break;
2695                 case SVC_NFS4CBD:
2696                         service = NFS4CBD;
2697                         break;
2698                 case SVC_NFSMAPID:
2699                         service = NFSMAPID;
2700                         break;
2701                 case SVC_RQUOTAD:
2702                         service = RQUOTAD;
2703                         break;
2704                 case SVC_NFSLOGD:
2705                         service = NFSLOGD;
2706                         break;
2707                 case SVC_REPARSED:
2708                         service = REPARSED;
2709                         break;
2710                 default:
2711                         service = NFSD;
2712         }
2713         return (service);
2714 }
2715 
2716 /*
2717  * initprotofromsmf()
2718  *
2719  * Read NFS SMF properties and add the defined values to the
2720  * protoset.  Note that default values are known from the built in
2721  * table in case SMF doesn't have a definition. Not having
2722  * SMF properties is OK since we have builtin default
2723  * values.
2724  */
2725 static int
2726 initprotofromsmf()
2727 {
2728         char name[PATH_MAX];
2729         char value[PATH_MAX];
2730         int ret = SA_OK, bufsz = 0, i;
2731 
2732         protoset = sa_create_protocol_properties("nfs");
2733         if (protoset != NULL) {
2734                 for (i = 0; proto_options[i].tag != NULL; i++) {
2735                         scf_type_t ptype;
2736                         char *svc_name;
2737 
2738                         bzero(value, PATH_MAX);
2739                         (void) strncpy(name, proto_options[i].name, PATH_MAX);
2740                         /* Replace NULL with the correct instance */
2741                         ptype = getscftype(proto_options[i].type);
2742                         svc_name = getsvcname(proto_options[i].svcs);
2743                         bufsz = PATH_MAX;
2744                         ret = nfs_smf_get_prop(name, value,
2745                             (char *)DEFAULT_INSTANCE, ptype,
2746                             svc_name, &bufsz);
2747                         if (ret == SA_OK) {
2748                                 ret = extractprop(name, value);
2749                         }
2750                 }
2751         } else {
2752                 ret = SA_NO_MEMORY;
2753         }
2754 
2755         return (ret);
2756 }
2757 
2758 /*
2759  * add_defaults()
2760  *
2761  * Add the default values for any property not defined
2762  * in NFS SMF repository.
2763  * Values are set according to their defined types.
2764  */
2765 
2766 static void
2767 add_defaults()
2768 {
2769         int i;
2770         char number[MAXDIGITS];
2771 
2772         for (i = 0; proto_options[i].tag != NULL; i++) {
2773                 sa_property_t prop;
2774                 prop = sa_get_protocol_property(protoset,
2775                     proto_options[i].name);
2776                 if (prop == NULL) {
2777                         /* add the default value */
2778                         switch (proto_options[i].type) {
2779                         case OPT_TYPE_NUMBER:
2780                                 (void) snprintf(number, sizeof (number), "%d",
2781                                     proto_options[i].defvalue.intval);
2782                                 prop = sa_create_property(proto_options[i].name,
2783                                     number);
2784                                 break;
2785 
2786                         case OPT_TYPE_BOOLEAN:
2787                                 prop = sa_create_property(proto_options[i].name,
2788                                     proto_options[i].defvalue.intval ?
2789                                     "true" : "false");
2790                                 break;
2791 
2792                         case OPT_TYPE_ONOFF:
2793                                 prop = sa_create_property(proto_options[i].name,
2794                                     proto_options[i].defvalue.intval ?
2795                                     "on" : "off");
2796                                 break;
2797 
2798                         default:
2799                                 /* treat as strings of zero length */
2800                                 prop = sa_create_property(proto_options[i].name,
2801                                     "");
2802                                 break;
2803                         }
2804                         if (prop != NULL)
2805                                 (void) sa_add_protocol_property(protoset, prop);
2806                 }
2807         }
2808 }
2809 
2810 static void
2811 free_protoprops()
2812 {
2813         if (protoset != NULL) {
2814                 xmlFreeNode(protoset);
2815                 protoset = NULL;
2816         }
2817 }
2818 
2819 /*
2820  * nfs_init()
2821  *
2822  * Initialize the NFS plugin.
2823  */
2824 
2825 static int
2826 nfs_init()
2827 {
2828         int ret = SA_OK;
2829 
2830         if (sa_plugin_ops.sa_init != nfs_init) {
2831                 (void) printf(dgettext(TEXT_DOMAIN,
2832                     "NFS plugin not properly initialized\n"));
2833                 return (SA_CONFIG_ERR);
2834         }
2835 
2836         ret = initprotofromsmf();
2837         if (ret != SA_OK) {
2838                 (void) printf(dgettext(TEXT_DOMAIN,
2839                     "NFS plugin problem with SMF repository: %s\n"),
2840                     sa_errorstr(ret));
2841                 ret = SA_OK;
2842         }
2843         add_defaults();
2844 
2845         return (ret);
2846 }
2847 
2848 /*
2849  * nfs_fini()
2850  *
2851  * uninitialize the NFS plugin. Want to avoid memory leaks.
2852  */
2853 
2854 static void
2855 nfs_fini()
2856 {
2857         free_protoprops();
2858 }
2859 
2860 /*
2861  * nfs_get_proto_set()
2862  *
2863  * Return an optionset with all the protocol specific properties in
2864  * it.
2865  */
2866 
2867 static sa_protocol_properties_t
2868 nfs_get_proto_set()
2869 {
2870         return (protoset);
2871 }
2872 
2873 /*
2874  * service_in_state(service, chkstate)
2875  *
2876  * Want to know if the specified service is in the desired state
2877  * (chkstate) or not. Return true (1) if it is and false (0) if it
2878  * isn't.
2879  */
2880 static int
2881 service_in_state(char *service, const char *chkstate)
2882 {
2883         char *state;
2884         int ret = B_FALSE;
2885 
2886         state = smf_get_state(service);
2887         if (state != NULL) {
2888                 /* got the state so get the equality for the return value */
2889                 ret = strcmp(state, chkstate) == 0 ? B_TRUE : B_FALSE;
2890                 free(state);
2891         }
2892         return (ret);
2893 }
2894 
2895 /*
2896  * restart_service(svcs)
2897  *
2898  * Walk through the bit mask of services that need to be restarted in
2899  * order to use the new property values. Some properties affect
2900  * multiple daemons. Should only restart a service if it is currently
2901  * enabled (online).
2902  */
2903 
2904 static void
2905 restart_service(uint32_t svcs)
2906 {
2907         uint32_t mask;
2908         int ret;
2909         char *service;
2910 
2911         for (mask = 1; svcs != 0; mask <<= 1) {
2912                 switch (svcs & mask) {
2913                 case SVC_LOCKD:
2914                         service = LOCKD;
2915                         break;
2916                 case SVC_STATD:
2917                         service = STATD;
2918                         break;
2919                 case SVC_NFSD:
2920                         service = NFSD;
2921                         break;
2922                 case SVC_MOUNTD:
2923                         service = MOUNTD;
2924                         break;
2925                 case SVC_NFS4CBD:
2926                         service = NFS4CBD;
2927                         break;
2928                 case SVC_NFSMAPID:
2929                         service = NFSMAPID;
2930                         break;
2931                 case SVC_RQUOTAD:
2932                         service = RQUOTAD;
2933                         break;
2934                 case SVC_NFSLOGD:
2935                         service = NFSLOGD;
2936                         break;
2937                 case SVC_REPARSED:
2938                         service = REPARSED;
2939                         break;
2940                 case SVC_CLIENT:
2941                         service = NFS_CLIENT_SVC;
2942                         break;
2943                 default:
2944                         continue;
2945                 }
2946 
2947                 /*
2948                  * Only attempt to restart the service if it is
2949                  * currently running. In the future, it may be
2950                  * desirable to use smf_refresh_instance if the NFS
2951                  * services ever implement the refresh method.
2952                  */
2953                 if (service_in_state(service, SCF_STATE_STRING_ONLINE)) {
2954                         ret = smf_restart_instance(service);
2955                         /*
2956                          * There are only a few SMF errors at this point, but
2957                          * it is also possible that a bad value may have put
2958                          * the service into maintenance if there wasn't an
2959                          * SMF level error.
2960                          */
2961                         if (ret != 0) {
2962                                 (void) fprintf(stderr,
2963                                     dgettext(TEXT_DOMAIN,
2964                                     "%s failed to restart: %s\n"),
2965                                     service, scf_strerror(scf_error()));
2966                         } else {
2967                                 /*
2968                                  * Check whether it has gone to "maintenance"
2969                                  * mode or not. Maintenance implies something
2970                                  * went wrong.
2971                                  */
2972                                 if (service_in_state(service,
2973                                     SCF_STATE_STRING_MAINT)) {
2974                                         (void) fprintf(stderr,
2975                                             dgettext(TEXT_DOMAIN,
2976                                             "%s failed to restart\n"),
2977                                             service);
2978                                 }
2979                         }
2980                 }
2981                 svcs &= ~mask;
2982         }
2983 }
2984 
2985 /*
2986  * nfs_minmax_check(name, value)
2987  *
2988  * Verify that the value for the property specified by index is valid
2989  * relative to the opposite value in the case of a min/max variable.
2990  * Currently, server_minvers/server_maxvers and
2991  * client_minvers/client_maxvers are the only ones to check.
2992  */
2993 
2994 static int
2995 nfs_minmax_check(int index, int value)
2996 {
2997         int val;
2998         char *pval;
2999         sa_property_t prop;
3000         sa_optionset_t opts;
3001         int ret = B_TRUE;
3002 
3003         if (proto_options[index].other != NULL) {
3004                 /* have a property to compare against */
3005                 opts = nfs_get_proto_set();
3006                 prop = sa_get_property(opts, proto_options[index].other);
3007                 /*
3008                  * If we don't find the property, assume default
3009                  * values which will work since the max will be at the
3010                  * max and the min at the min.
3011                  */
3012                 if (prop != NULL) {
3013                         pval = sa_get_property_attr(prop, "value");
3014                         if (pval != NULL) {
3015                                 val = strtoul(pval, NULL, 0);
3016                                 if (proto_options[index].compare ==
3017                                     OPT_CMP_LE) {
3018                                         ret = value <= val ? B_TRUE : B_FALSE;
3019                                 } else if (proto_options[index].compare ==
3020                                     OPT_CMP_GE) {
3021                                         ret = value >= val ? B_TRUE : B_FALSE;
3022                                 }
3023                                 sa_free_attr_string(pval);
3024                         }
3025                 }
3026         }
3027         return (ret);
3028 }
3029 
3030 /*
3031  * nfs_validate_proto_prop(index, name, value)
3032  *
3033  * Verify that the property specified by name can take the new
3034  * value. This is a sanity check to prevent bad values getting into
3035  * the default files. All values need to be checked against what is
3036  * allowed by their defined type. If a type isn't explicitly defined
3037  * here, it is treated as a string.
3038  *
3039  * Note that OPT_TYPE_NUMBER will additionally check that the value is
3040  * within the range specified and potentially against another property
3041  * value as well as specified in the proto_options members other and
3042  * compare.
3043  */
3044 
3045 static int
3046 nfs_validate_proto_prop(int index, char *name, char *value)
3047 {
3048         int ret = SA_OK;
3049         char *cp;
3050 #ifdef lint
3051         name = name;
3052 #endif
3053         switch (proto_options[index].type) {
3054         case OPT_TYPE_NUMBER:
3055                 if (!is_a_number(value))
3056                         ret = SA_BAD_VALUE;
3057                 else {
3058                         int val;
3059                         val = strtoul(value, NULL, 0);
3060                         if (val < proto_options[index].minval ||
3061                             val > proto_options[index].maxval)
3062                                 ret = SA_BAD_VALUE;
3063                         /*
3064                          * For server_versmin/server_versmax and
3065                          * client_versmin/client_versmax, the value of the
3066                          * min(max) should be checked to be correct relative
3067                          * to the current max(min).
3068                          */
3069                         if (!nfs_minmax_check(index, val)) {
3070                                 ret = SA_BAD_VALUE;
3071                         }
3072                 }
3073                 break;
3074 
3075         case OPT_TYPE_DOMAIN:
3076                 /*
3077                  * needs to be a qualified domain so will have at
3078                  * least one period and other characters on either
3079                  * side of it.  A zero length string is also allowed
3080                  * and is the way to turn off the override.
3081                  */
3082                 if (strlen(value) == 0)
3083                         break;
3084                 cp = strchr(value, '.');
3085                 if (cp == NULL || cp == value || strchr(value, '@') != NULL)
3086                         ret = SA_BAD_VALUE;
3087                 break;
3088 
3089         case OPT_TYPE_BOOLEAN:
3090                 if (strlen(value) == 0 ||
3091                     strcasecmp(value, "true") == 0 ||
3092                     strcmp(value, "1") == 0 ||
3093                     strcasecmp(value, "false") == 0 ||
3094                     strcmp(value, "0") == 0) {
3095                         ret = SA_OK;
3096                 } else {
3097                         ret = SA_BAD_VALUE;
3098                 }
3099                 break;
3100 
3101         case OPT_TYPE_ONOFF:
3102                 if (strcasecmp(value, "on") != 0 &&
3103                     strcasecmp(value, "off") != 0) {
3104                         ret = SA_BAD_VALUE;
3105                 }
3106                 break;
3107 
3108         case OPT_TYPE_PROTOCOL: {
3109                 struct netconfig *nconf;
3110                 void *nc;
3111                 boolean_t pfound = B_FALSE;
3112 
3113                 if (strcasecmp(value, "all") == 0)
3114                         break;
3115 
3116                 if ((nc = setnetconfig()) == NULL) {
3117                         (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
3118                             "setnetconfig failed: %s\n"), strerror(errno));
3119                 } else {
3120                         while ((nconf = getnetconfig(nc)) != NULL) {
3121                                 if (strcmp(nconf->nc_proto, value) == 0) {
3122                                         pfound = B_TRUE;
3123                                         break;
3124                                 }
3125                         }
3126                         (void) endnetconfig(nc);
3127                 }
3128 
3129                 if (!pfound)
3130                         ret = SA_BAD_VALUE;
3131                 break;
3132         }
3133 
3134         default:
3135                 /* treat as a string */
3136                 break;
3137         }
3138         return (ret);
3139 }
3140 
3141 /*
3142  * nfs_set_proto_prop(prop)
3143  *
3144  * check that prop is valid.
3145  */
3146 
3147 static int
3148 nfs_set_proto_prop(sa_property_t prop)
3149 {
3150         int ret = SA_OK;
3151         char *name;
3152         char *value;
3153 
3154         name = sa_get_property_attr(prop, "type");
3155         value = sa_get_property_attr(prop, "value");
3156         if (name != NULL && value != NULL) {
3157                 scf_type_t sctype;
3158                 char *svc_name;
3159                 char *instance = NULL;
3160                 int index = findprotoopt(name, 1);
3161 
3162                 ret = nfs_validate_proto_prop(index, name, value);
3163                 if (ret == SA_OK) {
3164                         sctype = getscftype(proto_options[index].type);
3165                         svc_name = getsvcname(proto_options[index].svcs);
3166                         if (sctype == SCF_TYPE_BOOLEAN) {
3167                                 if (value != NULL)
3168                                         sa_free_attr_string(value);
3169                                 if (string_to_boolean(value) == 0)
3170                                         value = strdup("0");
3171                                 else
3172                                         value = strdup("1");
3173                         }
3174                         ret = nfs_smf_set_prop(name, value, instance, sctype,
3175                             svc_name);
3176                         if (ret == SA_OK) {
3177                                 restart_service(proto_options[index].svcs);
3178                         } else {
3179                                 (void) printf(dgettext(TEXT_DOMAIN,
3180                                     "Cannot restart NFS services : %s\n"),
3181                                     sa_errorstr(ret));
3182                         }
3183                 }
3184         }
3185         if (name != NULL)
3186                 sa_free_attr_string(name);
3187         if (value != NULL)
3188                 sa_free_attr_string(value);
3189         return (ret);
3190 }
3191 
3192 /*
3193  * nfs_get_status()
3194  *
3195  * What is the current status of the nfsd? We use the SMF state here.
3196  * Caller must free the returned value.
3197  */
3198 
3199 static char *
3200 nfs_get_status()
3201 {
3202         return (smf_get_state(NFSD));
3203 }
3204 
3205 /*
3206  * nfs_space_alias(alias)
3207  *
3208  * Lookup the space (security) name. If it is default, convert to the
3209  * real name.
3210  */
3211 
3212 static char *
3213 nfs_space_alias(char *space)
3214 {
3215         char *name = space;
3216         seconfig_t secconf;
3217 
3218         /*
3219          * Only the space named "default" is special. If it is used,
3220          * the default needs to be looked up and the real name used.
3221          * This is normally "sys" but could be changed.  We always
3222          * change default to the real name.
3223          */
3224         if (strcmp(space, "default") == 0 &&
3225             nfs_getseconfig_default(&secconf) == 0) {
3226                 if (nfs_getseconfig_bynumber(secconf.sc_nfsnum, &secconf) == 0)
3227                         name = secconf.sc_name;
3228         }
3229         return (strdup(name));
3230 }
3231 
3232 /*
3233  * nfs_features()
3234  *
3235  * Return a mask of the features required.
3236  */
3237 
3238 static uint64_t
3239 nfs_features()
3240 {
3241         return ((uint64_t)SA_FEATURE_DFSTAB | SA_FEATURE_SERVER);
3242 }