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