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