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