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