7029 want per-process exploit mitigation features (secflags)
7030 want basic address space layout randomization (aslr)
7031 noexec_user_stack should be a secflag
7032 want a means to forbid mappings around NULL.

   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 2014 Gary Mills
  24  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  25  * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
  26  */
  27 
  28 #include <libsysevent.h>
  29 #include <pthread.h>
  30 #include <stdlib.h>
  31 #include <errno.h>
  32 #include <fnmatch.h>
  33 #include <strings.h>
  34 #include <unistd.h>
  35 #include <assert.h>
  36 #include <libgen.h>
  37 #include <libintl.h>
  38 #include <alloca.h>
  39 #include <ctype.h>
  40 #include <sys/acl.h>
  41 #include <sys/stat.h>
  42 #include <sys/brand.h>
  43 #include <sys/mntio.h>
  44 #include <sys/mnttab.h>
  45 #include <sys/nvpair.h>
  46 #include <sys/types.h>
  47 #include <sys/sockio.h>
  48 #include <sys/systeminfo.h>
  49 #include <ftw.h>
  50 #include <pool.h>
  51 #include <libscf.h>
  52 #include <libproc.h>
  53 #include <sys/priocntl.h>
  54 #include <libuutil.h>
  55 #include <wait.h>
  56 #include <bsm/adt.h>
  57 #include <auth_attr.h>
  58 #include <auth_list.h>
  59 #include <secdb.h>
  60 #include <user_attr.h>
  61 #include <prof_attr.h>
  62 
  63 #include <arpa/inet.h>
  64 #include <netdb.h>
  65 
  66 #include <libxml/xmlmemory.h>
  67 #include <libxml/parser.h>
  68 
  69 #include <libdevinfo.h>
  70 #include <uuid/uuid.h>
  71 #include <dirent.h>
  72 #include <libbrand.h>
  73 
  74 #include <libzonecfg.h>
  75 #include "zonecfg_impl.h"
  76 
  77 #define _PATH_TMPFILE   "/zonecfg.XXXXXX"
  78 #define ZONE_CB_RETRY_COUNT             10
  79 #define ZONE_EVENT_PING_SUBCLASS        "ping"
  80 #define ZONE_EVENT_PING_PUBLISHER       "solaris"
  81 
  82 /* Hard-code the DTD element/attribute/entity names just once, here. */
  83 #define DTD_ELEM_ATTR           (const xmlChar *) "attr"
  84 #define DTD_ELEM_COMMENT        (const xmlChar *) "comment"
  85 #define DTD_ELEM_DEVICE         (const xmlChar *) "device"
  86 #define DTD_ELEM_FS             (const xmlChar *) "filesystem"
  87 #define DTD_ELEM_FSOPTION       (const xmlChar *) "fsoption"
  88 #define DTD_ELEM_NET            (const xmlChar *) "network"
  89 #define DTD_ELEM_RCTL           (const xmlChar *) "rctl"
  90 #define DTD_ELEM_RCTLVALUE      (const xmlChar *) "rctl-value"
  91 #define DTD_ELEM_ZONE           (const xmlChar *) "zone"
  92 #define DTD_ELEM_DATASET        (const xmlChar *) "dataset"
  93 #define DTD_ELEM_TMPPOOL        (const xmlChar *) "tmp_pool"
  94 #define DTD_ELEM_PSET           (const xmlChar *) "pset"
  95 #define DTD_ELEM_MCAP           (const xmlChar *) "mcap"
  96 #define DTD_ELEM_PACKAGE        (const xmlChar *) "package"
  97 #define DTD_ELEM_OBSOLETES      (const xmlChar *) "obsoletes"
  98 #define DTD_ELEM_DEV_PERM       (const xmlChar *) "dev-perm"
  99 #define DTD_ELEM_ADMIN          (const xmlChar *) "admin"

 100 
 101 #define DTD_ATTR_ACTION         (const xmlChar *) "action"
 102 #define DTD_ATTR_ADDRESS        (const xmlChar *) "address"
 103 #define DTD_ATTR_ALLOWED_ADDRESS        (const xmlChar *) "allowed-address"
 104 #define DTD_ATTR_AUTOBOOT       (const xmlChar *) "autoboot"
 105 #define DTD_ATTR_IPTYPE         (const xmlChar *) "ip-type"
 106 #define DTD_ATTR_DEFROUTER      (const xmlChar *) "defrouter"
 107 #define DTD_ATTR_DIR            (const xmlChar *) "directory"
 108 #define DTD_ATTR_LIMIT          (const xmlChar *) "limit"
 109 #define DTD_ATTR_LIMITPRIV      (const xmlChar *) "limitpriv"
 110 #define DTD_ATTR_BOOTARGS       (const xmlChar *) "bootargs"
 111 #define DTD_ATTR_SCHED          (const xmlChar *) "scheduling-class"
 112 #define DTD_ATTR_MATCH          (const xmlChar *) "match"
 113 #define DTD_ATTR_NAME           (const xmlChar *) "name"
 114 #define DTD_ATTR_PHYSICAL       (const xmlChar *) "physical"
 115 #define DTD_ATTR_POOL           (const xmlChar *) "pool"
 116 #define DTD_ATTR_PRIV           (const xmlChar *) "priv"
 117 #define DTD_ATTR_RAW            (const xmlChar *) "raw"
 118 #define DTD_ATTR_SPECIAL        (const xmlChar *) "special"
 119 #define DTD_ATTR_TYPE           (const xmlChar *) "type"
 120 #define DTD_ATTR_VALUE          (const xmlChar *) "value"
 121 #define DTD_ATTR_ZONEPATH       (const xmlChar *) "zonepath"
 122 #define DTD_ATTR_NCPU_MIN       (const xmlChar *) "ncpu_min"
 123 #define DTD_ATTR_NCPU_MAX       (const xmlChar *) "ncpu_max"
 124 #define DTD_ATTR_IMPORTANCE     (const xmlChar *) "importance"
 125 #define DTD_ATTR_PHYSCAP        (const xmlChar *) "physcap"
 126 #define DTD_ATTR_VERSION        (const xmlChar *) "version"
 127 #define DTD_ATTR_ID             (const xmlChar *) "id"
 128 #define DTD_ATTR_UID            (const xmlChar *) "uid"
 129 #define DTD_ATTR_GID            (const xmlChar *) "gid"
 130 #define DTD_ATTR_MODE           (const xmlChar *) "mode"
 131 #define DTD_ATTR_ACL            (const xmlChar *) "acl"
 132 #define DTD_ATTR_BRAND          (const xmlChar *) "brand"
 133 #define DTD_ATTR_HOSTID         (const xmlChar *) "hostid"
 134 #define DTD_ATTR_USER           (const xmlChar *) "user"
 135 #define DTD_ATTR_AUTHS          (const xmlChar *) "auths"
 136 #define DTD_ATTR_FS_ALLOWED     (const xmlChar *) "fs-allowed"




 137 
 138 #define DTD_ENTITY_BOOLEAN      "boolean"
 139 #define DTD_ENTITY_DEVPATH      "devpath"
 140 #define DTD_ENTITY_DRIVER       "driver"
 141 #define DTD_ENTITY_DRVMIN       "drv_min"
 142 #define DTD_ENTITY_FALSE        "false"
 143 #define DTD_ENTITY_INT          "int"
 144 #define DTD_ENTITY_STRING       "string"
 145 #define DTD_ENTITY_TRUE         "true"
 146 #define DTD_ENTITY_UINT         "uint"
 147 
 148 #define DTD_ENTITY_BOOL_LEN     6       /* "false" */
 149 
 150 #define ATTACH_FORCED   "SUNWattached.xml"
 151 
 152 #define TMP_POOL_NAME   "SUNWtmp_%s"
 153 #define MAX_TMP_POOL_NAME       (ZONENAME_MAX + 9)
 154 #define RCAP_SERVICE    "system/rcap:default"
 155 #define POOLD_SERVICE   "system/pools/dynamic:default"
 156 
 157 /*
 158  * rctl alias definitions
 159  *
 160  * This holds the alias, the full rctl name, the default priv value, action
 161  * and lower limit.  The functions that handle rctl aliases step through
 162  * this table, matching on the alias, and using the full values for setting
 163  * the rctl entry as well the limit for validation.
 164  */
 165 static struct alias {
 166         char *shortname;
 167         char *realname;
 168         char *priv;
 169         char *action;
 170         uint64_t low_limit;
 171 } aliases[] = {
 172         {ALIAS_MAXLWPS, "zone.max-lwps", "privileged", "deny", 100},
 173         {ALIAS_MAXSHMMEM, "zone.max-shm-memory", "privileged", "deny", 0},
 174         {ALIAS_MAXSHMIDS, "zone.max-shm-ids", "privileged", "deny", 0},
 175         {ALIAS_MAXMSGIDS, "zone.max-msg-ids", "privileged", "deny", 0},
 176         {ALIAS_MAXSEMIDS, "zone.max-sem-ids", "privileged", "deny", 0},
 177         {ALIAS_MAXLOCKEDMEM, "zone.max-locked-memory", "privileged", "deny", 0},
 178         {ALIAS_MAXSWAP, "zone.max-swap", "privileged", "deny", 0},
 179         {ALIAS_SHARES, "zone.cpu-shares", "privileged", "none", 0},
 180         {ALIAS_CPUCAP, "zone.cpu-cap", "privileged", "deny", 0},
 181         {ALIAS_MAXPROCS, "zone.max-processes", "privileged", "deny", 100},
 182         {NULL, NULL, NULL, NULL, 0}
 183 };
 184 
 185 /*
 186  * Structure for applying rctls to a running zone.  It allows important
 187  * process values to be passed together easily.
 188  */
 189 typedef struct pr_info_handle {
 190         struct ps_prochandle *pr;
 191         pid_t pid;
 192 } pr_info_handle_t;
 193 
 194 struct zone_dochandle {
 195         char            *zone_dh_rootdir;
 196         xmlDocPtr       zone_dh_doc;
 197         xmlNodePtr      zone_dh_cur;
 198         xmlNodePtr      zone_dh_top;
 199         boolean_t       zone_dh_newzone;
 200         boolean_t       zone_dh_snapshot;
 201         boolean_t       zone_dh_sw_inv;
 202         zone_userauths_t        *zone_dh_userauths;
 203         char            zone_dh_delete_name[ZONENAME_MAX];
 204 };
 205 
 206 struct znotify {
 207         void * zn_private;
 208         evchan_t *zn_eventchan;
 209         int (*zn_callback)(const  char *zonename, zoneid_t zid,
 210             const char *newstate, const char *oldstate, hrtime_t when, void *p);
 211         pthread_mutex_t zn_mutex;
 212         pthread_cond_t zn_cond;
 213         pthread_mutex_t zn_bigmutex;
 214         volatile enum {ZN_UNLOCKED, ZN_LOCKED, ZN_PING_INFLIGHT,
 215             ZN_PING_RECEIVED} zn_state;
 216         char zn_subscriber_id[MAX_SUBID_LEN];
 217         volatile boolean_t zn_failed;
 218         int zn_failure_count;
 219 };
 220 
 221 /* used to track nested zone-lock operations */
 222 static int zone_lock_cnt = 0;
 223 
 224 /* used to communicate lock status to children */
 225 #define LOCK_ENV_VAR    "_ZONEADM_LOCK_HELD"
 226 static char zoneadm_lock_held[] = LOCK_ENV_VAR"=1";
 227 static char zoneadm_lock_not_held[] = LOCK_ENV_VAR"=0";
 228 
 229 char *zonecfg_root = "";
 230 
 231 /*
 232  * For functions which return int, which is most of the functions herein,
 233  * the return values should be from the Z_foo set defined in <libzonecfg.h>.
 234  * In some instances, we take pains mapping some libc errno values to Z_foo
 235  * values from this set.
 236  */
 237 
 238 /*
 239  * Set the root (/) path for all zonecfg configuration files.  This is a
 240  * private interface used by Live Upgrade extensions to access zone
 241  * configuration inside mounted alternate boot environments.
 242  * This interface is also used by zoneadm mount and unmount subcommands.
 243  */
 244 void
 245 zonecfg_set_root(const char *rootpath)
 246 {
 247         if (*zonecfg_root != '\0')
 248                 free(zonecfg_root);
 249         if (rootpath == NULL || rootpath[0] == '\0' || rootpath[1] == '\0' ||
 250             (zonecfg_root = strdup(rootpath)) == NULL)
 251                 zonecfg_root = "";
 252 }
 253 
 254 const char *
 255 zonecfg_get_root(void)
 256 {
 257         return (zonecfg_root);
 258 }
 259 
 260 boolean_t
 261 zonecfg_in_alt_root(void)
 262 {
 263         return (*zonecfg_root != '\0');
 264 }
 265 
 266 /*
 267  * Callers of the _file_path() functions are expected to have the second
 268  * parameter be a (char foo[MAXPATHLEN]).
 269  */
 270 
 271 static boolean_t
 272 config_file_path(const char *zonename, char *answer)
 273 {
 274         return (snprintf(answer, MAXPATHLEN, "%s%s/%s.xml", zonecfg_root,
 275             ZONE_CONFIG_ROOT, zonename) < MAXPATHLEN);
 276 }
 277 
 278 static boolean_t
 279 snap_file_path(const char *zonename, char *answer)
 280 {
 281         return (snprintf(answer, MAXPATHLEN, "%s%s/%s.snapshot.xml",
 282             zonecfg_root, ZONE_SNAPSHOT_ROOT, zonename) < MAXPATHLEN);
 283 }
 284 
 285 /*ARGSUSED*/
 286 static void
 287 zonecfg_error_func(void *ctx, const char *msg, ...)
 288 {
 289         /*
 290          * This function does nothing by design.  Its purpose is to prevent
 291          * libxml from dumping unwanted messages to stdout/stderr.
 292          */
 293 }
 294 
 295 zone_dochandle_t
 296 zonecfg_init_handle(void)
 297 {
 298         zone_dochandle_t handle = calloc(1, sizeof (struct zone_dochandle));
 299         if (handle == NULL) {
 300                 errno = Z_NOMEM;
 301                 return (NULL);
 302         }
 303 
 304         /* generic libxml initialization */
 305         (void) xmlLineNumbersDefault(1);
 306         xmlLoadExtDtdDefaultValue |= XML_DETECT_IDS;
 307         xmlDoValidityCheckingDefaultValue = 1;
 308         (void) xmlKeepBlanksDefault(0);
 309         xmlGetWarningsDefaultValue = 0;
 310         xmlSetGenericErrorFunc(NULL, zonecfg_error_func);
 311 
 312         return (handle);
 313 }
 314 
 315 int
 316 zonecfg_check_handle(zone_dochandle_t handle)
 317 {
 318         if (handle == NULL || handle->zone_dh_doc == NULL)
 319                 return (Z_BAD_HANDLE);
 320         return (Z_OK);
 321 }
 322 
 323 void
 324 zonecfg_fini_handle(zone_dochandle_t handle)
 325 {
 326         if (zonecfg_check_handle(handle) == Z_OK)
 327                 xmlFreeDoc(handle->zone_dh_doc);
 328         if (handle != NULL)
 329                 free(handle);
 330 }
 331 
 332 static int
 333 zonecfg_destroy_impl(char *filename)
 334 {
 335         if (unlink(filename) == -1) {
 336                 if (errno == EACCES)
 337                         return (Z_ACCES);
 338                 if (errno == ENOENT)
 339                         return (Z_NO_ZONE);
 340                 return (Z_MISC_FS);
 341         }
 342         return (Z_OK);
 343 }
 344 
 345 int
 346 zonecfg_destroy(const char *zonename, boolean_t force)
 347 {
 348         char path[MAXPATHLEN];
 349         struct zoneent ze;
 350         int err, state_err;
 351         zone_state_t state;
 352 
 353         if (!config_file_path(zonename, path))
 354                 return (Z_MISC_FS);
 355 
 356         state_err = zone_get_state((char *)zonename, &state);
 357         err = access(path, W_OK);
 358 
 359         /*
 360          * If there is no file, and no index entry, reliably indicate that no
 361          * such zone exists.
 362          */
 363         if ((state_err == Z_NO_ZONE) && (err == -1) && (errno == ENOENT))
 364                 return (Z_NO_ZONE);
 365 
 366         /*
 367          * Handle any other filesystem related errors (except if the XML
 368          * file is missing, which we treat silently), unless we're forcing,
 369          * in which case we plow on.
 370          */
 371         if (err == -1 && errno != ENOENT) {
 372                 if (errno == EACCES)
 373                         return (Z_ACCES);
 374                 else if (!force)
 375                         return (Z_MISC_FS);
 376         }
 377 
 378         if (state > ZONE_STATE_INSTALLED)
 379                 return (Z_BAD_ZONE_STATE);
 380 
 381         if (!force && state > ZONE_STATE_CONFIGURED)
 382                 return (Z_BAD_ZONE_STATE);
 383 
 384         /*
 385          * Index deletion succeeds even if the entry doesn't exist.  So this
 386          * will fail only if we've had some more severe problem.
 387          */
 388         bzero(&ze, sizeof (ze));
 389         (void) strlcpy(ze.zone_name, zonename, sizeof (ze.zone_name));
 390         if ((err = putzoneent(&ze, PZE_REMOVE)) != Z_OK)
 391                 if (!force)
 392                         return (err);
 393 
 394         err = zonecfg_destroy_impl(path);
 395 
 396         /*
 397          * Treat failure to find the XML file silently, since, well, it's
 398          * gone, and with the index file cleaned up, we're done.
 399          */
 400         if (err == Z_OK || err == Z_NO_ZONE)
 401                 return (Z_OK);
 402         return (err);
 403 }
 404 
 405 int
 406 zonecfg_destroy_snapshot(const char *zonename)
 407 {
 408         char path[MAXPATHLEN];
 409 
 410         if (!snap_file_path(zonename, path))
 411                 return (Z_MISC_FS);
 412         return (zonecfg_destroy_impl(path));
 413 }
 414 
 415 static int
 416 getroot(zone_dochandle_t handle, xmlNodePtr *root)
 417 {
 418         if (zonecfg_check_handle(handle) == Z_BAD_HANDLE)
 419                 return (Z_BAD_HANDLE);
 420 
 421         *root = xmlDocGetRootElement(handle->zone_dh_doc);
 422 
 423         if (*root == NULL)
 424                 return (Z_EMPTY_DOCUMENT);
 425 
 426         if (xmlStrcmp((*root)->name, DTD_ELEM_ZONE))
 427                 return (Z_WRONG_DOC_TYPE);
 428 
 429         return (Z_OK);
 430 }
 431 
 432 static int
 433 operation_prep(zone_dochandle_t handle)
 434 {
 435         xmlNodePtr root;
 436         int err;
 437 
 438         if ((err = getroot(handle, &root)) != 0)
 439                 return (err);
 440 
 441         handle->zone_dh_cur = root;
 442         handle->zone_dh_top = root;
 443         return (Z_OK);
 444 }
 445 
 446 static int
 447 fetchprop(xmlNodePtr cur, const xmlChar *propname, char *dst, size_t dstsize)
 448 {
 449         xmlChar *property;
 450         size_t srcsize;
 451 
 452         if ((property = xmlGetProp(cur, propname)) == NULL)
 453                 return (Z_BAD_PROPERTY);
 454         srcsize = strlcpy(dst, (char *)property, dstsize);
 455         xmlFree(property);
 456         if (srcsize >= dstsize)
 457                 return (Z_TOO_BIG);
 458         return (Z_OK);
 459 }
 460 
 461 static int
 462 fetch_alloc_prop(xmlNodePtr cur, const xmlChar *propname, char **dst)
 463 {
 464         xmlChar *property;
 465 
 466         if ((property = xmlGetProp(cur, propname)) == NULL)
 467                 return (Z_BAD_PROPERTY);
 468         if ((*dst = strdup((char *)property)) == NULL) {
 469                 xmlFree(property);
 470                 return (Z_NOMEM);
 471         }
 472         xmlFree(property);
 473         return (Z_OK);
 474 }
 475 
 476 static int
 477 getrootattr(zone_dochandle_t handle, const xmlChar *propname,
 478     char *propval, size_t propsize)
 479 {
 480         xmlNodePtr root;
 481         int err;
 482 
 483         if ((err = getroot(handle, &root)) != 0)
 484                 return (err);
 485 
 486         return (fetchprop(root, propname, propval, propsize));
 487 }
 488 
 489 static int
 490 get_alloc_rootattr(zone_dochandle_t handle, const xmlChar *propname,
 491     char **propval)
 492 {
 493         xmlNodePtr root;
 494         int err;
 495 
 496         if ((err = getroot(handle, &root)) != 0)
 497                 return (err);
 498 
 499         return (fetch_alloc_prop(root, propname, propval));
 500 }
 501 
 502 static int
 503 setrootattr(zone_dochandle_t handle, const xmlChar *propname,
 504     const char *propval)
 505 {
 506         int err;
 507         xmlNodePtr root;
 508 
 509         if ((err = getroot(handle, &root)) != Z_OK)
 510                 return (err);
 511 
 512         /*
 513          * If we get a null propval remove the property (ignore return since it
 514          * may not be set to begin with).
 515          */
 516         if (propval == NULL) {
 517                 (void) xmlUnsetProp(root, propname);
 518         } else {
 519                 if (xmlSetProp(root, propname, (const xmlChar *) propval)
 520                     == NULL)
 521                         return (Z_INVAL);
 522         }
 523         return (Z_OK);
 524 }
 525 
 526 static void
 527 addcomment(zone_dochandle_t handle, const char *comment)
 528 {
 529         xmlNodePtr node;
 530         node = xmlNewComment((xmlChar *) comment);
 531 
 532         if (node != NULL)
 533                 (void) xmlAddPrevSibling(handle->zone_dh_top, node);
 534 }
 535 
 536 static void
 537 stripcomments(zone_dochandle_t handle)
 538 {
 539         xmlDocPtr top;
 540         xmlNodePtr child, next;
 541 
 542         top = handle->zone_dh_doc;
 543         for (child = top->xmlChildrenNode; child != NULL; child = next) {
 544                 next = child->next;
 545                 if (child->name == NULL)
 546                         continue;
 547                 if (xmlStrcmp(child->name, DTD_ELEM_COMMENT) == 0) {
 548                         next = child->next;
 549                         xmlUnlinkNode(child);
 550                         xmlFreeNode(child);
 551                 }
 552         }
 553 }
 554 
 555 static void
 556 strip_sw_inv(zone_dochandle_t handle)
 557 {
 558         xmlNodePtr root, child, next;
 559 
 560         root = xmlDocGetRootElement(handle->zone_dh_doc);
 561         for (child = root->xmlChildrenNode; child != NULL; child = next) {
 562                 next = child->next;
 563                 if (child->name == NULL)
 564                         continue;
 565                 if (xmlStrcmp(child->name, DTD_ELEM_PACKAGE) == 0) {
 566                         next = child->next;
 567                         xmlUnlinkNode(child);
 568                         xmlFreeNode(child);
 569                 }
 570         }
 571 }
 572 
 573 static int
 574 zonecfg_get_handle_impl(const char *zonename, const char *filename,
 575     zone_dochandle_t handle)
 576 {
 577         xmlValidCtxtPtr cvp;
 578         struct stat statbuf;
 579         int valid;
 580 
 581         if (zonename == NULL)
 582                 return (Z_NO_ZONE);
 583 
 584         if ((handle->zone_dh_doc = xmlParseFile(filename)) == NULL) {
 585                 /* distinguish file not found vs. found but not parsed */
 586                 if (stat(filename, &statbuf) == 0)
 587                         return (Z_INVALID_DOCUMENT);
 588                 return (Z_NO_ZONE);
 589         }
 590         if ((cvp = xmlNewValidCtxt()) == NULL)
 591                 return (Z_NOMEM);
 592         cvp->error = zonecfg_error_func;
 593         cvp->warning = zonecfg_error_func;
 594         valid = xmlValidateDocument(cvp, handle->zone_dh_doc);
 595         xmlFreeValidCtxt(cvp);
 596         if (valid == 0)
 597                 return (Z_INVALID_DOCUMENT);
 598 
 599         /* delete any comments such as inherited Sun copyright / ident str */
 600         stripcomments(handle);
 601         return (Z_OK);
 602 }
 603 
 604 int
 605 zonecfg_get_handle(const char *zonename, zone_dochandle_t handle)
 606 {
 607         char path[MAXPATHLEN];
 608 
 609         if (!config_file_path(zonename, path))
 610                 return (Z_MISC_FS);
 611         handle->zone_dh_newzone = B_FALSE;
 612 
 613         return (zonecfg_get_handle_impl(zonename, path, handle));
 614 }
 615 
 616 int
 617 zonecfg_get_attach_handle(const char *path, const char *fname,
 618     const char *zonename, boolean_t preserve_sw, zone_dochandle_t handle)
 619 {
 620         char            migpath[MAXPATHLEN];
 621         int             err;
 622         struct stat     buf;
 623 
 624         if (snprintf(migpath, sizeof (migpath), "%s/root", path) >=
 625             sizeof (migpath))
 626                 return (Z_NOMEM);
 627 
 628         if (stat(migpath, &buf) == -1 || !S_ISDIR(buf.st_mode))
 629                 return (Z_NO_ZONE);
 630 
 631         if (snprintf(migpath, sizeof (migpath), "%s/%s", path, fname) >=
 632             sizeof (migpath))
 633                 return (Z_NOMEM);
 634 
 635         if ((err = zonecfg_get_handle_impl(zonename, migpath, handle)) != Z_OK)
 636                 return (err);
 637 
 638         if (!preserve_sw)
 639                 strip_sw_inv(handle);
 640 
 641         handle->zone_dh_newzone = B_TRUE;
 642         if ((err = setrootattr(handle, DTD_ATTR_ZONEPATH, path)) != Z_OK)
 643                 return (err);
 644 
 645         return (setrootattr(handle, DTD_ATTR_NAME, zonename));
 646 }
 647 
 648 int
 649 zonecfg_get_snapshot_handle(const char *zonename, zone_dochandle_t handle)
 650 {
 651         char path[MAXPATHLEN];
 652 
 653         if (!snap_file_path(zonename, path))
 654                 return (Z_MISC_FS);
 655         handle->zone_dh_newzone = B_FALSE;
 656         return (zonecfg_get_handle_impl(zonename, path, handle));
 657 }
 658 
 659 int
 660 zonecfg_get_template_handle(const char *template, const char *zonename,
 661     zone_dochandle_t handle)
 662 {
 663         char path[MAXPATHLEN];
 664         int err;
 665 
 666         if (!config_file_path(template, path))
 667                 return (Z_MISC_FS);
 668 
 669         if ((err = zonecfg_get_handle_impl(template, path, handle)) != Z_OK)
 670                 return (err);
 671         handle->zone_dh_newzone = B_TRUE;
 672         return (setrootattr(handle, DTD_ATTR_NAME, zonename));
 673 }
 674 
 675 int
 676 zonecfg_get_xml_handle(const char *path, zone_dochandle_t handle)
 677 {
 678         struct stat buf;
 679         int err;
 680 
 681         if (stat(path, &buf) == -1)
 682                 return (Z_MISC_FS);
 683 
 684         if ((err = zonecfg_get_handle_impl("xml", path, handle)) != Z_OK)
 685                 return (err);
 686         handle->zone_dh_newzone = B_TRUE;
 687         return (Z_OK);
 688 }
 689 
 690 /*
 691  * Initialize two handles from the manifest read on fd.  The rem_handle
 692  * is initialized from the input file, including the sw inventory.  The
 693  * local_handle is initialized with the same zone configuration but with
 694  * no sw inventory.
 695  */
 696 int
 697 zonecfg_attach_manifest(int fd, zone_dochandle_t local_handle,
 698     zone_dochandle_t rem_handle)
 699 {
 700         xmlValidCtxtPtr cvp;
 701         int valid;
 702 
 703         /* load the manifest into the handle for the remote system */
 704         if ((rem_handle->zone_dh_doc = xmlReadFd(fd, NULL, NULL, 0)) == NULL) {
 705                 return (Z_INVALID_DOCUMENT);
 706         }
 707         if ((cvp = xmlNewValidCtxt()) == NULL)
 708                 return (Z_NOMEM);
 709         cvp->error = zonecfg_error_func;
 710         cvp->warning = zonecfg_error_func;
 711         valid = xmlValidateDocument(cvp, rem_handle->zone_dh_doc);
 712         xmlFreeValidCtxt(cvp);
 713         if (valid == 0)
 714                 return (Z_INVALID_DOCUMENT);
 715 
 716         /* delete any comments such as inherited Sun copyright / ident str */
 717         stripcomments(rem_handle);
 718 
 719         rem_handle->zone_dh_newzone = B_TRUE;
 720         rem_handle->zone_dh_sw_inv = B_TRUE;
 721 
 722         /*
 723          * Now use the remote system handle to generate a local system handle
 724          * with an identical zones configuration but no sw inventory.
 725          */
 726         if ((local_handle->zone_dh_doc = xmlCopyDoc(rem_handle->zone_dh_doc,
 727             1)) == NULL) {
 728                 return (Z_INVALID_DOCUMENT);
 729         }
 730 
 731         /*
 732          * We need to re-run xmlValidateDocument on local_handle to properly
 733          * update the in-core representation of the configuration.
 734          */
 735         if ((cvp = xmlNewValidCtxt()) == NULL)
 736                 return (Z_NOMEM);
 737         cvp->error = zonecfg_error_func;
 738         cvp->warning = zonecfg_error_func;
 739         valid = xmlValidateDocument(cvp, local_handle->zone_dh_doc);
 740         xmlFreeValidCtxt(cvp);
 741         if (valid == 0)
 742                 return (Z_INVALID_DOCUMENT);
 743 
 744         strip_sw_inv(local_handle);
 745 
 746         local_handle->zone_dh_newzone = B_TRUE;
 747         local_handle->zone_dh_sw_inv = B_FALSE;
 748 
 749         return (Z_OK);
 750 }
 751 
 752 static boolean_t
 753 is_renaming(zone_dochandle_t handle)
 754 {
 755         if (handle->zone_dh_newzone)
 756                 return (B_FALSE);
 757         if (strlen(handle->zone_dh_delete_name) > 0)
 758                 return (B_TRUE);
 759         return (B_FALSE);
 760 }
 761 
 762 static boolean_t
 763 is_new(zone_dochandle_t handle)
 764 {
 765         return (handle->zone_dh_newzone || handle->zone_dh_snapshot);
 766 }
 767 
 768 static boolean_t
 769 is_snapshot(zone_dochandle_t handle)
 770 {
 771         return (handle->zone_dh_snapshot);
 772 }
 773 
 774 /*
 775  * It would be great to be able to use libc's ctype(3c) macros, but we
 776  * can't, as they are locale sensitive, and it would break our limited thread
 777  * safety if this routine had to change the app locale on the fly.
 778  */
 779 int
 780 zonecfg_validate_zonename(const char *zone)
 781 {
 782         int i;
 783 
 784         if (strcmp(zone, GLOBAL_ZONENAME) == 0)
 785                 return (Z_BOGUS_ZONE_NAME);
 786 
 787         if (strlen(zone) >= ZONENAME_MAX)
 788                 return (Z_BOGUS_ZONE_NAME);
 789 
 790         if (!((zone[0] >= 'a' && zone[0] <= 'z') ||
 791             (zone[0] >= 'A' && zone[0] <= 'Z') ||
 792             (zone[0] >= '0' && zone[0] <= '9')))
 793                 return (Z_BOGUS_ZONE_NAME);
 794 
 795         for (i = 1; zone[i] != '\0'; i++) {
 796                 if (!((zone[i] >= 'a' && zone[i] <= 'z') ||
 797                     (zone[i] >= 'A' && zone[i] <= 'Z') ||
 798                     (zone[i] >= '0' && zone[i] <= '9') ||
 799                     (zone[i] == '-') || (zone[i] == '_') || (zone[i] == '.')))
 800                         return (Z_BOGUS_ZONE_NAME);
 801         }
 802 
 803         return (Z_OK);
 804 }
 805 
 806 /*
 807  * Changing the zone name requires us to track both the old and new
 808  * name of the zone until commit time.
 809  */
 810 int
 811 zonecfg_get_name(zone_dochandle_t handle, char *name, size_t namesize)
 812 {
 813         return (getrootattr(handle, DTD_ATTR_NAME, name, namesize));
 814 }
 815 
 816 static int
 817 insert_admins(zone_dochandle_t handle, char *zonename)
 818 {
 819         int err;
 820         struct zone_admintab admintab;
 821 
 822         if ((err = zonecfg_setadminent(handle)) != Z_OK) {
 823                 return (err);
 824         }
 825         while (zonecfg_getadminent(handle, &admintab) == Z_OK) {
 826                 err = zonecfg_insert_userauths(handle,
 827                     admintab.zone_admin_user, zonename);
 828                 if (err != Z_OK) {
 829                         (void) zonecfg_endadminent(handle);
 830                         return (err);
 831                 }
 832         }
 833         (void) zonecfg_endadminent(handle);
 834         return (Z_OK);
 835 }
 836 
 837 int
 838 zonecfg_set_name(zone_dochandle_t handle, char *name)
 839 {
 840         zone_state_t state;
 841         char curname[ZONENAME_MAX], old_delname[ZONENAME_MAX];
 842         int err;
 843 
 844         if ((err = getrootattr(handle, DTD_ATTR_NAME, curname,
 845             sizeof (curname))) != Z_OK)
 846                 return (err);
 847 
 848         if (strcmp(name, curname) == 0)
 849                 return (Z_OK);
 850 
 851         /*
 852          * Switching zone names to one beginning with SUNW is not permitted.
 853          */
 854         if (strncmp(name, "SUNW", 4) == 0)
 855                 return (Z_BOGUS_ZONE_NAME);
 856 
 857         if ((err = zonecfg_validate_zonename(name)) != Z_OK)
 858                 return (err);
 859 
 860         /*
 861          * Setting the name back to the original name (effectively a revert of
 862          * the name) is fine.  But if we carry on, we'll falsely identify the
 863          * name as "in use," so special case here.
 864          */
 865         if (strcmp(name, handle->zone_dh_delete_name) == 0) {
 866                 err = setrootattr(handle, DTD_ATTR_NAME, name);
 867                 handle->zone_dh_delete_name[0] = '\0';
 868                 return (err);
 869         }
 870 
 871         /* Check to see if new name chosen is already in use */
 872         if (zone_get_state(name, &state) != Z_NO_ZONE)
 873                 return (Z_NAME_IN_USE);
 874 
 875         /*
 876          * If this isn't already "new" or in a renaming transition, then
 877          * we're initiating a rename here; so stash the "delete name"
 878          * (i.e. the name of the zone we'll be removing) for the rename.
 879          */
 880         (void) strlcpy(old_delname, handle->zone_dh_delete_name,
 881             sizeof (old_delname));
 882         if (!is_new(handle) && !is_renaming(handle)) {
 883                 /*
 884                  * Name change is allowed only when the zone we're altering
 885                  * is not ready or running.
 886                  */
 887                 err = zone_get_state(curname, &state);
 888                 if (err == Z_OK) {
 889                         if (state > ZONE_STATE_INSTALLED)
 890                                 return (Z_BAD_ZONE_STATE);
 891                 } else if (err != Z_NO_ZONE) {
 892                         return (err);
 893                 }
 894 
 895                 (void) strlcpy(handle->zone_dh_delete_name, curname,
 896                     sizeof (handle->zone_dh_delete_name));
 897                 assert(is_renaming(handle));
 898         } else if (is_renaming(handle)) {
 899                 err = zone_get_state(handle->zone_dh_delete_name, &state);
 900                 if (err == Z_OK) {
 901                         if (state > ZONE_STATE_INSTALLED)
 902                                 return (Z_BAD_ZONE_STATE);
 903                 } else if (err != Z_NO_ZONE) {
 904                         return (err);
 905                 }
 906         }
 907 
 908         if ((err = setrootattr(handle, DTD_ATTR_NAME, name)) != Z_OK) {
 909                 /*
 910                  * Restore the deletename to whatever it was at the
 911                  * top of the routine, since we've had a failure.
 912                  */
 913                 (void) strlcpy(handle->zone_dh_delete_name, old_delname,
 914                     sizeof (handle->zone_dh_delete_name));
 915                 return (err);
 916         }
 917 
 918         /*
 919          * Record the old admins from the old zonename
 920          * so that they can be deleted when the operation is committed.
 921          */
 922         if ((err = insert_admins(handle, curname)) != Z_OK)
 923                 return (err);
 924         else
 925                 return (Z_OK);
 926 }
 927 
 928 int
 929 zonecfg_get_zonepath(zone_dochandle_t handle, char *path, size_t pathsize)
 930 {
 931         size_t len;
 932 
 933         if ((len = strlcpy(path, zonecfg_root, pathsize)) >= pathsize)
 934                 return (Z_TOO_BIG);
 935         return (getrootattr(handle, DTD_ATTR_ZONEPATH, path + len,
 936             pathsize - len));
 937 }
 938 
 939 int
 940 zonecfg_set_zonepath(zone_dochandle_t handle, char *zonepath)
 941 {
 942         size_t len;
 943         char *modpath, *copy_mp, *curr_mp;      /* modified path ptrs */
 944         char last_copied;
 945         int ret;
 946 
 947         /*
 948          * Collapse multiple contiguous slashes and remove trailing slash.
 949          */
 950         modpath = strdup(zonepath);
 951         if (modpath == NULL)
 952                 return (Z_NOMEM);
 953         last_copied = '\0';
 954         for (copy_mp = curr_mp = modpath; *curr_mp != '\0'; curr_mp++) {
 955                 if (*curr_mp != '/' || last_copied != '/') {
 956                         last_copied = *copy_mp = *curr_mp;
 957                         copy_mp++;
 958                 }
 959         }
 960         if (last_copied == '/')
 961                 copy_mp--;
 962         *copy_mp = '\0';
 963 
 964         /*
 965          * The user deals in absolute paths in the running global zone, but the
 966          * internal configuration files deal with boot environment relative
 967          * paths.  Strip out the alternate root when specified.
 968          */
 969         len = strlen(zonecfg_root);
 970         if (strncmp(modpath, zonecfg_root, len) != 0 || modpath[len] != '/') {
 971                 free(modpath);
 972                 return (Z_BAD_PROPERTY);
 973         }
 974         curr_mp = modpath + len;
 975         ret = setrootattr(handle, DTD_ATTR_ZONEPATH, curr_mp);
 976         free(modpath);
 977         return (ret);
 978 }
 979 
 980 static int
 981 i_zonecfg_get_brand(zone_dochandle_t handle, char *brand, size_t brandsize,
 982     boolean_t default_query)
 983 {
 984         int ret, sz;
 985 
 986         ret = getrootattr(handle, DTD_ATTR_BRAND, brand, brandsize);
 987 
 988         /*
 989          * If the lookup failed, or succeeded in finding a non-null brand
 990          * string then return.
 991          */
 992         if (ret != Z_OK || brand[0] != '\0')
 993                 return (ret);
 994 
 995         if (!default_query) {
 996                 /* If the zone has no brand, it is the default brand. */
 997                 return (zonecfg_default_brand(brand, brandsize));
 998         }
 999 
1000         /* if SUNWdefault didn't specify a brand, fallback to "native" */
1001         sz = strlcpy(brand, NATIVE_BRAND_NAME, brandsize);
1002         if (sz >= brandsize)
1003                 return (Z_TOO_BIG);
1004         return (Z_OK);
1005 }
1006 
1007 int
1008 zonecfg_get_brand(zone_dochandle_t handle, char *brand, size_t brandsize)
1009 {
1010         return (i_zonecfg_get_brand(handle, brand, brandsize, B_FALSE));
1011 }
1012 
1013 int
1014 zonecfg_set_brand(zone_dochandle_t handle, char *brand)
1015 {
1016         return (setrootattr(handle, DTD_ATTR_BRAND, brand));
1017 }
1018 
1019 int
1020 zonecfg_get_autoboot(zone_dochandle_t handle, boolean_t *autoboot)
1021 {
1022         char autobootstr[DTD_ENTITY_BOOL_LEN];
1023         int ret;
1024 
1025         if ((ret = getrootattr(handle, DTD_ATTR_AUTOBOOT, autobootstr,
1026             sizeof (autobootstr))) != Z_OK)
1027                 return (ret);
1028 
1029         if (strcmp(autobootstr, DTD_ENTITY_TRUE) == 0)
1030                 *autoboot = B_TRUE;
1031         else if (strcmp(autobootstr, DTD_ENTITY_FALSE) == 0)
1032                 *autoboot = B_FALSE;
1033         else
1034                 ret = Z_BAD_PROPERTY;
1035         return (ret);
1036 }
1037 
1038 int
1039 zonecfg_set_autoboot(zone_dochandle_t handle, boolean_t autoboot)
1040 {
1041         return (setrootattr(handle, DTD_ATTR_AUTOBOOT,
1042             autoboot ? DTD_ENTITY_TRUE : DTD_ENTITY_FALSE));
1043 }
1044 
1045 int
1046 zonecfg_get_pool(zone_dochandle_t handle, char *pool, size_t poolsize)
1047 {
1048         return (getrootattr(handle, DTD_ATTR_POOL, pool, poolsize));
1049 }
1050 
1051 int
1052 zonecfg_set_pool(zone_dochandle_t handle, char *pool)
1053 {
1054         return (setrootattr(handle, DTD_ATTR_POOL, pool));
1055 }
1056 
1057 int
1058 zonecfg_get_limitpriv(zone_dochandle_t handle, char **limitpriv)
1059 {
1060         return (get_alloc_rootattr(handle, DTD_ATTR_LIMITPRIV, limitpriv));
1061 }
1062 
1063 int
1064 zonecfg_set_limitpriv(zone_dochandle_t handle, char *limitpriv)
1065 {
1066         return (setrootattr(handle, DTD_ATTR_LIMITPRIV, limitpriv));
1067 }
1068 
1069 int
1070 zonecfg_get_bootargs(zone_dochandle_t handle, char *bargs, size_t bargssize)
1071 {
1072         return (getrootattr(handle, DTD_ATTR_BOOTARGS, bargs, bargssize));
1073 }
1074 
1075 int
1076 zonecfg_set_bootargs(zone_dochandle_t handle, char *bargs)
1077 {
1078         return (setrootattr(handle, DTD_ATTR_BOOTARGS, bargs));
1079 }
1080 
1081 int
1082 zonecfg_get_sched_class(zone_dochandle_t handle, char *sched, size_t schedsize)
1083 {
1084         return (getrootattr(handle, DTD_ATTR_SCHED, sched, schedsize));
1085 }
1086 
1087 int
1088 zonecfg_set_sched(zone_dochandle_t handle, char *sched)
1089 {
1090         return (setrootattr(handle, DTD_ATTR_SCHED, sched));
1091 }
1092 
1093 /*
1094  * /etc/zones/index caches a vital piece of information which is also
1095  * in the <zonename>.xml file: the path to the zone.  This is for performance,
1096  * since we need to walk all zonepath's in order to be able to detect conflicts
1097  * (see crosscheck_zonepaths() in the zoneadm command).
1098  *
1099  * An additional complexity is that when doing a rename, we'd like the entire
1100  * index update operation (rename, and potential state changes) to be atomic.
1101  * In general, the operation of this function should succeed or fail as
1102  * a unit.
1103  */
1104 int
1105 zonecfg_refresh_index_file(zone_dochandle_t handle)
1106 {
1107         char name[ZONENAME_MAX], zonepath[MAXPATHLEN];
1108         struct zoneent ze;
1109         int err;
1110         int opcode;
1111         char *zn;
1112 
1113         bzero(&ze, sizeof (ze));
1114         ze.zone_state = -1;     /* Preserve existing state in index */
1115 
1116         if ((err = zonecfg_get_name(handle, name, sizeof (name))) != Z_OK)
1117                 return (err);
1118         (void) strlcpy(ze.zone_name, name, sizeof (ze.zone_name));
1119 
1120         if ((err = zonecfg_get_zonepath(handle, zonepath,
1121             sizeof (zonepath))) != Z_OK)
1122                 return (err);
1123         (void) strlcpy(ze.zone_path, zonepath + strlen(zonecfg_root),
1124             sizeof (ze.zone_path));
1125 
1126         if (is_renaming(handle)) {
1127                 opcode = PZE_MODIFY;
1128                 (void) strlcpy(ze.zone_name, handle->zone_dh_delete_name,
1129                     sizeof (ze.zone_name));
1130                 (void) strlcpy(ze.zone_newname, name, sizeof (ze.zone_newname));
1131         } else if (is_new(handle)) {
1132                 FILE *cookie;
1133                 /*
1134                  * Be tolerant of the zone already existing in the index file,
1135                  * since we might be forcibly overwriting an existing
1136                  * configuration with a new one (for example 'create -F'
1137                  * in zonecfg).
1138                  */
1139                 opcode = PZE_ADD;
1140                 cookie = setzoneent();
1141                 while ((zn = getzoneent(cookie)) != NULL) {
1142                         if (strcmp(zn, name) == 0) {
1143                                 opcode = PZE_MODIFY;
1144                                 free(zn);
1145                                 break;
1146                         }
1147                         free(zn);
1148                 }
1149                 endzoneent(cookie);
1150                 ze.zone_state = ZONE_STATE_CONFIGURED;
1151         } else {
1152                 opcode = PZE_MODIFY;
1153         }
1154 
1155         if ((err = putzoneent(&ze, opcode)) != Z_OK)
1156                 return (err);
1157 
1158         return (Z_OK);
1159 }
1160 
1161 /*
1162  * The goal of this routine is to cause the index file update and the
1163  * document save to happen as an atomic operation.  We do the document
1164  * first, saving a backup copy using a hard link; if that succeeds, we go
1165  * on to the index.  If that fails, we roll the document back into place.
1166  *
1167  * Strategy:
1168  *
1169  * New zone 'foo' configuration:
1170  *      Create tmpfile (zonecfg.xxxxxx)
1171  *      Write XML to tmpfile
1172  *      Rename tmpfile to xmlfile (zonecfg.xxxxxx -> foo.xml)
1173  *      Add entry to index file
1174  *      If it fails, delete foo.xml, leaving nothing behind.
1175  *
1176  * Save existing zone 'foo':
1177  *      Make backup of foo.xml -> .backup
1178  *      Create tmpfile (zonecfg.xxxxxx)
1179  *      Write XML to tmpfile
1180  *      Rename tmpfile to xmlfile (zonecfg.xxxxxx -> foo.xml)
1181  *      Modify index file as needed
1182  *      If it fails, recover from .backup -> foo.xml
1183  *
1184  * Rename 'foo' to 'bar':
1185  *      Create tmpfile (zonecfg.xxxxxx)
1186  *      Write XML to tmpfile
1187  *      Rename tmpfile to xmlfile (zonecfg.xxxxxx -> bar.xml)
1188  *      Add entry for 'bar' to index file, Remove entry for 'foo' (refresh)
1189  *      If it fails, delete bar.xml; foo.xml is left behind.
1190  */
1191 static int
1192 zonecfg_save_impl(zone_dochandle_t handle, char *filename)
1193 {
1194         char tmpfile[MAXPATHLEN];
1195         char bakdir[MAXPATHLEN], bakbase[MAXPATHLEN], bakfile[MAXPATHLEN];
1196         int tmpfd, err, valid;
1197         xmlValidCtxt cvp = { NULL };
1198         boolean_t backup;
1199 
1200         (void) strlcpy(tmpfile, filename, sizeof (tmpfile));
1201         (void) dirname(tmpfile);
1202         (void) strlcat(tmpfile, _PATH_TMPFILE, sizeof (tmpfile));
1203 
1204         tmpfd = mkstemp(tmpfile);
1205         if (tmpfd == -1) {
1206                 (void) unlink(tmpfile);
1207                 return (Z_TEMP_FILE);
1208         }
1209         (void) close(tmpfd);
1210 
1211         cvp.error = zonecfg_error_func;
1212         cvp.warning = zonecfg_error_func;
1213 
1214         /*
1215          * We do a final validation of the document.  Since the library has
1216          * malfunctioned if it fails to validate, we follow-up with an
1217          * assert() that the doc is valid.
1218          */
1219         valid = xmlValidateDocument(&cvp, handle->zone_dh_doc);
1220         assert(valid != 0);
1221 
1222         if (xmlSaveFormatFile(tmpfile, handle->zone_dh_doc, 1) <= 0)
1223                 goto err;
1224 
1225         (void) chmod(tmpfile, 0644);
1226 
1227         /*
1228          * In the event we are doing a standard save, hard link a copy of the
1229          * original file in .backup.<pid>.filename so we can restore it if
1230          * something goes wrong.
1231          */
1232         if (!is_new(handle) && !is_renaming(handle)) {
1233                 backup = B_TRUE;
1234 
1235                 (void) strlcpy(bakdir, filename, sizeof (bakdir));
1236                 (void) strlcpy(bakbase, filename, sizeof (bakbase));
1237                 (void) snprintf(bakfile, sizeof (bakfile), "%s/.backup.%d.%s",
1238                     dirname(bakdir), getpid(), basename(bakbase));
1239 
1240                 if (link(filename, bakfile) == -1) {
1241                         err = errno;
1242                         (void) unlink(tmpfile);
1243                         if (errno == EACCES)
1244                                 return (Z_ACCES);
1245                         return (Z_MISC_FS);
1246                 }
1247         }
1248 
1249         /*
1250          * Move the new document over top of the old.
1251          * i.e.:   zonecfg.XXXXXX  ->  myzone.xml
1252          */
1253         if (rename(tmpfile, filename) == -1) {
1254                 err = errno;
1255                 (void) unlink(tmpfile);
1256                 if (backup)
1257                         (void) unlink(bakfile);
1258                 if (err == EACCES)
1259                         return (Z_ACCES);
1260                 return (Z_MISC_FS);
1261         }
1262 
1263         /*
1264          * If this is a snapshot, we're done-- don't add an index entry.
1265          */
1266         if (is_snapshot(handle))
1267                 return (Z_OK);
1268 
1269         /* now update the index file to reflect whatever we just did */
1270         if ((err = zonecfg_refresh_index_file(handle)) != Z_OK) {
1271                 if (backup) {
1272                         /*
1273                          * Try to restore from our backup.
1274                          */
1275                         (void) unlink(filename);
1276                         (void) rename(bakfile, filename);
1277                 } else {
1278                         /*
1279                          * Either the zone is new, in which case we can delete
1280                          * new.xml, or we're doing a rename, so ditto.
1281                          */
1282                         assert(is_new(handle) || is_renaming(handle));
1283                         (void) unlink(filename);
1284                 }
1285                 return (Z_UPDATING_INDEX);
1286         }
1287 
1288         if (backup)
1289                 (void) unlink(bakfile);
1290 
1291         return (Z_OK);
1292 
1293 err:
1294         (void) unlink(tmpfile);
1295         return (Z_SAVING_FILE);
1296 }
1297 
1298 int
1299 zonecfg_save(zone_dochandle_t handle)
1300 {
1301         char zname[ZONENAME_MAX], path[MAXPATHLEN];
1302         char delpath[MAXPATHLEN];
1303         int err = Z_SAVING_FILE;
1304 
1305         if (zonecfg_check_handle(handle) != Z_OK)
1306                 return (Z_BAD_HANDLE);
1307 
1308         /*
1309          * We don't support saving snapshots or a tree containing a sw
1310          * inventory at this time.
1311          */
1312         if (handle->zone_dh_snapshot || handle->zone_dh_sw_inv)
1313                 return (Z_INVAL);
1314 
1315         if ((err = zonecfg_get_name(handle, zname, sizeof (zname))) != Z_OK)
1316                 return (err);
1317 
1318         if (!config_file_path(zname, path))
1319                 return (Z_MISC_FS);
1320 
1321         addcomment(handle, "\n    DO NOT EDIT THIS "
1322             "FILE.  Use zonecfg(1M) instead.\n");
1323 
1324         /*
1325          * Update user_attr first so that it will be older
1326          * than the config file.
1327          */
1328         (void) zonecfg_authorize_users(handle, zname);
1329         err = zonecfg_save_impl(handle, path);
1330 
1331         stripcomments(handle);
1332 
1333         if (err != Z_OK)
1334                 return (err);
1335 
1336         handle->zone_dh_newzone = B_FALSE;
1337 
1338         if (is_renaming(handle)) {
1339                 if (config_file_path(handle->zone_dh_delete_name, delpath))
1340                         (void) unlink(delpath);
1341                 handle->zone_dh_delete_name[0] = '\0';
1342         }
1343 
1344         return (Z_OK);
1345 }
1346 
1347 int
1348 zonecfg_verify_save(zone_dochandle_t handle, char *filename)
1349 {
1350         int valid;
1351 
1352         xmlValidCtxt cvp = { NULL };
1353 
1354         if (zonecfg_check_handle(handle) != Z_OK)
1355                 return (Z_BAD_HANDLE);
1356 
1357         cvp.error = zonecfg_error_func;
1358         cvp.warning = zonecfg_error_func;
1359 
1360         /*
1361          * We do a final validation of the document.  Since the library has
1362          * malfunctioned if it fails to validate, we follow-up with an
1363          * assert() that the doc is valid.
1364          */
1365         valid = xmlValidateDocument(&cvp, handle->zone_dh_doc);
1366         assert(valid != 0);
1367 
1368         if (xmlSaveFormatFile(filename, handle->zone_dh_doc, 1) <= 0)
1369                 return (Z_SAVING_FILE);
1370 
1371         return (Z_OK);
1372 }
1373 
1374 int
1375 zonecfg_detach_save(zone_dochandle_t handle, uint_t flags)
1376 {
1377         char zname[ZONENAME_MAX];
1378         char path[MAXPATHLEN];
1379         char migpath[MAXPATHLEN];
1380         xmlValidCtxt cvp = { NULL };
1381         int err = Z_SAVING_FILE;
1382         int valid;
1383 
1384         if (zonecfg_check_handle(handle) != Z_OK)
1385                 return (Z_BAD_HANDLE);
1386 
1387         if (flags & ZONE_DRY_RUN) {
1388                 (void) strlcpy(migpath, "-", sizeof (migpath));
1389         } else {
1390                 if ((err = zonecfg_get_name(handle, zname, sizeof (zname)))
1391                     != Z_OK)
1392                         return (err);
1393 
1394                 if ((err = zone_get_zonepath(zname, path, sizeof (path)))
1395                     != Z_OK)
1396                         return (err);
1397 
1398                 if (snprintf(migpath, sizeof (migpath), "%s/%s", path,
1399                     ZONE_DETACHED) >= sizeof (migpath))
1400                         return (Z_NOMEM);
1401         }
1402 
1403         if ((err = operation_prep(handle)) != Z_OK)
1404                 return (err);
1405 
1406         addcomment(handle, "\n    DO NOT EDIT THIS FILE.  "
1407             "Use zonecfg(1M) and zoneadm(1M) attach.\n");
1408 
1409         cvp.error = zonecfg_error_func;
1410         cvp.warning = zonecfg_error_func;
1411 
1412         /*
1413          * We do a final validation of the document.  Since the library has
1414          * malfunctioned if it fails to validate, we follow-up with an
1415          * assert() that the doc is valid.
1416          */
1417         valid = xmlValidateDocument(&cvp, handle->zone_dh_doc);
1418         assert(valid != 0);
1419 
1420         if (xmlSaveFormatFile(migpath, handle->zone_dh_doc, 1) <= 0)
1421                 return (Z_SAVING_FILE);
1422 
1423         if (!(flags & ZONE_DRY_RUN))
1424                 (void) chmod(migpath, 0644);
1425 
1426         stripcomments(handle);
1427 
1428         handle->zone_dh_newzone = B_FALSE;
1429 
1430         return (Z_OK);
1431 }
1432 
1433 boolean_t
1434 zonecfg_detached(const char *path)
1435 {
1436         char            migpath[MAXPATHLEN];
1437         struct stat     buf;
1438 
1439         if (snprintf(migpath, sizeof (migpath), "%s/%s", path, ZONE_DETACHED) >=
1440             sizeof (migpath))
1441                 return (B_FALSE);
1442 
1443         if (stat(migpath, &buf) != -1)
1444                 return (B_TRUE);
1445 
1446         return (B_FALSE);
1447 }
1448 
1449 void
1450 zonecfg_rm_detached(zone_dochandle_t handle, boolean_t forced)
1451 {
1452         char zname[ZONENAME_MAX];
1453         char path[MAXPATHLEN];
1454         char detached[MAXPATHLEN];
1455         char attached[MAXPATHLEN];
1456 
1457         if (zonecfg_check_handle(handle) != Z_OK)
1458                 return;
1459 
1460         if (zonecfg_get_name(handle, zname, sizeof (zname)) != Z_OK)
1461                 return;
1462 
1463         if (zone_get_zonepath(zname, path, sizeof (path)) != Z_OK)
1464                 return;
1465 
1466         (void) snprintf(detached, sizeof (detached), "%s/%s", path,
1467             ZONE_DETACHED);
1468         (void) snprintf(attached, sizeof (attached), "%s/%s", path,
1469             ATTACH_FORCED);
1470 
1471         if (forced) {
1472                 (void) rename(detached, attached);
1473         } else {
1474                 (void) unlink(attached);
1475                 (void) unlink(detached);
1476         }
1477 }
1478 
1479 /*
1480  * Special case: if access(2) fails with ENOENT, then try again using
1481  * ZONE_CONFIG_ROOT instead of config_file_path(zonename).  This is how we
1482  * work around the case of a config file which has not been created yet:
1483  * the user will need access to the directory so use that as a heuristic.
1484  */
1485 
1486 int
1487 zonecfg_access(const char *zonename, int amode)
1488 {
1489         char path[MAXPATHLEN];
1490 
1491         if (!config_file_path(zonename, path))
1492                 return (Z_INVAL);
1493         if (access(path, amode) == 0)
1494                 return (Z_OK);
1495         if (errno == ENOENT) {
1496                 if (snprintf(path, sizeof (path), "%s%s", zonecfg_root,
1497                     ZONE_CONFIG_ROOT) >= sizeof (path))
1498                         return (Z_INVAL);
1499                 if (access(path, amode) == 0)
1500                         return (Z_OK);
1501         }
1502         if (errno == EACCES)
1503                 return (Z_ACCES);
1504         if (errno == EINVAL)
1505                 return (Z_INVAL);
1506         return (Z_MISC_FS);
1507 }
1508 
1509 int
1510 zonecfg_create_snapshot(const char *zonename)
1511 {
1512         zone_dochandle_t handle;
1513         char path[MAXPATHLEN], zonepath[MAXPATHLEN], rpath[MAXPATHLEN];
1514         int error = Z_OK, res;
1515 
1516         if ((handle = zonecfg_init_handle()) == NULL) {
1517                 return (Z_NOMEM);
1518         }
1519 
1520         handle->zone_dh_newzone = B_TRUE;
1521         handle->zone_dh_snapshot = B_TRUE;
1522 
1523         if ((error = zonecfg_get_handle(zonename, handle)) != Z_OK)
1524                 goto out;
1525         if ((error = operation_prep(handle)) != Z_OK)
1526                 goto out;
1527         error = zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath));
1528         if (error != Z_OK)
1529                 goto out;
1530         if ((res = resolvepath(zonepath, rpath, sizeof (rpath))) == -1) {
1531                 error = Z_RESOLVED_PATH;
1532                 goto out;
1533         }
1534         /*
1535          * If the resolved path is not the same as the original path, then
1536          * save the resolved path in the snapshot, thus preventing any
1537          * potential problems down the line when zoneadmd goes to unmount
1538          * file systems and depends on initial string matches with resolved
1539          * paths.
1540          */
1541         rpath[res] = '\0';
1542         if (strcmp(zonepath, rpath) != 0) {
1543                 if ((error = zonecfg_set_zonepath(handle, rpath)) != Z_OK)
1544                         goto out;
1545         }
1546         if (snprintf(path, sizeof (path), "%s%s", zonecfg_root,
1547             ZONE_SNAPSHOT_ROOT) >= sizeof (path)) {
1548                 error = Z_MISC_FS;
1549                 goto out;
1550         }
1551         if ((mkdir(path, S_IRWXU) == -1) && (errno != EEXIST)) {
1552                 error = Z_MISC_FS;
1553                 goto out;
1554         }
1555 
1556         if (!snap_file_path(zonename, path)) {
1557                 error = Z_MISC_FS;
1558                 goto out;
1559         }
1560 
1561         addcomment(handle, "\n    DO NOT EDIT THIS FILE.  "
1562             "It is a snapshot of running zone state.\n");
1563 
1564         error = zonecfg_save_impl(handle, path);
1565 
1566         stripcomments(handle);
1567 
1568 out:
1569         zonecfg_fini_handle(handle);
1570         return (error);
1571 }
1572 
1573 int
1574 zonecfg_get_iptype(zone_dochandle_t handle, zone_iptype_t *iptypep)
1575 {
1576         char property[10]; /* 10 is big enough for "shared"/"exclusive" */
1577         int err;
1578 
1579         err = getrootattr(handle, DTD_ATTR_IPTYPE, property, sizeof (property));
1580         if (err == Z_BAD_PROPERTY) {
1581                 /* Return default value */
1582                 *iptypep = ZS_SHARED;
1583                 return (Z_OK);
1584         } else if (err != Z_OK) {
1585                 return (err);
1586         }
1587 
1588         if (strlen(property) == 0 ||
1589             strcmp(property, "shared") == 0)
1590                 *iptypep = ZS_SHARED;
1591         else if (strcmp(property, "exclusive") == 0)
1592                 *iptypep = ZS_EXCLUSIVE;
1593         else
1594                 return (Z_INVAL);
1595 
1596         return (Z_OK);
1597 }
1598 
1599 int
1600 zonecfg_set_iptype(zone_dochandle_t handle, zone_iptype_t iptype)
1601 {
1602         xmlNodePtr cur;
1603 
1604         if (handle == NULL)
1605                 return (Z_INVAL);
1606 
1607         cur = xmlDocGetRootElement(handle->zone_dh_doc);
1608         if (cur == NULL) {
1609                 return (Z_EMPTY_DOCUMENT);
1610         }
1611 
1612         if (xmlStrcmp(cur->name, DTD_ELEM_ZONE) != 0) {
1613                 return (Z_WRONG_DOC_TYPE);
1614         }
1615         switch (iptype) {
1616         case ZS_SHARED:
1617                 /*
1618                  * Since "shared" is the default, we don't write it to the
1619                  * configuration file, so that it's easier to migrate those
1620                  * zones elsewhere, eg., to systems which are not IP-Instances
1621                  * aware.
1622                  * xmlUnsetProp only fails when the attribute doesn't exist,
1623                  * which we don't care.
1624                  */
1625                 (void) xmlUnsetProp(cur, DTD_ATTR_IPTYPE);
1626                 break;
1627         case ZS_EXCLUSIVE:
1628                 if (xmlSetProp(cur, DTD_ATTR_IPTYPE,
1629                     (const xmlChar *) "exclusive") == NULL)
1630                         return (Z_INVAL);
1631                 break;
1632         }
1633         return (Z_OK);
1634 }
1635 
1636 static int
1637 newprop(xmlNodePtr node, const xmlChar *attrname, char *src)
1638 {
1639         xmlAttrPtr newattr;
1640 
1641         newattr = xmlNewProp(node, attrname, (xmlChar *)src);
1642         if (newattr == NULL) {
1643                 xmlUnlinkNode(node);
1644                 xmlFreeNode(node);
1645                 return (Z_BAD_PROPERTY);
1646         }
1647         return (Z_OK);
1648 }
1649 
1650 static int
1651 zonecfg_add_filesystem_core(zone_dochandle_t handle, struct zone_fstab *tabptr)
1652 {
1653         xmlNodePtr newnode, cur = handle->zone_dh_cur, options_node;
1654         zone_fsopt_t *ptr;
1655         int err;
1656 
1657         newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_FS, NULL);
1658         if ((err = newprop(newnode, DTD_ATTR_SPECIAL,
1659             tabptr->zone_fs_special)) != Z_OK)
1660                 return (err);
1661         if (tabptr->zone_fs_raw[0] != '\0' &&
1662             (err = newprop(newnode, DTD_ATTR_RAW, tabptr->zone_fs_raw)) != Z_OK)
1663                 return (err);
1664         if ((err = newprop(newnode, DTD_ATTR_DIR, tabptr->zone_fs_dir)) != Z_OK)
1665                 return (err);
1666         if ((err = newprop(newnode, DTD_ATTR_TYPE,
1667             tabptr->zone_fs_type)) != Z_OK)
1668                 return (err);
1669         if (tabptr->zone_fs_options != NULL) {
1670                 for (ptr = tabptr->zone_fs_options; ptr != NULL;
1671                     ptr = ptr->zone_fsopt_next) {
1672                         options_node = xmlNewTextChild(newnode, NULL,
1673                             DTD_ELEM_FSOPTION, NULL);
1674                         if ((err = newprop(options_node, DTD_ATTR_NAME,
1675                             ptr->zone_fsopt_opt)) != Z_OK)
1676                                 return (err);
1677                 }
1678         }
1679         return (Z_OK);
1680 }
1681 
1682 int
1683 zonecfg_add_filesystem(zone_dochandle_t handle, struct zone_fstab *tabptr)
1684 {
1685         int err;
1686 
1687         if (tabptr == NULL)
1688                 return (Z_INVAL);
1689 
1690         if ((err = operation_prep(handle)) != Z_OK)
1691                 return (err);
1692 
1693         if ((err = zonecfg_add_filesystem_core(handle, tabptr)) != Z_OK)
1694                 return (err);
1695 
1696         return (Z_OK);
1697 }
1698 
1699 int
1700 zonecfg_add_fs_option(struct zone_fstab *tabptr, char *option)
1701 {
1702         zone_fsopt_t *last, *old, *new;
1703 
1704         last = tabptr->zone_fs_options;
1705         for (old = last; old != NULL; old = old->zone_fsopt_next)
1706                 last = old;     /* walk to the end of the list */
1707         new = (zone_fsopt_t *)malloc(sizeof (zone_fsopt_t));
1708         if (new == NULL)
1709                 return (Z_NOMEM);
1710         (void) strlcpy(new->zone_fsopt_opt, option,
1711             sizeof (new->zone_fsopt_opt));
1712         new->zone_fsopt_next = NULL;
1713         if (last == NULL)
1714                 tabptr->zone_fs_options = new;
1715         else
1716                 last->zone_fsopt_next = new;
1717         return (Z_OK);
1718 }
1719 
1720 int
1721 zonecfg_remove_fs_option(struct zone_fstab *tabptr, char *option)
1722 {
1723         zone_fsopt_t *last, *this, *next;
1724 
1725         last = tabptr->zone_fs_options;
1726         for (this = last; this != NULL; this = this->zone_fsopt_next) {
1727                 if (strcmp(this->zone_fsopt_opt, option) == 0) {
1728                         next = this->zone_fsopt_next;
1729                         if (this == tabptr->zone_fs_options)
1730                                 tabptr->zone_fs_options = next;
1731                         else
1732                                 last->zone_fsopt_next = next;
1733                         free(this);
1734                         return (Z_OK);
1735                 } else
1736                         last = this;
1737         }
1738         return (Z_NO_PROPERTY_ID);
1739 }
1740 
1741 void
1742 zonecfg_free_fs_option_list(zone_fsopt_t *list)
1743 {
1744         zone_fsopt_t *this, *next;
1745 
1746         for (this = list; this != NULL; this = next) {
1747                 next = this->zone_fsopt_next;
1748                 free(this);
1749         }
1750 }
1751 
1752 void
1753 zonecfg_free_rctl_value_list(struct zone_rctlvaltab *valtab)
1754 {
1755         if (valtab == NULL)
1756                 return;
1757         zonecfg_free_rctl_value_list(valtab->zone_rctlval_next);
1758         free(valtab);
1759 }
1760 
1761 static boolean_t
1762 match_prop(xmlNodePtr cur, const xmlChar *attr, char *user_prop)
1763 {
1764         xmlChar *gotten_prop;
1765         int prop_result;
1766 
1767         gotten_prop = xmlGetProp(cur, attr);
1768         if (gotten_prop == NULL)        /* shouldn't happen */
1769                 return (B_FALSE);
1770         prop_result = xmlStrcmp(gotten_prop, (const xmlChar *) user_prop);
1771         xmlFree(gotten_prop);
1772         return ((prop_result == 0));    /* empty strings will match */
1773 }
1774 
1775 static int
1776 zonecfg_delete_filesystem_core(zone_dochandle_t handle,
1777     struct zone_fstab *tabptr)
1778 {
1779         xmlNodePtr cur = handle->zone_dh_cur;
1780         boolean_t dir_match, spec_match, raw_match, type_match;
1781 
1782         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
1783                 if (xmlStrcmp(cur->name, DTD_ELEM_FS))
1784                         continue;
1785                 dir_match = match_prop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir);
1786                 spec_match = match_prop(cur, DTD_ATTR_SPECIAL,
1787                     tabptr->zone_fs_special);
1788                 raw_match = match_prop(cur, DTD_ATTR_RAW,
1789                     tabptr->zone_fs_raw);
1790                 type_match = match_prop(cur, DTD_ATTR_TYPE,
1791                     tabptr->zone_fs_type);
1792                 if (dir_match && spec_match && raw_match && type_match) {
1793                         xmlUnlinkNode(cur);
1794                         xmlFreeNode(cur);
1795                         return (Z_OK);
1796                 }
1797         }
1798         return (Z_NO_RESOURCE_ID);
1799 }
1800 
1801 int
1802 zonecfg_delete_filesystem(zone_dochandle_t handle, struct zone_fstab *tabptr)
1803 {
1804         int err;
1805 
1806         if (tabptr == NULL)
1807                 return (Z_INVAL);
1808 
1809         if ((err = operation_prep(handle)) != Z_OK)
1810                 return (err);
1811 
1812         if ((err = zonecfg_delete_filesystem_core(handle, tabptr)) != Z_OK)
1813                 return (err);
1814 
1815         return (Z_OK);
1816 }
1817 
1818 int
1819 zonecfg_modify_filesystem(
1820         zone_dochandle_t handle,
1821         struct zone_fstab *oldtabptr,
1822         struct zone_fstab *newtabptr)
1823 {
1824         int err;
1825 
1826         if (oldtabptr == NULL || newtabptr == NULL)
1827                 return (Z_INVAL);
1828 
1829         if ((err = operation_prep(handle)) != Z_OK)
1830                 return (err);
1831 
1832         if ((err = zonecfg_delete_filesystem_core(handle, oldtabptr)) != Z_OK)
1833                 return (err);
1834 
1835         if ((err = zonecfg_add_filesystem_core(handle, newtabptr)) != Z_OK)
1836                 return (err);
1837 
1838         return (Z_OK);
1839 }
1840 
1841 int
1842 zonecfg_lookup_filesystem(
1843         zone_dochandle_t handle,
1844         struct zone_fstab *tabptr)
1845 {
1846         xmlNodePtr cur, options, firstmatch;
1847         int err;
1848         char dirname[MAXPATHLEN], special[MAXPATHLEN], raw[MAXPATHLEN];
1849         char type[FSTYPSZ];
1850         char options_str[MAX_MNTOPT_STR];
1851 
1852         if (tabptr == NULL)
1853                 return (Z_INVAL);
1854 
1855         if ((err = operation_prep(handle)) != Z_OK)
1856                 return (err);
1857 
1858         /*
1859          * Walk the list of children looking for matches on any properties
1860          * specified in the fstab parameter.  If more than one resource
1861          * matches, we return Z_INSUFFICIENT_SPEC; if none match, we return
1862          * Z_NO_RESOURCE_ID.
1863          */
1864         cur = handle->zone_dh_cur;
1865         firstmatch = NULL;
1866         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
1867                 if (xmlStrcmp(cur->name, DTD_ELEM_FS))
1868                         continue;
1869                 if (strlen(tabptr->zone_fs_dir) > 0) {
1870                         if ((fetchprop(cur, DTD_ATTR_DIR, dirname,
1871                             sizeof (dirname)) == Z_OK) &&
1872                             (strcmp(tabptr->zone_fs_dir, dirname) == 0)) {
1873                                 if (firstmatch == NULL)
1874                                         firstmatch = cur;
1875                                 else
1876                                         return (Z_INSUFFICIENT_SPEC);
1877                         }
1878                 }
1879                 if (strlen(tabptr->zone_fs_special) > 0) {
1880                         if ((fetchprop(cur, DTD_ATTR_SPECIAL, special,
1881                             sizeof (special)) == Z_OK)) {
1882                                 if (strcmp(tabptr->zone_fs_special,
1883                                     special) == 0) {
1884                                         if (firstmatch == NULL)
1885                                                 firstmatch = cur;
1886                                         else if (firstmatch != cur)
1887                                                 return (Z_INSUFFICIENT_SPEC);
1888                                 } else {
1889                                         /*
1890                                          * If another property matched but this
1891                                          * one doesn't then reset firstmatch.
1892                                          */
1893                                         if (firstmatch == cur)
1894                                                 firstmatch = NULL;
1895                                 }
1896                         }
1897                 }
1898                 if (strlen(tabptr->zone_fs_raw) > 0) {
1899                         if ((fetchprop(cur, DTD_ATTR_RAW, raw,
1900                             sizeof (raw)) == Z_OK)) {
1901                                 if (strcmp(tabptr->zone_fs_raw, raw) == 0) {
1902                                         if (firstmatch == NULL)
1903                                                 firstmatch = cur;
1904                                         else if (firstmatch != cur)
1905                                                 return (Z_INSUFFICIENT_SPEC);
1906                                 } else {
1907                                         /*
1908                                          * If another property matched but this
1909                                          * one doesn't then reset firstmatch.
1910                                          */
1911                                         if (firstmatch == cur)
1912                                                 firstmatch = NULL;
1913                                 }
1914                         }
1915                 }
1916                 if (strlen(tabptr->zone_fs_type) > 0) {
1917                         if ((fetchprop(cur, DTD_ATTR_TYPE, type,
1918                             sizeof (type)) == Z_OK)) {
1919                                 if (strcmp(tabptr->zone_fs_type, type) == 0) {
1920                                         if (firstmatch == NULL)
1921                                                 firstmatch = cur;
1922                                         else if (firstmatch != cur)
1923                                                 return (Z_INSUFFICIENT_SPEC);
1924                                 } else {
1925                                         /*
1926                                          * If another property matched but this
1927                                          * one doesn't then reset firstmatch.
1928                                          */
1929                                         if (firstmatch == cur)
1930                                                 firstmatch = NULL;
1931                                 }
1932                         }
1933                 }
1934         }
1935 
1936         if (firstmatch == NULL)
1937                 return (Z_NO_RESOURCE_ID);
1938 
1939         cur = firstmatch;
1940 
1941         if ((err = fetchprop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir,
1942             sizeof (tabptr->zone_fs_dir))) != Z_OK)
1943                 return (err);
1944 
1945         if ((err = fetchprop(cur, DTD_ATTR_SPECIAL, tabptr->zone_fs_special,
1946             sizeof (tabptr->zone_fs_special))) != Z_OK)
1947                 return (err);
1948 
1949         if ((err = fetchprop(cur, DTD_ATTR_RAW, tabptr->zone_fs_raw,
1950             sizeof (tabptr->zone_fs_raw))) != Z_OK)
1951                 return (err);
1952 
1953         if ((err = fetchprop(cur, DTD_ATTR_TYPE, tabptr->zone_fs_type,
1954             sizeof (tabptr->zone_fs_type))) != Z_OK)
1955                 return (err);
1956 
1957         /* options are optional */
1958         tabptr->zone_fs_options = NULL;
1959         for (options = cur->xmlChildrenNode; options != NULL;
1960             options = options->next) {
1961                 if ((fetchprop(options, DTD_ATTR_NAME, options_str,
1962                     sizeof (options_str)) != Z_OK))
1963                         break;
1964                 if (zonecfg_add_fs_option(tabptr, options_str) != Z_OK)
1965                         break;
1966         }
1967         return (Z_OK);
1968 }
1969 
1970 /*
1971  * Compare two IP addresses in string form.  Allow for the possibility that
1972  * one might have "/<prefix-length>" at the end: allow a match on just the
1973  * IP address (or host name) part.
1974  */
1975 
1976 boolean_t
1977 zonecfg_same_net_address(char *a1, char *a2)
1978 {
1979         char *slashp, *slashp1, *slashp2;
1980         int result;
1981 
1982         if (strcmp(a1, a2) == 0)
1983                 return (B_TRUE);
1984 
1985         /*
1986          * If neither has a slash or both do, they need to match to be
1987          * considered the same, but they did not match above, so fail.
1988          */
1989         slashp1 = strchr(a1, '/');
1990         slashp2 = strchr(a2, '/');
1991         if ((slashp1 == NULL && slashp2 == NULL) ||
1992             (slashp1 != NULL && slashp2 != NULL))
1993                 return (B_FALSE);
1994 
1995         /*
1996          * Only one had a slash: pick that one, zero out the slash, compare
1997          * the "address only" strings, restore the slash, and return the
1998          * result of the comparison.
1999          */
2000         slashp = (slashp1 == NULL) ? slashp2 : slashp1;
2001         *slashp = '\0';
2002         result = strcmp(a1, a2);
2003         *slashp = '/';
2004         return ((result == 0));
2005 }
2006 
2007 int
2008 zonecfg_valid_net_address(char *address, struct lifreq *lifr)
2009 {
2010         struct sockaddr_in *sin4;
2011         struct sockaddr_in6 *sin6;
2012         struct addrinfo hints, *result;
2013         char *slashp = strchr(address, '/');
2014 
2015         bzero(lifr, sizeof (struct lifreq));
2016         sin4 = (struct sockaddr_in *)&lifr->lifr_addr;
2017         sin6 = (struct sockaddr_in6 *)&lifr->lifr_addr;
2018         if (slashp != NULL)
2019                 *slashp = '\0';
2020         if (inet_pton(AF_INET, address, &sin4->sin_addr) == 1) {
2021                 sin4->sin_family = AF_INET;
2022         } else if (inet_pton(AF_INET6, address, &sin6->sin6_addr) == 1) {
2023                 if (slashp == NULL)
2024                         return (Z_IPV6_ADDR_PREFIX_LEN);
2025                 sin6->sin6_family = AF_INET6;
2026         } else {
2027                 /* "address" may be a host name */
2028                 (void) memset(&hints, 0, sizeof (hints));
2029                 hints.ai_family = PF_INET;
2030                 if (getaddrinfo(address, NULL, &hints, &result) != 0)
2031                         return (Z_BOGUS_ADDRESS);
2032                 sin4->sin_family = result->ai_family;
2033 
2034                 (void) memcpy(&sin4->sin_addr,
2035                     /* LINTED E_BAD_PTR_CAST_ALIGN */
2036                     &((struct sockaddr_in *)result->ai_addr)->sin_addr,
2037                     sizeof (struct in_addr));
2038 
2039                 freeaddrinfo(result);
2040         }
2041         return (Z_OK);
2042 }
2043 
2044 boolean_t
2045 zonecfg_ifname_exists(sa_family_t af, char *ifname)
2046 {
2047         struct lifreq lifr;
2048         int so;
2049         int save_errno;
2050 
2051         (void) memset(&lifr, 0, sizeof (lifr));
2052         (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
2053         lifr.lifr_addr.ss_family = af;
2054         if ((so = socket(af, SOCK_DGRAM, 0)) < 0) {
2055                 /* Odd - can't tell if the ifname exists */
2056                 return (B_FALSE);
2057         }
2058         if (ioctl(so, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
2059                 save_errno = errno;
2060                 (void) close(so);
2061                 errno = save_errno;
2062                 return (B_FALSE);
2063         }
2064         (void) close(so);
2065         return (B_TRUE);
2066 }
2067 
2068 /*
2069  * Determines whether there is a net resource with the physical interface, IP
2070  * address, and default router specified by 'tabptr' in the zone configuration
2071  * to which 'handle' refers.  'tabptr' must have an interface, an address, a
2072  * default router, or a combination of the three.  This function returns Z_OK
2073  * iff there is exactly one net resource matching the query specified by
2074  * 'tabptr'.  The function returns Z_INSUFFICIENT_SPEC if there are multiple
2075  * matches or 'tabptr' does not specify a physical interface, address, or
2076  * default router.  The function returns Z_NO_RESOURCE_ID if are no matches.
2077  *
2078  * Errors might also be returned if the entry that exactly matches the
2079  * query lacks critical network resource information.
2080  *
2081  * If there is a single match, then the matching entry's physical interface, IP
2082  * address, and default router information are stored in 'tabptr'.
2083  */
2084 int
2085 zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
2086 {
2087         xmlNodePtr cur;
2088         xmlNodePtr firstmatch;
2089         int err;
2090         char address[INET6_ADDRSTRLEN];
2091         char physical[LIFNAMSIZ];
2092         size_t addrspec;                /* nonzero if tabptr has IP addr */
2093         size_t physspec;                /* nonzero if tabptr has interface */
2094         size_t defrouterspec;           /* nonzero if tabptr has def. router */
2095         size_t allowed_addrspec;
2096         zone_iptype_t iptype;
2097 
2098         if (tabptr == NULL)
2099                 return (Z_INVAL);
2100 
2101         /*
2102          * Determine the fields that will be searched.  There must be at least
2103          * one.
2104          *
2105          * zone_nwif_address, zone_nwif_physical, and zone_nwif_defrouter are
2106          * arrays, so no NULL checks are necessary.
2107          */
2108         addrspec = strlen(tabptr->zone_nwif_address);
2109         physspec = strlen(tabptr->zone_nwif_physical);
2110         defrouterspec = strlen(tabptr->zone_nwif_defrouter);
2111         allowed_addrspec = strlen(tabptr->zone_nwif_allowed_address);
2112         if (addrspec != 0 && allowed_addrspec != 0)
2113                 return (Z_INVAL); /* can't specify both */
2114         if (addrspec == 0 && physspec == 0 && defrouterspec == 0 &&
2115             allowed_addrspec == 0)
2116                 return (Z_INSUFFICIENT_SPEC);
2117 
2118         if ((err = operation_prep(handle)) != Z_OK)
2119                 return (err);
2120 
2121         if ((err = zonecfg_get_iptype(handle, &iptype)) != Z_OK)
2122                 return (err);
2123         /*
2124          * Iterate over the configuration's elements and look for net elements
2125          * that match the query.
2126          */
2127         firstmatch = NULL;
2128         cur = handle->zone_dh_cur;
2129         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2130                 /* Skip non-net elements */
2131                 if (xmlStrcmp(cur->name, DTD_ELEM_NET))
2132                         continue;
2133 
2134                 /*
2135                  * If any relevant fields don't match the query, then skip
2136                  * the current net element.
2137                  */
2138                 if (physspec != 0 && (fetchprop(cur, DTD_ATTR_PHYSICAL,
2139                     physical, sizeof (physical)) != Z_OK ||
2140                     strcmp(tabptr->zone_nwif_physical, physical) != 0))
2141                         continue;
2142                 if (iptype == ZS_SHARED && addrspec != 0 &&
2143                     (fetchprop(cur, DTD_ATTR_ADDRESS, address,
2144                     sizeof (address)) != Z_OK ||
2145                     !zonecfg_same_net_address(tabptr->zone_nwif_address,
2146                     address)))
2147                         continue;
2148                 if (iptype == ZS_EXCLUSIVE && allowed_addrspec != 0 &&
2149                     (fetchprop(cur, DTD_ATTR_ALLOWED_ADDRESS, address,
2150                     sizeof (address)) != Z_OK ||
2151                     !zonecfg_same_net_address(tabptr->zone_nwif_allowed_address,
2152                     address)))
2153                         continue;
2154                 if (defrouterspec != 0 && (fetchprop(cur, DTD_ATTR_DEFROUTER,
2155                     address, sizeof (address)) != Z_OK ||
2156                     !zonecfg_same_net_address(tabptr->zone_nwif_defrouter,
2157                     address)))
2158                         continue;
2159 
2160                 /*
2161                  * The current net element matches the query.  Select it if
2162                  * it's the first match; otherwise, abort the search.
2163                  */
2164                 if (firstmatch == NULL)
2165                         firstmatch = cur;
2166                 else
2167                         return (Z_INSUFFICIENT_SPEC);
2168         }
2169         if (firstmatch == NULL)
2170                 return (Z_NO_RESOURCE_ID);
2171 
2172         cur = firstmatch;
2173 
2174         if ((err = fetchprop(cur, DTD_ATTR_PHYSICAL, tabptr->zone_nwif_physical,
2175             sizeof (tabptr->zone_nwif_physical))) != Z_OK)
2176                 return (err);
2177 
2178         if (iptype == ZS_SHARED &&
2179             (err = fetchprop(cur, DTD_ATTR_ADDRESS, tabptr->zone_nwif_address,
2180             sizeof (tabptr->zone_nwif_address))) != Z_OK)
2181                 return (err);
2182 
2183         if (iptype == ZS_EXCLUSIVE &&
2184             (err = fetchprop(cur, DTD_ATTR_ALLOWED_ADDRESS,
2185             tabptr->zone_nwif_allowed_address,
2186             sizeof (tabptr->zone_nwif_allowed_address))) != Z_OK)
2187                 return (err);
2188 
2189         if ((err = fetchprop(cur, DTD_ATTR_DEFROUTER,
2190             tabptr->zone_nwif_defrouter,
2191             sizeof (tabptr->zone_nwif_defrouter))) != Z_OK)
2192                 return (err);
2193 
2194         return (Z_OK);
2195 }
2196 
2197 static int
2198 zonecfg_add_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
2199 {
2200         xmlNodePtr newnode, cur = handle->zone_dh_cur;
2201         int err;
2202 
2203         newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_NET, NULL);
2204         if (strlen(tabptr->zone_nwif_address) > 0 &&
2205             (err = newprop(newnode, DTD_ATTR_ADDRESS,
2206             tabptr->zone_nwif_address)) != Z_OK)
2207                 return (err);
2208         if (strlen(tabptr->zone_nwif_allowed_address) > 0 &&
2209             (err = newprop(newnode, DTD_ATTR_ALLOWED_ADDRESS,
2210             tabptr->zone_nwif_allowed_address)) != Z_OK)
2211                 return (err);
2212         if ((err = newprop(newnode, DTD_ATTR_PHYSICAL,
2213             tabptr->zone_nwif_physical)) != Z_OK)
2214                 return (err);
2215         /*
2216          * Do not add this property when it is not set, for backwards
2217          * compatibility and because it is optional.
2218          */
2219         if ((strlen(tabptr->zone_nwif_defrouter) > 0) &&
2220             ((err = newprop(newnode, DTD_ATTR_DEFROUTER,
2221             tabptr->zone_nwif_defrouter)) != Z_OK))
2222                 return (err);
2223         return (Z_OK);
2224 }
2225 
2226 int
2227 zonecfg_add_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
2228 {
2229         int err;
2230 
2231         if (tabptr == NULL)
2232                 return (Z_INVAL);
2233 
2234         if ((err = operation_prep(handle)) != Z_OK)
2235                 return (err);
2236 
2237         if ((err = zonecfg_add_nwif_core(handle, tabptr)) != Z_OK)
2238                 return (err);
2239 
2240         return (Z_OK);
2241 }
2242 
2243 static int
2244 zonecfg_delete_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
2245 {
2246         xmlNodePtr cur = handle->zone_dh_cur;
2247         boolean_t addr_match, phys_match, allowed_addr_match;
2248 
2249         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2250                 if (xmlStrcmp(cur->name, DTD_ELEM_NET))
2251                         continue;
2252 
2253                 addr_match = match_prop(cur, DTD_ATTR_ADDRESS,
2254                     tabptr->zone_nwif_address);
2255                 allowed_addr_match = match_prop(cur, DTD_ATTR_ALLOWED_ADDRESS,
2256                     tabptr->zone_nwif_allowed_address);
2257                 phys_match = match_prop(cur, DTD_ATTR_PHYSICAL,
2258                     tabptr->zone_nwif_physical);
2259 
2260                 if (addr_match && allowed_addr_match && phys_match) {
2261                         xmlUnlinkNode(cur);
2262                         xmlFreeNode(cur);
2263                         return (Z_OK);
2264                 }
2265         }
2266         return (Z_NO_RESOURCE_ID);
2267 }
2268 
2269 int
2270 zonecfg_delete_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
2271 {
2272         int err;
2273 
2274         if (tabptr == NULL)
2275                 return (Z_INVAL);
2276 
2277         if ((err = operation_prep(handle)) != Z_OK)
2278                 return (err);
2279 
2280         if ((err = zonecfg_delete_nwif_core(handle, tabptr)) != Z_OK)
2281                 return (err);
2282 
2283         return (Z_OK);
2284 }
2285 
2286 int
2287 zonecfg_modify_nwif(
2288         zone_dochandle_t handle,
2289         struct zone_nwiftab *oldtabptr,
2290         struct zone_nwiftab *newtabptr)
2291 {
2292         int err;
2293 
2294         if (oldtabptr == NULL || newtabptr == NULL)
2295                 return (Z_INVAL);
2296 
2297         if ((err = operation_prep(handle)) != Z_OK)
2298                 return (err);
2299 
2300         if ((err = zonecfg_delete_nwif_core(handle, oldtabptr)) != Z_OK)
2301                 return (err);
2302 
2303         if ((err = zonecfg_add_nwif_core(handle, newtabptr)) != Z_OK)
2304                 return (err);
2305 
2306         return (Z_OK);
2307 }
2308 
2309 /*
2310  * Must be a comma-separated list of alpha-numeric file system names.
2311  */
2312 static int
2313 zonecfg_valid_fs_allowed(const char *fsallowedp)
2314 {
2315         char tmp[ZONE_FS_ALLOWED_MAX];
2316         char *cp = tmp;
2317         char *p;
2318 
2319         if (strlen(fsallowedp) > ZONE_FS_ALLOWED_MAX)
2320                 return (Z_TOO_BIG);
2321 
2322         (void) strlcpy(tmp, fsallowedp, sizeof (tmp));
2323 
2324         while (*cp != '\0') {
2325                 p = cp;
2326                 while (*p != '\0' && *p != ',') {
2327                         if (!isalnum(*p) && *p != '-')
2328                                 return (Z_INVALID_PROPERTY);
2329                         p++;
2330                 }
2331 
2332                 if (*p == ',') {
2333                         if (p == cp)
2334                                 return (Z_INVALID_PROPERTY);
2335 
2336                         p++;
2337 
2338                         if (*p == '\0')
2339                                 return (Z_INVALID_PROPERTY);
2340                 }
2341 
2342                 cp = p;
2343         }
2344 
2345         return (Z_OK);
2346 }
2347 
2348 int
2349 zonecfg_get_fs_allowed(zone_dochandle_t handle, char *bufp, size_t buflen)
2350 {
2351         int err;
2352 
2353         if ((err = getrootattr(handle, DTD_ATTR_FS_ALLOWED,
2354             bufp, buflen)) != Z_OK)
2355                 return (err);
2356         if (bufp[0] == '\0')
2357                 return (Z_BAD_PROPERTY);
2358         return (zonecfg_valid_fs_allowed(bufp));
2359 }
2360 
2361 int
2362 zonecfg_set_fs_allowed(zone_dochandle_t handle, const char *bufp)
2363 {
2364         int err;
2365 
2366         if (bufp == NULL || (err = zonecfg_valid_fs_allowed(bufp)) == Z_OK)
2367                 return (setrootattr(handle, DTD_ATTR_FS_ALLOWED, bufp));
2368         return (err);
2369 }
2370 
2371 /*
2372  * Determines if the specified string is a valid hostid string.  This function
2373  * returns Z_OK if the string is a valid hostid string.  It returns Z_INVAL if
2374  * 'hostidp' is NULL, Z_TOO_BIG if 'hostidp' refers to a string buffer
2375  * containing a hex string with more than 8 digits, and Z_INVALID_PROPERTY if
2376  * the string has an invalid format.
2377  */
2378 static int
2379 zonecfg_valid_hostid(const char *hostidp)
2380 {
2381         char *currentp;
2382         u_longlong_t hostidval;
2383         size_t len;
2384 
2385         if (hostidp == NULL)
2386                 return (Z_INVAL);
2387 
2388         /* Empty strings and strings with whitespace are invalid. */
2389         if (*hostidp == '\0')
2390                 return (Z_INVALID_PROPERTY);
2391         for (currentp = (char *)hostidp; *currentp != '\0'; ++currentp) {
2392                 if (isspace(*currentp))
2393                         return (Z_INVALID_PROPERTY);
2394         }
2395         len = (size_t)(currentp - hostidp);
2396 
2397         /*
2398          * The caller might pass a hostid that is larger than the maximum
2399          * unsigned 32-bit integral value.  Check for this!  Also, make sure
2400          * that the whole string is converted (this helps us find illegal
2401          * characters) and that the whole string fits within a buffer of size
2402          * HW_HOSTID_LEN.
2403          */
2404         currentp = (char *)hostidp;
2405         if (strncmp(hostidp, "0x", 2) == 0 || strncmp(hostidp, "0X", 2) == 0)
2406                 currentp += 2;
2407         hostidval = strtoull(currentp, &currentp, 16);
2408         if ((size_t)(currentp - hostidp) >= HW_HOSTID_LEN)
2409                 return (Z_TOO_BIG);
2410         if (hostidval > UINT_MAX || hostidval == HW_INVALID_HOSTID ||
2411             currentp != hostidp + len)
2412                 return (Z_INVALID_PROPERTY);
2413         return (Z_OK);
2414 }
2415 
2416 /*
2417  * Gets the zone hostid string stored in the specified zone configuration
2418  * document.  This function returns Z_OK on success.  Z_BAD_PROPERTY is returned
2419  * if the config file doesn't specify a hostid or if the hostid is blank.
2420  *
2421  * Note that buflen should be at least HW_HOSTID_LEN.
2422  */
2423 int
2424 zonecfg_get_hostid(zone_dochandle_t handle, char *bufp, size_t buflen)
2425 {
2426         int err;
2427 
2428         if ((err = getrootattr(handle, DTD_ATTR_HOSTID, bufp, buflen)) != Z_OK)
2429                 return (err);
2430         if (bufp[0] == '\0')
2431                 return (Z_BAD_PROPERTY);
2432         return (zonecfg_valid_hostid(bufp));
2433 }
2434 
2435 /*
2436  * Sets the hostid string in the specified zone config document to the given
2437  * string value.  If 'hostidp' is NULL, then the config document's hostid
2438  * attribute is cleared.  Non-NULL hostids are validated.  This function returns
2439  * Z_OK on success.  Any other return value indicates failure.
2440  */
2441 int
2442 zonecfg_set_hostid(zone_dochandle_t handle, const char *hostidp)
2443 {
2444         int err;
2445 
2446         /*
2447          * A NULL hostid string is interpreted as a request to clear the
2448          * hostid.
2449          */
2450         if (hostidp == NULL || (err = zonecfg_valid_hostid(hostidp)) == Z_OK)
2451                 return (setrootattr(handle, DTD_ATTR_HOSTID, hostidp));
2452         return (err);
2453 }
2454 
2455 int
2456 zonecfg_lookup_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
2457 {
2458         xmlNodePtr cur, firstmatch;
2459         int err;
2460         char match[MAXPATHLEN];
2461 
2462         if (tabptr == NULL)
2463                 return (Z_INVAL);
2464 
2465         if ((err = operation_prep(handle)) != Z_OK)
2466                 return (err);
2467 
2468         cur = handle->zone_dh_cur;
2469         firstmatch = NULL;
2470         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2471                 if (xmlStrcmp(cur->name, DTD_ELEM_DEVICE))
2472                         continue;
2473                 if (strlen(tabptr->zone_dev_match) == 0)
2474                         continue;
2475 
2476                 if ((fetchprop(cur, DTD_ATTR_MATCH, match,
2477                     sizeof (match)) == Z_OK)) {
2478                         if (strcmp(tabptr->zone_dev_match,
2479                             match) == 0) {
2480                                 if (firstmatch == NULL)
2481                                         firstmatch = cur;
2482                                 else if (firstmatch != cur)
2483                                         return (Z_INSUFFICIENT_SPEC);
2484                         } else {
2485                                 /*
2486                                  * If another property matched but this
2487                                  * one doesn't then reset firstmatch.
2488                                  */
2489                                 if (firstmatch == cur)
2490                                         firstmatch = NULL;
2491                         }
2492                 }
2493         }
2494         if (firstmatch == NULL)
2495                 return (Z_NO_RESOURCE_ID);
2496 
2497         cur = firstmatch;
2498 
2499         if ((err = fetchprop(cur, DTD_ATTR_MATCH, tabptr->zone_dev_match,
2500             sizeof (tabptr->zone_dev_match))) != Z_OK)
2501                 return (err);
2502 
2503         return (Z_OK);
2504 }
2505 
2506 static int
2507 zonecfg_add_dev_core(zone_dochandle_t handle, struct zone_devtab *tabptr)
2508 {
2509         xmlNodePtr newnode, cur = handle->zone_dh_cur;
2510         int err;
2511 
2512         newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DEVICE, NULL);
2513 
2514         if ((err = newprop(newnode, DTD_ATTR_MATCH,
2515             tabptr->zone_dev_match)) != Z_OK)
2516                 return (err);
2517 
2518         return (Z_OK);
2519 }
2520 
2521 int
2522 zonecfg_add_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
2523 {
2524         int err;
2525 
2526         if (tabptr == NULL)
2527                 return (Z_INVAL);
2528 
2529         if ((err = operation_prep(handle)) != Z_OK)
2530                 return (err);
2531 
2532         if ((err = zonecfg_add_dev_core(handle, tabptr)) != Z_OK)
2533                 return (err);
2534 
2535         return (Z_OK);
2536 }
2537 
2538 static int
2539 zonecfg_delete_dev_core(zone_dochandle_t handle, struct zone_devtab *tabptr)
2540 {
2541         xmlNodePtr cur = handle->zone_dh_cur;
2542         int match_match;
2543 
2544         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2545                 if (xmlStrcmp(cur->name, DTD_ELEM_DEVICE))
2546                         continue;
2547 
2548                 match_match = match_prop(cur, DTD_ATTR_MATCH,
2549                     tabptr->zone_dev_match);
2550 
2551                 if (match_match) {
2552                         xmlUnlinkNode(cur);
2553                         xmlFreeNode(cur);
2554                         return (Z_OK);
2555                 }
2556         }
2557         return (Z_NO_RESOURCE_ID);
2558 }
2559 
2560 int
2561 zonecfg_delete_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
2562 {
2563         int err;
2564 
2565         if (tabptr == NULL)
2566                 return (Z_INVAL);
2567 
2568         if ((err = operation_prep(handle)) != Z_OK)
2569                 return (err);
2570 
2571         if ((err = zonecfg_delete_dev_core(handle, tabptr)) != Z_OK)
2572                 return (err);
2573 
2574         return (Z_OK);
2575 }
2576 
2577 int
2578 zonecfg_modify_dev(
2579         zone_dochandle_t handle,
2580         struct zone_devtab *oldtabptr,
2581         struct zone_devtab *newtabptr)
2582 {
2583         int err;
2584 
2585         if (oldtabptr == NULL || newtabptr == NULL)
2586                 return (Z_INVAL);
2587 
2588         if ((err = operation_prep(handle)) != Z_OK)
2589                 return (err);
2590 
2591         if ((err = zonecfg_delete_dev_core(handle, oldtabptr)) != Z_OK)
2592                 return (err);
2593 
2594         if ((err = zonecfg_add_dev_core(handle, newtabptr)) != Z_OK)
2595                 return (err);
2596 
2597         return (Z_OK);
2598 }
2599 
2600 static int
2601 zonecfg_add_auth_core(zone_dochandle_t handle, struct zone_admintab *tabptr,
2602     char *zonename)
2603 {
2604         xmlNodePtr newnode, cur = handle->zone_dh_cur;
2605         int err;
2606 
2607         newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_ADMIN, NULL);
2608         err = newprop(newnode, DTD_ATTR_USER, tabptr->zone_admin_user);
2609         if (err != Z_OK)
2610                 return (err);
2611         err = newprop(newnode, DTD_ATTR_AUTHS, tabptr->zone_admin_auths);
2612         if (err != Z_OK)
2613                 return (err);
2614         if ((err = zonecfg_remove_userauths(
2615             handle, tabptr->zone_admin_user, zonename, B_FALSE)) != Z_OK)
2616                 return (err);
2617         return (Z_OK);
2618 }
2619 
2620 int
2621 zonecfg_add_admin(zone_dochandle_t handle, struct zone_admintab *tabptr,
2622     char *zonename)
2623 {
2624         int err;
2625 
2626         if (tabptr == NULL)
2627                 return (Z_INVAL);
2628 
2629         if ((err = operation_prep(handle)) != Z_OK)
2630                 return (err);
2631 
2632         if ((err = zonecfg_add_auth_core(handle, tabptr,
2633             zonename)) != Z_OK)
2634                 return (err);
2635 
2636         return (Z_OK);
2637 }

