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