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