2638 static int
2639 zonecfg_delete_auth_core(zone_dochandle_t handle, struct zone_admintab *tabptr,
2640     char *zonename)
2641 {
2642         xmlNodePtr cur = handle->zone_dh_cur;
2643         boolean_t auth_match;
2644         int err;
2645 
2646         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2647                 if (xmlStrcmp(cur->name, DTD_ELEM_ADMIN))
2648                         continue;
2649                 auth_match = match_prop(cur, DTD_ATTR_USER,
2650                     tabptr->zone_admin_user);
2651                 if (auth_match) {
2652                         if ((err = zonecfg_insert_userauths(
2653                             handle, tabptr->zone_admin_user,
2654                             zonename)) != Z_OK)
2655                                 return (err);
2656                         xmlUnlinkNode(cur);
2657                         xmlFreeNode(cur);
2658                         return (Z_OK);
2659                 }
2660         }
2661         return (Z_NO_RESOURCE_ID);
2662 }
2663 
2664 int
2665 zonecfg_delete_admin(zone_dochandle_t handle, struct zone_admintab *tabptr,
2666     char *zonename)
2667 {
2668         int err;
2669 
2670         if (tabptr == NULL)
2671                 return (Z_INVAL);
2672 
2673         if ((err = operation_prep(handle)) != Z_OK)
2674                 return (err);
2675 
2676         if ((err = zonecfg_delete_auth_core(handle, tabptr, zonename)) != Z_OK)
2677                 return (err);
2678 
2679         return (Z_OK);
2680 }
2681 
2682 int
2683 zonecfg_modify_admin(zone_dochandle_t handle, struct zone_admintab *oldtabptr,
2684     struct zone_admintab *newtabptr, char *zonename)
2685 {
2686         int err;
2687 
2688         if (oldtabptr == NULL || newtabptr == NULL)
2689                 return (Z_INVAL);
2690 
2691         if ((err = operation_prep(handle)) != Z_OK)
2692                 return (err);
2693 
2694         if ((err = zonecfg_delete_auth_core(handle, oldtabptr, zonename))
2695             != Z_OK)
2696                 return (err);
2697 
2698         if ((err = zonecfg_add_auth_core(handle, newtabptr,
2699             zonename)) != Z_OK)
2700                 return (err);
2701 
2702         return (Z_OK);
2703 }
2704 
2705 int
2706 zonecfg_lookup_admin(zone_dochandle_t handle, struct zone_admintab *tabptr)
2707 {
2708         xmlNodePtr cur, firstmatch;
2709         int err;
2710         char user[MAXUSERNAME];
2711 
2712         if (tabptr == NULL)
2713                 return (Z_INVAL);
2714 
2715         if ((err = operation_prep(handle)) != Z_OK)
2716                 return (err);
2717 
2718         cur = handle->zone_dh_cur;
2719         firstmatch = NULL;
2720         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2721                 if (xmlStrcmp(cur->name, DTD_ELEM_ADMIN))
2722                         continue;
2723                 if (strlen(tabptr->zone_admin_user) > 0) {
2724                         if ((fetchprop(cur, DTD_ATTR_USER, user,
2725                             sizeof (user)) == Z_OK) &&
2726                             (strcmp(tabptr->zone_admin_user, user) == 0)) {
2727                                 if (firstmatch == NULL)
2728                                         firstmatch = cur;
2729                                 else
2730                                         return (Z_INSUFFICIENT_SPEC);
2731                         }
2732                 }
2733         }
2734         if (firstmatch == NULL)
2735                 return (Z_NO_RESOURCE_ID);
2736 
2737         cur = firstmatch;
2738 
2739         if ((err = fetchprop(cur, DTD_ATTR_USER, tabptr->zone_admin_user,
2740             sizeof (tabptr->zone_admin_user))) != Z_OK)
2741                 return (err);
2742 
2743         if ((err = fetchprop(cur, DTD_ATTR_AUTHS, tabptr->zone_admin_auths,
2744             sizeof (tabptr->zone_admin_auths))) != Z_OK)
2745                 return (err);
2746 
2747         return (Z_OK);
2748 }
2749 

























































































































































