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