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