2750 
2751 /* Lock to serialize all devwalks */
2752 static pthread_mutex_t zonecfg_devwalk_lock = PTHREAD_MUTEX_INITIALIZER;
2753 /*
2754  * Global variables used to pass data from zonecfg_dev_manifest to the nftw
2755  * call-back (zonecfg_devwalk_cb).  g_devwalk_data is really the void*
2756  * parameter and g_devwalk_cb is really the *cb parameter from
2757  * zonecfg_dev_manifest.
2758  */
2759 typedef struct __g_devwalk_data *g_devwalk_data_t;
2760 static g_devwalk_data_t g_devwalk_data;
2761 static int (*g_devwalk_cb)(const char *, uid_t, gid_t, mode_t, const char *,
2762     void *);
2763 static size_t g_devwalk_skip_prefix;
2764 
2765 /*
2766  * zonecfg_dev_manifest call-back function used during detach to generate the
2767  * dev info in the manifest.
2768  */
2769 static int
2770 get_detach_dev_entry(const char *name, uid_t uid, gid_t gid, mode_t mode,
2771     const char *acl, void *hdl)
2772 {
2773         zone_dochandle_t handle = (zone_dochandle_t)hdl;
2774         xmlNodePtr newnode;
2775         xmlNodePtr cur;
2776         int err;
2777         char buf[128];
2778 
2779         if ((err = operation_prep(handle)) != Z_OK)
2780                 return (err);
2781 
2782         cur = handle->zone_dh_cur;
2783         newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DEV_PERM, NULL);
2784         if ((err = newprop(newnode, DTD_ATTR_NAME, (char *)name)) != Z_OK)
2785                 return (err);
2786         (void) snprintf(buf, sizeof (buf), "%lu", uid);
2787         if ((err = newprop(newnode, DTD_ATTR_UID, buf)) != Z_OK)
2788                 return (err);
2789         (void) snprintf(buf, sizeof (buf), "%lu", gid);
2790         if ((err = newprop(newnode, DTD_ATTR_GID, buf)) != Z_OK)
2791                 return (err);
2792         (void) snprintf(buf, sizeof (buf), "%o", mode);
2793         if ((err = newprop(newnode, DTD_ATTR_MODE, buf)) != Z_OK)
2794                 return (err);
2795         if ((err = newprop(newnode, DTD_ATTR_ACL, (char *)acl)) != Z_OK)
2796                 return (err);
2797         return (Z_OK);
2798 }
2799 
2800 /*
2801  * This is the nftw call-back function used by zonecfg_dev_manifest.  It is
2802  * responsible for calling the actual call-back.
2803  */
2804 /* ARGSUSED2 */
2805 static int
2806 zonecfg_devwalk_cb(const char *path, const struct stat *st, int f,
2807     struct FTW *ftw)
2808 {
2809         acl_t *acl;
2810         char *acl_txt = NULL;
2811 
2812         /* skip all but character and block devices */
2813         if (!S_ISBLK(st->st_mode) && !S_ISCHR(st->st_mode))
2814                 return (0);
2815 
2816         if ((acl_get(path, ACL_NO_TRIVIAL, &acl) == 0) &&
2817             acl != NULL) {
2818                 acl_txt = acl_totext(acl, ACL_NORESOLVE);
2819                 acl_free(acl);
2820         }
2821 
2822         if (strlen(path) <= g_devwalk_skip_prefix)
2823                 return (0);
2824 
2825         g_devwalk_cb(path + g_devwalk_skip_prefix, st->st_uid, st->st_gid,
2826             st->st_mode & S_IAMB, acl_txt != NULL ? acl_txt : "",
2827             g_devwalk_data);
2828         free(acl_txt);
2829         return (0);
2830 }
2831 
2832 /*
2833  * Walk the dev tree for the zone specified by hdl and call the
2834  * get_detach_dev_entry call-back function for each entry in the tree.  The
2835  * call-back will be passed the name, uid, gid, mode, acl string and the
2836  * handle input parameter for each dev entry.
2837  *
2838  * Data is passed to get_detach_dev_entry through the global variables
2839  * g_devwalk_data, *g_devwalk_cb, and g_devwalk_skip_prefix.  The
2840  * zonecfg_devwalk_cb function will actually call get_detach_dev_entry.
2841  */
2842 int
2843 zonecfg_dev_manifest(zone_dochandle_t hdl)
2844 {
2845         char path[MAXPATHLEN];
2846         int ret;
2847 
2848         if ((ret = zonecfg_get_zonepath(hdl, path, sizeof (path))) != Z_OK)
2849                 return (ret);
2850 
2851         if (strlcat(path, "/dev", sizeof (path)) >= sizeof (path))
2852                 return (Z_TOO_BIG);
2853 
2854         /*
2855          * We have to serialize all devwalks in the same process
2856          * (which should be fine), since nftw() is so badly designed.
2857          */
2858         (void) pthread_mutex_lock(&zonecfg_devwalk_lock);
2859 
2860         g_devwalk_skip_prefix = strlen(path) + 1;
2861         g_devwalk_data = (g_devwalk_data_t)hdl;
2862         g_devwalk_cb = get_detach_dev_entry;
2863         (void) nftw(path, zonecfg_devwalk_cb, 0, FTW_PHYS);
2864 
2865         (void) pthread_mutex_unlock(&zonecfg_devwalk_lock);
2866         return (Z_OK);
2867 }
2868 
2869 /*
2870  * Update the owner, group, mode and acl on the specified dev (inpath) for
2871  * the zone (hdl).  This function can be used to fix up the dev tree after
2872  * attaching a migrated zone.
2873  */
2874 int
2875 zonecfg_devperms_apply(zone_dochandle_t hdl, const char *inpath, uid_t owner,
2876     gid_t group, mode_t mode, const char *acltxt)
2877 {
2878         int ret;
2879         char path[MAXPATHLEN];
2880         struct stat st;
2881         acl_t *aclp;
2882 
2883         if ((ret = zonecfg_get_zonepath(hdl, path, sizeof (path))) != Z_OK)
2884                 return (ret);
2885 
2886         if (strlcat(path, "/dev/", sizeof (path)) >= sizeof (path))
2887                 return (Z_TOO_BIG);
2888         if (strlcat(path, inpath, sizeof (path)) >= sizeof (path))
2889                 return (Z_TOO_BIG);
2890 
2891         if (stat(path, &st) == -1)
2892                 return (Z_INVAL);
2893 
2894         /* make sure we're only touching device nodes */
2895         if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode))
2896                 return (Z_INVAL);
2897 
2898         if (chown(path, owner, group) == -1)
2899                 return (Z_SYSTEM);
2900 
2901         if (chmod(path, mode) == -1)
2902                 return (Z_SYSTEM);
2903 
2904         if ((acltxt == NULL) || (strcmp(acltxt, "") == 0))
2905                 return (Z_OK);
2906 
2907         if (acl_fromtext(acltxt, &aclp) != 0) {
2908                 errno = EINVAL;
2909                 return (Z_SYSTEM);
2910         }
2911 
2912         errno = 0;
2913         if (acl_set(path, aclp) == -1) {
2914                 free(aclp);
2915                 return (Z_SYSTEM);
2916         }
2917 
2918         free(aclp);
2919         return (Z_OK);
2920 }
2921 
2922 /*
2923  * This function finds everything mounted under a zone's rootpath.
2924  * This returns the number of mounts under rootpath, or -1 on error.
2925  * callback is called once per mount found with the first argument
2926  * pointing to a mnttab structure containing the mount's information.
2927  *
2928  * If the callback function returns non-zero zonecfg_find_mounts
2929  * aborts with an error.
2930  */
2931 int
2932 zonecfg_find_mounts(char *rootpath, int (*callback)(const struct mnttab *,
2933     void *), void *priv) {

2934         FILE *mnttab;
2935         struct mnttab m;
2936         size_t l;
2937         int zfsl;
2938         int rv = 0;
2939         char zfs_path[MAXPATHLEN];
2940 
2941         assert(rootpath != NULL);
2942 
2943         if ((zfsl = snprintf(zfs_path, sizeof (zfs_path), "%s/.zfs/", rootpath))
2944             >= sizeof (zfs_path))
2945                 return (-1);
2946 
2947         l = strlen(rootpath);
2948 
2949         mnttab = fopen("/etc/mnttab", "r");
2950 
2951         if (mnttab == NULL)
2952                 return (-1);
2953 
2954         if (ioctl(fileno(mnttab), MNTIOC_SHOWHIDDEN, NULL) < 0)  {
2955                 rv = -1;
2956                 goto out;
2957         }
2958 
2959         while (!getmntent(mnttab, &m)) {
2960                 if ((strncmp(rootpath, m.mnt_mountp, l) == 0) &&
2961                     (m.mnt_mountp[l] == '/') &&
2962                     (strncmp(zfs_path, m.mnt_mountp, zfsl) != 0)) {
2963                         rv++;
2964                         if (callback == NULL)
2965                                 continue;
2966                         if (callback(&m, priv)) {
2967                                 rv = -1;
2968                                 goto out;
2969 
2970                         }
2971                 }
2972         }
2973 
2974 out:
2975         (void) fclose(mnttab);
2976         return (rv);
2977 }
2978 
2979 int
2980 zonecfg_lookup_attr(zone_dochandle_t handle, struct zone_attrtab *tabptr)
2981 {
2982         xmlNodePtr cur, firstmatch;
2983         int err;
2984         char name[MAXNAMELEN], type[MAXNAMELEN], value[MAXNAMELEN];
2985 
2986         if (tabptr == NULL)
2987                 return (Z_INVAL);
2988 
2989         if ((err = operation_prep(handle)) != Z_OK)
2990                 return (err);
2991 
2992         cur = handle->zone_dh_cur;
2993         firstmatch = NULL;
2994         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2995                 if (xmlStrcmp(cur->name, DTD_ELEM_ATTR))
2996                         continue;
2997                 if (strlen(tabptr->zone_attr_name) > 0) {
2998                         if ((fetchprop(cur, DTD_ATTR_NAME, name,
2999                             sizeof (name)) == Z_OK) &&
3000                             (strcmp(tabptr->zone_attr_name, name) == 0)) {
3001                                 if (firstmatch == NULL)
3002                                         firstmatch = cur;
3003                                 else
3004                                         return (Z_INSUFFICIENT_SPEC);
3005                         }
3006                 }
3007                 if (strlen(tabptr->zone_attr_type) > 0) {
3008                         if ((fetchprop(cur, DTD_ATTR_TYPE, type,
3009                             sizeof (type)) == Z_OK)) {
3010                                 if (strcmp(tabptr->zone_attr_type, type) == 0) {
3011                                         if (firstmatch == NULL)
3012                                                 firstmatch = cur;
3013                                         else if (firstmatch != cur)
3014                                                 return (Z_INSUFFICIENT_SPEC);
3015                                 } else {
3016                                         /*
3017                                          * If another property matched but this
3018                                          * one doesn't then reset firstmatch.
3019                                          */
3020                                         if (firstmatch == cur)
3021                                                 firstmatch = NULL;
3022                                 }
3023                         }
3024                 }
3025                 if (strlen(tabptr->zone_attr_value) > 0) {
3026                         if ((fetchprop(cur, DTD_ATTR_VALUE, value,
3027                             sizeof (value)) == Z_OK)) {
3028                                 if (strcmp(tabptr->zone_attr_value, value) ==
3029                                     0) {
3030                                         if (firstmatch == NULL)
3031                                                 firstmatch = cur;
3032                                         else if (firstmatch != cur)
3033                                                 return (Z_INSUFFICIENT_SPEC);
3034                                 } else {
3035                                         /*
3036                                          * If another property matched but this
3037                                          * one doesn't then reset firstmatch.
3038                                          */
3039                                         if (firstmatch == cur)
3040                                                 firstmatch = NULL;
3041                                 }
3042                         }
3043                 }
3044         }
3045         if (firstmatch == NULL)
3046                 return (Z_NO_RESOURCE_ID);
3047 
3048         cur = firstmatch;
3049 
3050         if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_attr_name,
3051             sizeof (tabptr->zone_attr_name))) != Z_OK)
3052                 return (err);
3053 
3054         if ((err = fetchprop(cur, DTD_ATTR_TYPE, tabptr->zone_attr_type,
3055             sizeof (tabptr->zone_attr_type))) != Z_OK)
3056                 return (err);
3057 
3058         if ((err = fetchprop(cur, DTD_ATTR_VALUE, tabptr->zone_attr_value,
3059             sizeof (tabptr->zone_attr_value))) != Z_OK)
3060                 return (err);
3061 
3062         return (Z_OK);
3063 }
3064 
3065 static int
3066 zonecfg_add_attr_core(zone_dochandle_t handle, struct zone_attrtab *tabptr)
3067 {
3068         xmlNodePtr newnode, cur = handle->zone_dh_cur;
3069         int err;
3070 
3071         newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_ATTR, NULL);
3072         err = newprop(newnode, DTD_ATTR_NAME, tabptr->zone_attr_name);
3073         if (err != Z_OK)
3074                 return (err);
3075         err = newprop(newnode, DTD_ATTR_TYPE, tabptr->zone_attr_type);
3076         if (err != Z_OK)
3077                 return (err);
3078         err = newprop(newnode, DTD_ATTR_VALUE, tabptr->zone_attr_value);
3079         if (err != Z_OK)
3080                 return (err);
3081         return (Z_OK);
3082 }
3083 
3084 int
3085 zonecfg_add_attr(zone_dochandle_t handle, struct zone_attrtab *tabptr)
3086 {
3087         int err;
3088 
3089         if (tabptr == NULL)
3090                 return (Z_INVAL);
3091 
3092         if ((err = operation_prep(handle)) != Z_OK)
3093                 return (err);
3094 
3095         if ((err = zonecfg_add_attr_core(handle, tabptr)) != Z_OK)
3096                 return (err);
3097 
3098         return (Z_OK);
3099 }
3100 
3101 static int
3102 zonecfg_delete_attr_core(zone_dochandle_t handle, struct zone_attrtab *tabptr)
3103 {
3104         xmlNodePtr cur = handle->zone_dh_cur;
3105         int name_match, type_match, value_match;
3106 
3107         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
3108                 if (xmlStrcmp(cur->name, DTD_ELEM_ATTR))
3109                         continue;
3110 
3111                 name_match = match_prop(cur, DTD_ATTR_NAME,
3112                     tabptr->zone_attr_name);
3113                 type_match = match_prop(cur, DTD_ATTR_TYPE,
3114                     tabptr->zone_attr_type);
3115                 value_match = match_prop(cur, DTD_ATTR_VALUE,
3116                     tabptr->zone_attr_value);
3117 
3118                 if (name_match && type_match && value_match) {
3119                         xmlUnlinkNode(cur);
3120                         xmlFreeNode(cur);
3121                         return (Z_OK);
3122                 }
3123         }
3124         return (Z_NO_RESOURCE_ID);
3125 }
3126 
3127 int
3128 zonecfg_delete_attr(zone_dochandle_t handle, struct zone_attrtab *tabptr)
3129 {
3130         int err;
3131 
3132         if (tabptr == NULL)
3133                 return (Z_INVAL);
3134 
3135         if ((err = operation_prep(handle)) != Z_OK)
3136                 return (err);
3137 
3138         if ((err = zonecfg_delete_attr_core(handle, tabptr)) != Z_OK)
3139                 return (err);
3140 
3141         return (Z_OK);
3142 }
3143 
3144 int
3145 zonecfg_modify_attr(
3146         zone_dochandle_t handle,
3147         struct zone_attrtab *oldtabptr,
3148         struct zone_attrtab *newtabptr)
3149 {
3150         int err;
3151 
3152         if (oldtabptr == NULL || newtabptr == NULL)
3153                 return (Z_INVAL);
3154 
3155         if ((err = operation_prep(handle)) != Z_OK)
3156                 return (err);
3157 
3158         if ((err = zonecfg_delete_attr_core(handle, oldtabptr)) != Z_OK)
3159                 return (err);
3160 
3161         if ((err = zonecfg_add_attr_core(handle, newtabptr)) != Z_OK)
3162                 return (err);
3163 
3164         return (Z_OK);
3165 }
3166 
3167 int
3168 zonecfg_get_attr_boolean(const struct zone_attrtab *attr, boolean_t *value)
3169 {
3170         if (attr == NULL)
3171                 return (Z_INVAL);
3172 
3173         if (strcmp(attr->zone_attr_type, DTD_ENTITY_BOOLEAN) != 0)
3174                 return (Z_INVAL);
3175 
3176         if (strcmp(attr->zone_attr_value, DTD_ENTITY_TRUE) == 0) {
3177                 *value = B_TRUE;
3178                 return (Z_OK);
3179         }
3180         if (strcmp(attr->zone_attr_value, DTD_ENTITY_FALSE) == 0) {
3181                 *value = B_FALSE;
3182                 return (Z_OK);
3183         }
3184         return (Z_INVAL);
3185 }
3186 
3187 int
3188 zonecfg_get_attr_int(const struct zone_attrtab *attr, int64_t *value)
3189 {
3190         long long result;
3191         char *endptr;
3192 
3193         if (attr == NULL)
3194                 return (Z_INVAL);
3195 
3196         if (strcmp(attr->zone_attr_type, DTD_ENTITY_INT) != 0)
3197                 return (Z_INVAL);
3198 
3199         errno = 0;
3200         result = strtoll(attr->zone_attr_value, &endptr, 10);
3201         if (errno != 0 || *endptr != '\0')
3202                 return (Z_INVAL);
3203         *value = result;
3204         return (Z_OK);
3205 }
3206 
3207 int
3208 zonecfg_get_attr_string(const struct zone_attrtab *attr, char *value,
3209     size_t val_sz)
3210 {
3211         if (attr == NULL)
3212                 return (Z_INVAL);
3213 
3214         if (strcmp(attr->zone_attr_type, DTD_ENTITY_STRING) != 0)
3215                 return (Z_INVAL);
3216 
3217         if (strlcpy(value, attr->zone_attr_value, val_sz) >= val_sz)
3218                 return (Z_TOO_BIG);
3219         return (Z_OK);
3220 }
3221 
3222 int
3223 zonecfg_get_attr_uint(const struct zone_attrtab *attr, uint64_t *value)
3224 {
3225         unsigned long long result;
3226         long long neg_result;
3227         char *endptr;
3228 
3229         if (attr == NULL)
3230                 return (Z_INVAL);
3231 
3232         if (strcmp(attr->zone_attr_type, DTD_ENTITY_UINT) != 0)
3233                 return (Z_INVAL);
3234 
3235         errno = 0;
3236         result = strtoull(attr->zone_attr_value, &endptr, 10);
3237         if (errno != 0 || *endptr != '\0')
3238                 return (Z_INVAL);
3239         errno = 0;
3240         neg_result = strtoll(attr->zone_attr_value, &endptr, 10);
3241         /*
3242          * Incredibly, strtoull("<negative number>", ...) will not fail but
3243          * return whatever (negative) number cast as a u_longlong_t, so we
3244          * need to look for this here.
3245          */
3246         if (errno == 0 && neg_result < 0)
3247                 return (Z_INVAL);
3248         *value = result;
3249         return (Z_OK);
3250 }
3251 
3252 int
3253 zonecfg_lookup_rctl(zone_dochandle_t handle, struct zone_rctltab *tabptr)
3254 {
3255         xmlNodePtr cur, val;
3256         char savedname[MAXNAMELEN];
3257         struct zone_rctlvaltab *valptr;
3258         int err;
3259 
3260         if (strlen(tabptr->zone_rctl_name) == 0)
3261                 return (Z_INVAL);
3262 
3263         if ((err = operation_prep(handle)) != Z_OK)
3264                 return (err);
3265 
3266         cur = handle->zone_dh_cur;
3267         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
3268                 if (xmlStrcmp(cur->name, DTD_ELEM_RCTL))
3269                         continue;
3270                 if ((fetchprop(cur, DTD_ATTR_NAME, savedname,
3271                     sizeof (savedname)) == Z_OK) &&
3272                     (strcmp(savedname, tabptr->zone_rctl_name) == 0)) {
3273                         tabptr->zone_rctl_valptr = NULL;
3274                         for (val = cur->xmlChildrenNode; val != NULL;
3275                             val = val->next) {
3276                                 valptr = (struct zone_rctlvaltab *)malloc(
3277                                     sizeof (struct zone_rctlvaltab));
3278                                 if (valptr == NULL)
3279                                         return (Z_NOMEM);
3280                                 if ((fetchprop(val, DTD_ATTR_PRIV,
3281                                     valptr->zone_rctlval_priv,
3282                                     sizeof (valptr->zone_rctlval_priv)) !=
3283                                     Z_OK))
3284                                         break;
3285                                 if ((fetchprop(val, DTD_ATTR_LIMIT,
3286                                     valptr->zone_rctlval_limit,
3287                                     sizeof (valptr->zone_rctlval_limit)) !=
3288                                     Z_OK))
3289                                         break;
3290                                 if ((fetchprop(val, DTD_ATTR_ACTION,
3291                                     valptr->zone_rctlval_action,
3292                                     sizeof (valptr->zone_rctlval_action)) !=
3293                                     Z_OK))
3294                                         break;
3295                                 if (zonecfg_add_rctl_value(tabptr, valptr) !=
3296                                     Z_OK)
3297                                         break;
3298                         }
3299                         return (Z_OK);
3300                 }
3301         }
3302         return (Z_NO_RESOURCE_ID);
3303 }
3304 
3305 static int
3306 zonecfg_add_rctl_core(zone_dochandle_t handle, struct zone_rctltab *tabptr)
3307 {
3308         xmlNodePtr newnode, cur = handle->zone_dh_cur, valnode;
3309         struct zone_rctlvaltab *valptr;
3310         int err;
3311 
3312         newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_RCTL, NULL);
3313         err = newprop(newnode, DTD_ATTR_NAME, tabptr->zone_rctl_name);
3314         if (err != Z_OK)
3315                 return (err);
3316         for (valptr = tabptr->zone_rctl_valptr; valptr != NULL;
3317             valptr = valptr->zone_rctlval_next) {
3318                 valnode = xmlNewTextChild(newnode, NULL,
3319                     DTD_ELEM_RCTLVALUE, NULL);
3320                 err = newprop(valnode, DTD_ATTR_PRIV,
3321                     valptr->zone_rctlval_priv);
3322                 if (err != Z_OK)
3323                         return (err);
3324                 err = newprop(valnode, DTD_ATTR_LIMIT,
3325                     valptr->zone_rctlval_limit);
3326                 if (err != Z_OK)
3327                         return (err);
3328                 err = newprop(valnode, DTD_ATTR_ACTION,
3329                     valptr->zone_rctlval_action);
3330                 if (err != Z_OK)
3331                         return (err);
3332         }
3333         return (Z_OK);
3334 }
3335 
3336 int
3337 zonecfg_add_rctl(zone_dochandle_t handle, struct zone_rctltab *tabptr)
3338 {
3339         int err;
3340 
3341         if (tabptr == NULL)
3342                 return (Z_INVAL);
3343 
3344         if ((err = operation_prep(handle)) != Z_OK)
3345                 return (err);
3346 
3347         if ((err = zonecfg_add_rctl_core(handle, tabptr)) != Z_OK)
3348                 return (err);
3349 
3350         return (Z_OK);
3351 }
3352 
3353 static int
3354 zonecfg_delete_rctl_core(zone_dochandle_t handle, struct zone_rctltab *tabptr)
3355 {
3356         xmlNodePtr cur = handle->zone_dh_cur;
3357         xmlChar *savedname;
3358         int name_result;
3359 
3360         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
3361                 if (xmlStrcmp(cur->name, DTD_ELEM_RCTL))
3362                         continue;
3363 
3364                 savedname = xmlGetProp(cur, DTD_ATTR_NAME);
3365                 if (savedname == NULL)  /* shouldn't happen */
3366                         continue;
3367                 name_result = xmlStrcmp(savedname,
3368                     (const xmlChar *) tabptr->zone_rctl_name);
3369                 xmlFree(savedname);
3370 
3371                 if (name_result == 0) {
3372                         xmlUnlinkNode(cur);
3373                         xmlFreeNode(cur);
3374                         return (Z_OK);
3375                 }
3376         }
3377         return (Z_NO_RESOURCE_ID);
3378 }
3379 
3380 int
3381 zonecfg_delete_rctl(zone_dochandle_t handle, struct zone_rctltab *tabptr)
3382 {
3383         int err;
3384 
3385         if (tabptr == NULL)
3386                 return (Z_INVAL);
3387 
3388         if ((err = operation_prep(handle)) != Z_OK)
3389                 return (err);
3390 
3391         if ((err = zonecfg_delete_rctl_core(handle, tabptr)) != Z_OK)
3392                 return (err);
3393 
3394         return (Z_OK);
3395 }
3396 
3397 int
3398 zonecfg_modify_rctl(
3399         zone_dochandle_t handle,
3400         struct zone_rctltab *oldtabptr,
3401         struct zone_rctltab *newtabptr)
3402 {
3403         int err;
3404 
3405         if (oldtabptr == NULL || newtabptr == NULL)
3406                 return (Z_INVAL);
3407 
3408         if ((err = operation_prep(handle)) != Z_OK)
3409                 return (err);
3410 
3411         if ((err = zonecfg_delete_rctl_core(handle, oldtabptr)) != Z_OK)
3412                 return (err);
3413 
3414         if ((err = zonecfg_add_rctl_core(handle, newtabptr)) != Z_OK)
3415                 return (err);
3416 
3417         return (Z_OK);
3418 }
3419 
3420 int
3421 zonecfg_add_rctl_value(
3422         struct zone_rctltab *tabptr,
3423         struct zone_rctlvaltab *valtabptr)
3424 {
3425         struct zone_rctlvaltab *last, *old, *new;
3426         rctlblk_t *rctlblk = alloca(rctlblk_size());
3427 
3428         last = tabptr->zone_rctl_valptr;
3429         for (old = last; old != NULL; old = old->zone_rctlval_next)
3430                 last = old;     /* walk to the end of the list */
3431         new = valtabptr;        /* alloc'd by caller */
3432         new->zone_rctlval_next = NULL;
3433         if (zonecfg_construct_rctlblk(valtabptr, rctlblk) != Z_OK)
3434                 return (Z_INVAL);
3435         if (!zonecfg_valid_rctlblk(rctlblk))
3436                 return (Z_INVAL);
3437         if (last == NULL)
3438                 tabptr->zone_rctl_valptr = new;
3439         else
3440                 last->zone_rctlval_next = new;
3441         return (Z_OK);
3442 }
3443 
3444 int
3445 zonecfg_remove_rctl_value(
3446         struct zone_rctltab *tabptr,
3447         struct zone_rctlvaltab *valtabptr)
3448 {
3449         struct zone_rctlvaltab *last, *this, *next;
3450 
3451         last = tabptr->zone_rctl_valptr;
3452         for (this = last; this != NULL; this = this->zone_rctlval_next) {
3453                 if (strcmp(this->zone_rctlval_priv,
3454                     valtabptr->zone_rctlval_priv) == 0 &&
3455                     strcmp(this->zone_rctlval_limit,
3456                     valtabptr->zone_rctlval_limit) == 0 &&
3457                     strcmp(this->zone_rctlval_action,
3458                     valtabptr->zone_rctlval_action) == 0) {
3459                         next = this->zone_rctlval_next;
3460                         if (this == tabptr->zone_rctl_valptr)
3461                                 tabptr->zone_rctl_valptr = next;
3462                         else
3463                                 last->zone_rctlval_next = next;
3464                         free(this);
3465                         return (Z_OK);
3466                 } else
3467                         last = this;
3468         }
3469         return (Z_NO_PROPERTY_ID);
3470 }
3471 
3472 void
3473 zonecfg_set_swinv(zone_dochandle_t handle)
3474 {
3475         handle->zone_dh_sw_inv = B_TRUE;
3476 }
3477 
3478 /*
3479  * Add the pkg to the sw inventory on the handle.
3480  */
3481 int
3482 zonecfg_add_pkg(zone_dochandle_t handle, char *name, char *version)
3483 {
3484         xmlNodePtr newnode;
3485         xmlNodePtr cur;
3486         int err;
3487 
3488         if ((err = operation_prep(handle)) != Z_OK)
3489                 return (err);
3490 
3491         cur = handle->zone_dh_cur;
3492         newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_PACKAGE, NULL);
3493         if ((err = newprop(newnode, DTD_ATTR_NAME, name)) != Z_OK)
3494                 return (err);
3495         if ((err = newprop(newnode, DTD_ATTR_VERSION, version)) != Z_OK)
3496                 return (err);
3497         return (Z_OK);
3498 }
3499 
3500 char *
3501 zonecfg_strerror(int errnum)
3502 {
3503         switch (errnum) {
3504         case Z_OK:
3505                 return (dgettext(TEXT_DOMAIN, "OK"));
3506         case Z_EMPTY_DOCUMENT:
3507                 return (dgettext(TEXT_DOMAIN, "Empty document"));
3508         case Z_WRONG_DOC_TYPE:
3509                 return (dgettext(TEXT_DOMAIN, "Wrong document type"));
3510         case Z_BAD_PROPERTY:
3511                 return (dgettext(TEXT_DOMAIN, "Bad document property"));
3512         case Z_TEMP_FILE:
3513                 return (dgettext(TEXT_DOMAIN,
3514                     "Problem creating temporary file"));
3515         case Z_SAVING_FILE:
3516                 return (dgettext(TEXT_DOMAIN, "Problem saving file"));
3517         case Z_NO_ENTRY:
3518                 return (dgettext(TEXT_DOMAIN, "No such entry"));
3519         case Z_BOGUS_ZONE_NAME:
3520                 return (dgettext(TEXT_DOMAIN, "Bogus zone name"));
3521         case Z_REQD_RESOURCE_MISSING:
3522                 return (dgettext(TEXT_DOMAIN, "Required resource missing"));
3523         case Z_REQD_PROPERTY_MISSING:
3524                 return (dgettext(TEXT_DOMAIN, "Required property missing"));
3525         case Z_BAD_HANDLE:
3526                 return (dgettext(TEXT_DOMAIN, "Bad handle"));
3527         case Z_NOMEM:
3528                 return (dgettext(TEXT_DOMAIN, "Out of memory"));
3529         case Z_INVAL:
3530                 return (dgettext(TEXT_DOMAIN, "Invalid argument"));
3531         case Z_ACCES:
3532                 return (dgettext(TEXT_DOMAIN, "Permission denied"));
3533         case Z_TOO_BIG:
3534                 return (dgettext(TEXT_DOMAIN, "Argument list too long"));
3535         case Z_MISC_FS:
3536                 return (dgettext(TEXT_DOMAIN,
3537                     "Miscellaneous file system error"));
3538         case Z_NO_ZONE:
3539                 return (dgettext(TEXT_DOMAIN, "No such zone configured"));
3540         case Z_NO_RESOURCE_TYPE:
3541                 return (dgettext(TEXT_DOMAIN, "No such resource type"));
3542         case Z_NO_RESOURCE_ID:
3543                 return (dgettext(TEXT_DOMAIN, "No such resource with that id"));
3544         case Z_NO_PROPERTY_TYPE:
3545                 return (dgettext(TEXT_DOMAIN, "No such property type"));
3546         case Z_NO_PROPERTY_ID:
3547                 return (dgettext(TEXT_DOMAIN, "No such property with that id"));
3548         case Z_BAD_ZONE_STATE:
3549                 return (dgettext(TEXT_DOMAIN,
3550                     "Zone state is invalid for the requested operation"));
3551         case Z_INVALID_DOCUMENT:
3552                 return (dgettext(TEXT_DOMAIN, "Invalid document"));
3553         case Z_NAME_IN_USE:
3554                 return (dgettext(TEXT_DOMAIN, "Zone name already in use"));
3555         case Z_NO_SUCH_ID:
3556                 return (dgettext(TEXT_DOMAIN, "No such zone ID"));
3557         case Z_UPDATING_INDEX:
3558                 return (dgettext(TEXT_DOMAIN, "Problem updating index file"));
3559         case Z_LOCKING_FILE:
3560                 return (dgettext(TEXT_DOMAIN, "Locking index file"));
3561         case Z_UNLOCKING_FILE:
3562                 return (dgettext(TEXT_DOMAIN, "Unlocking index file"));
3563         case Z_INSUFFICIENT_SPEC:
3564                 return (dgettext(TEXT_DOMAIN, "Insufficient specification"));
3565         case Z_RESOLVED_PATH:
3566                 return (dgettext(TEXT_DOMAIN, "Resolved path mismatch"));
3567         case Z_IPV6_ADDR_PREFIX_LEN:
3568                 return (dgettext(TEXT_DOMAIN,
3569                     "IPv6 address missing required prefix length"));
3570         case Z_BOGUS_ADDRESS:
3571                 return (dgettext(TEXT_DOMAIN,
3572                     "Neither an IPv4 nor an IPv6 address nor a host name"));
3573         case Z_PRIV_PROHIBITED:
3574                 return (dgettext(TEXT_DOMAIN,
3575                     "Specified privilege is prohibited"));
3576         case Z_PRIV_REQUIRED:
3577                 return (dgettext(TEXT_DOMAIN,
3578                     "Required privilege is missing"));
3579         case Z_PRIV_UNKNOWN:
3580                 return (dgettext(TEXT_DOMAIN,
3581                     "Specified privilege is unknown"));
3582         case Z_BRAND_ERROR:
3583                 return (dgettext(TEXT_DOMAIN,
3584                     "Brand-specific error"));
3585         case Z_INCOMPATIBLE:
3586                 return (dgettext(TEXT_DOMAIN, "Incompatible settings"));
3587         case Z_ALIAS_DISALLOW:
3588                 return (dgettext(TEXT_DOMAIN,
3589                     "An incompatible rctl already exists for this property"));
3590         case Z_CLEAR_DISALLOW:
3591                 return (dgettext(TEXT_DOMAIN,
3592                     "Clearing this property is not allowed"));
3593         case Z_POOL:
3594                 return (dgettext(TEXT_DOMAIN, "libpool(3LIB) error"));
3595         case Z_POOLS_NOT_ACTIVE:
3596                 return (dgettext(TEXT_DOMAIN, "Pools facility not active; "
3597                     "zone will not be bound to pool"));
3598         case Z_POOL_ENABLE:
3599                 return (dgettext(TEXT_DOMAIN,
3600                     "Could not enable pools facility"));
3601         case Z_NO_POOL:
3602                 return (dgettext(TEXT_DOMAIN,
3603                     "Pool not found; using default pool"));
3604         case Z_POOL_CREATE:
3605                 return (dgettext(TEXT_DOMAIN,
3606                     "Could not create a temporary pool"));
3607         case Z_POOL_BIND:
3608                 return (dgettext(TEXT_DOMAIN, "Could not bind zone to pool"));
3609         case Z_INVALID_PROPERTY:
3610                 return (dgettext(TEXT_DOMAIN, "Specified property is invalid"));
3611         case Z_SYSTEM:
3612                 return (strerror(errno));
3613         default:
3614                 return (dgettext(TEXT_DOMAIN, "Unknown error"));
3615         }
3616 }
3617 
3618 /*
3619  * Note that the zonecfg_setXent() and zonecfg_endXent() calls are all the
3620  * same, as they just turn around and call zonecfg_setent() / zonecfg_endent().
3621  */
3622 
3623 static int
3624 zonecfg_setent(zone_dochandle_t handle)
3625 {
3626         xmlNodePtr cur;
3627         int err;
3628 
3629         if (handle == NULL)
3630                 return (Z_INVAL);
3631 
3632         if ((err = operation_prep(handle)) != Z_OK) {
3633                 handle->zone_dh_cur = NULL;
3634                 return (err);
3635         }
3636         cur = handle->zone_dh_cur;
3637         cur = cur->xmlChildrenNode;
3638         handle->zone_dh_cur = cur;
3639         return (Z_OK);
3640 }
3641 
3642 static int
3643 zonecfg_endent(zone_dochandle_t handle)
3644 {
3645         if (handle == NULL)
3646                 return (Z_INVAL);
3647 
3648         handle->zone_dh_cur = handle->zone_dh_top;
3649         return (Z_OK);
3650 }
3651 
3652 /*
3653  * Do the work required to manipulate a process through libproc.
3654  * If grab_process() returns no errors (0), then release_process()
3655  * must eventually be called.
3656  *
3657  * Return values:
3658  *      0 Successful creation of agent thread
3659  *      1 Error grabbing
3660  *      2 Error creating agent
3661  */
3662 static int
3663 grab_process(pr_info_handle_t *p)
3664 {
3665         int ret;
3666 
3667         if ((p->pr = Pgrab(p->pid, 0, &ret)) != NULL) {
3668 
3669                 if (Psetflags(p->pr, PR_RLC) != 0) {
3670                         Prelease(p->pr, 0);
3671                         return (1);
3672                 }
3673                 if (Pcreate_agent(p->pr) == 0) {
3674                         return (0);
3675 
3676                 } else {
3677                         Prelease(p->pr, 0);
3678                         return (2);
3679                 }
3680         } else {
3681                 return (1);
3682         }
3683 }
3684 
3685 /*
3686  * Release the specified process. This destroys the agent
3687  * and releases the process. If the process is NULL, nothing
3688  * is done. This function should only be called if grab_process()
3689  * has previously been called and returned success.
3690  *
3691  * This function is Pgrab-safe.
3692  */
3693 static void
3694 release_process(struct ps_prochandle *Pr)
3695 {
3696         if (Pr == NULL)
3697                 return;
3698 
3699         Pdestroy_agent(Pr);
3700         Prelease(Pr, 0);
3701 }
3702 
3703 static boolean_t
3704 grab_zone_proc(char *zonename, pr_info_handle_t *p)
3705 {
3706         DIR *dirp;
3707         struct dirent *dentp;
3708         zoneid_t zoneid;
3709         int pid_self;
3710         psinfo_t psinfo;
3711 
3712         if (zone_get_id(zonename, &zoneid) != 0)
3713                 return (B_FALSE);
3714 
3715         pid_self = getpid();
3716 
3717         if ((dirp = opendir("/proc")) == NULL)
3718                 return (B_FALSE);
3719 
3720         while (dentp = readdir(dirp)) {
3721                 p->pid = atoi(dentp->d_name);
3722 
3723                 /* Skip self */
3724                 if (p->pid == pid_self)
3725                         continue;
3726 
3727                 if (proc_get_psinfo(p->pid, &psinfo) != 0)
3728                         continue;
3729 
3730                 if (psinfo.pr_zoneid != zoneid)
3731                         continue;
3732 
3733                 /* attempt to grab process */
3734                 if (grab_process(p) != 0)
3735                         continue;
3736 
3737                 if (pr_getzoneid(p->pr) != zoneid) {
3738                         release_process(p->pr);
3739                         continue;
3740                 }
3741 
3742                 (void) closedir(dirp);
3743                 return (B_TRUE);
3744         }
3745 
3746         (void) closedir(dirp);
3747         return (B_FALSE);
3748 }
3749 
3750 static boolean_t
3751 get_priv_rctl(struct ps_prochandle *pr, char *name, rctlblk_t *rblk)
3752 {
3753         if (pr_getrctl(pr, name, NULL, rblk, RCTL_FIRST))
3754                 return (B_FALSE);
3755 
3756         if (rctlblk_get_privilege(rblk) == RCPRIV_PRIVILEGED)
3757                 return (B_TRUE);
3758 
3759         while (pr_getrctl(pr, name, rblk, rblk, RCTL_NEXT) == 0) {
3760                 if (rctlblk_get_privilege(rblk) == RCPRIV_PRIVILEGED)
3761                         return (B_TRUE);
3762         }
3763 
3764         return (B_FALSE);
3765 }
3766 
3767 /*
3768  * Apply the current rctl settings to the specified, running zone.
3769  */
3770 int
3771 zonecfg_apply_rctls(char *zone_name, zone_dochandle_t handle)
3772 {
3773         int err;
3774         int res = Z_OK;
3775         rctlblk_t *rblk;
3776         pr_info_handle_t p;
3777         struct zone_rctltab rctl;
3778 
3779         if ((err = zonecfg_setrctlent(handle)) != Z_OK)
3780                 return (err);
3781 
3782         if ((rblk = (rctlblk_t *)malloc(rctlblk_size())) == NULL) {
3783                 (void) zonecfg_endrctlent(handle);
3784                 return (Z_NOMEM);
3785         }
3786 
3787         if (!grab_zone_proc(zone_name, &p)) {
3788                 (void) zonecfg_endrctlent(handle);
3789                 free(rblk);
3790                 return (Z_SYSTEM);
3791         }
3792 
3793         while (zonecfg_getrctlent(handle, &rctl) == Z_OK) {
3794                 char *rname;
3795                 struct zone_rctlvaltab *valptr;
3796 
3797                 rname = rctl.zone_rctl_name;
3798 
3799                 /* first delete all current privileged settings for this rctl */
3800                 while (get_priv_rctl(p.pr, rname, rblk)) {
3801                         if (pr_setrctl(p.pr, rname, NULL, rblk, RCTL_DELETE) !=
3802                             0) {
3803                                 res = Z_SYSTEM;
3804                                 goto done;
3805                         }
3806                 }
3807 
3808                 /* now set each new value for the rctl */
3809                 for (valptr = rctl.zone_rctl_valptr; valptr != NULL;
3810                     valptr = valptr->zone_rctlval_next) {
3811                         if ((err = zonecfg_construct_rctlblk(valptr, rblk))
3812                             != Z_OK) {
3813                                 res = errno = err;
3814                                 goto done;
3815                         }
3816 
3817                         if (pr_setrctl(p.pr, rname, NULL, rblk, RCTL_INSERT)) {
3818                                 res = Z_SYSTEM;
3819                                 goto done;
3820                         }
3821                 }
3822         }
3823 
3824 done:
3825         release_process(p.pr);
3826         free(rblk);
3827         (void) zonecfg_endrctlent(handle);
3828 
3829         return (res);
3830 }
3831 
3832 static const xmlChar *
3833 nm_to_dtd(char *nm)
3834 {
3835         if (strcmp(nm, "device") == 0)
3836                 return (DTD_ELEM_DEVICE);
3837         if (strcmp(nm, "fs") == 0)
3838                 return (DTD_ELEM_FS);
3839         if (strcmp(nm, "net") == 0)
3840                 return (DTD_ELEM_NET);
3841         if (strcmp(nm, "attr") == 0)
3842                 return (DTD_ELEM_ATTR);
3843         if (strcmp(nm, "rctl") == 0)
3844                 return (DTD_ELEM_RCTL);
3845         if (strcmp(nm, "dataset") == 0)
3846                 return (DTD_ELEM_DATASET);
3847         if (strcmp(nm, "admin") == 0)
3848                 return (DTD_ELEM_ADMIN);
3849 
3850         return (NULL);
3851 }
3852 
3853 int
3854 zonecfg_num_resources(zone_dochandle_t handle, char *rsrc)
3855 {
3856         int num = 0;
3857         const xmlChar *dtd;
3858         xmlNodePtr cur;
3859 
3860         if ((dtd = nm_to_dtd(rsrc)) == NULL)
3861                 return (num);
3862 
3863         if (zonecfg_setent(handle) != Z_OK)
3864                 return (num);
3865 
3866         for (cur = handle->zone_dh_cur; cur != NULL; cur = cur->next)
3867                 if (xmlStrcmp(cur->name, dtd) == 0)
3868                         num++;
3869 
3870         (void) zonecfg_endent(handle);
3871 
3872         return (num);
3873 }
3874 
3875 int
3876 zonecfg_del_all_resources(zone_dochandle_t handle, char *rsrc)
3877 {
3878         int err;
3879         const xmlChar *dtd;
3880         xmlNodePtr cur;
3881 
3882         if ((dtd = nm_to_dtd(rsrc)) == NULL)
3883                 return (Z_NO_RESOURCE_TYPE);
3884 
3885         if ((err = zonecfg_setent(handle)) != Z_OK)
3886                 return (err);
3887 
3888         cur = handle->zone_dh_cur;
3889         while (cur != NULL) {
3890                 xmlNodePtr tmp;
3891 
3892                 if (xmlStrcmp(cur->name, dtd)) {
3893                         cur = cur->next;
3894                         continue;
3895                 }
3896 
3897                 tmp = cur->next;
3898                 xmlUnlinkNode(cur);
3899                 xmlFreeNode(cur);
3900                 cur = tmp;
3901         }
3902 
3903         (void) zonecfg_endent(handle);
3904         return (Z_OK);
3905 }
3906 
3907 static boolean_t
3908 valid_uint(char *s, uint64_t *n)
3909 {
3910         char *endp;
3911 
3912         /* strtoull accepts '-'?! so we want to flag that as an error */
3913         if (strchr(s, '-') != NULL)
3914                 return (B_FALSE);
3915 
3916         errno = 0;
3917         *n = strtoull(s, &endp, 10);
3918 
3919         if (errno != 0 || *endp != '\0')
3920                 return (B_FALSE);
3921         return (B_TRUE);
3922 }
3923 
3924 /*
3925  * Convert a string representing a number (possibly a fraction) into an integer.
3926  * The string can have a modifier (K, M, G or T).   The modifiers are treated
3927  * as powers of two (not 10).
3928  */
3929 int
3930 zonecfg_str_to_bytes(char *str, uint64_t *bytes)
3931 {
3932         long double val;
3933         char *unitp;
3934         uint64_t scale;
3935 
3936         if ((val = strtold(str, &unitp)) < 0)
3937                 return (-1);
3938 
3939         /* remove any leading white space from units string */
3940         while (isspace(*unitp) != 0)
3941                 ++unitp;
3942 
3943         /* if no units explicitly set, error */
3944         if (unitp == NULL || *unitp == '\0') {
3945                 scale = 1;
3946         } else {
3947                 int i;
3948                 char *units[] = {"K", "M", "G", "T", NULL};
3949 
3950                 scale = 1024;
3951 
3952                 /* update scale based on units */
3953                 for (i = 0; units[i] != NULL; i++) {
3954                         if (strcasecmp(unitp, units[i]) == 0)
3955                                 break;
3956                         scale <<= 10;
3957                 }
3958 
3959                 if (units[i] == NULL)
3960                         return (-1);
3961         }
3962 
3963         *bytes = (uint64_t)(val * scale);
3964         return (0);
3965 }
3966 
3967 boolean_t
3968 zonecfg_valid_ncpus(char *lowstr, char *highstr)
3969 {
3970         uint64_t low, high;
3971 
3972         if (!valid_uint(lowstr, &low) || !valid_uint(highstr, &high) ||
3973             low < 1 || low > high)
3974                 return (B_FALSE);
3975 
3976         return (B_TRUE);
3977 }
3978 
3979 boolean_t
3980 zonecfg_valid_importance(char *impstr)
3981 {
3982         uint64_t num;
3983 
3984         if (!valid_uint(impstr, &num))
3985                 return (B_FALSE);
3986 
3987         return (B_TRUE);
3988 }
3989 
3990 boolean_t
3991 zonecfg_valid_alias_limit(char *name, char *limitstr, uint64_t *limit)
3992 {
3993         int i;
3994 
3995         for (i = 0; aliases[i].shortname != NULL; i++)
3996                 if (strcmp(name, aliases[i].shortname) == 0)
3997                         break;
3998 
3999         if (aliases[i].shortname == NULL)
4000                 return (B_FALSE);
4001 
4002         if (!valid_uint(limitstr, limit) || *limit < aliases[i].low_limit)
4003                 return (B_FALSE);
4004 
4005         return (B_TRUE);
4006 }
4007 
4008 boolean_t
4009 zonecfg_valid_memlimit(char *memstr, uint64_t *mem_val)
4010 {
4011         if (zonecfg_str_to_bytes(memstr, mem_val) != 0)
4012                 return (B_FALSE);
4013 
4014         return (B_TRUE);
4015 }
4016 
4017 static int
4018 zerr_pool(char *pool_err, int err_size, int res)
4019 {
4020         (void) strlcpy(pool_err, pool_strerror(pool_error()), err_size);
4021         return (res);
4022 }
4023 
4024 static int
4025 create_tmp_pset(char *pool_err, int err_size, pool_conf_t *pconf, pool_t *pool,
4026     char *name, int min, int max)
4027 {
4028         pool_resource_t *res;
4029         pool_elem_t *elem;
4030         pool_value_t *val;
4031 
4032         if ((res = pool_resource_create(pconf, "pset", name)) == NULL)
4033                 return (zerr_pool(pool_err, err_size, Z_POOL));
4034 
4035         if (pool_associate(pconf, pool, res) != PO_SUCCESS)
4036                 return (zerr_pool(pool_err, err_size, Z_POOL));
4037 
4038         if ((elem = pool_resource_to_elem(pconf, res)) == NULL)
4039                 return (zerr_pool(pool_err, err_size, Z_POOL));
4040 
4041         if ((val = pool_value_alloc()) == NULL)
4042                 return (zerr_pool(pool_err, err_size, Z_POOL));
4043 
4044         /* set the maximum number of cpus for the pset */
4045         pool_value_set_uint64(val, (uint64_t)max);
4046 
4047         if (pool_put_property(pconf, elem, "pset.max", val) != PO_SUCCESS) {
4048                 pool_value_free(val);
4049                 return (zerr_pool(pool_err, err_size, Z_POOL));
4050         }
4051 
4052         /* set the minimum number of cpus for the pset */
4053         pool_value_set_uint64(val, (uint64_t)min);
4054 
4055         if (pool_put_property(pconf, elem, "pset.min", val) != PO_SUCCESS) {
4056                 pool_value_free(val);
4057                 return (zerr_pool(pool_err, err_size, Z_POOL));
4058         }
4059 
4060         pool_value_free(val);
4061 
4062         return (Z_OK);
4063 }
4064 
4065 static int
4066 create_tmp_pool(char *pool_err, int err_size, pool_conf_t *pconf, char *name,
4067     struct zone_psettab *pset_tab)
4068 {
4069         pool_t *pool;
4070         int res = Z_OK;
4071 
4072         /* create a temporary pool configuration */
4073         if (pool_conf_open(pconf, NULL, PO_TEMP) != PO_SUCCESS) {
4074                 res = zerr_pool(pool_err, err_size, Z_POOL);
4075                 return (res);
4076         }
4077 
4078         if ((pool = pool_create(pconf, name)) == NULL) {
4079                 res = zerr_pool(pool_err, err_size, Z_POOL_CREATE);
4080                 goto done;
4081         }
4082 
4083         /* set pool importance */
4084         if (pset_tab->zone_importance[0] != '\0') {
4085                 pool_elem_t *elem;
4086                 pool_value_t *val;
4087 
4088                 if ((elem = pool_to_elem(pconf, pool)) == NULL) {
4089                         res = zerr_pool(pool_err, err_size, Z_POOL);
4090                         goto done;
4091                 }
4092 
4093                 if ((val = pool_value_alloc()) == NULL) {
4094                         res = zerr_pool(pool_err, err_size, Z_POOL);
4095                         goto done;
4096                 }
4097 
4098                 pool_value_set_int64(val,
4099                     (int64_t)atoi(pset_tab->zone_importance));
4100 
4101                 if (pool_put_property(pconf, elem, "pool.importance", val)
4102                     != PO_SUCCESS) {
4103                         res = zerr_pool(pool_err, err_size, Z_POOL);
4104                         pool_value_free(val);
4105                         goto done;
4106                 }
4107 
4108                 pool_value_free(val);
4109         }
4110 
4111         if ((res = create_tmp_pset(pool_err, err_size, pconf, pool, name,
4112             atoi(pset_tab->zone_ncpu_min),
4113             atoi(pset_tab->zone_ncpu_max))) != Z_OK)
4114                 goto done;
4115 
4116         /* validation */
4117         if (pool_conf_status(pconf) == POF_INVALID) {
4118                 res = zerr_pool(pool_err, err_size, Z_POOL);
4119                 goto done;
4120         }
4121 
4122         /*
4123          * This validation is the one we expect to fail if the user specified
4124          * an invalid configuration (too many cpus) for this system.
4125          */
4126         if (pool_conf_validate(pconf, POV_RUNTIME) != PO_SUCCESS) {
4127                 res = zerr_pool(pool_err, err_size, Z_POOL_CREATE);
4128                 goto done;
4129         }
4130 
4131         /*
4132          * Commit the dynamic configuration but not the pool configuration
4133          * file.
4134          */
4135         if (pool_conf_commit(pconf, 1) != PO_SUCCESS)
4136                 res = zerr_pool(pool_err, err_size, Z_POOL);
4137 
4138 done:
4139         (void) pool_conf_close(pconf);
4140         return (res);
4141 }
4142 
4143 static int
4144 get_running_tmp_pset(pool_conf_t *pconf, pool_t *pool, pool_resource_t *pset,
4145     struct zone_psettab *pset_tab)
4146 {
4147         int nfound = 0;
4148         pool_elem_t *pe;
4149         pool_value_t *pv = pool_value_alloc();
4150         uint64_t val_uint;
4151 
4152         if (pool != NULL) {
4153                 pe = pool_to_elem(pconf, pool);
4154                 if (pool_get_property(pconf, pe, "pool.importance", pv)
4155                     != POC_INVAL) {
4156                         int64_t val_int;
4157 
4158                         (void) pool_value_get_int64(pv, &val_int);
4159                         (void) snprintf(pset_tab->zone_importance,
4160                             sizeof (pset_tab->zone_importance), "%d", val_int);
4161                         nfound++;
4162                 }
4163         }
4164 
4165         if (pset != NULL) {
4166                 pe = pool_resource_to_elem(pconf, pset);
4167                 if (pool_get_property(pconf, pe, "pset.min", pv) != POC_INVAL) {
4168                         (void) pool_value_get_uint64(pv, &val_uint);
4169                         (void) snprintf(pset_tab->zone_ncpu_min,
4170                             sizeof (pset_tab->zone_ncpu_min), "%u", val_uint);
4171                         nfound++;
4172                 }
4173 
4174                 if (pool_get_property(pconf, pe, "pset.max", pv) != POC_INVAL) {
4175                         (void) pool_value_get_uint64(pv, &val_uint);
4176                         (void) snprintf(pset_tab->zone_ncpu_max,
4177                             sizeof (pset_tab->zone_ncpu_max), "%u", val_uint);
4178                         nfound++;
4179                 }
4180         }
4181 
4182         pool_value_free(pv);
4183 
4184         if (nfound == 3)
4185                 return (PO_SUCCESS);
4186 
4187         return (PO_FAIL);
4188 }
4189 
4190 /*
4191  * Determine if a tmp pool is configured and if so, if the configuration is
4192  * still valid or if it has been changed since the tmp pool was created.
4193  * If the tmp pool configuration is no longer valid, delete the tmp pool.
4194  *
4195  * Set *valid=B_TRUE if there is an existing, valid tmp pool configuration.
4196  */
4197 static int
4198 verify_del_tmp_pool(pool_conf_t *pconf, char *tmp_name, char *pool_err,
4199     int err_size, struct zone_psettab *pset_tab, boolean_t *exists)
4200 {
4201         int res = Z_OK;
4202         pool_t *pool;
4203         pool_resource_t *pset;
4204         struct zone_psettab pset_current;
4205 
4206         *exists = B_FALSE;
4207 
4208         if (pool_conf_open(pconf, pool_dynamic_location(), PO_RDWR)
4209             != PO_SUCCESS) {
4210                 res = zerr_pool(pool_err, err_size, Z_POOL);
4211                 return (res);
4212         }
4213 
4214         pool = pool_get_pool(pconf, tmp_name);
4215         pset = pool_get_resource(pconf, "pset", tmp_name);
4216 
4217         if (pool == NULL && pset == NULL) {
4218                 /* no tmp pool configured */
4219                 goto done;
4220         }
4221 
4222         /*
4223          * If an existing tmp pool for this zone is configured with the proper
4224          * settings, then the tmp pool is valid.
4225          */
4226         if (get_running_tmp_pset(pconf, pool, pset, &pset_current)
4227             == PO_SUCCESS &&
4228             strcmp(pset_tab->zone_ncpu_min,
4229             pset_current.zone_ncpu_min) == 0 &&
4230             strcmp(pset_tab->zone_ncpu_max,
4231             pset_current.zone_ncpu_max) == 0 &&
4232             strcmp(pset_tab->zone_importance,
4233             pset_current.zone_importance) == 0) {
4234                 *exists = B_TRUE;
4235 
4236         } else {
4237                 /*
4238                  * An out-of-date tmp pool configuration exists.  Delete it
4239                  * so that we can create the correct tmp pool config.
4240                  */
4241                 if (pset != NULL &&
4242                     pool_resource_destroy(pconf, pset) != PO_SUCCESS) {
4243                         res = zerr_pool(pool_err, err_size, Z_POOL);
4244                         goto done;
4245                 }
4246 
4247                 if (pool != NULL &&
4248                     pool_destroy(pconf, pool) != PO_SUCCESS) {
4249                         res = zerr_pool(pool_err, err_size, Z_POOL);
4250                         goto done;
4251                 }
4252 
4253                 /* commit dynamic config */
4254                 if (pool_conf_commit(pconf, 0) != PO_SUCCESS)
4255                         res = zerr_pool(pool_err, err_size, Z_POOL);
4256         }
4257 
4258 done:
4259         (void) pool_conf_close(pconf);
4260 
4261         return (res);
4262 }
4263 
4264 /*
4265  * Destroy any existing tmp pool.
4266  */
4267 int
4268 zonecfg_destroy_tmp_pool(char *zone_name, char *pool_err, int err_size)
4269 {
4270         int status;
4271         int res = Z_OK;
4272         pool_conf_t *pconf;
4273         pool_t *pool;
4274         pool_resource_t *pset;
4275         char tmp_name[MAX_TMP_POOL_NAME];
4276 
4277         /* if pools not enabled then nothing to do */
4278         if (pool_get_status(&status) != PO_SUCCESS || status != POOL_ENABLED)
4279                 return (Z_OK);
4280 
4281         if ((pconf = pool_conf_alloc()) == NULL)
4282                 return (zerr_pool(pool_err, err_size, Z_POOL));
4283 
4284         (void) snprintf(tmp_name, sizeof (tmp_name), TMP_POOL_NAME, zone_name);
4285 
4286         if (pool_conf_open(pconf, pool_dynamic_location(), PO_RDWR)
4287             != PO_SUCCESS) {
4288                 res = zerr_pool(pool_err, err_size, Z_POOL);
4289                 pool_conf_free(pconf);
4290                 return (res);
4291         }
4292 
4293         pool = pool_get_pool(pconf, tmp_name);
4294         pset = pool_get_resource(pconf, "pset", tmp_name);
4295 
4296         if (pool == NULL && pset == NULL) {
4297                 /* nothing to destroy, we're done */
4298                 goto done;
4299         }
4300 
4301         if (pset != NULL && pool_resource_destroy(pconf, pset) != PO_SUCCESS) {
4302                 res = zerr_pool(pool_err, err_size, Z_POOL);
4303                 goto done;
4304         }
4305 
4306         if (pool != NULL && pool_destroy(pconf, pool) != PO_SUCCESS) {
4307                 res = zerr_pool(pool_err, err_size, Z_POOL);
4308                 goto done;
4309         }
4310 
4311         /* commit dynamic config */
4312         if (pool_conf_commit(pconf, 0) != PO_SUCCESS)
4313                 res = zerr_pool(pool_err, err_size, Z_POOL);
4314 
4315 done:
4316         (void) pool_conf_close(pconf);
4317         pool_conf_free(pconf);
4318 
4319         return (res);
4320 }
4321 
4322 /*
4323  * Attempt to bind to a tmp pool for this zone.  If there is no tmp pool
4324  * configured, we just return Z_OK.
4325  *
4326  * We either attempt to create the tmp pool for this zone or rebind to an
4327  * existing tmp pool for this zone.
4328  *
4329  * Rebinding is used when a zone with a tmp pool reboots so that we don't have
4330  * to recreate the tmp pool.  To do this we need to be sure we work correctly
4331  * for the following cases:
4332  *
4333  *      - there is an existing, properly configured tmp pool.
4334  *      - zonecfg added tmp pool after zone was booted, must now create.
4335  *      - zonecfg updated tmp pool config after zone was booted, in this case
4336  *        we destroy the old tmp pool and create a new one.
4337  */
4338 int
4339 zonecfg_bind_tmp_pool(zone_dochandle_t handle, zoneid_t zoneid, char *pool_err,
4340     int err_size)
4341 {
4342         struct zone_psettab pset_tab;
4343         int err;
4344         int status;
4345         pool_conf_t *pconf;
4346         boolean_t exists;
4347         char zone_name[ZONENAME_MAX];
4348         char tmp_name[MAX_TMP_POOL_NAME];
4349 
4350         (void) getzonenamebyid(zoneid, zone_name, sizeof (zone_name));
4351 
4352         err = zonecfg_lookup_pset(handle, &pset_tab);
4353 
4354         /* if no temporary pool configured, we're done */
4355         if (err == Z_NO_ENTRY)
4356                 return (Z_OK);
4357 
4358         /*
4359          * importance might not have a value but we need to validate it here,
4360          * so set the default.
4361          */
4362         if (pset_tab.zone_importance[0] == '\0')
4363                 (void) strlcpy(pset_tab.zone_importance, "1",
4364                     sizeof (pset_tab.zone_importance));
4365 
4366         /* if pools not enabled, enable them now */
4367         if (pool_get_status(&status) != PO_SUCCESS || status != POOL_ENABLED) {
4368                 if (pool_set_status(POOL_ENABLED) != PO_SUCCESS)
4369                         return (Z_POOL_ENABLE);
4370         }
4371 
4372         if ((pconf = pool_conf_alloc()) == NULL)
4373                 return (zerr_pool(pool_err, err_size, Z_POOL));
4374 
4375         (void) snprintf(tmp_name, sizeof (tmp_name), TMP_POOL_NAME, zone_name);
4376 
4377         /*
4378          * Check if a valid tmp pool/pset already exists.  If so, we just
4379          * reuse it.
4380          */
4381         if ((err = verify_del_tmp_pool(pconf, tmp_name, pool_err, err_size,
4382             &pset_tab, &exists)) != Z_OK) {
4383                 pool_conf_free(pconf);
4384                 return (err);
4385         }
4386 
4387         if (!exists)
4388                 err = create_tmp_pool(pool_err, err_size, pconf, tmp_name,
4389                     &pset_tab);
4390 
4391         pool_conf_free(pconf);
4392 
4393         if (err != Z_OK)
4394                 return (err);
4395 
4396         /* Bind the zone to the pool. */
4397         if (pool_set_binding(tmp_name, P_ZONEID, zoneid) != PO_SUCCESS)
4398                 return (zerr_pool(pool_err, err_size, Z_POOL_BIND));
4399 
4400         return (Z_OK);
4401 }
4402 
4403 /*
4404  * Attempt to bind to a permanent pool for this zone.  If there is no
4405  * permanent pool configured, we just return Z_OK.
4406  */
4407 int
4408 zonecfg_bind_pool(zone_dochandle_t handle, zoneid_t zoneid, char *pool_err,
4409     int err_size)
4410 {
4411         pool_conf_t *poolconf;
4412         pool_t *pool;
4413         char poolname[MAXPATHLEN];
4414         int status;
4415         int error;
4416 
4417         /*
4418          * Find the pool mentioned in the zone configuration, and bind to it.
4419          */
4420         error = zonecfg_get_pool(handle, poolname, sizeof (poolname));
4421         if (error == Z_NO_ENTRY || (error == Z_OK && strlen(poolname) == 0)) {
4422                 /*
4423                  * The property is not set on the zone, so the pool
4424                  * should be bound to the default pool.  But that's
4425                  * already done by the kernel, so we can just return.
4426                  */
4427                 return (Z_OK);
4428         }
4429         if (error != Z_OK) {
4430                 /*
4431                  * Not an error, even though it shouldn't be happening.
4432                  */
4433                 return (Z_OK);
4434         }
4435         /*
4436          * Don't do anything if pools aren't enabled.
4437          */
4438         if (pool_get_status(&status) != PO_SUCCESS || status != POOL_ENABLED)
4439                 return (Z_POOLS_NOT_ACTIVE);
4440 
4441         /*
4442          * Try to provide a sane error message if the requested pool doesn't
4443          * exist.
4444          */
4445         if ((poolconf = pool_conf_alloc()) == NULL)
4446                 return (zerr_pool(pool_err, err_size, Z_POOL));
4447 
4448         if (pool_conf_open(poolconf, pool_dynamic_location(), PO_RDONLY) !=
4449             PO_SUCCESS) {
4450                 pool_conf_free(poolconf);
4451                 return (zerr_pool(pool_err, err_size, Z_POOL));
4452         }
4453         pool = pool_get_pool(poolconf, poolname);
4454         (void) pool_conf_close(poolconf);
4455         pool_conf_free(poolconf);
4456         if (pool == NULL)
4457                 return (Z_NO_POOL);
4458 
4459         /*
4460          * Bind the zone to the pool.
4461          */
4462         if (pool_set_binding(poolname, P_ZONEID, zoneid) != PO_SUCCESS) {
4463                 /* if bind fails, return poolname for the error msg */
4464                 (void) strlcpy(pool_err, poolname, err_size);
4465                 return (Z_POOL_BIND);
4466         }
4467 
4468         return (Z_OK);
4469 }
4470 
4471 int
4472 zonecfg_get_poolname(zone_dochandle_t handle, char *zone, char *pool,
4473     size_t poolsize)
4474 {
4475         int err;
4476         struct zone_psettab pset_tab;
4477 
4478         err = zonecfg_lookup_pset(handle, &pset_tab);
4479         if ((err != Z_NO_ENTRY) && (err != Z_OK))
4480                 return (err);
4481 
4482         /* pset was found so a temporary pool was created */
4483         if (err == Z_OK) {
4484                 (void) snprintf(pool, poolsize, TMP_POOL_NAME, zone);
4485                 return (Z_OK);
4486         }
4487 
4488         /* lookup the poolname in zonecfg */
4489         return (zonecfg_get_pool(handle, pool, poolsize));
4490 }
4491 
4492 static boolean_t
4493 svc_enabled(char *svc_name)
4494 {
4495         scf_simple_prop_t       *prop;
4496         boolean_t               found = B_FALSE;
4497 
4498         prop = scf_simple_prop_get(NULL, svc_name, SCF_PG_GENERAL,
4499             SCF_PROPERTY_ENABLED);
4500 
4501         if (scf_simple_prop_numvalues(prop) == 1 &&
4502             *scf_simple_prop_next_boolean(prop) != 0)
4503                 found = B_TRUE;
4504 
4505         scf_simple_prop_free(prop);
4506 
4507         return (found);
4508 }
4509 
4510 /*
4511  * If the zone has capped-memory, make sure the rcap service is enabled.
4512  */
4513 int
4514 zonecfg_enable_rcapd(char *err, int size)
4515 {
4516         if (!svc_enabled(RCAP_SERVICE) &&
4517             smf_enable_instance(RCAP_SERVICE, 0) == -1) {
4518                 (void) strlcpy(err, scf_strerror(scf_error()), size);
4519                 return (Z_SYSTEM);
4520         }
4521 
4522         return (Z_OK);
4523 }
4524 
4525 /*
4526  * Return true if pset has cpu range specified and poold is not enabled.
4527  */
4528 boolean_t
4529 zonecfg_warn_poold(zone_dochandle_t handle)
4530 {
4531         struct zone_psettab pset_tab;
4532         int min, max;
4533         int err;
4534 
4535         err = zonecfg_lookup_pset(handle, &pset_tab);
4536 
4537         /* if no temporary pool configured, we're done */
4538         if (err == Z_NO_ENTRY)
4539                 return (B_FALSE);
4540 
4541         min = atoi(pset_tab.zone_ncpu_min);
4542         max = atoi(pset_tab.zone_ncpu_max);
4543 
4544         /* range not specified, no need for poold */
4545         if (min == max)
4546                 return (B_FALSE);
4547 
4548         /* we have a range, check if poold service is enabled */
4549         if (svc_enabled(POOLD_SERVICE))
4550                 return (B_FALSE);
4551 
4552         return (B_TRUE);
4553 }
4554 
4555 /*
4556  * Retrieve the specified pool's thread scheduling class.  'poolname' must
4557  * refer to the name of a configured resource pool.  The thread scheduling
4558  * class specified by the pool will be stored in the buffer to which 'class'
4559  * points.  'clsize' is the byte size of the buffer to which 'class' points.
4560  *
4561  * This function returns Z_OK if it successfully stored the specified pool's
4562  * thread scheduling class into the buffer to which 'class' points.  It returns
4563  * Z_NO_POOL if resource pools are not enabled, the function is unable to
4564  * access the system's resource pools configuration, or the specified pool
4565  * does not exist.  The function returns Z_TOO_BIG if the buffer to which
4566  * 'class' points is not large enough to contain the thread scheduling class'
4567  * name.  The function returns Z_NO_ENTRY if the pool does not specify a thread
4568  * scheduling class.
4569  */
4570 static int
4571 get_pool_sched_class(char *poolname, char *class, int clsize)
4572 {
4573         int status;
4574         pool_conf_t *poolconf;
4575         pool_t *pool;
4576         pool_elem_t *pe;
4577         pool_value_t *pv = pool_value_alloc();
4578         const char *sched_str;
4579 
4580         if (pool_get_status(&status) != PO_SUCCESS || status != POOL_ENABLED)
4581                 return (Z_NO_POOL);
4582 
4583         if ((poolconf = pool_conf_alloc()) == NULL)
4584                 return (Z_NO_POOL);
4585 
4586         if (pool_conf_open(poolconf, pool_dynamic_location(), PO_RDONLY) !=
4587             PO_SUCCESS) {
4588                 pool_conf_free(poolconf);
4589                 return (Z_NO_POOL);
4590         }
4591 
4592         if ((pool = pool_get_pool(poolconf, poolname)) == NULL) {
4593                 (void) pool_conf_close(poolconf);
4594                 pool_conf_free(poolconf);
4595                 return (Z_NO_POOL);
4596         }
4597 
4598         pe = pool_to_elem(poolconf, pool);
4599         if (pool_get_property(poolconf, pe, "pool.scheduler", pv) !=
4600             POC_STRING) {
4601                 (void) pool_conf_close(poolconf);
4602                 pool_conf_free(poolconf);
4603                 return (Z_NO_ENTRY);
4604         }
4605         (void) pool_value_get_string(pv, &sched_str);
4606         (void) pool_conf_close(poolconf);
4607         pool_conf_free(poolconf);
4608         if (strlcpy(class, sched_str, clsize) >= clsize)
4609                 return (Z_TOO_BIG);
4610         return (Z_OK);
4611 }
4612 
4613 /*
4614  * Get the default scheduling class for the zone.  This will either be the
4615  * class set on the zone's pool or the system default scheduling class.
4616  */
4617 int
4618 zonecfg_get_dflt_sched_class(zone_dochandle_t handle, char *class, int clsize)
4619 {
4620         char poolname[MAXPATHLEN];
4621 
4622         if (zonecfg_get_pool(handle, poolname, sizeof (poolname)) == Z_OK) {
4623                 /* check if the zone's pool specified a sched class */
4624                 if (get_pool_sched_class(poolname, class, clsize) == Z_OK)
4625                         return (Z_OK);
4626         }
4627 
4628         if (priocntl(0, 0, PC_GETDFLCL, class, (uint64_t)clsize) == -1)
4629                 return (Z_TOO_BIG);
4630 
4631         return (Z_OK);
4632 }
4633 
4634 int
4635 zonecfg_setfsent(zone_dochandle_t handle)
4636 {
4637         return (zonecfg_setent(handle));
4638 }
4639 
4640 int
4641 zonecfg_getfsent(zone_dochandle_t handle, struct zone_fstab *tabptr)
4642 {
4643         xmlNodePtr cur, options;
4644         char options_str[MAX_MNTOPT_STR];
4645         int err;
4646 
4647         if (handle == NULL)
4648                 return (Z_INVAL);
4649 
4650         if ((cur = handle->zone_dh_cur) == NULL)
4651                 return (Z_NO_ENTRY);
4652 
4653         for (; cur != NULL; cur = cur->next)
4654                 if (!xmlStrcmp(cur->name, DTD_ELEM_FS))
4655                         break;
4656         if (cur == NULL) {
4657                 handle->zone_dh_cur = handle->zone_dh_top;
4658                 return (Z_NO_ENTRY);
4659         }
4660 
4661         if ((err = fetchprop(cur, DTD_ATTR_SPECIAL, tabptr->zone_fs_special,
4662             sizeof (tabptr->zone_fs_special))) != Z_OK) {
4663                 handle->zone_dh_cur = handle->zone_dh_top;
4664                 return (err);
4665         }
4666 
4667         if ((err = fetchprop(cur, DTD_ATTR_RAW, tabptr->zone_fs_raw,
4668             sizeof (tabptr->zone_fs_raw))) != Z_OK) {
4669                 handle->zone_dh_cur = handle->zone_dh_top;
4670                 return (err);
4671         }
4672 
4673         if ((err = fetchprop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir,
4674             sizeof (tabptr->zone_fs_dir))) != Z_OK) {
4675                 handle->zone_dh_cur = handle->zone_dh_top;
4676                 return (err);
4677         }
4678 
4679         if ((err = fetchprop(cur, DTD_ATTR_TYPE, tabptr->zone_fs_type,
4680             sizeof (tabptr->zone_fs_type))) != Z_OK) {
4681                 handle->zone_dh_cur = handle->zone_dh_top;
4682                 return (err);
4683         }
4684 
4685         /* OK for options to be NULL */
4686         tabptr->zone_fs_options = NULL;
4687         for (options = cur->xmlChildrenNode; options != NULL;
4688             options = options->next) {
4689                 if (fetchprop(options, DTD_ATTR_NAME, options_str,
4690                     sizeof (options_str)) != Z_OK)
4691                         break;
4692                 if (zonecfg_add_fs_option(tabptr, options_str) != Z_OK)
4693                         break;
4694         }
4695 
4696         handle->zone_dh_cur = cur->next;
4697         return (Z_OK);
4698 }
4699 
4700 int
4701 zonecfg_endfsent(zone_dochandle_t handle)
4702 {
4703         return (zonecfg_endent(handle));
4704 }
4705 
4706 int
4707 zonecfg_setnwifent(zone_dochandle_t handle)
4708 {
4709         return (zonecfg_setent(handle));
4710 }
4711 
4712 int
4713 zonecfg_getnwifent(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
4714 {
4715         xmlNodePtr cur;
4716         int err;
4717 
4718         if (handle == NULL)
4719                 return (Z_INVAL);
4720 
4721         if ((cur = handle->zone_dh_cur) == NULL)
4722                 return (Z_NO_ENTRY);
4723 
4724         for (; cur != NULL; cur = cur->next)
4725                 if (!xmlStrcmp(cur->name, DTD_ELEM_NET))
4726                         break;
4727         if (cur == NULL) {
4728                 handle->zone_dh_cur = handle->zone_dh_top;
4729                 return (Z_NO_ENTRY);
4730         }
4731 
4732         if ((err = fetchprop(cur, DTD_ATTR_ADDRESS, tabptr->zone_nwif_address,
4733             sizeof (tabptr->zone_nwif_address))) != Z_OK) {
4734                 handle->zone_dh_cur = handle->zone_dh_top;
4735                 return (err);
4736         }
4737 
4738         if ((err = fetchprop(cur, DTD_ATTR_ALLOWED_ADDRESS,
4739             tabptr->zone_nwif_allowed_address,
4740             sizeof (tabptr->zone_nwif_allowed_address))) != Z_OK) {
4741                 handle->zone_dh_cur = handle->zone_dh_top;
4742                 return (err);
4743         }
4744 
4745         if ((err = fetchprop(cur, DTD_ATTR_PHYSICAL, tabptr->zone_nwif_physical,
4746             sizeof (tabptr->zone_nwif_physical))) != Z_OK) {
4747                 handle->zone_dh_cur = handle->zone_dh_top;
4748                 return (err);
4749         }
4750 
4751         if ((err = fetchprop(cur, DTD_ATTR_DEFROUTER,
4752             tabptr->zone_nwif_defrouter,
4753             sizeof (tabptr->zone_nwif_defrouter))) != Z_OK) {
4754                 handle->zone_dh_cur = handle->zone_dh_top;
4755                 return (err);
4756         }
4757 
4758         handle->zone_dh_cur = cur->next;
4759         return (Z_OK);
4760 }
4761 
4762 int
4763 zonecfg_endnwifent(zone_dochandle_t handle)
4764 {
4765         return (zonecfg_endent(handle));
4766 }
4767 
4768 int
4769 zonecfg_setdevent(zone_dochandle_t handle)
4770 {
4771         return (zonecfg_setent(handle));
4772 }
4773 
4774 int
4775 zonecfg_getdevent(zone_dochandle_t handle, struct zone_devtab *tabptr)
4776 {
4777         xmlNodePtr cur;
4778         int err;
4779 
4780         if (handle == NULL)
4781                 return (Z_INVAL);
4782 
4783         if ((cur = handle->zone_dh_cur) == NULL)
4784                 return (Z_NO_ENTRY);
4785 
4786         for (; cur != NULL; cur = cur->next)
4787                 if (!xmlStrcmp(cur->name, DTD_ELEM_DEVICE))
4788                         break;
4789         if (cur == NULL) {
4790                 handle->zone_dh_cur = handle->zone_dh_top;
4791                 return (Z_NO_ENTRY);
4792         }
4793 
4794         if ((err = fetchprop(cur, DTD_ATTR_MATCH, tabptr->zone_dev_match,
4795             sizeof (tabptr->zone_dev_match))) != Z_OK) {
4796                 handle->zone_dh_cur = handle->zone_dh_top;
4797                 return (err);
4798         }
4799 
4800         handle->zone_dh_cur = cur->next;
4801         return (Z_OK);
4802 }
4803 
4804 int
4805 zonecfg_enddevent(zone_dochandle_t handle)
4806 {
4807         return (zonecfg_endent(handle));
4808 }
4809 
4810 int
4811 zonecfg_setrctlent(zone_dochandle_t handle)
4812 {
4813         return (zonecfg_setent(handle));
4814 }
4815 
4816 int
4817 zonecfg_getrctlent(zone_dochandle_t handle, struct zone_rctltab *tabptr)
4818 {
4819         xmlNodePtr cur, val;
4820         struct zone_rctlvaltab *valptr;
4821         int err;
4822 
4823         if (handle == NULL)
4824                 return (Z_INVAL);
4825 
4826         if ((cur = handle->zone_dh_cur) == NULL)
4827                 return (Z_NO_ENTRY);
4828 
4829         for (; cur != NULL; cur = cur->next)
4830                 if (!xmlStrcmp(cur->name, DTD_ELEM_RCTL))
4831                         break;
4832         if (cur == NULL) {
4833                 handle->zone_dh_cur = handle->zone_dh_top;
4834                 return (Z_NO_ENTRY);
4835         }
4836 
4837         if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_rctl_name,
4838             sizeof (tabptr->zone_rctl_name))) != Z_OK) {
4839                 handle->zone_dh_cur = handle->zone_dh_top;
4840                 return (err);
4841         }
4842 
4843         tabptr->zone_rctl_valptr = NULL;
4844         for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
4845                 valptr = (struct zone_rctlvaltab *)malloc(
4846                     sizeof (struct zone_rctlvaltab));
4847                 if (valptr == NULL)
4848                         return (Z_NOMEM);
4849                 if (fetchprop(val, DTD_ATTR_PRIV, valptr->zone_rctlval_priv,
4850                     sizeof (valptr->zone_rctlval_priv)) != Z_OK)
4851                         break;
4852                 if (fetchprop(val, DTD_ATTR_LIMIT, valptr->zone_rctlval_limit,
4853                     sizeof (valptr->zone_rctlval_limit)) != Z_OK)
4854                         break;
4855                 if (fetchprop(val, DTD_ATTR_ACTION, valptr->zone_rctlval_action,
4856                     sizeof (valptr->zone_rctlval_action)) != Z_OK)
4857                         break;
4858                 if (zonecfg_add_rctl_value(tabptr, valptr) != Z_OK)
4859                         break;
4860         }
4861 
4862         handle->zone_dh_cur = cur->next;
4863         return (Z_OK);
4864 }
4865 
4866 int
4867 zonecfg_endrctlent(zone_dochandle_t handle)
4868 {
4869         return (zonecfg_endent(handle));
4870 }
4871 
4872 int
4873 zonecfg_setattrent(zone_dochandle_t handle)
4874 {
4875         return (zonecfg_setent(handle));
4876 }
4877 
4878 int
4879 zonecfg_getattrent(zone_dochandle_t handle, struct zone_attrtab *tabptr)
4880 {
4881         xmlNodePtr cur;
4882         int err;
4883 
4884         if (handle == NULL)
4885                 return (Z_INVAL);
4886 
4887         if ((cur = handle->zone_dh_cur) == NULL)
4888                 return (Z_NO_ENTRY);
4889 
4890         for (; cur != NULL; cur = cur->next)
4891                 if (!xmlStrcmp(cur->name, DTD_ELEM_ATTR))
4892                         break;
4893         if (cur == NULL) {
4894                 handle->zone_dh_cur = handle->zone_dh_top;
4895                 return (Z_NO_ENTRY);
4896         }
4897 
4898         if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_attr_name,
4899             sizeof (tabptr->zone_attr_name))) != Z_OK) {
4900                 handle->zone_dh_cur = handle->zone_dh_top;
4901                 return (err);
4902         }
4903 
4904         if ((err = fetchprop(cur, DTD_ATTR_TYPE, tabptr->zone_attr_type,
4905             sizeof (tabptr->zone_attr_type))) != Z_OK) {
4906                 handle->zone_dh_cur = handle->zone_dh_top;
4907                 return (err);
4908         }
4909 
4910         if ((err = fetchprop(cur, DTD_ATTR_VALUE, tabptr->zone_attr_value,
4911             sizeof (tabptr->zone_attr_value))) != Z_OK) {
4912                 handle->zone_dh_cur = handle->zone_dh_top;
4913                 return (err);
4914         }
4915 
4916         handle->zone_dh_cur = cur->next;
4917         return (Z_OK);
4918 }
4919 
4920 int
4921 zonecfg_endattrent(zone_dochandle_t handle)
4922 {
4923         return (zonecfg_endent(handle));
4924 }
4925 
4926 int
4927 zonecfg_setadminent(zone_dochandle_t handle)
4928 {
4929         return (zonecfg_setent(handle));
4930 }
4931 
4932 int
4933 zonecfg_getadminent(zone_dochandle_t handle, struct zone_admintab *tabptr)
4934 {
4935         xmlNodePtr cur;
4936         int err;
4937 
4938         if (handle == NULL)
4939                 return (Z_INVAL);
4940 
4941         if ((cur = handle->zone_dh_cur) == NULL)
4942                 return (Z_NO_ENTRY);
4943 
4944         for (; cur != NULL; cur = cur->next)
4945                 if (!xmlStrcmp(cur->name, DTD_ELEM_ADMIN))
4946                         break;
4947         if (cur == NULL) {
4948                 handle->zone_dh_cur = handle->zone_dh_top;
4949                 return (Z_NO_ENTRY);
4950         }
4951 
4952         if ((err = fetchprop(cur, DTD_ATTR_USER, tabptr->zone_admin_user,
4953             sizeof (tabptr->zone_admin_user))) != Z_OK) {
4954                 handle->zone_dh_cur = handle->zone_dh_top;
4955                 return (err);
4956         }
4957 
4958 
4959         if ((err = fetchprop(cur, DTD_ATTR_AUTHS, tabptr->zone_admin_auths,
4960             sizeof (tabptr->zone_admin_auths))) != Z_OK) {
4961                 handle->zone_dh_cur = handle->zone_dh_top;
4962                 return (err);
4963         }
4964 
4965         handle->zone_dh_cur = cur->next;
4966         return (Z_OK);
4967 }
4968 
4969 int
4970 zonecfg_endadminent(zone_dochandle_t handle)
4971 {
4972         return (zonecfg_endent(handle));
4973 }
4974 
4975 /*
4976  * The privileges available on the system and described in privileges(5)
4977  * fall into four categories with respect to non-global zones:
4978  *
4979  *      Default set of privileges considered safe for all non-global
4980  *      zones.  These privileges are "safe" in the sense that a
4981  *      privileged process in the zone cannot affect processes in any
4982  *      other zone on the system.
4983  *
4984  *      Set of privileges not currently permitted within a non-global
4985  *      zone.  These privileges are considered by default, "unsafe,"
4986  *      and include ones which affect global resources (such as the
4987  *      system clock or physical memory) or are overly broad and cover
4988  *      more than one mechanism in the system.  In other cases, there
4989  *      has not been sufficient virtualization in the parts of the
4990  *      system the privilege covers to allow its use within a
4991  *      non-global zone.
4992  *
4993  *      Set of privileges required in order to get a zone booted and
4994  *      init(1M) started.  These cannot be removed from the zone's
4995  *      privilege set.
4996  *
4997  * All other privileges are optional and are potentially useful for
4998  * processes executing inside a non-global zone.
4999  *
5000  * When privileges are added to the system, a determination needs to be
5001  * made as to which category the privilege belongs to.  Ideally,
5002  * privileges should be fine-grained enough and the mechanisms they cover
5003  * virtualized enough so that they can be made available to non-global
5004  * zones.
5005  */
5006 
5007 /*
5008  * Define some of the tokens that priv_str_to_set(3C) recognizes.  Since
5009  * the privilege string separator can be any character, although it is
5010  * usually a comma character, define these here as well in the event that
5011  * they change or are augmented in the future.
5012  */
5013 #define BASIC_TOKEN             "basic"
5014 #define DEFAULT_TOKEN           "default"
5015 #define ZONE_TOKEN              "zone"
5016 #define TOKEN_PRIV_CHAR         ','
5017 #define TOKEN_PRIV_STR          ","
5018 
5019 typedef struct priv_node {
5020         struct priv_node        *pn_next;       /* Next privilege */
5021         char                    *pn_priv;       /* Privileges name */
5022 } priv_node_t;
5023 
5024 /* Privileges lists can differ across brands */
5025 typedef struct priv_lists {
5026         /* Privileges considered safe for all non-global zones of a brand */
5027         struct priv_node        *pl_default;
5028 
5029         /* Privileges not permitted for all non-global zones of a brand */
5030         struct priv_node        *pl_prohibited;
5031 
5032         /* Privileges required for all non-global zones of a brand */
5033         struct priv_node        *pl_required;
5034 
5035         /*
5036          * ip-type of the zone these privileges lists apply to.
5037          * It is used to pass ip-type to the callback function,
5038          * priv_lists_cb, which has no way of getting the ip-type.
5039          */
5040         const char              *pl_iptype;
5041 } priv_lists_t;
5042 
5043 static int
5044 priv_lists_cb(void *data, priv_iter_t *priv_iter)
5045 {
5046         priv_lists_t *plp = (priv_lists_t *)data;
5047         priv_node_t *pnp;
5048 
5049         /* Skip this privilege if ip-type does not match */
5050         if ((strcmp(priv_iter->pi_iptype, "all") != 0) &&
5051             (strcmp(priv_iter->pi_iptype, plp->pl_iptype) != 0))
5052                 return (0);
5053 
5054         /* Allocate a new priv list node. */
5055         if ((pnp = malloc(sizeof (*pnp))) == NULL)
5056                 return (-1);
5057         if ((pnp->pn_priv = strdup(priv_iter->pi_name)) == NULL) {
5058                 free(pnp);
5059                 return (-1);
5060         }
5061 
5062         /* Insert the new priv list node into the right list */
5063         if (strcmp(priv_iter->pi_set, "default") == 0) {
5064                 pnp->pn_next = plp->pl_default;
5065                 plp->pl_default = pnp;
5066         } else if (strcmp(priv_iter->pi_set, "prohibited") == 0) {
5067                 pnp->pn_next = plp->pl_prohibited;
5068                 plp->pl_prohibited = pnp;
5069         } else if (strcmp(priv_iter->pi_set, "required") == 0) {
5070                 pnp->pn_next = plp->pl_required;
5071                 plp->pl_required = pnp;
5072         } else {
5073                 free(pnp->pn_priv);
5074                 free(pnp);
5075                 return (-1);
5076         }
5077         return (0);
5078 }
5079 
5080 static void
5081 priv_lists_destroy(priv_lists_t *plp)
5082 {
5083         priv_node_t *pnp;
5084 
5085         assert(plp != NULL);
5086 
5087         while ((pnp = plp->pl_default) != NULL) {
5088                 plp->pl_default = pnp->pn_next;
5089                 free(pnp->pn_priv);
5090                 free(pnp);
5091         }
5092         while ((pnp = plp->pl_prohibited) != NULL) {
5093                 plp->pl_prohibited = pnp->pn_next;
5094                 free(pnp->pn_priv);
5095                 free(pnp);
5096         }
5097         while ((pnp = plp->pl_required) != NULL) {
5098                 plp->pl_required = pnp->pn_next;
5099                 free(pnp->pn_priv);
5100                 free(pnp);
5101         }
5102         free(plp);
5103 }
5104 
5105 static int
5106 priv_lists_create(zone_dochandle_t handle, char *brand, priv_lists_t **plpp,
5107     const char *curr_iptype)
5108 {
5109         priv_lists_t *plp;
5110         brand_handle_t bh;
5111         char brand_str[MAXNAMELEN];
5112 
5113         /* handle or brand must be set, but never both */
5114         assert((handle != NULL) || (brand != NULL));
5115         assert((handle == NULL) || (brand == NULL));
5116 
5117         if (handle != NULL) {
5118                 brand = brand_str;
5119                 if (zonecfg_get_brand(handle, brand, sizeof (brand_str)) != 0)
5120                         return (Z_BRAND_ERROR);
5121         }
5122 
5123         if ((bh = brand_open(brand)) == NULL)
5124                 return (Z_BRAND_ERROR);
5125 
5126         if ((plp = calloc(1, sizeof (priv_lists_t))) == NULL) {
5127                 brand_close(bh);
5128                 return (Z_NOMEM);
5129         }
5130 
5131         plp->pl_iptype = curr_iptype;
5132 
5133         /* construct the privilege lists */
5134         if (brand_config_iter_privilege(bh, priv_lists_cb, plp) != 0) {
5135                 priv_lists_destroy(plp);
5136                 brand_close(bh);
5137                 return (Z_BRAND_ERROR);
5138         }
5139 
5140         brand_close(bh);
5141         *plpp = plp;
5142         return (Z_OK);
5143 }
5144 
5145 static int
5146 get_default_privset(priv_set_t *privs, priv_lists_t *plp)
5147 {
5148         priv_node_t *pnp;
5149         priv_set_t *basic;
5150 
5151         basic = priv_str_to_set(BASIC_TOKEN, TOKEN_PRIV_STR, NULL);
5152         if (basic == NULL)
5153                 return (errno == ENOMEM ? Z_NOMEM : Z_INVAL);
5154 
5155         priv_union(basic, privs);
5156         priv_freeset(basic);
5157 
5158         for (pnp = plp->pl_default; pnp != NULL; pnp = pnp->pn_next) {
5159                 if (priv_addset(privs, pnp->pn_priv) != 0)
5160                         return (Z_INVAL);
5161         }
5162 
5163         return (Z_OK);
5164 }
5165 
5166 int
5167 zonecfg_default_brand(char *brand, size_t brandsize)
5168 {
5169         zone_dochandle_t handle;
5170         int myzoneid = getzoneid();
5171         int ret;
5172 
5173         /*
5174          * If we're running within a zone, then the default brand is the
5175          * current zone's brand.
5176          */
5177         if (myzoneid != GLOBAL_ZONEID) {
5178                 ret = zone_getattr(myzoneid, ZONE_ATTR_BRAND, brand, brandsize);
5179                 if (ret < 0)
5180                         return ((errno == EFAULT) ? Z_TOO_BIG : Z_INVAL);
5181                 return (Z_OK);
5182         }
5183 
5184         if ((handle = zonecfg_init_handle()) == NULL)
5185                 return (Z_NOMEM);
5186         if ((ret = zonecfg_get_handle("SUNWdefault", handle)) == Z_OK) {
5187                 ret = i_zonecfg_get_brand(handle, brand, brandsize, B_TRUE);
5188                 zonecfg_fini_handle(handle);
5189                 return (ret);
5190         }
5191         return (ret);
5192 }
5193 
5194 int
5195 zonecfg_default_privset(priv_set_t *privs, const char *curr_iptype)
5196 {
5197         priv_lists_t *plp;
5198         char buf[MAXNAMELEN];
5199         int ret;
5200 
5201         if ((ret = zonecfg_default_brand(buf, sizeof (buf))) != Z_OK)
5202                 return (ret);
5203         if ((ret = priv_lists_create(NULL, buf, &plp, curr_iptype)) != Z_OK)
5204                 return (ret);
5205         ret = get_default_privset(privs, plp);
5206         priv_lists_destroy(plp);
5207         return (ret);
5208 }
5209 
5210 void
5211 append_priv_token(char *priv, char *str, size_t strlen)
5212 {
5213         if (*str != '\0')
5214                 (void) strlcat(str, TOKEN_PRIV_STR, strlen);
5215         (void) strlcat(str, priv, strlen);
5216 }
5217 
5218 /*
5219  * Verify that the supplied string is a valid privilege limit set for a
5220  * non-global zone.  This string must not only be acceptable to
5221  * priv_str_to_set(3C) which parses it, but it also must resolve to a
5222  * privilege set that includes certain required privileges and lacks
5223  * certain prohibited privileges.
5224  */
5225 static int
5226 verify_privset(char *privbuf, priv_set_t *privs, char **privname,
5227     boolean_t add_default, priv_lists_t *plp)
5228 {
5229         priv_node_t *pnp;
5230         char *tmp, *cp, *lasts;
5231         size_t len;
5232         priv_set_t *mergeset;
5233         const char *token;
5234 
5235         /*
5236          * The verification of the privilege string occurs in several
5237          * phases.  In the first phase, the supplied string is scanned for
5238          * the ZONE_TOKEN token which is not support as part of the
5239          * "limitpriv" property.
5240          *
5241          * Duplicate the supplied privilege string since strtok_r(3C)
5242          * tokenizes its input by null-terminating the tokens.
5243          */
5244         if ((tmp = strdup(privbuf)) == NULL)
5245                 return (Z_NOMEM);
5246         for (cp = strtok_r(tmp, TOKEN_PRIV_STR, &lasts); cp != NULL;
5247             cp = strtok_r(NULL, TOKEN_PRIV_STR, &lasts)) {
5248                 if (strcmp(cp, ZONE_TOKEN) == 0) {
5249                         free(tmp);
5250                         if ((*privname = strdup(ZONE_TOKEN)) == NULL)
5251                                 return (Z_NOMEM);
5252                         else
5253                                 return (Z_PRIV_UNKNOWN);
5254                 }
5255         }
5256         free(tmp);
5257 
5258         if (add_default) {
5259                 /*
5260                  * If DEFAULT_TOKEN was specified, a string needs to be
5261                  * built containing the privileges from the default, safe
5262                  * set along with those of the "limitpriv" property.
5263                  */
5264                 len = strlen(privbuf) + sizeof (BASIC_TOKEN) + 2;
5265 
5266                 for (pnp = plp->pl_default; pnp != NULL; pnp = pnp->pn_next)
5267                         len += strlen(pnp->pn_priv) + 1;
5268                 tmp = alloca(len);
5269                 *tmp = '\0';
5270 
5271                 append_priv_token(BASIC_TOKEN, tmp, len);
5272                 for (pnp = plp->pl_default; pnp != NULL; pnp = pnp->pn_next)
5273                         append_priv_token(pnp->pn_priv, tmp, len);
5274                 (void) strlcat(tmp, TOKEN_PRIV_STR, len);
5275                 (void) strlcat(tmp, privbuf, len);
5276         } else {
5277                 tmp = privbuf;
5278         }
5279 
5280 
5281         /*
5282          * In the next phase, attempt to convert the merged privilege
5283          * string into a privilege set.  In the case of an error, either
5284          * there was a memory allocation failure or there was an invalid
5285          * privilege token in the string.  In either case, return an
5286          * appropriate error code but in the event of an invalid token,
5287          * allocate a string containing its name and return that back to
5288          * the caller.
5289          */
5290         mergeset = priv_str_to_set(tmp, TOKEN_PRIV_STR, &token);
5291         if (mergeset == NULL) {
5292                 if (token == NULL)
5293                         return (Z_NOMEM);
5294                 if ((cp = strchr(token, TOKEN_PRIV_CHAR)) != NULL)
5295                         *cp = '\0';
5296                 if ((*privname = strdup(token)) == NULL)
5297                         return (Z_NOMEM);
5298                 else
5299                         return (Z_PRIV_UNKNOWN);
5300         }
5301 
5302         /*
5303          * Next, verify that none of the prohibited zone privileges are
5304          * present in the merged privilege set.
5305          */
5306         for (pnp = plp->pl_prohibited; pnp != NULL; pnp = pnp->pn_next) {
5307                 if (priv_ismember(mergeset, pnp->pn_priv)) {
5308                         priv_freeset(mergeset);
5309                         if ((*privname = strdup(pnp->pn_priv)) == NULL)
5310                                 return (Z_NOMEM);
5311                         else
5312                                 return (Z_PRIV_PROHIBITED);
5313                 }
5314         }
5315 
5316         /*
5317          * Finally, verify that all of the required zone privileges are
5318          * present in the merged privilege set.
5319          */
5320         for (pnp = plp->pl_required; pnp != NULL; pnp = pnp->pn_next) {
5321                 if (!priv_ismember(mergeset, pnp->pn_priv)) {
5322                         priv_freeset(mergeset);
5323                         if ((*privname = strdup(pnp->pn_priv)) == NULL)
5324                                 return (Z_NOMEM);
5325                         else
5326                                 return (Z_PRIV_REQUIRED);
5327                 }
5328         }
5329 
5330         priv_copyset(mergeset, privs);
5331         priv_freeset(mergeset);
5332         return (Z_OK);
5333 }
5334 
5335 /*
5336  * Fill in the supplied privilege set with either the default, safe set of
5337  * privileges suitable for a non-global zone, or one based on the
5338  * "limitpriv" property in the zone's configuration.
5339  *
5340  * In the event of an invalid privilege specification in the
5341  * configuration, a string is allocated and returned containing the
5342  * "privilege" causing the issue.  It is the caller's responsibility to
5343  * free this memory when it is done with it.
5344  */
5345 int
5346 zonecfg_get_privset(zone_dochandle_t handle, priv_set_t *privs,
5347     char **privname)
5348 {
5349         priv_lists_t *plp;
5350         char *cp, *limitpriv = NULL;
5351         int err, limitlen;
5352         zone_iptype_t iptype;
5353         const char *curr_iptype;
5354 
5355         /*
5356          * Attempt to lookup the "limitpriv" property.  If it does not
5357          * exist or matches the string DEFAULT_TOKEN exactly, then the
5358          * default, safe privilege set is returned.
5359          */
5360         if ((err = zonecfg_get_limitpriv(handle, &limitpriv)) != Z_OK)
5361                 return (err);
5362 
5363         if ((err = zonecfg_get_iptype(handle, &iptype)) != Z_OK)
5364                 return (err);
5365 
5366         switch (iptype) {
5367         case ZS_SHARED:
5368                 curr_iptype = "shared";
5369                 break;
5370         case ZS_EXCLUSIVE:
5371                 curr_iptype = "exclusive";
5372                 break;
5373         }
5374 
5375         if ((err = priv_lists_create(handle, NULL, &plp, curr_iptype)) != Z_OK)
5376                 return (err);
5377 
5378         limitlen = strlen(limitpriv);
5379         if (limitlen == 0 || strcmp(limitpriv, DEFAULT_TOKEN) == 0) {
5380                 free(limitpriv);
5381                 err = get_default_privset(privs, plp);
5382                 priv_lists_destroy(plp);
5383                 return (err);
5384         }
5385 
5386         /*
5387          * Check if the string DEFAULT_TOKEN is the first token in a list
5388          * of privileges.
5389          */
5390         cp = strchr(limitpriv, TOKEN_PRIV_CHAR);
5391         if (cp != NULL &&
5392             strncmp(limitpriv, DEFAULT_TOKEN, cp - limitpriv) == 0)
5393                 err = verify_privset(cp + 1, privs, privname, B_TRUE, plp);
5394         else
5395                 err = verify_privset(limitpriv, privs, privname, B_FALSE, plp);
5396 
5397         free(limitpriv);
5398         priv_lists_destroy(plp);
5399         return (err);
5400 }
5401 
5402 int
5403 zone_get_zonepath(char *zone_name, char *zonepath, size_t rp_sz)
5404 {
5405         zone_dochandle_t handle;
5406         boolean_t found = B_FALSE;
5407         struct zoneent *ze;
5408         FILE *cookie;
5409         int err;
5410         char *cp;
5411 
5412         if (zone_name == NULL)
5413                 return (Z_INVAL);
5414 
5415         (void) strlcpy(zonepath, zonecfg_root, rp_sz);
5416         cp = zonepath + strlen(zonepath);
5417         while (cp > zonepath && cp[-1] == '/')
5418                 *--cp = '\0';
5419 
5420         if (strcmp(zone_name, GLOBAL_ZONENAME) == 0) {
5421                 if (zonepath[0] == '\0')
5422                         (void) strlcpy(zonepath, "/", rp_sz);
5423                 return (Z_OK);
5424         }
5425 
5426         /*
5427          * First check the index file.  Because older versions did not have
5428          * a copy of the zone path, allow for it to be zero length, in which
5429          * case we ignore this result and fall back to the XML files.
5430          */
5431         cookie = setzoneent();
5432         while ((ze = getzoneent_private(cookie)) != NULL) {
5433                 if (strcmp(ze->zone_name, zone_name) == 0) {
5434                         found = B_TRUE;
5435                         if (ze->zone_path[0] != '\0')
5436                                 (void) strlcpy(cp, ze->zone_path,
5437                                     rp_sz - (cp - zonepath));
5438                 }
5439                 free(ze);
5440                 if (found)
5441                         break;
5442         }
5443         endzoneent(cookie);
5444         if (found && *cp != '\0')
5445                 return (Z_OK);
5446 
5447         /* Fall back to the XML files. */
5448         if ((handle = zonecfg_init_handle()) == NULL)
5449                 return (Z_NOMEM);
5450 
5451         /*
5452          * Check the snapshot first: if a zone is running, its zonepath
5453          * may have changed.
5454          */
5455         if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) {
5456                 if ((err = zonecfg_get_handle(zone_name, handle)) != Z_OK) {
5457                         zonecfg_fini_handle(handle);
5458                         return (err);
5459                 }
5460         }
5461         err = zonecfg_get_zonepath(handle, zonepath, rp_sz);
5462         zonecfg_fini_handle(handle);
5463         return (err);
5464 }
5465 
5466 int
5467 zone_get_rootpath(char *zone_name, char *rootpath, size_t rp_sz)
5468 {
5469         int err;
5470 
5471         /* This function makes sense for non-global zones only. */
5472         if (strcmp(zone_name, GLOBAL_ZONENAME) == 0)
5473                 return (Z_BOGUS_ZONE_NAME);
5474         if ((err = zone_get_zonepath(zone_name, rootpath, rp_sz)) != Z_OK)
5475                 return (err);
5476         if (strlcat(rootpath, "/root", rp_sz) >= rp_sz)
5477                 return (Z_TOO_BIG);
5478         return (Z_OK);
5479 }
5480 
5481 int
5482 zone_get_brand(char *zone_name, char *brandname, size_t rp_sz)
5483 {
5484         int err;
5485         zone_dochandle_t handle;
5486         char myzone[MAXNAMELEN];
5487         int myzoneid = getzoneid();
5488 
5489         /*
5490          * If we are not in the global zone, then we don't have the zone
5491          * .xml files with the brand name available.  Thus, we are going to
5492          * have to ask the kernel for the information.
5493          */
5494         if (myzoneid != GLOBAL_ZONEID) {
5495                 if (is_system_labeled()) {
5496                         (void) strlcpy(brandname, NATIVE_BRAND_NAME, rp_sz);
5497                         return (Z_OK);
5498                 }
5499                 if (zone_getattr(myzoneid, ZONE_ATTR_NAME, myzone,
5500                     sizeof (myzone)) < 0)
5501                         return (Z_NO_ZONE);
5502                 if (!zonecfg_is_scratch(myzone)) {
5503                         if (strncmp(zone_name, myzone, MAXNAMELEN) != 0)
5504                                 return (Z_NO_ZONE);
5505                 }
5506                 err = zone_getattr(myzoneid, ZONE_ATTR_BRAND, brandname, rp_sz);
5507                 if (err < 0)
5508                         return ((errno == EFAULT) ? Z_TOO_BIG : Z_INVAL);
5509 
5510                 return (Z_OK);
5511         }
5512 
5513         if (strcmp(zone_name, "global") == 0)
5514                 return (zonecfg_default_brand(brandname, rp_sz));
5515 
5516         if ((handle = zonecfg_init_handle()) == NULL)
5517                 return (Z_NOMEM);
5518 
5519         err = zonecfg_get_handle((char *)zone_name, handle);
5520         if (err == Z_OK)
5521                 err = zonecfg_get_brand(handle, brandname, rp_sz);
5522 
5523         zonecfg_fini_handle(handle);
5524         return (err);
5525 }
5526 
5527 /*
5528  * Return the appropriate root for the active /dev.
5529  * For normal zone, the path is $ZONEPATH/root;
5530  * for scratch zone, the dev path is $ZONEPATH/lu.
5531  */
5532 int
5533 zone_get_devroot(char *zone_name, char *devroot, size_t rp_sz)
5534 {
5535         int err;
5536         char *suffix;
5537         zone_state_t state;
5538 
5539         /* This function makes sense for non-global zones only. */
5540         if (strcmp(zone_name, GLOBAL_ZONENAME) == 0)
5541                 return (Z_BOGUS_ZONE_NAME);
5542         if ((err = zone_get_zonepath(zone_name, devroot, rp_sz)) != Z_OK)
5543                 return (err);
5544 
5545         if (zone_get_state(zone_name, &state) == Z_OK &&
5546             state == ZONE_STATE_MOUNTED)
5547                 suffix = "/lu";
5548         else
5549                 suffix = "/root";
5550         if (strlcat(devroot, suffix, rp_sz) >= rp_sz)
5551                 return (Z_TOO_BIG);
5552         return (Z_OK);
5553 }
5554 
5555 static zone_state_t
5556 kernel_state_to_user_state(zoneid_t zoneid, zone_status_t kernel_state)
5557 {
5558         char zoneroot[MAXPATHLEN];
5559         size_t zlen;
5560 
5561         assert(kernel_state <= ZONE_MAX_STATE);
5562         switch (kernel_state) {
5563                 case ZONE_IS_UNINITIALIZED:
5564                 case ZONE_IS_INITIALIZED:
5565                         /* The kernel will not return these two states */
5566                         return (ZONE_STATE_READY);
5567                 case ZONE_IS_READY:
5568                         /*
5569                          * If the zone's root is mounted on $ZONEPATH/lu, then
5570                          * it's a mounted scratch zone.
5571                          */
5572                         if (zone_getattr(zoneid, ZONE_ATTR_ROOT, zoneroot,
5573                             sizeof (zoneroot)) >= 0) {
5574                                 zlen = strlen(zoneroot);
5575                                 if (zlen > 3 &&
5576                                     strcmp(zoneroot + zlen - 3, "/lu") == 0)
5577                                         return (ZONE_STATE_MOUNTED);
5578                         }
5579                         return (ZONE_STATE_READY);
5580                 case ZONE_IS_BOOTING:
5581                 case ZONE_IS_RUNNING:
5582                         return (ZONE_STATE_RUNNING);
5583                 case ZONE_IS_SHUTTING_DOWN:
5584                 case ZONE_IS_EMPTY:
5585                         return (ZONE_STATE_SHUTTING_DOWN);
5586                 case ZONE_IS_DOWN:
5587                 case ZONE_IS_DYING:
5588                 case ZONE_IS_DEAD:
5589                 default:
5590                         return (ZONE_STATE_DOWN);
5591         }
5592         /* NOTREACHED */
5593 }
5594 
5595 int
5596 zone_get_state(char *zone_name, zone_state_t *state_num)
5597 {
5598         zone_status_t status;
5599         zoneid_t zone_id;
5600         struct zoneent *ze;
5601         boolean_t found = B_FALSE;
5602         FILE *cookie;
5603         char kernzone[ZONENAME_MAX];
5604         FILE *fp;
5605 
5606         if (zone_name == NULL)
5607                 return (Z_INVAL);
5608 
5609         /*
5610          * If we're looking at an alternate root, then we need to query the
5611          * kernel using the scratch zone name.
5612          */
5613         zone_id = -1;
5614         if (*zonecfg_root != '\0' && !zonecfg_is_scratch(zone_name)) {
5615                 if ((fp = zonecfg_open_scratch("", B_FALSE)) != NULL) {
5616                         if (zonecfg_find_scratch(fp, zone_name, zonecfg_root,
5617                             kernzone, sizeof (kernzone)) == 0)
5618                                 zone_id = getzoneidbyname(kernzone);
5619                         zonecfg_close_scratch(fp);
5620                 }
5621         } else {
5622                 zone_id = getzoneidbyname(zone_name);
5623         }
5624 
5625         /* check to see if zone is running */
5626         if (zone_id != -1 &&
5627             zone_getattr(zone_id, ZONE_ATTR_STATUS, &status,
5628             sizeof (status)) >= 0) {
5629                 *state_num = kernel_state_to_user_state(zone_id, status);
5630                 return (Z_OK);
5631         }
5632 
5633         cookie = setzoneent();
5634         while ((ze = getzoneent_private(cookie)) != NULL) {
5635                 if (strcmp(ze->zone_name, zone_name) == 0) {
5636                         found = B_TRUE;
5637                         *state_num = ze->zone_state;
5638                 }
5639                 free(ze);
5640                 if (found)
5641                         break;
5642         }
5643         endzoneent(cookie);
5644         return ((found) ? Z_OK : Z_NO_ZONE);
5645 }
5646 
5647 int
5648 zone_set_state(char *zone, zone_state_t state)
5649 {
5650         struct zoneent ze;
5651 
5652         if (state != ZONE_STATE_CONFIGURED && state != ZONE_STATE_INSTALLED &&
5653             state != ZONE_STATE_INCOMPLETE)
5654                 return (Z_INVAL);
5655 
5656         bzero(&ze, sizeof (ze));
5657         (void) strlcpy(ze.zone_name, zone, sizeof (ze.zone_name));
5658         ze.zone_state = state;
5659         (void) strlcpy(ze.zone_path, "", sizeof (ze.zone_path));
5660         return (putzoneent(&ze, PZE_MODIFY));
5661 }
5662 
5663 /*
5664  * Get id (if any) for specified zone.  There are four possible outcomes:
5665  * - If the string corresponds to the numeric id of an active (booted)
5666  *   zone, sets *zip to the zone id and returns 0.
5667  * - If the string corresponds to the name of an active (booted) zone,
5668  *   sets *zip to the zone id and returns 0.
5669  * - If the string is a name in the configuration but is not booted,
5670  *   sets *zip to ZONE_ID_UNDEFINED and returns 0.
5671  * - Otherwise, leaves *zip unchanged and returns -1.
5672  *
5673  * This function acts as an auxiliary filter on the function of the same
5674  * name in libc; the linker binds to this version if libzonecfg exists,
5675  * and the libc version if it doesn't.  Any changes to this version of
5676  * the function should probably be reflected in the libc version as well.
5677  */
5678 int
5679 zone_get_id(const char *str, zoneid_t *zip)
5680 {
5681         zone_dochandle_t hdl;
5682         zoneid_t zoneid;
5683         char *cp;
5684         int err;
5685 
5686         /* first try looking for active zone by id */
5687         errno = 0;
5688         zoneid = (zoneid_t)strtol(str, &cp, 0);
5689         if (errno == 0 && cp != str && *cp == '\0' &&
5690             getzonenamebyid(zoneid, NULL, 0) != -1) {
5691                 *zip = zoneid;
5692                 return (0);
5693         }
5694 
5695         /* then look for active zone by name */
5696         if ((zoneid = getzoneidbyname(str)) != -1) {
5697                 *zip = zoneid;
5698                 return (0);
5699         }
5700 
5701         /* if in global zone, try looking up name in configuration database */
5702         if (getzoneid() != GLOBAL_ZONEID ||
5703             (hdl = zonecfg_init_handle()) == NULL)
5704                 return (-1);
5705 
5706         if (zonecfg_get_handle(str, hdl) == Z_OK) {
5707                 /* zone exists but isn't active */
5708                 *zip = ZONE_ID_UNDEFINED;
5709                 err = 0;
5710         } else {
5711                 err = -1;
5712         }
5713 
5714         zonecfg_fini_handle(hdl);
5715         return (err);
5716 }
5717 
5718 char *
5719 zone_state_str(zone_state_t state_num)
5720 {
5721         switch (state_num) {
5722         case ZONE_STATE_CONFIGURED:
5723                 return (ZONE_STATE_STR_CONFIGURED);
5724         case ZONE_STATE_INCOMPLETE:
5725                 return (ZONE_STATE_STR_INCOMPLETE);
5726         case ZONE_STATE_INSTALLED:
5727                 return (ZONE_STATE_STR_INSTALLED);
5728         case ZONE_STATE_READY:
5729                 return (ZONE_STATE_STR_READY);
5730         case ZONE_STATE_MOUNTED:
5731                 return (ZONE_STATE_STR_MOUNTED);
5732         case ZONE_STATE_RUNNING:
5733                 return (ZONE_STATE_STR_RUNNING);
5734         case ZONE_STATE_SHUTTING_DOWN:
5735                 return (ZONE_STATE_STR_SHUTTING_DOWN);
5736         case ZONE_STATE_DOWN:
5737                 return (ZONE_STATE_STR_DOWN);
5738         default:
5739                 return ("unknown");
5740         }
5741 }
5742 
5743 /*
5744  * Given a UUID value, find an associated zone name.  This is intended to be
5745  * used by callers who set up some 'default' name (corresponding to the
5746  * expected name for the zone) in the zonename buffer, and thus the function
5747  * doesn't touch this buffer on failure.
5748  */
5749 int
5750 zonecfg_get_name_by_uuid(const uuid_t uuidin, char *zonename, size_t namelen)
5751 {
5752         FILE *fp;
5753         struct zoneent *ze;
5754         uchar_t *uuid;
5755 
5756         /*
5757          * A small amount of subterfuge via casts is necessary here because
5758          * libuuid doesn't use const correctly, but we don't want to export
5759          * this brokenness to our clients.
5760          */
5761         uuid = (uchar_t *)uuidin;
5762         if (uuid_is_null(uuid))
5763                 return (Z_NO_ZONE);
5764         if ((fp = setzoneent()) == NULL)
5765                 return (Z_NO_ZONE);
5766         while ((ze = getzoneent_private(fp)) != NULL) {
5767                 if (uuid_compare(uuid, ze->zone_uuid) == 0)
5768                         break;
5769                 free(ze);
5770         }
5771         endzoneent(fp);
5772         if (ze != NULL) {
5773                 (void) strlcpy(zonename, ze->zone_name, namelen);
5774                 free(ze);
5775                 return (Z_OK);
5776         } else {
5777                 return (Z_NO_ZONE);
5778         }
5779 }
5780 
5781 /*
5782  * Given a zone name, get its UUID.  Returns a "NULL" UUID value if the zone
5783  * exists but the file doesn't have a value set yet.  Returns an error if the
5784  * zone cannot be located.
5785  */
5786 int
5787 zonecfg_get_uuid(const char *zonename, uuid_t uuid)
5788 {
5789         FILE *fp;
5790         struct zoneent *ze;
5791 
5792         if ((fp = setzoneent()) == NULL)
5793                 return (Z_NO_ZONE);
5794         while ((ze = getzoneent_private(fp)) != NULL) {
5795                 if (strcmp(ze->zone_name, zonename) == 0)
5796                         break;
5797                 free(ze);
5798         }
5799         endzoneent(fp);
5800         if (ze != NULL) {
5801                 uuid_copy(uuid, ze->zone_uuid);
5802                 free(ze);
5803                 return (Z_OK);
5804         } else {
5805                 return (Z_NO_ZONE);
5806         }
5807 }
5808 
5809 /*
5810  * File-system convenience functions.
5811  */
5812 boolean_t
5813 zonecfg_valid_fs_type(const char *type)
5814 {
5815         /*
5816          * We already know which FS types don't work.
5817          */
5818         if (strcmp(type, "proc") == 0 ||
5819             strcmp(type, "mntfs") == 0 ||
5820             strcmp(type, "autofs") == 0 ||
5821             strncmp(type, "nfs", sizeof ("nfs") - 1) == 0)
5822                 return (B_FALSE);
5823         /*
5824          * The caller may do more detailed verification to make sure other
5825          * aspects of this filesystem type make sense.
5826          */
5827         return (B_TRUE);
5828 }
5829 
5830 /*
5831  * Generally uninteresting rctl convenience functions.
5832  */
5833 
5834 int
5835 zonecfg_construct_rctlblk(const struct zone_rctlvaltab *rctlval,
5836     rctlblk_t *rctlblk)
5837 {
5838         unsigned long long ull;
5839         char *endp;
5840         rctl_priv_t priv;
5841         rctl_qty_t limit;
5842         uint_t action;
5843 
5844         /* Get the privilege */
5845         if (strcmp(rctlval->zone_rctlval_priv, "basic") == 0) {
5846                 priv = RCPRIV_BASIC;
5847         } else if (strcmp(rctlval->zone_rctlval_priv, "privileged") == 0) {
5848                 priv = RCPRIV_PRIVILEGED;
5849         } else {
5850                 /* Invalid privilege */
5851                 return (Z_INVAL);
5852         }
5853 
5854         /* deal with negative input; strtoull(3c) doesn't do what we want */
5855         if (rctlval->zone_rctlval_limit[0] == '-')
5856                 return (Z_INVAL);
5857         /* Get the limit */
5858         errno = 0;
5859         ull = strtoull(rctlval->zone_rctlval_limit, &endp, 0);
5860         if (errno != 0 || *endp != '\0') {
5861                 /* parse failed */
5862                 return (Z_INVAL);
5863         }
5864         limit = (rctl_qty_t)ull;
5865 
5866         /* Get the action */
5867         if (strcmp(rctlval->zone_rctlval_action, "none") == 0) {
5868                 action = RCTL_LOCAL_NOACTION;
5869         } else if (strcmp(rctlval->zone_rctlval_action, "signal") == 0) {
5870                 action = RCTL_LOCAL_SIGNAL;
5871         } else if (strcmp(rctlval->zone_rctlval_action, "deny") == 0) {
5872                 action = RCTL_LOCAL_DENY;
5873         } else {
5874                 /* Invalid Action */
5875                 return (Z_INVAL);
5876         }
5877         rctlblk_set_local_action(rctlblk, action, 0);
5878         rctlblk_set_privilege(rctlblk, priv);
5879         rctlblk_set_value(rctlblk, limit);
5880         return (Z_OK);
5881 }
5882 
5883 static int
5884 rctl_check(const char *rctlname, void *arg)
5885 {
5886         const char *attrname = arg;
5887 
5888         /*
5889          * Returning 1 here is our signal to zonecfg_is_rctl() that it is
5890          * indeed an rctl name recognized by the system.
5891          */
5892         return (strcmp(rctlname, attrname) == 0 ? 1 : 0);
5893 }
5894 
5895 boolean_t
5896 zonecfg_is_rctl(const char *name)
5897 {
5898         return (rctl_walk(rctl_check, (void *)name) == 1);
5899 }
5900 
5901 boolean_t
5902 zonecfg_valid_rctlname(const char *name)
5903 {
5904         const char *c;
5905 
5906         if (strncmp(name, "zone.", sizeof ("zone.") - 1) != 0)
5907                 return (B_FALSE);
5908         if (strlen(name) == sizeof ("zone.") - 1)
5909                 return (B_FALSE);
5910         for (c = name + sizeof ("zone.") - 1; *c != '\0'; c++) {
5911                 if (!isalpha(*c) && *c != '-')
5912                         return (B_FALSE);
5913         }
5914         return (B_TRUE);
5915 }
5916 
5917 boolean_t
5918 zonecfg_valid_rctlblk(const rctlblk_t *rctlblk)
5919 {
5920         rctl_priv_t priv = rctlblk_get_privilege((rctlblk_t *)rctlblk);
5921         uint_t action = rctlblk_get_local_action((rctlblk_t *)rctlblk, NULL);
5922 
5923         if (priv != RCPRIV_PRIVILEGED)
5924                 return (B_FALSE);
5925         if (action != RCTL_LOCAL_NOACTION && action != RCTL_LOCAL_DENY)
5926                 return (B_FALSE);
5927         return (B_TRUE);
5928 }
5929 
5930 boolean_t
5931 zonecfg_valid_rctl(const char *name, const rctlblk_t *rctlblk)
5932 {
5933         rctlblk_t *current, *next;
5934         rctl_qty_t limit = rctlblk_get_value((rctlblk_t *)rctlblk);
5935         uint_t action = rctlblk_get_local_action((rctlblk_t *)rctlblk, NULL);
5936         uint_t global_flags;
5937 
5938         if (!zonecfg_valid_rctlblk(rctlblk))
5939                 return (B_FALSE);
5940         if (!zonecfg_valid_rctlname(name))
5941                 return (B_FALSE);
5942 
5943         current = alloca(rctlblk_size());
5944         if (getrctl(name, NULL, current, RCTL_FIRST) != 0)
5945                 return (B_TRUE);        /* not an rctl on this system */
5946         /*
5947          * Make sure the proposed value isn't greater than the current system
5948          * value.
5949          */
5950         next = alloca(rctlblk_size());
5951         while (rctlblk_get_privilege(current) != RCPRIV_SYSTEM) {
5952                 rctlblk_t *tmp;
5953 
5954                 if (getrctl(name, current, next, RCTL_NEXT) != 0)
5955                         return (B_FALSE);       /* shouldn't happen */
5956                 tmp = current;
5957                 current = next;
5958                 next = tmp;
5959         }
5960         if (limit > rctlblk_get_value(current))
5961                 return (B_FALSE);
5962 
5963         /*
5964          * Make sure the proposed action is allowed.
5965          */
5966         global_flags = rctlblk_get_global_flags(current);
5967         if ((global_flags & RCTL_GLOBAL_DENY_NEVER) &&
5968             action == RCTL_LOCAL_DENY)
5969                 return (B_FALSE);
5970         if ((global_flags & RCTL_GLOBAL_DENY_ALWAYS) &&
5971             action == RCTL_LOCAL_NOACTION)
5972                 return (B_FALSE);
5973 
5974         return (B_TRUE);
5975 }
5976 
5977 /*
5978  * There is always a race condition between reading the initial copy of
5979  * a zones state and its state changing.  We address this by providing
5980  * zonecfg_notify_critical_enter and zonecfg_noticy_critical_exit functions.
5981  * When zonecfg_critical_enter is called, sets the state field to LOCKED
5982  * and aquires biglock. Biglock protects against other threads executing
5983  * critical_enter and the state field protects against state changes during
5984  * the critical period.
5985  *
5986  * If any state changes occur, zn_cb will set the failed field of the znotify
5987  * structure.  This will cause the critical_exit function to re-lock the
5988  * channel and return an error. Since evsnts may be delayed, the critical_exit
5989  * function "flushes" the queue by putting an event on the queue and waiting for
5990  * zn_cb to notify critical_exit that it received the ping event.
5991  */
5992 static const char *
5993 string_get_tok(const char *in, char delim, int num)
5994 {
5995         int i = 0;
5996 
5997         for (; i < num; in++) {
5998                 if (*in == delim)
5999                         i++;
6000                 if (*in == 0)
6001                         return (NULL);
6002         }
6003         return (in);
6004 }
6005 
6006 static boolean_t
6007 is_ping(sysevent_t *ev)
6008 {
6009         if (strcmp(sysevent_get_subclass_name(ev),
6010             ZONE_EVENT_PING_SUBCLASS) == 0) {
6011                 return (B_TRUE);
6012         } else {
6013                 return (B_FALSE);
6014         }
6015 }
6016 
6017 static boolean_t
6018 is_my_ping(sysevent_t *ev)
6019 {
6020         const char *sender;
6021         char mypid[sizeof (pid_t) * 3 + 1];
6022 
6023         (void) snprintf(mypid, sizeof (mypid), "%i", getpid());
6024         sender = string_get_tok(sysevent_get_pub(ev), ':', 3);
6025         if (sender == NULL)
6026                 return (B_FALSE);
6027         if (strcmp(sender, mypid) != 0)
6028                 return (B_FALSE);
6029         return (B_TRUE);
6030 }
6031 
6032 static int
6033 do_callback(struct znotify *zevtchan, sysevent_t *ev)
6034 {
6035         nvlist_t *l;
6036         int zid;
6037         char *zonename;
6038         char *newstate;
6039         char *oldstate;
6040         int ret;
6041         hrtime_t when;
6042 
6043         if (strcmp(sysevent_get_subclass_name(ev),
6044             ZONE_EVENT_STATUS_SUBCLASS) == 0) {
6045 
6046                 if (sysevent_get_attr_list(ev, &l) != 0) {
6047                         if (errno == ENOMEM) {
6048                                 zevtchan->zn_failure_count++;
6049                                 return (EAGAIN);
6050                         }
6051                         return (0);
6052                 }
6053                 ret = 0;
6054 
6055                 if ((nvlist_lookup_string(l, ZONE_CB_NAME, &zonename) == 0) &&
6056                     (nvlist_lookup_string(l, ZONE_CB_NEWSTATE, &newstate)
6057                     == 0) &&
6058                     (nvlist_lookup_string(l, ZONE_CB_OLDSTATE, &oldstate)
6059                     == 0) &&
6060                     (nvlist_lookup_uint64(l, ZONE_CB_TIMESTAMP,
6061                     (uint64_t *)&when) == 0) &&
6062                     (nvlist_lookup_int32(l, ZONE_CB_ZONEID, &zid) == 0)) {
6063                         ret = zevtchan->zn_callback(zonename, zid, newstate,
6064                             oldstate, when, zevtchan->zn_private);
6065                 }
6066 
6067                 zevtchan->zn_failure_count = 0;
6068                 nvlist_free(l);
6069                 return (ret);
6070         } else {
6071                 /*
6072                  * We have received an event in an unknown subclass. Ignore.
6073                  */
6074                 zevtchan->zn_failure_count = 0;
6075                 return (0);
6076         }
6077 }
6078 
6079 static int
6080 zn_cb(sysevent_t *ev, void *p)
6081 {
6082         struct znotify *zevtchan = p;
6083         int error;
6084 
6085         (void) pthread_mutex_lock(&(zevtchan->zn_mutex));
6086 
6087         if (is_ping(ev) && !is_my_ping(ev)) {
6088                 (void) pthread_mutex_unlock((&zevtchan->zn_mutex));
6089                 return (0);
6090         }
6091 
6092         if (zevtchan->zn_state == ZN_LOCKED) {
6093                 assert(!is_ping(ev));
6094                 zevtchan->zn_failed = B_TRUE;
6095                 (void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
6096                 return (0);
6097         }
6098 
6099         if (zevtchan->zn_state == ZN_PING_INFLIGHT) {
6100                 if (is_ping(ev)) {
6101                         zevtchan->zn_state = ZN_PING_RECEIVED;
6102                         (void) pthread_cond_signal(&(zevtchan->zn_cond));
6103                         (void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
6104                         return (0);
6105                 } else {
6106                         zevtchan->zn_failed = B_TRUE;
6107                         (void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
6108                         return (0);
6109                 }
6110         }
6111 
6112         if (zevtchan->zn_state == ZN_UNLOCKED) {
6113 
6114                 error = do_callback(zevtchan, ev);
6115                 (void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
6116                 /*
6117                  * Every ENOMEM failure causes do_callback to increment
6118                  * zn_failure_count and every success causes it to
6119                  * set zn_failure_count to zero.  If we got EAGAIN,
6120                  * we will sleep for zn_failure_count seconds and return
6121                  * EAGAIN to gpec to try again.
6122                  *
6123                  * After 55 seconds, or 10 try's we give up and drop the
6124                  * event.
6125                  */
6126                 if (error == EAGAIN) {
6127                         if (zevtchan->zn_failure_count > ZONE_CB_RETRY_COUNT) {
6128                                 return (0);
6129                         }
6130                         (void) sleep(zevtchan->zn_failure_count);
6131                 }
6132                 return (error);
6133         }
6134 
6135         if (zevtchan->zn_state == ZN_PING_RECEIVED) {
6136                 (void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
6137                 return (0);
6138         }
6139 
6140         abort();
6141         return (0);
6142 }
6143 
6144 void
6145 zonecfg_notify_critical_enter(void *h)
6146 {
6147         struct znotify *zevtchan = h;
6148 
6149         (void) pthread_mutex_lock(&(zevtchan->zn_bigmutex));
6150         zevtchan->zn_state = ZN_LOCKED;
6151 }
6152 
6153 int
6154 zonecfg_notify_critical_exit(void * h)
6155 {
6156 
6157         struct znotify *zevtchan = h;
6158 
6159         if (zevtchan->zn_state == ZN_UNLOCKED)
6160                 return (0);
6161 
6162         (void) pthread_mutex_lock(&(zevtchan->zn_mutex));
6163         zevtchan->zn_state = ZN_PING_INFLIGHT;
6164 
6165         (void) sysevent_evc_publish(zevtchan->zn_eventchan,
6166             ZONE_EVENT_STATUS_CLASS,
6167             ZONE_EVENT_PING_SUBCLASS, ZONE_EVENT_PING_PUBLISHER,
6168             zevtchan->zn_subscriber_id, NULL, EVCH_SLEEP);
6169 
6170         while (zevtchan->zn_state != ZN_PING_RECEIVED) {
6171                 (void) pthread_cond_wait(&(zevtchan->zn_cond),
6172                     &(zevtchan->zn_mutex));
6173         }
6174 
6175         if (zevtchan->zn_failed == B_TRUE) {
6176                 zevtchan->zn_state = ZN_LOCKED;
6177                 zevtchan->zn_failed = B_FALSE;
6178                 (void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
6179                 return (1);
6180         }
6181 
6182         zevtchan->zn_state = ZN_UNLOCKED;
6183         (void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
6184         (void) pthread_mutex_unlock(&(zevtchan->zn_bigmutex));
6185         return (0);
6186 }
6187 
6188 void
6189 zonecfg_notify_critical_abort(void *h)
6190 {
6191         struct znotify *zevtchan = h;
6192 
6193         zevtchan->zn_state = ZN_UNLOCKED;
6194         zevtchan->zn_failed = B_FALSE;
6195         /*
6196          * Don't do anything about zn_lock. If it is held, it could only be
6197          * held by zn_cb and it will be unlocked soon.
6198          */
6199         (void) pthread_mutex_unlock(&(zevtchan->zn_bigmutex));
6200 }
6201 
6202 void *
6203 zonecfg_notify_bind(int(*func)(const char *zonename, zoneid_t zid,
6204     const char *newstate, const char *oldstate, hrtime_t when, void *p),
6205     void *p)
6206 {
6207         struct znotify *zevtchan;
6208         int i = 1;
6209         int r;
6210 
6211         zevtchan = malloc(sizeof (struct znotify));
6212 
6213         if (zevtchan == NULL)
6214                 return (NULL);
6215 
6216         zevtchan->zn_private = p;
6217         zevtchan->zn_callback = func;
6218         zevtchan->zn_state = ZN_UNLOCKED;
6219         zevtchan->zn_failed = B_FALSE;
6220 
6221         if (pthread_mutex_init(&(zevtchan->zn_mutex), NULL))
6222                 goto out3;
6223         if (pthread_cond_init(&(zevtchan->zn_cond), NULL)) {
6224                 (void) pthread_mutex_destroy(&(zevtchan->zn_mutex));
6225                 goto out3;
6226         }
6227         if (pthread_mutex_init(&(zevtchan->zn_bigmutex), NULL)) {
6228                 (void) pthread_mutex_destroy(&(zevtchan->zn_mutex));
6229                 (void) pthread_cond_destroy(&(zevtchan->zn_cond));
6230                 goto out3;
6231         }
6232 
6233         if (sysevent_evc_bind(ZONE_EVENT_CHANNEL, &(zevtchan->zn_eventchan),
6234             0) != 0)
6235                 goto out2;
6236 
6237         do {
6238                 /*
6239                  * At 4 digits the subscriber ID gets too long and we have
6240                  * no chance of successfully registering.
6241                  */
6242                 if (i > 999)
6243                         goto out1;
6244 
6245                 (void) sprintf(zevtchan->zn_subscriber_id, "zone_%li_%i",
6246                     getpid() % 999999l, i);
6247 
6248                 r = sysevent_evc_subscribe(zevtchan->zn_eventchan,
6249                     zevtchan->zn_subscriber_id, ZONE_EVENT_STATUS_CLASS, zn_cb,
6250                     zevtchan, 0);
6251 
6252                 i++;
6253 
6254         } while (r);
6255 
6256         return (zevtchan);
6257 out1:
6258         (void) sysevent_evc_unbind(zevtchan->zn_eventchan);
6259 out2:
6260         (void) pthread_mutex_destroy(&zevtchan->zn_mutex);
6261         (void) pthread_cond_destroy(&zevtchan->zn_cond);
6262         (void) pthread_mutex_destroy(&(zevtchan->zn_bigmutex));
6263 out3:
6264         free(zevtchan);
6265 
6266         return (NULL);
6267 }
6268 
6269 void
6270 zonecfg_notify_unbind(void *handle)
6271 {
6272 
6273         int ret;
6274 
6275         (void) sysevent_evc_unbind(((struct znotify *)handle)->zn_eventchan);
6276         /*
6277          * Check that all evc threads have gone away. This should be
6278          * enforced by sysevent_evc_unbind.
6279          */
6280         ret = pthread_mutex_trylock(&((struct znotify *)handle)->zn_mutex);
6281 
6282         if (ret)
6283                 abort();
6284 
6285         (void) pthread_mutex_unlock(&((struct znotify *)handle)->zn_mutex);
6286         (void) pthread_mutex_destroy(&((struct znotify *)handle)->zn_mutex);
6287         (void) pthread_cond_destroy(&((struct znotify *)handle)->zn_cond);
6288         (void) pthread_mutex_destroy(&((struct znotify *)handle)->zn_bigmutex);
6289 
6290         free(handle);
6291 }
6292 
6293 static int
6294 zonecfg_add_ds_core(zone_dochandle_t handle, struct zone_dstab *tabptr)
6295 {
6296         xmlNodePtr newnode, cur = handle->zone_dh_cur;
6297         int err;
6298 
6299         newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DATASET, NULL);
6300         if ((err = newprop(newnode, DTD_ATTR_NAME,
6301             tabptr->zone_dataset_name)) != Z_OK)
6302                 return (err);
6303         return (Z_OK);
6304 }
6305 
6306 int
6307 zonecfg_add_ds(zone_dochandle_t handle, struct zone_dstab *tabptr)
6308 {
6309         int err;
6310 
6311         if (tabptr == NULL)
6312                 return (Z_INVAL);
6313 
6314         if ((err = operation_prep(handle)) != Z_OK)
6315                 return (err);
6316 
6317         if ((err = zonecfg_add_ds_core(handle, tabptr)) != Z_OK)
6318                 return (err);
6319 
6320         return (Z_OK);
6321 }
6322 
6323 static int
6324 zonecfg_delete_ds_core(zone_dochandle_t handle, struct zone_dstab *tabptr)
6325 {
6326         xmlNodePtr cur = handle->zone_dh_cur;
6327 
6328         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
6329                 if (xmlStrcmp(cur->name, DTD_ELEM_DATASET))
6330                         continue;
6331 
6332                 if (match_prop(cur, DTD_ATTR_NAME,
6333                     tabptr->zone_dataset_name)) {
6334                         xmlUnlinkNode(cur);
6335                         xmlFreeNode(cur);
6336                         return (Z_OK);
6337                 }
6338         }
6339         return (Z_NO_RESOURCE_ID);
6340 }
6341 
6342 int
6343 zonecfg_delete_ds(zone_dochandle_t handle, struct zone_dstab *tabptr)
6344 {
6345         int err;
6346 
6347         if (tabptr == NULL)
6348                 return (Z_INVAL);
6349 
6350         if ((err = operation_prep(handle)) != Z_OK)
6351                 return (err);
6352 
6353         if ((err = zonecfg_delete_ds_core(handle, tabptr)) != Z_OK)
6354                 return (err);
6355 
6356         return (Z_OK);
6357 }
6358 
6359 int
6360 zonecfg_modify_ds(
6361         zone_dochandle_t handle,
6362         struct zone_dstab *oldtabptr,
6363         struct zone_dstab *newtabptr)
6364 {
6365         int err;
6366 
6367         if (oldtabptr == NULL || newtabptr == NULL)
6368                 return (Z_INVAL);
6369 
6370         if ((err = operation_prep(handle)) != Z_OK)
6371                 return (err);
6372 
6373         if ((err = zonecfg_delete_ds_core(handle, oldtabptr)) != Z_OK)
6374                 return (err);
6375 
6376         if ((err = zonecfg_add_ds_core(handle, newtabptr)) != Z_OK)
6377                 return (err);
6378 
6379         return (Z_OK);
6380 }
6381 
6382 int
6383 zonecfg_lookup_ds(zone_dochandle_t handle, struct zone_dstab *tabptr)
6384 {
6385         xmlNodePtr cur, firstmatch;
6386         int err;
6387         char dataset[MAXNAMELEN];
6388 
6389         if (tabptr == NULL)
6390                 return (Z_INVAL);
6391 
6392         if ((err = operation_prep(handle)) != Z_OK)
6393                 return (err);
6394 
6395         cur = handle->zone_dh_cur;
6396         firstmatch = NULL;
6397         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
6398                 if (xmlStrcmp(cur->name, DTD_ELEM_DATASET))
6399                         continue;
6400                 if (strlen(tabptr->zone_dataset_name) > 0) {
6401                         if ((fetchprop(cur, DTD_ATTR_NAME, dataset,
6402                             sizeof (dataset)) == Z_OK) &&
6403                             (strcmp(tabptr->zone_dataset_name,
6404                             dataset) == 0)) {
6405                                 if (firstmatch == NULL)
6406                                         firstmatch = cur;
6407                                 else
6408                                         return (Z_INSUFFICIENT_SPEC);
6409                         }
6410                 }
6411         }
6412         if (firstmatch == NULL)
6413                 return (Z_NO_RESOURCE_ID);
6414 
6415         cur = firstmatch;
6416 
6417         if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_dataset_name,
6418             sizeof (tabptr->zone_dataset_name))) != Z_OK)
6419                 return (err);
6420 
6421         return (Z_OK);
6422 }
6423 
6424 int
6425 zonecfg_setdsent(zone_dochandle_t handle)
6426 {
6427         return (zonecfg_setent(handle));
6428 }
6429 
6430 int
6431 zonecfg_getdsent(zone_dochandle_t handle, struct zone_dstab *tabptr)
6432 {
6433         xmlNodePtr cur;
6434         int err;
6435 
6436         if (handle == NULL)
6437                 return (Z_INVAL);
6438 
6439         if ((cur = handle->zone_dh_cur) == NULL)
6440                 return (Z_NO_ENTRY);
6441 
6442         for (; cur != NULL; cur = cur->next)
6443                 if (!xmlStrcmp(cur->name, DTD_ELEM_DATASET))
6444                         break;
6445         if (cur == NULL) {
6446                 handle->zone_dh_cur = handle->zone_dh_top;
6447                 return (Z_NO_ENTRY);
6448         }
6449 
6450         if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_dataset_name,
6451             sizeof (tabptr->zone_dataset_name))) != Z_OK) {
6452                 handle->zone_dh_cur = handle->zone_dh_top;
6453                 return (err);
6454         }
6455 
6456         handle->zone_dh_cur = cur->next;
6457         return (Z_OK);
6458 }
6459 
6460 int
6461 zonecfg_enddsent(zone_dochandle_t handle)
6462 {
6463         return (zonecfg_endent(handle));
6464 }
6465 
6466 /*
6467  * Support for aliased rctls; that is, rctls that have simplified names in
6468  * zonecfg.  For example, max-lwps is an alias for a well defined zone.max-lwps
6469  * rctl.  If there are multiple existing values for one of these rctls or if
6470  * there is a single value that does not match the well defined template (i.e.
6471  * it has a different action) then we cannot treat the rctl as having an alias
6472  * so we return Z_ALIAS_DISALLOW.  That means that the rctl cannot be
6473  * managed in zonecfg via an alias and that the standard rctl syntax must be
6474  * used.
6475  *
6476  * The possible return values are:
6477  *      Z_NO_PROPERTY_ID - invalid alias name
6478  *      Z_ALIAS_DISALLOW - pre-existing, incompatible rctl definition
6479  *      Z_NO_ENTRY - no rctl is configured for this alias
6480  *      Z_OK - we got a valid rctl for the specified alias
6481  */
6482 int
6483 zonecfg_get_aliased_rctl(zone_dochandle_t handle, char *name, uint64_t *rval)
6484 {
6485         boolean_t found = B_FALSE;
6486         boolean_t found_val = B_FALSE;
6487         xmlNodePtr cur, val;
6488         char savedname[MAXNAMELEN];
6489         struct zone_rctlvaltab rctl;
6490         int i;
6491         int err;
6492 
6493         for (i = 0; aliases[i].shortname != NULL; i++)
6494                 if (strcmp(name, aliases[i].shortname) == 0)
6495                         break;
6496 
6497         if (aliases[i].shortname == NULL)
6498                 return (Z_NO_PROPERTY_ID);
6499 
6500         if ((err = operation_prep(handle)) != Z_OK)
6501                 return (err);
6502 
6503         cur = handle->zone_dh_cur;
6504         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
6505                 if (xmlStrcmp(cur->name, DTD_ELEM_RCTL) != 0)
6506                         continue;
6507                 if ((fetchprop(cur, DTD_ATTR_NAME, savedname,
6508                     sizeof (savedname)) == Z_OK) &&
6509                     (strcmp(savedname, aliases[i].realname) == 0)) {
6510 
6511                         /*
6512                          * If we already saw one of these, we can't have an
6513                          * alias since we just found another.
6514                          */
6515                         if (found)
6516                                 return (Z_ALIAS_DISALLOW);
6517                         found = B_TRUE;
6518 
6519                         for (val = cur->xmlChildrenNode; val != NULL;
6520                             val = val->next) {
6521                                 /*
6522                                  * If we already have one value, we can't have
6523                                  * an alias since we just found another.
6524                                  */
6525                                 if (found_val)
6526                                         return (Z_ALIAS_DISALLOW);
6527                                 found_val = B_TRUE;
6528 
6529                                 if ((fetchprop(val, DTD_ATTR_PRIV,
6530                                     rctl.zone_rctlval_priv,
6531                                     sizeof (rctl.zone_rctlval_priv)) != Z_OK))
6532                                         break;
6533                                 if ((fetchprop(val, DTD_ATTR_LIMIT,
6534                                     rctl.zone_rctlval_limit,
6535                                     sizeof (rctl.zone_rctlval_limit)) != Z_OK))
6536                                         break;
6537                                 if ((fetchprop(val, DTD_ATTR_ACTION,
6538                                     rctl.zone_rctlval_action,
6539                                     sizeof (rctl.zone_rctlval_action)) != Z_OK))
6540                                         break;
6541                         }
6542 
6543                         /* check priv and action match the expected vals */
6544                         if (strcmp(rctl.zone_rctlval_priv,
6545                             aliases[i].priv) != 0 ||
6546                             strcmp(rctl.zone_rctlval_action,
6547                             aliases[i].action) != 0)
6548                                 return (Z_ALIAS_DISALLOW);
6549                 }
6550         }
6551 
6552         if (found) {
6553                 *rval = strtoull(rctl.zone_rctlval_limit, NULL, 10);
6554                 return (Z_OK);
6555         }
6556 
6557         return (Z_NO_ENTRY);
6558 }
6559 
6560 int
6561 zonecfg_rm_aliased_rctl(zone_dochandle_t handle, char *name)
6562 {
6563         int i;
6564         uint64_t val;
6565         struct zone_rctltab rctltab;
6566 
6567         /*
6568          * First check that we have a valid aliased rctl to remove.
6569          * This will catch an rctl entry with non-standard values or
6570          * multiple rctl values for this name.  We need to ignore those
6571          * rctl entries.
6572          */
6573         if (zonecfg_get_aliased_rctl(handle, name, &val) != Z_OK)
6574                 return (Z_OK);
6575 
6576         for (i = 0; aliases[i].shortname != NULL; i++)
6577                 if (strcmp(name, aliases[i].shortname) == 0)
6578                         break;
6579 
6580         if (aliases[i].shortname == NULL)
6581                 return (Z_NO_RESOURCE_ID);
6582 
6583         (void) strlcpy(rctltab.zone_rctl_name, aliases[i].realname,
6584             sizeof (rctltab.zone_rctl_name));
6585 
6586         return (zonecfg_delete_rctl(handle, &rctltab));
6587 }
6588 
6589 boolean_t
6590 zonecfg_aliased_rctl_ok(zone_dochandle_t handle, char *name)
6591 {
6592         uint64_t tmp_val;
6593 
6594         switch (zonecfg_get_aliased_rctl(handle, name, &tmp_val)) {
6595         case Z_OK:
6596                 /*FALLTHRU*/
6597         case Z_NO_ENTRY:
6598                 return (B_TRUE);
6599         default:
6600                 return (B_FALSE);
6601         }
6602 }
6603 
6604 int
6605 zonecfg_set_aliased_rctl(zone_dochandle_t handle, char *name, uint64_t val)
6606 {
6607         int i;
6608         int err;
6609         struct zone_rctltab rctltab;
6610         struct zone_rctlvaltab *rctlvaltab;
6611         char buf[128];
6612 
6613         if (!zonecfg_aliased_rctl_ok(handle, name))
6614                 return (Z_ALIAS_DISALLOW);
6615 
6616         for (i = 0; aliases[i].shortname != NULL; i++)
6617                 if (strcmp(name, aliases[i].shortname) == 0)
6618                         break;
6619 
6620         if (aliases[i].shortname == NULL)
6621                 return (Z_NO_RESOURCE_ID);
6622 
6623         /* remove any pre-existing definition for this rctl */
6624         (void) zonecfg_rm_aliased_rctl(handle, name);
6625 
6626         (void) strlcpy(rctltab.zone_rctl_name, aliases[i].realname,
6627             sizeof (rctltab.zone_rctl_name));
6628 
6629         rctltab.zone_rctl_valptr = NULL;
6630 
6631         if ((rctlvaltab = calloc(1, sizeof (struct zone_rctlvaltab))) == NULL)
6632                 return (Z_NOMEM);
6633 
6634         (void) snprintf(buf, sizeof (buf), "%llu", (long long)val);
6635 
6636         (void) strlcpy(rctlvaltab->zone_rctlval_priv, aliases[i].priv,
6637             sizeof (rctlvaltab->zone_rctlval_priv));
6638         (void) strlcpy(rctlvaltab->zone_rctlval_limit, buf,
6639             sizeof (rctlvaltab->zone_rctlval_limit));
6640         (void) strlcpy(rctlvaltab->zone_rctlval_action, aliases[i].action,
6641             sizeof (rctlvaltab->zone_rctlval_action));
6642 
6643         rctlvaltab->zone_rctlval_next = NULL;
6644 
6645         if ((err = zonecfg_add_rctl_value(&rctltab, rctlvaltab)) != Z_OK)
6646                 return (err);
6647 
6648         return (zonecfg_add_rctl(handle, &rctltab));
6649 }
6650 
6651 static int
6652 delete_tmp_pool(zone_dochandle_t handle)
6653 {
6654         int err;
6655         xmlNodePtr cur = handle->zone_dh_cur;
6656 
6657         if ((err = operation_prep(handle)) != Z_OK)
6658                 return (err);
6659 
6660         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
6661                 if (xmlStrcmp(cur->name, DTD_ELEM_TMPPOOL) == 0) {
6662                         xmlUnlinkNode(cur);
6663                         xmlFreeNode(cur);
6664                         return (Z_OK);
6665                 }
6666         }
6667 
6668         return (Z_NO_RESOURCE_ID);
6669 }
6670 
6671 static int
6672 modify_tmp_pool(zone_dochandle_t handle, char *pool_importance)
6673 {
6674         int err;
6675         xmlNodePtr cur = handle->zone_dh_cur;
6676         xmlNodePtr newnode;
6677 
6678         err = delete_tmp_pool(handle);
6679         if (err != Z_OK && err != Z_NO_RESOURCE_ID)
6680                 return (err);
6681 
6682         if (*pool_importance != '\0') {
6683                 if ((err = operation_prep(handle)) != Z_OK)
6684                         return (err);
6685 
6686                 newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_TMPPOOL, NULL);
6687                 if ((err = newprop(newnode, DTD_ATTR_IMPORTANCE,
6688                     pool_importance)) != Z_OK)
6689                         return (err);
6690         }
6691 
6692         return (Z_OK);
6693 }
6694 
6695 static int
6696 add_pset_core(zone_dochandle_t handle, struct zone_psettab *tabptr)
6697 {
6698         xmlNodePtr newnode, cur = handle->zone_dh_cur;
6699         int err;
6700 
6701         newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_PSET, NULL);
6702         if ((err = newprop(newnode, DTD_ATTR_NCPU_MIN,
6703             tabptr->zone_ncpu_min)) != Z_OK)
6704                 return (err);
6705         if ((err = newprop(newnode, DTD_ATTR_NCPU_MAX,
6706             tabptr->zone_ncpu_max)) != Z_OK)
6707                 return (err);
6708 
6709         if ((err = modify_tmp_pool(handle, tabptr->zone_importance)) != Z_OK)
6710                 return (err);
6711 
6712         return (Z_OK);
6713 }
6714 
6715 int
6716 zonecfg_add_pset(zone_dochandle_t handle, struct zone_psettab *tabptr)
6717 {
6718         int err;
6719 
6720         if (tabptr == NULL)
6721                 return (Z_INVAL);
6722 
6723         if ((err = operation_prep(handle)) != Z_OK)
6724                 return (err);
6725 
6726         if ((err = add_pset_core(handle, tabptr)) != Z_OK)
6727                 return (err);
6728 
6729         return (Z_OK);
6730 }
6731 
6732 int
6733 zonecfg_delete_pset(zone_dochandle_t handle)
6734 {
6735         int err;
6736         int res = Z_NO_RESOURCE_ID;
6737         xmlNodePtr cur = handle->zone_dh_cur;
6738 
6739         if ((err = operation_prep(handle)) != Z_OK)
6740                 return (err);
6741 
6742         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
6743                 if (xmlStrcmp(cur->name, DTD_ELEM_PSET) == 0) {
6744                         xmlUnlinkNode(cur);
6745                         xmlFreeNode(cur);
6746                         res = Z_OK;
6747                         break;
6748                 }
6749         }
6750 
6751         /*
6752          * Once we have msets, we should check that a mset
6753          * do not exist before we delete the tmp_pool data.
6754          */
6755         err = delete_tmp_pool(handle);
6756         if (err != Z_OK && err != Z_NO_RESOURCE_ID)
6757                 return (err);
6758 
6759         return (res);
6760 }
6761 
6762 int
6763 zonecfg_modify_pset(zone_dochandle_t handle, struct zone_psettab *tabptr)
6764 {
6765         int err;
6766 
6767         if (tabptr == NULL)
6768                 return (Z_INVAL);
6769 
6770         if ((err = zonecfg_delete_pset(handle)) != Z_OK)
6771                 return (err);
6772 
6773         if ((err = add_pset_core(handle, tabptr)) != Z_OK)
6774                 return (err);
6775 
6776         return (Z_OK);
6777 }
6778 
6779 int
6780 zonecfg_lookup_pset(zone_dochandle_t handle, struct zone_psettab *tabptr)
6781 {
6782         xmlNodePtr cur;
6783         int err;
6784         int res = Z_NO_ENTRY;
6785 
6786         if (tabptr == NULL)
6787                 return (Z_INVAL);
6788 
6789         if ((err = operation_prep(handle)) != Z_OK)
6790                 return (err);
6791 
6792         /* this is an optional component */
6793         tabptr->zone_importance[0] = '\0';
6794 
6795         cur = handle->zone_dh_cur;
6796         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
6797                 if (xmlStrcmp(cur->name, DTD_ELEM_PSET) == 0) {
6798                         if ((err = fetchprop(cur, DTD_ATTR_NCPU_MIN,
6799                             tabptr->zone_ncpu_min,
6800                             sizeof (tabptr->zone_ncpu_min))) != Z_OK) {
6801                                 handle->zone_dh_cur = handle->zone_dh_top;
6802                                 return (err);
6803                         }
6804 
6805                         if ((err = fetchprop(cur, DTD_ATTR_NCPU_MAX,
6806                             tabptr->zone_ncpu_max,
6807                             sizeof (tabptr->zone_ncpu_max))) != Z_OK) {
6808                                 handle->zone_dh_cur = handle->zone_dh_top;
6809                                 return (err);
6810                         }
6811 
6812                         res = Z_OK;
6813 
6814                 } else if (xmlStrcmp(cur->name, DTD_ELEM_TMPPOOL) == 0) {
6815                         if ((err = fetchprop(cur, DTD_ATTR_IMPORTANCE,
6816                             tabptr->zone_importance,
6817                             sizeof (tabptr->zone_importance))) != Z_OK) {
6818                                 handle->zone_dh_cur = handle->zone_dh_top;
6819                                 return (err);
6820                         }
6821                 }
6822         }
6823 
6824         return (res);
6825 }
6826 
6827 int
6828 zonecfg_getpsetent(zone_dochandle_t handle, struct zone_psettab *tabptr)
6829 {
6830         int err;
6831 
6832         if ((err = zonecfg_setent(handle)) != Z_OK)
6833                 return (err);
6834 
6835         err = zonecfg_lookup_pset(handle, tabptr);
6836 
6837         (void) zonecfg_endent(handle);
6838 
6839         return (err);
6840 }
6841 
6842 static int
6843 add_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
6844 {
6845         xmlNodePtr newnode, cur = handle->zone_dh_cur;
6846         int err;
6847 
6848         newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_MCAP, NULL);
6849         if ((err = newprop(newnode, DTD_ATTR_PHYSCAP, tabptr->zone_physmem_cap))
6850             != Z_OK)
6851                 return (err);
6852 
6853         return (Z_OK);
6854 }
6855 
6856 int
6857 zonecfg_delete_mcap(zone_dochandle_t handle)
6858 {
6859         int err;
6860         xmlNodePtr cur = handle->zone_dh_cur;
6861 
6862         if ((err = operation_prep(handle)) != Z_OK)
6863                 return (err);
6864 
6865         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
6866                 if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) != 0)
6867                         continue;
6868 
6869                 xmlUnlinkNode(cur);
6870                 xmlFreeNode(cur);
6871                 return (Z_OK);
6872         }
6873         return (Z_NO_RESOURCE_ID);
6874 }
6875 
6876 int
6877 zonecfg_modify_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
6878 {
6879         int err;
6880 
6881         if (tabptr == NULL)
6882                 return (Z_INVAL);
6883 
6884         err = zonecfg_delete_mcap(handle);
6885         /* it is ok if there is no mcap entry */
6886         if (err != Z_OK && err != Z_NO_RESOURCE_ID)
6887                 return (err);
6888 
6889         if ((err = add_mcap(handle, tabptr)) != Z_OK)
6890                 return (err);
6891 
6892         return (Z_OK);
6893 }
6894 
6895 int
6896 zonecfg_lookup_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
6897 {
6898         xmlNodePtr cur;
6899         int err;
6900 
6901         if (tabptr == NULL)
6902                 return (Z_INVAL);
6903 
6904         if ((err = operation_prep(handle)) != Z_OK)
6905                 return (err);
6906 
6907         cur = handle->zone_dh_cur;
6908         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
6909                 if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) != 0)
6910                         continue;
6911                 if ((err = fetchprop(cur, DTD_ATTR_PHYSCAP,
6912                     tabptr->zone_physmem_cap,
6913                     sizeof (tabptr->zone_physmem_cap))) != Z_OK) {
6914                         handle->zone_dh_cur = handle->zone_dh_top;
6915                         return (err);
6916                 }
6917 
6918                 return (Z_OK);
6919         }
6920 
6921         return (Z_NO_ENTRY);
6922 }
6923 























































