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