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