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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #define __STANDALONE_MODULE__ 27 28 #include <stdio.h> 29 #include <sys/types.h> 30 #include <stdlib.h> 31 #include <libintl.h> 32 #include <string.h> 33 #include <ctype.h> 34 35 #include <sys/stat.h> 36 #include <fcntl.h> 37 #include <unistd.h> 38 #include <syslog.h> 39 #include <locale.h> 40 #include <errno.h> 41 #include <sys/time.h> 42 43 #include <arpa/inet.h> 44 #include <netdb.h> 45 #include <strings.h> 46 47 #include <thread.h> 48 49 #include <nsswitch.h> 50 #include <nss_dbdefs.h> 51 #include <nss.h> 52 53 #include "ns_cache_door.h" 54 #include "ns_internal.h" 55 #include "ns_connmgmt.h" 56 57 typedef enum { 58 INFO_SERVER_JUST_INITED = -1, 59 INFO_SERVER_UNKNOWN = 0, 60 INFO_SERVER_CONNECTING = 1, 61 INFO_SERVER_UP = 2, 62 INFO_SERVER_ERROR = 3, 63 INFO_SERVER_REMOVED = 4 64 } dir_server_status_t; 65 66 typedef enum { 67 INFO_STATUS_NEW = 2, 68 INFO_STATUS_OLD = 3 69 } dir_server_info_t; 70 71 typedef struct dir_server { 72 char *ip; 73 char **controls; 74 char **saslMech; 75 dir_server_status_t status; 76 mutex_t updateStatus; 77 dir_server_info_t info; 78 } dir_server_t; 79 80 typedef struct dir_server_list { 81 dir_server_t **nsServers; 82 83 rwlock_t listDestroyLock; 84 } dir_server_list_t; 85 86 struct { 87 /* The local list of the directory servers' root DSEs. */ 88 dir_server_list_t *list; 89 /* The flag indicating if libsldap is in the 'Standalone' mode. */ 90 int standalone; 91 /* 92 * The mutex ensuring that only one thread performs 93 * the initialization of the list. 94 */ 95 mutex_t listReplaceLock; 96 /* 97 * A flag indicating that a particular thread is 98 * in the 'ldap_cachemgr' mode. It is stored by thread as 99 * a thread specific data. 100 */ 101 const int initFlag; 102 /* 103 * A thread specific key storing 104 * the the 'ldap_cachemgr' mode indicator. 105 */ 106 thread_key_t standaloneInitKey; 107 } dir_servers = {NULL, 0, DEFAULTMUTEX, '1'}; 108 109 typedef struct switchDatabase { 110 char *conf; 111 uint32_t alloced; 112 } switch_database_t; 113 114 static thread_key_t switchConfigKey; 115 116 #pragma init(createStandaloneKey) 117 118 #define DONT_INCLUDE_ATTR_NAMES 0 119 #define INCLUDE_ATTR_NAMES 1 120 #define IS_PROFILE 1 121 #define NOT_PROFILE 0 122 /* INET6_ADDRSTRLEN + ":" + <5-digit port> + some round-up */ 123 #define MAX_HOSTADDR_LEN (INET6_ADDRSTRLEN + 6 + 12) 124 125 static 126 void 127 switch_conf_disposer(void *data) 128 { 129 switch_database_t *localData = (switch_database_t *)data; 130 131 free(localData->conf); 132 free(localData); 133 } 134 135 /* 136 * This function initializes an indication that a thread obtaining a root DSE 137 * will be switched to the 'ldap_cachemgr' mode. Within the thread libsldap 138 * will not invoke the __s_api_requestServer function. Instead, the library 139 * will establish a connection to the server specified by 140 * the __ns_ldap_getRootDSE function. 141 * Since ldap_cachmgr can obtain a DUAProfile and root DSEs at the same time 142 * and we do not want to affect a thread obtaining a DUAProfile, 143 * the 'ldap_cachemgr' mode is thread private. 144 * In addition, this function creates a key holding temporary configuration 145 * for the "hosts" and "ipnodes" databases which is used by the "SKIPDB" 146 * mechanism (__s_api_ip2hostname() & _s_api_hostname2ip()). 147 */ 148 static 149 void 150 createStandaloneKey() 151 { 152 if (thr_keycreate(&dir_servers.standaloneInitKey, NULL) != 0) { 153 syslog(LOG_ERR, gettext("libsldap: unable to create a thread " 154 "key needed for sharing ldap connections")); 155 } 156 if (thr_keycreate(&switchConfigKey, switch_conf_disposer) != 0) { 157 syslog(LOG_ERR, gettext("libsldap: unable to create a thread " 158 "key containing current nsswitch configuration")); 159 } 160 } 161 162 /* 163 * This function sets the 'ldap_cachemgr' mode indication. 164 */ 165 void 166 __s_api_setInitMode() 167 { 168 (void) thr_setspecific(dir_servers.standaloneInitKey, 169 (void *) &dir_servers.initFlag); 170 } 171 172 /* 173 * This function unset the 'ldap_cachemgr' mode indication. 174 */ 175 void 176 __s_api_unsetInitMode() 177 { 178 (void) thr_setspecific(dir_servers.standaloneInitKey, NULL); 179 } 180 181 /* 182 * This function checks if the 'ldap_cachemgr' mode indication is set. 183 */ 184 int 185 __s_api_isInitializing() { 186 int *flag = NULL; 187 188 (void) thr_getspecific(dir_servers.standaloneInitKey, (void **) &flag); 189 190 return (flag != NULL && *flag == dir_servers.initFlag); 191 } 192 193 /* 194 * This function checks if the process runs in the 'Standalone' mode. 195 * In this mode libsldap will check the local, process private list of root DSEs 196 * instead of requesting them via a door call to ldap_cachemgr. 197 */ 198 int 199 __s_api_isStandalone() 200 { 201 int mode; 202 203 (void) mutex_lock(&dir_servers.listReplaceLock); 204 mode = dir_servers.standalone; 205 (void) mutex_unlock(&dir_servers.listReplaceLock); 206 207 return (mode); 208 } 209 210 211 static 212 int 213 remove_ldap(char *dst, char *src, int dst_buf_len) 214 { 215 int i = 0; 216 217 if (strlen(src) >= dst_buf_len) 218 return (0); 219 220 while (*src != '\0') { 221 /* Copy up to one space from source. */ 222 if (isspace(*src)) { 223 dst[i++] = *src; 224 while (isspace(*src)) 225 src++; 226 } 227 228 /* If not "ldap", just copy. */ 229 if (strncmp(src, "ldap", 4) != 0) { 230 while (!isspace(*src)) { 231 dst[i++] = *src++; 232 /* At the end of string? */ 233 if (dst[i-1] == '\0') 234 return (1); 235 } 236 /* Copy up to one space from source. */ 237 if (isspace(*src)) { 238 dst[i++] = *src; 239 while (isspace(*src)) 240 src++; 241 } 242 /* Copy also the criteria section */ 243 if (*src == '[') 244 while (*src != ']') { 245 dst[i++] = *src++; 246 /* Shouln't happen if format is right */ 247 if (dst[i-1] == '\0') 248 return (1); 249 } 250 } 251 252 /* If next part is ldap, skip over it ... */ 253 if (strncmp(src, "ldap", 4) == 0) { 254 if (isspace(*(src+4)) || *(src+4) == '\0') { 255 src += 4; 256 while (isspace(*src)) 257 src++; 258 if (*src == '[') { 259 while (*src++ != ']') { 260 /* 261 * See comment above about 262 * correct format. 263 */ 264 if (*src == '\0') { 265 dst[i++] = '\0'; 266 return (1); 267 } 268 } 269 } 270 while (isspace(*src)) 271 src++; 272 } 273 } 274 if (*src == '\0') 275 dst[i++] = '\0'; 276 } 277 278 return (1); 279 } 280 281 static 282 char * 283 get_db(const char *db_name) 284 { 285 char *ptr; 286 switch_database_t *hostService = NULL; 287 FILE *fp = fopen(__NSW_CONFIG_FILE, "rF"); 288 char *linep, line[NSS_BUFSIZ]; 289 290 if (fp == NULL) { 291 syslog(LOG_WARNING, gettext("libsldap: can not read %s"), 292 __NSW_CONFIG_FILE); 293 return (NULL); 294 } 295 296 while ((linep = fgets(line, NSS_BUFSIZ, fp)) != NULL) { 297 while (isspace(*linep)) { 298 ++linep; 299 } 300 if (*linep == '#') { 301 continue; 302 } 303 if (strncmp(linep, db_name, strlen(db_name)) != 0) { 304 continue; 305 } 306 if ((linep = strchr(linep, ':')) != NULL) { 307 if (linep[strlen(linep) - 1] == '\n') { 308 linep[strlen(linep) - 1] = '\0'; 309 } 310 311 while (isspace(*++linep)) 312 ; 313 314 if ((ptr = strchr(linep, '#')) != NULL) { 315 while (--ptr >= linep && isspace(*ptr)) 316 ; 317 *(ptr + 1) = '\0'; 318 } 319 320 if (strlen(linep) == 0) { 321 continue; 322 } 323 break; 324 } 325 } 326 327 (void) fclose(fp); 328 329 if (linep == NULL) { 330 syslog(LOG_WARNING, 331 gettext("libsldap: the %s database " 332 "is missing from %s"), 333 db_name, 334 __NSW_CONFIG_FILE); 335 return (NULL); 336 } 337 338 (void) thr_getspecific(switchConfigKey, (void **) &hostService); 339 if (hostService == NULL) { 340 hostService = calloc(1, sizeof (switch_database_t)); 341 if (hostService == NULL) { 342 return (NULL); 343 } 344 (void) thr_setspecific(switchConfigKey, hostService); 345 } 346 347 /* 348 * In a long-living process threads can perform several 349 * getXbyY requests. And the windows between those requests 350 * can be long. The nsswitch configuration can change from time 351 * to time. So instead of allocating/freeing memory every time 352 * the API is called, reallocate memory only when the current 353 * configuration for the database being used is longer than 354 * the previous one. 355 */ 356 if (strlen(linep) >= hostService->alloced) { 357 ptr = (char *)realloc((void *)hostService->conf, 358 strlen(linep) + 1); 359 if (ptr == NULL) { 360 free((void *)hostService->conf); 361 hostService->conf = NULL; 362 hostService->alloced = 0; 363 return (NULL); 364 } 365 bzero(ptr, strlen(linep) + 1); 366 hostService->conf = ptr; 367 hostService->alloced = strlen(linep) + 1; 368 } 369 370 if (remove_ldap(hostService->conf, linep, hostService->alloced)) 371 return (hostService->conf); 372 else 373 return (NULL); 374 } 375 376 static 377 void 378 _initf_ipnodes(nss_db_params_t *p) 379 { 380 char *services = get_db("ipnodes"); 381 382 p->name = NSS_DBNAM_IPNODES; 383 p->flags |= NSS_USE_DEFAULT_CONFIG; 384 p->default_config = services == NULL ? "" : services; 385 } 386 387 static 388 void 389 _initf_hosts(nss_db_params_t *p) 390 { 391 char *services = get_db("hosts"); 392 393 p->name = NSS_DBNAM_HOSTS; 394 p->flags |= NSS_USE_DEFAULT_CONFIG; 395 p->default_config = services == NULL ? "" : services; 396 } 397 398 /* 399 * This function is an analog of the standard gethostbyaddr_r() 400 * function with an exception that it removes the 'ldap' back-end 401 * (if any) from the host/ipnodes nsswitch's databases and then 402 * looks up using remaining back-ends. 403 */ 404 static 405 struct hostent * 406 _filter_gethostbyaddr_r(const char *addr, int len, int type, 407 struct hostent *result, char *buffer, int buflen, 408 int *h_errnop) 409 { 410 DEFINE_NSS_DB_ROOT(db_root_hosts); 411 DEFINE_NSS_DB_ROOT(db_root_ipnodes); 412 nss_XbyY_args_t arg; 413 nss_status_t res; 414 int (*str2ent)(); 415 void (*nss_initf)(); 416 nss_db_root_t *nss_db_root; 417 int dbop; 418 419 switch (type) { 420 case AF_INET: 421 str2ent = str2hostent; 422 nss_initf = _initf_hosts; 423 nss_db_root = &db_root_hosts; 424 dbop = NSS_DBOP_HOSTS_BYADDR; 425 break; 426 case AF_INET6: 427 str2ent = str2hostent6; 428 nss_initf = _initf_ipnodes; 429 nss_db_root = &db_root_ipnodes; 430 dbop = NSS_DBOP_IPNODES_BYADDR; 431 default: 432 return (NULL); 433 } 434 435 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2ent); 436 437 arg.key.hostaddr.addr = addr; 438 arg.key.hostaddr.len = len; 439 arg.key.hostaddr.type = type; 440 arg.stayopen = 0; 441 arg.h_errno = NETDB_SUCCESS; 442 443 res = nss_search(nss_db_root, nss_initf, dbop, &arg); 444 arg.status = res; 445 *h_errnop = arg.h_errno; 446 return (struct hostent *)NSS_XbyY_FINI(&arg); 447 } 448 449 /* 450 * This routine is an analog of gethostbyaddr_r(). 451 * But in addition __s_api_hostname2ip() performs the "LDAP SKIPDB" activity 452 * prior to querying the name services. 453 * If the buffer is not big enough to accommodate a returning data, 454 * NULL is returned and h_errnop is set to TRY_AGAIN. 455 */ 456 struct hostent * 457 __s_api_hostname2ip(const char *name, 458 struct hostent *result, char *buffer, int buflen, 459 int *h_errnop) 460 { 461 DEFINE_NSS_DB_ROOT(db_root_ipnodes); 462 DEFINE_NSS_DB_ROOT(db_root_hosts); 463 nss_XbyY_args_t arg; 464 nss_status_t res; 465 struct in_addr addr; 466 struct in6_addr addr6; 467 468 if (inet_pton(AF_INET, name, &addr) > 0) { 469 if (buflen < strlen(name) + 1 + 470 sizeof (char *) * 2 + /* The h_aliases member */ 471 sizeof (struct in_addr) + 472 sizeof (struct in_addr *) * 2) { 473 *h_errnop = TRY_AGAIN; 474 return (NULL); 475 } 476 477 result->h_addrtype = AF_INET; 478 result->h_length = sizeof (struct in_addr); 479 (void) strncpy(buffer, name, buflen); 480 481 result->h_addr_list = (char **)ROUND_UP( 482 buffer + strlen(name) + 1, 483 sizeof (char *)); 484 result->h_aliases = (char **)ROUND_UP(result->h_addr_list, 485 sizeof (char *)); 486 result->h_aliases[0] = buffer; 487 result->h_aliases[1] = NULL; 488 bcopy(&addr, 489 buffer + buflen - sizeof (struct in_addr), 490 sizeof (struct in_addr)); 491 result->h_addr_list[0] = buffer + buflen - 492 sizeof (struct in_addr); 493 result->h_addr_list[1] = NULL; 494 result->h_aliases = result->h_addr_list; 495 result->h_name = buffer; 496 497 *h_errnop = NETDB_SUCCESS; 498 return (result); 499 } 500 if (inet_pton(AF_INET6, name, &addr6) > 0) { 501 if (buflen < strlen(name) + 1 + 502 sizeof (char *) * 2 + /* The h_aliases member */ 503 sizeof (struct in6_addr) + 504 sizeof (struct in6_addr *) * 2) { 505 *h_errnop = TRY_AGAIN; 506 return (NULL); 507 } 508 509 result->h_addrtype = AF_INET6; 510 result->h_length = sizeof (struct in6_addr); 511 (void) strncpy(buffer, name, buflen); 512 513 result->h_addr_list = (char **)ROUND_UP( 514 buffer + strlen(name) + 1, 515 sizeof (char *)); 516 result->h_aliases = (char **)ROUND_UP(result->h_addr_list, 517 sizeof (char *)); 518 result->h_aliases[0] = buffer; 519 result->h_aliases[1] = NULL; 520 bcopy(&addr6, 521 buffer + buflen - sizeof (struct in6_addr), 522 sizeof (struct in6_addr)); 523 result->h_addr_list[0] = buffer + buflen - 524 sizeof (struct in6_addr); 525 result->h_addr_list[1] = NULL; 526 result->h_aliases = result->h_addr_list; 527 result->h_name = buffer; 528 529 *h_errnop = NETDB_SUCCESS; 530 return (result); 531 } 532 533 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent); 534 535 arg.key.name = name; 536 arg.stayopen = 0; 537 arg.h_errno = NETDB_SUCCESS; 538 539 res = nss_search(&db_root_ipnodes, _initf_ipnodes, 540 NSS_DBOP_IPNODES_BYNAME, &arg); 541 if (res == NSS_NOTFOUND || res == NSS_UNAVAIL) { 542 arg.h_errno = NETDB_SUCCESS; 543 res = nss_search(&db_root_hosts, _initf_hosts, 544 NSS_DBOP_HOSTS_BYNAME, &arg); 545 } 546 arg.status = res; 547 *h_errnop = arg.h_errno; 548 return ((struct hostent *)NSS_XbyY_FINI(&arg)); 549 } 550 551 /* 552 * Convert an IP to a host name. 553 */ 554 ns_ldap_return_code 555 __s_api_ip2hostname(char *ipaddr, char **hostname) { 556 struct in_addr in; 557 struct in6_addr in6; 558 struct hostent *hp = NULL, hostEnt; 559 char buffer[NSS_BUFLEN_HOSTS]; 560 int buflen = NSS_BUFLEN_HOSTS; 561 char *start = NULL, 562 *end = NULL, 563 delim = '\0'; 564 char *port = NULL, 565 *addr = NULL; 566 int errorNum = 0, 567 len = 0; 568 569 if (ipaddr == NULL || hostname == NULL) 570 return (NS_LDAP_INVALID_PARAM); 571 *hostname = NULL; 572 if ((addr = strdup(ipaddr)) == NULL) 573 return (NS_LDAP_MEMORY); 574 575 if (addr[0] == '[') { 576 /* 577 * Assume it's [ipv6]:port 578 * Extract ipv6 IP 579 */ 580 start = &addr[1]; 581 if ((end = strchr(addr, ']')) != NULL) { 582 *end = '\0'; 583 delim = ']'; 584 if (*(end + 1) == ':') 585 /* extract port */ 586 port = end + 2; 587 } else { 588 free(addr); 589 return (NS_LDAP_INVALID_PARAM); 590 } 591 } else if ((end = strchr(addr, ':')) != NULL) { 592 /* assume it's ipv4:port */ 593 *end = '\0'; 594 delim = ':'; 595 start = addr; 596 port = end + 1; 597 } else 598 /* No port */ 599 start = addr; 600 601 602 if (inet_pton(AF_INET, start, &in) == 1) { 603 /* IPv4 */ 604 hp = _filter_gethostbyaddr_r((char *)&in, 605 sizeof (in.s_addr), 606 AF_INET, 607 &hostEnt, 608 buffer, 609 buflen, 610 &errorNum); 611 if (hp && hp->h_name) { 612 /* hostname + '\0' */ 613 len = strlen(hp->h_name) + 1; 614 if (port) 615 /* ':' + port */ 616 len += strlen(port) + 1; 617 if ((*hostname = malloc(len)) == NULL) { 618 free(addr); 619 return (NS_LDAP_MEMORY); 620 } 621 622 if (port) 623 (void) snprintf(*hostname, len, "%s:%s", 624 hp->h_name, port); 625 else 626 (void) strlcpy(*hostname, hp->h_name, len); 627 628 free(addr); 629 return (NS_LDAP_SUCCESS); 630 } else { 631 free(addr); 632 return (NS_LDAP_NOTFOUND); 633 } 634 } else if (inet_pton(AF_INET6, start, &in6) == 1) { 635 /* IPv6 */ 636 hp = _filter_gethostbyaddr_r((char *)&in6, 637 sizeof (in6.s6_addr), 638 AF_INET6, 639 &hostEnt, 640 buffer, 641 buflen, 642 &errorNum); 643 if (hp && hp->h_name) { 644 /* hostname + '\0' */ 645 len = strlen(hp->h_name) + 1; 646 if (port) 647 /* ':' + port */ 648 len += strlen(port) + 1; 649 if ((*hostname = malloc(len)) == NULL) { 650 free(addr); 651 return (NS_LDAP_MEMORY); 652 } 653 654 if (port) 655 (void) snprintf(*hostname, len, "%s:%s", 656 hp->h_name, port); 657 else 658 (void) strlcpy(*hostname, hp->h_name, len); 659 660 free(addr); 661 return (NS_LDAP_SUCCESS); 662 } else { 663 free(addr); 664 return (NS_LDAP_NOTFOUND); 665 } 666 } else { 667 /* 668 * A hostname 669 * Return it as is 670 */ 671 if (end) 672 *end = delim; 673 *hostname = addr; 674 return (NS_LDAP_SUCCESS); 675 } 676 } 677 678 /* 679 * This function obtains data returned by an LDAP search request and puts it 680 * in a string in the ldap_cachmgr(1) door call format. 681 * 682 * INPUT: 683 * ld - a pointer to an LDAP structure used for a search operation, 684 * result_msg - a pointer to an LDAPMessage returned by the search, 685 * include_names - if set to INCLUDE_ATTR_NAMES, the output buffer will 686 * contain attribute names. 687 * Otherwise, only values will be return. 688 * 689 * OUTPUT: 690 * a buffer containing server info in the following format: 691 * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...] 692 * Should be free'ed by the caller. 693 */ 694 static 695 ns_ldap_return_code 696 convert_to_door_line(LDAP* ld, 697 LDAPMessage *result_msg, 698 int include_names, 699 int is_profile, 700 char **door_line) 701 { 702 uint32_t total_length = 0, attr_len = 0, i; 703 LDAPMessage *e; 704 char *a, **vals; 705 BerElement *ber; 706 int seen_objectclass = 0, rewind = 0; 707 708 if (!door_line) { 709 return (NS_LDAP_INVALID_PARAM); 710 } 711 *door_line = NULL; 712 713 if ((e = ldap_first_entry(ld, result_msg)) == NULL) { 714 return (NS_LDAP_NOTFOUND); 715 } 716 717 /* calculate length of received data */ 718 for (a = ldap_first_attribute(ld, e, &ber); 719 a != NULL; 720 a = ldap_next_attribute(ld, e, ber)) { 721 722 if ((vals = ldap_get_values(ld, e, a)) != NULL) { 723 for (i = 0; vals[i] != NULL; i++) { 724 total_length += (include_names ? 725 strlen(a) : 0) + 726 strlen(vals[i]) + 727 strlen(DOORLINESEP) +1; 728 } 729 ldap_value_free(vals); 730 } 731 ldap_memfree(a); 732 } 733 if (ber != NULL) { 734 ber_free(ber, 0); 735 } 736 737 if (total_length == 0) { 738 return (NS_LDAP_NOTFOUND); 739 } 740 741 /* copy the data */ 742 /* add 1 for the last '\0' */ 743 *door_line = (char *)malloc(total_length + 1); 744 if (*door_line == NULL) { 745 return (NS_LDAP_MEMORY); 746 } 747 748 /* make it an empty string first */ 749 **door_line = '\0'; 750 a = ldap_first_attribute(ld, e, &ber); 751 while (a != NULL) { 752 if (is_profile) { 753 /* 754 * If we're processing DUAConfigProfile, we need to make 755 * sure we put objectclass attribute first. 756 * __s_api_create_config_door_str depends on that. 757 */ 758 if (seen_objectclass) { 759 if (strcasecmp(a, "objectclass") == 0) { 760 /* Skip objectclass now. */ 761 a = ldap_next_attribute(ld, e, ber); 762 continue; 763 } 764 } else { 765 if (strcasecmp(a, "objectclass") == 0) { 766 seen_objectclass = 1; 767 rewind = 1; 768 } else { 769 /* Skip all but objectclass first. */ 770 a = ldap_next_attribute(ld, e, ber); 771 continue; 772 } 773 } 774 } 775 776 if ((vals = ldap_get_values(ld, e, a)) != NULL) { 777 for (i = 0; vals[i] != NULL; i++) { 778 if (include_names) { 779 attr_len += strlen(a); 780 } 781 attr_len += strlen(vals[i]) + 782 strlen(DOORLINESEP) + 2; 783 if (include_names) { 784 (void) snprintf(*door_line + 785 strlen(*door_line), 786 attr_len, 787 "%s=%s%s", 788 a, vals[i], 789 DOORLINESEP); 790 } else { 791 (void) snprintf(*door_line + 792 strlen(*door_line), 793 attr_len, 794 "%s%s", 795 vals[i], 796 DOORLINESEP); 797 } 798 } 799 ldap_value_free(vals); 800 } 801 ldap_memfree(a); 802 803 /* Rewind */ 804 if (rewind) { 805 if (ber != NULL) { 806 ber_free(ber, 0); 807 } 808 a = ldap_first_attribute(ld, e, &ber); 809 rewind = 0; 810 } else { 811 a = ldap_next_attribute(ld, e, ber); 812 } 813 } 814 if (ber != NULL) { 815 ber_free(ber, 0); 816 } 817 818 if (e != result_msg) { 819 (void) ldap_msgfree(e); 820 } 821 822 return (NS_LDAP_SUCCESS); 823 } 824 825 /* 826 * This function looks up the base DN of a directory serving 827 * a specified domain name. 828 * 829 * INPUT: 830 * ld - a pointer to an LDAP structure used for the search operation, 831 * domain_name - the name of a domain. 832 * 833 * OUTPUT: 834 * a buffer containing a directory's base DN found. 835 * Should be free'ed by the caller. 836 */ 837 static 838 ns_ldap_return_code 839 getDirBaseDN(LDAP *ld, const char *domain_name, char **dir_base_dn) 840 { 841 struct timeval tv = {NS_DEFAULT_SEARCH_TIMEOUT, 0}; 842 char *attrs[2], *DNlist, *rest, *ptr; 843 char filter[BUFSIZ], *a = NULL; 844 int ldap_rc; 845 LDAPMessage *resultMsg = NULL; 846 ns_ldap_return_code ret_code; 847 848 /* Get the whole list of naming contexts residing on the server */ 849 attrs[0] = "namingcontexts"; 850 attrs[1] = NULL; 851 ldap_rc = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE, "(objectclass=*)", 852 attrs, 0, NULL, NULL, &tv, 0, &resultMsg); 853 switch (ldap_rc) { 854 /* If successful, the root DSE was found. */ 855 case LDAP_SUCCESS: 856 break; 857 /* 858 * If the root DSE was not found, the server does 859 * not comply with the LDAP v3 protocol. 860 */ 861 default: 862 if (resultMsg) { 863 (void) ldap_msgfree(resultMsg); 864 resultMsg = NULL; 865 } 866 867 return (NS_LDAP_OP_FAILED); 868 break; 869 } 870 871 if ((ret_code = convert_to_door_line(ld, 872 resultMsg, 873 DONT_INCLUDE_ATTR_NAMES, 874 NOT_PROFILE, 875 &DNlist)) != NS_LDAP_SUCCESS) { 876 if (resultMsg) { 877 (void) ldap_msgfree(resultMsg); 878 resultMsg = NULL; 879 } 880 return (ret_code); 881 } 882 883 if (resultMsg) { 884 (void) ldap_msgfree(resultMsg); 885 resultMsg = NULL; 886 } 887 888 if (DNlist == NULL || 889 (ptr = strtok_r(DNlist, DOORLINESEP, &rest)) == NULL) { 890 return (NS_LDAP_NOTFOUND); 891 } 892 attrs[0] = "dn"; 893 do { 894 /* 895 * For each context try to find a NIS domain object 896 * which 'nisdomain' attribute's value matches the domain name 897 */ 898 (void) snprintf(filter, 899 BUFSIZ, 900 "(&(objectclass=nisDomainObject)" 901 "(nisdomain=%s))", 902 domain_name); 903 ldap_rc = ldap_search_ext_s(ld, 904 ptr, 905 LDAP_SCOPE_SUBTREE, 906 filter, 907 attrs, 908 0, 909 NULL, 910 NULL, 911 &tv, 912 0, 913 &resultMsg); 914 if (ldap_rc != LDAP_SUCCESS) { 915 if (resultMsg) { 916 (void) ldap_msgfree(resultMsg); 917 resultMsg = NULL; 918 } 919 continue; 920 } 921 if ((a = ldap_get_dn(ld, resultMsg)) != NULL) { 922 *dir_base_dn = strdup(a); 923 ldap_memfree(a); 924 925 if (resultMsg) { 926 (void) ldap_msgfree(resultMsg); 927 resultMsg = NULL; 928 } 929 930 if (!*dir_base_dn) { 931 free(DNlist); 932 return (NS_LDAP_MEMORY); 933 } 934 break; 935 } 936 937 if (resultMsg) { 938 (void) ldap_msgfree(resultMsg); 939 resultMsg = NULL; 940 } 941 } while (ptr = strtok_r(NULL, DOORLINESEP, &rest)); 942 943 free(DNlist); 944 945 if (!*dir_base_dn) { 946 return (NS_LDAP_NOTFOUND); 947 } 948 949 return (NS_LDAP_SUCCESS); 950 } 951 952 /* 953 * This function parses the results of a search operation 954 * requesting a DUAProfile. 955 * 956 * INPUT: 957 * ld - a pointer to an LDAP structure used for the search operation, 958 * dir_base_dn - the name of a directory's base DN, 959 * profile_name - the name of a DUAProfile to be obtained. 960 * 961 * OUTPUT: 962 * a buffer containing the DUAProfile in the following format: 963 * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...] 964 * Should be free'ed by the caller. 965 */ 966 static 967 ns_ldap_return_code 968 getDUAProfile(LDAP *ld, 969 const char *dir_base_dn, 970 const char *profile_name, 971 char **profile) 972 { 973 char searchBaseDN[BUFSIZ], filter[BUFSIZ]; 974 LDAPMessage *resultMsg = NULL; 975 struct timeval tv = {NS_DEFAULT_SEARCH_TIMEOUT, 0}; 976 int ldap_rc; 977 ns_ldap_return_code ret_code; 978 979 (void) snprintf(searchBaseDN, BUFSIZ, "ou=profile,%s", dir_base_dn); 980 (void) snprintf(filter, 981 BUFSIZ, 982 _PROFILE_FILTER, 983 _PROFILE1_OBJECTCLASS, 984 _PROFILE2_OBJECTCLASS, 985 profile_name); 986 ldap_rc = ldap_search_ext_s(ld, 987 searchBaseDN, 988 LDAP_SCOPE_SUBTREE, 989 filter, 990 NULL, 991 0, 992 NULL, 993 NULL, 994 &tv, 995 0, 996 &resultMsg); 997 998 switch (ldap_rc) { 999 /* If successful, the DUA profile was found. */ 1000 case LDAP_SUCCESS: 1001 break; 1002 /* 1003 * If the root DSE was not found, the server does 1004 * not comply with the LDAP v3 protocol. 1005 */ 1006 default: 1007 if (resultMsg) { 1008 (void) ldap_msgfree(resultMsg); 1009 resultMsg = NULL; 1010 } 1011 1012 return (NS_LDAP_OP_FAILED); 1013 break; 1014 } 1015 1016 ret_code = convert_to_door_line(ld, 1017 resultMsg, 1018 INCLUDE_ATTR_NAMES, 1019 IS_PROFILE, 1020 profile); 1021 if (resultMsg) { 1022 (void) ldap_msgfree(resultMsg); 1023 resultMsg = NULL; 1024 } 1025 return (ret_code); 1026 } 1027 1028 /* 1029 * This function derives the directory's base DN from a provided domain name. 1030 * 1031 * INPUT: 1032 * domain_name - the name of a domain to be converted into a base DN, 1033 * buffer - contains the derived base DN, 1034 * buf_len - the length of the buffer. 1035 * 1036 * OUTPUT: 1037 * The function returns the address of the buffer or NULL. 1038 */ 1039 static 1040 char * 1041 domainname2baseDN(char *domain_name, char *buffer, uint16_t buf_len) 1042 { 1043 char *nextDC, *chr; 1044 uint16_t i, length; 1045 1046 if (!domain_name || !buffer || buf_len == 0) { 1047 return (NULL); 1048 } 1049 1050 buffer[0] = '\0'; 1051 nextDC = chr = domain_name; 1052 length = strlen(domain_name); 1053 for (i = 0; i < length + 1; ++i, ++chr) { 1054 /* Simply replace dots with "dc=" */ 1055 if (*chr != '.' && *chr != '\0') { 1056 continue; 1057 } 1058 *chr = '\0'; 1059 if (strlcat(buffer, "dc=", buf_len) >= buf_len) 1060 return (NULL); 1061 if (strlcat(buffer, nextDC, buf_len) >= buf_len) 1062 return (NULL); 1063 if (i < length) { 1064 /* 1065 * The end of the domain name 1066 * has not been reached yet 1067 */ 1068 if (strlcat(buffer, ",", buf_len) >= buf_len) 1069 return (NULL); 1070 nextDC = chr + 1; 1071 *chr = '.'; 1072 } 1073 } 1074 1075 return (buffer); 1076 } 1077 1078 /* 1079 * This function obtains the directory's base DN and a DUAProfile 1080 * from a specified server. 1081 * 1082 * INPUT: 1083 * server - a structure describing a server to connect to and 1084 * a DUAProfile to be obtained from the server, 1085 * cred - credentials to be used during establishing connections to 1086 * the server. 1087 * 1088 * OUTPUT: 1089 * dua_profile - a buffer containing the DUAProfile in the following format: 1090 * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...] 1091 * dir_base_dn - a buffer containing the base DN, 1092 * errorp - an error object describing an error, if any. 1093 * 1094 * All the output data structures should be free'ed by the caller. 1095 */ 1096 ns_ldap_return_code 1097 __ns_ldap_getConnectionInfoFromDUA(const ns_dir_server_t *server, 1098 const ns_cred_t *cred, 1099 char **dua_profile, 1100 char **dir_base_dn, 1101 ns_ldap_error_t **errorp) 1102 { 1103 char serverAddr[MAX_HOSTADDR_LEN]; 1104 char *dirBaseDN = NULL, *duaProfile = NULL; 1105 ns_cred_t default_cred; 1106 ns_ldap_return_code ret_code; 1107 1108 ns_config_t *config_struct = __s_api_create_config(); 1109 ConnectionID sessionId = 0; 1110 Connection *session = NULL; 1111 char errmsg[MAXERROR]; 1112 char buffer[NSS_BUFLEN_HOSTS]; 1113 ns_conn_user_t *cu = NULL; 1114 1115 if (errorp == NULL) { 1116 __s_api_destroy_config(config_struct); 1117 return (NS_LDAP_INVALID_PARAM); 1118 } 1119 1120 *errorp = NULL; 1121 1122 if (server == NULL) { 1123 __s_api_destroy_config(config_struct); 1124 return (NS_LDAP_INVALID_PARAM); 1125 } 1126 1127 if (config_struct == NULL) { 1128 return (NS_LDAP_MEMORY); 1129 } 1130 1131 /* 1132 * If no credentials are specified, try to establish a connection 1133 * as anonymous. 1134 */ 1135 if (!cred) { 1136 default_cred.cred.unix_cred.passwd = NULL; 1137 default_cred.cred.unix_cred.userID = NULL; 1138 default_cred.auth.type = NS_LDAP_AUTH_NONE; 1139 } 1140 1141 /* Now create a default LDAP configuration */ 1142 1143 (void) strncpy(buffer, server->server, sizeof (buffer)); 1144 if (__ns_ldap_setParamValue(config_struct, NS_LDAP_SERVERS_P, buffer, 1145 errorp) != NS_LDAP_SUCCESS) { 1146 __s_api_destroy_config(config_struct); 1147 return (NS_LDAP_CONFIG); 1148 } 1149 1150 /* Put together the address and the port specified by the user app. */ 1151 if (server->port > 0) { 1152 (void) snprintf(serverAddr, 1153 sizeof (serverAddr), 1154 "%s:%hu", 1155 buffer, 1156 server->port); 1157 } else { 1158 (void) strncpy(serverAddr, buffer, sizeof (serverAddr)); 1159 } 1160 1161 /* 1162 * There is no default value for the 'Default Search Base DN' attribute. 1163 * Derive one from the domain name to make __s_api_crosscheck() happy. 1164 */ 1165 if (domainname2baseDN(server->domainName ? 1166 server->domainName : config_struct->domainName, 1167 buffer, NSS_BUFLEN_HOSTS) == NULL) { 1168 (void) snprintf(errmsg, 1169 sizeof (errmsg), 1170 gettext("Can not convert %s into a base DN name"), 1171 server->domainName ? 1172 server->domainName : config_struct->domainName); 1173 MKERROR(LOG_ERR, 1174 *errorp, 1175 NS_LDAP_INTERNAL, 1176 strdup(errmsg), 1177 NS_LDAP_MEMORY); 1178 __s_api_destroy_config(config_struct); 1179 return (NS_LDAP_INTERNAL); 1180 } 1181 if (__ns_ldap_setParamValue(config_struct, NS_LDAP_SEARCH_BASEDN_P, 1182 buffer, errorp) != NS_LDAP_SUCCESS) { 1183 __s_api_destroy_config(config_struct); 1184 return (NS_LDAP_CONFIG); 1185 } 1186 1187 if (__s_api_crosscheck(config_struct, errmsg, B_FALSE) != NS_SUCCESS) { 1188 __s_api_destroy_config(config_struct); 1189 return (NS_LDAP_CONFIG); 1190 } 1191 1192 __s_api_init_config(config_struct); 1193 1194 __s_api_setInitMode(); 1195 1196 cu = __s_api_conn_user_init(NS_CONN_USER_SEARCH, NULL, B_FALSE); 1197 if (cu == NULL) { 1198 return (NS_LDAP_INTERNAL); 1199 } 1200 1201 if ((ret_code = __s_api_getConnection(serverAddr, 1202 NS_LDAP_NEW_CONN, 1203 cred ? cred : &default_cred, 1204 &sessionId, 1205 &session, 1206 errorp, 1207 0, 1208 0, 1209 cu)) != NS_LDAP_SUCCESS) { 1210 __s_api_conn_user_free(cu); 1211 __s_api_unsetInitMode(); 1212 return (ret_code); 1213 } 1214 1215 __s_api_unsetInitMode(); 1216 1217 if ((ret_code = getDirBaseDN(session->ld, 1218 server->domainName ? 1219 server->domainName : 1220 config_struct->domainName, 1221 &dirBaseDN)) != NS_LDAP_SUCCESS) { 1222 (void) snprintf(errmsg, 1223 sizeof (errmsg), 1224 gettext("Can not find the " 1225 "nisDomainObject for domain %s\n"), 1226 server->domainName ? 1227 server->domainName : config_struct->domainName); 1228 MKERROR(LOG_ERR, 1229 *errorp, 1230 ret_code, 1231 strdup(errmsg), 1232 NS_LDAP_MEMORY); 1233 __s_api_conn_user_free(cu); 1234 DropConnection(sessionId, NS_LDAP_NEW_CONN); 1235 return (ret_code); 1236 } 1237 1238 /* 1239 * And here obtain a DUAProfile which will be used 1240 * as a real configuration. 1241 */ 1242 if ((ret_code = getDUAProfile(session->ld, 1243 dirBaseDN, 1244 server->profileName ? 1245 server->profileName : "default", 1246 &duaProfile)) != NS_LDAP_SUCCESS) { 1247 (void) snprintf(errmsg, 1248 sizeof (errmsg), 1249 gettext("Can not find the " 1250 "%s DUAProfile\n"), 1251 server->profileName ? 1252 server->profileName : "default"); 1253 MKERROR(LOG_ERR, 1254 *errorp, 1255 ret_code, 1256 strdup(errmsg), 1257 NS_LDAP_MEMORY); 1258 __s_api_conn_user_free(cu); 1259 DropConnection(sessionId, NS_LDAP_NEW_CONN); 1260 return (ret_code); 1261 } 1262 1263 if (dir_base_dn) { 1264 *dir_base_dn = dirBaseDN; 1265 } else { 1266 free(dirBaseDN); 1267 } 1268 1269 if (dua_profile) { 1270 *dua_profile = duaProfile; 1271 } else { 1272 free(duaProfile); 1273 } 1274 1275 __s_api_conn_user_free(cu); 1276 DropConnection(sessionId, NS_LDAP_NEW_CONN); 1277 1278 return (NS_LDAP_SUCCESS); 1279 } 1280 1281 /* 1282 * This function obtains the root DSE from a specified server. 1283 * 1284 * INPUT: 1285 * server_addr - an adress of a server to be connected to. 1286 * 1287 * OUTPUT: 1288 * root_dse - a buffer containing the root DSE in the following format: 1289 * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...] 1290 * For example: ( here | used as DOORLINESEP for visual purposes) 1291 * supportedControl=1.1.1.1|supportedSASLmechanisms=EXTERNAL 1292 * Should be free'ed by the caller. 1293 */ 1294 ns_ldap_return_code 1295 __ns_ldap_getRootDSE(const char *server_addr, 1296 char **root_dse, 1297 ns_ldap_error_t **errorp, 1298 int anon_fallback) 1299 { 1300 char errmsg[MAXERROR]; 1301 ns_ldap_return_code ret_code; 1302 1303 ConnectionID sessionId = 0; 1304 Connection *session = NULL; 1305 1306 struct timeval tv = {NS_DEFAULT_SEARCH_TIMEOUT, 0}; 1307 char *attrs[3]; 1308 int ldap_rc, ldaperrno = 0; 1309 LDAPMessage *resultMsg = NULL; 1310 void **paramVal = NULL; 1311 1312 ns_cred_t anon; 1313 ns_conn_user_t *cu = NULL; 1314 1315 if (errorp == NULL) { 1316 return (NS_LDAP_INVALID_PARAM); 1317 } 1318 1319 *errorp = NULL; 1320 1321 if (!root_dse) { 1322 return (NS_LDAP_INVALID_PARAM); 1323 } 1324 1325 if (!server_addr) { 1326 return (NS_LDAP_INVALID_PARAM); 1327 } 1328 1329 __s_api_setInitMode(); 1330 1331 cu = __s_api_conn_user_init(NS_CONN_USER_SEARCH, NULL, B_FALSE); 1332 if (cu == NULL) { 1333 return (NS_LDAP_INTERNAL); 1334 } 1335 1336 /* 1337 * All the credentials will be taken from the current 1338 * libsldap configuration. 1339 */ 1340 if ((ret_code = __s_api_getConnection(server_addr, 1341 NS_LDAP_NEW_CONN, 1342 NULL, 1343 &sessionId, 1344 &session, 1345 errorp, 1346 0, 1347 0, 1348 cu)) != NS_LDAP_SUCCESS) { 1349 /* Fallback to anonymous mode is disabled. Stop. */ 1350 if (anon_fallback == 0) { 1351 syslog(LOG_WARNING, 1352 gettext("libsldap: can not get the root DSE from " 1353 " the %s server: %s. " 1354 "Falling back to anonymous disabled.\n"), 1355 server_addr, 1356 errorp && *errorp && (*errorp)->message ? 1357 (*errorp)->message : ""); 1358 if (errorp != NULL && *errorp != NULL) { 1359 (void) __ns_ldap_freeError(errorp); 1360 } 1361 __s_api_unsetInitMode(); 1362 return (ret_code); 1363 } 1364 1365 /* 1366 * Fallback to anonymous, non-SSL mode for backward 1367 * compatibility reasons. This mode should only be used when 1368 * this function (__ns_ldap_getRootDSE) is called from 1369 * ldap_cachemgr(1M). 1370 */ 1371 syslog(LOG_WARNING, 1372 gettext("libsldap: Falling back to anonymous, non-SSL" 1373 " mode for __ns_ldap_getRootDSE. %s\n"), 1374 errorp && *errorp && (*errorp)->message ? 1375 (*errorp)->message : ""); 1376 1377 /* Setup the anon credential for anonymous connection. */ 1378 (void) memset(&anon, 0, sizeof (ns_cred_t)); 1379 anon.auth.type = NS_LDAP_AUTH_NONE; 1380 1381 if (*errorp != NULL) { 1382 (void) __ns_ldap_freeError(errorp); 1383 } 1384 *errorp = NULL; 1385 1386 ret_code = __s_api_getConnection(server_addr, 1387 NS_LDAP_NEW_CONN, 1388 &anon, 1389 &sessionId, 1390 &session, 1391 errorp, 1392 0, 1393 0, 1394 cu); 1395 1396 if (ret_code != NS_LDAP_SUCCESS) { 1397 __s_api_conn_user_free(cu); 1398 __s_api_unsetInitMode(); 1399 return (ret_code); 1400 } 1401 } 1402 1403 __s_api_unsetInitMode(); 1404 1405 /* get search timeout value */ 1406 (void) __ns_ldap_getParam(NS_LDAP_SEARCH_TIME_P, ¶mVal, errorp); 1407 if (paramVal != NULL && *paramVal != NULL) { 1408 tv.tv_sec = **((int **)paramVal); 1409 (void) __ns_ldap_freeParam(¶mVal); 1410 } 1411 if (*errorp != NULL) { 1412 (void) __ns_ldap_freeError(errorp); 1413 } 1414 1415 /* Get root DSE from the server specified by the caller. */ 1416 attrs[0] = "supportedControl"; 1417 attrs[1] = "supportedsaslmechanisms"; 1418 attrs[2] = NULL; 1419 ldap_rc = ldap_search_ext_s(session->ld, 1420 "", 1421 LDAP_SCOPE_BASE, 1422 "(objectclass=*)", 1423 attrs, 1424 0, 1425 NULL, 1426 NULL, 1427 &tv, 1428 0, 1429 &resultMsg); 1430 1431 if (ldap_rc != LDAP_SUCCESS) { 1432 /* 1433 * If the root DSE was not found, the server does 1434 * not comply with the LDAP v3 protocol. 1435 */ 1436 (void) ldap_get_option(session->ld, 1437 LDAP_OPT_ERROR_NUMBER, 1438 &ldaperrno); 1439 (void) snprintf(errmsg, 1440 sizeof (errmsg), 1441 gettext(ldap_err2string(ldaperrno))); 1442 MKERROR(LOG_ERR, 1443 *errorp, 1444 NS_LDAP_OP_FAILED, 1445 strdup(errmsg), 1446 NS_LDAP_MEMORY); 1447 1448 if (resultMsg) { 1449 (void) ldap_msgfree(resultMsg); 1450 resultMsg = NULL; 1451 } 1452 1453 __s_api_conn_user_free(cu); 1454 return (NS_LDAP_OP_FAILED); 1455 } 1456 __s_api_conn_user_free(cu); 1457 1458 ret_code = convert_to_door_line(session->ld, 1459 resultMsg, 1460 INCLUDE_ATTR_NAMES, 1461 NOT_PROFILE, 1462 root_dse); 1463 if (ret_code == NS_LDAP_NOTFOUND) { 1464 (void) snprintf(errmsg, 1465 sizeof (errmsg), 1466 gettext("No root DSE data " 1467 "for server %s returned."), 1468 server_addr); 1469 MKERROR(LOG_ERR, 1470 *errorp, 1471 NS_LDAP_NOTFOUND, 1472 strdup(errmsg), 1473 NS_LDAP_MEMORY); 1474 } 1475 1476 if (resultMsg) { 1477 (void) ldap_msgfree(resultMsg); 1478 resultMsg = NULL; 1479 } 1480 1481 DropConnection(sessionId, NS_LDAP_NEW_CONN); 1482 1483 return (ret_code); 1484 } 1485 1486 /* 1487 * This function destroys the local list of root DSEs. The input parameter is 1488 * a pointer to the list to be erased. 1489 * The type of the pointer passed to this function should be 1490 * (dir_server_list_t *). 1491 */ 1492 static 1493 void * 1494 disposeOfOldList(void *param) 1495 { 1496 dir_server_list_t *old_list = (dir_server_list_t *)param; 1497 long i = 0, j; 1498 1499 (void) rw_wrlock(&old_list->listDestroyLock); 1500 /* Destroy the old list */ 1501 while (old_list->nsServers[i]) { 1502 free(old_list->nsServers[i]->ip); 1503 j = 0; 1504 while (old_list->nsServers[i]->controls && 1505 old_list->nsServers[i]->controls[j]) { 1506 free(old_list->nsServers[i]->controls[j]); 1507 ++j; 1508 } 1509 free(old_list->nsServers[i]->controls); 1510 j = 0; 1511 while (old_list->nsServers[i]->saslMech && 1512 old_list->nsServers[i]->saslMech[j]) { 1513 free(old_list->nsServers[i]->saslMech[j]); 1514 ++j; 1515 } 1516 free(old_list->nsServers[i]->saslMech); 1517 ++i; 1518 } 1519 /* 1520 * All the structures pointed by old_list->nsServers were allocated 1521 * in one chunck. The nsServers[0] pointer points to the beginning 1522 * of that chunck. 1523 */ 1524 free(old_list->nsServers[0]); 1525 free(old_list->nsServers); 1526 (void) rw_unlock(&old_list->listDestroyLock); 1527 (void) rwlock_destroy(&old_list->listDestroyLock); 1528 free(old_list); 1529 1530 return (NULL); 1531 } 1532 1533 /* 1534 * This function cancels the Standalone mode and destroys the list of root DSEs. 1535 */ 1536 void 1537 __ns_ldap_cancelStandalone(void) 1538 { 1539 dir_server_list_t *old_list; 1540 1541 (void) mutex_lock(&dir_servers.listReplaceLock); 1542 dir_servers.standalone = 0; 1543 if (!dir_servers.list) { 1544 (void) mutex_unlock(&dir_servers.listReplaceLock); 1545 return; 1546 } 1547 old_list = dir_servers.list; 1548 dir_servers.list = NULL; 1549 (void) mutex_unlock(&dir_servers.listReplaceLock); 1550 1551 (void) disposeOfOldList(old_list); 1552 } 1553 1554 1555 static 1556 void* 1557 create_ns_servers_entry(void *param) 1558 { 1559 #define CHUNK_SIZE 16 1560 1561 dir_server_t *server = (dir_server_t *)param; 1562 ns_ldap_return_code *retCode = calloc(1, 1563 sizeof (ns_ldap_return_code)); 1564 uint32_t sc_counter = 0, sm_counter = 0; 1565 uint32_t sc_mem_blocks = 1, sm_mem_blocks = 1; 1566 char *rootDSE = NULL, *attr, *val, *rest, **ptr; 1567 ns_ldap_error_t *error = NULL; 1568 1569 if (retCode == NULL) { 1570 return (NULL); 1571 } 1572 1573 /* 1574 * We call this function in non anon-fallback mode because we 1575 * want the whole procedure to fail as soon as possible to 1576 * indicate there are problems with connecting to the server. 1577 */ 1578 *retCode = __ns_ldap_getRootDSE(server->ip, 1579 &rootDSE, 1580 &error, 1581 SA_ALLOW_FALLBACK); 1582 1583 if (*retCode == NS_LDAP_MEMORY) { 1584 free(retCode); 1585 return (NULL); 1586 } 1587 1588 /* 1589 * If the root DSE can not be obtained, log an error and keep the 1590 * server. 1591 */ 1592 if (*retCode != NS_LDAP_SUCCESS) { 1593 server->status = INFO_SERVER_ERROR; 1594 syslog(LOG_WARNING, 1595 gettext("libsldap (\"standalone\" mode): " 1596 "can not obtain the root DSE from %s. %s"), 1597 server->ip, 1598 error && error->message ? error->message : ""); 1599 if (error) { 1600 (void) __ns_ldap_freeError(&error); 1601 } 1602 return (retCode); 1603 } 1604 1605 /* Get the first attribute of the root DSE. */ 1606 attr = strtok_r(rootDSE, DOORLINESEP, &rest); 1607 if (attr == NULL) { 1608 free(rootDSE); 1609 server->status = INFO_SERVER_ERROR; 1610 syslog(LOG_WARNING, 1611 gettext("libsldap (\"standalone\" mode): " 1612 "the root DSE from %s is empty or corrupted."), 1613 server->ip); 1614 *retCode = NS_LDAP_INTERNAL; 1615 return (retCode); 1616 } 1617 1618 server->controls = (char **)calloc(CHUNK_SIZE, sizeof (char *)); 1619 server->saslMech = (char **)calloc(CHUNK_SIZE, sizeof (char *)); 1620 if (server->controls == NULL || server->saslMech == NULL) { 1621 free(rootDSE); 1622 free(retCode); 1623 return (NULL); 1624 } 1625 1626 do { 1627 if ((val = strchr(attr, '=')) == NULL) { 1628 continue; 1629 } 1630 ++val; 1631 1632 if (strncasecmp(attr, 1633 _SASLMECHANISM, 1634 _SASLMECHANISM_LEN) == 0) { 1635 if (sm_counter == CHUNK_SIZE * sm_mem_blocks - 1) { 1636 ptr = (char **)realloc(server->saslMech, 1637 CHUNK_SIZE * 1638 ++sm_mem_blocks * 1639 sizeof (char *)); 1640 if (ptr == NULL) { 1641 *retCode = NS_LDAP_MEMORY; 1642 break; 1643 } 1644 bzero((char *)ptr + 1645 (sm_counter + 1) * 1646 sizeof (char *), 1647 CHUNK_SIZE * 1648 sm_mem_blocks * 1649 sizeof (char *) - 1650 (sm_counter + 1) * 1651 sizeof (char *)); 1652 server->saslMech = ptr; 1653 } 1654 server->saslMech[sm_counter] = strdup(val); 1655 if (server->saslMech[sm_counter] == NULL) { 1656 *retCode = NS_LDAP_MEMORY; 1657 break; 1658 } 1659 ++sm_counter; 1660 continue; 1661 } 1662 if (strncasecmp(attr, 1663 _SUPPORTEDCONTROL, 1664 _SUPPORTEDCONTROL_LEN) == 0) { 1665 if (sc_counter == CHUNK_SIZE * sc_mem_blocks - 1) { 1666 ptr = (char **)realloc(server->controls, 1667 CHUNK_SIZE * 1668 ++sc_mem_blocks * 1669 sizeof (char *)); 1670 if (ptr == NULL) { 1671 *retCode = NS_LDAP_MEMORY; 1672 break; 1673 } 1674 bzero((char *)ptr + 1675 (sc_counter + 1) * 1676 sizeof (char *), 1677 CHUNK_SIZE * 1678 sc_mem_blocks * 1679 sizeof (char *) - 1680 (sc_counter + 1) * 1681 sizeof (char *)); 1682 server->controls = ptr; 1683 } 1684 1685 server->controls[sc_counter] = strdup(val); 1686 if (server->controls[sc_counter] == NULL) { 1687 *retCode = NS_LDAP_MEMORY; 1688 break; 1689 } 1690 ++sc_counter; 1691 continue; 1692 } 1693 1694 } while (attr = strtok_r(NULL, DOORLINESEP, &rest)); 1695 1696 free(rootDSE); 1697 1698 if (*retCode == NS_LDAP_MEMORY) { 1699 free(retCode); 1700 return (NULL); 1701 } 1702 1703 server->controls[sc_counter] = NULL; 1704 server->saslMech[sm_counter] = NULL; 1705 1706 server->status = INFO_SERVER_UP; 1707 1708 return (retCode); 1709 #undef CHUNK_SIZE 1710 } 1711 1712 1713 /* 1714 * This function creates a new local list of root DSEs from all the servers 1715 * mentioned in the DUAProfile (or local NS BEC) and returns 1716 * a pointer to the list. 1717 */ 1718 static 1719 ns_ldap_return_code 1720 createDirServerList(dir_server_list_t **new_list, 1721 ns_ldap_error_t **errorp) 1722 { 1723 char **serverList; 1724 ns_ldap_return_code retCode = NS_LDAP_SUCCESS; 1725 dir_server_t *tmpSrvArray; 1726 long srvListLength, i; 1727 thread_t *thrPool, thrID; 1728 void *status = NULL; 1729 1730 if (errorp == NULL) { 1731 return (NS_LDAP_INVALID_PARAM); 1732 } 1733 1734 *errorp = NULL; 1735 1736 if (new_list == NULL) { 1737 return (NS_LDAP_INVALID_PARAM); 1738 } 1739 1740 retCode = __s_api_getServers(&serverList, errorp); 1741 if (retCode != NS_LDAP_SUCCESS || serverList == NULL) { 1742 return (retCode); 1743 } 1744 1745 for (i = 0; serverList[i]; ++i) { 1746 ; 1747 } 1748 srvListLength = i; 1749 1750 thrPool = calloc(srvListLength, sizeof (thread_t)); 1751 if (thrPool == NULL) { 1752 __s_api_free2dArray(serverList); 1753 return (NS_LDAP_MEMORY); 1754 } 1755 1756 *new_list = (dir_server_list_t *)calloc(1, 1757 sizeof (dir_server_list_t)); 1758 if (*new_list == NULL) { 1759 __s_api_free2dArray(serverList); 1760 free(thrPool); 1761 return (NS_LDAP_MEMORY); 1762 } 1763 (void) rwlock_init(&(*new_list)->listDestroyLock, USYNC_THREAD, NULL); 1764 1765 (*new_list)->nsServers = (dir_server_t **)calloc(srvListLength + 1, 1766 sizeof (dir_server_t *)); 1767 if ((*new_list)->nsServers == NULL) { 1768 free(*new_list); 1769 *new_list = NULL; 1770 __s_api_free2dArray(serverList); 1771 free(thrPool); 1772 return (NS_LDAP_MEMORY); 1773 } 1774 1775 /* 1776 * Allocate a set of dir_server_t structures as an array, 1777 * with one alloc call and then initialize the nsServers pointers 1778 * with the addresses of the array's members. 1779 */ 1780 tmpSrvArray = (dir_server_t *)calloc(srvListLength, 1781 sizeof (dir_server_t)); 1782 for (i = 0; i < srvListLength; ++i) { 1783 (*new_list)->nsServers[i] = &tmpSrvArray[i]; 1784 1785 (*new_list)->nsServers[i]->info = INFO_STATUS_NEW; 1786 (void) mutex_init(&(*new_list)->nsServers[i]->updateStatus, 1787 USYNC_THREAD, 1788 NULL); 1789 1790 (*new_list)->nsServers[i]->ip = strdup(serverList[i]); 1791 if ((*new_list)->nsServers[i]->ip == NULL) { 1792 retCode = NS_LDAP_MEMORY; 1793 break; 1794 } 1795 1796 (*new_list)->nsServers[i]->status = INFO_SERVER_CONNECTING; 1797 1798 switch (thr_create(NULL, 1799 0, 1800 create_ns_servers_entry, 1801 (*new_list)->nsServers[i], 1802 0, 1803 &thrID)) { 1804 case EAGAIN: 1805 (*new_list)->nsServers[i]->status = 1806 INFO_SERVER_ERROR; 1807 continue; 1808 break; 1809 case ENOMEM: 1810 (*new_list)->nsServers[i]->status = 1811 INFO_SERVER_ERROR; 1812 continue; 1813 break; 1814 default: 1815 thrPool[i] = thrID; 1816 continue; 1817 break; 1818 } 1819 } 1820 1821 for (i = 0; i < srvListLength; ++i) { 1822 if (thrPool[i] != 0 && 1823 thr_join(thrPool[i], NULL, &status) == 0) { 1824 if (status == NULL) { 1825 /* 1826 * Some memory allocation problems occured. Just 1827 * ignore the server and hope there will be some 1828 * other good ones. 1829 */ 1830 (*new_list)->nsServers[i]->status = 1831 INFO_SERVER_ERROR; 1832 } 1833 free(status); 1834 } 1835 } 1836 1837 __s_api_free2dArray(serverList); 1838 free(thrPool); 1839 1840 if (retCode == NS_LDAP_MEMORY) { 1841 (void) disposeOfOldList(*new_list); 1842 return (NS_LDAP_MEMORY); 1843 } 1844 1845 return (NS_LDAP_SUCCESS); 1846 } 1847 1848 /* 1849 * This functions replaces the local list of root DSEs with a new one and starts 1850 * a thread destroying the old list. There is no need for other threads to wait 1851 * until the old list will be destroyed. 1852 * Since it is possible that more than one thread can start creating the list, 1853 * this function should be protected by mutexes to be sure that only one thread 1854 * performs the initialization. 1855 */ 1856 static 1857 ns_ldap_return_code 1858 initGlobalList(ns_ldap_error_t **error) 1859 { 1860 dir_server_list_t *new_list, *old_list; 1861 ns_ldap_return_code ret_code; 1862 thread_t tid; 1863 1864 ret_code = createDirServerList(&new_list, error); 1865 if (ret_code != NS_LDAP_SUCCESS) { 1866 return (ret_code); 1867 } 1868 1869 old_list = dir_servers.list; 1870 dir_servers.list = new_list; 1871 1872 if (old_list) { 1873 (void) thr_create(NULL, 1874 0, 1875 disposeOfOldList, 1876 old_list, 1877 THR_DETACHED, 1878 &tid); 1879 } 1880 1881 return (NS_LDAP_SUCCESS); 1882 } 1883 1884 static 1885 struct { 1886 char *authMech; 1887 ns_auth_t auth; 1888 } authArray[] = {{"none", {NS_LDAP_AUTH_NONE, 1889 NS_LDAP_TLS_NONE, 1890 NS_LDAP_SASL_NONE, 1891 NS_LDAP_SASLOPT_NONE}}, 1892 {"simple", {NS_LDAP_AUTH_SIMPLE, 1893 NS_LDAP_TLS_NONE, 1894 NS_LDAP_SASL_NONE, 1895 NS_LDAP_SASLOPT_NONE}}, 1896 {"tls:simple", {NS_LDAP_AUTH_TLS, 1897 NS_LDAP_TLS_SIMPLE, 1898 NS_LDAP_SASL_NONE, 1899 NS_LDAP_SASLOPT_NONE}}, 1900 {"tls:sasl/CRAM-MD5", {NS_LDAP_AUTH_TLS, 1901 NS_LDAP_TLS_SASL, 1902 NS_LDAP_SASL_CRAM_MD5, 1903 NS_LDAP_SASLOPT_NONE}}, 1904 {"tls:sasl/DIGEST-MD5", {NS_LDAP_AUTH_TLS, 1905 NS_LDAP_TLS_SASL, 1906 NS_LDAP_SASL_DIGEST_MD5, 1907 NS_LDAP_SASLOPT_NONE}}, 1908 {"sasl/CRAM-MD5", {NS_LDAP_AUTH_SASL, 1909 NS_LDAP_TLS_SASL, 1910 NS_LDAP_SASL_CRAM_MD5, 1911 NS_LDAP_SASLOPT_NONE}}, 1912 {"sasl/DIGEST-MD5", {NS_LDAP_AUTH_SASL, 1913 NS_LDAP_TLS_SASL, 1914 NS_LDAP_SASL_DIGEST_MD5, 1915 NS_LDAP_SASLOPT_NONE}}, 1916 {"sasl/GSSAPI", {NS_LDAP_AUTH_SASL, 1917 NS_LDAP_TLS_SASL, 1918 NS_LDAP_SASL_GSSAPI, 1919 NS_LDAP_SASLOPT_PRIV | NS_LDAP_SASLOPT_INT}}, 1920 {NULL, {NS_LDAP_AUTH_NONE, 1921 NS_LDAP_TLS_NONE, 1922 NS_LDAP_SASL_NONE, 1923 NS_LDAP_SASLOPT_NONE}}}; 1924 1925 ns_ldap_return_code 1926 __ns_ldap_initAuth(const char *auth_mech, 1927 ns_auth_t *auth, 1928 ns_ldap_error_t **errorp) 1929 { 1930 uint32_t i; 1931 char errmsg[MAXERROR]; 1932 1933 if (auth_mech == NULL) { 1934 (void) snprintf(errmsg, 1935 sizeof (errmsg), 1936 gettext("Invalid authentication method specified\n")); 1937 MKERROR(LOG_WARNING, 1938 *errorp, 1939 NS_LDAP_INTERNAL, 1940 strdup(errmsg), 1941 NS_LDAP_MEMORY); 1942 return (NS_LDAP_INTERNAL); 1943 } 1944 1945 for (i = 0; authArray[i].authMech != NULL; ++i) { 1946 if (strcasecmp(auth_mech, authArray[i].authMech) == 0) { 1947 *auth = authArray[i].auth; 1948 return (NS_LDAP_SUCCESS); 1949 } 1950 } 1951 1952 (void) snprintf(errmsg, 1953 sizeof (errmsg), 1954 gettext("Invalid authentication method specified\n")); 1955 MKERROR(LOG_WARNING, 1956 *errorp, 1957 NS_LDAP_INTERNAL, 1958 strdup(errmsg), 1959 NS_LDAP_MEMORY); 1960 return (NS_LDAP_INTERNAL); 1961 } 1962 1963 /* 1964 * This function "informs" libsldap that a client application has specified 1965 * a directory to use. The function obtains a DUAProfile, credentials, 1966 * and naming context. During all further operations on behalf 1967 * of the application requested a standalone schema libsldap will use 1968 * the information obtained by __ns_ldap_initStandalone() instead of 1969 * door_call(3C)ing ldap_cachemgr(1M). 1970 * 1971 * INPUT: 1972 * sa_conf - a structure describing where and in which way to obtain all 1973 * the configuration describing how to communicate to 1974 * a choosen LDAP directory, 1975 * errorp - an error object describing an error occured. 1976 */ 1977 ns_ldap_return_code 1978 __ns_ldap_initStandalone(const ns_standalone_conf_t *sa_conf, 1979 ns_ldap_error_t **errorp) { 1980 1981 ns_cred_t user_cred = {{NS_LDAP_AUTH_NONE, 1982 NS_LDAP_TLS_NONE, 1983 NS_LDAP_SASL_NONE, 1984 NS_LDAP_SASLOPT_NONE}, 1985 NULL, 1986 {NULL, NULL}}; 1987 char *dua_profile = NULL; 1988 char errmsg[MAXERROR]; 1989 ns_config_t *cfg; 1990 int ret_code; 1991 1992 if (sa_conf->SA_BIND_DN == NULL && sa_conf->SA_BIND_PWD != NULL || 1993 sa_conf->SA_BIND_DN != NULL && sa_conf->SA_BIND_PWD == NULL) { 1994 (void) snprintf(errmsg, 1995 sizeof (errmsg), 1996 gettext("Bind DN and bind password" 1997 " must both be provided\n")); 1998 MKERROR(LOG_ERR, 1999 *errorp, 2000 NS_CONFIG_NOTLOADED, 2001 strdup(errmsg), 2002 NS_LDAP_MEMORY); 2003 return (NS_LDAP_INTERNAL); 2004 } 2005 2006 switch (sa_conf->type) { 2007 case NS_LDAP_SERVER: 2008 if (sa_conf->SA_BIND_DN != NULL) { 2009 user_cred.cred.unix_cred.userID = sa_conf->SA_BIND_DN; 2010 user_cred.auth.type = NS_LDAP_AUTH_SIMPLE; 2011 } 2012 2013 if (sa_conf->SA_BIND_PWD != NULL) { 2014 user_cred.cred.unix_cred.passwd = sa_conf->SA_BIND_PWD; 2015 } 2016 2017 if (sa_conf->SA_AUTH != NULL) { 2018 user_cred.auth.type = sa_conf->SA_AUTH->type; 2019 user_cred.auth.tlstype = sa_conf->SA_AUTH->tlstype; 2020 user_cred.auth.saslmech = sa_conf->SA_AUTH->saslmech; 2021 user_cred.auth.saslopt = sa_conf->SA_AUTH->saslopt; 2022 } 2023 2024 if (sa_conf->SA_CERT_PATH != NULL) { 2025 user_cred.hostcertpath = sa_conf->SA_CERT_PATH; 2026 } 2027 2028 ret_code = __ns_ldap_getConnectionInfoFromDUA( 2029 &sa_conf->ds_profile.server, 2030 &user_cred, 2031 &dua_profile, 2032 NULL, 2033 errorp); 2034 if (ret_code != NS_LDAP_SUCCESS) { 2035 return (ret_code); 2036 } 2037 2038 cfg = __s_api_create_config_door_str(dua_profile, errorp); 2039 if (cfg == NULL) { 2040 free(dua_profile); 2041 return (NS_LDAP_CONFIG); 2042 } 2043 2044 if (sa_conf->SA_CERT_PATH != NULL) { 2045 char *certPathAttr; 2046 ParamIndexType type; 2047 2048 switch (cfg->version) { 2049 case NS_LDAP_V1: 2050 certPathAttr = "NS_LDAP_CERT_PATH"; 2051 break; 2052 default: /* Version 2 */ 2053 certPathAttr = "NS_LDAP_HOST_CERTPATH"; 2054 break; 2055 } 2056 2057 if (__s_api_get_versiontype(cfg, 2058 certPathAttr, 2059 &type) == 0 && 2060 (ret_code = __ns_ldap_setParamValue(cfg, 2061 type, 2062 sa_conf->SA_CERT_PATH, 2063 errorp)) != NS_LDAP_SUCCESS) { 2064 __s_api_destroy_config(cfg); 2065 return (ret_code); 2066 } 2067 } 2068 2069 if (sa_conf->SA_BIND_DN != NULL && 2070 sa_conf->SA_BIND_PWD != NULL) { 2071 char *authMethods; 2072 2073 authMethods = __s_api_strValue(cfg, NS_LDAP_AUTH_P, 2074 NS_FILE_FMT); 2075 if (authMethods != NULL && 2076 strstr(authMethods, "sasl/GSSAPI") != NULL) { 2077 /* 2078 * The received DUAProfile specifies 2079 * sasl/GSSAPI as an auth. mechanism. 2080 * The bind DN and password will be 2081 * ignored. 2082 */ 2083 syslog(LOG_INFO, gettext("sasl/GSSAPI will be " 2084 "used as an authentication method. " 2085 "The bind DN and password will " 2086 "be ignored.\n")); 2087 free(authMethods); 2088 break; 2089 } 2090 2091 if (authMethods != NULL) 2092 free(authMethods); 2093 2094 if (__ns_ldap_setParamValue(cfg, 2095 NS_LDAP_BINDDN_P, 2096 sa_conf->SA_BIND_DN, 2097 errorp) != NS_LDAP_SUCCESS) { 2098 __s_api_destroy_config(cfg); 2099 return (NS_LDAP_CONFIG); 2100 } 2101 2102 if (__ns_ldap_setParamValue(cfg, 2103 NS_LDAP_BINDPASSWD_P, 2104 sa_conf->SA_BIND_PWD, 2105 errorp) != NS_LDAP_SUCCESS) { 2106 __s_api_destroy_config(cfg); 2107 return (NS_LDAP_CONFIG); 2108 } 2109 } 2110 2111 break; 2112 default: /* NS_CACHEMGR */ 2113 return (NS_LDAP_SUCCESS); 2114 } 2115 2116 __s_api_init_config(cfg); 2117 /* Connection management should use the new config now. */ 2118 __s_api_reinit_conn_mgmt_new_config(cfg); 2119 __ns_ldap_setServer(TRUE); 2120 2121 (void) mutex_lock(&dir_servers.listReplaceLock); 2122 if ((ret_code = initGlobalList(errorp)) != NS_SUCCESS) { 2123 (void) mutex_unlock(&dir_servers.listReplaceLock); 2124 return (ret_code); 2125 } 2126 dir_servers.standalone = 1; 2127 (void) mutex_unlock(&dir_servers.listReplaceLock); 2128 2129 return (NS_LDAP_SUCCESS); 2130 } 2131 2132 /* 2133 * INPUT: 2134 * serverAddr is the address of a server and 2135 * request is one of the following: 2136 * NS_CACHE_NEW: get a new server address, addr is ignored. 2137 * NS_CACHE_NORESP: get the next one, remove addr from list. 2138 * NS_CACHE_NEXT: get the next one, keep addr on list. 2139 * NS_CACHE_WRITE: get a non-replica server, if possible, if not, same 2140 * as NS_CACHE_NEXT. 2141 * addrType: 2142 * NS_CACHE_ADDR_IP: return server address as is, this is default. 2143 * NS_CACHE_ADDR_HOSTNAME: return server addess as FQDN format, only 2144 * self credential case requires such format. 2145 * OUTPUT: 2146 * ret 2147 * 2148 * a structure of type ns_server_info_t containing the server address 2149 * or name, server controls and supported SASL mechanisms. 2150 * NOTE: Caller should allocate space for the structure and free 2151 * all the space allocated by the function for the information contained 2152 * in the structure. 2153 * 2154 * error - an error object describing an error, if any. 2155 */ 2156 ns_ldap_return_code 2157 __s_api_findRootDSE(const char *request, 2158 const char *serverAddr, 2159 const char *addrType, 2160 ns_server_info_t *ret, 2161 ns_ldap_error_t **error) 2162 { 2163 dir_server_list_t *current_list = NULL; 2164 ns_ldap_return_code ret_code; 2165 long i = 0; 2166 int matched = FALSE; 2167 dir_server_t *server = NULL; 2168 char errmsg[MAXERROR]; 2169 2170 (void) mutex_lock(&dir_servers.listReplaceLock); 2171 if (dir_servers.list == NULL) { 2172 (void) mutex_unlock(&dir_servers.listReplaceLock); 2173 (void) snprintf(errmsg, 2174 sizeof (errmsg), 2175 gettext("The list of root DSEs is empty: " 2176 "the Standalone mode was not properly initialized")); 2177 MKERROR(LOG_ERR, 2178 *error, 2179 NS_CONFIG_NOTLOADED, 2180 strdup(errmsg), 2181 NS_LDAP_MEMORY); 2182 return (NS_LDAP_INTERNAL); 2183 } 2184 2185 current_list = dir_servers.list; 2186 (void) rw_rdlock(¤t_list->listDestroyLock); 2187 (void) mutex_unlock(&dir_servers.listReplaceLock); 2188 2189 /* 2190 * The code below is mostly the clone of the 2191 * ldap_cachemgr::cachemgr_getldap.c::getldap_get_serverInfo() function. 2192 * Currently we have two different server lists: one is maintained 2193 * by libsldap ('standalone' mode), the other is in ldap_cachemgr 2194 * (a part of its standard functionality). 2195 */ 2196 2197 /* 2198 * If NS_CACHE_NEW, or the server info is new, 2199 * starts from the beginning of the list. 2200 */ 2201 (void) mutex_lock(¤t_list->nsServers[0]->updateStatus); 2202 if (strcmp(request, NS_CACHE_NEW) == 0 || 2203 current_list->nsServers[0]->info == INFO_STATUS_NEW) { 2204 matched = TRUE; 2205 } 2206 (void) mutex_unlock(¤t_list->nsServers[i]->updateStatus); 2207 2208 for (i = 0; current_list->nsServers[i]; ++i) { 2209 /* 2210 * Lock the updateStatus mutex to 2211 * make sure the server status stays the same 2212 * while the data is being processed. 2213 */ 2214 if (matched == FALSE && 2215 strcmp(current_list->nsServers[i]->ip, 2216 serverAddr) == 0) { 2217 matched = TRUE; 2218 if (strcmp(request, NS_CACHE_NORESP) == 0) { 2219 2220 /* 2221 * if the server has already been removed, 2222 * don't bother. 2223 */ 2224 (void) mutex_lock(¤t_list-> 2225 nsServers[i]->updateStatus); 2226 if (current_list->nsServers[i]->status == 2227 INFO_SERVER_REMOVED) { 2228 (void) mutex_unlock(¤t_list-> 2229 nsServers[i]-> 2230 updateStatus); 2231 continue; 2232 } 2233 (void) mutex_unlock(¤t_list-> 2234 nsServers[i]-> 2235 updateStatus); 2236 2237 /* 2238 * if the information is new, 2239 * give this server one more chance. 2240 */ 2241 (void) mutex_lock(¤t_list-> 2242 nsServers[i]-> 2243 updateStatus); 2244 if (current_list->nsServers[i]->info == 2245 INFO_STATUS_NEW && 2246 current_list->nsServers[i]->status == 2247 INFO_SERVER_UP) { 2248 server = current_list->nsServers[i]; 2249 (void) mutex_unlock(¤t_list-> 2250 nsServers[i]-> 2251 updateStatus); 2252 break; 2253 } else { 2254 /* 2255 * it is recommended that 2256 * before removing the 2257 * server from the list, 2258 * the server should be 2259 * contacted one more time 2260 * to make sure that it is 2261 * really unavailable. 2262 * For now, just trust the client 2263 * (i.e., the sldap library) 2264 * that it knows what it is 2265 * doing and would not try 2266 * to mess up the server 2267 * list. 2268 */ 2269 current_list->nsServers[i]->status = 2270 INFO_SERVER_REMOVED; 2271 (void) mutex_unlock(¤t_list-> 2272 nsServers[i]-> 2273 updateStatus); 2274 continue; 2275 } 2276 } else { 2277 /* 2278 * req == NS_CACHE_NEXT or NS_CACHE_WRITE 2279 */ 2280 continue; 2281 } 2282 } 2283 2284 if (matched) { 2285 if (strcmp(request, NS_CACHE_WRITE) == 0) { 2286 /* 2287 * ldap_cachemgr checks here if the server 2288 * is not a non-replica server (a server 2289 * of type INFO_RW_WRITEABLE). But currently 2290 * it considers all the servers in its list 2291 * as those. 2292 */ 2293 (void) mutex_lock(¤t_list-> 2294 nsServers[i]-> 2295 updateStatus); 2296 if (current_list->nsServers[i]->status == 2297 INFO_SERVER_UP) { 2298 (void) mutex_unlock(¤t_list-> 2299 nsServers[i]-> 2300 updateStatus); 2301 server = current_list->nsServers[i]; 2302 break; 2303 } 2304 } else { 2305 (void) mutex_lock(¤t_list-> 2306 nsServers[i]-> 2307 updateStatus); 2308 if (current_list->nsServers[i]->status == 2309 INFO_SERVER_UP) { 2310 (void) mutex_unlock(¤t_list-> 2311 nsServers[i]-> 2312 updateStatus); 2313 server = current_list->nsServers[i]; 2314 break; 2315 } 2316 } 2317 2318 (void) mutex_unlock(¤t_list-> 2319 nsServers[i]-> 2320 updateStatus); 2321 } 2322 } 2323 2324 if (server == NULL) { 2325 (void) rw_unlock(¤t_list->listDestroyLock); 2326 (void) snprintf(errmsg, 2327 sizeof (errmsg), 2328 gettext("No servers are available")); 2329 MKERROR(LOG_ERR, 2330 *error, 2331 NS_CONFIG_NOTLOADED, 2332 strdup(errmsg), 2333 NS_LDAP_MEMORY); 2334 return (NS_LDAP_NOTFOUND); 2335 } 2336 2337 (void) mutex_lock(&server->updateStatus); 2338 server->info = INFO_STATUS_OLD; 2339 (void) mutex_unlock(&server->updateStatus); 2340 2341 if (ret == NULL) { 2342 (void) rw_unlock(¤t_list->listDestroyLock); 2343 return (NS_LDAP_SUCCESS); 2344 } 2345 2346 if (strcmp(addrType, NS_CACHE_ADDR_HOSTNAME) == 0) { 2347 ret_code = __s_api_ip2hostname(server->ip, &ret->serverFQDN); 2348 if (ret_code != NS_LDAP_SUCCESS) { 2349 (void) snprintf(errmsg, 2350 sizeof (errmsg), 2351 gettext("The %s address " 2352 "can not be resolved into " 2353 "a host name. Returning " 2354 "the address as it is."), 2355 server->ip); 2356 MKERROR(LOG_ERR, 2357 *error, 2358 NS_CONFIG_NOTLOADED, 2359 strdup(errmsg), 2360 NS_LDAP_MEMORY); 2361 return (NS_LDAP_INTERNAL); 2362 } 2363 } 2364 2365 ret->server = strdup(server->ip); 2366 2367 ret->controls = __s_api_cp2dArray(server->controls); 2368 ret->saslMechanisms = __s_api_cp2dArray(server->saslMech); 2369 2370 (void) rw_unlock(¤t_list->listDestroyLock); 2371 2372 return (NS_LDAP_SUCCESS); 2373 } 2374 2375 /* 2376 * This function iterates through the list of the configured LDAP servers 2377 * and "pings" those which are marked as removed or if any error occurred 2378 * during the previous receiving of the server's root DSE. If the 2379 * function is able to reach such a server and get its root DSE, it 2380 * marks the server as on-line. Otherwise, the server's status is set 2381 * to "Error". 2382 * For each server the function tries to connect to, it fires up 2383 * a separate thread and then waits until all the treads finish. 2384 * The function returns NS_LDAP_INTERNAL if the Standalone mode was not 2385 * initialized or was canceled prior to an invocation of 2386 * __ns_ldap_pingOfflineServers(). 2387 */ 2388 ns_ldap_return_code 2389 __ns_ldap_pingOfflineServers(void) 2390 { 2391 dir_server_list_t *current_list = NULL; 2392 ns_ldap_return_code retCode = NS_LDAP_SUCCESS; 2393 long srvListLength, i = 0; 2394 thread_t *thrPool, thrID; 2395 void *status = NULL; 2396 2397 (void) mutex_lock(&dir_servers.listReplaceLock); 2398 if (dir_servers.list == NULL) { 2399 (void) mutex_unlock(&dir_servers.listReplaceLock); 2400 return (NS_LDAP_INTERNAL); 2401 } 2402 2403 current_list = dir_servers.list; 2404 (void) rw_wrlock(¤t_list->listDestroyLock); 2405 (void) mutex_unlock(&dir_servers.listReplaceLock); 2406 2407 while (current_list->nsServers[i] != NULL) { 2408 ++i; 2409 } 2410 srvListLength = i; 2411 2412 thrPool = calloc(srvListLength, sizeof (thread_t)); 2413 if (thrPool == NULL) { 2414 (void) rw_unlock(¤t_list->listDestroyLock); 2415 return (NS_LDAP_MEMORY); 2416 } 2417 2418 for (i = 0; i < srvListLength; ++i) { 2419 if (current_list->nsServers[i]->status != INFO_SERVER_REMOVED && 2420 current_list->nsServers[i]->status != INFO_SERVER_ERROR) { 2421 continue; 2422 } 2423 current_list->nsServers[i]->status = INFO_SERVER_CONNECTING; 2424 current_list->nsServers[i]->info = INFO_STATUS_NEW; 2425 2426 __s_api_free2dArray(current_list->nsServers[i]->controls); 2427 current_list->nsServers[i]->controls = NULL; 2428 __s_api_free2dArray(current_list->nsServers[i]->saslMech); 2429 current_list->nsServers[i]->saslMech = NULL; 2430 2431 switch (thr_create(NULL, 2432 0, 2433 create_ns_servers_entry, 2434 current_list->nsServers[i], 2435 0, 2436 &thrID)) { 2437 case EAGAIN: 2438 current_list->nsServers[i]->status = INFO_SERVER_ERROR; 2439 continue; 2440 break; 2441 case ENOMEM: 2442 current_list->nsServers[i]->status = INFO_SERVER_ERROR; 2443 retCode = NS_LDAP_MEMORY; 2444 break; 2445 default: 2446 thrPool[i] = thrID; 2447 continue; 2448 break; 2449 } 2450 /* A memory allocation error has occured */ 2451 break; 2452 2453 } 2454 2455 for (i = 0; i < srvListLength; ++i) { 2456 if (thrPool[i] != 0 && 2457 thr_join(thrPool[i], NULL, &status) == 0) { 2458 if (status == NULL) { 2459 current_list->nsServers[i]->status = 2460 INFO_SERVER_ERROR; 2461 retCode = NS_LDAP_MEMORY; 2462 } 2463 free(status); 2464 } 2465 } 2466 2467 (void) rw_unlock(¤t_list->listDestroyLock); 2468 2469 free(thrPool); 2470 2471 return (retCode); 2472 } 2473