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