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