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