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