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 == -2)
546 			(void) idmapdlog(LOG_ERR,
547 			    "idmapd: Various errors re-loading configuration "
548 			    "will cause AD lookups to fail");
549 		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 	char *str = NULL;
765 	ad_disc_t ad_ctx = handles->ad_ctx;
766 
767 	pgcfg->list_size_limit = 0;
768 	pgcfg->default_domain = NULL;
769 	pgcfg->domain_name = NULL;
770 	pgcfg->machine_sid = NULL;
771 	pgcfg->domain_controller = NULL;
772 	pgcfg->forest_name = NULL;
773 	pgcfg->site_name = NULL;
774 	pgcfg->global_catalog = NULL;
775 
776 	pthread_mutex_lock(&handles->mutex);
777 
778 	ad_disc_refresh(handles->ad_ctx);
779 
780 	if (scf_pg_update(handles->config_pg) < 0) {
781 		idmapdlog(LOG_ERR, "%s: scf_pg_update() failed: %s",
782 		    me, scf_strerror(scf_error()));
783 		rc = -2;
784 		goto exit;
785 	}
786 
787 	if (scf_pg_update(handles->general_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 	rc = get_val_int(handles, "list_size_limit",
795 	    &pgcfg->list_size_limit, SCF_TYPE_COUNT);
796 	if (rc != 0) {
797 		pgcfg->list_size_limit = 0;
798 		errors++;
799 	}
800 
801 	rc = get_val_astring(handles, "domain_name",
802 	    &pgcfg->domain_name);
803 	if (rc != 0)
804 		errors++;
805 	else
806 		(void) ad_disc_set_DomainName(ad_ctx, pgcfg->domain_name);
807 
808 	rc = get_val_astring(handles, "default_domain",
809 	    &pgcfg->default_domain);
810 	if (rc != 0) {
811 		/*
812 		 * SCF failures fetching config/default_domain we treat
813 		 * as fatal as they may leave ID mapping rules that
814 		 * match unqualified winnames flapping in the wind.
815 		 */
816 		rc = -2;
817 		goto exit;
818 	}
819 
820 	rc = get_val_astring(handles, "mapping_domain", &str);
821 	if (rc != 0)
822 		errors++;
823 
824 	/*
825 	 * We treat default_domain as having been specified in SMF IFF
826 	 * either (the config/default_domain property was set) or (the
827 	 * old, obsolete, never documented config/mapping_domain
828 	 * property was set and the new config/domain_name property was
829 	 * not set).
830 	 */
831 	pgcfg->dflt_dom_set_in_smf = TRUE;
832 	if (pgcfg->default_domain == NULL) {
833 
834 		pgcfg->dflt_dom_set_in_smf = FALSE;
835 
836 		if (pgcfg->domain_name != NULL) {
837 			pgcfg->default_domain = strdup(pgcfg->domain_name);
838 			if (str != NULL) {
839 				idmapdlog(LOG_WARNING,
840 				    "%s: Ignoring obsolete, undocumented "
841 				    "config/mapping_domain property", me);
842 			}
843 		} else if (str != NULL) {
844 			pgcfg->default_domain = strdup(str);
845 			pgcfg->dflt_dom_set_in_smf = TRUE;
846 			idmapdlog(LOG_WARNING,
847 			    "%s: The config/mapping_domain property is "
848 			    "obsolete; support for it will be removed, "
849 			    "please use config/default_domain instead",
850 			    me);
851 		}
852 	}
853 
854 	if (str != NULL)
855 		free(str);
856 
857 	rc = get_val_astring(handles, "machine_sid", &pgcfg->machine_sid);
858 	if (rc != 0)
859 		errors++;
860 	if (pgcfg->machine_sid == NULL) {
861 		/* If machine_sid not configured, generate one */
862 		if (generate_machine_sid(&pgcfg->machine_sid) < 0) {
863 			rc =  -2;
864 			goto exit;
865 		}
866 		rc = set_val_astring(handles, "machine_sid",
867 		    pgcfg->machine_sid);
868 		if (rc != 0)
869 			errors++;
870 	}
871 
872 	str = NULL;
873 	rc = get_val_ds(handles, "domain_controller", 389,
874 	    &pgcfg->domain_controller);
875 	if (rc != 0)
876 		errors++;
877 	else
878 		(void) ad_disc_set_DomainController(ad_ctx,
879 		    pgcfg->domain_controller);
880 
881 	rc = get_val_astring(handles, "forest_name", &pgcfg->forest_name);
882 	if (rc != 0)
883 		errors++;
884 	else
885 		(void) ad_disc_set_ForestName(ad_ctx, pgcfg->forest_name);
886 
887 	rc = get_val_astring(handles, "site_name", &pgcfg->site_name);
888 	if (rc != 0)
889 		errors++;
890 	else
891 		(void) ad_disc_set_SiteName(ad_ctx, pgcfg->site_name);
892 
893 	str = NULL;
894 	rc = get_val_ds(handles, "global_catalog", 3268,
895 	    &pgcfg->global_catalog);
896 	if (rc != 0)
897 		errors++;
898 	else
899 		(void) ad_disc_set_GlobalCatalog(ad_ctx, pgcfg->global_catalog);
900 
901 
902 	if (!discover)
903 		goto exit;
904 
905 	/*
906 	 * Auto Discover the rest
907 	 */
908 	if (pgcfg->default_domain == NULL) {
909 		pgcfg->default_domain = ad_disc_get_DomainName(ad_ctx);
910 		if (pgcfg->default_domain == NULL) {
911 			idmapdlog(LOG_INFO,
912 			    "%s: unable to discover Default Domain", me);
913 		}
914 	}
915 
916 	if (pgcfg->domain_name == NULL) {
917 		pgcfg->domain_name = ad_disc_get_DomainName(ad_ctx);
918 		if (pgcfg->domain_name == NULL) {
919 			idmapdlog(LOG_INFO,
920 			    "%s: unable to discover Domain Name", me);
921 		}
922 	}
923 
924 	if (pgcfg->domain_controller == NULL) {
925 		pgcfg->domain_controller =
926 		    ad_disc_get_DomainController(ad_ctx, AD_DISC_PREFER_SITE);
927 		if (pgcfg->domain_controller == NULL) {
928 			idmapdlog(LOG_INFO,
929 			    "%s: unable to discover Domain Controller", me);
930 		}
931 	}
932 
933 	if (pgcfg->forest_name == NULL) {
934 		pgcfg->forest_name = ad_disc_get_ForestName(ad_ctx);
935 		if (pgcfg->forest_name == NULL) {
936 			idmapdlog(LOG_INFO,
937 			    "%s: unable to discover Forest Name", me);
938 		}
939 	}
940 
941 	if (pgcfg->site_name == NULL) {
942 		pgcfg->site_name = ad_disc_get_SiteName(ad_ctx);
943 		if (pgcfg->site_name == NULL) {
944 			idmapdlog(LOG_INFO,
945 			    "%s: unable to discover Site Name", me);
946 		}
947 	}
948 
949 	if (pgcfg->global_catalog == NULL) {
950 		pgcfg->global_catalog =
951 		    ad_disc_get_GlobalCatalog(ad_ctx, AD_DISC_PREFER_SITE);
952 		if (pgcfg->global_catalog == NULL) {
953 			idmapdlog(LOG_INFO,
954 			    "%s: unable to discover Global Catalog", me);
955 		}
956 	}
957 
958 exit:
959 	pthread_mutex_unlock(&handles->mutex);
960 
961 	if (rc == -2)
962 		return (rc);
963 
964 	return ((errors == 0) ? 0 : -1);
965 }
966 
967 /*
968  * Initialize 'cfg'.
969  */
970 idmap_cfg_t *
971 idmap_cfg_init() {
972 	idmap_cfg_handles_t *handles;
973 
974 	/* First the smf repository handles: */
975 	idmap_cfg_t *cfg = calloc(1, sizeof (idmap_cfg_t));
976 	if (!cfg) {
977 		idmapdlog(LOG_ERR, "%s: Out of memory", me);
978 		return (NULL);
979 	}
980 	handles = &cfg->handles;
981 
982 	(void) pthread_mutex_init(&handles->mutex, NULL);
983 
984 	if (!(handles->main = scf_handle_create(SCF_VERSION))) {
985 		idmapdlog(LOG_ERR, "%s: scf_handle_create() failed: %s",
986 		    me, scf_strerror(scf_error()));
987 		goto error;
988 	}
989 
990 	if (scf_handle_bind(handles->main) < 0) {
991 		idmapdlog(LOG_ERR, "%s: scf_handle_bind() failed: %s",
992 		    me, scf_strerror(scf_error()));
993 		goto error;
994 	}
995 
996 	if (!(handles->service = scf_service_create(handles->main)) ||
997 	    !(handles->instance = scf_instance_create(handles->main)) ||
998 	    !(handles->config_pg = scf_pg_create(handles->main)) ||
999 	    !(handles->general_pg = scf_pg_create(handles->main))) {
1000 		idmapdlog(LOG_ERR, "%s: scf handle creation failed: %s",
1001 		    me, scf_strerror(scf_error()));
1002 		goto error;
1003 	}
1004 
1005 	if (scf_handle_decode_fmri(handles->main,
1006 		FMRI_BASE "/:properties/" CONFIG_PG,
1007 		NULL,				/* scope */
1008 		handles->service,		/* service */
1009 		handles->instance,		/* instance */
1010 		handles->config_pg,		/* pg */
1011 		NULL,				/* prop */
1012 		SCF_DECODE_FMRI_EXACT) < 0) {
1013 		idmapdlog(LOG_ERR, "%s: scf_handle_decode_fmri() failed: %s",
1014 		    me, scf_strerror(scf_error()));
1015 		goto error;
1016 
1017 	}
1018 
1019 	if (scf_service_get_pg(handles->service,
1020 		GENERAL_PG, handles->general_pg) < 0) {
1021 		idmapdlog(LOG_ERR, "%s: scf_service_get_pg() failed: %s",
1022 		    me, scf_strerror(scf_error()));
1023 		goto error;
1024 	}
1025 
1026 	/* Initialize AD Auto Discovery context */
1027 	handles->ad_ctx = ad_disc_init();
1028 	if (handles->ad_ctx == NULL)
1029 		goto error;
1030 
1031 	return (cfg);
1032 
1033 error:
1034 	(void) idmap_cfg_fini(cfg);
1035 	return (NULL);
1036 }
1037 
1038 void
1039 idmap_cfg_unload(idmap_pg_config_t *pgcfg) {
1040 
1041 	if (pgcfg->default_domain) {
1042 		free(pgcfg->default_domain);
1043 		pgcfg->default_domain = NULL;
1044 	}
1045 	if (pgcfg->domain_name) {
1046 		free(pgcfg->domain_name);
1047 		pgcfg->domain_name = NULL;
1048 	}
1049 	if (pgcfg->machine_sid) {
1050 		free(pgcfg->machine_sid);
1051 		pgcfg->machine_sid = NULL;
1052 	}
1053 	if (pgcfg->domain_controller) {
1054 		free(pgcfg->domain_controller);
1055 		pgcfg->domain_controller = NULL;
1056 	}
1057 	if (pgcfg->forest_name) {
1058 		free(pgcfg->forest_name);
1059 		pgcfg->forest_name = NULL;
1060 	}
1061 	if (pgcfg->site_name) {
1062 		free(pgcfg->site_name);
1063 		pgcfg->site_name = NULL;
1064 	}
1065 	if (pgcfg->global_catalog) {
1066 		free(pgcfg->global_catalog);
1067 		pgcfg->global_catalog = NULL;
1068 	}
1069 }
1070 
1071 int
1072 idmap_cfg_fini(idmap_cfg_t *cfg)
1073 {
1074 	idmap_cfg_handles_t *handles = &cfg->handles;
1075 	idmap_cfg_unload(&cfg->pgcfg);
1076 
1077 	(void) pthread_mutex_destroy(&handles->mutex);
1078 	scf_pg_destroy(handles->config_pg);
1079 	scf_pg_destroy(handles->general_pg);
1080 	scf_instance_destroy(handles->instance);
1081 	scf_service_destroy(handles->service);
1082 	scf_handle_destroy(handles->main);
1083 	ad_disc_fini(handles->ad_ctx);
1084 	free(cfg);
1085 
1086 	return (0);
1087 }
1088