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