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