1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * Copyright 2012 Milan Jurik. All rights reserved. 25 * Copyright 2012 Nexenta Systems, Inc. All rights reserved. 26 */ 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 } 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 } 1015 1016 ret_code = convert_to_door_line(ld, 1017 resultMsg, 1018 INCLUDE_ATTR_NAMES, 1019 IS_PROFILE, 1020 profile); 1021 if (resultMsg) { 1022 (void) ldap_msgfree(resultMsg); 1023 resultMsg = NULL; 1024 } 1025 return (ret_code); 1026 } 1027 1028 /* 1029 * This function derives the directory's base DN from a provided domain name. 1030 * 1031 * INPUT: 1032 * domain_name - the name of a domain to be converted into a base DN, 1033 * buffer - contains the derived base DN, 1034 * buf_len - the length of the buffer. 1035 * 1036 * OUTPUT: 1037 * The function returns the address of the buffer or NULL. 1038 */ 1039 static 1040 char * 1041 domainname2baseDN(char *domain_name, char *buffer, uint16_t buf_len) 1042 { 1043 char *nextDC, *chr; 1044 uint16_t i, length; 1045 1046 if (!domain_name || !buffer || buf_len == 0) { 1047 return (NULL); 1048 } 1049 1050 buffer[0] = '\0'; 1051 nextDC = chr = domain_name; 1052 length = strlen(domain_name); 1053 for (i = 0; i < length + 1; ++i, ++chr) { 1054 /* Simply replace dots with "dc=" */ 1055 if (*chr != '.' && *chr != '\0') { 1056 continue; 1057 } 1058 *chr = '\0'; 1059 if (strlcat(buffer, "dc=", buf_len) >= buf_len) 1060 return (NULL); 1061 if (strlcat(buffer, nextDC, buf_len) >= buf_len) 1062 return (NULL); 1063 if (i < length) { 1064 /* 1065 * The end of the domain name 1066 * has not been reached yet 1067 */ 1068 if (strlcat(buffer, ",", buf_len) >= buf_len) 1069 return (NULL); 1070 nextDC = chr + 1; 1071 *chr = '.'; 1072 } 1073 } 1074 1075 return (buffer); 1076 } 1077 1078 /* 1079 * This function obtains the directory's base DN and a DUAProfile 1080 * from a specified server. 1081 * 1082 * INPUT: 1083 * server - a structure describing a server to connect to and 1084 * a DUAProfile to be obtained from the server, 1085 * cred - credentials to be used during establishing connections to 1086 * the server. 1087 * 1088 * OUTPUT: 1089 * dua_profile - a buffer containing the DUAProfile in the following format: 1090 * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...] 1091 * dir_base_dn - a buffer containing the base DN, 1092 * errorp - an error object describing an error, if any. 1093 * 1094 * All the output data structures should be free'ed by the caller. 1095 */ 1096 ns_ldap_return_code 1097 __ns_ldap_getConnectionInfoFromDUA(const ns_dir_server_t *server, 1098 const ns_cred_t *cred, 1099 char **dua_profile, 1100 char **dir_base_dn, 1101 ns_ldap_error_t **errorp) 1102 { 1103 char serverAddr[MAX_HOSTADDR_LEN]; 1104 char *dirBaseDN = NULL, *duaProfile = NULL; 1105 ns_cred_t default_cred; 1106 ns_ldap_return_code ret_code; 1107 1108 ns_config_t *config_struct = __s_api_create_config(); 1109 ConnectionID sessionId = 0; 1110 Connection *session = NULL; 1111 char errmsg[MAXERROR]; 1112 char buffer[NSS_BUFLEN_HOSTS]; 1113 ns_conn_user_t *cu = NULL; 1114 1115 if (errorp == NULL) { 1116 __s_api_destroy_config(config_struct); 1117 return (NS_LDAP_INVALID_PARAM); 1118 } 1119 1120 *errorp = NULL; 1121 1122 if (server == NULL) { 1123 __s_api_destroy_config(config_struct); 1124 return (NS_LDAP_INVALID_PARAM); 1125 } 1126 1127 if (config_struct == NULL) { 1128 return (NS_LDAP_MEMORY); 1129 } 1130 1131 /* 1132 * If no credentials are specified, try to establish a connection 1133 * as anonymous. 1134 */ 1135 if (!cred) { 1136 default_cred.cred.unix_cred.passwd = NULL; 1137 default_cred.cred.unix_cred.userID = NULL; 1138 default_cred.auth.type = NS_LDAP_AUTH_NONE; 1139 } 1140 1141 /* Now create a default LDAP configuration */ 1142 1143 (void) strncpy(buffer, server->server, sizeof (buffer)); 1144 if (__ns_ldap_setParamValue(config_struct, NS_LDAP_SERVERS_P, buffer, 1145 errorp) != NS_LDAP_SUCCESS) { 1146 __s_api_destroy_config(config_struct); 1147 return (NS_LDAP_CONFIG); 1148 } 1149 1150 /* Put together the address and the port specified by the user app. */ 1151 if (server->port > 0) { 1152 (void) snprintf(serverAddr, 1153 sizeof (serverAddr), 1154 "%s:%hu", 1155 buffer, 1156 server->port); 1157 } else { 1158 (void) strncpy(serverAddr, buffer, sizeof (serverAddr)); 1159 } 1160 1161 /* 1162 * There is no default value for the 'Default Search Base DN' attribute. 1163 * Derive one from the domain name to make __s_api_crosscheck() happy. 1164 */ 1165 if (domainname2baseDN(server->domainName ? 1166 server->domainName : config_struct->domainName, 1167 buffer, NSS_BUFLEN_HOSTS) == NULL) { 1168 (void) snprintf(errmsg, 1169 sizeof (errmsg), 1170 gettext("Can not convert %s into a base DN name"), 1171 server->domainName ? 1172 server->domainName : config_struct->domainName); 1173 MKERROR(LOG_ERR, 1174 *errorp, 1175 NS_LDAP_INTERNAL, 1176 strdup(errmsg), 1177 NS_LDAP_MEMORY); 1178 __s_api_destroy_config(config_struct); 1179 return (NS_LDAP_INTERNAL); 1180 } 1181 if (__ns_ldap_setParamValue(config_struct, NS_LDAP_SEARCH_BASEDN_P, 1182 buffer, errorp) != NS_LDAP_SUCCESS) { 1183 __s_api_destroy_config(config_struct); 1184 return (NS_LDAP_CONFIG); 1185 } 1186 1187 if (__s_api_crosscheck(config_struct, errmsg, B_FALSE) != NS_SUCCESS) { 1188 __s_api_destroy_config(config_struct); 1189 return (NS_LDAP_CONFIG); 1190 } 1191 1192 __s_api_init_config(config_struct); 1193 1194 __s_api_setInitMode(); 1195 1196 cu = __s_api_conn_user_init(NS_CONN_USER_SEARCH, NULL, B_FALSE); 1197 if (cu == NULL) { 1198 return (NS_LDAP_INTERNAL); 1199 } 1200 1201 if ((ret_code = __s_api_getConnection(serverAddr, 1202 NS_LDAP_NEW_CONN, 1203 cred ? cred : &default_cred, 1204 &sessionId, 1205 &session, 1206 errorp, 1207 0, 1208 0, 1209 cu)) != NS_LDAP_SUCCESS) { 1210 __s_api_conn_user_free(cu); 1211 __s_api_unsetInitMode(); 1212 return (ret_code); 1213 } 1214 1215 __s_api_unsetInitMode(); 1216 1217 if ((ret_code = getDirBaseDN(session->ld, 1218 server->domainName ? 1219 server->domainName : 1220 config_struct->domainName, 1221 &dirBaseDN)) != NS_LDAP_SUCCESS) { 1222 (void) snprintf(errmsg, 1223 sizeof (errmsg), 1224 gettext("Can not find the " 1225 "nisDomainObject for domain %s\n"), 1226 server->domainName ? 1227 server->domainName : config_struct->domainName); 1228 MKERROR(LOG_ERR, 1229 *errorp, 1230 ret_code, 1231 strdup(errmsg), 1232 NS_LDAP_MEMORY); 1233 __s_api_conn_user_free(cu); 1234 DropConnection(sessionId, NS_LDAP_NEW_CONN); 1235 return (ret_code); 1236 } 1237 1238 /* 1239 * And here obtain a DUAProfile which will be used 1240 * as a real configuration. 1241 */ 1242 if ((ret_code = getDUAProfile(session->ld, 1243 dirBaseDN, 1244 server->profileName ? 1245 server->profileName : "default", 1246 &duaProfile)) != NS_LDAP_SUCCESS) { 1247 (void) snprintf(errmsg, 1248 sizeof (errmsg), 1249 gettext("Can not find the " 1250 "%s DUAProfile\n"), 1251 server->profileName ? 1252 server->profileName : "default"); 1253 MKERROR(LOG_ERR, 1254 *errorp, 1255 ret_code, 1256 strdup(errmsg), 1257 NS_LDAP_MEMORY); 1258 __s_api_conn_user_free(cu); 1259 DropConnection(sessionId, NS_LDAP_NEW_CONN); 1260 return (ret_code); 1261 } 1262 1263 if (dir_base_dn) { 1264 *dir_base_dn = dirBaseDN; 1265 } else { 1266 free(dirBaseDN); 1267 } 1268 1269 if (dua_profile) { 1270 *dua_profile = duaProfile; 1271 } else { 1272 free(duaProfile); 1273 } 1274 1275 __s_api_conn_user_free(cu); 1276 DropConnection(sessionId, NS_LDAP_NEW_CONN); 1277 1278 return (NS_LDAP_SUCCESS); 1279 } 1280 1281 /* 1282 * This function obtains the root DSE from a specified server. 1283 * 1284 * INPUT: 1285 * server_addr - an adress of a server to be connected to. 1286 * 1287 * OUTPUT: 1288 * root_dse - a buffer containing the root DSE in the following format: 1289 * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...] 1290 * For example: ( here | used as DOORLINESEP for visual purposes) 1291 * supportedControl=1.1.1.1|supportedSASLmechanisms=EXTERNAL 1292 * Should be free'ed by the caller. 1293 */ 1294 ns_ldap_return_code 1295 __ns_ldap_getRootDSE(const char *server_addr, 1296 char **root_dse, 1297 ns_ldap_error_t **errorp, 1298 int anon_fallback) 1299 { 1300 char errmsg[MAXERROR]; 1301 ns_ldap_return_code ret_code; 1302 1303 ConnectionID sessionId = 0; 1304 Connection *session = NULL; 1305 1306 struct timeval tv = {NS_DEFAULT_SEARCH_TIMEOUT, 0}; 1307 char *attrs[3]; 1308 int ldap_rc, ldaperrno = 0; 1309 LDAPMessage *resultMsg = NULL; 1310 void **paramVal = NULL; 1311 1312 ns_cred_t anon; 1313 ns_conn_user_t *cu = NULL; 1314 1315 if (errorp == NULL) { 1316 return (NS_LDAP_INVALID_PARAM); 1317 } 1318 1319 *errorp = NULL; 1320 1321 if (!root_dse) { 1322 return (NS_LDAP_INVALID_PARAM); 1323 } 1324 1325 if (!server_addr) { 1326 return (NS_LDAP_INVALID_PARAM); 1327 } 1328 1329 __s_api_setInitMode(); 1330 1331 cu = __s_api_conn_user_init(NS_CONN_USER_SEARCH, NULL, B_FALSE); 1332 if (cu == NULL) { 1333 return (NS_LDAP_INTERNAL); 1334 } 1335 1336 /* 1337 * All the credentials will be taken from the current 1338 * libsldap configuration. 1339 */ 1340 if ((ret_code = __s_api_getConnection(server_addr, 1341 NS_LDAP_NEW_CONN, 1342 NULL, 1343 &sessionId, 1344 &session, 1345 errorp, 1346 0, 1347 0, 1348 cu)) != NS_LDAP_SUCCESS) { 1349 /* Fallback to anonymous mode is disabled. Stop. */ 1350 if (anon_fallback == 0) { 1351 syslog(LOG_WARNING, 1352 gettext("libsldap: can not get the root DSE from " 1353 " the %s server: %s. " 1354 "Falling back to anonymous disabled.\n"), 1355 server_addr, 1356 errorp && *errorp && (*errorp)->message ? 1357 (*errorp)->message : ""); 1358 if (errorp != NULL && *errorp != NULL) { 1359 (void) __ns_ldap_freeError(errorp); 1360 } 1361 __s_api_unsetInitMode(); 1362 return (ret_code); 1363 } 1364 1365 /* 1366 * Fallback to anonymous, non-SSL mode for backward 1367 * compatibility reasons. This mode should only be used when 1368 * this function (__ns_ldap_getRootDSE) is called from 1369 * ldap_cachemgr(1M). 1370 */ 1371 syslog(LOG_WARNING, 1372 gettext("libsldap: Falling back to anonymous, non-SSL" 1373 " mode for __ns_ldap_getRootDSE. %s\n"), 1374 errorp && *errorp && (*errorp)->message ? 1375 (*errorp)->message : ""); 1376 1377 /* Setup the anon credential for anonymous connection. */ 1378 (void) memset(&anon, 0, sizeof (ns_cred_t)); 1379 anon.auth.type = NS_LDAP_AUTH_NONE; 1380 1381 if (*errorp != NULL) { 1382 (void) __ns_ldap_freeError(errorp); 1383 } 1384 *errorp = NULL; 1385 1386 ret_code = __s_api_getConnection(server_addr, 1387 NS_LDAP_NEW_CONN, 1388 &anon, 1389 &sessionId, 1390 &session, 1391 errorp, 1392 0, 1393 0, 1394 cu); 1395 1396 if (ret_code != NS_LDAP_SUCCESS) { 1397 __s_api_conn_user_free(cu); 1398 __s_api_unsetInitMode(); 1399 return (ret_code); 1400 } 1401 } 1402 1403 __s_api_unsetInitMode(); 1404 1405 /* get search timeout value */ 1406 (void) __ns_ldap_getParam(NS_LDAP_SEARCH_TIME_P, ¶mVal, errorp); 1407 if (paramVal != NULL && *paramVal != NULL) { 1408 tv.tv_sec = **((int **)paramVal); 1409 (void) __ns_ldap_freeParam(¶mVal); 1410 } 1411 if (*errorp != NULL) { 1412 (void) __ns_ldap_freeError(errorp); 1413 } 1414 1415 /* Get root DSE from the server specified by the caller. */ 1416 attrs[0] = "supportedControl"; 1417 attrs[1] = "supportedsaslmechanisms"; 1418 attrs[2] = NULL; 1419 ldap_rc = ldap_search_ext_s(session->ld, 1420 "", 1421 LDAP_SCOPE_BASE, 1422 "(objectclass=*)", 1423 attrs, 1424 0, 1425 NULL, 1426 NULL, 1427 &tv, 1428 0, 1429 &resultMsg); 1430 1431 if (ldap_rc != LDAP_SUCCESS) { 1432 /* 1433 * If the root DSE was not found, the server does 1434 * not comply with the LDAP v3 protocol. 1435 */ 1436 (void) ldap_get_option(session->ld, 1437 LDAP_OPT_ERROR_NUMBER, 1438 &ldaperrno); 1439 (void) snprintf(errmsg, 1440 sizeof (errmsg), 1441 gettext(ldap_err2string(ldaperrno))); 1442 MKERROR(LOG_ERR, 1443 *errorp, 1444 NS_LDAP_OP_FAILED, 1445 strdup(errmsg), 1446 NS_LDAP_MEMORY); 1447 1448 if (resultMsg) { 1449 (void) ldap_msgfree(resultMsg); 1450 resultMsg = NULL; 1451 } 1452 1453 __s_api_conn_user_free(cu); 1454 DropConnection(sessionId, NS_LDAP_NEW_CONN); 1455 return (NS_LDAP_OP_FAILED); 1456 } 1457 __s_api_conn_user_free(cu); 1458 1459 ret_code = convert_to_door_line(session->ld, 1460 resultMsg, 1461 INCLUDE_ATTR_NAMES, 1462 NOT_PROFILE, 1463 root_dse); 1464 if (ret_code == NS_LDAP_NOTFOUND) { 1465 (void) snprintf(errmsg, 1466 sizeof (errmsg), 1467 gettext("No root DSE data " 1468 "for server %s returned."), 1469 server_addr); 1470 MKERROR(LOG_ERR, 1471 *errorp, 1472 NS_LDAP_NOTFOUND, 1473 strdup(errmsg), 1474 NS_LDAP_MEMORY); 1475 } 1476 1477 if (resultMsg) { 1478 (void) ldap_msgfree(resultMsg); 1479 resultMsg = NULL; 1480 } 1481 1482 DropConnection(sessionId, NS_LDAP_NEW_CONN); 1483 1484 return (ret_code); 1485 } 1486 1487 /* 1488 * This function destroys the local list of root DSEs. The input parameter is 1489 * a pointer to the list to be erased. 1490 * The type of the pointer passed to this function should be 1491 * (dir_server_list_t *). 1492 */ 1493 static 1494 void * 1495 disposeOfOldList(void *param) 1496 { 1497 dir_server_list_t *old_list = (dir_server_list_t *)param; 1498 long i = 0, j; 1499 1500 (void) rw_wrlock(&old_list->listDestroyLock); 1501 /* Destroy the old list */ 1502 while (old_list->nsServers[i]) { 1503 free(old_list->nsServers[i]->ip); 1504 j = 0; 1505 while (old_list->nsServers[i]->controls && 1506 old_list->nsServers[i]->controls[j]) { 1507 free(old_list->nsServers[i]->controls[j]); 1508 ++j; 1509 } 1510 free(old_list->nsServers[i]->controls); 1511 j = 0; 1512 while (old_list->nsServers[i]->saslMech && 1513 old_list->nsServers[i]->saslMech[j]) { 1514 free(old_list->nsServers[i]->saslMech[j]); 1515 ++j; 1516 } 1517 free(old_list->nsServers[i]->saslMech); 1518 ++i; 1519 } 1520 /* 1521 * All the structures pointed by old_list->nsServers were allocated 1522 * in one chunck. The nsServers[0] pointer points to the beginning 1523 * of that chunck. 1524 */ 1525 free(old_list->nsServers[0]); 1526 free(old_list->nsServers); 1527 (void) rw_unlock(&old_list->listDestroyLock); 1528 (void) rwlock_destroy(&old_list->listDestroyLock); 1529 free(old_list); 1530 1531 return (NULL); 1532 } 1533 1534 /* 1535 * This function cancels the Standalone mode and destroys the list of root DSEs. 1536 */ 1537 void 1538 __ns_ldap_cancelStandalone(void) 1539 { 1540 dir_server_list_t *old_list; 1541 1542 (void) mutex_lock(&dir_servers.listReplaceLock); 1543 dir_servers.standalone = 0; 1544 if (!dir_servers.list) { 1545 (void) mutex_unlock(&dir_servers.listReplaceLock); 1546 return; 1547 } 1548 old_list = dir_servers.list; 1549 dir_servers.list = NULL; 1550 (void) mutex_unlock(&dir_servers.listReplaceLock); 1551 1552 (void) disposeOfOldList(old_list); 1553 } 1554 1555 1556 static 1557 void* 1558 create_ns_servers_entry(void *param) 1559 { 1560 #define CHUNK_SIZE 16 1561 1562 dir_server_t *server = (dir_server_t *)param; 1563 ns_ldap_return_code *retCode = calloc(1, 1564 sizeof (ns_ldap_return_code)); 1565 uint32_t sc_counter = 0, sm_counter = 0; 1566 uint32_t sc_mem_blocks = 1, sm_mem_blocks = 1; 1567 char *rootDSE = NULL, *attr, *val, *rest, **ptr; 1568 ns_ldap_error_t *error = NULL; 1569 1570 if (retCode == NULL) { 1571 return (NULL); 1572 } 1573 1574 /* 1575 * We call this function in non anon-fallback mode because we 1576 * want the whole procedure to fail as soon as possible to 1577 * indicate there are problems with connecting to the server. 1578 */ 1579 *retCode = __ns_ldap_getRootDSE(server->ip, 1580 &rootDSE, 1581 &error, 1582 SA_ALLOW_FALLBACK); 1583 1584 if (*retCode == NS_LDAP_MEMORY) { 1585 free(retCode); 1586 return (NULL); 1587 } 1588 1589 /* 1590 * If the root DSE can not be obtained, log an error and keep the 1591 * server. 1592 */ 1593 if (*retCode != NS_LDAP_SUCCESS) { 1594 server->status = INFO_SERVER_ERROR; 1595 syslog(LOG_WARNING, 1596 gettext("libsldap (\"standalone\" mode): " 1597 "can not obtain the root DSE from %s. %s"), 1598 server->ip, 1599 error && error->message ? error->message : ""); 1600 if (error) { 1601 (void) __ns_ldap_freeError(&error); 1602 } 1603 return (retCode); 1604 } 1605 1606 /* Get the first attribute of the root DSE. */ 1607 attr = strtok_r(rootDSE, DOORLINESEP, &rest); 1608 if (attr == NULL) { 1609 free(rootDSE); 1610 server->status = INFO_SERVER_ERROR; 1611 syslog(LOG_WARNING, 1612 gettext("libsldap (\"standalone\" mode): " 1613 "the root DSE from %s is empty or corrupted."), 1614 server->ip); 1615 *retCode = NS_LDAP_INTERNAL; 1616 return (retCode); 1617 } 1618 1619 server->controls = (char **)calloc(CHUNK_SIZE, sizeof (char *)); 1620 server->saslMech = (char **)calloc(CHUNK_SIZE, sizeof (char *)); 1621 if (server->controls == NULL || server->saslMech == NULL) { 1622 free(rootDSE); 1623 free(retCode); 1624 return (NULL); 1625 } 1626 1627 do { 1628 if ((val = strchr(attr, '=')) == NULL) { 1629 continue; 1630 } 1631 ++val; 1632 1633 if (strncasecmp(attr, 1634 _SASLMECHANISM, 1635 _SASLMECHANISM_LEN) == 0) { 1636 if (sm_counter == CHUNK_SIZE * sm_mem_blocks - 1) { 1637 ptr = (char **)realloc(server->saslMech, 1638 CHUNK_SIZE * 1639 ++sm_mem_blocks * 1640 sizeof (char *)); 1641 if (ptr == NULL) { 1642 *retCode = NS_LDAP_MEMORY; 1643 break; 1644 } 1645 bzero((char *)ptr + 1646 (sm_counter + 1) * 1647 sizeof (char *), 1648 CHUNK_SIZE * 1649 sm_mem_blocks * 1650 sizeof (char *) - 1651 (sm_counter + 1) * 1652 sizeof (char *)); 1653 server->saslMech = ptr; 1654 } 1655 server->saslMech[sm_counter] = strdup(val); 1656 if (server->saslMech[sm_counter] == NULL) { 1657 *retCode = NS_LDAP_MEMORY; 1658 break; 1659 } 1660 ++sm_counter; 1661 continue; 1662 } 1663 if (strncasecmp(attr, 1664 _SUPPORTEDCONTROL, 1665 _SUPPORTEDCONTROL_LEN) == 0) { 1666 if (sc_counter == CHUNK_SIZE * sc_mem_blocks - 1) { 1667 ptr = (char **)realloc(server->controls, 1668 CHUNK_SIZE * 1669 ++sc_mem_blocks * 1670 sizeof (char *)); 1671 if (ptr == NULL) { 1672 *retCode = NS_LDAP_MEMORY; 1673 break; 1674 } 1675 bzero((char *)ptr + 1676 (sc_counter + 1) * 1677 sizeof (char *), 1678 CHUNK_SIZE * 1679 sc_mem_blocks * 1680 sizeof (char *) - 1681 (sc_counter + 1) * 1682 sizeof (char *)); 1683 server->controls = ptr; 1684 } 1685 1686 server->controls[sc_counter] = strdup(val); 1687 if (server->controls[sc_counter] == NULL) { 1688 *retCode = NS_LDAP_MEMORY; 1689 break; 1690 } 1691 ++sc_counter; 1692 continue; 1693 } 1694 1695 } while (attr = strtok_r(NULL, DOORLINESEP, &rest)); 1696 1697 free(rootDSE); 1698 1699 if (*retCode == NS_LDAP_MEMORY) { 1700 free(retCode); 1701 return (NULL); 1702 } 1703 1704 server->controls[sc_counter] = NULL; 1705 server->saslMech[sm_counter] = NULL; 1706 1707 server->status = INFO_SERVER_UP; 1708 1709 return (retCode); 1710 #undef CHUNK_SIZE 1711 } 1712 1713 1714 /* 1715 * This function creates a new local list of root DSEs from all the servers 1716 * mentioned in the DUAProfile (or local NS BEC) and returns 1717 * a pointer to the list. 1718 */ 1719 static 1720 ns_ldap_return_code 1721 createDirServerList(dir_server_list_t **new_list, 1722 ns_ldap_error_t **errorp) 1723 { 1724 char **serverList; 1725 ns_ldap_return_code retCode = NS_LDAP_SUCCESS; 1726 dir_server_t *tmpSrvArray; 1727 long srvListLength, i; 1728 thread_t *thrPool, thrID; 1729 void *status = NULL; 1730 1731 if (errorp == NULL) { 1732 return (NS_LDAP_INVALID_PARAM); 1733 } 1734 1735 *errorp = NULL; 1736 1737 if (new_list == NULL) { 1738 return (NS_LDAP_INVALID_PARAM); 1739 } 1740 1741 retCode = __s_api_getServers(&serverList, errorp); 1742 if (retCode != NS_LDAP_SUCCESS || serverList == NULL) { 1743 return (retCode); 1744 } 1745 1746 for (i = 0; serverList[i]; ++i) { 1747 ; 1748 } 1749 srvListLength = i; 1750 1751 thrPool = calloc(srvListLength, sizeof (thread_t)); 1752 if (thrPool == NULL) { 1753 __s_api_free2dArray(serverList); 1754 return (NS_LDAP_MEMORY); 1755 } 1756 1757 *new_list = (dir_server_list_t *)calloc(1, 1758 sizeof (dir_server_list_t)); 1759 if (*new_list == NULL) { 1760 __s_api_free2dArray(serverList); 1761 free(thrPool); 1762 return (NS_LDAP_MEMORY); 1763 } 1764 (void) rwlock_init(&(*new_list)->listDestroyLock, USYNC_THREAD, NULL); 1765 1766 (*new_list)->nsServers = (dir_server_t **)calloc(srvListLength + 1, 1767 sizeof (dir_server_t *)); 1768 if ((*new_list)->nsServers == NULL) { 1769 free(*new_list); 1770 *new_list = NULL; 1771 __s_api_free2dArray(serverList); 1772 free(thrPool); 1773 return (NS_LDAP_MEMORY); 1774 } 1775 1776 /* 1777 * Allocate a set of dir_server_t structures as an array, 1778 * with one alloc call and then initialize the nsServers pointers 1779 * with the addresses of the array's members. 1780 */ 1781 tmpSrvArray = (dir_server_t *)calloc(srvListLength, 1782 sizeof (dir_server_t)); 1783 for (i = 0; i < srvListLength; ++i) { 1784 (*new_list)->nsServers[i] = &tmpSrvArray[i]; 1785 1786 (*new_list)->nsServers[i]->info = INFO_STATUS_NEW; 1787 (void) mutex_init(&(*new_list)->nsServers[i]->updateStatus, 1788 USYNC_THREAD, 1789 NULL); 1790 1791 (*new_list)->nsServers[i]->ip = strdup(serverList[i]); 1792 if ((*new_list)->nsServers[i]->ip == NULL) { 1793 retCode = NS_LDAP_MEMORY; 1794 break; 1795 } 1796 1797 (*new_list)->nsServers[i]->status = INFO_SERVER_CONNECTING; 1798 1799 switch (thr_create(NULL, 1800 0, 1801 create_ns_servers_entry, 1802 (*new_list)->nsServers[i], 1803 0, 1804 &thrID)) { 1805 case EAGAIN: 1806 (*new_list)->nsServers[i]->status = 1807 INFO_SERVER_ERROR; 1808 continue; 1809 case ENOMEM: 1810 (*new_list)->nsServers[i]->status = 1811 INFO_SERVER_ERROR; 1812 continue; 1813 default: 1814 thrPool[i] = thrID; 1815 continue; 1816 } 1817 } 1818 1819 for (i = 0; i < srvListLength; ++i) { 1820 if (thrPool[i] != 0 && 1821 thr_join(thrPool[i], NULL, &status) == 0) { 1822 if (status == NULL) { 1823 /* 1824 * Some memory allocation problems occured. Just 1825 * ignore the server and hope there will be some 1826 * other good ones. 1827 */ 1828 (*new_list)->nsServers[i]->status = 1829 INFO_SERVER_ERROR; 1830 } 1831 free(status); 1832 } 1833 } 1834 1835 __s_api_free2dArray(serverList); 1836 free(thrPool); 1837 1838 if (retCode == NS_LDAP_MEMORY) { 1839 (void) disposeOfOldList(*new_list); 1840 return (NS_LDAP_MEMORY); 1841 } 1842 1843 return (NS_LDAP_SUCCESS); 1844 } 1845 1846 /* 1847 * This functions replaces the local list of root DSEs with a new one and starts 1848 * a thread destroying the old list. There is no need for other threads to wait 1849 * until the old list will be destroyed. 1850 * Since it is possible that more than one thread can start creating the list, 1851 * this function should be protected by mutexes to be sure that only one thread 1852 * performs the initialization. 1853 */ 1854 static 1855 ns_ldap_return_code 1856 initGlobalList(ns_ldap_error_t **error) 1857 { 1858 dir_server_list_t *new_list, *old_list; 1859 ns_ldap_return_code ret_code; 1860 thread_t tid; 1861 1862 ret_code = createDirServerList(&new_list, error); 1863 if (ret_code != NS_LDAP_SUCCESS) { 1864 return (ret_code); 1865 } 1866 1867 old_list = dir_servers.list; 1868 dir_servers.list = new_list; 1869 1870 if (old_list) { 1871 (void) thr_create(NULL, 1872 0, 1873 disposeOfOldList, 1874 old_list, 1875 THR_DETACHED, 1876 &tid); 1877 } 1878 1879 return (NS_LDAP_SUCCESS); 1880 } 1881 1882 static 1883 struct { 1884 char *authMech; 1885 ns_auth_t auth; 1886 } authArray[] = {{"none", {NS_LDAP_AUTH_NONE, 1887 NS_LDAP_TLS_NONE, 1888 NS_LDAP_SASL_NONE, 1889 NS_LDAP_SASLOPT_NONE}}, 1890 {"simple", {NS_LDAP_AUTH_SIMPLE, 1891 NS_LDAP_TLS_NONE, 1892 NS_LDAP_SASL_NONE, 1893 NS_LDAP_SASLOPT_NONE}}, 1894 {"tls:simple", {NS_LDAP_AUTH_TLS, 1895 NS_LDAP_TLS_SIMPLE, 1896 NS_LDAP_SASL_NONE, 1897 NS_LDAP_SASLOPT_NONE}}, 1898 {"tls:sasl/CRAM-MD5", {NS_LDAP_AUTH_TLS, 1899 NS_LDAP_TLS_SASL, 1900 NS_LDAP_SASL_CRAM_MD5, 1901 NS_LDAP_SASLOPT_NONE}}, 1902 {"tls:sasl/DIGEST-MD5", {NS_LDAP_AUTH_TLS, 1903 NS_LDAP_TLS_SASL, 1904 NS_LDAP_SASL_DIGEST_MD5, 1905 NS_LDAP_SASLOPT_NONE}}, 1906 {"sasl/CRAM-MD5", {NS_LDAP_AUTH_SASL, 1907 NS_LDAP_TLS_SASL, 1908 NS_LDAP_SASL_CRAM_MD5, 1909 NS_LDAP_SASLOPT_NONE}}, 1910 {"sasl/DIGEST-MD5", {NS_LDAP_AUTH_SASL, 1911 NS_LDAP_TLS_SASL, 1912 NS_LDAP_SASL_DIGEST_MD5, 1913 NS_LDAP_SASLOPT_NONE}}, 1914 {"sasl/GSSAPI", {NS_LDAP_AUTH_SASL, 1915 NS_LDAP_TLS_SASL, 1916 NS_LDAP_SASL_GSSAPI, 1917 NS_LDAP_SASLOPT_PRIV | NS_LDAP_SASLOPT_INT}}, 1918 {NULL, {NS_LDAP_AUTH_NONE, 1919 NS_LDAP_TLS_NONE, 1920 NS_LDAP_SASL_NONE, 1921 NS_LDAP_SASLOPT_NONE}}}; 1922 1923 ns_ldap_return_code 1924 __ns_ldap_initAuth(const char *auth_mech, 1925 ns_auth_t *auth, 1926 ns_ldap_error_t **errorp) 1927 { 1928 uint32_t i; 1929 char errmsg[MAXERROR]; 1930 1931 if (auth_mech == NULL) { 1932 (void) snprintf(errmsg, 1933 sizeof (errmsg), 1934 gettext("Invalid authentication method specified\n")); 1935 MKERROR(LOG_WARNING, 1936 *errorp, 1937 NS_LDAP_INTERNAL, 1938 strdup(errmsg), 1939 NS_LDAP_MEMORY); 1940 return (NS_LDAP_INTERNAL); 1941 } 1942 1943 for (i = 0; authArray[i].authMech != NULL; ++i) { 1944 if (strcasecmp(auth_mech, authArray[i].authMech) == 0) { 1945 *auth = authArray[i].auth; 1946 return (NS_LDAP_SUCCESS); 1947 } 1948 } 1949 1950 (void) snprintf(errmsg, 1951 sizeof (errmsg), 1952 gettext("Invalid authentication method specified\n")); 1953 MKERROR(LOG_WARNING, 1954 *errorp, 1955 NS_LDAP_INTERNAL, 1956 strdup(errmsg), 1957 NS_LDAP_MEMORY); 1958 return (NS_LDAP_INTERNAL); 1959 } 1960 1961 /* 1962 * This function "informs" libsldap that a client application has specified 1963 * a directory to use. The function obtains a DUAProfile, credentials, 1964 * and naming context. During all further operations on behalf 1965 * of the application requested a standalone schema libsldap will use 1966 * the information obtained by __ns_ldap_initStandalone() instead of 1967 * door_call(3C)ing ldap_cachemgr(1M). 1968 * 1969 * INPUT: 1970 * sa_conf - a structure describing where and in which way to obtain all 1971 * the configuration describing how to communicate to 1972 * a choosen LDAP directory, 1973 * errorp - an error object describing an error occured. 1974 */ 1975 ns_ldap_return_code 1976 __ns_ldap_initStandalone(const ns_standalone_conf_t *sa_conf, 1977 ns_ldap_error_t **errorp) { 1978 1979 ns_cred_t user_cred = {{NS_LDAP_AUTH_NONE, 1980 NS_LDAP_TLS_NONE, 1981 NS_LDAP_SASL_NONE, 1982 NS_LDAP_SASLOPT_NONE}, 1983 NULL, 1984 {NULL, NULL}}; 1985 char *dua_profile = NULL; 1986 char errmsg[MAXERROR]; 1987 ns_config_t *cfg; 1988 int ret_code; 1989 1990 if (sa_conf->SA_BIND_DN == NULL && sa_conf->SA_BIND_PWD != NULL || 1991 sa_conf->SA_BIND_DN != NULL && sa_conf->SA_BIND_PWD == NULL) { 1992 (void) snprintf(errmsg, 1993 sizeof (errmsg), 1994 gettext("Bind DN and bind password" 1995 " must both be provided\n")); 1996 MKERROR(LOG_ERR, 1997 *errorp, 1998 NS_CONFIG_NOTLOADED, 1999 strdup(errmsg), 2000 NS_LDAP_MEMORY); 2001 return (NS_LDAP_INTERNAL); 2002 } 2003 2004 switch (sa_conf->type) { 2005 case NS_LDAP_SERVER: 2006 if (sa_conf->SA_BIND_DN != NULL) { 2007 user_cred.cred.unix_cred.userID = sa_conf->SA_BIND_DN; 2008 user_cred.auth.type = NS_LDAP_AUTH_SIMPLE; 2009 } 2010 2011 if (sa_conf->SA_BIND_PWD != NULL) { 2012 user_cred.cred.unix_cred.passwd = sa_conf->SA_BIND_PWD; 2013 } 2014 2015 if (sa_conf->SA_AUTH != NULL) { 2016 user_cred.auth.type = sa_conf->SA_AUTH->type; 2017 user_cred.auth.tlstype = sa_conf->SA_AUTH->tlstype; 2018 user_cred.auth.saslmech = sa_conf->SA_AUTH->saslmech; 2019 user_cred.auth.saslopt = sa_conf->SA_AUTH->saslopt; 2020 } 2021 2022 if (sa_conf->SA_CERT_PATH != NULL) { 2023 user_cred.hostcertpath = sa_conf->SA_CERT_PATH; 2024 } 2025 2026 ret_code = __ns_ldap_getConnectionInfoFromDUA( 2027 &sa_conf->ds_profile.server, 2028 &user_cred, 2029 &dua_profile, 2030 NULL, 2031 errorp); 2032 if (ret_code != NS_LDAP_SUCCESS) { 2033 return (ret_code); 2034 } 2035 2036 cfg = __s_api_create_config_door_str(dua_profile, errorp); 2037 if (cfg == NULL) { 2038 free(dua_profile); 2039 return (NS_LDAP_CONFIG); 2040 } 2041 2042 if (sa_conf->SA_CERT_PATH != NULL) { 2043 char *certPathAttr; 2044 ParamIndexType type; 2045 2046 switch (cfg->version) { 2047 case NS_LDAP_V1: 2048 certPathAttr = "NS_LDAP_CERT_PATH"; 2049 break; 2050 default: /* Version 2 */ 2051 certPathAttr = "NS_LDAP_HOST_CERTPATH"; 2052 break; 2053 } 2054 2055 if (__s_api_get_versiontype(cfg, 2056 certPathAttr, 2057 &type) == 0 && 2058 (ret_code = __ns_ldap_setParamValue(cfg, 2059 type, 2060 sa_conf->SA_CERT_PATH, 2061 errorp)) != NS_LDAP_SUCCESS) { 2062 __s_api_destroy_config(cfg); 2063 return (ret_code); 2064 } 2065 } 2066 2067 if (sa_conf->SA_BIND_DN != NULL && 2068 sa_conf->SA_BIND_PWD != NULL) { 2069 char *authMethods; 2070 2071 authMethods = __s_api_strValue(cfg, NS_LDAP_AUTH_P, 2072 NS_FILE_FMT); 2073 if (authMethods != NULL && 2074 strstr(authMethods, "sasl/GSSAPI") != NULL) { 2075 /* 2076 * The received DUAProfile specifies 2077 * sasl/GSSAPI as an auth. mechanism. 2078 * The bind DN and password will be 2079 * ignored. 2080 */ 2081 syslog(LOG_INFO, gettext("sasl/GSSAPI will be " 2082 "used as an authentication method. " 2083 "The bind DN and password will " 2084 "be ignored.\n")); 2085 free(authMethods); 2086 break; 2087 } 2088 2089 if (authMethods != NULL) 2090 free(authMethods); 2091 2092 if (__ns_ldap_setParamValue(cfg, 2093 NS_LDAP_BINDDN_P, 2094 sa_conf->SA_BIND_DN, 2095 errorp) != NS_LDAP_SUCCESS) { 2096 __s_api_destroy_config(cfg); 2097 return (NS_LDAP_CONFIG); 2098 } 2099 2100 if (__ns_ldap_setParamValue(cfg, 2101 NS_LDAP_BINDPASSWD_P, 2102 sa_conf->SA_BIND_PWD, 2103 errorp) != NS_LDAP_SUCCESS) { 2104 __s_api_destroy_config(cfg); 2105 return (NS_LDAP_CONFIG); 2106 } 2107 } 2108 2109 break; 2110 default: /* NS_CACHEMGR */ 2111 return (NS_LDAP_SUCCESS); 2112 } 2113 2114 __s_api_init_config(cfg); 2115 /* Connection management should use the new config now. */ 2116 __s_api_reinit_conn_mgmt_new_config(cfg); 2117 __ns_ldap_setServer(TRUE); 2118 2119 (void) mutex_lock(&dir_servers.listReplaceLock); 2120 if ((ret_code = initGlobalList(errorp)) != NS_SUCCESS) { 2121 (void) mutex_unlock(&dir_servers.listReplaceLock); 2122 return (ret_code); 2123 } 2124 dir_servers.standalone = 1; 2125 (void) mutex_unlock(&dir_servers.listReplaceLock); 2126 2127 return (NS_LDAP_SUCCESS); 2128 } 2129 2130 /* 2131 * INPUT: 2132 * serverAddr is the address of a server and 2133 * request is one of the following: 2134 * NS_CACHE_NEW: get a new server address, addr is ignored. 2135 * NS_CACHE_NORESP: get the next one, remove addr from list. 2136 * NS_CACHE_NEXT: get the next one, keep addr on list. 2137 * NS_CACHE_WRITE: get a non-replica server, if possible, if not, same 2138 * as NS_CACHE_NEXT. 2139 * addrType: 2140 * NS_CACHE_ADDR_IP: return server address as is, this is default. 2141 * NS_CACHE_ADDR_HOSTNAME: return server addess as FQDN format, only 2142 * self credential case requires such format. 2143 * OUTPUT: 2144 * ret 2145 * 2146 * a structure of type ns_server_info_t containing the server address 2147 * or name, server controls and supported SASL mechanisms. 2148 * NOTE: Caller should allocate space for the structure and free 2149 * all the space allocated by the function for the information contained 2150 * in the structure. 2151 * 2152 * error - an error object describing an error, if any. 2153 */ 2154 ns_ldap_return_code 2155 __s_api_findRootDSE(const char *request, 2156 const char *serverAddr, 2157 const char *addrType, 2158 ns_server_info_t *ret, 2159 ns_ldap_error_t **error) 2160 { 2161 dir_server_list_t *current_list = NULL; 2162 ns_ldap_return_code ret_code; 2163 long i = 0; 2164 int matched = FALSE; 2165 dir_server_t *server = NULL; 2166 char errmsg[MAXERROR]; 2167 2168 (void) mutex_lock(&dir_servers.listReplaceLock); 2169 if (dir_servers.list == NULL) { 2170 (void) mutex_unlock(&dir_servers.listReplaceLock); 2171 (void) snprintf(errmsg, 2172 sizeof (errmsg), 2173 gettext("The list of root DSEs is empty: " 2174 "the Standalone mode was not properly initialized")); 2175 MKERROR(LOG_ERR, 2176 *error, 2177 NS_CONFIG_NOTLOADED, 2178 strdup(errmsg), 2179 NS_LDAP_MEMORY); 2180 return (NS_LDAP_INTERNAL); 2181 } 2182 2183 current_list = dir_servers.list; 2184 (void) rw_rdlock(¤t_list->listDestroyLock); 2185 (void) mutex_unlock(&dir_servers.listReplaceLock); 2186 2187 /* 2188 * The code below is mostly the clone of the 2189 * ldap_cachemgr::cachemgr_getldap.c::getldap_get_serverInfo() function. 2190 * Currently we have two different server lists: one is maintained 2191 * by libsldap ('standalone' mode), the other is in ldap_cachemgr 2192 * (a part of its standard functionality). 2193 */ 2194 2195 /* 2196 * If NS_CACHE_NEW, or the server info is new, 2197 * starts from the beginning of the list. 2198 */ 2199 (void) mutex_lock(¤t_list->nsServers[0]->updateStatus); 2200 if (strcmp(request, NS_CACHE_NEW) == 0 || 2201 current_list->nsServers[0]->info == INFO_STATUS_NEW) { 2202 matched = TRUE; 2203 } 2204 (void) mutex_unlock(¤t_list->nsServers[i]->updateStatus); 2205 2206 for (i = 0; current_list->nsServers[i]; ++i) { 2207 /* 2208 * Lock the updateStatus mutex to 2209 * make sure the server status stays the same 2210 * while the data is being processed. 2211 */ 2212 if (matched == FALSE && 2213 strcmp(current_list->nsServers[i]->ip, 2214 serverAddr) == 0) { 2215 matched = TRUE; 2216 if (strcmp(request, NS_CACHE_NORESP) == 0) { 2217 2218 /* 2219 * if the server has already been removed, 2220 * don't bother. 2221 */ 2222 (void) mutex_lock(¤t_list-> 2223 nsServers[i]->updateStatus); 2224 if (current_list->nsServers[i]->status == 2225 INFO_SERVER_REMOVED) { 2226 (void) mutex_unlock(¤t_list-> 2227 nsServers[i]-> 2228 updateStatus); 2229 continue; 2230 } 2231 (void) mutex_unlock(¤t_list-> 2232 nsServers[i]-> 2233 updateStatus); 2234 2235 /* 2236 * if the information is new, 2237 * give this server one more chance. 2238 */ 2239 (void) mutex_lock(¤t_list-> 2240 nsServers[i]-> 2241 updateStatus); 2242 if (current_list->nsServers[i]->info == 2243 INFO_STATUS_NEW && 2244 current_list->nsServers[i]->status == 2245 INFO_SERVER_UP) { 2246 server = current_list->nsServers[i]; 2247 (void) mutex_unlock(¤t_list-> 2248 nsServers[i]-> 2249 updateStatus); 2250 break; 2251 } else { 2252 /* 2253 * it is recommended that 2254 * before removing the 2255 * server from the list, 2256 * the server should be 2257 * contacted one more time 2258 * to make sure that it is 2259 * really unavailable. 2260 * For now, just trust the client 2261 * (i.e., the sldap library) 2262 * that it knows what it is 2263 * doing and would not try 2264 * to mess up the server 2265 * list. 2266 */ 2267 current_list->nsServers[i]->status = 2268 INFO_SERVER_REMOVED; 2269 (void) mutex_unlock(¤t_list-> 2270 nsServers[i]-> 2271 updateStatus); 2272 continue; 2273 } 2274 } else { 2275 /* 2276 * req == NS_CACHE_NEXT or NS_CACHE_WRITE 2277 */ 2278 continue; 2279 } 2280 } 2281 2282 if (matched) { 2283 if (strcmp(request, NS_CACHE_WRITE) == 0) { 2284 /* 2285 * ldap_cachemgr checks here if the server 2286 * is not a non-replica server (a server 2287 * of type INFO_RW_WRITEABLE). But currently 2288 * it considers all the servers in its list 2289 * as those. 2290 */ 2291 (void) mutex_lock(¤t_list-> 2292 nsServers[i]-> 2293 updateStatus); 2294 if (current_list->nsServers[i]->status == 2295 INFO_SERVER_UP) { 2296 (void) mutex_unlock(¤t_list-> 2297 nsServers[i]-> 2298 updateStatus); 2299 server = current_list->nsServers[i]; 2300 break; 2301 } 2302 } else { 2303 (void) mutex_lock(¤t_list-> 2304 nsServers[i]-> 2305 updateStatus); 2306 if (current_list->nsServers[i]->status == 2307 INFO_SERVER_UP) { 2308 (void) mutex_unlock(¤t_list-> 2309 nsServers[i]-> 2310 updateStatus); 2311 server = current_list->nsServers[i]; 2312 break; 2313 } 2314 } 2315 2316 (void) mutex_unlock(¤t_list-> 2317 nsServers[i]-> 2318 updateStatus); 2319 } 2320 } 2321 2322 if (server == NULL) { 2323 (void) rw_unlock(¤t_list->listDestroyLock); 2324 (void) snprintf(errmsg, 2325 sizeof (errmsg), 2326 gettext("No servers are available")); 2327 MKERROR(LOG_ERR, 2328 *error, 2329 NS_CONFIG_NOTLOADED, 2330 strdup(errmsg), 2331 NS_LDAP_MEMORY); 2332 return (NS_LDAP_NOTFOUND); 2333 } 2334 2335 (void) mutex_lock(&server->updateStatus); 2336 server->info = INFO_STATUS_OLD; 2337 (void) mutex_unlock(&server->updateStatus); 2338 2339 if (ret == NULL) { 2340 (void) rw_unlock(¤t_list->listDestroyLock); 2341 return (NS_LDAP_SUCCESS); 2342 } 2343 2344 if (strcmp(addrType, NS_CACHE_ADDR_HOSTNAME) == 0) { 2345 ret_code = __s_api_ip2hostname(server->ip, &ret->serverFQDN); 2346 if (ret_code != NS_LDAP_SUCCESS) { 2347 (void) snprintf(errmsg, 2348 sizeof (errmsg), 2349 gettext("The %s address " 2350 "can not be resolved into " 2351 "a host name. Returning " 2352 "the address as it is."), 2353 server->ip); 2354 MKERROR(LOG_ERR, 2355 *error, 2356 NS_CONFIG_NOTLOADED, 2357 strdup(errmsg), 2358 NS_LDAP_MEMORY); 2359 return (NS_LDAP_INTERNAL); 2360 } 2361 } 2362 2363 ret->server = strdup(server->ip); 2364 2365 ret->controls = __s_api_cp2dArray(server->controls); 2366 ret->saslMechanisms = __s_api_cp2dArray(server->saslMech); 2367 2368 (void) rw_unlock(¤t_list->listDestroyLock); 2369 2370 return (NS_LDAP_SUCCESS); 2371 } 2372 2373 /* 2374 * This function iterates through the list of the configured LDAP servers 2375 * and "pings" those which are marked as removed or if any error occurred 2376 * during the previous receiving of the server's root DSE. If the 2377 * function is able to reach such a server and get its root DSE, it 2378 * marks the server as on-line. Otherwise, the server's status is set 2379 * to "Error". 2380 * For each server the function tries to connect to, it fires up 2381 * a separate thread and then waits until all the treads finish. 2382 * The function returns NS_LDAP_INTERNAL if the Standalone mode was not 2383 * initialized or was canceled prior to an invocation of 2384 * __ns_ldap_pingOfflineServers(). 2385 */ 2386 ns_ldap_return_code 2387 __ns_ldap_pingOfflineServers(void) 2388 { 2389 dir_server_list_t *current_list = NULL; 2390 ns_ldap_return_code retCode = NS_LDAP_SUCCESS; 2391 long srvListLength, i = 0; 2392 thread_t *thrPool, thrID; 2393 void *status = NULL; 2394 2395 (void) mutex_lock(&dir_servers.listReplaceLock); 2396 if (dir_servers.list == NULL) { 2397 (void) mutex_unlock(&dir_servers.listReplaceLock); 2398 return (NS_LDAP_INTERNAL); 2399 } 2400 2401 current_list = dir_servers.list; 2402 (void) rw_wrlock(¤t_list->listDestroyLock); 2403 (void) mutex_unlock(&dir_servers.listReplaceLock); 2404 2405 while (current_list->nsServers[i] != NULL) { 2406 ++i; 2407 } 2408 srvListLength = i; 2409 2410 thrPool = calloc(srvListLength, sizeof (thread_t)); 2411 if (thrPool == NULL) { 2412 (void) rw_unlock(¤t_list->listDestroyLock); 2413 return (NS_LDAP_MEMORY); 2414 } 2415 2416 for (i = 0; i < srvListLength; ++i) { 2417 if (current_list->nsServers[i]->status != INFO_SERVER_REMOVED && 2418 current_list->nsServers[i]->status != INFO_SERVER_ERROR) { 2419 continue; 2420 } 2421 current_list->nsServers[i]->status = INFO_SERVER_CONNECTING; 2422 current_list->nsServers[i]->info = INFO_STATUS_NEW; 2423 2424 __s_api_free2dArray(current_list->nsServers[i]->controls); 2425 current_list->nsServers[i]->controls = NULL; 2426 __s_api_free2dArray(current_list->nsServers[i]->saslMech); 2427 current_list->nsServers[i]->saslMech = NULL; 2428 2429 switch (thr_create(NULL, 2430 0, 2431 create_ns_servers_entry, 2432 current_list->nsServers[i], 2433 0, 2434 &thrID)) { 2435 case EAGAIN: 2436 current_list->nsServers[i]->status = INFO_SERVER_ERROR; 2437 continue; 2438 case ENOMEM: 2439 current_list->nsServers[i]->status = INFO_SERVER_ERROR; 2440 retCode = NS_LDAP_MEMORY; 2441 break; 2442 default: 2443 thrPool[i] = thrID; 2444 continue; 2445 } 2446 /* A memory allocation error has occured */ 2447 break; 2448 2449 } 2450 2451 for (i = 0; i < srvListLength; ++i) { 2452 if (thrPool[i] != 0 && 2453 thr_join(thrPool[i], NULL, &status) == 0) { 2454 if (status == NULL) { 2455 current_list->nsServers[i]->status = 2456 INFO_SERVER_ERROR; 2457 retCode = NS_LDAP_MEMORY; 2458 } 2459 free(status); 2460 } 2461 } 2462 2463 (void) rw_unlock(¤t_list->listDestroyLock); 2464 2465 free(thrPool); 2466 2467 return (retCode); 2468 } 2469