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  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
  24  */
  25 
  26 
  27 /*
  28  * Config routines common to idmap(1M) and idmapd(1M)
  29  */
  30 
  31 #include <stdlib.h>
  32 #include <strings.h>
  33 #include <libintl.h>
  34 #include <ctype.h>
  35 #include <errno.h>
  36 #include <stdio.h>
  37 #include <stdarg.h>
  38 #include <uuid/uuid.h>
  39 #include <pthread.h>
  40 #include <port.h>
  41 #include <sys/socket.h>
  42 #include <net/route.h>
  43 #include <sys/u8_textprep.h>
  44 #include <netinet/in.h>
  45 #include <arpa/inet.h>
  46 #include <netdb.h>
  47 #include <note.h>
  48 #include "idmapd.h"
  49 #include "addisc.h"
  50 
  51 #define MACHINE_SID_LEN         (9 + 3 * 11)
  52 #define FMRI_BASE               "svc:/system/idmap"
  53 #define CONFIG_PG               "config"
  54 #define DEBUG_PG                "debug"
  55 #define RECONFIGURE             1
  56 #define POKE_AUTO_DISCOVERY     2
  57 #define KICK_AUTO_DISCOVERY     3
  58 
  59 /*
  60  * Default cache timeouts.  Can override via svccfg
  61  * config/id_cache_timeout = count: seconds
  62  * config/name_cache_timeout = count: seconds
  63  */
  64 #define ID_CACHE_TMO_DEFAULT    86400
  65 #define NAME_CACHE_TMO_DEFAULT  604800
  66 
  67 /*
  68  * Default maximum time between rediscovery runs.
  69  * config/rediscovery_interval = count: seconds
  70  */
  71 #define REDISCOVERY_INTERVAL_DEFAULT    3600
  72 
  73 /*
  74  * Mininum time between rediscovery runs, in case adutils gives us a
  75  * really short TTL (which it never should, but be defensive)
  76  * (not configurable) seconds.
  77  */
  78 #define MIN_REDISCOVERY_INTERVAL        60
  79 
  80 enum event_type {
  81         EVENT_NOTHING,  /* Woke up for no good reason */
  82         EVENT_TIMEOUT,  /* Timeout expired */
  83         EVENT_ROUTING,  /* An interesting routing event happened */
  84         EVENT_POKED,    /* Requested from degrade_svc() */
  85         EVENT_KICKED,   /* Force rediscovery, i.e. DC failed. */
  86         EVENT_REFRESH,  /* SMF refresh */
  87 };
  88 
  89 
  90 static void idmapd_set_krb5_realm(char *);
  91 
  92 static pthread_t update_thread_handle = 0;
  93 
  94 static int idmapd_ev_port = -1;
  95 static int rt_sock = -1;
  96 
  97 struct enum_lookup_map directory_mapping_map[] = {
  98         { DIRECTORY_MAPPING_NONE, "none" },
  99         { DIRECTORY_MAPPING_NAME, "name" },
 100         { DIRECTORY_MAPPING_IDMU, "idmu" },
 101         { 0, NULL },
 102 };
 103 
 104 struct enum_lookup_map trust_dir_map[] = {
 105         { 1, "they trust us" },
 106         { 2, "we trust them" },
 107         { 3, "we trust each other" },
 108         { 0, NULL },
 109 };
 110 
 111 static int
 112 generate_machine_uuid(char **machine_uuid)
 113 {
 114         uuid_t uu;
 115 
 116         *machine_uuid = calloc(1, UUID_PRINTABLE_STRING_LENGTH + 1);
 117         if (*machine_uuid == NULL) {
 118                 idmapdlog(LOG_ERR, "Out of memory");
 119                 return (-1);
 120         }
 121 
 122         uuid_clear(uu);
 123         uuid_generate_time(uu);
 124         uuid_unparse(uu, *machine_uuid);
 125 
 126         return (0);
 127 }
 128 
 129 static int
 130 generate_machine_sid(char **machine_sid, char *machine_uuid)
 131 {
 132         union {
 133                 uuid_t uu;
 134                 uint32_t v[4];
 135         } uv;
 136         int len;
 137 
 138         /*
 139          * Split the 128-bit machine UUID into three 32-bit values
 140          * we'll use as the "sub-authorities" of the machine SID.
 141          * The machine_sid will have the form S-1-5-21-J-K-L
 142          * (that's four sub-authorities altogether) where:
 143          *      J = last 4 bytes of node_addr,
 144          *      K = time_mid, time_hi_and_version
 145          *      L = time_low
 146          * (see struct uuid)
 147          */
 148 
 149         (void) memset(&uv, 0, sizeof (uv));
 150         (void) uuid_parse(machine_uuid, uv.uu);
 151 
 152         len = asprintf(machine_sid, "S-1-5-21-%u-%u-%u",
 153             uv.v[3], uv.v[0], uv.v[1]);
 154 
 155         if (len == -1 || *machine_sid == NULL) {
 156                 idmapdlog(LOG_ERR, "Out of memory");
 157                 return (-1);
 158         }
 159 
 160         return (0);
 161 }
 162 
 163 
 164 /* In the case of error, exists is set to FALSE anyway */
 165 static int
 166 prop_exists(idmap_cfg_handles_t *handles, const char *name, boolean_t *exists)
 167 {
 168 
 169         scf_property_t *scf_prop;
 170 
 171         *exists = B_FALSE;
 172 
 173         scf_prop = scf_property_create(handles->main);
 174         if (scf_prop == NULL) {
 175                 idmapdlog(LOG_ERR, "scf_property_create() failed: %s",
 176                     scf_strerror(scf_error()));
 177                 return (-1);
 178         }
 179 
 180         if (scf_pg_get_property(handles->config_pg, name, scf_prop) == 0)
 181                 *exists = B_TRUE;
 182 
 183         scf_property_destroy(scf_prop);
 184 
 185         return (0);
 186 }
 187 
 188 static int
 189 get_debug(idmap_cfg_handles_t *handles, const char *name)
 190 {
 191         int64_t i64 = 0;
 192 
 193         scf_property_t *scf_prop;
 194         scf_value_t *value;
 195 
 196         scf_prop = scf_property_create(handles->main);
 197         if (scf_prop == NULL) {
 198                 idmapdlog(LOG_ERR, "scf_property_create() failed: %s",
 199                     scf_strerror(scf_error()));
 200                 abort();
 201         }
 202         value = scf_value_create(handles->main);
 203         if (value == NULL) {
 204                 idmapdlog(LOG_ERR, "scf_value_create() failed: %s",
 205                     scf_strerror(scf_error()));
 206                 abort();
 207         }
 208 
 209         if (scf_pg_get_property(handles->debug_pg, name, scf_prop) < 0) {
 210                 /* this is OK: the property is just undefined */
 211                 goto destruction;
 212         }
 213 
 214 
 215         if (scf_property_get_value(scf_prop, value) < 0) {
 216                 /* It is still OK when a property doesn't have any value */
 217                 goto destruction;
 218         }
 219 
 220         if (scf_value_get_integer(value, &i64) != 0) {
 221                 idmapdlog(LOG_ERR, "Can not retrieve %s/%s:  %s",
 222                     DEBUG_PG, name, scf_strerror(scf_error()));
 223                 abort();
 224         }
 225 
 226 destruction:
 227         scf_value_destroy(value);
 228         scf_property_destroy(scf_prop);
 229 
 230         return ((int)i64);
 231 }
 232 
 233 static int
 234 get_val_bool(idmap_cfg_handles_t *handles, const char *name,
 235         boolean_t *val, boolean_t default_val)
 236 {
 237         int rc = 0;
 238 
 239         scf_property_t *scf_prop;
 240         scf_value_t *value;
 241 
 242         *val = default_val;
 243 
 244         scf_prop = scf_property_create(handles->main);
 245         if (scf_prop == NULL) {
 246                 idmapdlog(LOG_ERR, "scf_property_create() failed: %s",
 247                     scf_strerror(scf_error()));
 248                 return (-1);
 249         }
 250         value = scf_value_create(handles->main);
 251         if (value == NULL) {
 252                 idmapdlog(LOG_ERR, "scf_value_create() failed: %s",
 253                     scf_strerror(scf_error()));
 254                 scf_property_destroy(scf_prop);
 255                 return (-1);
 256         }
 257 
 258         /* It is OK if the property is undefined */
 259         if (scf_pg_get_property(handles->config_pg, name, scf_prop) < 0)
 260                 goto destruction;
 261 
 262 
 263         /* It is still OK when a property doesn't have any value */
 264         if (scf_property_get_value(scf_prop, value) < 0)
 265                 goto destruction;
 266 
 267         uint8_t b;
 268         rc = scf_value_get_boolean(value, &b);
 269 
 270         if (rc == 0)
 271                 *val = (boolean_t)b;
 272 
 273 destruction:
 274         scf_value_destroy(value);
 275         scf_property_destroy(scf_prop);
 276 
 277         return (rc);
 278 }
 279 
 280 static int
 281 get_val_int(idmap_cfg_handles_t *handles, const char *name,
 282         void *val, scf_type_t type)
 283 {
 284         int rc = 0;
 285 
 286         scf_property_t *scf_prop;
 287         scf_value_t *value;
 288 
 289         switch (type) {
 290         case SCF_TYPE_COUNT:
 291                 *(uint64_t *)val = 0;
 292                 break;
 293         case SCF_TYPE_INTEGER:
 294                 *(int64_t *)val = 0;
 295                 break;
 296         default:
 297                 idmapdlog(LOG_ERR, "Invalid scf integer type (%d)",
 298                     type);
 299                 abort();
 300         }
 301 
 302         scf_prop = scf_property_create(handles->main);
 303         if (scf_prop == NULL) {
 304                 idmapdlog(LOG_ERR, "scf_property_create() failed: %s",
 305                     scf_strerror(scf_error()));
 306                 return (-1);
 307         }
 308         value = scf_value_create(handles->main);
 309         if (value == NULL) {
 310                 idmapdlog(LOG_ERR, "scf_value_create() failed: %s",
 311                     scf_strerror(scf_error()));
 312                 scf_property_destroy(scf_prop);
 313                 return (-1);
 314         }
 315 
 316         if (scf_pg_get_property(handles->config_pg, name, scf_prop) < 0)
 317         /* this is OK: the property is just undefined */
 318                 goto destruction;
 319 
 320 
 321         if (scf_property_get_value(scf_prop, value) < 0)
 322         /* It is still OK when a property doesn't have any value */
 323                 goto destruction;
 324 
 325         switch (type) {
 326         case SCF_TYPE_COUNT:
 327                 rc = scf_value_get_count(value, val);
 328                 break;
 329         case SCF_TYPE_INTEGER:
 330                 rc = scf_value_get_integer(value, val);
 331                 break;
 332         default:
 333                 abort();        /* tested above */
 334                 /* NOTREACHED */
 335         }
 336 
 337         if (rc != 0) {
 338                 idmapdlog(LOG_ERR, "Can not retrieve config/%s:  %s",
 339                     name, scf_strerror(scf_error()));
 340         }
 341 
 342 destruction:
 343         scf_value_destroy(value);
 344         scf_property_destroy(scf_prop);
 345 
 346         return (rc);
 347 }
 348 
 349 static char *
 350 scf_value2string(const char *name, scf_value_t *value)
 351 {
 352         static size_t max_val = 0;
 353 
 354         if (max_val == 0)
 355                 max_val = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
 356 
 357         char buf[max_val + 1];
 358         if (scf_value_get_astring(value, buf, max_val + 1) < 0) {
 359                 idmapdlog(LOG_ERR, "Can not retrieve config/%s:  %s",
 360                     name, scf_strerror(scf_error()));
 361                 return (NULL);
 362         }
 363 
 364         char *s = strdup(buf);
 365         if (s == NULL)
 366                 idmapdlog(LOG_ERR, "Out of memory");
 367 
 368         return (s);
 369 }
 370 
 371 static int
 372 get_val_ds(idmap_cfg_handles_t *handles, const char *name, int defport,
 373                 ad_disc_ds_t **val)
 374 {
 375         char port_str[8];
 376         struct addrinfo hints;
 377         struct addrinfo *ai;
 378         ad_disc_ds_t *servers = NULL;
 379         scf_property_t *scf_prop;
 380         scf_value_t *value;
 381         scf_iter_t *iter;
 382         char *host, *portstr;
 383         int err, len, i;
 384         int count = 0;
 385         int rc = -1;
 386 
 387         *val = NULL;
 388 
 389 restart:
 390         scf_prop = scf_property_create(handles->main);
 391         if (scf_prop == NULL) {
 392                 idmapdlog(LOG_ERR, "scf_property_create() failed: %s",
 393                     scf_strerror(scf_error()));
 394                 return (-1);
 395         }
 396 
 397         value = scf_value_create(handles->main);
 398         if (value == NULL) {
 399                 idmapdlog(LOG_ERR, "scf_value_create() failed: %s",
 400                     scf_strerror(scf_error()));
 401                 scf_property_destroy(scf_prop);
 402                 return (-1);
 403         }
 404 
 405         iter = scf_iter_create(handles->main);
 406         if (iter == NULL) {
 407                 idmapdlog(LOG_ERR, "scf_iter_create() failed: %s",
 408                     scf_strerror(scf_error()));
 409                 scf_value_destroy(value);
 410                 scf_property_destroy(scf_prop);
 411                 return (-1);
 412         }
 413 
 414         if (scf_pg_get_property(handles->config_pg, name, scf_prop) < 0) {
 415                 /* this is OK: the property is just undefined */
 416                 rc = 0;
 417                 goto destruction;
 418         }
 419 
 420         if (scf_iter_property_values(iter, scf_prop) < 0) {
 421                 idmapdlog(LOG_ERR,
 422                     "scf_iter_property_values(%s) failed: %s",
 423                     name, scf_strerror(scf_error()));
 424                 goto destruction;
 425         }
 426 
 427         /* Workaround scf bugs -- can't reset an iteration */
 428         if (count == 0) {
 429                 while (scf_iter_next_value(iter, value) > 0)
 430                         count++;
 431 
 432                 if (count == 0) {
 433                         /* no values */
 434                         rc = 0;
 435                         goto destruction;
 436                 }
 437 
 438                 scf_value_destroy(value);
 439                 scf_iter_destroy(iter);
 440                 scf_property_destroy(scf_prop);
 441                 goto restart;
 442         }
 443 
 444         if ((servers = calloc(count + 1, sizeof (*servers))) == NULL) {
 445                 idmapdlog(LOG_ERR, "Out of memory");
 446                 goto destruction;
 447         }
 448 
 449         (void) memset(&hints, 0, sizeof (hints));
 450         hints.ai_protocol = IPPROTO_TCP;
 451         hints.ai_socktype = SOCK_STREAM;
 452         host = NULL;
 453 
 454         i = 0;
 455         while (i < count && scf_iter_next_value(iter, value) > 0) {
 456                 if (host) {
 457                         free(host);
 458                         host = NULL;
 459                 }
 460                 servers[i].priority = 0;
 461                 servers[i].weight = 100;
 462                 servers[i].port = defport;
 463                 if ((host = scf_value2string(name, value)) == NULL)
 464                         continue;
 465                 if ((portstr = strchr(host, ':')) != NULL) {
 466                         *portstr++ = '\0';
 467                         servers[i].port = strtol(portstr,
 468                             (char **)NULL, 10);
 469                         if (servers[i].port == 0)
 470                                 servers[i].port = defport;
 471                 }
 472 
 473                 /*
 474                  * Ignore this server if the hostname is too long
 475                  * or empty (continue without i++)
 476                  */
 477                 len = strlen(host);
 478                 if (len == 0) {
 479                         if (DBG(CONFIG, 1)) {
 480                                 idmapdlog(LOG_INFO, "%s host=\"\"", name);
 481                         }
 482                         continue;
 483                 }
 484                 if (len >= sizeof (servers->host)) {
 485                         idmapdlog(LOG_ERR, "Host name too long: %s", host);
 486                         idmapdlog(LOG_ERR, "ignoring %s value", name);
 487                         continue;
 488                 }
 489 
 490                 /*
 491                  * Get the host address too.  If we can't, then
 492                  * log an error and skip this host.
 493                  */
 494                 (void) snprintf(port_str, sizeof (port_str),
 495                     "%d", servers[i].port);
 496                 ai = NULL;
 497                 err = getaddrinfo(host, port_str, &hints, &ai);
 498                 if (err != 0) {
 499                         idmapdlog(LOG_ERR, "No address for host: %s (%s)",
 500                             host, gai_strerror(err));
 501                         idmapdlog(LOG_ERR, "ignoring %s value", name);
 502                         continue;
 503                 }
 504 
 505                 (void) strlcpy(servers[i].host, host,
 506                     sizeof (servers->host));
 507                 (void) memcpy(&servers[i].addr, ai->ai_addr, ai->ai_addrlen);
 508                 freeaddrinfo(ai);
 509 
 510                 /* Added a DS to the array. */
 511                 i++;
 512         }
 513         free(host);
 514 
 515         if (i == 0) {
 516                 if (DBG(CONFIG, 1)) {
 517                         idmapdlog(LOG_INFO, "%s is empty", name);
 518                 }
 519                 free(servers);
 520                 servers = NULL;
 521         }
 522         *val = servers;
 523 
 524         rc = 0;
 525 
 526 destruction:
 527         scf_value_destroy(value);
 528         scf_iter_destroy(iter);
 529         scf_property_destroy(scf_prop);
 530 
 531         if (rc < 0) {
 532                 if (servers)
 533                         free(servers);
 534                 *val = NULL;
 535         }
 536 
 537         return (rc);
 538 }
 539 
 540 static int
 541 get_val_astring(idmap_cfg_handles_t *handles, const char *name, char **val)
 542 {
 543         int rc = 0;
 544 
 545         scf_property_t *scf_prop;
 546         scf_value_t *value;
 547 
 548         scf_prop = scf_property_create(handles->main);
 549         if (scf_prop == NULL) {
 550                 idmapdlog(LOG_ERR, "scf_property_create() failed: %s",
 551                     scf_strerror(scf_error()));
 552                 return (-1);
 553         }
 554         value = scf_value_create(handles->main);
 555         if (value == NULL) {
 556                 idmapdlog(LOG_ERR, "scf_value_create() failed: %s",
 557                     scf_strerror(scf_error()));
 558                 scf_property_destroy(scf_prop);
 559                 return (-1);
 560         }
 561 
 562         *val = NULL;
 563 
 564         if (scf_pg_get_property(handles->config_pg, name, scf_prop) < 0)
 565         /* this is OK: the property is just undefined */
 566                 goto destruction;
 567 
 568         if (scf_property_get_value(scf_prop, value) < 0) {
 569                 idmapdlog(LOG_ERR,
 570                     "scf_property_get_value(%s) failed: %s",
 571                     name, scf_strerror(scf_error()));
 572                 rc = -1;
 573                 goto destruction;
 574         }
 575 
 576         *val = scf_value2string(name, value);
 577         if (*val == NULL)
 578                 rc = -1;
 579 
 580 destruction:
 581         scf_value_destroy(value);
 582         scf_property_destroy(scf_prop);
 583 
 584         if (rc < 0) {
 585                 if (*val)
 586                         free(*val);
 587                 *val = NULL;
 588         }
 589 
 590         return (rc);
 591 }
 592 
 593 
 594 static int
 595 del_val(
 596     idmap_cfg_handles_t *handles,
 597     scf_propertygroup_t *pg,
 598     const char *name)
 599 {
 600         int                     rc = -1;
 601         int                     ret;
 602         scf_transaction_t       *tx = NULL;
 603         scf_transaction_entry_t *ent = NULL;
 604 
 605         if ((tx = scf_transaction_create(handles->main)) == NULL) {
 606                 idmapdlog(LOG_ERR,
 607                     "scf_transaction_create() failed: %s",
 608                     scf_strerror(scf_error()));
 609                 goto destruction;
 610         }
 611         if ((ent = scf_entry_create(handles->main)) == NULL) {
 612                 idmapdlog(LOG_ERR,
 613                     "scf_entry_create() failed: %s",
 614                     scf_strerror(scf_error()));
 615                 goto destruction;
 616         }
 617 
 618         do {
 619                 if (scf_pg_update(pg) == -1) {
 620                         idmapdlog(LOG_ERR,
 621                             "scf_pg_update(%s) failed: %s",
 622                             name, scf_strerror(scf_error()));
 623                         goto destruction;
 624                 }
 625                 if (scf_transaction_start(tx, pg) != 0) {
 626                         idmapdlog(LOG_ERR,
 627                             "scf_transaction_start(%s) failed: %s",
 628                             name, scf_strerror(scf_error()));
 629                         goto destruction;
 630                 }
 631 
 632                 if (scf_transaction_property_delete(tx, ent, name) != 0) {
 633                         /* Don't complain if it already doesn't exist. */
 634                         if (scf_error() != SCF_ERROR_NOT_FOUND) {
 635                                 idmapdlog(LOG_ERR,
 636                                     "scf_transaction_property_delete() failed:"
 637                                     " %s",
 638                                     scf_strerror(scf_error()));
 639                         }
 640                         goto destruction;
 641                 }
 642 
 643                 ret = scf_transaction_commit(tx);
 644 
 645                 if (ret == 0)
 646                         scf_transaction_reset(tx);
 647         } while (ret == 0);
 648 
 649         if (ret == -1) {
 650                 idmapdlog(LOG_ERR,
 651                     "scf_transaction_commit(%s) failed: %s",
 652                     name, scf_strerror(scf_error()));
 653                 goto destruction;
 654         }
 655 
 656         rc = 0;
 657 
 658 destruction:
 659         if (ent != NULL)
 660                 scf_entry_destroy(ent);
 661         if (tx != NULL)
 662                 scf_transaction_destroy(tx);
 663         return (rc);
 664 }
 665 
 666 
 667 static int
 668 set_val(
 669     idmap_cfg_handles_t *handles,
 670     scf_propertygroup_t *pg,
 671     const char *name,
 672     scf_value_t *value)
 673 {
 674         int                     rc = -1;
 675         int                     i;
 676         scf_property_t          *prop = NULL;
 677         scf_transaction_t       *tx = NULL;
 678         scf_transaction_entry_t *ent = NULL;
 679 
 680         if ((prop = scf_property_create(handles->main)) == NULL ||
 681             (tx = scf_transaction_create(handles->main)) == NULL ||
 682             (ent = scf_entry_create(handles->main)) == NULL) {
 683                 idmapdlog(LOG_ERR, "Unable to set property %s",
 684                     name, scf_strerror(scf_error()));
 685                 goto destruction;
 686         }
 687 
 688         for (i = 0; i < MAX_TRIES; i++) {
 689                 int ret;
 690 
 691                 if (scf_pg_update(pg) == -1) {
 692                         idmapdlog(LOG_ERR,
 693                             "scf_pg_update() failed: %s",
 694                             scf_strerror(scf_error()));
 695                         goto destruction;
 696                 }
 697 
 698                 if (scf_transaction_start(tx, pg) == -1) {
 699                         idmapdlog(LOG_ERR,
 700                             "scf_transaction_start(%s) failed: %s",
 701                             name, scf_strerror(scf_error()));
 702                         goto destruction;
 703                 }
 704 
 705                 ret = scf_pg_get_property(pg, name, prop);
 706                 if (ret == SCF_SUCCESS) {
 707                         if (scf_transaction_property_change_type(tx, ent, name,
 708                             scf_value_type(value)) < 0) {
 709                                 idmapdlog(LOG_ERR,
 710                                     "scf_transaction_property_change_type(%s)"
 711                                     " failed: %s",
 712                                     name, scf_strerror(scf_error()));
 713                                 goto destruction;
 714                         }
 715                 } else if (scf_error() == SCF_ERROR_NOT_FOUND) {
 716                         if (scf_transaction_property_new(tx, ent, name,
 717                             scf_value_type(value)) < 0) {
 718                                 idmapdlog(LOG_ERR,
 719                                     "scf_transaction_property_new() failed: %s",
 720                                     scf_strerror(scf_error()));
 721                                 goto destruction;
 722                         }
 723                 } else {
 724                         idmapdlog(LOG_ERR,
 725                             "scf_pg_get_property(%s) failed: %s",
 726                             name, scf_strerror(scf_error()));
 727                         goto destruction;
 728                 }
 729 
 730                 if (scf_entry_add_value(ent, value) == -1) {
 731                         idmapdlog(LOG_ERR,
 732                             "scf_entry_add_value() failed: %s",
 733                             scf_strerror(scf_error()));
 734                         goto destruction;
 735                 }
 736 
 737                 ret = scf_transaction_commit(tx);
 738                 if (ret == 0) {
 739                         /*
 740                          * Property group set in scf_transaction_start()
 741                          * is not the most recent. Update pg, reset tx and
 742                          * retry tx.
 743                          */
 744                         idmapdlog(LOG_WARNING,
 745                             "scf_transaction_commit(%s) failed: %s",
 746                             name, scf_strerror(scf_error()));
 747                         scf_transaction_reset(tx);
 748                         continue;
 749                 }
 750                 if (ret != 1) {
 751                         idmapdlog(LOG_ERR,
 752                             "scf_transaction_commit(%s) failed: %s",
 753                             name, scf_strerror(scf_error()));
 754                         goto destruction;
 755                 }
 756                 /* Success! */
 757                 rc = 0;
 758                 break;
 759         }
 760 
 761 destruction:
 762         scf_entry_destroy(ent);
 763         scf_transaction_destroy(tx);
 764         scf_property_destroy(prop);
 765         return (rc);
 766 }
 767 
 768 static int
 769 set_val_integer(
 770     idmap_cfg_handles_t *handles,
 771     scf_propertygroup_t *pg,
 772     const char *name,
 773     int64_t val)
 774 {
 775         scf_value_t             *value = NULL;
 776         int                     rc;
 777 
 778         if ((value = scf_value_create(handles->main)) == NULL) {
 779                 idmapdlog(LOG_ERR, "Unable to set property %s",
 780                     name, scf_strerror(scf_error()));
 781                 return (-1);
 782         }
 783 
 784         scf_value_set_integer(value, val);
 785 
 786         rc = set_val(handles, pg, name, value);
 787 
 788         scf_value_destroy(value);
 789 
 790         return (rc);
 791 }
 792 
 793 
 794 static int
 795 set_val_astring(
 796     idmap_cfg_handles_t *handles,
 797     scf_propertygroup_t *pg,
 798     const char *name,
 799     const char *val)
 800 {
 801         scf_value_t             *value = NULL;
 802         int                     rc = -1;
 803 
 804         if ((value = scf_value_create(handles->main)) == NULL) {
 805                 idmapdlog(LOG_ERR, "Unable to set property %s",
 806                     name, scf_strerror(scf_error()));
 807                 goto out;
 808         }
 809 
 810         if (scf_value_set_astring(value, val) == -1) {
 811                 idmapdlog(LOG_ERR,
 812                     "scf_value_set_astring() failed: %s",
 813                     scf_strerror(scf_error()));
 814                 goto out;
 815         }
 816 
 817         rc = set_val(handles, pg, name, value);
 818 
 819 out:
 820         scf_value_destroy(value);
 821         return (rc);
 822 }
 823 
 824 
 825 
 826 /*
 827  * This function updates a boolean value.
 828  * If nothing has changed it returns 0 else 1
 829  */
 830 static int
 831 update_bool(boolean_t *value, boolean_t *new, char *name)
 832 {
 833         if (*value == *new)
 834                 return (0);
 835 
 836         if (DBG(CONFIG, 1)) {
 837                 idmapdlog(LOG_INFO, "change %s=%s", name,
 838                     *new ? "true" : "false");
 839         }
 840 
 841         *value = *new;
 842         return (1);
 843 }
 844 
 845 /*
 846  * This function updates a uint64_t value.
 847  * If nothing has changed it returns 0 else 1
 848  */
 849 static int
 850 update_uint64(uint64_t *value, uint64_t *new, char *name)
 851 {
 852         if (*value == *new)
 853                 return (0);
 854 
 855         if (DBG(CONFIG, 1))
 856                 idmapdlog(LOG_INFO, "change %s=%llu", name, *new);
 857 
 858         *value = *new;
 859         return (1);
 860 }
 861 
 862 /*
 863  * This function updates a string value.
 864  * If nothing has changed it returns 0 else 1
 865  */
 866 static int
 867 update_string(char **value, char **new, char *name)
 868 {
 869         int changed;
 870 
 871         if (*new == NULL && *value != NULL)
 872                 changed = 1;
 873         else if (*new != NULL && *value == NULL)
 874                 changed = 1;
 875         else if (*new != NULL && *value != NULL && strcmp(*new, *value) != 0)
 876                 changed = 1;
 877         else
 878                 changed = 0;
 879 
 880         /*
 881          * Note that even if unchanged we can't just return; we must free one
 882          * of the values.
 883          */
 884 
 885         if (DBG(CONFIG, 1) && changed)
 886                 idmapdlog(LOG_INFO, "change %s=%s", name, CHECK_NULL(*new));
 887 
 888         free(*value);
 889         *value = *new;
 890         *new = NULL;
 891         return (changed);
 892 }
 893 
 894 static int
 895 update_enum(int *value, int *new, char *name, struct enum_lookup_map *map)
 896 {
 897         if (*value == *new)
 898                 return (0);
 899 
 900         if (DBG(CONFIG, 1)) {
 901                 idmapdlog(LOG_INFO, "change %s=%s", name,
 902                     enum_lookup(*new, map));
 903         }
 904 
 905         *value = *new;
 906 
 907         return (1);
 908 }
 909 
 910 /*
 911  * This function updates a directory service structure.
 912  * If nothing has changed it returns 0 else 1
 913  */
 914 static int
 915 update_dirs(ad_disc_ds_t **value, ad_disc_ds_t **new, char *name)
 916 {
 917 
 918         if (*value == *new)
 919                 /* Nothing to do */
 920                 return (0);
 921 
 922         if (*value != NULL && *new != NULL &&
 923             ad_disc_compare_ds(*value, *new) == 0) {
 924                 free(*new);
 925                 *new = NULL;
 926                 return (0);
 927         }
 928 
 929         if (*value != NULL)
 930                 free(*value);
 931 
 932         *value = *new;
 933         *new = NULL;
 934 
 935         if (*value == NULL) {
 936                 /* We're unsetting this DS property */
 937                 if (DBG(CONFIG, 1))
 938                         idmapdlog(LOG_INFO, "change %s=<none>", name);
 939                 return (1);
 940         }
 941 
 942         if (DBG(CONFIG, 1)) {
 943                 /* List all the new DSs */
 944                 char buf[64];
 945                 ad_disc_ds_t *ds;
 946                 for (ds = *value; ds->host[0] != '\0'; ds++) {
 947                         if (ad_disc_getnameinfo(buf, sizeof (buf), &ds->addr))
 948                                 (void) strlcpy(buf, "?", sizeof (buf));
 949                         idmapdlog(LOG_INFO, "change %s=%s addr=%s port=%d",
 950                             name, ds->host, buf, ds->port);
 951                 }
 952         }
 953         return (1);
 954 }
 955 
 956 /*
 957  * This function updates a trusted domains structure.
 958  * If nothing has changed it returns 0 else 1
 959  */
 960 static int
 961 update_trusted_domains(ad_disc_trusteddomains_t **value,
 962                         ad_disc_trusteddomains_t **new, char *name)
 963 {
 964         int i;
 965 
 966         if (*value == *new)
 967                 /* Nothing to do */
 968                 return (0);
 969 
 970         if (*value != NULL && *new != NULL &&
 971             ad_disc_compare_trusteddomains(*value, *new) == 0) {
 972                 free(*new);
 973                 *new = NULL;
 974                 return (0);
 975         }
 976 
 977         if (*value != NULL)
 978                 free(*value);
 979 
 980         *value = *new;
 981         *new = NULL;
 982 
 983         if (*value == NULL) {
 984                 /* We're unsetting this DS property */
 985                 if (DBG(CONFIG, 1))
 986                         idmapdlog(LOG_INFO, "change %s=<none>", name);
 987                 return (1);
 988         }
 989 
 990         if (DBG(CONFIG, 1)) {
 991                 /* List all the new domains */
 992                 for (i = 0; (*value)[i].domain[0] != '\0'; i++) {
 993                         idmapdlog(LOG_INFO, "change %s=%s direction=%s", name,
 994                             (*value)[i].domain,
 995                             enum_lookup((*value)[i].direction, trust_dir_map));
 996                 }
 997         }
 998         return (1);
 999 }
