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