xref: /illumos-gate/usr/src/cmd/idmap/idmapd/idmap_config.c (revision bea83d026ee1bd1b2a2419e1d0232f107a5d7d9b)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Config routines common to idmap(1M) and idmapd(1M)
30  */
31 
32 #include <stdlib.h>
33 #include <strings.h>
34 #include <libintl.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include "idmapd.h"
38 #include <stdio.h>
39 #include <stdarg.h>
40 #include <uuid/uuid.h>
41 #include <pthread.h>
42 #include <port.h>
43 #include "addisc.h"
44 
45 #define	MACHINE_SID_LEN	(9 + UUID_LEN/4 * 11)
46 #define	FMRI_BASE "svc:/system/idmap"
47 #define	CONFIG_PG "config"
48 #define	GENERAL_PG "general"
49 /* initial length of the array for policy options/attributes: */
50 #define	DEF_ARRAY_LENGTH 16
51 
52 /*LINTLIBRARY*/
53 
54 
55 static const char *me = "idmapd";
56 
57 
58 static pthread_t update_thread_handle = 0;
59 
60 int hup_ev_port = -1;
61 extern int hupped;
62 
63 static int
64 generate_machine_sid(char **machine_sid)
65 {
66 	char *p;
67 	uuid_t uu;
68 	int i, j, len, rlen;
69 	uint32_t rid;
70 
71 	/*
72 	 * Generate and split 128-bit UUID into four 32-bit RIDs
73 	 * The machine_sid will be of the form S-1-5-N1-N2-N3-N4
74 	 * We depart from Windows here, which instead of 128
75 	 * bits worth of random numbers uses 96 bits.
76 	 */
77 
78 	*machine_sid = calloc(1, MACHINE_SID_LEN);
79 	if (*machine_sid == NULL) {
80 		idmapdlog(LOG_ERR, "%s: Out of memory", me);
81 		return (-1);
82 	}
83 	(void) strcpy(*machine_sid, "S-1-5-21");
84 	p = *machine_sid + strlen("S-1-5-21");
85 	len = MACHINE_SID_LEN - strlen("S-1-5-21");
86 
87 	uuid_clear(uu);
88 	uuid_generate_random(uu);
89 
90 	for (i = 0; i < UUID_LEN/4; i++) {
91 		j = i * 4;
92 		rid = (uu[j] << 24) | (uu[j + 1] << 16) |
93 		    (uu[j + 2] << 8) | (uu[j + 3]);
94 		rlen = snprintf(p, len, "-%u", rid);
95 		p += rlen;
96 		len -= rlen;
97 	}
98 
99 	return (0);
100 }
101 
102 /* Check if in the case of failure the original value of *val is preserved */
103 static int
104 get_val_int(idmap_cfg_handles_t *handles, char *name,
105 	void *val, scf_type_t type)
106 {
107 	int rc = 0;
108 
109 	scf_property_t *scf_prop = scf_property_create(handles->main);
110 	scf_value_t *value = scf_value_create(handles->main);
111 
112 	if (scf_pg_get_property(handles->config_pg, name, scf_prop) < 0)
113 	/* this is OK: the property is just undefined */
114 		goto destruction;
115 
116 
117 	if (scf_property_get_value(scf_prop, value) < 0)
118 	/* It is still OK when a property doesn't have any value */
119 		goto destruction;
120 
121 	switch (type) {
122 	case SCF_TYPE_BOOLEAN:
123 		rc = scf_value_get_boolean(value, val);
124 		break;
125 	case SCF_TYPE_COUNT:
126 		rc = scf_value_get_count(value, val);
127 		break;
128 	case SCF_TYPE_INTEGER:
129 		rc = scf_value_get_integer(value, val);
130 		break;
131 	default:
132 		idmapdlog(LOG_ERR, "%s: Invalid scf integer type (%d)",
133 		    me, type);
134 		rc = -1;
135 		break;
136 	}
137 
138 
139 destruction:
140 	scf_value_destroy(value);
141 	scf_property_destroy(scf_prop);
142 
143 	return (rc);
144 }
145 
146 static char *
147 scf_value2string(scf_value_t *value)
148 {
149 	int rc = -1;
150 	char buf_size = 127;
151 	int length;
152 	char *buf = NULL;
153 	buf = (char *) malloc(sizeof (char) * buf_size);
154 
155 	for (;;) {
156 		length = scf_value_get_astring(value, buf, buf_size);
157 		if (length < 0) {
158 			rc = -1;
159 			goto destruction;
160 		}
161 
162 		if (length == buf_size - 1) {
163 			buf_size *= 2;
164 			buf = (char *)realloc(buf, buf_size * sizeof (char));
165 			if (!buf) {
166 				idmapdlog(LOG_ERR, "%s: Out of memory", me);
167 				rc = -1;
168 				goto destruction;
169 			}
170 		} else {
171 			rc = 0;
172 			break;
173 		}
174 	}
175 
176 destruction:
177 	if (rc < 0) {
178 		if (buf)
179 			free(buf);
180 		buf = NULL;
181 	}
182 
183 	return (buf);
184 }
185 
186 static int
187 get_val_ds(idmap_cfg_handles_t *handles, const char *name, int defport,
188 		ad_disc_ds_t **val)
189 {
190 	ad_disc_ds_t *servers = NULL;
191 	scf_property_t *scf_prop;
192 	scf_value_t *value;
193 	scf_iter_t *iter;
194 	char *host, *portstr;
195 	int len, i;
196 	int count = 0;
197 	int rc = -1;
198 
199 	*val = NULL;
200 
201 restart:
202 	scf_prop = scf_property_create(handles->main);
203 	value = scf_value_create(handles->main);
204 	iter = scf_iter_create(handles->main);
205 
206 	if (scf_pg_get_property(handles->config_pg, name, scf_prop) < 0) {
207 		/* this is OK: the property is just undefined */
208 		rc = 0;
209 		goto destruction;
210 	}
211 
212 	if (scf_iter_property_values(iter, scf_prop) < 0) {
213 		idmapdlog(LOG_ERR,
214 		    "%s: scf_iter_property_values(%s) failed: %s",
215 		    me, name, scf_strerror(scf_error()));
216 		goto destruction;
217 	}
218 
219 	/* Workaround scf bugs -- can't reset an iteration */
220 	if (count == 0) {
221 		while (scf_iter_next_value(iter, value) > 0)
222 			count++;
223 
224 		if (count == 0) {
225 			/* no values */
226 			rc = 0;
227 			goto destruction;
228 		}
229 
230 		scf_value_destroy(value);
231 		scf_iter_destroy(iter);
232 		scf_property_destroy(scf_prop);
233 		goto restart;
234 	}
235 
236 	if ((servers = calloc(count + 1, sizeof (*servers))) == NULL) {
237 		idmapdlog(LOG_ERR, "%s: Out of memory", me);
238 		goto destruction;
239 	}
240 
241 	i = 0;
242 	while (i < count && scf_iter_next_value(iter, value) > 0) {
243 		servers[i].priority = 0;
244 		servers[i].weight = 100;
245 		servers[i].port = defport;
246 		if ((host = scf_value2string(value)) == NULL) {
247 			goto destruction;
248 		}
249 		if ((portstr = strchr(host, ':')) != NULL) {
250 			*portstr++ = '\0';
251 			servers[i].port = strtol(portstr,
252 			    (char **)NULL, 10);
253 			if (servers[i].port == 0)
254 				servers[i].port = defport;
255 		}
256 		len = strlcpy(servers[i].host, host,
257 		    sizeof (servers->host));
258 
259 		free(host);
260 
261 		/* Ignore this server if the hostname is too long */
262 		if (len < sizeof (servers->host))
263 			i++;
264 	}
265 
266 	*val = servers;
267 
268 	rc = 0;
269 
270 destruction:
271 	scf_value_destroy(value);
272 	scf_iter_destroy(iter);
273 	scf_property_destroy(scf_prop);
274 
275 	if (rc < 0) {
276 		if (servers)
277 			free(servers);
278 		*val = NULL;
279 	}
280 
281 	return (rc);
282 }
283 
284 
285 static int
286 get_val_astring(idmap_cfg_handles_t *handles, char *name, char **val)
287 {
288 	int rc = 0;
289 
290 	scf_property_t *scf_prop = scf_property_create(handles->main);
291 	scf_value_t *value = scf_value_create(handles->main);
292 
293 	*val = NULL;
294 
295 	if (scf_pg_get_property(handles->config_pg, name, scf_prop) < 0)
296 	/* this is OK: the property is just undefined */
297 		goto destruction;
298 
299 	if (scf_property_get_value(scf_prop, value) < 0) {
300 		idmapdlog(LOG_ERR,
301 		    "%s: scf_property_get_value(%s) failed: %s",
302 		    me, name, scf_strerror(scf_error()));
303 		rc = -1;
304 		goto destruction;
305 	}
306 
307 	if (!(*val = scf_value2string(value)))
308 		rc = -1;
309 
310 destruction:
311 	scf_value_destroy(value);
312 	scf_property_destroy(scf_prop);
313 
314 	if (rc < 0) {
315 		if (*val)
316 			free(*val);
317 		*val = NULL;
318 	}
319 
320 	return (rc);
321 }
322 
323 
324 static int
325 set_val_astring(idmap_cfg_handles_t *handles, char *name, const char *val)
326 {
327 	int			rc = -1;
328 	int			ret = -2;
329 	int			i;
330 	scf_property_t		*scf_prop = NULL;
331 	scf_value_t		*value = NULL;
332 	scf_transaction_t	*tx = NULL;
333 	scf_transaction_entry_t	*ent = NULL;
334 
335 	if ((scf_prop = scf_property_create(handles->main)) == NULL ||
336 	    (value = scf_value_create(handles->main)) == NULL ||
337 	    (tx = scf_transaction_create(handles->main)) == NULL ||
338 	    (ent = scf_entry_create(handles->main)) == NULL) {
339 		idmapdlog(LOG_ERR, "%s: Unable to set property %s: %s",
340 		    me, name, scf_strerror(scf_error()));
341 		goto destruction;
342 	}
343 
344 	for (i = 0; i < MAX_TRIES && (ret == -2 || ret == 0); i++) {
345 		if (scf_transaction_start(tx, handles->config_pg) == -1) {
346 			idmapdlog(LOG_ERR,
347 			    "%s: scf_transaction_start(%s) failed: %s",
348 			    me, name, scf_strerror(scf_error()));
349 			goto destruction;
350 		}
351 
352 		if (scf_transaction_property_new(tx, ent, name,
353 		    SCF_TYPE_ASTRING) < 0) {
354 			idmapdlog(LOG_ERR,
355 			    "%s: scf_transaction_property_new() failed: %s",
356 			    me, scf_strerror(scf_error()));
357 			goto destruction;
358 		}
359 
360 		if (scf_value_set_astring(value, val) == -1) {
361 			idmapdlog(LOG_ERR,
362 			    "%s: scf_value_set_astring() failed: %s",
363 			    me, scf_strerror(scf_error()));
364 			goto destruction;
365 		}
366 
367 		if (scf_entry_add_value(ent, value) == -1) {
368 			idmapdlog(LOG_ERR,
369 			    "%s: scf_entry_add_value() failed: %s",
370 			    me, scf_strerror(scf_error()));
371 			goto destruction;
372 		}
373 
374 		if ((ret = scf_transaction_commit(tx)) == 1)
375 			break;
376 
377 		if (ret == 0 && i < MAX_TRIES - 1) {
378 			/*
379 			 * Property group set in scf_transaction_start()
380 			 * is not the most recent. Update pg, reset tx and
381 			 * retry tx.
382 			 */
383 			idmapdlog(LOG_WARNING,
384 			    "%s: scf_transaction_commit(%s) failed - Retry: %s",
385 			    me, name, scf_strerror(scf_error()));
386 			if (scf_pg_update(handles->config_pg) == -1) {
387 				idmapdlog(LOG_ERR,
388 				    "%s: scf_pg_update() failed: %s",
389 				    me, scf_strerror(scf_error()));
390 				goto destruction;
391 			}
392 			scf_transaction_reset(tx);
393 		}
394 	}
395 
396 
397 	if (ret == 1)
398 		rc = 0;
399 	else if (ret != -2)
400 		idmapdlog(LOG_ERR,
401 		    "%s: scf_transaction_commit(%s) failed: %s",
402 		    me, name, scf_strerror(scf_error()));
403 
404 destruction:
405 	scf_value_destroy(value);
406 	scf_entry_destroy(ent);
407 	scf_transaction_destroy(tx);
408 	scf_property_destroy(scf_prop);
409 	return (rc);
410 }
411 
412 static int
413 update_value(char **value, char **new, char *name)
414 {
415 	if (*new == NULL)
416 		return (FALSE);
417 
418 	if (*value != NULL && strcmp(*new, *value) == 0) {
419 		free(*new);
420 		*new = NULL;
421 		return (FALSE);
422 	}
423 
424 	idmapdlog(LOG_INFO, "%s: change %s=%s", me, name, CHECK_NULL(*new));
425 	if (*value != NULL)
426 		free(*value);
427 	*value = *new;
428 	*new = NULL;
429 	return (TRUE);
430 }
431 
432 static int
433 update_dirs(ad_disc_ds_t **value, ad_disc_ds_t **new, char *name)
434 {
435 	int i;
436 
437 	if (*new == NULL)
438 		return (FALSE);
439 
440 	if (*value != NULL && ad_disc_compare_ds(*value, *new) == 0) {
441 		free(*new);
442 		*new = NULL;
443 		return (FALSE);
444 	}
445 
446 	if (*value)
447 		free(*value);
448 
449 	for (i = 0; (*new)[i].host[0] != '\0'; i++)
450 		idmapdlog(LOG_INFO, "%s: change %s=%s port=%d", me, name,
451 		    (*new)[i].host, (*new)[i].port);
452 	*value = *new;
453 	*new = NULL;
454 	return (TRUE);
455 }
456 
457 
458 #define	SUBNET_CHECK_TIME	(2 * 60)
459 #define	MAX_CHECK_TIME		(10 * 60)
460 
461 /*
462  * Returns 1 if SIGHUP has been received (see hup_handler elsewhere), 0
463  * otherwise.  Uses an event port and a user-defined event.
464  *
465  * Note that port_get() does not update its timeout argument when EINTR,
466  * unlike nanosleep().  We probably don't care very much here because
467  * the only signals we expect are ones that will lead to idmapd dying or
468  * SIGHUP, and we intend for the latter to cause this function to
469  * return.  But if we did care then we could always use a timer event
470  * (see timer_create(3RT)) and associate it with the same event port,
471  * then we could get accurate waiting regardless of EINTRs.
472  */
473 static
474 int
475 wait_for_ttl(struct timespec *timeout)
476 {
477 	port_event_t pe;
478 	int retries = 1;
479 
480 	/*
481 	 * If event port creation failed earlier and fails now then we
482 	 * simply don't learn about SIGHUPs in a timely fashion.  No big
483 	 * deal
484 	 */
485 	if (hup_ev_port == -1 && (hup_ev_port = port_create()) < 0) {
486 		(void) nanosleep(timeout, NULL);
487 		return (0);
488 	}
489 
490 retry:
491 	if (port_get(hup_ev_port, &pe, timeout) != 0) {
492 		switch (errno) {
493 		case EBADF:
494 		case EBADFD:
495 			hup_ev_port = -1;
496 			(void) nanosleep(timeout, NULL);
497 			break;
498 		case EINVAL:
499 			/*
500 			 * Shouldn't happen, except, perhaps, near the
501 			 * end of time
502 			 */
503 			timeout->tv_nsec = 0;
504 			timeout->tv_sec = MAX_CHECK_TIME;
505 			if (retries-- > 0)
506 				goto retry;
507 			/* NOTREACHED */
508 			break;
509 		case EINTR:
510 			if (!hupped)
511 				goto retry;
512 			break;
513 		case ETIME:
514 			/* Timeout */
515 			break;
516 		default:
517 			/* EFAULT */
518 			(void) nanosleep(timeout, NULL);
519 			break;
520 		}
521 	}
522 
523 	/*
524 	 * We only have one event that we care about, a user event, so
525 	 * there's nothing to check or clean up about pe.
526 	 *
527 	 * If we get here it's either because we had a SIGHUP and a user
528 	 * event was sent to our port, or because the port_get() timed
529 	 * out (or even both!).
530 	 */
531 
532 	if (hupped) {
533 		int rc;
534 
535 		hupped = 0;
536 		/*
537 		 * Blow away the ccache, we might have re-joined the
538 		 * domain or joined a new one
539 		 */
540 		(void) unlink(IDMAP_CACHEDIR "/ccache");
541 		/* HUP is the refresh method, so re-read SMF config */
542 		(void) idmapdlog(LOG_INFO, "idmapd: SMF refresh");
543 		WRLOCK_CONFIG();
544 		(void) idmap_cfg_unload(&_idmapdstate.cfg->pgcfg);
545 		rc = idmap_cfg_load(&_idmapdstate.cfg->handles,
546 		    &_idmapdstate.cfg->pgcfg, 1);
547 		if (rc < -1)
548 			(void) idmapdlog(LOG_ERR,
549 			    "idmapd: Various errors re-loading configuration "
550 			    "will cause AD lookups to fail");
551 		else if (rc == -1)
552 			(void) idmapdlog(LOG_WARNING,
553 			    "idmapd: Various errors re-loading configuration "
554 			    "may cause AD lookups to fail");
555 		UNLOCK_CONFIG();
556 		return (1);
557 	}
558 
559 	return (0);
560 }
561 
562 void *
563 idmap_cfg_update_thread(void *arg)
564 {
565 
566 	idmap_pg_config_t	new_cfg;
567 	int			ttl, changed;
568 	idmap_cfg_handles_t	*handles = &_idmapdstate.cfg->handles;
569 	idmap_pg_config_t	*live_cfg = &_idmapdstate.cfg->pgcfg;
570 	ad_disc_t		ad_ctx = handles->ad_ctx;
571 	struct timespec		delay;
572 	int			first = 1;
573 
574 	(void) memset(&new_cfg, 0, sizeof (new_cfg));
575 
576 	for (;;) {
577 		changed = FALSE;
578 
579 		if (first) {
580 			ttl = 1;
581 			first = 0;
582 		} else {
583 			ttl = ad_disc_get_TTL(ad_ctx);
584 		}
585 
586 		if (ttl > MAX_CHECK_TIME)
587 			ttl = MAX_CHECK_TIME;
588 		while (ttl > 0 || ttl == -1) {
589 			if (ttl == -1) {
590 				wait_for_ttl(NULL);
591 			} else if (ttl > SUBNET_CHECK_TIME) {
592 				/*
593 				 * We really ought to just monitor
594 				 * network interfaces with a PF_ROUTE
595 				 * socket...  This crude method of
596 				 * discovering subnet changes will do
597 				 * for now.  Though might even not want
598 				 * to bother: subnet changes leading to
599 				 * sitename changes ought never happen,
600 				 * and requiring a refresh when they do
601 				 * should be no problem (SMF/NWAM ought
602 				 * to be able to refresh us).
603 				 */
604 				delay.tv_sec = SUBNET_CHECK_TIME;
605 				delay.tv_nsec = 0;
606 				if (wait_for_ttl(&delay)) {
607 					/* Got SIGHUP, re-discover */
608 					ttl = 0;
609 					changed = TRUE;
610 					break;
611 				}
612 				ttl -= SUBNET_CHECK_TIME;
613 				if (ad_disc_SubnetChanged(ad_ctx))
614 					break;
615 			} else {
616 				delay.tv_sec = ttl;
617 				delay.tv_nsec = 0;
618 				if (wait_for_ttl(&delay))
619 					changed = TRUE;
620 				ttl = 0;
621 			}
622 		}
623 
624 		/*
625 		 * Load configuration data into a private copy.
626 		 *
627 		 * The fixed values (i.e., from SMF) have already been
628 		 * set in AD auto discovery, so if all values have been
629 		 * set in SMF and they haven't been changed or the
630 		 * service been refreshed then the rest of this loop's
631 		 * body is one big no-op.
632 		 */
633 		pthread_mutex_lock(&handles->mutex);
634 
635 		new_cfg.default_domain = ad_disc_get_DomainName(ad_ctx);
636 		if (new_cfg.default_domain == NULL) {
637 			idmapdlog(LOG_INFO,
638 			    "%s: unable to discover Default Domain", me);
639 		}
640 
641 		new_cfg.domain_name = ad_disc_get_DomainName(ad_ctx);
642 		if (new_cfg.domain_name == NULL) {
643 			idmapdlog(LOG_INFO,
644 			    "%s: unable to discover Domain Name", me);
645 		}
646 
647 		new_cfg.domain_controller =
648 		    ad_disc_get_DomainController(ad_ctx, AD_DISC_PREFER_SITE);
649 		if (new_cfg.domain_controller == NULL) {
650 			idmapdlog(LOG_INFO,
651 			    "%s: unable to discover Domain Controller", me);
652 		}
653 
654 		new_cfg.forest_name = ad_disc_get_ForestName(ad_ctx);
655 		if (new_cfg.forest_name == NULL) {
656 			idmapdlog(LOG_INFO,
657 			    "%s: unable to discover Forest Name", me);
658 		}
659 
660 		new_cfg.site_name = ad_disc_get_SiteName(ad_ctx);
661 		if (new_cfg.site_name == NULL) {
662 			idmapdlog(LOG_INFO,
663 			    "%s: unable to discover Site Name", me);
664 		}
665 
666 		new_cfg.global_catalog =
667 		    ad_disc_get_GlobalCatalog(ad_ctx, AD_DISC_PREFER_SITE);
668 		if (new_cfg.global_catalog == NULL) {
669 			idmapdlog(LOG_INFO,
670 			    "%s: unable to discover Global Catalog", me);
671 		}
672 
673 		pthread_mutex_unlock(&handles->mutex);
674 
675 		if (new_cfg.default_domain == NULL &&
676 		    new_cfg.domain_name == NULL &&
677 		    new_cfg.domain_controller == NULL &&
678 		    new_cfg.forest_name == NULL &&
679 		    new_cfg.global_catalog == NULL) {
680 			idmapdlog(LOG_NOTICE, "%s: Could not auto-discover AD "
681 			    "domain and forest names nor domain controllers "
682 			    "and global catalog servers", me);
683 			idmap_cfg_unload(&new_cfg);
684 			continue;
685 		}
686 
687 		/*
688 		 * Update the live configuration
689 		 */
690 		WRLOCK_CONFIG();
691 
692 		if (live_cfg->list_size_limit != new_cfg.list_size_limit) {
693 			idmapdlog(LOG_INFO, "%s: change list_size=%d", me,
694 			    new_cfg.list_size_limit);
695 			live_cfg->list_size_limit = new_cfg.list_size_limit;
696 		}
697 
698 		/*
699 		 * If default_domain came from SMF then we must not
700 		 * auto-discover it.
701 		 */
702 		if (live_cfg->dflt_dom_set_in_smf == FALSE &&
703 		    update_value(&live_cfg->default_domain,
704 		    &new_cfg.default_domain, "default_domain") == TRUE)
705 			changed = TRUE;
706 
707 		(void) update_value(&live_cfg->domain_name,
708 		    &new_cfg.domain_name, "domain_name");
709 
710 		(void) update_dirs(&live_cfg->domain_controller,
711 		    &new_cfg.domain_controller, "domain_controller");
712 
713 		(void) update_value(&live_cfg->forest_name,
714 		    &new_cfg.forest_name, "forest_name");
715 
716 		(void) update_value(&live_cfg->site_name,
717 		    &new_cfg.site_name, "site_name");
718 
719 		if (update_dirs(&live_cfg->global_catalog,
720 		    &new_cfg.global_catalog, "global_catalog") == TRUE)
721 			changed = TRUE;
722 		UNLOCK_CONFIG();
723 
724 		idmap_cfg_unload(&new_cfg);
725 
726 
727 		/*
728 		 * Re-create the ad_t/ad_host_t objects if
729 		 * either the default domain or the global
730 		 * catalog server list changed.
731 		 */
732 
733 		if (changed) {
734 			RDLOCK_CONFIG();
735 			(void) reload_ad();
736 			UNLOCK_CONFIG();
737 			print_idmapdstate();
738 		}
739 	}
740 	/*NOTREACHED*/
741 	return (NULL);
742 }
743 
744 
745 int
746 idmap_cfg_start_updates(idmap_cfg_t *cfg)
747 {
748 	/* Don't check for failure -- see wait_for_ttl() */
749 	hup_ev_port = port_create();
750 
751 	errno = pthread_create(&update_thread_handle, NULL,
752 	    idmap_cfg_update_thread, NULL);
753 	if (errno == 0)
754 		return (0);
755 	else
756 		return (-1);
757 }
758 
759 
760 int
761 idmap_cfg_load(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg,
762 	int discover)
763 {
764 	int rc;
765 	int errors = 0;
766 	uint8_t bool_val;
767 	char *str = NULL;
768 	ad_disc_t ad_ctx = handles->ad_ctx;
769 
770 	pgcfg->list_size_limit = 0;
771 	pgcfg->default_domain = NULL;
772 	pgcfg->domain_name = NULL;
773 	pgcfg->machine_sid = NULL;
774 	pgcfg->domain_controller = NULL;
775 	pgcfg->forest_name = NULL;
776 	pgcfg->site_name = NULL;
777 	pgcfg->global_catalog = NULL;
778 	pgcfg->ad_unixuser_attr = NULL;
779 	pgcfg->ad_unixgroup_attr = NULL;
780 	pgcfg->nldap_winname_attr = NULL;
781 	pgcfg->ds_name_mapping_enabled = FALSE;
782 
783 	pthread_mutex_lock(&handles->mutex);
784 
785 	ad_disc_refresh(handles->ad_ctx);
786 
787 	if (scf_pg_update(handles->config_pg) < 0) {
788 		idmapdlog(LOG_ERR, "%s: scf_pg_update() failed: %s",
789 		    me, scf_strerror(scf_error()));
790 		rc = -2;
791 		goto exit;
792 	}
793 
794 	if (scf_pg_update(handles->general_pg) < 0) {
795 		idmapdlog(LOG_ERR, "%s: scf_pg_update() failed: %s",
796 		    me, scf_strerror(scf_error()));
797 		rc = -2;
798 		goto exit;
799 	}
800 
801 	rc = get_val_int(handles, "list_size_limit",
802 	    &pgcfg->list_size_limit, SCF_TYPE_COUNT);
803 	if (rc != 0) {
804 		pgcfg->list_size_limit = 0;
805 		errors++;
806 	}
807 
808 	rc = get_val_astring(handles, "domain_name",
809 	    &pgcfg->domain_name);
810 	if (rc != 0)
811 		errors++;
812 	else
813 		(void) ad_disc_set_DomainName(ad_ctx, pgcfg->domain_name);
814 
815 	rc = get_val_astring(handles, "default_domain",
816 	    &pgcfg->default_domain);
817 	if (rc != 0) {
818 		/*
819 		 * SCF failures fetching config/default_domain we treat
820 		 * as fatal as they may leave ID mapping rules that
821 		 * match unqualified winnames flapping in the wind.
822 		 */
823 		rc = -2;
824 		goto exit;
825 	}
826 
827 	rc = get_val_astring(handles, "mapping_domain", &str);
828 	if (rc != 0)
829 		errors++;
830 
831 	/*
832 	 * We treat default_domain as having been specified in SMF IFF
833 	 * either (the config/default_domain property was set) or (the
834 	 * old, obsolete, never documented config/mapping_domain
835 	 * property was set and the new config/domain_name property was
836 	 * not set).
837 	 */
838 	pgcfg->dflt_dom_set_in_smf = TRUE;
839 	if (pgcfg->default_domain == NULL) {
840 
841 		pgcfg->dflt_dom_set_in_smf = FALSE;
842 
843 		if (pgcfg->domain_name != NULL) {
844 			pgcfg->default_domain = strdup(pgcfg->domain_name);
845 			if (str != NULL) {
846 				idmapdlog(LOG_WARNING,
847 				    "%s: Ignoring obsolete, undocumented "
848 				    "config/mapping_domain property", me);
849 			}
850 		} else if (str != NULL) {
851 			pgcfg->default_domain = strdup(str);
852 			pgcfg->dflt_dom_set_in_smf = TRUE;
853 			idmapdlog(LOG_WARNING,
854 			    "%s: The config/mapping_domain property is "
855 			    "obsolete; support for it will be removed, "
856 			    "please use config/default_domain instead",
857 			    me);
858 		}
859 	}
860 
861 	if (str != NULL)
862 		free(str);
863 
864 	rc = get_val_astring(handles, "machine_sid", &pgcfg->machine_sid);
865 	if (rc != 0)
866 		errors++;
867 	if (pgcfg->machine_sid == NULL) {
868 		/* If machine_sid not configured, generate one */
869 		if (generate_machine_sid(&pgcfg->machine_sid) < 0) {
870 			rc =  -2;
871 			goto exit;
872 		}
873 		rc = set_val_astring(handles, "machine_sid",
874 		    pgcfg->machine_sid);
875 		if (rc != 0)
876 			errors++;
877 	}
878 
879 	str = NULL;
880 	rc = get_val_ds(handles, "domain_controller", 389,
881 	    &pgcfg->domain_controller);
882 	if (rc != 0)
883 		errors++;
884 	else
885 		(void) ad_disc_set_DomainController(ad_ctx,
886 		    pgcfg->domain_controller);
887 
888 	rc = get_val_astring(handles, "forest_name", &pgcfg->forest_name);
889 	if (rc != 0)
890 		errors++;
891 	else
892 		(void) ad_disc_set_ForestName(ad_ctx, pgcfg->forest_name);
893 
894 	rc = get_val_astring(handles, "site_name", &pgcfg->site_name);
895 	if (rc != 0)
896 		errors++;
897 	else
898 		(void) ad_disc_set_SiteName(ad_ctx, pgcfg->site_name);
899 
900 	str = NULL;
901 	rc = get_val_ds(handles, "global_catalog", 3268,
902 	    &pgcfg->global_catalog);
903 	if (rc != 0)
904 		errors++;
905 	else
906 		(void) ad_disc_set_GlobalCatalog(ad_ctx, pgcfg->global_catalog);
907 
908 	/*
909 	 * Read directory-based name mappings related SMF properties
910 	 */
911 	bool_val = 0;
912 	rc = get_val_int(handles, "ds_name_mapping_enabled",
913 	    &bool_val, SCF_TYPE_BOOLEAN);
914 	if (rc != 0) {
915 		rc = -2;
916 		goto exit;
917 	} else if (bool_val) {
918 		pgcfg->ds_name_mapping_enabled = TRUE;
919 		rc = get_val_astring(handles, "ad_unixuser_attr",
920 		    &pgcfg->ad_unixuser_attr);
921 		if (rc != 0) {
922 			rc = -2;
923 			goto exit;
924 		}
925 
926 		rc = get_val_astring(handles, "ad_unixgroup_attr",
927 		    &pgcfg->ad_unixgroup_attr);
928 		if (rc != 0) {
929 			rc = -2;
930 			goto exit;
931 		}
932 
933 		rc = get_val_astring(handles, "nldap_winname_attr",
934 		    &pgcfg->nldap_winname_attr);
935 		if (rc != 0) {
936 			rc = -2;
937 			goto exit;
938 		}
939 
940 		if (pgcfg->nldap_winname_attr != NULL) {
941 			idmapdlog(LOG_ERR,
942 			    "%s: native LDAP based name mapping not supported "
943 			    "at this time. Please unset "
944 			    "config/nldap_winname_attr and restart idmapd.",
945 			    me);
946 			rc = -3;
947 			goto exit;
948 		}
949 
950 		if (pgcfg->ad_unixuser_attr == NULL &&
951 		    pgcfg->ad_unixgroup_attr == NULL) {
952 			idmapdlog(LOG_ERR,
953 			    "%s: If config/ds_name_mapping_enabled property "
954 			    "is set to true then atleast one of the following "
955 			    "name mapping attributes must be specified. "
956 			    "(config/ad_unixuser_attr OR "
957 			    "config/ad_unixgroup_attr)", me);
958 			rc = -3;
959 			goto exit;
960 		}
961 	}
962 
963 
964 	if (!discover)
965 		goto exit;
966 
967 	/*
968 	 * Auto Discover the rest
969 	 */
970 	if (pgcfg->default_domain == NULL) {
971 		pgcfg->default_domain = ad_disc_get_DomainName(ad_ctx);
972 		if (pgcfg->default_domain == NULL) {
973 			idmapdlog(LOG_INFO,
974 			    "%s: unable to discover Default Domain", me);
975 		}
976 	}
977 
978 	if (pgcfg->domain_name == NULL) {
979 		pgcfg->domain_name = ad_disc_get_DomainName(ad_ctx);
980 		if (pgcfg->domain_name == NULL) {
981 			idmapdlog(LOG_INFO,
982 			    "%s: unable to discover Domain Name", me);
983 		}
984 	}
985 
986 	if (pgcfg->domain_controller == NULL) {
987 		pgcfg->domain_controller =
988 		    ad_disc_get_DomainController(ad_ctx, AD_DISC_PREFER_SITE);
989 		if (pgcfg->domain_controller == NULL) {
990 			idmapdlog(LOG_INFO,
991 			    "%s: unable to discover Domain Controller", me);
992 		}
993 	}
994 
995 	if (pgcfg->forest_name == NULL) {
996 		pgcfg->forest_name = ad_disc_get_ForestName(ad_ctx);
997 		if (pgcfg->forest_name == NULL) {
998 			idmapdlog(LOG_INFO,
999 			    "%s: unable to discover Forest Name", me);
1000 		}
1001 	}
1002 
1003 	if (pgcfg->site_name == NULL) {
1004 		pgcfg->site_name = ad_disc_get_SiteName(ad_ctx);
1005 		if (pgcfg->site_name == NULL) {
1006 			idmapdlog(LOG_INFO,
1007 			    "%s: unable to discover Site Name", me);
1008 		}
1009 	}
1010 
1011 	if (pgcfg->global_catalog == NULL) {
1012 		pgcfg->global_catalog =
1013 		    ad_disc_get_GlobalCatalog(ad_ctx, AD_DISC_PREFER_SITE);
1014 		if (pgcfg->global_catalog == NULL) {
1015 			idmapdlog(LOG_INFO,
1016 			    "%s: unable to discover Global Catalog", me);
1017 		}
1018 	}
1019 
1020 exit:
1021 	pthread_mutex_unlock(&handles->mutex);
1022 
1023 	if (rc < -1)
1024 		return (rc);
1025 
1026 	return ((errors == 0) ? 0 : -1);
1027 }
1028 
1029 /*
1030  * Initialize 'cfg'.
1031  */
1032 idmap_cfg_t *
1033 idmap_cfg_init()
1034 {
1035 	idmap_cfg_handles_t *handles;
1036 
1037 	/* First the smf repository handles: */
1038 	idmap_cfg_t *cfg = calloc(1, sizeof (idmap_cfg_t));
1039 	if (!cfg) {
1040 		idmapdlog(LOG_ERR, "%s: Out of memory", me);
1041 		return (NULL);
1042 	}
1043 	handles = &cfg->handles;
1044 
1045 	(void) pthread_mutex_init(&handles->mutex, NULL);
1046 
1047 	if (!(handles->main = scf_handle_create(SCF_VERSION))) {
1048 		idmapdlog(LOG_ERR, "%s: scf_handle_create() failed: %s",
1049 		    me, scf_strerror(scf_error()));
1050 		goto error;
1051 	}
1052 
1053 	if (scf_handle_bind(handles->main) < 0) {
1054 		idmapdlog(LOG_ERR, "%s: scf_handle_bind() failed: %s",
1055 		    me, scf_strerror(scf_error()));
1056 		goto error;
1057 	}
1058 
1059 	if (!(handles->service = scf_service_create(handles->main)) ||
1060 	    !(handles->instance = scf_instance_create(handles->main)) ||
1061 	    !(handles->config_pg = scf_pg_create(handles->main)) ||
1062 	    !(handles->general_pg = scf_pg_create(handles->main))) {
1063 		idmapdlog(LOG_ERR, "%s: scf handle creation failed: %s",
1064 		    me, scf_strerror(scf_error()));
1065 		goto error;
1066 	}
1067 
1068 	if (scf_handle_decode_fmri(handles->main,
1069 	    FMRI_BASE "/:properties/" CONFIG_PG,
1070 	    NULL,				/* scope */
1071 	    handles->service,		/* service */
1072 	    handles->instance,		/* instance */
1073 	    handles->config_pg,		/* pg */
1074 	    NULL,				/* prop */
1075 	    SCF_DECODE_FMRI_EXACT) < 0) {
1076 		idmapdlog(LOG_ERR, "%s: scf_handle_decode_fmri() failed: %s",
1077 		    me, scf_strerror(scf_error()));
1078 		goto error;
1079 	}
1080 
1081 	if (scf_service_get_pg(handles->service,
1082 	    GENERAL_PG, handles->general_pg) < 0) {
1083 		idmapdlog(LOG_ERR, "%s: scf_service_get_pg() failed: %s",
1084 		    me, scf_strerror(scf_error()));
1085 		goto error;
1086 	}
1087 
1088 	/* Initialize AD Auto Discovery context */
1089 	handles->ad_ctx = ad_disc_init();
1090 	if (handles->ad_ctx == NULL)
1091 		goto error;
1092 
1093 	return (cfg);
1094 
1095 error:
1096 	(void) idmap_cfg_fini(cfg);
1097 	return (NULL);
1098 }
1099 
1100 void
1101 idmap_cfg_unload(idmap_pg_config_t *pgcfg)
1102 {
1103 
1104 	if (pgcfg->default_domain) {
1105 		free(pgcfg->default_domain);
1106 		pgcfg->default_domain = NULL;
1107 	}
1108 	if (pgcfg->domain_name) {
1109 		free(pgcfg->domain_name);
1110 		pgcfg->domain_name = NULL;
1111 	}
1112 	if (pgcfg->machine_sid) {
1113 		free(pgcfg->machine_sid);
1114 		pgcfg->machine_sid = NULL;
1115 	}
1116 	if (pgcfg->domain_controller) {
1117 		free(pgcfg->domain_controller);
1118 		pgcfg->domain_controller = NULL;
1119 	}
1120 	if (pgcfg->forest_name) {
1121 		free(pgcfg->forest_name);
1122 		pgcfg->forest_name = NULL;
1123 	}
1124 	if (pgcfg->site_name) {
1125 		free(pgcfg->site_name);
1126 		pgcfg->site_name = NULL;
1127 	}
1128 	if (pgcfg->global_catalog) {
1129 		free(pgcfg->global_catalog);
1130 		pgcfg->global_catalog = NULL;
1131 	}
1132 	if (pgcfg->ad_unixuser_attr) {
1133 		free(pgcfg->ad_unixuser_attr);
1134 		pgcfg->ad_unixuser_attr = NULL;
1135 	}
1136 	if (pgcfg->ad_unixgroup_attr) {
1137 		free(pgcfg->ad_unixgroup_attr);
1138 		pgcfg->ad_unixgroup_attr = NULL;
1139 	}
1140 	if (pgcfg->nldap_winname_attr) {
1141 		free(pgcfg->nldap_winname_attr);
1142 		pgcfg->nldap_winname_attr = NULL;
1143 	}
1144 }
1145 
1146 int
1147 idmap_cfg_fini(idmap_cfg_t *cfg)
1148 {
1149 	idmap_cfg_handles_t *handles = &cfg->handles;
1150 	idmap_cfg_unload(&cfg->pgcfg);
1151 
1152 	(void) pthread_mutex_destroy(&handles->mutex);
1153 	scf_pg_destroy(handles->config_pg);
1154 	scf_pg_destroy(handles->general_pg);
1155 	scf_instance_destroy(handles->instance);
1156 	scf_service_destroy(handles->service);
1157 	scf_handle_destroy(handles->main);
1158 	if (handles->ad_ctx != NULL)
1159 		ad_disc_fini(handles->ad_ctx);
1160 	free(cfg);
1161 
1162 	return (0);
1163 }
1164