1000 
1001 
1002 /*
1003  * This function updates a domains in a forest structure.
1004  * If nothing has changed it returns 0 else 1
1005  */
1006 static int
1007 update_domains_in_forest(ad_disc_domainsinforest_t **value,
1008                         ad_disc_domainsinforest_t **new, char *name)
1009 {
1010         int i;
1011 
1012         if (*value == *new)
1013                 /* Nothing to do */
1014                 return (0);
1015 
1016         if (*value != NULL && *new != NULL &&
1017             ad_disc_compare_domainsinforest(*value, *new) == 0) {
1018                 free(*new);
1019                 *new = NULL;
1020                 return (0);
1021         }
1022 
1023         if (*value != NULL)
1024                 free(*value);
1025 
1026         *value = *new;
1027         *new = NULL;
1028 
1029         if (*value == NULL) {
1030                 /* We're unsetting this DS property */
1031                 if (DBG(CONFIG, 1))
1032                         idmapdlog(LOG_INFO, "change %s=<none>", name);
1033                 return (1);
1034         }
1035 
1036         if (DBG(CONFIG, 1)) {
1037                 /* List all the new domains */
1038                 for (i = 0; (*value)[i].domain[0] != '\0'; i++) {
1039                         idmapdlog(LOG_INFO, "change %s=%s", name,
1040                             (*value)[i].domain);
1041                 }
1042         }
1043         return (1);
1044 }
1045 
1046 
1047 static void
1048 free_trusted_forests(idmap_trustedforest_t **value, int *num_values)
1049 {
1050         int i;
1051 
1052         for (i = 0; i < *num_values; i++) {
1053                 free((*value)[i].forest_name);
1054                 free((*value)[i].global_catalog);
1055                 free((*value)[i].domains_in_forest);
1056         }
1057         free(*value);
1058         *value = NULL;
1059         *num_values = 0;
1060 }
1061 
1062 
1063 static int
1064 compare_trusteddomainsinforest(ad_disc_domainsinforest_t *df1,
1065                         ad_disc_domainsinforest_t *df2)
1066 {
1067         int             i, j;
1068         int             num_df1 = 0;
1069         int             num_df2 = 0;
1070         boolean_t       match;
1071 
1072         for (i = 0; df1[i].domain[0] != '\0'; i++)
1073                 if (df1[i].trusted)
1074                         num_df1++;
1075 
1076         for (j = 0; df2[j].domain[0] != '\0'; j++)
1077                 if (df2[j].trusted)
1078                         num_df2++;
1079 
1080         if (num_df1 != num_df2)
1081                 return (1);
1082 
1083         for (i = 0; df1[i].domain[0] != '\0'; i++) {
1084                 if (df1[i].trusted) {
1085                         match = B_FALSE;
1086                         for (j = 0; df2[j].domain[0] != '\0'; j++) {
1087                                 if (df2[j].trusted &&
1088                                     domain_eq(df1[i].domain, df2[j].domain) &&
1089                                     strcmp(df1[i].sid, df2[j].sid) == 0) {
1090                                         match = B_TRUE;
1091                                         break;
1092                                 }
1093                         }
1094                         if (!match)
1095                                 return (1);
1096                 }
1097         }
1098         return (0);
1099 }
1100 
1101 
1102 
1103 /*
1104  * This function updates trusted forest structure.
1105  * If nothing has changed it returns 0 else 1
1106  */
1107 static int
1108 update_trusted_forest(idmap_trustedforest_t **value, int *num_value,
1109                         idmap_trustedforest_t **new, int *num_new, char *name)
1110 {
1111         int i, j;
1112         boolean_t match;
1113 
1114         if (*value == *new)
1115                 /* Nothing to do */
1116                 return (0);
1117 
1118         if (*value != NULL && *new != NULL) {
1119                 if (*num_value != *num_new)
1120                         goto not_equal;
1121                 for (i = 0; i < *num_value; i++) {
1122                         match = B_FALSE;
1123                         for (j = 0; j < *num_new; j++) {
1124                                 if (strcmp((*value)[i].forest_name,
1125                                     (*new)[j].forest_name) == 0 &&
1126                                     ad_disc_compare_ds(
1127                                     (*value)[i].global_catalog,
1128                                     (*new)[j].global_catalog) == 0 &&
1129                                     compare_trusteddomainsinforest(
1130                                     (*value)[i].domains_in_forest,
1131                                     (*new)[j].domains_in_forest) == 0) {
1132                                         match = B_TRUE;
1133                                         break;
1134                                 }
1135                         }
1136                         if (!match)
1137                                 goto not_equal;
1138                 }
1139                 free_trusted_forests(new, num_new);
1140                 return (0);
1141         }
1142 not_equal:
1143         if (*value != NULL)
1144                 free_trusted_forests(value, num_value);
1145         *value = *new;
1146         *num_value = *num_new;
1147         *new = NULL;
1148         *num_new = 0;
1149 
1150         if (*value == NULL) {
1151                 /* We're unsetting this DS property */
1152                 if (DBG(CONFIG, 1))
1153                         idmapdlog(LOG_INFO, "change %s=<none>", name);
1154                 return (1);
1155         }
1156 
1157         if (DBG(CONFIG, 1)) {
1158                 /* List all the trusted forests */
1159                 for (i = 0; i < *num_value; i++) {
1160                         idmap_trustedforest_t *f = &(*value)[i];
1161                         for (j = 0;
1162                             f->domains_in_forest[j].domain[0] != '\0';
1163                             j++) {
1164                                 /* List trusted Domains in the forest. */
1165                                 if (f->domains_in_forest[j].trusted)
1166                                         idmapdlog(LOG_INFO,
1167                                             "change %s=%s domain=%s",
1168                                             name, f->forest_name,
1169                                             f->domains_in_forest[j].domain);
1170                         }
1171                         /* List the hosts */
1172                         for (j = 0;
1173                             f->global_catalog[j].host[0] != '\0';
1174                             j++) {
1175                                 idmapdlog(LOG_INFO,
1176                                     "change %s=%s host=%s port=%d",
1177                                     name, f->forest_name,
1178                                     f->global_catalog[j].host,
1179                                     f->global_catalog[j].port);
1180                         }
1181                 }
1182         }
1183         return (1);
1184 }
1185 
1186 const char *
1187 enum_lookup(int value, struct enum_lookup_map *map)
1188 {
1189         for (; map->string != NULL; map++) {
1190                 if (value == map->value) {
1191                         return (map->string);
1192                 }
1193         }
1194         return ("(invalid)");
1195 }
1196 
1197 /*
1198  * Returns 1 if the PF_ROUTE socket event indicates that we should rescan the
1199  * interfaces.
1200  *
1201  * Shamelessly based on smb_nics_changed() and other PF_ROUTE uses in ON.
1202  */
1203 static
1204 boolean_t
1205 pfroute_event_is_interesting(int rt_sock)
1206 {
1207         int nbytes;
1208         int64_t msg[2048 / 8];
1209         struct rt_msghdr *rtm;
1210         boolean_t is_interesting = B_FALSE;
1211 
1212         for (;;) {
1213                 if ((nbytes = read(rt_sock, msg, sizeof (msg))) <= 0)
1214                         break;
1215                 rtm = (struct rt_msghdr *)msg;
1216                 if (rtm->rtm_version != RTM_VERSION)
1217                         continue;
1218                 if (nbytes < rtm->rtm_msglen)
1219                         continue;
1220                 switch (rtm->rtm_type) {
1221                 case RTM_NEWADDR:
1222                 case RTM_DELADDR:
1223                 case RTM_IFINFO:
1224                         is_interesting = B_TRUE;
1225                         break;
1226                 default:
1227                         break;
1228                 }
1229         }
1230         return (is_interesting);
1231 }
1232 
1233 /*
1234  * Wait for an event, and report what kind of event occurred.
1235  *
1236  * Note that there are cases where we are awoken but don't care about
1237  * the lower-level event.  We can't just loop here because we can't
1238  * readily calculate how long to sleep the next time.  We return
1239  * EVENT_NOTHING and let the caller loop.
1240  */
1241 static
1242 enum event_type
1243 wait_for_event(struct timespec *timeoutp)
1244 {
1245         port_event_t pe;
1246 
1247         (void) memset(&pe, 0, sizeof (pe));
1248         if (port_get(idmapd_ev_port, &pe, timeoutp) != 0) {
1249                 switch (errno) {
1250                 case EINTR:
1251                         return (EVENT_NOTHING);
1252                 case ETIME:
1253                         /* Timeout */
1254                         return (EVENT_TIMEOUT);
1255                 default:
1256                         /* EBADF, EBADFD, EFAULT, EINVAL (end of time?)? */
1257                         idmapdlog(LOG_ERR, "Event port failed: %s",
1258                             strerror(errno));
1259                         exit(1);
1260                         /* NOTREACHED */
1261                 }
1262         }
1263 
1264 
1265         switch (pe.portev_source) {
1266         case 0:
1267                 /*
1268                  * This isn't documented, but seems to be what you get if
1269                  * the timeout is zero seconds and there are no events
1270                  * pending.
1271                  */
1272                 return (EVENT_TIMEOUT);
1273 
1274         case PORT_SOURCE_USER:
1275                 switch (pe.portev_events) {
1276                 case RECONFIGURE:
1277                         return (EVENT_REFRESH);
1278                 case POKE_AUTO_DISCOVERY:
1279                         return (EVENT_POKED);
1280                 case KICK_AUTO_DISCOVERY:
1281                         return (EVENT_KICKED);
1282                 }
1283                 return (EVENT_NOTHING);
1284 
1285         case PORT_SOURCE_FD:
1286                 if (pe.portev_object == rt_sock) {
1287                         /*
1288                          * PF_ROUTE socket read event:
1289                          *    re-associate fd
1290                          *    handle event
1291                          */
1292                         if (port_associate(idmapd_ev_port, PORT_SOURCE_FD,
1293                             rt_sock, POLLIN, NULL) != 0) {
1294                                 idmapdlog(LOG_ERR, "Failed to re-associate the "
1295                                     "routing socket with the event port: %s",
1296                                     strerror(errno));
1297                                 abort();
1298                         }
1299                         /*
1300                          * The network configuration may still be in flux.
1301                          * No matter, the resolver will re-transmit and
1302                          * timeout if need be.
1303                          */
1304                         if (pfroute_event_is_interesting(rt_sock)) {
1305                                 if (DBG(CONFIG, 1)) {
1306                                         idmapdlog(LOG_DEBUG,
1307                                             "Interesting routing event");
1308                                 }
1309                                 return (EVENT_ROUTING);
1310                         } else {
1311                                 if (DBG(CONFIG, 2)) {
1312                                         idmapdlog(LOG_DEBUG,
1313                                             "Boring routing event");
1314                                 }
1315                                 return (EVENT_NOTHING);
1316                         }
1317                 }
1318                 /* Event on an FD other than the routing FD? Ignore it. */
1319                 break;
1320         }
1321 
1322         return (EVENT_NOTHING);
1323 }
1324 
1325 void *
1326 idmap_cfg_update_thread(void *arg)
1327 {
1328         NOTE(ARGUNUSED(arg))
1329         idmap_pg_config_t *pgcfg = &_idmapdstate.cfg->pgcfg;
1330         const ad_disc_t         ad_ctx = _idmapdstate.cfg->handles.ad_ctx;
1331         int flags = CFG_DISCOVER;
1332 
1333         for (;;) {
1334                 struct timespec timeout;
1335                 struct timespec *timeoutp;
1336                 int             rc;
1337                 int             ttl, max_ttl;
1338 
1339                 (void) ad_disc_SubnetChanged(ad_ctx);
1340 
1341                 rc = idmap_cfg_load(_idmapdstate.cfg, flags);
1342                 if (rc < -1) {
1343                         idmapdlog(LOG_ERR, "Fatal errors while reading "
1344                             "SMF properties");
1345                         exit(1);
1346                 } else if (rc == -1) {
1347                         idmapdlog(LOG_WARNING,
1348                             "Errors re-loading configuration may cause AD "
1349                             "lookups to fail");
1350                 }
1351 
1352                 /*
1353                  * Wait for an interesting event.  Note that we might get
1354                  * boring events between interesting events.  If so, we loop.
1355                  */
1356                 flags = CFG_DISCOVER;
1357                 for (;;) {
1358                         /*
1359                          * If we don't know our domain name, don't bother
1360                          * with rediscovery until the next config change.
1361                          * Avoids hourly noise in workgroup mode.
1362                          */
1363                         if (pgcfg->domain_name == NULL)
1364                                 ttl = -1;
1365                         else
1366                                 ttl = ad_disc_get_TTL(ad_ctx);
1367                         if (ttl < 0) {
1368                                 timeoutp = NULL;
1369                         } else {
1370                                 max_ttl = (int)pgcfg->rediscovery_interval;
1371                                 if (ttl > max_ttl)
1372                                         ttl = max_ttl;
1373                                 if (ttl < MIN_REDISCOVERY_INTERVAL)
1374                                         ttl = MIN_REDISCOVERY_INTERVAL;
1375                                 timeout.tv_sec = ttl;
1376                                 timeout.tv_nsec = 0;
1377                                 timeoutp = &timeout;
1378                         }
1379 
1380                         if (DBG(CONFIG, 1))
1381                                 idmapdlog(LOG_DEBUG,
1382                                     "_cfg_update_thread waiting");
1383 
1384                         switch (wait_for_event(timeoutp)) {
1385                         case EVENT_NOTHING:
1386                                 if (DBG(CONFIG, 2))
1387                                         idmapdlog(LOG_DEBUG, "Boring event.");
1388                                 continue;
1389                         case EVENT_REFRESH:
1390                                 if (DBG(CONFIG, 1))
1391                                         idmapdlog(LOG_INFO, "SMF refresh");
1392                                 /*
1393                                  * Forget any DC we had previously.
1394                                  */
1395                                 flags |= CFG_FORGET_DC;
1396 
1397                                 /*
1398                                  * Blow away the ccache, we might have
1399                                  * re-joined the domain or joined a new one
1400                                  */
1401                                 (void) unlink(IDMAP_CACHEDIR "/ccache");
1402                                 break;
1403                         case EVENT_POKED:
1404                                 if (DBG(CONFIG, 1))
1405                                         idmapdlog(LOG_DEBUG, "poked");
1406                                 break;
1407                         case EVENT_KICKED:
1408                                 if (DBG(CONFIG, 1))
1409                                         idmapdlog(LOG_DEBUG, "kicked");
1410                                 flags |= CFG_FORGET_DC;
1411                                 break;
1412                         case EVENT_TIMEOUT:
1413                                 if (DBG(CONFIG, 1))
1414                                         idmapdlog(LOG_DEBUG, "TTL expired");
1415                                 break;
1416                         case EVENT_ROUTING:
1417                                 /* Already logged to DEBUG */
1418                                 break;
1419                         }
1420                         /* An interesting event! */
1421                         break;
1422                 }
1423         }
1424         /*
1425          * Lint isn't happy with the concept of a function declared to
1426          * return something, that doesn't return.  Of course, merely adding
1427          * the return isn't enough, because it's never reached...
1428          */
1429         /*NOTREACHED*/
1430         return (NULL);
1431 }
1432 
1433 int
1434 idmap_cfg_start_updates(void)
1435 {
1436         if ((idmapd_ev_port = port_create()) < 0) {
1437                 idmapdlog(LOG_ERR, "Failed to create event port: %s",
1438                     strerror(errno));
1439                 return (-1);
1440         }
1441 
1442         if ((rt_sock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) {
1443                 idmapdlog(LOG_ERR, "Failed to open routing socket: %s",
1444                     strerror(errno));
1445                 (void) close(idmapd_ev_port);
1446                 return (-1);
1447         }
1448 
1449         if (fcntl(rt_sock, F_SETFL, O_NDELAY|O_NONBLOCK) < 0) {
1450                 idmapdlog(LOG_ERR, "Failed to set routing socket flags: %s",
1451                     strerror(errno));
1452                 (void) close(rt_sock);
1453                 (void) close(idmapd_ev_port);
1454                 return (-1);
1455         }
1456 
1457         if (port_associate(idmapd_ev_port, PORT_SOURCE_FD,
1458             rt_sock, POLLIN, NULL) != 0) {
1459                 idmapdlog(LOG_ERR, "Failed to associate the routing "
1460                     "socket with the event port: %s", strerror(errno));
1461                 (void) close(rt_sock);
1462                 (void) close(idmapd_ev_port);
1463                 return (-1);
1464         }
1465 
1466         if ((errno = pthread_create(&update_thread_handle, NULL,
1467             idmap_cfg_update_thread, NULL)) != 0) {
1468                 idmapdlog(LOG_ERR, "Failed to start update thread: %s",
1469                     strerror(errno));
1470                 (void) port_dissociate(idmapd_ev_port, PORT_SOURCE_FD, rt_sock);
1471                 (void) close(rt_sock);
1472                 (void) close(idmapd_ev_port);
1473                 return (-1);
1474         }
1475 
1476         return (0);
1477 }
1478 
1479 /*
1480  * Reject attribute names with invalid characters.
1481  */
1482 static
1483 int
1484 valid_ldap_attr(const char *attr) {
1485         for (; *attr; attr++) {
1486                 if (!isalnum(*attr) && *attr != '-' &&
1487                     *attr != '_' && *attr != '.' && *attr != ';')
1488                         return (0);
1489         }
1490         return (1);
1491 }
1492 
1493 static
1494 void
1495 idmapd_set_debug(
1496     idmap_cfg_handles_t *handles,
1497     enum idmapd_debug item,
1498     const char *name)
1499 {
1500         int val;
1501 
1502         if (item < 0 || item > IDMAPD_DEBUG_MAX)
1503                 return;
1504 
1505         val = get_debug(handles, name);
1506 
1507         if (val != _idmapdstate.debug[item])
1508                 idmapdlog(LOG_DEBUG, "%s/%s = %d", DEBUG_PG, name, val);
1509 
1510         _idmapdstate.debug[item] = val;
1511 }
1512 
1513 static
1514 void
1515 check_smf_debug_mode(idmap_cfg_handles_t *handles)
1516 {
1517         idmapd_set_debug(handles, IDMAPD_DEBUG_ALL, "all");
1518         idmapd_set_debug(handles, IDMAPD_DEBUG_CONFIG, "config");
1519         idmapd_set_debug(handles, IDMAPD_DEBUG_MAPPING, "mapping");
1520         idmapd_set_debug(handles, IDMAPD_DEBUG_DISC, "discovery");
1521         idmapd_set_debug(handles, IDMAPD_DEBUG_DNS, "dns");
1522         idmapd_set_debug(handles, IDMAPD_DEBUG_LDAP, "ldap");
1523 
1524         adutils_set_debug(AD_DEBUG_ALL, _idmapdstate.debug[IDMAPD_DEBUG_ALL]);
1525         adutils_set_debug(AD_DEBUG_DISC, _idmapdstate.debug[IDMAPD_DEBUG_DISC]);
1526         adutils_set_debug(AD_DEBUG_DNS, _idmapdstate.debug[IDMAPD_DEBUG_DNS]);
1527         adutils_set_debug(AD_DEBUG_LDAP, _idmapdstate.debug[IDMAPD_DEBUG_LDAP]);
1528 }
1529 
1530 /*
1531  * This is the half of idmap_cfg_load() that loads property values from
1532  * SMF (using the config/ property group of the idmap FMRI).
1533  *
1534  * Return values: 0 -> success, -1 -> failure, -2 -> hard failures
1535  *               -3 -> hard smf config failures
1536  * reading from SMF.
1537  */
1538 static int
1539 idmap_cfg_load_smf(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg,
1540         int * const errors)
1541 {
1542         int rc;
1543         char *s;
1544 
1545         *errors = 0;
1546 
1547         if (scf_pg_update(handles->config_pg) < 0) {
1548                 idmapdlog(LOG_ERR, "scf_pg_update() failed: %s",
1549                     scf_strerror(scf_error()));
1550                 return (-2);
1551         }
1552 
1553         if (scf_pg_update(handles->debug_pg) < 0) {
1554                 idmapdlog(LOG_ERR, "scf_pg_update() failed: %s",
1555                     scf_strerror(scf_error()));
1556                 return (-2);
1557         }
1558 
1559         check_smf_debug_mode(handles);
1560 
1561         rc = get_val_bool(handles, "unresolvable_sid_mapping",
1562             &pgcfg->eph_map_unres_sids, B_TRUE);
1563         if (rc != 0)
1564                 (*errors)++;
1565 
1566         rc = get_val_bool(handles, "use_ads",
1567             &pgcfg->use_ads, B_TRUE);
1568         if (rc != 0)
1569                 (*errors)++;
1570 
1571         rc = get_val_bool(handles, "use_lsa",
1572             &pgcfg->use_lsa, B_TRUE);
1573         if (rc != 0)
1574                 (*errors)++;
1575 
1576         rc = get_val_bool(handles, "disable_cross_forest_trusts",
1577             &pgcfg->disable_cross_forest_trusts, B_TRUE);
1578         if (rc != 0)
1579                 (*errors)++;
1580 
1581         rc = get_val_astring(handles, "directory_based_mapping", &s);
1582         if (rc != 0)
1583                 (*errors)++;
1584         else if (s == NULL || strcasecmp(s, "none") == 0)
1585                 pgcfg->directory_based_mapping = DIRECTORY_MAPPING_NONE;
1586         else if (strcasecmp(s, "name") == 0)
1587                 pgcfg->directory_based_mapping = DIRECTORY_MAPPING_NAME;
1588         else if (strcasecmp(s, "idmu") == 0)
1589                 pgcfg->directory_based_mapping = DIRECTORY_MAPPING_IDMU;
1590         else {
1591                 pgcfg->directory_based_mapping = DIRECTORY_MAPPING_NONE;
1592                 idmapdlog(LOG_ERR,
1593                 "config/directory_based_mapping:  invalid value \"%s\" ignored",
1594                     s);
1595                 (*errors)++;
1596         }
1597         free(s);
1598 
1599         rc = get_val_int(handles, "list_size_limit",
1600             &pgcfg->list_size_limit, SCF_TYPE_COUNT);
1601         if (rc != 0)
1602                 (*errors)++;
1603 
1604         rc = get_val_int(handles, "id_cache_timeout",
1605             &pgcfg->id_cache_timeout, SCF_TYPE_COUNT);
1606         if (rc != 0)
1607                 (*errors)++;
1608         if (pgcfg->id_cache_timeout == 0)
1609                 pgcfg->id_cache_timeout = ID_CACHE_TMO_DEFAULT;
1610 
1611         rc = get_val_int(handles, "name_cache_timeout",
1612             &pgcfg->name_cache_timeout, SCF_TYPE_COUNT);
1613         if (rc != 0)
1614                 (*errors)++;
1615         if (pgcfg->name_cache_timeout == 0)
1616                 pgcfg->name_cache_timeout = NAME_CACHE_TMO_DEFAULT;
1617 
1618         rc = get_val_int(handles, "rediscovery_interval",
1619             &pgcfg->rediscovery_interval, SCF_TYPE_COUNT);
1620         if (rc != 0)
1621                 (*errors)++;
1622         if (pgcfg->rediscovery_interval == 0)
1623                 pgcfg->rediscovery_interval = REDISCOVERY_INTERVAL_DEFAULT;
1624 
1625         rc = get_val_astring(handles, "domain_name",
1626             &pgcfg->domain_name);
1627         if (rc != 0)
1628                 (*errors)++;
1629         else {
1630                 if (pgcfg->domain_name != NULL &&
1631                     pgcfg->domain_name[0] == '\0') {
1632                         free(pgcfg->domain_name);
1633                         pgcfg->domain_name = NULL;
1634                 }
1635                 (void) ad_disc_set_DomainName(handles->ad_ctx,
1636                     pgcfg->domain_name);
1637                 pgcfg->domain_name_auto_disc = B_FALSE;
1638         }
1639 
1640         rc = get_val_astring(handles, "default_domain",
1641             &pgcfg->default_domain);
1642         if (rc != 0) {
1643                 /*
1644                  * SCF failures fetching config/default_domain we treat
1645                  * as fatal as they may leave ID mapping rules that
1646                  * match unqualified winnames flapping in the wind.
1647                  */
1648                 return (-2);
1649         }
1650 
1651         if (pgcfg->default_domain == NULL && pgcfg->domain_name != NULL) {
1652                 pgcfg->default_domain = strdup(pgcfg->domain_name);
1653         }
1654 
1655         rc = get_val_astring(handles, "domain_guid", &s);
1656         if (rc != 0) {
1657                 (*errors)++;
1658         } else if (s == NULL || s[0] == '\0') {
1659                 /* OK, not set. */
1660                 free(s);
1661         } else {
1662                 uuid_t u;
1663 
1664                 if (uuid_parse(s, u) != 0) {
1665                         idmapdlog(LOG_ERR,
1666                 "config/domain_guid: invalid value \"%s\" ignored", s);
1667                         free(s);
1668                         (*errors)++;
1669                 } else {
1670                         pgcfg->domain_guid = s;
1671                         pgcfg->domain_guid_auto_disc = B_FALSE;
1672                         (void) ad_disc_set_DomainGUID(handles->ad_ctx, u);
1673                 }
1674         }
1675 
1676         rc = get_val_astring(handles, "machine_uuid", &pgcfg->machine_uuid);
1677         if (rc != 0)
1678                 (*errors)++;
1679         if (pgcfg->machine_uuid == NULL) {
1680                 /* If machine_uuid not configured, generate one */
1681                 if (generate_machine_uuid(&pgcfg->machine_uuid) < 0)
1682                         return (-2);
1683                 rc = set_val_astring(handles, handles->config_pg,
1684                     "machine_uuid", pgcfg->machine_uuid);
1685                 if (rc != 0)
1686                         (*errors)++;
1687         }
1688 
1689         rc = get_val_astring(handles, "machine_sid", &pgcfg->machine_sid);
1690         if (rc != 0)
1691                 (*errors)++;
1692         if (pgcfg->machine_sid == NULL) {
1693                 /*
1694                  * If machine_sid not configured, generate one
1695                  * from the machine UUID.
1696                  */
1697                 if (generate_machine_sid(&pgcfg->machine_sid,
1698                     pgcfg->machine_uuid) < 0)
1699                         return (-2);
1700                 rc = set_val_astring(handles, handles->config_pg,
1701                     "machine_sid", pgcfg->machine_sid);
1702                 if (rc != 0)
1703                         (*errors)++;
1704         }
1705 
1706         rc = get_val_ds(handles, "domain_controller", 389,
1707             &pgcfg->domain_controller);
1708         if (rc != 0)
1709                 (*errors)++;
1710         else {
1711                 (void) ad_disc_set_DomainController(handles->ad_ctx,
1712                     pgcfg->domain_controller);
1713                 pgcfg->domain_controller_auto_disc = B_FALSE;
1714         }
1715 
1716         rc = get_val_ds(handles, "preferred_dc", 389,
1717             &pgcfg->preferred_dc);
1718         if (rc != 0)
1719                 (*errors)++;
1720         else {
1721                 (void) ad_disc_set_PreferredDC(handles->ad_ctx,
1722                     pgcfg->preferred_dc);
1723                 pgcfg->preferred_dc_auto_disc = B_FALSE;
1724         }
1725 
1726         rc = get_val_astring(handles, "forest_name", &pgcfg->forest_name);
1727         if (rc != 0)
1728                 (*errors)++;
1729         else {
1730                 (void) ad_disc_set_ForestName(handles->ad_ctx,
1731                     pgcfg->forest_name);
1732                 pgcfg->forest_name_auto_disc = B_FALSE;
1733         }
1734 
1735         rc = get_val_astring(handles, "site_name", &pgcfg->site_name);
1736         if (rc != 0)
1737                 (*errors)++;
1738         else
1739                 (void) ad_disc_set_SiteName(handles->ad_ctx, pgcfg->site_name);
1740 
1741         rc = get_val_ds(handles, "global_catalog", 3268,
1742             &pgcfg->global_catalog);
1743         if (rc != 0)
1744                 (*errors)++;
1745         else {
1746                 (void) ad_disc_set_GlobalCatalog(handles->ad_ctx,
1747                     pgcfg->global_catalog);
1748                 pgcfg->global_catalog_auto_disc = B_FALSE;
1749         }
1750 
1751         /* Unless we're doing directory-based name mapping, we're done. */
1752         if (pgcfg->directory_based_mapping != DIRECTORY_MAPPING_NAME)
1753                 return (0);
1754 
1755         rc = get_val_astring(handles, "ad_unixuser_attr",
1756             &pgcfg->ad_unixuser_attr);
1757         if (rc != 0)
1758                 return (-2);
1759         if (pgcfg->ad_unixuser_attr != NULL &&
1760             !valid_ldap_attr(pgcfg->ad_unixuser_attr)) {
1761                 idmapdlog(LOG_ERR, "config/ad_unixuser_attr=%s is not a "
1762                     "valid LDAP attribute name", pgcfg->ad_unixuser_attr);
1763                 return (-3);
1764         }
1765 
1766         rc = get_val_astring(handles, "ad_unixgroup_attr",
1767             &pgcfg->ad_unixgroup_attr);
1768         if (rc != 0)
1769                 return (-2);
1770         if (pgcfg->ad_unixgroup_attr != NULL &&
1771             !valid_ldap_attr(pgcfg->ad_unixgroup_attr)) {
1772                 idmapdlog(LOG_ERR, "config/ad_unixgroup_attr=%s is not a "
1773                     "valid LDAP attribute name", pgcfg->ad_unixgroup_attr);
1774                 return (-3);
1775         }
1776 
1777         rc = get_val_astring(handles, "nldap_winname_attr",
1778             &pgcfg->nldap_winname_attr);
1779         if (rc != 0)
1780                 return (-2);
1781         if (pgcfg->nldap_winname_attr != NULL &&
1782             !valid_ldap_attr(pgcfg->nldap_winname_attr)) {
1783                 idmapdlog(LOG_ERR, "config/nldap_winname_attr=%s is not a "
1784                     "valid LDAP attribute name", pgcfg->nldap_winname_attr);
1785                 return (-3);
1786         }
1787         if (pgcfg->ad_unixuser_attr == NULL &&
1788             pgcfg->ad_unixgroup_attr == NULL &&
1789             pgcfg->nldap_winname_attr == NULL) {
1790                 idmapdlog(LOG_ERR,
1791                     "If config/directory_based_mapping property is set to "
1792                     "\"name\" then at least one of the following name mapping "
1793                     "attributes must be specified. (config/ad_unixuser_attr OR "
1794                     "config/ad_unixgroup_attr OR config/nldap_winname_attr)");
1795                 return (-3);
1796         }
1797 
1798         return (rc);
1799 }
1800 
1801 static
1802 void
1803 log_if_unable(const void *val, const char *what)
1804 {
1805         if (val == NULL) {
1806                 idmapdlog(LOG_DEBUG, "unable to discover %s", what);
1807         }
1808 }
1809 
1810 static
1811 void
1812 discover_trusted_domains(idmap_pg_config_t *pgcfg, ad_disc_t ad_ctx)
1813 {
1814         ad_disc_t trusted_ctx;
1815         int i, j, k, l;
1816         char *forestname;
1817         int num_trusteddomains;
1818         boolean_t new_forest;
1819         char *trusteddomain;
1820         ad_disc_ds_t *globalcatalog;
1821         idmap_trustedforest_t *trustedforests;
1822         ad_disc_domainsinforest_t *domainsinforest;
1823 
1824         pgcfg->trusted_domains =
1825             ad_disc_get_TrustedDomains(ad_ctx, NULL);
1826 
1827         if (pgcfg->forest_name != NULL && pgcfg->trusted_domains != NULL &&
1828             pgcfg->trusted_domains[0].domain[0] != '\0') {
1829                 /*
1830                  * We have trusted domains.  We need to go through every
1831                  * one and find its forest. If it is a new forest we then need
1832                  * to find its Global Catalog and the domains in the forest
1833                  */
1834                 for (i = 0; pgcfg->trusted_domains[i].domain[0] != '\0'; i++)
1835                         continue;
1836                 num_trusteddomains = i;
1837 
1838                 trustedforests = calloc(num_trusteddomains,
1839                     sizeof (idmap_trustedforest_t));
1840                 j = 0;
1841                 for (i = 0; pgcfg->trusted_domains[i].domain[0] != '\0'; i++) {
1842                         trusteddomain = pgcfg->trusted_domains[i].domain;
1843                         trusted_ctx = ad_disc_init();
1844                         (void) ad_disc_set_DomainName(trusted_ctx,
1845                             trusteddomain);
1846                         forestname =
1847                             ad_disc_get_ForestName(trusted_ctx, NULL);
1848                         if (forestname == NULL) {
1849                                 if (DBG(CONFIG, 1)) {
1850                                         idmapdlog(LOG_DEBUG,
1851                                             "unable to discover Forest Name"
1852                                             " for the trusted domain %s",
1853                                             trusteddomain);
1854                                 }
1855                                 ad_disc_fini(trusted_ctx);
1856                                 continue;
1857                         }
1858 
1859                         if (strcasecmp(forestname, pgcfg->forest_name) == 0) {
1860                                 /*
1861                                  * Ignore the domain as it is part of
1862                                  * the primary forest
1863                                  */
1864                                 free(forestname);
1865                                 ad_disc_fini(trusted_ctx);
1866                                 continue;
1867                         }
1868 
1869                         /* Is this a new forest? */
1870                         new_forest = B_TRUE;
1871                         for (k = 0; k < j; k++) {
1872                                 if (strcasecmp(forestname,
1873                                     trustedforests[k].forest_name) == 0) {
1874                                         new_forest = B_FALSE;
1875                                         domainsinforest =
1876                                             trustedforests[k].domains_in_forest;
1877                                         break;
1878                                 }
1879                         }
1880                         if (!new_forest) {
1881                                 /* Mark the domain as trusted */
1882                                 for (l = 0;
1883                                     domainsinforest[l].domain[0] != '\0'; l++) {
1884                                         if (domain_eq(trusteddomain,
1885                                             domainsinforest[l].domain)) {
1886                                                 domainsinforest[l].trusted =
1887                                                     TRUE;
1888                                                 break;
1889                                         }
1890                                 }
1891                                 free(forestname);
1892                                 ad_disc_fini(trusted_ctx);
1893                                 continue;
1894                         }
1895 
1896                         /*
1897                          * Get the Global Catalog and the domains in
1898                          * this new forest.
1899                          */
1900                         globalcatalog =
1901                             ad_disc_get_GlobalCatalog(trusted_ctx,
1902                             AD_DISC_PREFER_SITE, NULL);
1903                         if (globalcatalog == NULL) {
1904                                 if (DBG(CONFIG, 1)) {
1905                                         idmapdlog(LOG_DEBUG,
1906                                             "unable to discover Global Catalog"
1907                                             " for the trusted domain %s",
1908                                             trusteddomain);
1909                                 }
1910                                 free(forestname);
1911                                 ad_disc_fini(trusted_ctx);
1912                                 continue;
1913                         }
1914                         domainsinforest =
1915                             ad_disc_get_DomainsInForest(trusted_ctx, NULL);
1916                         if (domainsinforest == NULL) {
1917                                 if (DBG(CONFIG, 1)) {
1918                                         idmapdlog(LOG_DEBUG,
1919                                             "unable to discover Domains in the"
1920                                             " Forest for the trusted domain %s",
1921                                             trusteddomain);
1922                                 }
1923                                 free(globalcatalog);
1924                                 free(forestname);
1925                                 ad_disc_fini(trusted_ctx);
1926                                 continue;
1927                         }
1928 
1929                         trustedforests[j].forest_name = forestname;
1930                         trustedforests[j].global_catalog = globalcatalog;
1931                         trustedforests[j].domains_in_forest = domainsinforest;
1932                         j++;
1933                         /* Mark the domain as trusted */
1934                         for (l = 0; domainsinforest[l].domain[0] != '\0';
1935                             l++) {
1936                                 if (domain_eq(trusteddomain,
1937                                     domainsinforest[l].domain)) {
1938                                         domainsinforest[l].trusted = TRUE;
1939                                         break;
1940                                 }
1941                         }
1942                         ad_disc_fini(trusted_ctx);
1943                 }
1944                 if (j > 0) {
1945                         pgcfg->num_trusted_forests = j;
1946                         pgcfg->trusted_forests = trustedforests;
1947                 } else {
1948                         free(trustedforests);
1949                 }
1950         }
1951 }
1952 
1953 /*
1954  * This is the half of idmap_cfg_load() that auto-discovers values of
1955  * discoverable properties that weren't already set via SMF properties.
1956  *
1957  * idmap_cfg_discover() is called *after* idmap_cfg_load_smf(), so it
1958  * needs to be careful not to overwrite any properties set in SMF.
1959  */
1960 static void
1961 idmap_cfg_discover1(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg)
1962 {
1963         ad_disc_t ad_ctx = handles->ad_ctx;
1964         FILE *status_fp = NULL;
1965         time_t t0, t1;
1966 
1967         t0 = time(NULL);
1968         if (DBG(CONFIG, 1))
1969                 idmapdlog(LOG_DEBUG, "Running domain discovery.");
1970 
1971         (void) unlink(IDMAP_CACHEDIR "/discovery.log");
1972         status_fp = fopen(IDMAP_CACHEDIR "/discovery.log", "w");
1973         if (status_fp) {
1974                 (void) fchmod(fileno(status_fp), 0644);
1975                 ad_disc_set_StatusFP(ad_ctx, status_fp);
1976         }
1977 
1978         if (pgcfg->domain_name == NULL) {
1979                 idmapdlog(LOG_DEBUG, "No domain name specified.");
1980                 if (status_fp)
1981                         (void) fprintf(status_fp, "(no domain name)\n");
1982                 goto out;
1983         }
1984 
1985         if (pgcfg->domain_controller == NULL)
1986                 pgcfg->domain_controller =
1987                     ad_disc_get_DomainController(ad_ctx,
1988                     AD_DISC_PREFER_SITE,
1989                     &pgcfg->domain_controller_auto_disc);
1990 
1991         if (pgcfg->domain_guid == NULL) {
1992                 char buf[UUID_PRINTABLE_STRING_LENGTH];
1993                 uchar_t *u = ad_disc_get_DomainGUID(ad_ctx,
1994                     &pgcfg->domain_guid_auto_disc);
1995                 (void) memset(buf, 0, sizeof (buf));
1996                 if (u != NULL) {
1997                         uuid_unparse(u, buf);
1998                         pgcfg->domain_guid = strdup(buf);
1999                 }
2000         }
2001 
2002         if (pgcfg->forest_name == NULL)
2003                 pgcfg->forest_name = ad_disc_get_ForestName(ad_ctx,
2004                     &pgcfg->forest_name_auto_disc);
2005 
2006         if (pgcfg->site_name == NULL)
2007                 pgcfg->site_name = ad_disc_get_SiteName(ad_ctx,
2008                     &pgcfg->site_name_auto_disc);
2009 
2010         if (DBG(CONFIG, 1)) {
2011                 log_if_unable(pgcfg->domain_name, "Domain Name");
2012                 log_if_unable(pgcfg->domain_controller,
2013                     "Domain Controller");
2014                 log_if_unable(pgcfg->domain_guid, "Domain GUID");
2015                 log_if_unable(pgcfg->forest_name, "Forest Name");
2016                 log_if_unable(pgcfg->site_name, "Site Name");
2017         }
2018 
2019 out:
2020         if (status_fp) {
2021                 ad_disc_set_StatusFP(ad_ctx, NULL);
2022                 (void) fclose(status_fp);
2023                 status_fp = NULL;
2024         }
2025 
2026         if (DBG(CONFIG, 1))
2027                 idmapdlog(LOG_DEBUG, "Domain discovery done.");
2028 
2029         /*
2030          * Log when this took more than 15 sec.
2031          */
2032         t1 = time(NULL);
2033         if (t1 > (t0 + 15)) {
2034                 idmapdlog(LOG_NOTICE, "Domain discovery took %d sec.",
2035                     (int)(t1 - t0));
2036                 idmapdlog(LOG_NOTICE, "Check the DNS configuration.");
2037         }
2038 }
2039 
2040 /*
2041  * This is the second part of discovery, which can take a while.
2042  * We don't want to hold up parties who just want to know what
2043  * domain controller we're using (like smbd), so this part runs
2044  * after we've updated that info in the "live" config and told
2045  * such consumers to go ahead.
2046  *
2047  * This is a lot like idmap_cfg_discover(), but used LDAP queries
2048  * get the forest information from the global catalog servers.
2049  *
2050  * Note: the previous update_* calls have usually nuked any
2051  * useful information from pgcfg before we get here, so we
2052  * can only use it store discovery results, not to read.
2053  */
2054 static void
2055 idmap_cfg_discover2(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg)
2056 {
2057         ad_disc_t ad_ctx = handles->ad_ctx;
2058         FILE *status_fp = NULL;
2059         time_t t0, t1;
2060 
2061         t0 = time(NULL);
2062         if (DBG(CONFIG, 1))
2063                 idmapdlog(LOG_DEBUG, "Running forest discovery.");
2064 
2065         status_fp = fopen(IDMAP_CACHEDIR "/discovery.log", "a");
2066         if (status_fp)
2067                 ad_disc_set_StatusFP(ad_ctx, status_fp);
2068 
2069         if (pgcfg->global_catalog == NULL)
2070                 pgcfg->global_catalog =
2071                     ad_disc_get_GlobalCatalog(ad_ctx,
2072                     AD_DISC_PREFER_SITE,
2073                     &pgcfg->global_catalog_auto_disc);
2074 
2075         if (pgcfg->global_catalog != NULL) {
2076                 pgcfg->domains_in_forest =
2077                     ad_disc_get_DomainsInForest(ad_ctx, NULL);
2078 
2079                 if (!pgcfg->disable_cross_forest_trusts)
2080                         discover_trusted_domains(pgcfg, ad_ctx);
2081         }
2082 
2083         if (DBG(CONFIG, 1)) {
2084                 log_if_unable(pgcfg->global_catalog, "Global Catalog");
2085                 log_if_unable(pgcfg->domains_in_forest,
2086                     "Domains in the Forest");
2087                 /* Empty trusted domains list is OK. */
2088         }
2089 
2090         if (status_fp) {
2091                 ad_disc_set_StatusFP(ad_ctx, NULL);
2092                 (void) fclose(status_fp);
2093                 status_fp = NULL;
2094         }
2095 
2096         if (DBG(CONFIG, 1))
2097                 idmapdlog(LOG_DEBUG, "Forest discovery done.");
2098 
2099         /*
2100          * Log when this took more than 30 sec.
2101          */
2102         t1 = time(NULL);
2103         if (t1 > (t0 + 30)) {
2104                 idmapdlog(LOG_NOTICE, "Forest discovery took %d sec.",
2105                     (int)(t1 - t0));
2106                 idmapdlog(LOG_NOTICE, "Check AD join status.");
2107         }
2108 }
2109 
2110 
2111 /*
2112  * idmap_cfg_load() is called at startup, and periodically via the
2113  * update thread when the auto-discovery TTLs expire, as well as part of
2114  * the refresh method, to update the current configuration.  It always
2115  * reads from SMF, but you still have to refresh the service after
2116  * changing the config pg in order for the changes to take effect.
2117  *
2118  * There is one flag:
2119  *
2120  *  - CFG_DISCOVER
2121  *
2122  * If CFG_DISCOVER is set then idmap_cfg_load() calls
2123  * idmap_cfg_discover() to discover, via DNS and LDAP lookups, property
2124  * values that weren't set in SMF.
2125  *
2126  * idmap_cfg_load() will log (to LOG_NOTICE) whether the configuration
2127  * changed.
2128  *
2129  * Return values: 0 -> success, -1 -> failure, -2 -> hard failures
2130  * reading from SMF.
2131  */
2132 int
2133 idmap_cfg_load(idmap_cfg_t *cfg, int flags)
2134 {
2135         const ad_disc_t ad_ctx = cfg->handles.ad_ctx;
2136         int rc = 0;
2137         int errors;
2138         int changed = 0;
2139         int dc_changed = 0;
2140         int ad_reload_required = 0;
2141         idmap_pg_config_t new_pgcfg, *live_pgcfg;
2142 
2143         if (DBG(CONFIG, 1))
2144                 idmapdlog(LOG_DEBUG, "Loading configuration.");
2145 
2146         live_pgcfg = &cfg->pgcfg;
2147         (void) memset(&new_pgcfg, 0, sizeof (new_pgcfg));
2148 
2149         (void) pthread_mutex_lock(&cfg->handles.mutex);
2150 
2151         if ((rc = idmap_cfg_load_smf(&cfg->handles, &new_pgcfg, &errors)) < -1)
2152                 goto err;
2153 
2154         if (flags & CFG_DISCOVER) {
2155 
2156                 ad_disc_refresh(ad_ctx);
2157 
2158                 /*
2159                  * Unless we've been asked to forget the current DC,
2160                  * give preference (in order) to the preferred DC if
2161                  * configured, or the current DC.  These preferences
2162                  * reduce undesirable DC changes.
2163                  */
2164                 if (flags & CFG_FORGET_DC) {
2165                         (void) ad_disc_set_PreferredDC(ad_ctx, NULL);
2166                 } else if (new_pgcfg.preferred_dc != NULL) {
2167                         (void) ad_disc_set_PreferredDC(ad_ctx,
2168                             new_pgcfg.preferred_dc);
2169                 } else if (live_pgcfg->domain_controller != NULL) {
2170                         (void) ad_disc_set_PreferredDC(ad_ctx,
2171                             live_pgcfg->domain_controller);
2172                 } else {
2173                         (void) ad_disc_set_PreferredDC(ad_ctx, NULL);
2174                 }
2175 
2176                 /*
2177                  * We want a way to tell adspriv_getdcname_1_svc()
2178                  * (and others) that discovery is running and therefore
2179                  * they may want to wait a bit or return an error...
2180                  */
2181                 (void) mutex_lock(&_idmapdstate.addisc_lk);
2182                 _idmapdstate.addisc_st |= ADDISC_ST_RUNNING;
2183                 (void) mutex_unlock(&_idmapdstate.addisc_lk);
2184 
2185                 idmap_cfg_discover1(&cfg->handles, &new_pgcfg);
2186 
2187                 WRLOCK_CONFIG();
2188                 (void) mutex_lock(&_idmapdstate.addisc_lk);
2189                 _idmapdstate.addisc_st = 0;
2190                 (void) cond_broadcast(&_idmapdstate.addisc_cv);
2191                 (void) mutex_unlock(&_idmapdstate.addisc_lk);
2192         } else {
2193                 WRLOCK_CONFIG();
2194         }
2195 
2196         /* Non-discoverable props updated here */
2197 
2198         changed += update_uint64(&live_pgcfg->list_size_limit,
2199             &new_pgcfg.list_size_limit, "list_size_limit");
2200 
2201         changed += update_uint64(&live_pgcfg->id_cache_timeout,
2202             &new_pgcfg.id_cache_timeout, "id_cache_timeout");
2203 
2204         changed += update_uint64(&live_pgcfg->name_cache_timeout,
2205             &new_pgcfg.name_cache_timeout, "name_cache_timeout");
2206 
2207         changed += update_uint64(&live_pgcfg->rediscovery_interval,
2208             &new_pgcfg.rediscovery_interval, "rediscovery_interval");
2209 
2210         changed += update_string(&live_pgcfg->machine_sid,
2211             &new_pgcfg.machine_sid, "machine_sid");
2212 
2213         changed += update_bool(&live_pgcfg->eph_map_unres_sids,
2214             &new_pgcfg.eph_map_unres_sids, "unresolvable_sid_mapping");
2215 
2216         changed += update_bool(&live_pgcfg->use_ads,
2217             &new_pgcfg.use_ads, "use_ads");
2218 
2219         changed += update_bool(&live_pgcfg->use_lsa,
2220             &new_pgcfg.use_lsa, "use_lsa");
2221 
2222         changed += update_bool(&live_pgcfg->disable_cross_forest_trusts,
2223             &new_pgcfg.disable_cross_forest_trusts,
2224             "disable_cross_forest_trusts");
2225 
2226         changed += update_enum(&live_pgcfg->directory_based_mapping,
2227             &new_pgcfg.directory_based_mapping, "directory_based_mapping",
2228             directory_mapping_map);
2229 
2230         changed += update_string(&live_pgcfg->ad_unixuser_attr,
2231             &new_pgcfg.ad_unixuser_attr, "ad_unixuser_attr");
2232 
2233         changed += update_string(&live_pgcfg->ad_unixgroup_attr,
2234             &new_pgcfg.ad_unixgroup_attr, "ad_unixgroup_attr");
2235 
2236         changed += update_string(&live_pgcfg->nldap_winname_attr,
2237             &new_pgcfg.nldap_winname_attr, "nldap_winname_attr");
2238 
2239         changed += update_string(&live_pgcfg->default_domain,
2240             &new_pgcfg.default_domain, "default_domain");
2241 
2242         changed += update_dirs(&live_pgcfg->preferred_dc,
2243             &new_pgcfg.preferred_dc, "preferred_dc");
2244 
2245         /* Props that can be discovered or set in SMF updated here */
2246 
2247         if (update_string(&live_pgcfg->domain_name,
2248             &new_pgcfg.domain_name, "domain_name")) {
2249                 changed++;
2250                 ad_reload_required = TRUE;
2251                 idmapd_set_krb5_realm(live_pgcfg->domain_name);
2252         }
2253         live_pgcfg->domain_name_auto_disc = new_pgcfg.domain_name_auto_disc;
2254 
2255         changed += update_string(&live_pgcfg->domain_guid,
2256             &new_pgcfg.domain_guid, "domain_guid");
2257         live_pgcfg->domain_guid_auto_disc = new_pgcfg.domain_guid_auto_disc;
2258 
2259         dc_changed = update_dirs(&live_pgcfg->domain_controller,
2260             &new_pgcfg.domain_controller, "domain_controller");
2261         changed += dc_changed;
2262         live_pgcfg->domain_controller_auto_disc =
2263             new_pgcfg.domain_controller_auto_disc;
2264 
2265         changed += update_string(&live_pgcfg->forest_name,
2266             &new_pgcfg.forest_name, "forest_name");
2267         live_pgcfg->forest_name_auto_disc = new_pgcfg.forest_name_auto_disc;
2268 
2269         changed += update_string(&live_pgcfg->site_name,
2270             &new_pgcfg.site_name, "site_name");
2271         live_pgcfg->site_name_auto_disc = new_pgcfg.site_name_auto_disc;
2272 
2273         if (DBG(CONFIG, 1)) {
2274                 if (changed)
2275                         idmapdlog(LOG_NOTICE, "Configuration changed");
2276                 else
2277                         idmapdlog(LOG_NOTICE, "Configuration unchanged");
2278         }
2279 
2280         UNLOCK_CONFIG();
2281 
2282         if (dc_changed != 0) {
2283                 notify_dc_changed();
2284         }
2285 
2286         /*
2287          * Discovery2 can take a while.
2288          */
2289         if (flags & CFG_DISCOVER) {
2290                 if (live_pgcfg->domain_name != NULL &&
2291                     live_pgcfg->forest_name != NULL)
2292                         idmap_cfg_discover2(&cfg->handles, &new_pgcfg);
2293                 ad_disc_done(ad_ctx);
2294         }
2295 
2296         WRLOCK_CONFIG();
2297 
2298         /* More props that can be discovered or set in SMF */
2299 
2300         changed += update_dirs(&live_pgcfg->global_catalog,
2301             &new_pgcfg.global_catalog, "global_catalog");
2302         live_pgcfg->global_catalog_auto_disc =
2303             new_pgcfg.global_catalog_auto_disc;
2304 
2305         /* Props that are only discovered (never in SMF) */
2306 
2307         if (update_domains_in_forest(&live_pgcfg->domains_in_forest,
2308             &new_pgcfg.domains_in_forest, "domains_in_forest")) {
2309                 changed++;
2310                 ad_reload_required = TRUE;
2311         }
2312 
2313         if (update_trusted_domains(&live_pgcfg->trusted_domains,
2314             &new_pgcfg.trusted_domains, "trusted_domains")) {
2315                 changed++;
2316                 if (live_pgcfg->trusted_domains != NULL &&
2317                     live_pgcfg->trusted_domains[0].domain[0] != '\0')
2318                         ad_reload_required = TRUE;
2319         }
2320 
2321         if (update_trusted_forest(&live_pgcfg->trusted_forests,
2322             &live_pgcfg->num_trusted_forests, &new_pgcfg.trusted_forests,
2323             &new_pgcfg.num_trusted_forests, "trusted_forest")) {
2324                 changed++;
2325                 if (live_pgcfg->trusted_forests != NULL)
2326                         ad_reload_required = TRUE;
2327         }
2328 
2329         if (DBG(CONFIG, 1)) {
2330                 if (changed)
2331                         idmapdlog(LOG_NOTICE, "Configuration changed");
2332                 else
2333                         idmapdlog(LOG_NOTICE, "Configuration unchanged");
2334         }
2335 
2336         UNLOCK_CONFIG();
2337 
2338         if (ad_reload_required)
2339                 reload_ad();
2340 
2341         idmap_cfg_unload(&new_pgcfg);
2342 
2343 err:
2344         (void) pthread_mutex_unlock(&cfg->handles.mutex);
2345 
2346         if (rc < -1)
2347                 return (rc);
2348 
2349         return ((errors == 0) ? 0 : -1);
2350 }
2351 
2352 /*
2353  * Initialize 'cfg'.
2354  */
2355 idmap_cfg_t *
2356 idmap_cfg_init()
2357 {
2358         idmap_cfg_handles_t *handles;
2359 
2360         /* First the smf repository handles: */
2361         idmap_cfg_t *cfg = calloc(1, sizeof (idmap_cfg_t));
2362         if (!cfg) {
2363                 idmapdlog(LOG_ERR, "Out of memory");
2364                 return (NULL);
2365         }
2366         handles = &cfg->handles;
2367 
2368         (void) pthread_mutex_init(&handles->mutex, NULL);
2369 
2370         if (!(handles->main = scf_handle_create(SCF_VERSION))) {
2371                 idmapdlog(LOG_ERR, "scf_handle_create() failed: %s",
2372                     scf_strerror(scf_error()));
2373                 goto error;
2374         }
2375 
2376         if (scf_handle_bind(handles->main) < 0) {
2377                 idmapdlog(LOG_ERR, "scf_handle_bind() failed: %s",
2378                     scf_strerror(scf_error()));
2379                 goto error;
2380         }
2381 
2382         if (!(handles->service = scf_service_create(handles->main)) ||
2383             !(handles->instance = scf_instance_create(handles->main)) ||
2384             !(handles->config_pg = scf_pg_create(handles->main)) ||
2385             !(handles->debug_pg = scf_pg_create(handles->main))) {
2386                 idmapdlog(LOG_ERR, "scf handle creation failed: %s",
2387                     scf_strerror(scf_error()));
2388                 goto error;
2389         }
2390 
2391         if (scf_handle_decode_fmri(handles->main,
2392             FMRI_BASE "/:properties/" CONFIG_PG,
2393             NULL,                               /* scope */
2394             handles->service,                /* service */
2395             handles->instance,               /* instance */
2396             handles->config_pg,              /* pg */
2397             NULL,                               /* prop */
2398             SCF_DECODE_FMRI_EXACT) < 0) {
2399                 idmapdlog(LOG_ERR, "scf_handle_decode_fmri() failed: %s",
2400                     scf_strerror(scf_error()));
2401                 goto error;
2402         }
2403 
2404         if (scf_service_get_pg(handles->service,
2405             DEBUG_PG, handles->debug_pg) < 0) {
2406                 idmapdlog(LOG_ERR, "Property group \"%s\": %s",
2407                     DEBUG_PG, scf_strerror(scf_error()));
2408                 goto error;
2409         }
2410 
2411         check_smf_debug_mode(handles);
2412 
2413         /* Initialize AD Auto Discovery context */
2414         handles->ad_ctx = ad_disc_init();
2415         if (handles->ad_ctx == NULL)
2416                 goto error;
2417 
2418         return (cfg);
2419 
2420 error:
2421         (void) idmap_cfg_fini(cfg);
2422         return (NULL);
2423 }
2424 
2425 void
2426 idmap_cfg_unload(idmap_pg_config_t *pgcfg)
2427 {
2428 
2429         if (pgcfg->default_domain) {
2430                 free(pgcfg->default_domain);
2431                 pgcfg->default_domain = NULL;
2432         }
2433         if (pgcfg->domain_name) {
2434                 free(pgcfg->domain_name);
2435                 pgcfg->domain_name = NULL;
2436         }
2437         if (pgcfg->domain_guid) {
2438                 free(pgcfg->domain_guid);
2439                 pgcfg->domain_guid = NULL;
2440         }
2441         if (pgcfg->machine_sid) {
2442                 free(pgcfg->machine_sid);
2443                 pgcfg->machine_sid = NULL;
2444         }
2445         if (pgcfg->domain_controller) {
2446                 free(pgcfg->domain_controller);
2447                 pgcfg->domain_controller = NULL;
2448         }
2449         if (pgcfg->forest_name) {
2450                 free(pgcfg->forest_name);
2451                 pgcfg->forest_name = NULL;
2452         }
2453         if (pgcfg->site_name) {
2454                 free(pgcfg->site_name);
2455                 pgcfg->site_name = NULL;
2456         }
2457         if (pgcfg->global_catalog) {
2458                 free(pgcfg->global_catalog);
2459                 pgcfg->global_catalog = NULL;
2460         }
2461         if (pgcfg->trusted_domains) {
2462                 free(pgcfg->trusted_domains);
2463                 pgcfg->trusted_domains = NULL;
2464         }
2465         if (pgcfg->trusted_forests)
2466                 free_trusted_forests(&pgcfg->trusted_forests,
2467                     &pgcfg->num_trusted_forests);
2468 
2469         if (pgcfg->ad_unixuser_attr) {
2470                 free(pgcfg->ad_unixuser_attr);
2471                 pgcfg->ad_unixuser_attr = NULL;
2472         }
2473         if (pgcfg->ad_unixgroup_attr) {
2474                 free(pgcfg->ad_unixgroup_attr);
2475                 pgcfg->ad_unixgroup_attr = NULL;
2476         }
2477         if (pgcfg->nldap_winname_attr) {
2478                 free(pgcfg->nldap_winname_attr);
2479                 pgcfg->nldap_winname_attr = NULL;
2480         }
2481 }
2482 
2483 int
2484 idmap_cfg_fini(idmap_cfg_t *cfg)
2485 {
2486         idmap_cfg_handles_t *handles = &cfg->handles;
2487         idmap_cfg_unload(&cfg->pgcfg);
2488 
2489         (void) pthread_mutex_destroy(&handles->mutex);
2490         scf_pg_destroy(handles->config_pg);
2491         if (handles->debug_pg != NULL)
2492                 scf_pg_destroy(handles->debug_pg);
2493         scf_instance_destroy(handles->instance);
2494         scf_service_destroy(handles->service);
2495         scf_handle_destroy(handles->main);
2496         if (handles->ad_ctx != NULL)
2497                 ad_disc_fini(handles->ad_ctx);
2498         free(cfg);
2499 
2500         return (0);
2501 }
2502 
2503 void
2504 idmap_cfg_poke_updates(void)
2505 {
2506         int prev_st;
2507 
2508         if (DBG(CONFIG, 1)) {
2509                 idmapdlog(LOG_INFO, "idmap_cfg_poke_updates");
2510         }
2511 
2512         (void) mutex_lock(&_idmapdstate.addisc_lk);
2513         prev_st = _idmapdstate.addisc_st;
2514         _idmapdstate.addisc_st |= ADDISC_ST_REQUESTED;
2515         (void) mutex_unlock(&_idmapdstate.addisc_lk);
2516 
2517         if (prev_st & ADDISC_ST_REQUESTED) {
2518                 idmapdlog(LOG_DEBUG, "already poked");
2519         } else {
2520                 idmapdlog(LOG_DEBUG, "port send poke");
2521                 (void) port_send(idmapd_ev_port, POKE_AUTO_DISCOVERY, NULL);
2522         }
2523 }
2524 
2525 void
2526 idmap_cfg_force_rediscovery(void)
2527 {
2528         int prev_st;
2529 
2530         if (DBG(CONFIG, 1)) {
2531                 idmapdlog(LOG_INFO, "idmap_cfg_force_rediscovery");
2532         }
2533 
2534         (void) mutex_lock(&_idmapdstate.addisc_lk);
2535         prev_st = _idmapdstate.addisc_st;
2536         _idmapdstate.addisc_st |= ADDISC_ST_REQUESTED;
2537         (void) mutex_unlock(&_idmapdstate.addisc_lk);
2538 
2539         if (prev_st & ADDISC_ST_REQUESTED) {
2540                 idmapdlog(LOG_DEBUG, "already kicked");
2541         } else {
2542                 idmapdlog(LOG_DEBUG, "port send kick");
2543                 (void) port_send(idmapd_ev_port, KICK_AUTO_DISCOVERY, NULL);
2544         }
2545 }
2546 
2547 /*ARGSUSED*/
2548 void
2549 idmap_cfg_hup_handler(int sig)
2550 {
2551         if (idmapd_ev_port >= 0)
2552                 (void) port_send(idmapd_ev_port, RECONFIGURE, NULL);
2553 }
2554 
2555 /*
2556  * Upgrade the debug flags.
2557  *
2558  * We're replacing a single debug flag with a fine-grained mechanism that
2559  * is also capable of considerably more verbosity.  We'll take a stab at
2560  * producing roughly the same level of output.
2561  */
2562 static
2563 int
2564 upgrade_debug(idmap_cfg_handles_t *handles)
2565 {
2566         boolean_t debug_present;
2567         const char DEBUG_PROP[] = "debug";
2568         int rc;
2569 
2570         rc = prop_exists(handles, DEBUG_PROP, &debug_present);
2571 
2572         if (rc != 0)
2573                 return (rc);
2574 
2575         if (!debug_present)
2576                 return (0);
2577 
2578         idmapdlog(LOG_INFO,
2579             "Upgrading old %s/%s setting to %s/* settings.",
2580             CONFIG_PG, DEBUG_PROP, DEBUG_PG);
2581 
2582         rc = set_val_integer(handles, handles->debug_pg, "config", 1);
2583         if (rc != 0)
2584                 return (rc);
2585         rc = set_val_integer(handles, handles->debug_pg, "discovery", 1);
2586         if (rc != 0)
2587                 return (rc);
2588 
2589         rc = del_val(handles, handles->config_pg, DEBUG_PROP);
2590         if (rc != 0)
2591                 return (rc);
2592 
2593         return (0);
2594 }
2595 
2596 /*
2597  * Upgrade the DS mapping flags.
2598  *
2599  * If the old ds_name_mapping_enabled flag is present, then
2600  *     if the new directory_based_mapping value is present, then
2601  *         if the two are compatible, delete the old and note it
2602  *         else delete the old and warn
2603  *     else
2604  *         set the new based on the old, and note it
2605  *         delete the old
2606  */
2607 static
2608 int
2609 upgrade_directory_mapping(idmap_cfg_handles_t *handles)
2610 {
2611         boolean_t legacy_ds_name_mapping_present;
2612         const char DS_NAME_MAPPING_ENABLED[] = "ds_name_mapping_enabled";
2613         const char DIRECTORY_BASED_MAPPING[] = "directory_based_mapping";
2614         int rc;
2615 
2616         rc = prop_exists(handles, DS_NAME_MAPPING_ENABLED,
2617             &legacy_ds_name_mapping_present);
2618 
2619         if (rc != 0)
2620                 return (rc);
2621 
2622         if (!legacy_ds_name_mapping_present)
2623                 return (0);
2624 
2625         boolean_t legacy_ds_name_mapping_enabled;
2626         rc = get_val_bool(handles, DS_NAME_MAPPING_ENABLED,
2627             &legacy_ds_name_mapping_enabled, B_FALSE);
2628         if (rc != 0)
2629                 return (rc);
2630 
2631         char *legacy_mode;
2632         char *legacy_bool_string;
2633         if (legacy_ds_name_mapping_enabled) {
2634                 legacy_mode = "name";
2635                 legacy_bool_string = "true";
2636         } else {
2637                 legacy_mode = "none";
2638                 legacy_bool_string = "false";
2639         }
2640 
2641         char *directory_based_mapping;
2642         rc = get_val_astring(handles, DIRECTORY_BASED_MAPPING,
2643             &directory_based_mapping);
2644         if (rc != 0)
2645                 return (rc);
2646 
2647         if (directory_based_mapping == NULL) {
2648                 idmapdlog(LOG_INFO,
2649                     "Upgrading old %s=%s setting\n"
2650                     "to %s=%s.",
2651                     DS_NAME_MAPPING_ENABLED, legacy_bool_string,
2652                     DIRECTORY_BASED_MAPPING, legacy_mode);
2653                 rc = set_val_astring(handles, handles->config_pg,
2654                     DIRECTORY_BASED_MAPPING, legacy_mode);
2655                 if (rc != 0)
2656                         return (rc);
2657         } else {
2658                 boolean_t new_name_mapping;
2659                 if (strcasecmp(directory_based_mapping, "name") == 0)
2660                         new_name_mapping = B_TRUE;
2661                 else
2662                         new_name_mapping = B_FALSE;
2663 
2664                 if (legacy_ds_name_mapping_enabled == new_name_mapping) {
2665                         idmapdlog(LOG_INFO,
2666                             "Automatically removing old %s=%s setting\n"
2667                             "in favor of %s=%s.",
2668                             DS_NAME_MAPPING_ENABLED, legacy_bool_string,
2669                             DIRECTORY_BASED_MAPPING, directory_based_mapping);
2670                 } else {
2671                         idmapdlog(LOG_WARNING,
2672                             "Removing conflicting %s=%s setting\n"
2673                             "in favor of %s=%s.",
2674                             DS_NAME_MAPPING_ENABLED, legacy_bool_string,
2675                             DIRECTORY_BASED_MAPPING, directory_based_mapping);
2676                 }
2677                 free(directory_based_mapping);
2678         }
2679 
2680         rc = del_val(handles, handles->config_pg, DS_NAME_MAPPING_ENABLED);
2681         if (rc != 0)
2682                 return (rc);
2683 
2684         return (0);
2685 }
2686 
2687 /*
2688  * Do whatever is necessary to upgrade idmap's configuration before
2689  * we load it.
2690  */
2691 int
2692 idmap_cfg_upgrade(idmap_cfg_t *cfg)
2693 {
2694         int rc;
2695 
2696         rc = upgrade_directory_mapping(&cfg->handles);
2697         if (rc != 0)
2698                 return (rc);
2699 
2700         rc = upgrade_debug(&cfg->handles);
2701         if (rc != 0)
2702                 return (rc);
2703 
2704         return (0);
2705 }
2706 
2707 /*
2708  * The LDAP code passes principal names lacking any
2709  * realm information, which causes mech_krb5 to do
2710  * awful things trying to figure out the realm.
2711  * Avoid that by making sure it has a default,
2712  * even when krb5.conf is not configured.
2713  */
2714 static void
2715 idmapd_set_krb5_realm(char *domain)
2716 {
2717         static char realm[MAXHOSTNAMELEN];
2718         size_t ilen, olen;
2719         int err;
2720 
2721         if (domain == NULL) {
2722                 (void) unsetenv("KRB5_DEFAULT_REALM");
2723                 return;
2724         }
2725 
2726         /* Convert to upper case, in place. */
2727         (void) strlcpy(realm, domain, sizeof (realm));
2728         olen = ilen = strlen(realm);
2729         (void) u8_textprep_str(realm, &ilen, realm, &olen,
2730             U8_TEXTPREP_TOUPPER, U8_UNICODE_LATEST, &err);
2731 
2732         (void) setenv("KRB5_DEFAULT_REALM", realm, 1);
2733 }