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