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