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