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