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;
194 	int count = 0;
195 	int rc = 0;
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 		goto destruction;
207 
208 	if (scf_iter_property_values(iter, scf_prop) < 0) {
209 		idmapdlog(LOG_ERR,
210 		    "%s: scf_iter_property_values(%s) failed: %s",
211 		    me, name, scf_strerror(scf_error()));
212 		rc = -1;
213 		goto destruction;
214 	}
215 
216 	/* Workaround scf bugs -- can't reset an iteration */
217 	if (count == 0) {
218 		while (scf_iter_next_value(iter, value) > 0)
219 			count++;
220 
221 		if (count == 0)
222 			/* no values */
223 			goto destruction;
224 
225 		scf_value_destroy(value);
226 		scf_iter_destroy(iter);
227 		scf_property_destroy(scf_prop);
228 		goto restart;
229 	}
230 
231 	if ((servers = calloc(count + 1, sizeof (*servers))) == NULL) {
232 		idmapdlog(LOG_ERR, "%s: Out of memory", me);
233 		goto destruction;
234 	}
235 
236 	count = 0;
237 	while (scf_iter_next_value(iter, value) > 0) {
238 		servers[count].priority = 0;
239 		servers[count].weight = 100;
240 		servers[count].port = defport;
241 		if ((host = scf_value2string(value)) == NULL) {
242 			rc = -1;
243 			goto destruction;
244 		}
245 		if ((portstr = strchr(host, ':')) != NULL) {
246 			*portstr++ = '\0';
247 			servers[count].port = strtol(portstr,
248 			    (char **)NULL, 10);
249 			if (servers[count].port == 0)
250 				servers[count].port = defport;
251 		}
252 		len = strlcpy(servers[count].host, host,
253 		    sizeof (servers->host));
254 
255 		free(host);
256 
257 		/* Ignore this server if the hostname is too long */
258 		if (len < sizeof (servers->host))
259 			count++;
260 	}
261 
262 	*val = servers;
263 
264 destruction:
265 	scf_value_destroy(value);
266 	scf_iter_destroy(iter);
267 	scf_property_destroy(scf_prop);
268 
269 	if (rc < 0) {
270 		if (servers)
271 			free(servers);
272 		*val = NULL;
273 	}
274 
275 	return (rc);
276 }
277 
278 
279 static int
280 get_val_astring(idmap_cfg_handles_t *handles, char *name, char **val)
281 {
282 	int rc = 0;
283 
284 	scf_property_t *scf_prop = scf_property_create(handles->main);
285 	scf_value_t *value = scf_value_create(handles->main);
286 
287 	*val = NULL;
288 
289 	if (scf_pg_get_property(handles->config_pg, name, scf_prop) < 0)
290 	/* this is OK: the property is just undefined */
291 		goto destruction;
292 
293 	if (scf_property_get_value(scf_prop, value) < 0) {
294 		idmapdlog(LOG_ERR,
295 		    "%s: scf_property_get_value(%s) failed: %s",
296 		    me, name, scf_strerror(scf_error()));
297 		rc = -1;
298 		goto destruction;
299 	}
300 
301 	if (!(*val = scf_value2string(value)))
302 		rc = -1;
303 
304 destruction:
305 	scf_value_destroy(value);
306 	scf_property_destroy(scf_prop);
307 
308 	if (rc < 0) {
309 		if (*val)
310 			free(*val);
311 		*val = NULL;
312 	}
313 
314 	return (rc);
315 }
316 
317 
318 static int
319 set_val_astring(idmap_cfg_handles_t *handles, char *name, const char *val)
320 {
321 	int			rc = 0, i;
322 	scf_property_t		*scf_prop = NULL;
323 	scf_value_t		*value = NULL;
324 	scf_transaction_t	*tx = NULL;
325 	scf_transaction_entry_t	*ent = NULL;
326 
327 	if ((scf_prop = scf_property_create(handles->main)) == NULL ||
328 	    (value = scf_value_create(handles->main)) == NULL ||
329 	    (tx = scf_transaction_create(handles->main)) == NULL ||
330 	    (ent = scf_entry_create(handles->main)) == NULL) {
331 		idmapdlog(LOG_ERR, "%s: Unable to set property %s: %s",
332 		    me, name, scf_strerror(scf_error()));
333 		rc = -1;
334 		goto destruction;
335 	}
336 
337 	for (i = 0; i < MAX_TRIES && rc == 0; i++) {
338 		if (scf_transaction_start(tx, handles->config_pg) == -1) {
339 			idmapdlog(LOG_ERR,
340 			    "%s: scf_transaction_start(%s) failed: %s",
341 			    me, name, scf_strerror(scf_error()));
342 			rc = -1;
343 			goto destruction;
344 		}
345 
346 		rc = scf_transaction_property_new(tx, ent, name,
347 		    SCF_TYPE_ASTRING);
348 		if (rc == -1) {
349 			idmapdlog(LOG_ERR,
350 			    "%s: scf_transaction_property_new() failed: %s",
351 			    me, scf_strerror(scf_error()));
352 			goto destruction;
353 		}
354 
355 		if (scf_value_set_astring(value, val) == -1) {
356 			idmapdlog(LOG_ERR,
357 			    "%s: scf_value_set_astring() failed: %s",
358 			    me, scf_strerror(scf_error()));
359 			rc = -1;
360 			goto destruction;
361 		}
362 
363 		if (scf_entry_add_value(ent, value) == -1) {
364 			idmapdlog(LOG_ERR,
365 			    "%s: scf_entry_add_value() failed: %s",
366 			    me, scf_strerror(scf_error()));
367 			rc = -1;
368 			goto destruction;
369 		}
370 
371 		rc = scf_transaction_commit(tx);
372 		if (rc == 0 && i < MAX_TRIES - 1) {
373 			/*
374 			 * Property group set in scf_transaction_start()
375 			 * is not the most recent. Update pg, reset tx and
376 			 * retry tx.
377 			 */
378 			idmapdlog(LOG_WARNING,
379 			    "%s: scf_transaction_commit(%s) failed - Retry: %s",
380 			    me, name, scf_strerror(scf_error()));
381 			if (scf_pg_update(handles->config_pg) == -1) {
382 				idmapdlog(LOG_ERR,
383 				    "%s: scf_pg_update() failed: %s",
384 				    me, scf_strerror(scf_error()));
385 				rc = -1;
386 				goto destruction;
387 			}
388 			scf_transaction_reset(tx);
389 		}
390 	}
391 
392 	/* Log failure message if all retries failed */
393 	if (rc == 0) {
394 		idmapdlog(LOG_ERR,
395 		    "%s: scf_transaction_commit(%s) failed: %s",
396 		    me, name, scf_strerror(scf_error()));
397 		rc = -1;
398 	}
399 
400 destruction:
401 	scf_value_destroy(value);
402 	scf_entry_destroy(ent);
403 	scf_transaction_destroy(tx);
404 	scf_property_destroy(scf_prop);
405 	return (rc);
406 }
407 
408 static int
409 update_value(char **value, char **new, char *name)
410 {
411 	if (*new == NULL)
412 		return (FALSE);
413 
414 	if (*value != NULL && strcmp(*new, *value) == 0) {
415 		free(*new);
416 		*new = NULL;
417 		return (FALSE);
418 	}
419 
420 	idmapdlog(LOG_INFO, "%s: change %s=%s", me, name, CHECK_NULL(*new));
421 	if (*value != NULL)
422 		free(*value);
423 	*value = *new;
424 	*new = NULL;
425 	return (TRUE);
426 }
427 
428 static int
429 update_dirs(ad_disc_ds_t **value, ad_disc_ds_t **new, char *name)
430 {
431 	int i;
432 
433 	if (*new == NULL)
434 		return (FALSE);
435 
436 	if (*value != NULL && ad_disc_compare_ds(*value, *new) == 0) {
437 		free(*new);
438 		*new = NULL;
439 		return (FALSE);
440 	}
441 
442 	if (*value)
443 		free(*value);
444 
445 	for (i = 0; (*new)[i].host[0] != '\0'; i++)
446 		idmapdlog(LOG_INFO, "%s: change %s=%s port=%d", me, name,
447 		    (*new)[i].host, (*new)[i].port);
448 	*value = *new;
449 	*new = NULL;
450 	return (TRUE);
451 }
452 
453 
454 #define	SUBNET_CHECK_TIME	(2 * 60)
455 #define	MAX_CHECK_TIME		(10 * 60)
456 
457 /*
458  * Returns 1 if SIGHUP has been received (see hup_handler elsewhere), 0
459  * otherwise.  Uses an event port and a user-defined event.
460  *
461  * Note that port_get() does not update its timeout argument when EINTR,
462  * unlike nanosleep().  We probably don't care very much here because
463  * the only signals we expect are ones that will lead to idmapd dying or
464  * SIGHUP, and we intend for the latter to cause this function to
465  * return.  But if we did care then we could always use a timer event
466  * (see timer_create(3RT)) and associate it with the same event port,
467  * then we could get accurate waiting regardless of EINTRs.
468  */
469 static
470 int
471 wait_for_ttl(struct timespec *timeout)
472 {
473 	port_event_t pe;
474 	int retries = 1;
475 
476 	/*
477 	 * If event port creation failed earlier and fails now then we
478 	 * simply don't learn about SIGHUPs in a timely fashion.  No big
479 	 * deal
480 	 */
481 	if (hup_ev_port == -1 && (hup_ev_port = port_create()) < 0) {
482 		(void) nanosleep(timeout, NULL);
483 		return (0);
484 	}
485 
486 retry:
487 	if (port_get(hup_ev_port, &pe, timeout) != 0) {
488 		switch (errno) {
489 		case EBADF:
490 		case EBADFD:
491 			hup_ev_port = -1;
492 			(void) nanosleep(timeout, NULL);
493 			break;
494 		case EINVAL:
495 			/*
496 			 * Shouldn't happen, except, perhaps, near the
497 			 * end of time
498 			 */
499 			timeout->tv_nsec = 0;
500 			timeout->tv_sec = MAX_CHECK_TIME;
501 			if (retries-- > 0)
502 				goto retry;
503 			/* NOTREACHED */
504 			break;
505 		case EINTR:
506 			if (!hupped)
507 				goto retry;
508 			break;
509 		case ETIME:
510 			/* Timeout */
511 			break;
512 		default:
513 			/* EFAULT */
514 			(void) nanosleep(timeout, NULL);
515 			break;
516 		}
517 	}
518 
519 	/*
520 	 * We only have one event that we care about, a user event, so
521 	 * there's nothing to check or clean up about pe.
522 	 *
523 	 * If we get here it's either because we had a SIGHUP and a user
524 	 * event was sent to our port, or because the port_get() timed
525 	 * out (or even both!).
526 	 */
527 
528 	if (hupped) {
529 		hupped = 0;
530 		/* Got SIGHUP, re-read SMF config */
531 		(void) idmapdlog(LOG_INFO, "idmapd: SMF refresh");
532 		WRLOCK_CONFIG();
533 		(void) idmap_cfg_unload(&_idmapdstate.cfg->pgcfg);
534 		if (idmap_cfg_load(&_idmapdstate.cfg->handles,
535 		    &_idmapdstate.cfg->pgcfg) < 0)
536 			(void) idmapdlog(LOG_NOTICE,
537 			    "idmapd: Failed to reload config");
538 		UNLOCK_CONFIG();
539 		return (1);
540 	}
541 
542 	return (0);
543 }
544 
545 void *
546 idmap_cfg_update_thread(void *arg)
547 {
548 
549 	idmap_pg_config_t	new_cfg;
550 	int			ttl;
551 	idmap_cfg_handles_t	*handles = &_idmapdstate.cfg->handles;
552 	idmap_pg_config_t	*live_cfg = &_idmapdstate.cfg->pgcfg;
553 	ad_disc_t		ad_ctx = handles->ad_ctx;
554 	struct timespec		delay;
555 	int			changed;
556 
557 	(void) memset(&new_cfg, 0, sizeof (new_cfg));
558 
559 	for (;;) {
560 		changed = FALSE;
561 		ttl = ad_disc_get_TTL(ad_ctx);
562 		if (ttl > MAX_CHECK_TIME)
563 			ttl = MAX_CHECK_TIME;
564 		while (ttl > 0 || ttl == -1) {
565 			if (ttl == -1) {
566 				wait_for_ttl(NULL);
567 			} else if (ttl > SUBNET_CHECK_TIME) {
568 				/*
569 				 * We really ought to just monitor
570 				 * network interfaces with a PF_ROUTE
571 				 * socket...  This crude method of
572 				 * discovering subnet changes will do
573 				 * for now.  Though might even not want
574 				 * to bother: subnet changes leading to
575 				 * sitename changes ought never happen,
576 				 * and requiring a refresh when they do
577 				 * should be no problem (SMF/NWAM ought
578 				 * to be able to refresh us).
579 				 */
580 				delay.tv_sec = SUBNET_CHECK_TIME;
581 				delay.tv_nsec = 0;
582 				if (wait_for_ttl(&delay)) {
583 					/* Got SIGHUP, re-discover */
584 					ttl = 0;
585 					changed = TRUE;
586 					break;
587 				}
588 				ttl -= SUBNET_CHECK_TIME;
589 				if (ad_disc_SubnetChanged(ad_ctx))
590 					break;
591 			} else {
592 				delay.tv_sec = ttl;
593 				delay.tv_nsec = 0;
594 				if (wait_for_ttl(&delay))
595 					changed = TRUE;
596 				ttl = 0;
597 			}
598 		}
599 
600 		/*
601 		 * Load configuration data into a private copy.
602 		 *
603 		 * The fixed values (i.e., from SMF) have already been
604 		 * set in AD auto discovery, so if all values have been
605 		 * set in SMF and they haven't been changed or the
606 		 * service been refreshed then the rest of this loop's
607 		 * body is one big no-op.
608 		 */
609 		pthread_mutex_lock(&handles->mutex);
610 
611 		new_cfg.default_domain = ad_disc_get_DomainName(ad_ctx);
612 		if (new_cfg.default_domain == NULL) {
613 			idmapdlog(LOG_INFO,
614 			    "%s: unable to discover Default Domain", me);
615 		}
616 
617 		new_cfg.domain_name = ad_disc_get_DomainName(ad_ctx);
618 		if (new_cfg.domain_name == NULL) {
619 			idmapdlog(LOG_INFO,
620 			    "%s: unable to discover Domain Name", me);
621 		}
622 
623 		new_cfg.domain_controller =
624 		    ad_disc_get_DomainController(ad_ctx, AD_DISC_PREFER_SITE);
625 		if (new_cfg.domain_controller == NULL) {
626 			idmapdlog(LOG_INFO,
627 			    "%s: unable to discover Domain Controller", me);
628 		}
629 
630 		new_cfg.forest_name = ad_disc_get_ForestName(ad_ctx);
631 		if (new_cfg.forest_name == NULL) {
632 			idmapdlog(LOG_INFO,
633 			    "%s: unable to discover Forest Name", me);
634 		}
635 
636 		new_cfg.site_name = ad_disc_get_SiteName(ad_ctx);
637 		if (new_cfg.site_name == NULL) {
638 			idmapdlog(LOG_INFO,
639 			    "%s: unable to discover Site Name", me);
640 		}
641 
642 		new_cfg.global_catalog =
643 		    ad_disc_get_GlobalCatalog(ad_ctx, AD_DISC_PREFER_SITE);
644 		if (new_cfg.global_catalog == NULL) {
645 			idmapdlog(LOG_INFO,
646 			    "%s: unable to discover Global Catalog", me);
647 		}
648 
649 		pthread_mutex_unlock(&handles->mutex);
650 
651 		if (new_cfg.default_domain == NULL &&
652 		    new_cfg.domain_name == NULL &&
653 		    new_cfg.domain_controller == NULL &&
654 		    new_cfg.forest_name == NULL &&
655 		    new_cfg.global_catalog == NULL) {
656 			idmapdlog(LOG_NOTICE, "%s: Could not auto-discover AD "
657 			    "domain and forest names nor domain controllers "
658 			    "and global catalog servers", me);
659 			idmap_cfg_unload(&new_cfg);
660 			continue;
661 		}
662 
663 		/*
664 		 * Update the live configuration
665 		 */
666 		WRLOCK_CONFIG();
667 
668 		if (live_cfg->list_size_limit != new_cfg.list_size_limit) {
669 			idmapdlog(LOG_INFO, "%s: change list_size=%d", me,
670 			    new_cfg.list_size_limit);
671 			live_cfg->list_size_limit = new_cfg.list_size_limit;
672 		}
673 
674 		/*
675 		 * If default_domain came from SMF then we must not
676 		 * auto-discover it.
677 		 */
678 		if (live_cfg->dflt_dom_set_in_smf == FALSE &&
679 		    update_value(&live_cfg->default_domain,
680 		    &new_cfg.default_domain, "default_domain") == TRUE)
681 			changed = TRUE;
682 
683 		(void) update_value(&live_cfg->domain_name,
684 		    &new_cfg.domain_name, "domain_name");
685 
686 		(void) update_dirs(&live_cfg->domain_controller,
687 		    &new_cfg.domain_controller, "domain_controller");
688 
689 		(void) update_value(&live_cfg->forest_name,
690 		    &new_cfg.forest_name, "forest_name");
691 
692 		(void) update_value(&live_cfg->site_name,
693 		    &new_cfg.site_name, "site_name");
694 
695 		if (update_dirs(&live_cfg->global_catalog,
696 		    &new_cfg.global_catalog, "global_catalog") == TRUE)
697 			changed = TRUE;
698 		UNLOCK_CONFIG();
699 
700 		idmap_cfg_unload(&new_cfg);
701 
702 
703 		/*
704 		 * Re-create the ad_t/ad_host_t objects if
705 		 * either the default domain or the global
706 		 * catalog server list changed.
707 		 */
708 
709 		if (changed) {
710 			RDLOCK_CONFIG();
711 			(void) reload_ad();
712 			UNLOCK_CONFIG();
713 			print_idmapdstate();
714 		}
715 	}
716 	/*NOTREACHED*/
717 	return (NULL);
718 }
719 
720 
721 int
722 idmap_cfg_start_updates(idmap_cfg_t *cfg)
723 {
724 	/* Don't check for failure -- see wait_for_ttl() */
725 	hup_ev_port = port_create();
726 
727 	errno = pthread_create(&update_thread_handle, NULL,
728 	    idmap_cfg_update_thread, NULL);
729 	if (errno == 0)
730 		return (0);
731 	else
732 		return (-1);
733 }
734 
735 
736 int
737 idmap_cfg_load(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg)
738 {
739 	int rc = 0;
740 	char *str = NULL;
741 	ad_disc_t ad_ctx = handles->ad_ctx;
742 
743 	pgcfg->list_size_limit = 0;
744 	pgcfg->default_domain = NULL;
745 	pgcfg->domain_name = NULL;
746 	pgcfg->machine_sid = NULL;
747 	pgcfg->domain_controller = NULL;
748 	pgcfg->forest_name = NULL;
749 	pgcfg->site_name = NULL;
750 	pgcfg->global_catalog = NULL;
751 
752 	pthread_mutex_lock(&handles->mutex);
753 
754 	ad_disc_refresh(handles->ad_ctx);
755 
756 	if (scf_pg_update(handles->config_pg) < 0) {
757 		idmapdlog(LOG_ERR, "%s: scf_pg_update() failed: %s",
758 		    me, scf_strerror(scf_error()));
759 		rc = -1;
760 		goto exit;
761 	}
762 
763 	if (scf_pg_update(handles->general_pg) < 0) {
764 		idmapdlog(LOG_ERR, "%s: scf_pg_update() failed: %s",
765 		    me, scf_strerror(scf_error()));
766 		rc = -1;
767 		goto exit;
768 	}
769 
770 	rc = get_val_int(handles, "list_size_limit",
771 	    &pgcfg->list_size_limit, SCF_TYPE_COUNT);
772 	if (rc != 0) {
773 		rc = -1;
774 		goto exit;
775 	}
776 
777 	rc = get_val_astring(handles, "domain_name",
778 	    &pgcfg->domain_name);
779 	if (rc != 0) {
780 		rc = -1;
781 		goto exit;
782 	}
783 	(void) ad_disc_set_DomainName(ad_ctx, pgcfg->domain_name);
784 
785 	rc = get_val_astring(handles, "default_domain",
786 	    &pgcfg->default_domain);
787 	if (rc != 0) {
788 		rc = -1;
789 		goto exit;
790 	}
791 
792 	rc = get_val_astring(handles, "mapping_domain", &str);
793 	if (rc != 0) {
794 		rc = -1;
795 		goto exit;
796 	}
797 
798 	/*
799 	 * We treat default_domain as having been specified in SMF IFF
800 	 * either (the config/default_domain property was set) or (the
801 	 * old, obsolete, never documented config/mapping_domain
802 	 * property was set and the new config/domain_name property was
803 	 * not set).
804 	 */
805 	pgcfg->dflt_dom_set_in_smf = TRUE;
806 	if (pgcfg->default_domain == NULL) {
807 
808 		pgcfg->dflt_dom_set_in_smf = FALSE;
809 
810 		if (pgcfg->domain_name != NULL) {
811 			pgcfg->default_domain = strdup(pgcfg->domain_name);
812 			if (str != NULL) {
813 				idmapdlog(LOG_WARNING,
814 				    "%s: Ignoring obsolete, undocumented "
815 				    "config/mapping_domain property", me);
816 			}
817 		} else if (str != NULL) {
818 			pgcfg->default_domain = strdup(str);
819 			pgcfg->dflt_dom_set_in_smf = TRUE;
820 			idmapdlog(LOG_WARNING,
821 			    "%s: The config/mapping_domain property is "
822 			    "obsolete; support for it will be removed, "
823 			    "please use config/default_domain instead",
824 			    me);
825 		}
826 	}
827 
828 	if (str != NULL)
829 		free(str);
830 
831 	rc = get_val_astring(handles, "machine_sid", &pgcfg->machine_sid);
832 	if (rc != 0) {
833 		rc = -1;
834 		goto exit;
835 	}
836 	if (pgcfg->machine_sid == NULL) {
837 		/* If machine_sid not configured, generate one */
838 		if (generate_machine_sid(&pgcfg->machine_sid) < 0) {
839 			rc =  -1;
840 			goto exit;
841 		}
842 		rc = set_val_astring(handles, "machine_sid",
843 		    pgcfg->machine_sid);
844 		if (rc < 0) {
845 			free(pgcfg->machine_sid);
846 			pgcfg->machine_sid = NULL;
847 			rc = -1;
848 			goto exit;
849 		}
850 	}
851 
852 	str = NULL;
853 	rc = get_val_ds(handles, "domain_controller", 389,
854 	    &pgcfg->domain_controller);
855 	if (rc != 0) {
856 		rc = -1;
857 		goto exit;
858 	}
859 	(void) ad_disc_set_DomainController(ad_ctx, pgcfg->domain_controller);
860 
861 	rc = get_val_astring(handles, "forest_name", &pgcfg->forest_name);
862 	if (rc != 0) {
863 		rc = -1;
864 		goto exit;
865 	}
866 	(void) ad_disc_set_ForestName(ad_ctx, pgcfg->forest_name);
867 
868 	rc = get_val_astring(handles, "site_name", &pgcfg->site_name);
869 	if (rc != 0) {
870 		rc = -1;
871 		goto exit;
872 	}
873 	(void) ad_disc_set_SiteName(ad_ctx, pgcfg->site_name);
874 
875 	str = NULL;
876 	rc = get_val_ds(handles, "global_catalog", 3268,
877 	    &pgcfg->global_catalog);
878 	if (rc != 0) {
879 		rc = -1;
880 		goto exit;
881 	}
882 	(void) ad_disc_set_GlobalCatalog(ad_ctx, pgcfg->global_catalog);
883 
884 
885 	/*
886 	 * Auto Discover the rest
887 	 */
888 	if (pgcfg->default_domain == NULL) {
889 		pgcfg->default_domain = ad_disc_get_DomainName(ad_ctx);
890 		if (pgcfg->default_domain == NULL) {
891 			idmapdlog(LOG_INFO,
892 			    "%s: unable to discover Default Domain", me);
893 		}
894 	}
895 
896 	if (pgcfg->domain_name == NULL) {
897 		pgcfg->domain_name = ad_disc_get_DomainName(ad_ctx);
898 		if (pgcfg->domain_name == NULL) {
899 			idmapdlog(LOG_INFO,
900 			    "%s: unable to discover Domain Name", me);
901 		}
902 	}
903 
904 	if (pgcfg->domain_controller == NULL) {
905 		pgcfg->domain_controller =
906 		    ad_disc_get_DomainController(ad_ctx, AD_DISC_PREFER_SITE);
907 		if (pgcfg->domain_controller == NULL) {
908 			idmapdlog(LOG_INFO,
909 			    "%s: unable to discover Domain Controller", me);
910 		}
911 	}
912 
913 	if (pgcfg->forest_name == NULL) {
914 		pgcfg->forest_name = ad_disc_get_ForestName(ad_ctx);
915 		if (pgcfg->forest_name == NULL) {
916 			idmapdlog(LOG_INFO,
917 			    "%s: unable to discover Forest Name", me);
918 		}
919 	}
920 
921 	if (pgcfg->site_name == NULL) {
922 		pgcfg->site_name = ad_disc_get_SiteName(ad_ctx);
923 		if (pgcfg->site_name == NULL) {
924 			idmapdlog(LOG_INFO,
925 			    "%s: unable to discover Site Name", me);
926 		}
927 	}
928 
929 	if (pgcfg->global_catalog == NULL) {
930 		pgcfg->global_catalog =
931 		    ad_disc_get_GlobalCatalog(ad_ctx, AD_DISC_PREFER_SITE);
932 		if (pgcfg->global_catalog == NULL) {
933 			idmapdlog(LOG_INFO,
934 			    "%s: unable to discover Global Catalog", me);
935 		}
936 	}
937 exit:
938 	pthread_mutex_unlock(&handles->mutex);
939 
940 	return (rc);
941 }
942 
943 /*
944  * Initialize 'cfg'.
945  */
946 idmap_cfg_t *
947 idmap_cfg_init() {
948 	idmap_cfg_handles_t *handles;
949 
950 	/* First the smf repository handles: */
951 	idmap_cfg_t *cfg = calloc(1, sizeof (idmap_cfg_t));
952 	if (!cfg) {
953 		idmapdlog(LOG_ERR, "%s: Out of memory", me);
954 		return (NULL);
955 	}
956 	handles = &cfg->handles;
957 
958 	(void) pthread_mutex_init(&handles->mutex, NULL);
959 
960 	if (!(handles->main = scf_handle_create(SCF_VERSION))) {
961 		idmapdlog(LOG_ERR, "%s: scf_handle_create() failed: %s",
962 		    me, scf_strerror(scf_error()));
963 		goto error;
964 	}
965 
966 	if (scf_handle_bind(handles->main) < 0) {
967 		idmapdlog(LOG_ERR, "%s: scf_handle_bind() failed: %s",
968 		    me, scf_strerror(scf_error()));
969 		goto error;
970 	}
971 
972 	if (!(handles->service = scf_service_create(handles->main)) ||
973 	    !(handles->instance = scf_instance_create(handles->main)) ||
974 	    !(handles->config_pg = scf_pg_create(handles->main)) ||
975 	    !(handles->general_pg = scf_pg_create(handles->main))) {
976 		idmapdlog(LOG_ERR, "%s: scf handle creation failed: %s",
977 		    me, scf_strerror(scf_error()));
978 		goto error;
979 	}
980 
981 	if (scf_handle_decode_fmri(handles->main,
982 		FMRI_BASE "/:properties/" CONFIG_PG,
983 		NULL,				/* scope */
984 		handles->service,		/* service */
985 		handles->instance,		/* instance */
986 		handles->config_pg,		/* pg */
987 		NULL,				/* prop */
988 		SCF_DECODE_FMRI_EXACT) < 0) {
989 		idmapdlog(LOG_ERR, "%s: scf_handle_decode_fmri() failed: %s",
990 		    me, scf_strerror(scf_error()));
991 		goto error;
992 
993 	}
994 
995 	if (scf_service_get_pg(handles->service,
996 		GENERAL_PG, handles->general_pg) < 0) {
997 		idmapdlog(LOG_ERR, "%s: scf_service_get_pg() failed: %s",
998 		    me, scf_strerror(scf_error()));
999 		goto error;
1000 	}
1001 
1002 	/* Initialize AD Auto Discovery context */
1003 	handles->ad_ctx = ad_disc_init();
1004 	if (handles->ad_ctx == NULL)
1005 		goto error;
1006 
1007 	return (cfg);
1008 
1009 error:
1010 	(void) idmap_cfg_fini(cfg);
1011 	return (NULL);
1012 }
1013 
1014 void
1015 idmap_cfg_unload(idmap_pg_config_t *pgcfg) {
1016 
1017 	if (pgcfg->default_domain) {
1018 		free(pgcfg->default_domain);
1019 		pgcfg->default_domain = NULL;
1020 	}
1021 	if (pgcfg->domain_name) {
1022 		free(pgcfg->domain_name);
1023 		pgcfg->domain_name = NULL;
1024 	}
1025 	if (pgcfg->machine_sid) {
1026 		free(pgcfg->machine_sid);
1027 		pgcfg->machine_sid = NULL;
1028 	}
1029 	if (pgcfg->domain_controller) {
1030 		free(pgcfg->domain_controller);
1031 		pgcfg->domain_controller = NULL;
1032 	}
1033 	if (pgcfg->forest_name) {
1034 		free(pgcfg->forest_name);
1035 		pgcfg->forest_name = NULL;
1036 	}
1037 	if (pgcfg->site_name) {
1038 		free(pgcfg->site_name);
1039 		pgcfg->site_name = NULL;
1040 	}
1041 	if (pgcfg->global_catalog) {
1042 		free(pgcfg->global_catalog);
1043 		pgcfg->global_catalog = NULL;
1044 	}
1045 }
1046 
1047 int
1048 idmap_cfg_fini(idmap_cfg_t *cfg)
1049 {
1050 	idmap_cfg_handles_t *handles = &cfg->handles;
1051 	idmap_cfg_unload(&cfg->pgcfg);
1052 
1053 	(void) pthread_mutex_destroy(&handles->mutex);
1054 	scf_pg_destroy(handles->config_pg);
1055 	scf_pg_destroy(handles->general_pg);
1056 	scf_instance_destroy(handles->instance);
1057 	scf_service_destroy(handles->service);
1058 	scf_handle_destroy(handles->main);
1059 	ad_disc_fini(handles->ad_ctx);
1060 	free(cfg);
1061 
1062 	return (0);
1063 }
1064