6924 static int
6925 getmcapent_core(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
6926 {
6927         xmlNodePtr cur;
6928         int err;
6929 
6930         if (handle == NULL)
6931                 return (Z_INVAL);
6932 
6933         if ((cur = handle->zone_dh_cur) == NULL)
6934                 return (Z_NO_ENTRY);
6935 
6936         for (; cur != NULL; cur = cur->next)
6937                 if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) == 0)
6938                         break;
6939         if (cur == NULL) {
6940                 handle->zone_dh_cur = handle->zone_dh_top;
6941                 return (Z_NO_ENTRY);
6942         }
6943 
6944         if ((err = fetchprop(cur, DTD_ATTR_PHYSCAP, tabptr->zone_physmem_cap,
6945             sizeof (tabptr->zone_physmem_cap))) != Z_OK) {
6946                 handle->zone_dh_cur = handle->zone_dh_top;
6947                 return (err);
6948         }
6949 
6950         handle->zone_dh_cur = cur->next;
6951         return (Z_OK);
6952 }
6953 
6954 int
6955 zonecfg_getmcapent(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
6956 {
6957         int err;
6958 
6959         if ((err = zonecfg_setent(handle)) != Z_OK)
6960                 return (err);
6961 
6962         err = getmcapent_core(handle, tabptr);
6963 
6964         (void) zonecfg_endent(handle);
6965 
6966         return (err);
6967 }
6968 
6969 /*
6970  * Get the full tree of pkg metadata in a set of nested AVL trees.
6971  * pkgs_avl is an AVL tree of pkgs.
6972  *
6973  * The zone xml data contains DTD_ELEM_PACKAGE elements.
6974  */
6975 int
6976 zonecfg_getpkgdata(zone_dochandle_t handle, uu_avl_pool_t *pkg_pool,
6977     uu_avl_t *pkgs_avl)
6978 {
6979         xmlNodePtr cur;
6980         int res;
6981         zone_pkg_entry_t *pkg;
6982         char name[MAXNAMELEN];
6983         char version[ZONE_PKG_VERSMAX];
6984 
6985         if (handle == NULL)
6986                 return (Z_INVAL);
6987 
6988         if ((res = zonecfg_setent(handle)) != Z_OK)
6989                 return (res);
6990 
6991         if ((cur = handle->zone_dh_cur) == NULL) {
6992                 res = Z_NO_ENTRY;
6993                 goto done;
6994         }
6995 
6996         for (; cur != NULL; cur = cur->next) {
6997                 if (xmlStrcmp(cur->name, DTD_ELEM_PACKAGE) == 0) {
6998                         uu_avl_index_t where;
6999 
7000                         if ((res = fetchprop(cur, DTD_ATTR_NAME, name,
7001                             sizeof (name))) != Z_OK)
7002                                 goto done;
7003 
7004                         if ((res = fetchprop(cur, DTD_ATTR_VERSION, version,
7005                             sizeof (version))) != Z_OK)
7006                                 goto done;
7007 
7008                         if ((pkg = (zone_pkg_entry_t *)
7009                             malloc(sizeof (zone_pkg_entry_t))) == NULL) {
7010                                 res = Z_NOMEM;
7011                                 goto done;
7012                         }
7013 
7014                         if ((pkg->zpe_name = strdup(name)) == NULL) {
7015                                 free(pkg);
7016                                 res = Z_NOMEM;
7017                                 goto done;
7018                         }
7019 
7020                         if ((pkg->zpe_vers = strdup(version)) == NULL) {
7021                                 free(pkg->zpe_name);
7022                                 free(pkg);
7023                                 res = Z_NOMEM;
7024                                 goto done;
7025                         }
7026 
7027                         uu_avl_node_init(pkg, &pkg->zpe_entry, pkg_pool);
7028                         if (uu_avl_find(pkgs_avl, pkg, NULL, &where) != NULL) {
7029                                 free(pkg->zpe_name);
7030                                 free(pkg->zpe_vers);
7031                                 free(pkg);
7032                         } else {
7033                                 uu_avl_insert(pkgs_avl, pkg, where);
7034                         }
7035                 }
7036         }
7037 
7038 done:
7039         (void) zonecfg_endent(handle);
7040         return (res);
7041 }
7042 
7043 int
7044 zonecfg_setdevperment(zone_dochandle_t handle)
7045 {
7046         return (zonecfg_setent(handle));
7047 }
7048 
7049 int
7050 zonecfg_getdevperment(zone_dochandle_t handle, struct zone_devpermtab *tabptr)
7051 {
7052         xmlNodePtr cur;
7053         int err;
7054         char buf[128];
7055 
7056         tabptr->zone_devperm_acl = NULL;
7057 
7058         if (handle == NULL)
7059                 return (Z_INVAL);
7060 
7061         if ((cur = handle->zone_dh_cur) == NULL)
7062                 return (Z_NO_ENTRY);
7063 
7064         for (; cur != NULL; cur = cur->next)
7065                 if (!xmlStrcmp(cur->name, DTD_ELEM_DEV_PERM))
7066                         break;
7067         if (cur == NULL) {
7068                 handle->zone_dh_cur = handle->zone_dh_top;
7069                 return (Z_NO_ENTRY);
7070         }
7071 
7072         if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_devperm_name,
7073             sizeof (tabptr->zone_devperm_name))) != Z_OK) {
7074                 handle->zone_dh_cur = handle->zone_dh_top;
7075                 return (err);
7076         }
7077 
7078         if ((err = fetchprop(cur, DTD_ATTR_UID, buf, sizeof (buf))) != Z_OK) {
7079                 handle->zone_dh_cur = handle->zone_dh_top;
7080                 return (err);
7081         }
7082         tabptr->zone_devperm_uid = (uid_t)atol(buf);
7083 
7084         if ((err = fetchprop(cur, DTD_ATTR_GID, buf, sizeof (buf))) != Z_OK) {
7085                 handle->zone_dh_cur = handle->zone_dh_top;
7086                 return (err);
7087         }
7088         tabptr->zone_devperm_gid = (gid_t)atol(buf);
7089 
7090         if ((err = fetchprop(cur, DTD_ATTR_MODE, buf, sizeof (buf))) != Z_OK) {
7091                 handle->zone_dh_cur = handle->zone_dh_top;
7092                 return (err);
7093         }
7094         tabptr->zone_devperm_mode = (mode_t)strtol(buf, (char **)NULL, 8);
7095 
7096         if ((err = fetch_alloc_prop(cur, DTD_ATTR_ACL,
7097             &(tabptr->zone_devperm_acl))) != Z_OK) {
7098                 handle->zone_dh_cur = handle->zone_dh_top;
7099                 return (err);
7100         }
7101 
7102         handle->zone_dh_cur = cur->next;
7103         return (Z_OK);
7104 }
7105 
7106 int
7107 zonecfg_enddevperment(zone_dochandle_t handle)
7108 {
7109         return (zonecfg_endent(handle));
7110 }
7111 
7112 /* PRINTFLIKE1 */
7113 static void
7114 zerror(const char *zone_name, const char *fmt, ...)
7115 {
7116         va_list alist;
7117 
7118         va_start(alist, fmt);
7119         (void) fprintf(stderr, "zone '%s': ", zone_name);
7120         (void) vfprintf(stderr, fmt, alist);
7121         (void) fprintf(stderr, "\n");
7122         va_end(alist);
7123 }
7124 
7125 static void
7126 zperror(const char *str)
7127 {
7128         (void) fprintf(stderr, "%s: %s\n", str, strerror(errno));
7129 }
7130 
7131 /*
7132  * The following three routines implement a simple locking mechanism to
7133  * ensure that only one instance of zoneadm at a time is able to manipulate
7134  * a given zone.  The lock is built on top of an fcntl(2) lock of
7135  * [<altroot>]/var/run/zones/<zonename>.zoneadm.lock.  If a zoneadm instance
7136  * can grab that lock, it is allowed to manipulate the zone.
7137  *
7138  * Since zoneadm may call external applications which in turn invoke
7139  * zoneadm again, we introduce the notion of "lock inheritance".  Any
7140  * instance of zoneadm that has another instance in its ancestry is assumed
7141  * to be acting on behalf of the original zoneadm, and is thus allowed to
7142  * manipulate its zone.
7143  *
7144  * This inheritance is implemented via the _ZONEADM_LOCK_HELD environment
7145  * variable.  When zoneadm is granted a lock on its zone, this environment
7146  * variable is set to 1.  When it releases the lock, the variable is set to
7147  * 0.  Since a child process inherits its parent's environment, checking
7148  * the state of this variable indicates whether or not any ancestor owns
7149  * the lock.
7150  */
7151 void
7152 zonecfg_init_lock_file(const char *zone_name, char **lock_env)
7153 {
7154         *lock_env = getenv(LOCK_ENV_VAR);
7155         if (*lock_env == NULL) {
7156                 if (putenv(zoneadm_lock_not_held) != 0) {
7157                         zerror(zone_name, gettext("could not set env: %s"),
7158                             strerror(errno));
7159                         exit(1);
7160                 }
7161         } else {
7162                 if (atoi(*lock_env) == 1)
7163                         zone_lock_cnt = 1;
7164         }
7165 }
7166 
7167 void
7168 zonecfg_release_lock_file(const char *zone_name, int lockfd)
7169 {
7170         /*
7171          * If we are cleaning up from a failed attempt to lock the zone for
7172          * the first time, we might have a zone_lock_cnt of 0.  In that
7173          * error case, we don't want to do anything but close the lock
7174          * file.
7175          */
7176         assert(zone_lock_cnt >= 0);
7177         if (zone_lock_cnt > 0) {
7178                 assert(getenv(LOCK_ENV_VAR) != NULL);
7179                 assert(atoi(getenv(LOCK_ENV_VAR)) == 1);
7180                 if (--zone_lock_cnt > 0) {
7181                         assert(lockfd == -1);
7182                         return;
7183                 }
7184                 if (putenv(zoneadm_lock_not_held) != 0) {
7185                         zerror(zone_name, gettext("could not set env: %s"),
7186                             strerror(errno));
7187                         exit(1);
7188                 }
7189         }
7190         assert(lockfd >= 0);
7191         (void) close(lockfd);
7192 }
7193 
7194 int
7195 zonecfg_grab_lock_file(const char *zone_name, int *lockfd)
7196 {
7197         char pathbuf[PATH_MAX];
7198         struct flock flock;
7199 
7200         /*
7201          * If we already have the lock, we can skip this expensive song
7202          * and dance.
7203          */
7204         assert(zone_lock_cnt >= 0);
7205         assert(getenv(LOCK_ENV_VAR) != NULL);
7206         if (zone_lock_cnt > 0) {
7207                 assert(atoi(getenv(LOCK_ENV_VAR)) == 1);
7208                 zone_lock_cnt++;
7209                 *lockfd = -1;
7210                 return (Z_OK);
7211         }
7212         assert(getenv(LOCK_ENV_VAR) != NULL);
7213         assert(atoi(getenv(LOCK_ENV_VAR)) == 0);
7214 
7215         if (snprintf(pathbuf, sizeof (pathbuf), "%s%s", zonecfg_get_root(),
7216             ZONES_TMPDIR) >= sizeof (pathbuf)) {
7217                 zerror(zone_name, gettext("alternate root path is too long"));
7218                 return (-1);
7219         }
7220         if (mkdir(pathbuf, S_IRWXU) < 0 && errno != EEXIST) {
7221                 zerror(zone_name, gettext("could not mkdir %s: %s"), pathbuf,
7222                     strerror(errno));
7223                 return (-1);
7224         }
7225         (void) chmod(pathbuf, S_IRWXU);
7226 
7227         /*
7228          * One of these lock files is created for each zone (when needed).
7229          * The lock files are not cleaned up (except on system reboot),
7230          * but since there is only one per zone, there is no resource
7231          * starvation issue.
7232          */
7233         if (snprintf(pathbuf, sizeof (pathbuf), "%s%s/%s.zoneadm.lock",
7234             zonecfg_get_root(), ZONES_TMPDIR, zone_name) >= sizeof (pathbuf)) {
7235                 zerror(zone_name, gettext("alternate root path is too long"));
7236                 return (-1);
7237         }
7238         if ((*lockfd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
7239                 zerror(zone_name, gettext("could not open %s: %s"), pathbuf,
7240                     strerror(errno));
7241                 return (-1);
7242         }
7243         /*
7244          * Lock the file to synchronize with other zoneadmds
7245          */
7246         flock.l_type = F_WRLCK;
7247         flock.l_whence = SEEK_SET;
7248         flock.l_start = (off_t)0;
7249         flock.l_len = (off_t)0;
7250         if ((fcntl(*lockfd, F_SETLKW, &flock) < 0) ||
7251             (putenv(zoneadm_lock_held) != 0)) {
7252                 zerror(zone_name, gettext("unable to lock %s: %s"), pathbuf,
7253                     strerror(errno));
7254                 zonecfg_release_lock_file(zone_name, *lockfd);
7255                 return (-1);
7256         }
7257         zone_lock_cnt = 1;
7258         return (Z_OK);
7259 }
7260 
7261 boolean_t
7262 zonecfg_lock_file_held(int *lockfd)
7263 {
7264         if (*lockfd >= 0 || zone_lock_cnt > 0)
7265                 return (B_TRUE);
7266         return (B_FALSE);
7267 }
7268 
7269 static boolean_t
7270 get_doorname(const char *zone_name, char *buffer)
7271 {
7272         return (snprintf(buffer, PATH_MAX, "%s" ZONE_DOOR_PATH,
7273             zonecfg_get_root(), zone_name) < PATH_MAX);
7274 }
7275 
7276 /*
7277  * system daemons are not audited.  For the global zone, this occurs
7278  * "naturally" since init is started with the default audit
7279  * characteristics.  Since zoneadmd is a system daemon and it starts
7280  * init for a zone, it is necessary to clear out the audit
7281  * characteristics inherited from whomever started zoneadmd.  This is
7282  * indicated by the audit id, which is set from the ruid parameter of
7283  * adt_set_user(), below.
7284  */
7285 
7286 static void
7287 prepare_audit_context(const char *zone_name)
7288 {
7289         adt_session_data_t      *ah;
7290         char                    *failure = gettext("audit failure: %s");
7291 
7292         if (adt_start_session(&ah, NULL, 0)) {
7293                 zerror(zone_name, failure, strerror(errno));
7294                 return;
7295         }
7296         if (adt_set_user(ah, ADT_NO_AUDIT, ADT_NO_AUDIT,
7297             ADT_NO_AUDIT, ADT_NO_AUDIT, NULL, ADT_NEW)) {
7298                 zerror(zone_name, failure, strerror(errno));
7299                 (void) adt_end_session(ah);
7300                 return;
7301         }
7302         if (adt_set_proc(ah))
7303                 zerror(zone_name, failure, strerror(errno));
7304 
7305         (void) adt_end_session(ah);
7306 }
7307 
7308 static int
7309 start_zoneadmd(const char *zone_name, boolean_t lock)
7310 {
7311         char doorpath[PATH_MAX];
7312         pid_t child_pid;
7313         int error = -1;
7314         int doorfd, lockfd;
7315         struct door_info info;
7316 
7317         if (!get_doorname(zone_name, doorpath))
7318                 return (-1);
7319 
7320         if (lock)
7321                 if (zonecfg_grab_lock_file(zone_name, &lockfd) != Z_OK)
7322                         return (-1);
7323 
7324         /*
7325          * Now that we have the lock, re-confirm that the daemon is
7326          * *not* up and working fine.  If it is still down, we have a green
7327          * light to start it.
7328          */
7329         if ((doorfd = open(doorpath, O_RDONLY)) < 0) {
7330                 if (errno != ENOENT) {
7331                         zperror(doorpath);
7332                         goto out;
7333                 }
7334         } else {
7335                 if (door_info(doorfd, &info) == 0 &&
7336                     ((info.di_attributes & DOOR_REVOKED) == 0)) {
7337                         error = Z_OK;
7338                         (void) close(doorfd);
7339                         goto out;
7340                 }
7341                 (void) close(doorfd);
7342         }
7343 
7344         if ((child_pid = fork()) == -1) {
7345                 zperror(gettext("could not fork"));
7346                 goto out;
7347         }
7348 
7349         if (child_pid == 0) {
7350                 const char *argv[6], **ap;
7351 
7352                 /* child process */
7353                 prepare_audit_context(zone_name);
7354 
7355                 ap = argv;
7356                 *ap++ = "zoneadmd";
7357                 *ap++ = "-z";
7358                 *ap++ = zone_name;
7359                 if (zonecfg_in_alt_root()) {
7360                         *ap++ = "-R";
7361                         *ap++ = zonecfg_get_root();
7362                 }
7363                 *ap = NULL;
7364 
7365                 (void) execv("/usr/lib/zones/zoneadmd", (char * const *)argv);
7366                 /*
7367                  * TRANSLATION_NOTE
7368                  * zoneadmd is a literal that should not be translated.
7369                  */
7370                 zperror(gettext("could not exec zoneadmd"));
7371                 _exit(1);
7372         } else {
7373                 /* parent process */
7374                 pid_t retval;
7375                 int pstatus = 0;
7376 
7377                 do {
7378                         retval = waitpid(child_pid, &pstatus, 0);
7379                 } while (retval != child_pid);
7380                 if (WIFSIGNALED(pstatus) || (WIFEXITED(pstatus) &&
7381                     WEXITSTATUS(pstatus) != 0)) {
7382                         zerror(zone_name, gettext("could not start %s"),
7383                             "zoneadmd");
7384                         goto out;
7385                 }
7386         }
7387         error = Z_OK;
7388 out:
7389         if (lock)
7390                 zonecfg_release_lock_file(zone_name, lockfd);
7391         return (error);
7392 }
7393 
7394 int
7395 zonecfg_ping_zoneadmd(const char *zone_name)
7396 {
7397         char doorpath[PATH_MAX];
7398         int doorfd;
7399         struct door_info info;
7400 
7401         if (!get_doorname(zone_name, doorpath))
7402                 return (-1);
7403 
7404         if ((doorfd = open(doorpath, O_RDONLY)) < 0) {
7405                 return (-1);
7406         }
7407         if (door_info(doorfd, &info) == 0 &&
7408             ((info.di_attributes & DOOR_REVOKED) == 0)) {
7409                 (void) close(doorfd);
7410                 return (Z_OK);
7411         }
7412         (void) close(doorfd);
7413         return (-1);
7414 }
7415 
7416 int
7417 zonecfg_call_zoneadmd(const char *zone_name, zone_cmd_arg_t *arg, char *locale,
7418     boolean_t lock)
7419 {
7420         char doorpath[PATH_MAX];
7421         int doorfd, result;
7422         door_arg_t darg;
7423 
7424         zoneid_t zoneid;
7425         uint64_t uniqid = 0;
7426 
7427         zone_cmd_rval_t *rvalp;
7428         size_t rlen;
7429         char *cp, *errbuf;
7430 
7431         rlen = getpagesize();
7432         if ((rvalp = malloc(rlen)) == NULL) {
7433                 zerror(zone_name, gettext("failed to allocate %lu bytes: %s"),
7434                     rlen, strerror(errno));
7435                 return (-1);
7436         }
7437 
7438         if ((zoneid = getzoneidbyname(zone_name)) != ZONE_ID_UNDEFINED) {
7439                 (void) zone_getattr(zoneid, ZONE_ATTR_UNIQID, &uniqid,
7440                     sizeof (uniqid));
7441         }
7442         arg->uniqid = uniqid;
7443         (void) strlcpy(arg->locale, locale, sizeof (arg->locale));
7444         if (!get_doorname(zone_name, doorpath)) {
7445                 zerror(zone_name, gettext("alternate root path is too long"));
7446                 free(rvalp);
7447                 return (-1);
7448         }
7449 
7450         /*
7451          * Loop trying to start zoneadmd; if something goes seriously
7452          * wrong we break out and fail.
7453          */
7454         for (;;) {
7455                 if (start_zoneadmd(zone_name, lock) != Z_OK)
7456                         break;
7457 
7458                 if ((doorfd = open(doorpath, O_RDONLY)) < 0) {
7459                         zperror(gettext("failed to open zone door"));
7460                         break;
7461                 }
7462 
7463                 darg.data_ptr = (char *)arg;
7464                 darg.data_size = sizeof (*arg);
7465                 darg.desc_ptr = NULL;
7466                 darg.desc_num = 0;
7467                 darg.rbuf = (char *)rvalp;
7468                 darg.rsize = rlen;
7469                 if (door_call(doorfd, &darg) != 0) {
7470                         (void) close(doorfd);
7471                         /*
7472                          * We'll get EBADF if the door has been revoked.
7473                          */
7474                         if (errno != EBADF) {
7475                                 zperror(gettext("door_call failed"));
7476                                 break;
7477                         }
7478                         continue;       /* take another lap */
7479                 }
7480                 (void) close(doorfd);
7481 
7482                 if (darg.data_size == 0) {
7483                         /* Door server is going away; kick it again. */
7484                         continue;
7485                 }
7486 
7487                 errbuf = rvalp->errbuf;
7488                 while (*errbuf != '\0') {
7489                         /*
7490                          * Remove any newlines since zerror()
7491                          * will append one automatically.
7492                          */
7493                         cp = strchr(errbuf, '\n');
7494                         if (cp != NULL)
7495                                 *cp = '\0';
7496                         zerror(zone_name, "%s", errbuf);
7497                         if (cp == NULL)
7498                                 break;
7499                         errbuf = cp + 1;
7500                 }
7501                 result = rvalp->rval == 0 ? 0 : -1;
7502                 free(rvalp);
7503                 return (result);
7504         }
7505 
7506         free(rvalp);
7507         return (-1);
7508 }
7509 
7510 boolean_t
7511 zonecfg_valid_auths(const char *auths, const char *zonename)
7512 {
7513         char *right;
7514         char *tmpauths;
7515         char *lasts;
7516         char authname[MAXAUTHS];
7517         boolean_t status = B_TRUE;
7518 
7519         tmpauths = strdup(auths);
7520         if (tmpauths == NULL) {
7521                 zerror(zonename, gettext("Out of memory"));
7522                 return (B_FALSE);
7523         }
7524         right = strtok_r(tmpauths, ",", &lasts);
7525         while (right != NULL) {
7526                 (void) snprintf(authname, MAXAUTHS, "%s%s",
7527                     ZONE_AUTH_PREFIX, right);
7528                 if (getauthnam(authname) == NULL) {
7529                         status = B_FALSE;
7530                         zerror(zonename,
7531                             gettext("'%s' is not a valid authorization"),
7532                             right);
7533                 }
7534                 right = strtok_r(NULL, ",", &lasts);
7535         }
7536         free(tmpauths);
7537         return (status);
7538 }
7539 
7540 int
7541 zonecfg_delete_admins(zone_dochandle_t handle, char *zonename)
7542 {
7543         int err;
7544         struct zone_admintab admintab;
7545         boolean_t changed = B_FALSE;
7546 
7547         if ((err = zonecfg_setadminent(handle)) != Z_OK) {
7548                 return (err);
7549         }
7550         while (zonecfg_getadminent(handle, &admintab) == Z_OK) {
7551                 err = zonecfg_delete_admin(handle, &admintab,
7552                     zonename);
7553                 if (err != Z_OK) {
7554                         (void) zonecfg_endadminent(handle);
7555                         return (err);
7556                 } else {
7557                         changed = B_TRUE;
7558                 }
7559                 if ((err = zonecfg_setadminent(handle)) != Z_OK) {
7560                         return (err);
7561                 }
7562         }
7563         (void) zonecfg_endadminent(handle);
7564         return (changed? Z_OK:Z_NO_ENTRY);
7565 }
7566 
7567 /*
7568  * Checks if a long authorization applies to this zone.
7569  * If so, it returns true, after destructively stripping
7570  * the authorization of its prefix and zone suffix.
7571  */
7572 static boolean_t
7573 is_zone_auth(char **auth, char *zonename, char *oldzonename)
7574 {
7575         char *suffix;
7576         size_t offset;
7577 
7578         offset = strlen(ZONE_AUTH_PREFIX);
7579         if ((strncmp(*auth, ZONE_AUTH_PREFIX, offset) == 0) &&
7580             ((suffix = strchr(*auth, '/')) != NULL)) {
7581                 if (strcmp(suffix + 1, zonename) == 0) {
7582                         *auth += offset;
7583                         suffix[0] = '\0';
7584                         return (B_TRUE);
7585                 } else if ((oldzonename != NULL) &&
7586                     (strcmp(suffix + 1, oldzonename) == 0)) {
7587                         *auth += offset;
7588                         suffix[0] = '\0';
7589                         return (B_TRUE);
7590                 }
7591         }
7592         return (B_FALSE);
7593 }
7594 
7595 /*
7596  * This function determines whether the zone-specific authorization
7597  * assignments in /etc/user_attr have been changed more recently
7598  * than the equivalent data stored in the zone's configuration file.
7599  * This should only happen if the zone-specific authorizations in
7600  * the user_attr file were modified using a tool other than zonecfg.
7601  * If the configuration file is out-of-date with respect to these
7602  * authorization assignments, it is updated to match those specified
7603  * in /etc/user_attr.
7604  */
7605 
7606 int
7607 zonecfg_update_userauths(zone_dochandle_t handle, char *zonename)
7608 {
7609         userattr_t *ua_ptr;
7610         char *authlist;
7611         char *lasts;
7612         FILE  *uaf;
7613         struct zone_admintab admintab;
7614         struct stat config_st, ua_st;
7615         char config_file[MAXPATHLEN];
7616         boolean_t changed = B_FALSE;
7617         int err;
7618 
7619         if ((uaf = fopen(USERATTR_FILENAME, "r")) == NULL) {
7620                 zerror(zonename, gettext("could not open file %s: %s"),
7621                     USERATTR_FILENAME, strerror(errno));
7622                 if (errno == EACCES)
7623                         return (Z_ACCES);
7624                 if (errno == ENOENT)
7625                         return (Z_NO_ZONE);
7626                 return (Z_MISC_FS);
7627         }
7628         if ((err = fstat(fileno(uaf), &ua_st)) != 0) {
7629                 zerror(zonename, gettext("could not stat file %s: %s"),
7630                     USERATTR_FILENAME, strerror(errno));
7631                 (void) fclose(uaf);
7632                 return (Z_MISC_FS);
7633         }
7634         if (!config_file_path(zonename, config_file)) {
7635                 (void) fclose(uaf);
7636                 return (Z_MISC_FS);
7637         }
7638 
7639         if ((err = stat(config_file, &config_st)) != 0) {
7640                 zerror(zonename, gettext("could not stat file %s: %s"),
7641                     config_file, strerror(errno));
7642                 (void) fclose(uaf);
7643                 return (Z_MISC_FS);
7644         }
7645         if (config_st.st_mtime >= ua_st.st_mtime) {
7646                 (void) fclose(uaf);
7647                 return (Z_NO_ENTRY);
7648         }
7649         if ((err = zonecfg_delete_admins(handle, zonename)) == Z_OK) {
7650                 changed = B_TRUE;
7651         } else if (err != Z_NO_ENTRY) {
7652                 (void) fclose(uaf);
7653                 return (err);
7654         }
7655         while ((ua_ptr = fgetuserattr(uaf)) != NULL) {
7656                 if (ua_ptr->name[0] == '#') {
7657                         continue;
7658                 }
7659                 authlist = kva_match(ua_ptr->attr, USERATTR_AUTHS_KW);
7660                 if (authlist != NULL) {
7661                         char *cur_auth;
7662                         boolean_t first;
7663 
7664                         first = B_TRUE;
7665                         bzero(&admintab.zone_admin_auths, MAXAUTHS);
7666                         cur_auth = strtok_r(authlist, ",", &lasts);
7667                         while (cur_auth != NULL) {
7668                                 if (is_zone_auth(&cur_auth, zonename,
7669                                     NULL)) {
7670                                         /*
7671                                          * Add auths for this zone
7672                                          */
7673                                         if (first) {
7674                                                 first = B_FALSE;
7675                                         } else {
7676                                                 (void) strlcat(
7677                                                     admintab.zone_admin_auths,
7678                                                     ",", MAXAUTHS);
7679                                         }
7680                                         (void) strlcat(
7681                                             admintab.zone_admin_auths,
7682                                             cur_auth, MAXAUTHS);
7683                                 }
7684                                 cur_auth = strtok_r(NULL, ",", &lasts);
7685                         }
7686                         if (!first) {
7687                                 /*
7688                                  * Add this right to config file
7689                                  */
7690                                 (void) strlcpy(admintab.zone_admin_user,
7691                                     ua_ptr->name,
7692                                     sizeof (admintab.zone_admin_user));
7693                                 err = zonecfg_add_admin(handle,
7694                                     &admintab, zonename);
7695                                 if (err != Z_OK) {
7696                                         (void) fclose(uaf);
7697                                         return (err);
7698                                 } else {
7699                                         changed = B_TRUE;
7700                                 }
7701                         }
7702                 }
7703         } /* end-of-while-loop */
7704         (void) fclose(uaf);
7705         return (changed? Z_OK: Z_NO_ENTRY);
7706 }
7707 
7708 static void
7709 update_profiles(char *rbac_profs, boolean_t add)
7710 {
7711         char new_profs[MAXPROFS];
7712         char *cur_prof;
7713         boolean_t first = B_TRUE;
7714         boolean_t found = B_FALSE;
7715         char *lasts;
7716 
7717         cur_prof = strtok_r(rbac_profs, ",", &lasts);
7718         while (cur_prof != NULL) {
7719                 if (strcmp(cur_prof, ZONE_MGMT_PROF) == 0) {
7720                         found = B_TRUE;
7721                         if (!add) {
7722                                 cur_prof = strtok_r(NULL, ",", &lasts);
7723                                 continue;
7724                         }
7725                 }
7726                 if (first) {
7727                         first = B_FALSE;
7728                 } else {
7729                         (void) strlcat(new_profs, ",",
7730                             MAXPROFS);
7731                 }
7732                 (void) strlcat(new_profs, cur_prof,
7733                     MAXPROFS);
7734                 cur_prof = strtok_r(NULL, ",", &lasts);
7735         }
7736         /*
7737          * Now prepend the Zone Management profile at the beginning
7738          * of the list if it is needed, and append the rest.
7739          * Return the updated list in the original buffer.
7740          */
7741         if (add && !found) {
7742                 first = B_FALSE;
7743                 (void) strlcpy(rbac_profs, ZONE_MGMT_PROF, MAXPROFS);
7744         } else {
7745                 first = B_TRUE;
7746                 rbac_profs[0] = '\0';
7747         }
7748         if (strlen(new_profs) > 0) {
7749                 if (!first)
7750                         (void) strlcat(rbac_profs, ",", MAXPROFS);
7751                 (void) strlcat(rbac_profs, new_profs, MAXPROFS);
7752         }
7753 }
7754 
7755 #define MAX_CMD_LEN     1024
7756 
7757 static int
7758 do_subproc(char *zonename, char *cmdbuf)
7759 {
7760         char inbuf[MAX_CMD_LEN];
7761         FILE *file;
7762         int status;
7763 
7764         file = popen(cmdbuf, "r");
7765         if (file == NULL) {
7766                 zerror(zonename, gettext("Could not launch: %s"), cmdbuf);
7767                 return (-1);
7768         }
7769 
7770         while (fgets(inbuf, sizeof (inbuf), file) != NULL)
7771                 (void) fprintf(stderr, "%s", inbuf);
7772         status = pclose(file);
7773 
7774         if (WIFSIGNALED(status)) {
7775                 zerror(zonename, gettext("%s unexpectedly terminated "
7776                     "due to signal %d"),
7777                     cmdbuf, WTERMSIG(status));
7778                 return (-1);
7779         }
7780         assert(WIFEXITED(status));
7781         return (WEXITSTATUS(status));
7782 }
7783 
7784 /*
7785  * This function updates the local /etc/user_attr file to
7786  * correspond to the admin settings that are currently being
7787  * committed. The updates are done via usermod and/or rolemod
7788  * depending on the type of the specified user. It is also
7789  * invoked to remove entries from user_attr corresponding to
7790  * removed admin assignments, using an empty auths string.
7791  *
7792  * Because the removed entries are no longer included in the
7793  * cofiguration that is being committed, a linked list of
7794  * removed admin entries is maintained to keep track of such
7795  * transactions. The head of the list is stored in the zone_dh_userauths
7796  * element of the handle strcture.
7797  */
7798 static int
7799 zonecfg_authorize_user_impl(zone_dochandle_t handle, char *user,
7800     char *auths, char *zonename)
7801 {
7802         char *right;
7803         char old_auths[MAXAUTHS];
7804         char new_auths[MAXAUTHS];
7805         char rbac_profs[MAXPROFS];
7806         char *lasts;
7807         userattr_t *u;
7808         boolean_t first = B_TRUE;
7809         boolean_t is_zone_admin = B_FALSE;
7810         char user_cmd[] = "/usr/sbin/usermod";
7811         char role_cmd[] = "/usr/sbin/rolemod";
7812         char *auths_cmd = user_cmd;     /* either usermod or rolemod */
7813         char *new_auth_start;           /* string containing the new auths */
7814         int new_auth_cnt = 0;           /* delta of changed authorizations */
7815 
7816         /*
7817          * First get the existing authorizations for this user
7818          */
7819 
7820         bzero(&old_auths, sizeof (old_auths));
7821         bzero(&new_auths, sizeof (new_auths));
7822         bzero(&rbac_profs, sizeof (rbac_profs));
7823         if ((u = getusernam(user)) != NULL) {
7824                 char *current_auths;
7825                 char *current_profs;
7826                 char *type;
7827 
7828                 type = kva_match(u->attr, USERATTR_TYPE_KW);
7829                 if (type != NULL) {
7830                         if (strcmp(type, USERATTR_TYPE_NONADMIN_KW) == 0)
7831                                 auths_cmd = role_cmd;
7832                 }
7833 
7834                 current_auths = kva_match(u->attr, USERATTR_AUTHS_KW);
7835                 if (current_auths != NULL) {
7836                         char *cur_auth;
7837                         char *delete_name;
7838                         size_t offset;
7839 
7840                         offset = strlen(ZONE_AUTH_PREFIX);
7841 
7842                         (void) strlcpy(old_auths, current_auths, MAXAUTHS);
7843                         cur_auth = strtok_r(current_auths, ",", &lasts);
7844 
7845                         /*
7846                          * Next, remove any existing authorizations
7847                          * for this zone, and determine if the
7848                          * user still needs the Zone Management Profile.
7849                          */
7850                         if (is_renaming(handle))
7851                                 delete_name = handle->zone_dh_delete_name;
7852                         else
7853                                 delete_name = NULL;
7854                         while (cur_auth != NULL) {
7855                                 if (!is_zone_auth(&cur_auth, zonename,
7856                                     delete_name)) {
7857                                         if (first) {
7858                                                 first = B_FALSE;
7859                                         } else {
7860                                                 (void) strlcat(new_auths, ",",
7861                                                     MAXAUTHS);
7862                                         }
7863                                         (void) strlcat(new_auths, cur_auth,
7864                                             MAXAUTHS);
7865                                         /*
7866                                          * If the user has authorizations
7867                                          * for other zones, then set a
7868                                          * flag indicate that the Zone
7869                                          * Management profile should be
7870                                          * preserved in user_attr.
7871                                          */
7872                                         if (strncmp(cur_auth,
7873                                             ZONE_AUTH_PREFIX, offset) == 0)
7874                                                 is_zone_admin = B_TRUE;
7875                                 } else {
7876                                         new_auth_cnt++;
7877                                 }
7878                                 cur_auth = strtok_r(NULL, ",", &lasts);
7879                         }
7880                 }
7881                 current_profs = kva_match(u->attr, USERATTR_PROFILES_KW);
7882                 if (current_profs != NULL) {
7883                         (void) strlcpy(rbac_profs, current_profs, MAXPROFS);
7884                 }
7885                 free_userattr(u);
7886         }
7887         /*
7888          * The following is done to avoid revisiting the
7889          * user_attr entry for this user
7890          */
7891         (void) zonecfg_remove_userauths(handle, user, "", B_FALSE);
7892 
7893         /*
7894          * Convert each right into a properly formatted authorization
7895          */
7896         new_auth_start = new_auths + strlen(new_auths);
7897         if (!first)
7898                 new_auth_start++;
7899         right = strtok_r(auths, ",", &lasts);
7900         while (right != NULL) {
7901                 char auth[MAXAUTHS];
7902 
7903                 (void) snprintf(auth, MAXAUTHS, "%s%s/%s",
7904                     ZONE_AUTH_PREFIX, right, zonename);
7905                 if (first) {
7906                         first = B_FALSE;
7907                 } else {
7908                         (void) strlcat(new_auths, ",", MAXAUTHS);
7909                 }
7910                 (void) strlcat(new_auths, auth, MAXAUTHS);
7911                 is_zone_admin = B_TRUE;
7912                 new_auth_cnt--;
7913                 right = strtok_r(NULL, ",", &lasts);
7914         }
7915 
7916         /*
7917          * Need to update the authorizations in user_attr unless
7918          * the number of old and new authorizations is unchanged
7919          * and the new auths are a substrings of the old auths.
7920          *
7921          * If the user's previous authorizations have changed
7922          * execute the usermod progam to update them in user_attr.
7923          */
7924         if ((new_auth_cnt != 0) ||
7925             (strstr(old_auths, new_auth_start) == NULL)) {
7926                 char    *cmdbuf;
7927                 size_t  cmd_len;
7928 
7929                 update_profiles(rbac_profs, is_zone_admin);
7930                 cmd_len = snprintf(NULL, 0, "%s -A \"%s\" -P \"%s\" %s",
7931                     auths_cmd, new_auths, rbac_profs, user) + 1;
7932                 if ((cmdbuf = malloc(cmd_len)) == NULL) {
7933                         return (Z_NOMEM);
7934                 }
7935                 (void) snprintf(cmdbuf, cmd_len, "%s -A \"%s\" -P \"%s\" %s",
7936                     auths_cmd, new_auths, rbac_profs, user);
7937                 if (do_subproc(zonename, cmdbuf) != 0) {
7938                         free(cmdbuf);
7939                         return (Z_SYSTEM);
7940                 }
7941                 free(cmdbuf);
7942         }
7943 
7944         return (Z_OK);
7945 }
7946 
7947 int
7948 zonecfg_authorize_users(zone_dochandle_t handle, char *zonename)
7949 {
7950         xmlNodePtr cur;
7951         int err;
7952         char user[MAXUSERNAME];
7953         char auths[MAXAUTHS];
7954 
7955         if ((err = operation_prep(handle)) != Z_OK)
7956                 return (err);
7957 
7958         cur = handle->zone_dh_cur;
7959         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
7960                 if (xmlStrcmp(cur->name, DTD_ELEM_ADMIN))
7961                         continue;
7962                 if (fetchprop(cur, DTD_ATTR_USER, user,
7963                     sizeof (user)) != Z_OK)
7964                         continue;
7965                 if (fetchprop(cur, DTD_ATTR_AUTHS, auths,
7966                     sizeof (auths)) != Z_OK)
7967                         continue;
7968                 if (zonecfg_authorize_user_impl(handle, user, auths, zonename)
7969                     != Z_OK)
7970                         return (Z_SYSTEM);
7971         }
7972         (void) zonecfg_remove_userauths(handle, "", "", B_TRUE);
7973 
7974         return (Z_OK);
7975 }
7976 
7977 int
7978 zonecfg_deauthorize_user(zone_dochandle_t handle, char *user, char *zonename)
7979 {
7980         return (zonecfg_authorize_user_impl(handle, user, "", zonename));
7981 }
7982 
7983 int
7984 zonecfg_deauthorize_users(zone_dochandle_t handle, char *zonename)
7985 {
7986         xmlNodePtr cur;
7987         int err;
7988         char user[MAXUSERNAME];
7989 
7990         if ((err = operation_prep(handle)) != Z_OK)
7991                 return (err);
7992 
7993         cur = handle->zone_dh_cur;
7994         for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
7995                 if (xmlStrcmp(cur->name, DTD_ELEM_ADMIN))
7996                         continue;
7997                 if (fetchprop(cur, DTD_ATTR_USER, user,
7998                     sizeof (user)) != Z_OK)
7999                         continue;
8000                 if ((err = zonecfg_deauthorize_user(handle, user,
8001                     zonename)) != Z_OK)
8002                         return (err);
8003         }
8004         return (Z_OK);
8005 }
8006 
8007 int
8008 zonecfg_insert_userauths(zone_dochandle_t handle, char *user, char *zonename)
8009 {
8010         zone_userauths_t *new, **prev, *next;
8011 
8012         prev = &handle->zone_dh_userauths;
8013         next = *prev;
8014         while (next) {
8015                 if ((strncmp(next->user, user, MAXUSERNAME) == 0) &&
8016                     (strncmp(next->zonename, zonename,
8017                     ZONENAME_MAX) == 0)) {
8018                         /*
8019                          * user is already in list
8020                          * which isn't supposed to happen!
8021                          */
8022                         return (Z_OK);
8023                 }
8024                 prev = &next->next;
8025                 next = *prev;
8026         }
8027         new = (zone_userauths_t *)malloc(sizeof (zone_userauths_t));
8028         if (new == NULL)
8029                 return (Z_NOMEM);
8030 
8031         (void) strlcpy(new->user, user, sizeof (new->user));
8032         (void) strlcpy(new->zonename, zonename, sizeof (new->zonename));
8033         new->next = NULL;
8034         *prev = new;
8035         return (Z_OK);
8036 }
8037 
8038 int
8039 zonecfg_remove_userauths(zone_dochandle_t handle, char *user, char *zonename,
8040         boolean_t deauthorize)
8041 {
8042         zone_userauths_t *new, **prev, *next;
8043 
8044         prev = &handle->zone_dh_userauths;
8045         next = *prev;
8046 
8047         while (next) {
8048                 if ((strlen(user) == 0 ||
8049                     strncmp(next->user, user, MAXUSERNAME) == 0) &&
8050                     (strlen(zonename) == 0 ||
8051                     (strncmp(next->zonename, zonename, ZONENAME_MAX) == 0))) {
8052                         new = next;
8053                         *prev = next->next;
8054                         next =  *prev;
8055                         if (deauthorize)
8056                                 (void) zonecfg_deauthorize_user(handle,
8057                                     new->user, new->zonename);
8058                         free(new);
8059                         continue;
8060                 }
8061                 prev = &next->next;
8062                 next = *prev;
8063         }
8064         return (Z_OK);
8065 }
--- EOF ---