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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2019 Nexenta Systems, Inc. All rights reserved.
24 */
25
26
27 /*
28 * Config routines common to idmap(1M) and idmapd(1M)
29 */
30
31 #include <stdlib.h>
32 #include <strings.h>
33 #include <libintl.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <stdio.h>
37 #include <stdarg.h>
38 #include <uuid/uuid.h>
39 #include <pthread.h>
40 #include <port.h>
41 #include <sys/socket.h>
42 #include <net/route.h>
43 #include <sys/u8_textprep.h>
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
46 #include <netdb.h>
47 #include <note.h>
48 #include <limits.h>
49 #include "idmapd.h"
50 #include "addisc.h"
51
52 #define MACHINE_SID_LEN (9 + 3 * 11)
53 #define FMRI_BASE "svc:/system/idmap"
54 #define CONFIG_PG "config"
55 #define DEBUG_PG "debug"
56 #define RECONFIGURE 1
57 #define POKE_AUTO_DISCOVERY 2
58 #define KICK_AUTO_DISCOVERY 3
59
60 /*
61 * Default cache timeouts. Can override via svccfg
62 * config/id_cache_timeout = count: seconds
63 * config/name_cache_timeout = count: seconds
64 */
65 #define ID_CACHE_TMO_DEFAULT 86400
66 #define NAME_CACHE_TMO_DEFAULT 604800
67
68 /*
69 * Default maximum time between rediscovery runs.
70 * config/rediscovery_interval = count: seconds
71 */
72 #define REDISCOVERY_INTERVAL_DEFAULT 3600
73
74 /*
75 * Mininum time between rediscovery runs, in case adutils gives us a
76 * really short TTL (which it never should, but be defensive)
77 * (not configurable) seconds.
78 */
79 #define MIN_REDISCOVERY_INTERVAL 60
80
81 /*
82 * Max number of concurrent door calls
83 */
84 #define MAX_THREADS_DEFAULT 40
85
86 enum event_type {
87 EVENT_NOTHING, /* Woke up for no good reason */
88 EVENT_TIMEOUT, /* Timeout expired */
89 EVENT_ROUTING, /* An interesting routing event happened */
90 EVENT_POKED, /* Requested from degrade_svc() */
91 EVENT_KICKED, /* Force rediscovery, i.e. DC failed. */
92 EVENT_REFRESH, /* SMF refresh */
93 };
94
95
96 static void idmapd_set_krb5_realm(char *);
97
98 static pthread_t update_thread_handle = 0;
99
100 static int idmapd_ev_port = -1;
101 static int rt_sock = -1;
102
103 struct enum_lookup_map directory_mapping_map[] = {
104 { DIRECTORY_MAPPING_NONE, "none" },
105 { DIRECTORY_MAPPING_NAME, "name" },
106 { DIRECTORY_MAPPING_IDMU, "idmu" },
107 { 0, NULL },
108 };
109
110 struct enum_lookup_map trust_dir_map[] = {
111 { 1, "they trust us" },
112 { 2, "we trust them" },
113 { 3, "we trust each other" },
114 { 0, NULL },
115 };
116
117 static int
generate_machine_uuid(char ** machine_uuid)118 generate_machine_uuid(char **machine_uuid)
119 {
120 uuid_t uu;
121
122 *machine_uuid = calloc(1, UUID_PRINTABLE_STRING_LENGTH + 1);
123 if (*machine_uuid == NULL) {
124 idmapdlog(LOG_ERR, "Out of memory");
125 return (-1);
126 }
127
128 uuid_clear(uu);
129 uuid_generate_time(uu);
130 uuid_unparse(uu, *machine_uuid);
131
132 return (0);
133 }
134
135 static int
generate_machine_sid(char ** machine_sid,char * machine_uuid)136 generate_machine_sid(char **machine_sid, char *machine_uuid)
137 {
138 union {
139 uuid_t uu;
140 uint32_t v[4];
141 } uv;
142 int len;
143
144 /*
145 * Split the 128-bit machine UUID into three 32-bit values
146 * we'll use as the "sub-authorities" of the machine SID.
147 * The machine_sid will have the form S-1-5-21-J-K-L
148 * (that's four sub-authorities altogether) where:
149 * J = last 4 bytes of node_addr,
150 * K = time_mid, time_hi_and_version
151 * L = time_low
152 * (see struct uuid)
153 */
154
155 (void) memset(&uv, 0, sizeof (uv));
156 (void) uuid_parse(machine_uuid, uv.uu);
157
158 len = asprintf(machine_sid, "S-1-5-21-%u-%u-%u",
159 uv.v[3], uv.v[0], uv.v[1]);
160
161 if (len == -1 || *machine_sid == NULL) {
162 idmapdlog(LOG_ERR, "Out of memory");
163 return (-1);
164 }
165
166 return (0);
167 }
168
169
170 /* In the case of error, exists is set to FALSE anyway */
171 static int
prop_exists(idmap_cfg_handles_t * handles,const char * name,boolean_t * exists)172 prop_exists(idmap_cfg_handles_t *handles, const char *name, boolean_t *exists)
173 {
174
175 scf_property_t *scf_prop;
176
177 *exists = B_FALSE;
178
179 scf_prop = scf_property_create(handles->main);
180 if (scf_prop == NULL) {
181 idmapdlog(LOG_ERR, "scf_property_create() failed: %s",
182 scf_strerror(scf_error()));
183 return (-1);
184 }
185
186 if (scf_pg_get_property(handles->config_pg, name, scf_prop) == 0)
187 *exists = B_TRUE;
188
189 scf_property_destroy(scf_prop);
190
191 return (0);
192 }
193
194 static int
get_debug(idmap_cfg_handles_t * handles,const char * name)195 get_debug(idmap_cfg_handles_t *handles, const char *name)
196 {
197 int64_t i64 = 0;
198
199 scf_property_t *scf_prop;
200 scf_value_t *value;
201
202 scf_prop = scf_property_create(handles->main);
203 if (scf_prop == NULL) {
204 idmapdlog(LOG_ERR, "scf_property_create() failed: %s",
205 scf_strerror(scf_error()));
206 abort();
207 }
208 value = scf_value_create(handles->main);
209 if (value == NULL) {
210 idmapdlog(LOG_ERR, "scf_value_create() failed: %s",
211 scf_strerror(scf_error()));
212 abort();
213 }
214
215 if (scf_pg_get_property(handles->debug_pg, name, scf_prop) < 0) {
216 /* this is OK: the property is just undefined */
217 goto destruction;
218 }
219
220
221 if (scf_property_get_value(scf_prop, value) < 0) {
222 /* It is still OK when a property doesn't have any value */
223 goto destruction;
224 }
225
226 if (scf_value_get_integer(value, &i64) != 0) {
227 idmapdlog(LOG_ERR, "Can not retrieve %s/%s: %s",
228 DEBUG_PG, name, scf_strerror(scf_error()));
229 abort();
230 }
231
232 destruction:
233 scf_value_destroy(value);
234 scf_property_destroy(scf_prop);
235
236 return ((int)i64);
237 }
238
239 static int
get_val_bool(idmap_cfg_handles_t * handles,const char * name,boolean_t * val,boolean_t default_val)240 get_val_bool(idmap_cfg_handles_t *handles, const char *name,
241 boolean_t *val, boolean_t default_val)
242 {
243 int rc = 0;
244
245 scf_property_t *scf_prop;
246 scf_value_t *value;
247
248 *val = default_val;
249
250 scf_prop = scf_property_create(handles->main);
251 if (scf_prop == NULL) {
252 idmapdlog(LOG_ERR, "scf_property_create() failed: %s",
253 scf_strerror(scf_error()));
254 return (-1);
255 }
256 value = scf_value_create(handles->main);
257 if (value == NULL) {
258 idmapdlog(LOG_ERR, "scf_value_create() failed: %s",
259 scf_strerror(scf_error()));
260 scf_property_destroy(scf_prop);
261 return (-1);
262 }
263
264 /* It is OK if the property is undefined */
265 if (scf_pg_get_property(handles->config_pg, name, scf_prop) < 0)
266 goto destruction;
267
268
269 /* It is still OK when a property doesn't have any value */
270 if (scf_property_get_value(scf_prop, value) < 0)
271 goto destruction;
272
273 uint8_t b;
274 rc = scf_value_get_boolean(value, &b);
275
276 if (rc == 0)
277 *val = (boolean_t)b;
278
279 destruction:
280 scf_value_destroy(value);
281 scf_property_destroy(scf_prop);
282
283 return (rc);
284 }
285
286 static int
get_val_int(idmap_cfg_handles_t * handles,const char * name,void * val,scf_type_t type)287 get_val_int(idmap_cfg_handles_t *handles, const char *name,
288 void *val, scf_type_t type)
289 {
290 int rc = 0;
291
292 scf_property_t *scf_prop;
293 scf_value_t *value;
294
295 switch (type) {
296 case SCF_TYPE_COUNT:
297 *(uint64_t *)val = 0;
298 break;
299 case SCF_TYPE_INTEGER:
300 *(int64_t *)val = 0;
301 break;
302 default:
303 idmapdlog(LOG_ERR, "Invalid scf integer type (%d)",
304 type);
305 abort();
306 }
307
308 scf_prop = scf_property_create(handles->main);
309 if (scf_prop == NULL) {
310 idmapdlog(LOG_ERR, "scf_property_create() failed: %s",
311 scf_strerror(scf_error()));
312 return (-1);
313 }
314 value = scf_value_create(handles->main);
315 if (value == NULL) {
316 idmapdlog(LOG_ERR, "scf_value_create() failed: %s",
317 scf_strerror(scf_error()));
318 scf_property_destroy(scf_prop);
319 return (-1);
320 }
321
322 if (scf_pg_get_property(handles->config_pg, name, scf_prop) < 0)
323 /* this is OK: the property is just undefined */
324 goto destruction;
325
326
327 if (scf_property_get_value(scf_prop, value) < 0)
328 /* It is still OK when a property doesn't have any value */
329 goto destruction;
330
331 switch (type) {
332 case SCF_TYPE_COUNT:
333 rc = scf_value_get_count(value, val);
334 break;
335 case SCF_TYPE_INTEGER:
336 rc = scf_value_get_integer(value, val);
337 break;
338 default:
339 abort(); /* tested above */
340 /* NOTREACHED */
341 }
342
343 if (rc != 0) {
344 idmapdlog(LOG_ERR, "Can not retrieve config/%s: %s",
345 name, scf_strerror(scf_error()));
346 }
347
348 destruction:
349 scf_value_destroy(value);
350 scf_property_destroy(scf_prop);
351
352 return (rc);
353 }
354
355 static char *
scf_value2string(const char * name,scf_value_t * value)356 scf_value2string(const char *name, scf_value_t *value)
357 {
358 static size_t max_val = 0;
359
360 if (max_val == 0)
361 max_val = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
362
363 char buf[max_val + 1];
364 if (scf_value_get_astring(value, buf, max_val + 1) < 0) {
365 idmapdlog(LOG_ERR, "Can not retrieve config/%s: %s",
366 name, scf_strerror(scf_error()));
367 return (NULL);
368 }
369
370 char *s = strdup(buf);
371 if (s == NULL)
372 idmapdlog(LOG_ERR, "Out of memory");
373
374 return (s);
375 }
376
377 static int
get_val_ds(idmap_cfg_handles_t * handles,const char * name,int defport,ad_disc_ds_t ** val)378 get_val_ds(idmap_cfg_handles_t *handles, const char *name, int defport,
379 ad_disc_ds_t **val)
380 {
381 char port_str[8];
382 struct addrinfo hints;
383 struct addrinfo *ai;
384 ad_disc_ds_t *servers = NULL;
385 scf_property_t *scf_prop;
386 scf_value_t *value;
387 scf_iter_t *iter;
388 char *host, *portstr;
389 int err, len, i;
390 int count = 0;
391 int rc = -1;
392
393 *val = NULL;
394
395 restart:
396 scf_prop = scf_property_create(handles->main);
397 if (scf_prop == NULL) {
398 idmapdlog(LOG_ERR, "scf_property_create() failed: %s",
399 scf_strerror(scf_error()));
400 return (-1);
401 }
402
403 value = scf_value_create(handles->main);
404 if (value == NULL) {
405 idmapdlog(LOG_ERR, "scf_value_create() failed: %s",
406 scf_strerror(scf_error()));
407 scf_property_destroy(scf_prop);
408 return (-1);
409 }
410
411 iter = scf_iter_create(handles->main);
412 if (iter == NULL) {
413 idmapdlog(LOG_ERR, "scf_iter_create() failed: %s",
414 scf_strerror(scf_error()));
415 scf_value_destroy(value);
416 scf_property_destroy(scf_prop);
417 return (-1);
418 }
419
420 if (scf_pg_get_property(handles->config_pg, name, scf_prop) < 0) {
421 /* this is OK: the property is just undefined */
422 rc = 0;
423 goto destruction;
424 }
425
426 if (scf_iter_property_values(iter, scf_prop) < 0) {
427 idmapdlog(LOG_ERR,
428 "scf_iter_property_values(%s) failed: %s",
429 name, scf_strerror(scf_error()));
430 goto destruction;
431 }
432
433 /* Workaround scf bugs -- can't reset an iteration */
434 if (count == 0) {
435 while (scf_iter_next_value(iter, value) > 0)
436 count++;
437
438 if (count == 0) {
439 /* no values */
440 rc = 0;
441 goto destruction;
442 }
443
444 scf_value_destroy(value);
445 scf_iter_destroy(iter);
446 scf_property_destroy(scf_prop);
447 goto restart;
448 }
449
450 if ((servers = calloc(count + 1, sizeof (*servers))) == NULL) {
451 idmapdlog(LOG_ERR, "Out of memory");
452 goto destruction;
453 }
454
455 (void) memset(&hints, 0, sizeof (hints));
456 hints.ai_protocol = IPPROTO_TCP;
457 hints.ai_socktype = SOCK_STREAM;
458 host = NULL;
459
460 i = 0;
461 while (i < count && scf_iter_next_value(iter, value) > 0) {
462 if (host) {
463 free(host);
464 host = NULL;
465 }
466 servers[i].priority = 0;
467 servers[i].weight = 100;
468 servers[i].port = defport;
469 if ((host = scf_value2string(name, value)) == NULL)
470 continue;
471 if ((portstr = strchr(host, ':')) != NULL) {
472 *portstr++ = '\0';
473 servers[i].port = strtol(portstr,
474 (char **)NULL, 10);
475 if (servers[i].port == 0)
476 servers[i].port = defport;
477 }
478
479 /*
480 * Ignore this server if the hostname is too long
481 * or empty (continue without i++)
482 */
483 len = strlen(host);
484 if (len == 0) {
485 if (DBG(CONFIG, 1)) {
486 idmapdlog(LOG_INFO, "%s host=\"\"", name);
487 }
488 continue;
489 }
490 if (len >= sizeof (servers->host)) {
491 idmapdlog(LOG_ERR, "Host name too long: %s", host);
492 idmapdlog(LOG_ERR, "ignoring %s value", name);
493 continue;
494 }
495
496 /*
497 * Get the host address too. If we can't, then
498 * log an error and skip this host.
499 */
500 (void) snprintf(port_str, sizeof (port_str),
501 "%d", servers[i].port);
502 ai = NULL;
503 err = getaddrinfo(host, port_str, &hints, &ai);
504 if (err != 0) {
505 idmapdlog(LOG_ERR, "No address for host: %s (%s)",
506 host, gai_strerror(err));
507 idmapdlog(LOG_ERR, "ignoring %s value", name);
508 continue;
509 }
510
511 (void) strlcpy(servers[i].host, host,
512 sizeof (servers->host));
513 (void) memcpy(&servers[i].addr, ai->ai_addr, ai->ai_addrlen);
514 freeaddrinfo(ai);
515
516 /* Added a DS to the array. */
517 i++;
518 }
519 free(host);
520
521 if (i == 0) {
522 if (DBG(CONFIG, 1)) {
523 idmapdlog(LOG_INFO, "%s is empty", name);
524 }
525 free(servers);
526 servers = NULL;
527 }
528 *val = servers;
529
530 rc = 0;
531
532 destruction:
533 scf_value_destroy(value);
534 scf_iter_destroy(iter);
535 scf_property_destroy(scf_prop);
536
537 if (rc < 0) {
538 if (servers)
539 free(servers);
540 *val = NULL;
541 }
542
543 return (rc);
544 }
545
546 static int
get_val_astring(idmap_cfg_handles_t * handles,const char * name,char ** val)547 get_val_astring(idmap_cfg_handles_t *handles, const char *name, char **val)
548 {
549 int rc = 0;
550
551 scf_property_t *scf_prop;
552 scf_value_t *value;
553
554 scf_prop = scf_property_create(handles->main);
555 if (scf_prop == NULL) {
556 idmapdlog(LOG_ERR, "scf_property_create() failed: %s",
557 scf_strerror(scf_error()));
558 return (-1);
559 }
560 value = scf_value_create(handles->main);
561 if (value == NULL) {
562 idmapdlog(LOG_ERR, "scf_value_create() failed: %s",
563 scf_strerror(scf_error()));
564 scf_property_destroy(scf_prop);
565 return (-1);
566 }
567
568 *val = NULL;
569
570 if (scf_pg_get_property(handles->config_pg, name, scf_prop) < 0)
571 /* this is OK: the property is just undefined */
572 goto destruction;
573
574 if (scf_property_get_value(scf_prop, value) < 0) {
575 idmapdlog(LOG_ERR,
576 "scf_property_get_value(%s) failed: %s",
577 name, scf_strerror(scf_error()));
578 rc = -1;
579 goto destruction;
580 }
581
582 *val = scf_value2string(name, value);
583 if (*val == NULL)
584 rc = -1;
585
586 destruction:
587 scf_value_destroy(value);
588 scf_property_destroy(scf_prop);
589
590 if (rc < 0) {
591 if (*val)
592 free(*val);
593 *val = NULL;
594 }
595
596 return (rc);
597 }
598
599
600 static int
del_val(idmap_cfg_handles_t * handles,scf_propertygroup_t * pg,const char * name)601 del_val(
602 idmap_cfg_handles_t *handles,
603 scf_propertygroup_t *pg,
604 const char *name)
605 {
606 int rc = -1;
607 int ret;
608 scf_transaction_t *tx = NULL;
609 scf_transaction_entry_t *ent = NULL;
610
611 if ((tx = scf_transaction_create(handles->main)) == NULL) {
612 idmapdlog(LOG_ERR,
613 "scf_transaction_create() failed: %s",
614 scf_strerror(scf_error()));
615 goto destruction;
616 }
617 if ((ent = scf_entry_create(handles->main)) == NULL) {
618 idmapdlog(LOG_ERR,
619 "scf_entry_create() failed: %s",
620 scf_strerror(scf_error()));
621 goto destruction;
622 }
623
624 do {
625 if (scf_pg_update(pg) == -1) {
626 idmapdlog(LOG_ERR,
627 "scf_pg_update(%s) failed: %s",
628 name, scf_strerror(scf_error()));
629 goto destruction;
630 }
631 if (scf_transaction_start(tx, pg) != 0) {
632 idmapdlog(LOG_ERR,
633 "scf_transaction_start(%s) failed: %s",
634 name, scf_strerror(scf_error()));
635 goto destruction;
636 }
637
638 if (scf_transaction_property_delete(tx, ent, name) != 0) {
639 /* Don't complain if it already doesn't exist. */
640 if (scf_error() != SCF_ERROR_NOT_FOUND) {
641 idmapdlog(LOG_ERR,
642 "scf_transaction_property_delete() failed:"
643 " %s",
644 scf_strerror(scf_error()));
645 }
646 goto destruction;
647 }
648
649 ret = scf_transaction_commit(tx);
650
651 if (ret == 0)
652 scf_transaction_reset(tx);
653 } while (ret == 0);
654
655 if (ret == -1) {
656 idmapdlog(LOG_ERR,
657 "scf_transaction_commit(%s) failed: %s",
658 name, scf_strerror(scf_error()));
659 goto destruction;
660 }
661
662 rc = 0;
663
664 destruction:
665 if (ent != NULL)
666 scf_entry_destroy(ent);
667 if (tx != NULL)
668 scf_transaction_destroy(tx);
669 return (rc);
670 }
671
672
673 static int
set_val(idmap_cfg_handles_t * handles,scf_propertygroup_t * pg,const char * name,scf_value_t * value)674 set_val(
675 idmap_cfg_handles_t *handles,
676 scf_propertygroup_t *pg,
677 const char *name,
678 scf_value_t *value)
679 {
680 int rc = -1;
681 int i;
682 scf_property_t *prop = NULL;
683 scf_transaction_t *tx = NULL;
684 scf_transaction_entry_t *ent = NULL;
685
686 if ((prop = scf_property_create(handles->main)) == NULL ||
687 (tx = scf_transaction_create(handles->main)) == NULL ||
688 (ent = scf_entry_create(handles->main)) == NULL) {
689 idmapdlog(LOG_ERR, "Unable to set property %s",
690 name, scf_strerror(scf_error()));
691 goto destruction;
692 }
693
694 for (i = 0; i < MAX_TRIES; i++) {
695 int ret;
696
697 if (scf_pg_update(pg) == -1) {
698 idmapdlog(LOG_ERR,
699 "scf_pg_update() failed: %s",
700 scf_strerror(scf_error()));
701 goto destruction;
702 }
703
704 if (scf_transaction_start(tx, pg) == -1) {
705 idmapdlog(LOG_ERR,
706 "scf_transaction_start(%s) failed: %s",
707 name, scf_strerror(scf_error()));
708 goto destruction;
709 }
710
711 ret = scf_pg_get_property(pg, name, prop);
712 if (ret == SCF_SUCCESS) {
713 if (scf_transaction_property_change_type(tx, ent, name,
714 scf_value_type(value)) < 0) {
715 idmapdlog(LOG_ERR,
716 "scf_transaction_property_change_type(%s)"
717 " failed: %s",
718 name, scf_strerror(scf_error()));
719 goto destruction;
720 }
721 } else if (scf_error() == SCF_ERROR_NOT_FOUND) {
722 if (scf_transaction_property_new(tx, ent, name,
723 scf_value_type(value)) < 0) {
724 idmapdlog(LOG_ERR,
725 "scf_transaction_property_new() failed: %s",
726 scf_strerror(scf_error()));
727 goto destruction;
728 }
729 } else {
730 idmapdlog(LOG_ERR,
731 "scf_pg_get_property(%s) failed: %s",
732 name, scf_strerror(scf_error()));
733 goto destruction;
734 }
735
736 if (scf_entry_add_value(ent, value) == -1) {
737 idmapdlog(LOG_ERR,
738 "scf_entry_add_value() failed: %s",
739 scf_strerror(scf_error()));
740 goto destruction;
741 }
742
743 ret = scf_transaction_commit(tx);
744 if (ret == 0) {
745 /*
746 * Property group set in scf_transaction_start()
747 * is not the most recent. Update pg, reset tx and
748 * retry tx.
749 */
750 idmapdlog(LOG_WARNING,
751 "scf_transaction_commit(%s) failed: %s",
752 name, scf_strerror(scf_error()));
753 scf_transaction_reset(tx);
754 continue;
755 }
756 if (ret != 1) {
757 idmapdlog(LOG_ERR,
758 "scf_transaction_commit(%s) failed: %s",
759 name, scf_strerror(scf_error()));
760 goto destruction;
761 }
762 /* Success! */
763 rc = 0;
764 break;
765 }
766
767 destruction:
768 scf_entry_destroy(ent);
769 scf_transaction_destroy(tx);
770 scf_property_destroy(prop);
771 return (rc);
772 }
773
774 static int
set_val_integer(idmap_cfg_handles_t * handles,scf_propertygroup_t * pg,const char * name,int64_t val)775 set_val_integer(
776 idmap_cfg_handles_t *handles,
777 scf_propertygroup_t *pg,
778 const char *name,
779 int64_t val)
780 {
781 scf_value_t *value = NULL;
782 int rc;
783
784 if ((value = scf_value_create(handles->main)) == NULL) {
785 idmapdlog(LOG_ERR, "Unable to set property %s",
786 name, scf_strerror(scf_error()));
787 return (-1);
788 }
789
790 scf_value_set_integer(value, val);
791
792 rc = set_val(handles, pg, name, value);
793
794 scf_value_destroy(value);
795
796 return (rc);
797 }
798
799
800 static int
set_val_astring(idmap_cfg_handles_t * handles,scf_propertygroup_t * pg,const char * name,const char * val)801 set_val_astring(
802 idmap_cfg_handles_t *handles,
803 scf_propertygroup_t *pg,
804 const char *name,
805 const char *val)
806 {
807 scf_value_t *value = NULL;
808 int rc = -1;
809
810 if ((value = scf_value_create(handles->main)) == NULL) {
811 idmapdlog(LOG_ERR, "Unable to set property %s",
812 name, scf_strerror(scf_error()));
813 goto out;
814 }
815
816 if (scf_value_set_astring(value, val) == -1) {
817 idmapdlog(LOG_ERR,
818 "scf_value_set_astring() failed: %s",
819 scf_strerror(scf_error()));
820 goto out;
821 }
822
823 rc = set_val(handles, pg, name, value);
824
825 out:
826 scf_value_destroy(value);
827 return (rc);
828 }
829
830
831
832 /*
833 * This function updates a boolean value.
834 * If nothing has changed it returns 0 else 1
835 */
836 static int
update_bool(boolean_t * value,boolean_t * new,char * name)837 update_bool(boolean_t *value, boolean_t *new, char *name)
838 {
839 if (*value == *new)
840 return (0);
841
842 if (DBG(CONFIG, 1)) {
843 idmapdlog(LOG_INFO, "change %s=%s", name,
844 *new ? "true" : "false");
845 }
846
847 *value = *new;
848 return (1);
849 }
850
851 /*
852 * This function updates a uint64_t value.
853 * If nothing has changed it returns 0 else 1
854 */
855 static int
update_uint64(uint64_t * value,uint64_t * new,char * name)856 update_uint64(uint64_t *value, uint64_t *new, char *name)
857 {
858 if (*value == *new)
859 return (0);
860
861 if (DBG(CONFIG, 1))
862 idmapdlog(LOG_INFO, "change %s=%llu", name, *new);
863
864 *value = *new;
865 return (1);
866 }
867
868 /*
869 * This function updates a string value.
870 * If nothing has changed it returns 0 else 1
871 */
872 static int
update_string(char ** value,char ** new,char * name)873 update_string(char **value, char **new, char *name)
874 {
875 int changed;
876
877 if (*new == NULL && *value != NULL)
878 changed = 1;
879 else if (*new != NULL && *value == NULL)
880 changed = 1;
881 else if (*new != NULL && *value != NULL && strcmp(*new, *value) != 0)
882 changed = 1;
883 else
884 changed = 0;
885
886 /*
887 * Note that even if unchanged we can't just return; we must free one
888 * of the values.
889 */
890
891 if (DBG(CONFIG, 1) && changed)
892 idmapdlog(LOG_INFO, "change %s=%s", name, CHECK_NULL(*new));
893
894 free(*value);
895 *value = *new;
896 *new = NULL;
897 return (changed);
898 }
899
900 static int
update_enum(int * value,int * new,char * name,struct enum_lookup_map * map)901 update_enum(int *value, int *new, char *name, struct enum_lookup_map *map)
902 {
903 if (*value == *new)
904 return (0);
905
906 if (DBG(CONFIG, 1)) {
907 idmapdlog(LOG_INFO, "change %s=%s", name,
908 enum_lookup(*new, map));
909 }
910
911 *value = *new;
912
913 return (1);
914 }
915
916 /*
917 * This function updates a directory service structure.
918 * If nothing has changed it returns 0 else 1
919 */
920 static int
update_dirs(ad_disc_ds_t ** value,ad_disc_ds_t ** new,char * name)921 update_dirs(ad_disc_ds_t **value, ad_disc_ds_t **new, char *name)
922 {
923
924 if (*value == *new)
925 /* Nothing to do */
926 return (0);
927
928 if (*value != NULL && *new != NULL &&
929 ad_disc_compare_ds(*value, *new) == 0) {
930 free(*new);
931 *new = NULL;
932 return (0);
933 }
934
935 if (*value != NULL)
936 free(*value);
937
938 *value = *new;
939 *new = NULL;
940
941 if (*value == NULL) {
942 /* We're unsetting this DS property */
943 if (DBG(CONFIG, 1))
944 idmapdlog(LOG_INFO, "change %s=<none>", name);
945 return (1);
946 }
947
948 if (DBG(CONFIG, 1)) {
949 /* List all the new DSs */
950 char buf[64];
951 ad_disc_ds_t *ds;
952 for (ds = *value; ds->host[0] != '\0'; ds++) {
953 if (ad_disc_getnameinfo(buf, sizeof (buf), &ds->addr))
954 (void) strlcpy(buf, "?", sizeof (buf));
955 idmapdlog(LOG_INFO, "change %s=%s addr=%s port=%d",
956 name, ds->host, buf, ds->port);
957 }
958 }
959 return (1);
960 }
961
962 /*
963 * This function updates a trusted domains structure.
964 * If nothing has changed it returns 0 else 1
965 */
966 static int
update_trusted_domains(ad_disc_trusteddomains_t ** value,ad_disc_trusteddomains_t ** new,char * name)967 update_trusted_domains(ad_disc_trusteddomains_t **value,
968 ad_disc_trusteddomains_t **new, char *name)
969 {
970 int i;
971
972 if (*value == *new)
973 /* Nothing to do */
974 return (0);
975
976 if (*value != NULL && *new != NULL &&
977 ad_disc_compare_trusteddomains(*value, *new) == 0) {
978 free(*new);
979 *new = NULL;
980 return (0);
981 }
982
983 if (*value != NULL)
984 free(*value);
985
986 *value = *new;
987 *new = NULL;
988
989 if (*value == NULL) {
990 /* We're unsetting this DS property */
991 if (DBG(CONFIG, 1))
992 idmapdlog(LOG_INFO, "change %s=<none>", name);
993 return (1);
994 }
995
996 if (DBG(CONFIG, 1)) {
997 /* List all the new domains */
998 for (i = 0; (*value)[i].domain[0] != '\0'; i++) {
999 idmapdlog(LOG_INFO, "change %s=%s direction=%s", name,
1000 (*value)[i].domain,
1001 enum_lookup((*value)[i].direction, trust_dir_map));
1002 }
1003 }
1004 return (1);
1005 }
1006
1007
1008 /*
1009 * This function updates a domains in a forest structure.
1010 * If nothing has changed it returns 0 else 1
1011 */
1012 static int
update_domains_in_forest(ad_disc_domainsinforest_t ** value,ad_disc_domainsinforest_t ** new,char * name)1013 update_domains_in_forest(ad_disc_domainsinforest_t **value,
1014 ad_disc_domainsinforest_t **new, char *name)
1015 {
1016 int i;
1017
1018 if (*value == *new)
1019 /* Nothing to do */
1020 return (0);
1021
1022 if (*value != NULL && *new != NULL &&
1023 ad_disc_compare_domainsinforest(*value, *new) == 0) {
1024 free(*new);
1025 *new = NULL;
1026 return (0);
1027 }
1028
1029 if (*value != NULL)
1030 free(*value);
1031
1032 *value = *new;
1033 *new = NULL;
1034
1035 if (*value == NULL) {
1036 /* We're unsetting this DS property */
1037 if (DBG(CONFIG, 1))
1038 idmapdlog(LOG_INFO, "change %s=<none>", name);
1039 return (1);
1040 }
1041
1042 if (DBG(CONFIG, 1)) {
1043 /* List all the new domains */
1044 for (i = 0; (*value)[i].domain[0] != '\0'; i++) {
1045 idmapdlog(LOG_INFO, "change %s=%s", name,
1046 (*value)[i].domain);
1047 }
1048 }
1049 return (1);
1050 }
1051
1052
1053 static void
free_trusted_forests(idmap_trustedforest_t ** value,int * num_values)1054 free_trusted_forests(idmap_trustedforest_t **value, int *num_values)
1055 {
1056 int i;
1057
1058 for (i = 0; i < *num_values; i++) {
1059 free((*value)[i].forest_name);
1060 free((*value)[i].global_catalog);
1061 free((*value)[i].domains_in_forest);
1062 }
1063 free(*value);
1064 *value = NULL;
1065 *num_values = 0;
1066 }
1067
1068
1069 static int
compare_trusteddomainsinforest(ad_disc_domainsinforest_t * df1,ad_disc_domainsinforest_t * df2)1070 compare_trusteddomainsinforest(ad_disc_domainsinforest_t *df1,
1071 ad_disc_domainsinforest_t *df2)
1072 {
1073 int i, j;
1074 int num_df1 = 0;
1075 int num_df2 = 0;
1076 boolean_t match;
1077
1078 for (i = 0; df1[i].domain[0] != '\0'; i++)
1079 if (df1[i].trusted)
1080 num_df1++;
1081
1082 for (j = 0; df2[j].domain[0] != '\0'; j++)
1083 if (df2[j].trusted)
1084 num_df2++;
1085
1086 if (num_df1 != num_df2)
1087 return (1);
1088
1089 for (i = 0; df1[i].domain[0] != '\0'; i++) {
1090 if (df1[i].trusted) {
1091 match = B_FALSE;
1092 for (j = 0; df2[j].domain[0] != '\0'; j++) {
1093 if (df2[j].trusted &&
1094 domain_eq(df1[i].domain, df2[j].domain) &&
1095 strcmp(df1[i].sid, df2[j].sid) == 0) {
1096 match = B_TRUE;
1097 break;
1098 }
1099 }
1100 if (!match)
1101 return (1);
1102 }
1103 }
1104 return (0);
1105 }
1106
1107
1108
1109 /*
1110 * This function updates trusted forest structure.
1111 * If nothing has changed it returns 0 else 1
1112 */
1113 static int
update_trusted_forest(idmap_trustedforest_t ** value,int * num_value,idmap_trustedforest_t ** new,int * num_new,char * name)1114 update_trusted_forest(idmap_trustedforest_t **value, int *num_value,
1115 idmap_trustedforest_t **new, int *num_new, char *name)
1116 {
1117 int i, j;
1118 boolean_t match;
1119
1120 if (*value == *new)
1121 /* Nothing to do */
1122 return (0);
1123
1124 if (*value != NULL && *new != NULL) {
1125 if (*num_value != *num_new)
1126 goto not_equal;
1127 for (i = 0; i < *num_value; i++) {
1128 match = B_FALSE;
1129 for (j = 0; j < *num_new; j++) {
1130 if (strcmp((*value)[i].forest_name,
1131 (*new)[j].forest_name) == 0 &&
1132 ad_disc_compare_ds(
1133 (*value)[i].global_catalog,
1134 (*new)[j].global_catalog) == 0 &&
1135 compare_trusteddomainsinforest(
1136 (*value)[i].domains_in_forest,
1137 (*new)[j].domains_in_forest) == 0) {
1138 match = B_TRUE;
1139 break;
1140 }
1141 }
1142 if (!match)
1143 goto not_equal;
1144 }
1145 free_trusted_forests(new, num_new);
1146 return (0);
1147 }
1148 not_equal:
1149 if (*value != NULL)
1150 free_trusted_forests(value, num_value);
1151 *value = *new;
1152 *num_value = *num_new;
1153 *new = NULL;
1154 *num_new = 0;
1155
1156 if (*value == NULL) {
1157 /* We're unsetting this DS property */
1158 if (DBG(CONFIG, 1))
1159 idmapdlog(LOG_INFO, "change %s=<none>", name);
1160 return (1);
1161 }
1162
1163 if (DBG(CONFIG, 1)) {
1164 /* List all the trusted forests */
1165 for (i = 0; i < *num_value; i++) {
1166 idmap_trustedforest_t *f = &(*value)[i];
1167 for (j = 0;
1168 f->domains_in_forest[j].domain[0] != '\0';
1169 j++) {
1170 /* List trusted Domains in the forest. */
1171 if (f->domains_in_forest[j].trusted)
1172 idmapdlog(LOG_INFO,
1173 "change %s=%s domain=%s",
1174 name, f->forest_name,
1175 f->domains_in_forest[j].domain);
1176 }
1177 /* List the hosts */
1178 for (j = 0;
1179 f->global_catalog[j].host[0] != '\0';
1180 j++) {
1181 idmapdlog(LOG_INFO,
1182 "change %s=%s host=%s port=%d",
1183 name, f->forest_name,
1184 f->global_catalog[j].host,
1185 f->global_catalog[j].port);
1186 }
1187 }
1188 }
1189 return (1);
1190 }
1191
1192 const char *
enum_lookup(int value,struct enum_lookup_map * map)1193 enum_lookup(int value, struct enum_lookup_map *map)
1194 {
1195 for (; map->string != NULL; map++) {
1196 if (value == map->value) {
1197 return (map->string);
1198 }
1199 }
1200 return ("(invalid)");
1201 }
1202
1203 /*
1204 * Returns 1 if the PF_ROUTE socket event indicates that we should rescan the
1205 * interfaces.
1206 *
1207 * Shamelessly based on smb_nics_changed() and other PF_ROUTE uses in ON.
1208 */
1209 static
1210 boolean_t
pfroute_event_is_interesting(int rt_sock)1211 pfroute_event_is_interesting(int rt_sock)
1212 {
1213 int nbytes;
1214 int64_t msg[2048 / 8];
1215 struct rt_msghdr *rtm;
1216 boolean_t is_interesting = B_FALSE;
1217
1218 for (;;) {
1219 if ((nbytes = read(rt_sock, msg, sizeof (msg))) <= 0)
1220 break;
1221 rtm = (struct rt_msghdr *)msg;
1222 if (rtm->rtm_version != RTM_VERSION)
1223 continue;
1224 if (nbytes < rtm->rtm_msglen)
1225 continue;
1226 switch (rtm->rtm_type) {
1227 case RTM_NEWADDR:
1228 case RTM_DELADDR:
1229 case RTM_IFINFO:
1230 is_interesting = B_TRUE;
1231 break;
1232 default:
1233 break;
1234 }
1235 }
1236 return (is_interesting);
1237 }
1238
1239 /*
1240 * Wait for an event, and report what kind of event occurred.
1241 *
1242 * Note that there are cases where we are awoken but don't care about
1243 * the lower-level event. We can't just loop here because we can't
1244 * readily calculate how long to sleep the next time. We return
1245 * EVENT_NOTHING and let the caller loop.
1246 */
1247 static
1248 enum event_type
wait_for_event(struct timespec * timeoutp)1249 wait_for_event(struct timespec *timeoutp)
1250 {
1251 port_event_t pe;
1252
1253 (void) memset(&pe, 0, sizeof (pe));
1254 if (port_get(idmapd_ev_port, &pe, timeoutp) != 0) {
1255 switch (errno) {
1256 case EINTR:
1257 return (EVENT_NOTHING);
1258 case ETIME:
1259 /* Timeout */
1260 return (EVENT_TIMEOUT);
1261 default:
1262 /* EBADF, EBADFD, EFAULT, EINVAL (end of time?)? */
1263 idmapdlog(LOG_ERR, "Event port failed: %s",
1264 strerror(errno));
1265 exit(1);
1266 /* NOTREACHED */
1267 }
1268 }
1269
1270
1271 switch (pe.portev_source) {
1272 case 0:
1273 /*
1274 * This isn't documented, but seems to be what you get if
1275 * the timeout is zero seconds and there are no events
1276 * pending.
1277 */
1278 return (EVENT_TIMEOUT);
1279
1280 case PORT_SOURCE_USER:
1281 switch (pe.portev_events) {
1282 case RECONFIGURE:
1283 return (EVENT_REFRESH);
1284 case POKE_AUTO_DISCOVERY:
1285 return (EVENT_POKED);
1286 case KICK_AUTO_DISCOVERY:
1287 return (EVENT_KICKED);
1288 }
1289 return (EVENT_NOTHING);
1290
1291 case PORT_SOURCE_FD:
1292 if (pe.portev_object == rt_sock) {
1293 /*
1294 * PF_ROUTE socket read event:
1295 * re-associate fd
1296 * handle event
1297 */
1298 if (port_associate(idmapd_ev_port, PORT_SOURCE_FD,
1299 rt_sock, POLLIN, NULL) != 0) {
1300 idmapdlog(LOG_ERR, "Failed to re-associate the "
1301 "routing socket with the event port: %s",
1302 strerror(errno));
1303 abort();
1304 }
1305 /*
1306 * The network configuration may still be in flux.
1307 * No matter, the resolver will re-transmit and
1308 * timeout if need be.
1309 */
1310 if (pfroute_event_is_interesting(rt_sock)) {
1311 if (DBG(CONFIG, 1)) {
1312 idmapdlog(LOG_DEBUG,
1313 "Interesting routing event");
1314 }
1315 return (EVENT_ROUTING);
1316 } else {
1317 if (DBG(CONFIG, 2)) {
1318 idmapdlog(LOG_DEBUG,
1319 "Boring routing event");
1320 }
1321 return (EVENT_NOTHING);
1322 }
1323 }
1324 /* Event on an FD other than the routing FD? Ignore it. */
1325 break;
1326 }
1327
1328 return (EVENT_NOTHING);
1329 }
1330
1331 void *
idmap_cfg_update_thread(void * arg)1332 idmap_cfg_update_thread(void *arg)
1333 {
1334 NOTE(ARGUNUSED(arg))
1335 idmap_pg_config_t *pgcfg = &_idmapdstate.cfg->pgcfg;
1336 const ad_disc_t ad_ctx = _idmapdstate.cfg->handles.ad_ctx;
1337 int flags = CFG_DISCOVER;
1338
1339 for (;;) {
1340 struct timespec timeout;
1341 struct timespec *timeoutp;
1342 int rc;
1343 int ttl, max_ttl;
1344
1345 (void) ad_disc_SubnetChanged(ad_ctx);
1346
1347 rc = idmap_cfg_load(_idmapdstate.cfg, flags);
1348 if (rc < -1) {
1349 idmapdlog(LOG_ERR, "Fatal errors while reading "
1350 "SMF properties");
1351 exit(1);
1352 } else if (rc == -1) {
1353 idmapdlog(LOG_WARNING,
1354 "Errors re-loading configuration may cause AD "
1355 "lookups to fail");
1356 }
1357
1358 /*
1359 * Wait for an interesting event. Note that we might get
1360 * boring events between interesting events. If so, we loop.
1361 */
1362 flags = CFG_DISCOVER;
1363 for (;;) {
1364 /*
1365 * If we don't know our domain name, don't bother
1366 * with rediscovery until the next config change.
1367 * Avoids hourly noise in workgroup mode.
1368 */
1369 if (pgcfg->domain_name == NULL)
1370 ttl = -1;
1371 else
1372 ttl = ad_disc_get_TTL(ad_ctx);
1373 if (ttl < 0) {
1374 timeoutp = NULL;
1375 } else {
1376 max_ttl = (int)pgcfg->rediscovery_interval;
1377 if (ttl > max_ttl)
1378 ttl = max_ttl;
1379 if (ttl < MIN_REDISCOVERY_INTERVAL)
1380 ttl = MIN_REDISCOVERY_INTERVAL;
1381 timeout.tv_sec = ttl;
1382 timeout.tv_nsec = 0;
1383 timeoutp = &timeout;
1384 }
1385
1386 if (DBG(CONFIG, 1))
1387 idmapdlog(LOG_DEBUG,
1388 "_cfg_update_thread waiting");
1389
1390 switch (wait_for_event(timeoutp)) {
1391 case EVENT_NOTHING:
1392 if (DBG(CONFIG, 2))
1393 idmapdlog(LOG_DEBUG, "Boring event.");
1394 continue;
1395 case EVENT_REFRESH:
1396 if (DBG(CONFIG, 1))
1397 idmapdlog(LOG_INFO, "SMF refresh");
1398 /*
1399 * Forget any DC we had previously.
1400 */
1401 flags |= CFG_FORGET_DC;
1402
1403 /*
1404 * Blow away the ccache, we might have
1405 * re-joined the domain or joined a new one
1406 */
1407 (void) unlink(IDMAP_CACHEDIR "/ccache");
1408 break;
1409 case EVENT_POKED:
1410 if (DBG(CONFIG, 1))
1411 idmapdlog(LOG_DEBUG, "poked");
1412 break;
1413 case EVENT_KICKED:
1414 if (DBG(CONFIG, 1))
1415 idmapdlog(LOG_DEBUG, "kicked");
1416 flags |= CFG_FORGET_DC;
1417 break;
1418 case EVENT_TIMEOUT:
1419 if (DBG(CONFIG, 1))
1420 idmapdlog(LOG_DEBUG, "TTL expired");
1421 break;
1422 case EVENT_ROUTING:
1423 /* Already logged to DEBUG */
1424 break;
1425 }
1426 /* An interesting event! */
1427 break;
1428 }
1429 }
1430 /*
1431 * Lint isn't happy with the concept of a function declared to
1432 * return something, that doesn't return. Of course, merely adding
1433 * the return isn't enough, because it's never reached...
1434 */
1435 /*NOTREACHED*/
1436 return (NULL);
1437 }
1438
1439 int
idmap_cfg_start_updates(void)1440 idmap_cfg_start_updates(void)
1441 {
1442 if ((idmapd_ev_port = port_create()) < 0) {
1443 idmapdlog(LOG_ERR, "Failed to create event port: %s",
1444 strerror(errno));
1445 return (-1);
1446 }
1447
1448 if ((rt_sock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) {
1449 idmapdlog(LOG_ERR, "Failed to open routing socket: %s",
1450 strerror(errno));
1451 (void) close(idmapd_ev_port);
1452 return (-1);
1453 }
1454
1455 if (fcntl(rt_sock, F_SETFL, O_NDELAY|O_NONBLOCK) < 0) {
1456 idmapdlog(LOG_ERR, "Failed to set routing socket flags: %s",
1457 strerror(errno));
1458 (void) close(rt_sock);
1459 (void) close(idmapd_ev_port);
1460 return (-1);
1461 }
1462
1463 if (port_associate(idmapd_ev_port, PORT_SOURCE_FD,
1464 rt_sock, POLLIN, NULL) != 0) {
1465 idmapdlog(LOG_ERR, "Failed to associate the routing "
1466 "socket with the event port: %s", strerror(errno));
1467 (void) close(rt_sock);
1468 (void) close(idmapd_ev_port);
1469 return (-1);
1470 }
1471
1472 if ((errno = pthread_create(&update_thread_handle, NULL,
1473 idmap_cfg_update_thread, NULL)) != 0) {
1474 idmapdlog(LOG_ERR, "Failed to start update thread: %s",
1475 strerror(errno));
1476 (void) port_dissociate(idmapd_ev_port, PORT_SOURCE_FD, rt_sock);
1477 (void) close(rt_sock);
1478 (void) close(idmapd_ev_port);
1479 return (-1);
1480 }
1481
1482 return (0);
1483 }
1484
1485 /*
1486 * Reject attribute names with invalid characters.
1487 */
1488 static
1489 int
valid_ldap_attr(const char * attr)1490 valid_ldap_attr(const char *attr)
1491 {
1492 for (; *attr; attr++) {
1493 if (!isalnum(*attr) && *attr != '-' &&
1494 *attr != '_' && *attr != '.' && *attr != ';')
1495 return (0);
1496 }
1497 return (1);
1498 }
1499
1500 static
1501 void
idmapd_set_debug(idmap_cfg_handles_t * handles,enum idmapd_debug item,const char * name)1502 idmapd_set_debug(
1503 idmap_cfg_handles_t *handles,
1504 enum idmapd_debug item,
1505 const char *name)
1506 {
1507 int val;
1508
1509 if (item < 0 || item > IDMAPD_DEBUG_MAX)
1510 return;
1511
1512 val = get_debug(handles, name);
1513
1514 if (val != _idmapdstate.debug[item])
1515 idmapdlog(LOG_DEBUG, "%s/%s = %d", DEBUG_PG, name, val);
1516
1517 _idmapdstate.debug[item] = val;
1518 }
1519
1520 static
1521 void
check_smf_debug_mode(idmap_cfg_handles_t * handles)1522 check_smf_debug_mode(idmap_cfg_handles_t *handles)
1523 {
1524 idmapd_set_debug(handles, IDMAPD_DEBUG_ALL, "all");
1525 idmapd_set_debug(handles, IDMAPD_DEBUG_CONFIG, "config");
1526 idmapd_set_debug(handles, IDMAPD_DEBUG_MAPPING, "mapping");
1527 idmapd_set_debug(handles, IDMAPD_DEBUG_DISC, "discovery");
1528 idmapd_set_debug(handles, IDMAPD_DEBUG_DNS, "dns");
1529 idmapd_set_debug(handles, IDMAPD_DEBUG_LDAP, "ldap");
1530
1531 adutils_set_debug(AD_DEBUG_ALL, _idmapdstate.debug[IDMAPD_DEBUG_ALL]);
1532 adutils_set_debug(AD_DEBUG_DISC, _idmapdstate.debug[IDMAPD_DEBUG_DISC]);
1533 adutils_set_debug(AD_DEBUG_DNS, _idmapdstate.debug[IDMAPD_DEBUG_DNS]);
1534 adutils_set_debug(AD_DEBUG_LDAP, _idmapdstate.debug[IDMAPD_DEBUG_LDAP]);
1535 }
1536
1537 /*
1538 * This is the half of idmap_cfg_load() that loads property values from
1539 * SMF (using the config/ property group of the idmap FMRI).
1540 *
1541 * Return values: 0 -> success, -1 -> failure, -2 -> hard failures
1542 * -3 -> hard smf config failures
1543 * reading from SMF.
1544 */
1545 static int
idmap_cfg_load_smf(idmap_cfg_handles_t * handles,idmap_pg_config_t * pgcfg,int * const errors)1546 idmap_cfg_load_smf(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg,
1547 int * const errors)
1548 {
1549 int rc;
1550 char *s;
1551
1552 *errors = 0;
1553
1554 if (scf_pg_update(handles->config_pg) < 0) {
1555 idmapdlog(LOG_ERR, "scf_pg_update() failed: %s",
1556 scf_strerror(scf_error()));
1557 return (-2);
1558 }
1559
1560 if (scf_pg_update(handles->debug_pg) < 0) {
1561 idmapdlog(LOG_ERR, "scf_pg_update() failed: %s",
1562 scf_strerror(scf_error()));
1563 return (-2);
1564 }
1565
1566 check_smf_debug_mode(handles);
1567
1568 rc = get_val_bool(handles, "unresolvable_sid_mapping",
1569 &pgcfg->eph_map_unres_sids, B_TRUE);
1570 if (rc != 0)
1571 (*errors)++;
1572
1573 rc = get_val_bool(handles, "use_ads",
1574 &pgcfg->use_ads, B_TRUE);
1575 if (rc != 0)
1576 (*errors)++;
1577
1578 rc = get_val_bool(handles, "use_lsa",
1579 &pgcfg->use_lsa, B_TRUE);
1580 if (rc != 0)
1581 (*errors)++;
1582
1583 rc = get_val_bool(handles, "disable_cross_forest_trusts",
1584 &pgcfg->disable_cross_forest_trusts, B_TRUE);
1585 if (rc != 0)
1586 (*errors)++;
1587
1588 rc = get_val_astring(handles, "directory_based_mapping", &s);
1589 if (rc != 0)
1590 (*errors)++;
1591 else if (s == NULL || strcasecmp(s, "none") == 0)
1592 pgcfg->directory_based_mapping = DIRECTORY_MAPPING_NONE;
1593 else if (strcasecmp(s, "name") == 0)
1594 pgcfg->directory_based_mapping = DIRECTORY_MAPPING_NAME;
1595 else if (strcasecmp(s, "idmu") == 0)
1596 pgcfg->directory_based_mapping = DIRECTORY_MAPPING_IDMU;
1597 else {
1598 pgcfg->directory_based_mapping = DIRECTORY_MAPPING_NONE;
1599 idmapdlog(LOG_ERR,
1600 "config/directory_based_mapping: invalid value \"%s\" ignored",
1601 s);
1602 (*errors)++;
1603 }
1604 free(s);
1605
1606 rc = get_val_int(handles, "list_size_limit",
1607 &pgcfg->list_size_limit, SCF_TYPE_COUNT);
1608 if (rc != 0)
1609 (*errors)++;
1610
1611 rc = get_val_int(handles, "max_threads",
1612 &pgcfg->max_threads, SCF_TYPE_COUNT);
1613 if (rc != 0)
1614 (*errors)++;
1615 if (pgcfg->max_threads == 0)
1616 pgcfg->max_threads = MAX_THREADS_DEFAULT;
1617 if (pgcfg->max_threads > UINT_MAX)
1618 pgcfg->max_threads = UINT_MAX;
1619
1620 rc = get_val_int(handles, "id_cache_timeout",
1621 &pgcfg->id_cache_timeout, SCF_TYPE_COUNT);
1622 if (rc != 0)
1623 (*errors)++;
1624 if (pgcfg->id_cache_timeout == 0)
1625 pgcfg->id_cache_timeout = ID_CACHE_TMO_DEFAULT;
1626
1627 rc = get_val_int(handles, "name_cache_timeout",
1628 &pgcfg->name_cache_timeout, SCF_TYPE_COUNT);
1629 if (rc != 0)
1630 (*errors)++;
1631 if (pgcfg->name_cache_timeout == 0)
1632 pgcfg->name_cache_timeout = NAME_CACHE_TMO_DEFAULT;
1633
1634 rc = get_val_int(handles, "rediscovery_interval",
1635 &pgcfg->rediscovery_interval, SCF_TYPE_COUNT);
1636 if (rc != 0)
1637 (*errors)++;
1638 if (pgcfg->rediscovery_interval == 0)
1639 pgcfg->rediscovery_interval = REDISCOVERY_INTERVAL_DEFAULT;
1640
1641 rc = get_val_astring(handles, "domain_name",
1642 &pgcfg->domain_name);
1643 if (rc != 0)
1644 (*errors)++;
1645 else {
1646 if (pgcfg->domain_name != NULL &&
1647 pgcfg->domain_name[0] == '\0') {
1648 free(pgcfg->domain_name);
1649 pgcfg->domain_name = NULL;
1650 }
1651 if (pgcfg->domain_name != NULL)
1652 pgcfg->domain_name_auto_disc = B_FALSE;
1653 (void) ad_disc_set_DomainName(handles->ad_ctx,
1654 pgcfg->domain_name);
1655 }
1656
1657 rc = get_val_astring(handles, "default_domain",
1658 &pgcfg->default_domain);
1659 if (rc != 0) {
1660 /*
1661 * SCF failures fetching config/default_domain we treat
1662 * as fatal as they may leave ID mapping rules that
1663 * match unqualified winnames flapping in the wind.
1664 */
1665 return (-2);
1666 }
1667
1668 if (pgcfg->default_domain == NULL && pgcfg->domain_name != NULL) {
1669 pgcfg->default_domain = strdup(pgcfg->domain_name);
1670 }
1671
1672 rc = get_val_astring(handles, "domain_guid", &s);
1673 if (rc != 0) {
1674 (*errors)++;
1675 } else if (s == NULL || s[0] == '\0') {
1676 /* OK, not set. */
1677 free(s);
1678 } else {
1679 uuid_t u;
1680
1681 if (uuid_parse(s, u) != 0) {
1682 idmapdlog(LOG_ERR,
1683 "config/domain_guid: invalid value \"%s\" ignored", s);
1684 free(s);
1685 (*errors)++;
1686 } else {
1687 pgcfg->domain_guid = s;
1688 pgcfg->domain_guid_auto_disc = B_FALSE;
1689 (void) ad_disc_set_DomainGUID(handles->ad_ctx, u);
1690 }
1691 }
1692
1693 rc = get_val_astring(handles, "machine_uuid", &pgcfg->machine_uuid);
1694 if (rc != 0)
1695 (*errors)++;
1696 if (pgcfg->machine_uuid == NULL) {
1697 /* If machine_uuid not configured, generate one */
1698 if (generate_machine_uuid(&pgcfg->machine_uuid) < 0)
1699 return (-2);
1700 rc = set_val_astring(handles, handles->config_pg,
1701 "machine_uuid", pgcfg->machine_uuid);
1702 if (rc != 0)
1703 (*errors)++;
1704 }
1705
1706 rc = get_val_astring(handles, "machine_sid", &pgcfg->machine_sid);
1707 if (rc != 0)
1708 (*errors)++;
1709 if (pgcfg->machine_sid == NULL) {
1710 /*
1711 * If machine_sid not configured, generate one
1712 * from the machine UUID.
1713 */
1714 if (generate_machine_sid(&pgcfg->machine_sid,
1715 pgcfg->machine_uuid) < 0)
1716 return (-2);
1717 rc = set_val_astring(handles, handles->config_pg,
1718 "machine_sid", pgcfg->machine_sid);
1719 if (rc != 0)
1720 (*errors)++;
1721 }
1722
1723 rc = get_val_ds(handles, "domain_controller", 389,
1724 &pgcfg->domain_controller);
1725 if (rc != 0)
1726 (*errors)++;
1727 else {
1728 (void) ad_disc_set_DomainController(handles->ad_ctx,
1729 pgcfg->domain_controller);
1730 pgcfg->domain_controller_auto_disc = B_FALSE;
1731 }
1732
1733 rc = get_val_ds(handles, "preferred_dc", 389,
1734 &pgcfg->preferred_dc);
1735 if (rc != 0)
1736 (*errors)++;
1737 else {
1738 (void) ad_disc_set_PreferredDC(handles->ad_ctx,
1739 pgcfg->preferred_dc);
1740 pgcfg->preferred_dc_auto_disc = B_FALSE;
1741 }
1742
1743 rc = get_val_astring(handles, "forest_name", &pgcfg->forest_name);
1744 if (rc != 0)
1745 (*errors)++;
1746 else {
1747 if (pgcfg->forest_name != NULL &&
1748 pgcfg->forest_name[0] == '\0') {
1749 free(pgcfg->forest_name);
1750 pgcfg->forest_name = NULL;
1751 }
1752 if (pgcfg->forest_name != NULL)
1753 pgcfg->forest_name_auto_disc = B_FALSE;
1754 (void) ad_disc_set_ForestName(handles->ad_ctx,
1755 pgcfg->forest_name);
1756 }
1757
1758 rc = get_val_astring(handles, "site_name", &pgcfg->site_name);
1759 if (rc != 0)
1760 (*errors)++;
1761 else {
1762 if (pgcfg->site_name != NULL &&
1763 pgcfg->site_name[0] == '\0') {
1764 free(pgcfg->site_name);
1765 pgcfg->site_name = NULL;
1766 }
1767 if (pgcfg->site_name != NULL)
1768 pgcfg->site_name_auto_disc = B_FALSE;
1769 (void) ad_disc_set_SiteName(handles->ad_ctx, pgcfg->site_name);
1770 }
1771
1772 rc = get_val_ds(handles, "global_catalog", 3268,
1773 &pgcfg->global_catalog);
1774 if (rc != 0)
1775 (*errors)++;
1776 else {
1777 (void) ad_disc_set_GlobalCatalog(handles->ad_ctx,
1778 pgcfg->global_catalog);
1779 pgcfg->global_catalog_auto_disc = B_FALSE;
1780 }
1781
1782 /* Unless we're doing directory-based name mapping, we're done. */
1783 if (pgcfg->directory_based_mapping != DIRECTORY_MAPPING_NAME)
1784 return (0);
1785
1786 rc = get_val_astring(handles, "ad_unixuser_attr",
1787 &pgcfg->ad_unixuser_attr);
1788 if (rc != 0)
1789 return (-2);
1790 if (pgcfg->ad_unixuser_attr != NULL &&
1791 !valid_ldap_attr(pgcfg->ad_unixuser_attr)) {
1792 idmapdlog(LOG_ERR, "config/ad_unixuser_attr=%s is not a "
1793 "valid LDAP attribute name", pgcfg->ad_unixuser_attr);
1794 return (-3);
1795 }
1796
1797 rc = get_val_astring(handles, "ad_unixgroup_attr",
1798 &pgcfg->ad_unixgroup_attr);
1799 if (rc != 0)
1800 return (-2);
1801 if (pgcfg->ad_unixgroup_attr != NULL &&
1802 !valid_ldap_attr(pgcfg->ad_unixgroup_attr)) {
1803 idmapdlog(LOG_ERR, "config/ad_unixgroup_attr=%s is not a "
1804 "valid LDAP attribute name", pgcfg->ad_unixgroup_attr);
1805 return (-3);
1806 }
1807
1808 rc = get_val_astring(handles, "nldap_winname_attr",
1809 &pgcfg->nldap_winname_attr);
1810 if (rc != 0)
1811 return (-2);
1812 if (pgcfg->nldap_winname_attr != NULL &&
1813 !valid_ldap_attr(pgcfg->nldap_winname_attr)) {
1814 idmapdlog(LOG_ERR, "config/nldap_winname_attr=%s is not a "
1815 "valid LDAP attribute name", pgcfg->nldap_winname_attr);
1816 return (-3);
1817 }
1818 if (pgcfg->ad_unixuser_attr == NULL &&
1819 pgcfg->ad_unixgroup_attr == NULL &&
1820 pgcfg->nldap_winname_attr == NULL) {
1821 idmapdlog(LOG_ERR,
1822 "If config/directory_based_mapping property is set to "
1823 "\"name\" then at least one of the following name mapping "
1824 "attributes must be specified. (config/ad_unixuser_attr OR "
1825 "config/ad_unixgroup_attr OR config/nldap_winname_attr)");
1826 return (-3);
1827 }
1828
1829 return (rc);
1830 }
1831
1832 static
1833 void
log_if_unable(const void * val,const char * what)1834 log_if_unable(const void *val, const char *what)
1835 {
1836 if (val == NULL) {
1837 idmapdlog(LOG_DEBUG, "unable to discover %s", what);
1838 }
1839 }
1840
1841 static
1842 void
discover_trusted_domains(idmap_pg_config_t * pgcfg,ad_disc_t ad_ctx)1843 discover_trusted_domains(idmap_pg_config_t *pgcfg, ad_disc_t ad_ctx)
1844 {
1845 ad_disc_t trusted_ctx;
1846 int i, j, k, l;
1847 char *forestname;
1848 int num_trusteddomains;
1849 boolean_t new_forest;
1850 char *trusteddomain;
1851 ad_disc_ds_t *globalcatalog;
1852 idmap_trustedforest_t *trustedforests;
1853 ad_disc_domainsinforest_t *domainsinforest;
1854
1855 pgcfg->trusted_domains =
1856 ad_disc_get_TrustedDomains(ad_ctx, NULL);
1857
1858 if (pgcfg->forest_name != NULL && pgcfg->trusted_domains != NULL &&
1859 pgcfg->trusted_domains[0].domain[0] != '\0') {
1860 /*
1861 * We have trusted domains. We need to go through every
1862 * one and find its forest. If it is a new forest we then need
1863 * to find its Global Catalog and the domains in the forest
1864 */
1865 for (i = 0; pgcfg->trusted_domains[i].domain[0] != '\0'; i++)
1866 continue;
1867 num_trusteddomains = i;
1868
1869 trustedforests = calloc(num_trusteddomains,
1870 sizeof (idmap_trustedforest_t));
1871 j = 0;
1872 for (i = 0; pgcfg->trusted_domains[i].domain[0] != '\0'; i++) {
1873 trusteddomain = pgcfg->trusted_domains[i].domain;
1874 trusted_ctx = ad_disc_init();
1875 (void) ad_disc_set_DomainName(trusted_ctx,
1876 trusteddomain);
1877 forestname =
1878 ad_disc_get_ForestName(trusted_ctx, NULL);
1879 if (forestname == NULL) {
1880 if (DBG(CONFIG, 1)) {
1881 idmapdlog(LOG_DEBUG,
1882 "unable to discover Forest Name"
1883 " for the trusted domain %s",
1884 trusteddomain);
1885 }
1886 ad_disc_fini(trusted_ctx);
1887 continue;
1888 }
1889
1890 if (strcasecmp(forestname, pgcfg->forest_name) == 0) {
1891 /*
1892 * Ignore the domain as it is part of
1893 * the primary forest
1894 */
1895 free(forestname);
1896 ad_disc_fini(trusted_ctx);
1897 continue;
1898 }
1899
1900 /* Is this a new forest? */
1901 new_forest = B_TRUE;
1902 for (k = 0; k < j; k++) {
1903 if (strcasecmp(forestname,
1904 trustedforests[k].forest_name) == 0) {
1905 new_forest = B_FALSE;
1906 domainsinforest =
1907 trustedforests[k].domains_in_forest;
1908 break;
1909 }
1910 }
1911 if (!new_forest) {
1912 /* Mark the domain as trusted */
1913 for (l = 0;
1914 domainsinforest[l].domain[0] != '\0'; l++) {
1915 if (domain_eq(trusteddomain,
1916 domainsinforest[l].domain)) {
1917 domainsinforest[l].trusted =
1918 TRUE;
1919 break;
1920 }
1921 }
1922 free(forestname);
1923 ad_disc_fini(trusted_ctx);
1924 continue;
1925 }
1926
1927 /*
1928 * Get the Global Catalog and the domains in
1929 * this new forest.
1930 */
1931 globalcatalog =
1932 ad_disc_get_GlobalCatalog(trusted_ctx,
1933 AD_DISC_PREFER_SITE, NULL);
1934 if (globalcatalog == NULL) {
1935 if (DBG(CONFIG, 1)) {
1936 idmapdlog(LOG_DEBUG,
1937 "unable to discover Global Catalog"
1938 " for the trusted domain %s",
1939 trusteddomain);
1940 }
1941 free(forestname);
1942 ad_disc_fini(trusted_ctx);
1943 continue;
1944 }
1945 domainsinforest =
1946 ad_disc_get_DomainsInForest(trusted_ctx, NULL);
1947 if (domainsinforest == NULL) {
1948 if (DBG(CONFIG, 1)) {
1949 idmapdlog(LOG_DEBUG,
1950 "unable to discover Domains in the"
1951 " Forest for the trusted domain %s",
1952 trusteddomain);
1953 }
1954 free(globalcatalog);
1955 free(forestname);
1956 ad_disc_fini(trusted_ctx);
1957 continue;
1958 }
1959
1960 trustedforests[j].forest_name = forestname;
1961 trustedforests[j].global_catalog = globalcatalog;
1962 trustedforests[j].domains_in_forest = domainsinforest;
1963 j++;
1964 /* Mark the domain as trusted */
1965 for (l = 0; domainsinforest[l].domain[0] != '\0';
1966 l++) {
1967 if (domain_eq(trusteddomain,
1968 domainsinforest[l].domain)) {
1969 domainsinforest[l].trusted = TRUE;
1970 break;
1971 }
1972 }
1973 ad_disc_fini(trusted_ctx);
1974 }
1975 if (j > 0) {
1976 pgcfg->num_trusted_forests = j;
1977 pgcfg->trusted_forests = trustedforests;
1978 } else {
1979 free(trustedforests);
1980 }
1981 }
1982 }
1983
1984 /*
1985 * This is the half of idmap_cfg_load() that auto-discovers values of
1986 * discoverable properties that weren't already set via SMF properties.
1987 *
1988 * idmap_cfg_discover() is called *after* idmap_cfg_load_smf(), so it
1989 * needs to be careful not to overwrite any properties set in SMF.
1990 */
1991 static void
idmap_cfg_discover1(idmap_cfg_handles_t * handles,idmap_pg_config_t * pgcfg)1992 idmap_cfg_discover1(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg)
1993 {
1994 ad_disc_t ad_ctx = handles->ad_ctx;
1995 FILE *status_fp = NULL;
1996 time_t t0, t1;
1997
1998 t0 = time(NULL);
1999 if (DBG(CONFIG, 1))
2000 idmapdlog(LOG_DEBUG, "Running domain discovery.");
2001
2002 (void) unlink(IDMAP_CACHEDIR "/discovery.log");
2003 status_fp = fopen(IDMAP_CACHEDIR "/discovery.log", "w");
2004 if (status_fp) {
2005 (void) fchmod(fileno(status_fp), 0644);
2006 ad_disc_set_StatusFP(ad_ctx, status_fp);
2007 }
2008
2009 if (pgcfg->domain_name == NULL) {
2010 idmapdlog(LOG_DEBUG, "No domain name specified.");
2011 if (status_fp)
2012 (void) fprintf(status_fp, "(no domain name)\n");
2013 goto out;
2014 }
2015
2016 if (pgcfg->domain_controller == NULL)
2017 pgcfg->domain_controller =
2018 ad_disc_get_DomainController(ad_ctx,
2019 AD_DISC_PREFER_SITE,
2020 &pgcfg->domain_controller_auto_disc);
2021
2022 if (pgcfg->domain_guid == NULL) {
2023 char buf[UUID_PRINTABLE_STRING_LENGTH];
2024 uchar_t *u = ad_disc_get_DomainGUID(ad_ctx,
2025 &pgcfg->domain_guid_auto_disc);
2026 (void) memset(buf, 0, sizeof (buf));
2027 if (u != NULL) {
2028 uuid_unparse(u, buf);
2029 pgcfg->domain_guid = strdup(buf);
2030 }
2031 }
2032
2033 if (pgcfg->forest_name == NULL)
2034 pgcfg->forest_name = ad_disc_get_ForestName(ad_ctx,
2035 &pgcfg->forest_name_auto_disc);
2036
2037 if (pgcfg->site_name == NULL)
2038 pgcfg->site_name = ad_disc_get_SiteName(ad_ctx,
2039 &pgcfg->site_name_auto_disc);
2040
2041 if (DBG(CONFIG, 1)) {
2042 log_if_unable(pgcfg->domain_name, "Domain Name");
2043 log_if_unable(pgcfg->domain_controller,
2044 "Domain Controller");
2045 log_if_unable(pgcfg->domain_guid, "Domain GUID");
2046 log_if_unable(pgcfg->forest_name, "Forest Name");
2047 log_if_unable(pgcfg->site_name, "Site Name");
2048 }
2049
2050 out:
2051 if (status_fp) {
2052 ad_disc_set_StatusFP(ad_ctx, NULL);
2053 (void) fclose(status_fp);
2054 status_fp = NULL;
2055 }
2056
2057 if (DBG(CONFIG, 1))
2058 idmapdlog(LOG_DEBUG, "Domain discovery done.");
2059
2060 /*
2061 * Log when this took more than 15 sec.
2062 */
2063 t1 = time(NULL);
2064 if (t1 > (t0 + 15)) {
2065 idmapdlog(LOG_NOTICE, "Domain discovery took %d sec.",
2066 (int)(t1 - t0));
2067 idmapdlog(LOG_NOTICE, "Check the DNS configuration.");
2068 }
2069 }
2070
2071 /*
2072 * This is the second part of discovery, which can take a while.
2073 * We don't want to hold up parties who just want to know what
2074 * domain controller we're using (like smbd), so this part runs
2075 * after we've updated that info in the "live" config and told
2076 * such consumers to go ahead.
2077 *
2078 * This is a lot like idmap_cfg_discover(), but used LDAP queries
2079 * get the forest information from the global catalog servers.
2080 *
2081 * Note: the previous update_* calls have usually nuked any
2082 * useful information from pgcfg before we get here, so we
2083 * can only use it store discovery results, not to read.
2084 */
2085 static void
idmap_cfg_discover2(idmap_cfg_handles_t * handles,idmap_pg_config_t * pgcfg)2086 idmap_cfg_discover2(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg)
2087 {
2088 ad_disc_t ad_ctx = handles->ad_ctx;
2089 FILE *status_fp = NULL;
2090 time_t t0, t1;
2091
2092 t0 = time(NULL);
2093 if (DBG(CONFIG, 1))
2094 idmapdlog(LOG_DEBUG, "Running forest discovery.");
2095
2096 status_fp = fopen(IDMAP_CACHEDIR "/discovery.log", "a");
2097 if (status_fp)
2098 ad_disc_set_StatusFP(ad_ctx, status_fp);
2099
2100 if (pgcfg->global_catalog == NULL)
2101 pgcfg->global_catalog =
2102 ad_disc_get_GlobalCatalog(ad_ctx,
2103 AD_DISC_PREFER_SITE,
2104 &pgcfg->global_catalog_auto_disc);
2105
2106 if (pgcfg->global_catalog != NULL) {
2107 pgcfg->domains_in_forest =
2108 ad_disc_get_DomainsInForest(ad_ctx, NULL);
2109
2110 if (!pgcfg->disable_cross_forest_trusts)
2111 discover_trusted_domains(pgcfg, ad_ctx);
2112 }
2113
2114 if (DBG(CONFIG, 1)) {
2115 log_if_unable(pgcfg->global_catalog, "Global Catalog");
2116 log_if_unable(pgcfg->domains_in_forest,
2117 "Domains in the Forest");
2118 /* Empty trusted domains list is OK. */
2119 }
2120
2121 if (status_fp) {
2122 ad_disc_set_StatusFP(ad_ctx, NULL);
2123 (void) fclose(status_fp);
2124 status_fp = NULL;
2125 }
2126
2127 if (DBG(CONFIG, 1))
2128 idmapdlog(LOG_DEBUG, "Forest discovery done.");
2129
2130 /*
2131 * Log when this took more than 30 sec.
2132 */
2133 t1 = time(NULL);
2134 if (t1 > (t0 + 30)) {
2135 idmapdlog(LOG_NOTICE, "Forest discovery took %d sec.",
2136 (int)(t1 - t0));
2137 idmapdlog(LOG_NOTICE, "Check AD join status.");
2138 }
2139 }
2140
2141
2142 /*
2143 * idmap_cfg_load() is called at startup, and periodically via the
2144 * update thread when the auto-discovery TTLs expire, as well as part of
2145 * the refresh method, to update the current configuration. It always
2146 * reads from SMF, but you still have to refresh the service after
2147 * changing the config pg in order for the changes to take effect.
2148 *
2149 * There is one flag:
2150 *
2151 * - CFG_DISCOVER
2152 *
2153 * If CFG_DISCOVER is set then idmap_cfg_load() calls
2154 * idmap_cfg_discover() to discover, via DNS and LDAP lookups, property
2155 * values that weren't set in SMF.
2156 *
2157 * idmap_cfg_load() will log (to LOG_NOTICE) whether the configuration
2158 * changed.
2159 *
2160 * Return values: 0 -> success, -1 -> failure, -2 -> hard failures
2161 * reading from SMF.
2162 */
2163 int
idmap_cfg_load(idmap_cfg_t * cfg,int flags)2164 idmap_cfg_load(idmap_cfg_t *cfg, int flags)
2165 {
2166 const ad_disc_t ad_ctx = cfg->handles.ad_ctx;
2167 int rc = 0;
2168 int errors;
2169 int changed = 0;
2170 int dc_changed = 0;
2171 int ad_reload_required = 0;
2172 idmap_pg_config_t new_pgcfg, *live_pgcfg;
2173
2174 if (DBG(CONFIG, 1))
2175 idmapdlog(LOG_DEBUG, "Loading configuration.");
2176
2177 live_pgcfg = &cfg->pgcfg;
2178 (void) memset(&new_pgcfg, 0, sizeof (new_pgcfg));
2179
2180 (void) pthread_mutex_lock(&cfg->handles.mutex);
2181
2182 if ((rc = idmap_cfg_load_smf(&cfg->handles, &new_pgcfg, &errors)) < -1)
2183 goto err;
2184
2185 if (flags & CFG_DISCOVER) {
2186
2187 ad_disc_refresh(ad_ctx);
2188
2189 /*
2190 * Unless we've been asked to forget the current DC,
2191 * give preference (in order) to the preferred DC if
2192 * configured, or the current DC. These preferences
2193 * reduce undesirable DC changes.
2194 */
2195 if (flags & CFG_FORGET_DC) {
2196 (void) ad_disc_set_PreferredDC(ad_ctx, NULL);
2197 } else if (new_pgcfg.preferred_dc != NULL) {
2198 (void) ad_disc_set_PreferredDC(ad_ctx,
2199 new_pgcfg.preferred_dc);
2200 } else if (live_pgcfg->domain_controller != NULL) {
2201 (void) ad_disc_set_PreferredDC(ad_ctx,
2202 live_pgcfg->domain_controller);
2203 } else {
2204 (void) ad_disc_set_PreferredDC(ad_ctx, NULL);
2205 }
2206
2207 /*
2208 * We want a way to tell adspriv_getdcname_1_svc()
2209 * (and others) that discovery is running and therefore
2210 * they may want to wait a bit or return an error...
2211 */
2212 (void) mutex_lock(&_idmapdstate.addisc_lk);
2213 _idmapdstate.addisc_st |= ADDISC_ST_RUNNING;
2214 (void) mutex_unlock(&_idmapdstate.addisc_lk);
2215
2216 idmap_cfg_discover1(&cfg->handles, &new_pgcfg);
2217
2218 WRLOCK_CONFIG();
2219 (void) mutex_lock(&_idmapdstate.addisc_lk);
2220 _idmapdstate.addisc_st = 0;
2221 (void) cond_broadcast(&_idmapdstate.addisc_cv);
2222 (void) mutex_unlock(&_idmapdstate.addisc_lk);
2223 } else {
2224 WRLOCK_CONFIG();
2225 }
2226
2227 /* Non-discoverable props updated here */
2228
2229 changed += update_uint64(&live_pgcfg->list_size_limit,
2230 &new_pgcfg.list_size_limit, "list_size_limit");
2231
2232 changed += update_uint64(&live_pgcfg->max_threads,
2233 &new_pgcfg.max_threads, "max_threads");
2234
2235 changed += update_uint64(&live_pgcfg->id_cache_timeout,
2236 &new_pgcfg.id_cache_timeout, "id_cache_timeout");
2237
2238 changed += update_uint64(&live_pgcfg->name_cache_timeout,
2239 &new_pgcfg.name_cache_timeout, "name_cache_timeout");
2240
2241 changed += update_uint64(&live_pgcfg->rediscovery_interval,
2242 &new_pgcfg.rediscovery_interval, "rediscovery_interval");
2243
2244 changed += update_string(&live_pgcfg->machine_sid,
2245 &new_pgcfg.machine_sid, "machine_sid");
2246
2247 changed += update_bool(&live_pgcfg->eph_map_unres_sids,
2248 &new_pgcfg.eph_map_unres_sids, "unresolvable_sid_mapping");
2249
2250 changed += update_bool(&live_pgcfg->use_ads,
2251 &new_pgcfg.use_ads, "use_ads");
2252
2253 changed += update_bool(&live_pgcfg->use_lsa,
2254 &new_pgcfg.use_lsa, "use_lsa");
2255
2256 changed += update_bool(&live_pgcfg->disable_cross_forest_trusts,
2257 &new_pgcfg.disable_cross_forest_trusts,
2258 "disable_cross_forest_trusts");
2259
2260 changed += update_enum(&live_pgcfg->directory_based_mapping,
2261 &new_pgcfg.directory_based_mapping, "directory_based_mapping",
2262 directory_mapping_map);
2263
2264 changed += update_string(&live_pgcfg->ad_unixuser_attr,
2265 &new_pgcfg.ad_unixuser_attr, "ad_unixuser_attr");
2266
2267 changed += update_string(&live_pgcfg->ad_unixgroup_attr,
2268 &new_pgcfg.ad_unixgroup_attr, "ad_unixgroup_attr");
2269
2270 changed += update_string(&live_pgcfg->nldap_winname_attr,
2271 &new_pgcfg.nldap_winname_attr, "nldap_winname_attr");
2272
2273 changed += update_string(&live_pgcfg->default_domain,
2274 &new_pgcfg.default_domain, "default_domain");
2275
2276 changed += update_dirs(&live_pgcfg->preferred_dc,
2277 &new_pgcfg.preferred_dc, "preferred_dc");
2278
2279 /* Props that can be discovered or set in SMF updated here */
2280
2281 if (update_string(&live_pgcfg->domain_name,
2282 &new_pgcfg.domain_name, "domain_name")) {
2283 changed++;
2284 ad_reload_required = TRUE;
2285 idmapd_set_krb5_realm(live_pgcfg->domain_name);
2286 }
2287 live_pgcfg->domain_name_auto_disc = new_pgcfg.domain_name_auto_disc;
2288
2289 changed += update_string(&live_pgcfg->domain_guid,
2290 &new_pgcfg.domain_guid, "domain_guid");
2291 live_pgcfg->domain_guid_auto_disc = new_pgcfg.domain_guid_auto_disc;
2292
2293 dc_changed = update_dirs(&live_pgcfg->domain_controller,
2294 &new_pgcfg.domain_controller, "domain_controller");
2295 changed += dc_changed;
2296 live_pgcfg->domain_controller_auto_disc =
2297 new_pgcfg.domain_controller_auto_disc;
2298
2299 changed += update_string(&live_pgcfg->forest_name,
2300 &new_pgcfg.forest_name, "forest_name");
2301 live_pgcfg->forest_name_auto_disc = new_pgcfg.forest_name_auto_disc;
2302
2303 changed += update_string(&live_pgcfg->site_name,
2304 &new_pgcfg.site_name, "site_name");
2305 live_pgcfg->site_name_auto_disc = new_pgcfg.site_name_auto_disc;
2306
2307 if (DBG(CONFIG, 1)) {
2308 if (changed)
2309 idmapdlog(LOG_NOTICE, "Configuration changed");
2310 else
2311 idmapdlog(LOG_NOTICE, "Configuration unchanged");
2312 }
2313
2314 UNLOCK_CONFIG();
2315
2316 if (dc_changed != 0) {
2317 notify_dc_changed();
2318 }
2319
2320 /*
2321 * Discovery2 can take a while.
2322 */
2323 if (flags & CFG_DISCOVER) {
2324 if (live_pgcfg->domain_name != NULL &&
2325 live_pgcfg->forest_name != NULL)
2326 idmap_cfg_discover2(&cfg->handles, &new_pgcfg);
2327 ad_disc_done(ad_ctx);
2328 }
2329
2330 WRLOCK_CONFIG();
2331
2332 /* More props that can be discovered or set in SMF */
2333
2334 changed += update_dirs(&live_pgcfg->global_catalog,
2335 &new_pgcfg.global_catalog, "global_catalog");
2336 live_pgcfg->global_catalog_auto_disc =
2337 new_pgcfg.global_catalog_auto_disc;
2338
2339 /* Props that are only discovered (never in SMF) */
2340
2341 if (update_domains_in_forest(&live_pgcfg->domains_in_forest,
2342 &new_pgcfg.domains_in_forest, "domains_in_forest")) {
2343 changed++;
2344 ad_reload_required = TRUE;
2345 }
2346
2347 if (update_trusted_domains(&live_pgcfg->trusted_domains,
2348 &new_pgcfg.trusted_domains, "trusted_domains")) {
2349 changed++;
2350 if (live_pgcfg->trusted_domains != NULL &&
2351 live_pgcfg->trusted_domains[0].domain[0] != '\0')
2352 ad_reload_required = TRUE;
2353 }
2354
2355 if (update_trusted_forest(&live_pgcfg->trusted_forests,
2356 &live_pgcfg->num_trusted_forests, &new_pgcfg.trusted_forests,
2357 &new_pgcfg.num_trusted_forests, "trusted_forest")) {
2358 changed++;
2359 if (live_pgcfg->trusted_forests != NULL)
2360 ad_reload_required = TRUE;
2361 }
2362
2363 if (DBG(CONFIG, 1)) {
2364 if (changed)
2365 idmapdlog(LOG_NOTICE, "Configuration changed");
2366 else
2367 idmapdlog(LOG_NOTICE, "Configuration unchanged");
2368 }
2369
2370 UNLOCK_CONFIG();
2371
2372 if (ad_reload_required)
2373 reload_ad();
2374
2375 idmap_cfg_unload(&new_pgcfg);
2376
2377 err:
2378 (void) pthread_mutex_unlock(&cfg->handles.mutex);
2379
2380 if (rc < -1)
2381 return (rc);
2382
2383 return ((errors == 0) ? 0 : -1);
2384 }
2385
2386 /*
2387 * Initialize 'cfg'.
2388 */
2389 idmap_cfg_t *
idmap_cfg_init()2390 idmap_cfg_init()
2391 {
2392 idmap_cfg_handles_t *handles;
2393
2394 /* First the smf repository handles: */
2395 idmap_cfg_t *cfg = calloc(1, sizeof (idmap_cfg_t));
2396 if (!cfg) {
2397 idmapdlog(LOG_ERR, "Out of memory");
2398 return (NULL);
2399 }
2400 handles = &cfg->handles;
2401
2402 (void) pthread_mutex_init(&handles->mutex, NULL);
2403
2404 if (!(handles->main = scf_handle_create(SCF_VERSION))) {
2405 idmapdlog(LOG_ERR, "scf_handle_create() failed: %s",
2406 scf_strerror(scf_error()));
2407 goto error;
2408 }
2409
2410 if (scf_handle_bind(handles->main) < 0) {
2411 idmapdlog(LOG_ERR, "scf_handle_bind() failed: %s",
2412 scf_strerror(scf_error()));
2413 goto error;
2414 }
2415
2416 if (!(handles->service = scf_service_create(handles->main)) ||
2417 !(handles->instance = scf_instance_create(handles->main)) ||
2418 !(handles->config_pg = scf_pg_create(handles->main)) ||
2419 !(handles->debug_pg = scf_pg_create(handles->main))) {
2420 idmapdlog(LOG_ERR, "scf handle creation failed: %s",
2421 scf_strerror(scf_error()));
2422 goto error;
2423 }
2424
2425 if (scf_handle_decode_fmri(handles->main,
2426 FMRI_BASE "/:properties/" CONFIG_PG,
2427 NULL, /* scope */
2428 handles->service, /* service */
2429 handles->instance, /* instance */
2430 handles->config_pg, /* pg */
2431 NULL, /* prop */
2432 SCF_DECODE_FMRI_EXACT) < 0) {
2433 idmapdlog(LOG_ERR, "scf_handle_decode_fmri() failed: %s",
2434 scf_strerror(scf_error()));
2435 goto error;
2436 }
2437
2438 if (scf_service_get_pg(handles->service,
2439 DEBUG_PG, handles->debug_pg) < 0) {
2440 idmapdlog(LOG_ERR, "Property group \"%s\": %s",
2441 DEBUG_PG, scf_strerror(scf_error()));
2442 goto error;
2443 }
2444
2445 check_smf_debug_mode(handles);
2446
2447 /* Initialize AD Auto Discovery context */
2448 handles->ad_ctx = ad_disc_init();
2449 if (handles->ad_ctx == NULL)
2450 goto error;
2451
2452 return (cfg);
2453
2454 error:
2455 (void) idmap_cfg_fini(cfg);
2456 return (NULL);
2457 }
2458
2459 void
idmap_cfg_unload(idmap_pg_config_t * pgcfg)2460 idmap_cfg_unload(idmap_pg_config_t *pgcfg)
2461 {
2462
2463 if (pgcfg->default_domain) {
2464 free(pgcfg->default_domain);
2465 pgcfg->default_domain = NULL;
2466 }
2467 if (pgcfg->domain_name) {
2468 free(pgcfg->domain_name);
2469 pgcfg->domain_name = NULL;
2470 }
2471 if (pgcfg->domain_guid) {
2472 free(pgcfg->domain_guid);
2473 pgcfg->domain_guid = NULL;
2474 }
2475 if (pgcfg->machine_sid) {
2476 free(pgcfg->machine_sid);
2477 pgcfg->machine_sid = NULL;
2478 }
2479 if (pgcfg->domain_controller) {
2480 free(pgcfg->domain_controller);
2481 pgcfg->domain_controller = NULL;
2482 }
2483 if (pgcfg->forest_name) {
2484 free(pgcfg->forest_name);
2485 pgcfg->forest_name = NULL;
2486 }
2487 if (pgcfg->site_name) {
2488 free(pgcfg->site_name);
2489 pgcfg->site_name = NULL;
2490 }
2491 if (pgcfg->global_catalog) {
2492 free(pgcfg->global_catalog);
2493 pgcfg->global_catalog = NULL;
2494 }
2495 if (pgcfg->trusted_domains) {
2496 free(pgcfg->trusted_domains);
2497 pgcfg->trusted_domains = NULL;
2498 }
2499 if (pgcfg->trusted_forests)
2500 free_trusted_forests(&pgcfg->trusted_forests,
2501 &pgcfg->num_trusted_forests);
2502
2503 if (pgcfg->ad_unixuser_attr) {
2504 free(pgcfg->ad_unixuser_attr);
2505 pgcfg->ad_unixuser_attr = NULL;
2506 }
2507 if (pgcfg->ad_unixgroup_attr) {
2508 free(pgcfg->ad_unixgroup_attr);
2509 pgcfg->ad_unixgroup_attr = NULL;
2510 }
2511 if (pgcfg->nldap_winname_attr) {
2512 free(pgcfg->nldap_winname_attr);
2513 pgcfg->nldap_winname_attr = NULL;
2514 }
2515 }
2516
2517 int
idmap_cfg_fini(idmap_cfg_t * cfg)2518 idmap_cfg_fini(idmap_cfg_t *cfg)
2519 {
2520 idmap_cfg_handles_t *handles = &cfg->handles;
2521 idmap_cfg_unload(&cfg->pgcfg);
2522
2523 (void) pthread_mutex_destroy(&handles->mutex);
2524 scf_pg_destroy(handles->config_pg);
2525 if (handles->debug_pg != NULL)
2526 scf_pg_destroy(handles->debug_pg);
2527 scf_instance_destroy(handles->instance);
2528 scf_service_destroy(handles->service);
2529 scf_handle_destroy(handles->main);
2530 if (handles->ad_ctx != NULL)
2531 ad_disc_fini(handles->ad_ctx);
2532 free(cfg);
2533
2534 return (0);
2535 }
2536
2537 void
idmap_cfg_poke_updates(void)2538 idmap_cfg_poke_updates(void)
2539 {
2540 int prev_st;
2541
2542 if (DBG(CONFIG, 1)) {
2543 idmapdlog(LOG_INFO, "idmap_cfg_poke_updates");
2544 }
2545
2546 (void) mutex_lock(&_idmapdstate.addisc_lk);
2547 prev_st = _idmapdstate.addisc_st;
2548 _idmapdstate.addisc_st |= ADDISC_ST_REQUESTED;
2549 (void) mutex_unlock(&_idmapdstate.addisc_lk);
2550
2551 if (prev_st & ADDISC_ST_REQUESTED) {
2552 idmapdlog(LOG_DEBUG, "already poked");
2553 } else {
2554 idmapdlog(LOG_DEBUG, "port send poke");
2555 (void) port_send(idmapd_ev_port, POKE_AUTO_DISCOVERY, NULL);
2556 }
2557 }
2558
2559 void
idmap_cfg_force_rediscovery(void)2560 idmap_cfg_force_rediscovery(void)
2561 {
2562 int prev_st;
2563
2564 if (DBG(CONFIG, 1)) {
2565 idmapdlog(LOG_INFO, "idmap_cfg_force_rediscovery");
2566 }
2567
2568 (void) mutex_lock(&_idmapdstate.addisc_lk);
2569 prev_st = _idmapdstate.addisc_st;
2570 _idmapdstate.addisc_st |= ADDISC_ST_REQUESTED;
2571 (void) mutex_unlock(&_idmapdstate.addisc_lk);
2572
2573 if (prev_st & ADDISC_ST_REQUESTED) {
2574 idmapdlog(LOG_DEBUG, "already kicked");
2575 } else {
2576 idmapdlog(LOG_DEBUG, "port send kick");
2577 (void) port_send(idmapd_ev_port, KICK_AUTO_DISCOVERY, NULL);
2578 }
2579 }
2580
2581 /*ARGSUSED*/
2582 void
idmap_cfg_hup_handler(int sig)2583 idmap_cfg_hup_handler(int sig)
2584 {
2585 if (idmapd_ev_port >= 0)
2586 (void) port_send(idmapd_ev_port, RECONFIGURE, NULL);
2587 }
2588
2589 /*
2590 * Upgrade the debug flags.
2591 *
2592 * We're replacing a single debug flag with a fine-grained mechanism that
2593 * is also capable of considerably more verbosity. We'll take a stab at
2594 * producing roughly the same level of output.
2595 */
2596 static
2597 int
upgrade_debug(idmap_cfg_handles_t * handles)2598 upgrade_debug(idmap_cfg_handles_t *handles)
2599 {
2600 boolean_t debug_present;
2601 const char DEBUG_PROP[] = "debug";
2602 int rc;
2603
2604 rc = prop_exists(handles, DEBUG_PROP, &debug_present);
2605
2606 if (rc != 0)
2607 return (rc);
2608
2609 if (!debug_present)
2610 return (0);
2611
2612 idmapdlog(LOG_INFO,
2613 "Upgrading old %s/%s setting to %s/* settings.",
2614 CONFIG_PG, DEBUG_PROP, DEBUG_PG);
2615
2616 rc = set_val_integer(handles, handles->debug_pg, "config", 1);
2617 if (rc != 0)
2618 return (rc);
2619 rc = set_val_integer(handles, handles->debug_pg, "discovery", 1);
2620 if (rc != 0)
2621 return (rc);
2622
2623 rc = del_val(handles, handles->config_pg, DEBUG_PROP);
2624 if (rc != 0)
2625 return (rc);
2626
2627 return (0);
2628 }
2629
2630 /*
2631 * Upgrade the DS mapping flags.
2632 *
2633 * If the old ds_name_mapping_enabled flag is present, then
2634 * if the new directory_based_mapping value is present, then
2635 * if the two are compatible, delete the old and note it
2636 * else delete the old and warn
2637 * else
2638 * set the new based on the old, and note it
2639 * delete the old
2640 */
2641 static
2642 int
upgrade_directory_mapping(idmap_cfg_handles_t * handles)2643 upgrade_directory_mapping(idmap_cfg_handles_t *handles)
2644 {
2645 boolean_t legacy_ds_name_mapping_present;
2646 const char DS_NAME_MAPPING_ENABLED[] = "ds_name_mapping_enabled";
2647 const char DIRECTORY_BASED_MAPPING[] = "directory_based_mapping";
2648 int rc;
2649
2650 rc = prop_exists(handles, DS_NAME_MAPPING_ENABLED,
2651 &legacy_ds_name_mapping_present);
2652
2653 if (rc != 0)
2654 return (rc);
2655
2656 if (!legacy_ds_name_mapping_present)
2657 return (0);
2658
2659 boolean_t legacy_ds_name_mapping_enabled;
2660 rc = get_val_bool(handles, DS_NAME_MAPPING_ENABLED,
2661 &legacy_ds_name_mapping_enabled, B_FALSE);
2662 if (rc != 0)
2663 return (rc);
2664
2665 char *legacy_mode;
2666 char *legacy_bool_string;
2667 if (legacy_ds_name_mapping_enabled) {
2668 legacy_mode = "name";
2669 legacy_bool_string = "true";
2670 } else {
2671 legacy_mode = "none";
2672 legacy_bool_string = "false";
2673 }
2674
2675 char *directory_based_mapping;
2676 rc = get_val_astring(handles, DIRECTORY_BASED_MAPPING,
2677 &directory_based_mapping);
2678 if (rc != 0)
2679 return (rc);
2680
2681 if (directory_based_mapping == NULL) {
2682 idmapdlog(LOG_INFO,
2683 "Upgrading old %s=%s setting\n"
2684 "to %s=%s.",
2685 DS_NAME_MAPPING_ENABLED, legacy_bool_string,
2686 DIRECTORY_BASED_MAPPING, legacy_mode);
2687 rc = set_val_astring(handles, handles->config_pg,
2688 DIRECTORY_BASED_MAPPING, legacy_mode);
2689 if (rc != 0)
2690 return (rc);
2691 } else {
2692 boolean_t new_name_mapping;
2693 if (strcasecmp(directory_based_mapping, "name") == 0)
2694 new_name_mapping = B_TRUE;
2695 else
2696 new_name_mapping = B_FALSE;
2697
2698 if (legacy_ds_name_mapping_enabled == new_name_mapping) {
2699 idmapdlog(LOG_INFO,
2700 "Automatically removing old %s=%s setting\n"
2701 "in favor of %s=%s.",
2702 DS_NAME_MAPPING_ENABLED, legacy_bool_string,
2703 DIRECTORY_BASED_MAPPING, directory_based_mapping);
2704 } else {
2705 idmapdlog(LOG_WARNING,
2706 "Removing conflicting %s=%s setting\n"
2707 "in favor of %s=%s.",
2708 DS_NAME_MAPPING_ENABLED, legacy_bool_string,
2709 DIRECTORY_BASED_MAPPING, directory_based_mapping);
2710 }
2711 free(directory_based_mapping);
2712 }
2713
2714 rc = del_val(handles, handles->config_pg, DS_NAME_MAPPING_ENABLED);
2715 if (rc != 0)
2716 return (rc);
2717
2718 return (0);
2719 }
2720
2721 /*
2722 * Do whatever is necessary to upgrade idmap's configuration before
2723 * we load it.
2724 */
2725 int
idmap_cfg_upgrade(idmap_cfg_t * cfg)2726 idmap_cfg_upgrade(idmap_cfg_t *cfg)
2727 {
2728 int rc;
2729
2730 rc = upgrade_directory_mapping(&cfg->handles);
2731 if (rc != 0)
2732 return (rc);
2733
2734 rc = upgrade_debug(&cfg->handles);
2735 if (rc != 0)
2736 return (rc);
2737
2738 return (0);
2739 }
2740
2741 /*
2742 * The LDAP code passes principal names lacking any
2743 * realm information, which causes mech_krb5 to do
2744 * awful things trying to figure out the realm.
2745 * Avoid that by making sure it has a default,
2746 * even when krb5.conf is not configured.
2747 */
2748 static void
idmapd_set_krb5_realm(char * domain)2749 idmapd_set_krb5_realm(char *domain)
2750 {
2751 static char realm[MAXHOSTNAMELEN];
2752 size_t ilen, olen;
2753 int err;
2754
2755 if (domain == NULL) {
2756 (void) unsetenv("KRB5_DEFAULT_REALM");
2757 return;
2758 }
2759
2760 /* Convert to upper case, in place. */
2761 (void) strlcpy(realm, domain, sizeof (realm));
2762 olen = ilen = strlen(realm);
2763 (void) u8_textprep_str(realm, &ilen, realm, &olen,
2764 U8_TEXTPREP_TOUPPER, U8_UNICODE_LATEST, &err);
2765
2766 (void) setenv("KRB5_DEFAULT_REALM", realm, 1);
2767 }
2768