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 break; 434 default: 435 return (NULL); 436 } 437 438 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2ent); 439 440 arg.key.hostaddr.addr = addr; 441 arg.key.hostaddr.len = len; 442 arg.key.hostaddr.type = type; 443 arg.stayopen = 0; 444 arg.h_errno = NETDB_SUCCESS; 445 446 res = nss_search(nss_db_root, nss_initf, dbop, &arg); 447 arg.status = res; 448 *h_errnop = arg.h_errno; 449 return (struct hostent *)NSS_XbyY_FINI(&arg); 450 } 451 452 /* 453 * This routine is an analog of gethostbyaddr_r(). 454 * But in addition __s_api_hostname2ip() performs the "LDAP SKIPDB" activity 455 * prior to querying the name services. 456 * If the buffer is not big enough to accommodate a returning data, 457 * NULL is returned and h_errnop is set to TRY_AGAIN. 458 */ 459 struct hostent * 460 __s_api_hostname2ip(const char *name, 461 struct hostent *result, char *buffer, int buflen, 462 int *h_errnop) 463 { 464 DEFINE_NSS_DB_ROOT(db_root_ipnodes); 465 DEFINE_NSS_DB_ROOT(db_root_hosts); 466 nss_XbyY_args_t arg; 467 nss_status_t res; 468 struct in_addr addr; 469 struct in6_addr addr6; 470 471 if (inet_pton(AF_INET, name, &addr) > 0) { 472 if (buflen < strlen(name) + 1 + 473 sizeof (char *) * 2 + /* The h_aliases member */ 474 sizeof (struct in_addr) + 475 sizeof (struct in_addr *) * 2) { 476 *h_errnop = TRY_AGAIN; 477 return (NULL); 478 } 479 480 result->h_addrtype = AF_INET; 481 result->h_length = sizeof (struct in_addr); 482 (void) strncpy(buffer, name, buflen); 483 484 result->h_addr_list = (char **)ROUND_UP( 485 buffer + strlen(name) + 1, 486 sizeof (char *)); 487 result->h_aliases = (char **)ROUND_UP(result->h_addr_list, 488 sizeof (char *)); 489 result->h_aliases[0] = buffer; 490 result->h_aliases[1] = NULL; 491 bcopy(&addr, 492 buffer + buflen - sizeof (struct in_addr), 493 sizeof (struct in_addr)); 494 result->h_addr_list[0] = buffer + buflen - 495 sizeof (struct in_addr); 496 result->h_addr_list[1] = NULL; 497 result->h_aliases = result->h_addr_list; 498 result->h_name = buffer; 499 500 *h_errnop = NETDB_SUCCESS; 501 return (result); 502 } 503 if (inet_pton(AF_INET6, name, &addr6) > 0) { 504 if (buflen < strlen(name) + 1 + 505 sizeof (char *) * 2 + /* The h_aliases member */ 506 sizeof (struct in6_addr) + 507 sizeof (struct in6_addr *) * 2) { 508 *h_errnop = TRY_AGAIN; 509 return (NULL); 510 } 511 512 result->h_addrtype = AF_INET6; 513 result->h_length = sizeof (struct in6_addr); 514 (void) strncpy(buffer, name, buflen); 515 516 result->h_addr_list = (char **)ROUND_UP( 517 buffer + strlen(name) + 1, 518 sizeof (char *)); 519 result->h_aliases = (char **)ROUND_UP(result->h_addr_list, 520 sizeof (char *)); 521 result->h_aliases[0] = buffer; 522 result->h_aliases[1] = NULL; 523 bcopy(&addr6, 524 buffer + buflen - sizeof (struct in6_addr), 525 sizeof (struct in6_addr)); 526 result->h_addr_list[0] = buffer + buflen - 527 sizeof (struct in6_addr); 528 result->h_addr_list[1] = NULL; 529 result->h_aliases = result->h_addr_list; 530 result->h_name = buffer; 531 532 *h_errnop = NETDB_SUCCESS; 533 return (result); 534 } 535 536 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent); 537 538 arg.key.name = name; 539 arg.stayopen = 0; 540 arg.h_errno = NETDB_SUCCESS; 541 542 res = nss_search(&db_root_ipnodes, _initf_ipnodes, 543 NSS_DBOP_IPNODES_BYNAME, &arg); 544 if (res == NSS_NOTFOUND || res == NSS_UNAVAIL) { 545 arg.h_errno = NETDB_SUCCESS; 546 res = nss_search(&db_root_hosts, _initf_hosts, 547 NSS_DBOP_HOSTS_BYNAME, &arg); 548 } 549 arg.status = res; 550 *h_errnop = arg.h_errno; 551 return ((struct hostent *)NSS_XbyY_FINI(&arg)); 552 } 553 554 /* 555 * Convert an IP to a host name. 556 */ 557 ns_ldap_return_code 558 __s_api_ip2hostname(char *ipaddr, char **hostname) { 559 struct in_addr in; 560 struct in6_addr in6; 561 struct hostent *hp = NULL, hostEnt; 562 char buffer[NSS_BUFLEN_HOSTS]; 563 int buflen = NSS_BUFLEN_HOSTS; 564 char *start = NULL, 565 *end = NULL, 566 delim = '\0'; 567 char *port = NULL, 568 *addr = NULL; 569 int errorNum = 0, 570 len = 0; 571 572 if (ipaddr == NULL || hostname == NULL) 573 return (NS_LDAP_INVALID_PARAM); 574 *hostname = NULL; 575 if ((addr = strdup(ipaddr)) == NULL) 576 return (NS_LDAP_MEMORY); 577 578 if (addr[0] == '[') { 579 /* 580 * Assume it's [ipv6]:port 581 * Extract ipv6 IP 582 */ 583 start = &addr[1]; 584 if ((end = strchr(addr, ']')) != NULL) { 585 *end = '\0'; 586 delim = ']'; 587 if (*(end + 1) == ':') 588 /* extract port */ 589 port = end + 2; 590 } else { 591 free(addr); 592 return (NS_LDAP_INVALID_PARAM); 593 } 594 } else if ((end = strchr(addr, ':')) != NULL) { 595 /* assume it's ipv4:port */ 596 *end = '\0'; 597 delim = ':'; 598 start = addr; 599 port = end + 1; 600 } else 601 /* No port */ 602 start = addr; 603 604 605 if (inet_pton(AF_INET, start, &in) == 1) { 606 /* IPv4 */ 607 hp = _filter_gethostbyaddr_r((char *)&in, 608 sizeof (in.s_addr), 609 AF_INET, 610 &hostEnt, 611 buffer, 612 buflen, 613 &errorNum); 614 if (hp && hp->h_name) { 615 /* hostname + '\0' */ 616 len = strlen(hp->h_name) + 1; 617 if (port) 618 /* ':' + port */ 619 len += strlen(port) + 1; 620 if ((*hostname = malloc(len)) == NULL) { 621 free(addr); 622 return (NS_LDAP_MEMORY); 623 } 624 625 if (port) 626 (void) snprintf(*hostname, len, "%s:%s", 627 hp->h_name, port); 628 else 629 (void) strlcpy(*hostname, hp->h_name, len); 630 631 free(addr); 632 return (NS_LDAP_SUCCESS); 633 } else { 634 free(addr); 635 return (NS_LDAP_NOTFOUND); 636 } 637 } else if (inet_pton(AF_INET6, start, &in6) == 1) { 638 /* IPv6 */ 639 hp = _filter_gethostbyaddr_r((char *)&in6, 640 sizeof (in6.s6_addr), 641 AF_INET6, 642 &hostEnt, 643 buffer, 644 buflen, 645 &errorNum); 646 if (hp && hp->h_name) { 647 /* hostname + '\0' */ 648 len = strlen(hp->h_name) + 1; 649 if (port) 650 /* ':' + port */ 651 len += strlen(port) + 1; 652 if ((*hostname = malloc(len)) == NULL) { 653 free(addr); 654 return (NS_LDAP_MEMORY); 655 } 656 657 if (port) 658 (void) snprintf(*hostname, len, "%s:%s", 659 hp->h_name, port); 660 else 661 (void) strlcpy(*hostname, hp->h_name, len); 662 663 free(addr); 664 return (NS_LDAP_SUCCESS); 665 } else { 666 free(addr); 667 return (NS_LDAP_NOTFOUND); 668 } 669 } else { 670 /* 671 * A hostname 672 * Return it as is 673 */ 674 if (end) 675 *end = delim; 676 *hostname = addr; 677 return (NS_LDAP_SUCCESS); 678 } 679 } 680 681 /* 682 * This function obtains data returned by an LDAP search request and puts it 683 * in a string in the ldap_cachmgr(1) door call format. 684 * 685 * INPUT: 686 * ld - a pointer to an LDAP structure used for a search operation, 687 * result_msg - a pointer to an LDAPMessage returned by the search, 688 * include_names - if set to INCLUDE_ATTR_NAMES, the output buffer will 689 * contain attribute names. 690 * Otherwise, only values will be return. 691 * 692 * OUTPUT: 693 * a buffer containing server info in the following format: 694 * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...] 695 * Should be free'ed by the caller. 696 */ 697 static 698 ns_ldap_return_code 699 convert_to_door_line(LDAP* ld, 700 LDAPMessage *result_msg, 701 int include_names, 702 int is_profile, 703 char **door_line) 704 { 705 uint32_t total_length = 0, attr_len = 0, i; 706 LDAPMessage *e; 707 char *a, **vals; 708 BerElement *ber; 709 int seen_objectclass = 0, rewind = 0; 710 711 if (!door_line) { 712 return (NS_LDAP_INVALID_PARAM); 713 } 714 *door_line = NULL; 715 716 if ((e = ldap_first_entry(ld, result_msg)) == NULL) { 717 return (NS_LDAP_NOTFOUND); 718 } 719 720 /* calculate length of received data */ 721 for (a = ldap_first_attribute(ld, e, &ber); 722 a != NULL; 723 a = ldap_next_attribute(ld, e, ber)) { 724 725 if ((vals = ldap_get_values(ld, e, a)) != NULL) { 726 for (i = 0; vals[i] != NULL; i++) { 727 total_length += (include_names ? 728 strlen(a) : 0) + 729 strlen(vals[i]) + 730 strlen(DOORLINESEP) +1; 731 } 732 ldap_value_free(vals); 733 } 734 ldap_memfree(a); 735 } 736 if (ber != NULL) { 737 ber_free(ber, 0); 738 } 739 740 if (total_length == 0) { 741 return (NS_LDAP_NOTFOUND); 742 } 743 744 /* copy the data */ 745 /* add 1 for the last '\0' */ 746 *door_line = (char *)malloc(total_length + 1); 747 if (*door_line == NULL) { 748 return (NS_LDAP_MEMORY); 749 } 750 751 /* make it an empty string first */ 752 **door_line = '\0'; 753 a = ldap_first_attribute(ld, e, &ber); 754 while (a != NULL) { 755 if (is_profile) { 756 /* 757 * If we're processing DUAConfigProfile, we need to make 758 * sure we put objectclass attribute first. 759 * __s_api_create_config_door_str depends on that. 760 */ 761 if (seen_objectclass) { 762 if (strcasecmp(a, "objectclass") == 0) { 763 /* Skip objectclass now. */ 764 a = ldap_next_attribute(ld, e, ber); 765 continue; 766 } 767 } else { 768 if (strcasecmp(a, "objectclass") == 0) { 769 seen_objectclass = 1; 770 rewind = 1; 771 } else { 772 /* Skip all but objectclass first. */ 773 a = ldap_next_attribute(ld, e, ber); 774 continue; 775 } 776 } 777 } 778 779 if ((vals = ldap_get_values(ld, e, a)) != NULL) { 780 for (i = 0; vals[i] != NULL; i++) { 781 if (include_names) { 782 attr_len += strlen(a); 783 } 784 attr_len += strlen(vals[i]) + 785 strlen(DOORLINESEP) + 2; 786 if (include_names) { 787 (void) snprintf(*door_line + 788 strlen(*door_line), 789 attr_len, 790 "%s=%s%s", 791 a, vals[i], 792 DOORLINESEP); 793 } else { 794 (void) snprintf(*door_line + 795 strlen(*door_line), 796 attr_len, 797 "%s%s", 798 vals[i], 799 DOORLINESEP); 800 } 801 } 802 ldap_value_free(vals); 803 } 804 ldap_memfree(a); 805 806 /* Rewind */ 807 if (rewind) { 808 if (ber != NULL) { 809 ber_free(ber, 0); 810 } 811 a = ldap_first_attribute(ld, e, &ber); 812 rewind = 0; 813 } else { 814 a = ldap_next_attribute(ld, e, ber); 815 } 816 } 817 if (ber != NULL) { 818 ber_free(ber, 0); 819 } 820 821 if (e != result_msg) { 822 (void) ldap_msgfree(e); 823 } 824 825 return (NS_LDAP_SUCCESS); 826 } 827 828 /* 829 * This function looks up the base DN of a directory serving 830 * a specified domain name. 831 * 832 * INPUT: 833 * ld - a pointer to an LDAP structure used for the search operation, 834 * domain_name - the name of a domain. 835 * 836 * OUTPUT: 837 * a buffer containing a directory's base DN found. 838 * Should be free'ed by the caller. 839 */ 840 static 841 ns_ldap_return_code 842 getDirBaseDN(LDAP *ld, const char *domain_name, char **dir_base_dn) 843 { 844 struct timeval tv = {NS_DEFAULT_SEARCH_TIMEOUT, 0}; 845 char *attrs[2], *DNlist, *rest, *ptr; 846 char filter[BUFSIZ], *a = NULL; 847 int ldap_rc; 848 LDAPMessage *resultMsg = NULL; 849 ns_ldap_return_code ret_code; 850 851 /* Get the whole list of naming contexts residing on the server */ 852 attrs[0] = "namingcontexts"; 853 attrs[1] = NULL; 854 ldap_rc = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE, "(objectclass=*)", 855 attrs, 0, NULL, NULL, &tv, 0, &resultMsg); 856 switch (ldap_rc) { 857 /* If successful, the root DSE was found. */ 858 case LDAP_SUCCESS: 859 break; 860 /* 861 * If the root DSE was not found, the server does 862 * not comply with the LDAP v3 protocol. 863 */ 864 default: 865 if (resultMsg) { 866 (void) ldap_msgfree(resultMsg); 867 resultMsg = NULL; 868 } 869 870 return (NS_LDAP_OP_FAILED); 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 } 1016 1017 ret_code = convert_to_door_line(ld, 1018 resultMsg, 1019 INCLUDE_ATTR_NAMES, 1020 IS_PROFILE, 1021 profile); 1022 if (resultMsg) { 1023 (void) ldap_msgfree(resultMsg); 1024 resultMsg = NULL; 1025 } 1026 return (ret_code); 1027 } 1028 1029 /* 1030 * This function derives the directory's base DN from a provided domain name. 1031 * 1032 * INPUT: 1033 * domain_name - the name of a domain to be converted into a base DN, 1034 * buffer - contains the derived base DN, 1035 * buf_len - the length of the buffer. 1036 * 1037 * OUTPUT: 1038 * The function returns the address of the buffer or NULL. 1039 */ 1040 static 1041 char * 1042 domainname2baseDN(char *domain_name, char *buffer, uint16_t buf_len) 1043 { 1044 char *nextDC, *chr; 1045 uint16_t i, length; 1046 1047 if (!domain_name || !buffer || buf_len == 0) { 1048 return (NULL); 1049 } 1050 1051 buffer[0] = '\0'; 1052 nextDC = chr = domain_name; 1053 length = strlen(domain_name); 1054 for (i = 0; i < length + 1; ++i, ++chr) { 1055 /* Simply replace dots with "dc=" */ 1056 if (*chr != '.' && *chr != '\0') { 1057 continue; 1058 } 1059 *chr = '\0'; 1060 if (strlcat(buffer, "dc=", buf_len) >= buf_len) 1061 return (NULL); 1062 if (strlcat(buffer, nextDC, buf_len) >= buf_len) 1063 return (NULL); 1064 if (i < length) { 1065 /* 1066 * The end of the domain name 1067 * has not been reached yet 1068 */ 1069 if (strlcat(buffer, ",", buf_len) >= buf_len) 1070 return (NULL); 1071 nextDC = chr + 1; 1072 *chr = '.'; 1073 } 1074 } 1075 1076 return (buffer); 1077 } 1078 1079 /* 1080 * This function obtains the directory's base DN and a DUAProfile 1081 * from a specified server. 1082 * 1083 * INPUT: 1084 * server - a structure describing a server to connect to and 1085 * a DUAProfile to be obtained from the server, 1086 * cred - credentials to be used during establishing connections to 1087 * the server. 1088 * 1089 * OUTPUT: 1090 * dua_profile - a buffer containing the DUAProfile in the following format: 1091 * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...] 1092 * dir_base_dn - a buffer containing the base DN, 1093 * errorp - an error object describing an error, if any. 1094 * 1095 * All the output data structures should be free'ed by the caller. 1096 */ 1097 ns_ldap_return_code 1098 __ns_ldap_getConnectionInfoFromDUA(const ns_dir_server_t *server, 1099 const ns_cred_t *cred, 1100 char **dua_profile, 1101 char **dir_base_dn, 1102 ns_ldap_error_t **errorp) 1103 { 1104 char serverAddr[MAX_HOSTADDR_LEN]; 1105 char *dirBaseDN = NULL, *duaProfile = NULL; 1106 ns_cred_t default_cred; 1107 ns_ldap_return_code ret_code; 1108 1109 ns_config_t *config_struct = __s_api_create_config(); 1110 ConnectionID sessionId = 0; 1111 Connection *session = NULL; 1112 char errmsg[MAXERROR]; 1113 char buffer[NSS_BUFLEN_HOSTS]; 1114 ns_conn_user_t *cu = NULL; 1115 1116 if (errorp == NULL) { 1117 __s_api_destroy_config(config_struct); 1118 return (NS_LDAP_INVALID_PARAM); 1119 } 1120 1121 *errorp = NULL; 1122 1123 if (server == NULL) { 1124 __s_api_destroy_config(config_struct); 1125 return (NS_LDAP_INVALID_PARAM); 1126 } 1127 1128 if (config_struct == NULL) { 1129 return (NS_LDAP_MEMORY); 1130 } 1131 1132 /* 1133 * If no credentials are specified, try to establish a connection 1134 * as anonymous. 1135 */ 1136 if (!cred) { 1137 default_cred.cred.unix_cred.passwd = NULL; 1138 default_cred.cred.unix_cred.userID = NULL; 1139 default_cred.auth.type = NS_LDAP_AUTH_NONE; 1140 } 1141 1142 /* Now create a default LDAP configuration */ 1143 1144 (void) strncpy(buffer, server->server, sizeof (buffer)); 1145 if (__ns_ldap_setParamValue(config_struct, NS_LDAP_SERVERS_P, buffer, 1146 errorp) != NS_LDAP_SUCCESS) { 1147 __s_api_destroy_config(config_struct); 1148 return (NS_LDAP_CONFIG); 1149 } 1150 1151 /* Put together the address and the port specified by the user app. */ 1152 if (server->port > 0) { 1153 (void) snprintf(serverAddr, 1154 sizeof (serverAddr), 1155 "%s:%hu", 1156 buffer, 1157 server->port); 1158 } else { 1159 (void) strncpy(serverAddr, buffer, sizeof (serverAddr)); 1160 } 1161 1162 /* 1163 * There is no default value for the 'Default Search Base DN' attribute. 1164 * Derive one from the domain name to make __s_api_crosscheck() happy. 1165 */ 1166 if (domainname2baseDN(server->domainName ? 1167 server->domainName : config_struct->domainName, 1168 buffer, NSS_BUFLEN_HOSTS) == NULL) { 1169 (void) snprintf(errmsg, 1170 sizeof (errmsg), 1171 gettext("Can not convert %s into a base DN name"), 1172 server->domainName ? 1173 server->domainName : config_struct->domainName); 1174 MKERROR(LOG_ERR, 1175 *errorp, 1176 NS_LDAP_INTERNAL, 1177 strdup(errmsg), 1178 NS_LDAP_MEMORY); 1179 __s_api_destroy_config(config_struct); 1180 return (NS_LDAP_INTERNAL); 1181 } 1182 if (__ns_ldap_setParamValue(config_struct, NS_LDAP_SEARCH_BASEDN_P, 1183 buffer, errorp) != NS_LDAP_SUCCESS) { 1184 __s_api_destroy_config(config_struct); 1185 return (NS_LDAP_CONFIG); 1186 } 1187 1188 if (__s_api_crosscheck(config_struct, errmsg, B_FALSE) != NS_SUCCESS) { 1189 __s_api_destroy_config(config_struct); 1190 return (NS_LDAP_CONFIG); 1191 } 1192 1193 __s_api_init_config(config_struct); 1194 1195 __s_api_setInitMode(); 1196 1197 cu = __s_api_conn_user_init(NS_CONN_USER_SEARCH, NULL, B_FALSE); 1198 if (cu == NULL) { 1199 return (NS_LDAP_INTERNAL); 1200 } 1201 1202 if ((ret_code = __s_api_getConnection(serverAddr, 1203 NS_LDAP_NEW_CONN, 1204 cred ? cred : &default_cred, 1205 &sessionId, 1206 &session, 1207 errorp, 1208 0, 1209 0, 1210 cu)) != NS_LDAP_SUCCESS) { 1211 __s_api_conn_user_free(cu); 1212 __s_api_unsetInitMode(); 1213 return (ret_code); 1214 } 1215 1216 __s_api_unsetInitMode(); 1217 1218 if ((ret_code = getDirBaseDN(session->ld, 1219 server->domainName ? 1220 server->domainName : 1221 config_struct->domainName, 1222 &dirBaseDN)) != NS_LDAP_SUCCESS) { 1223 (void) snprintf(errmsg, 1224 sizeof (errmsg), 1225 gettext("Can not find the " 1226 "nisDomainObject for domain %s\n"), 1227 server->domainName ? 1228 server->domainName : config_struct->domainName); 1229 MKERROR(LOG_ERR, 1230 *errorp, 1231 ret_code, 1232 strdup(errmsg), 1233 NS_LDAP_MEMORY); 1234 __s_api_conn_user_free(cu); 1235 DropConnection(sessionId, NS_LDAP_NEW_CONN); 1236 return (ret_code); 1237 } 1238 1239 /* 1240 * And here obtain a DUAProfile which will be used 1241 * as a real configuration. 1242 */ 1243 if ((ret_code = getDUAProfile(session->ld, 1244 dirBaseDN, 1245 server->profileName ? 1246 server->profileName : "default", 1247 &duaProfile)) != NS_LDAP_SUCCESS) { 1248 (void) snprintf(errmsg, 1249 sizeof (errmsg), 1250 gettext("Can not find the " 1251 "%s DUAProfile\n"), 1252 server->profileName ? 1253 server->profileName : "default"); 1254 MKERROR(LOG_ERR, 1255 *errorp, 1256 ret_code, 1257 strdup(errmsg), 1258 NS_LDAP_MEMORY); 1259 __s_api_conn_user_free(cu); 1260 DropConnection(sessionId, NS_LDAP_NEW_CONN); 1261 return (ret_code); 1262 } 1263 1264 if (dir_base_dn) { 1265 *dir_base_dn = dirBaseDN; 1266 } else { 1267 free(dirBaseDN); 1268 } 1269 1270 if (dua_profile) { 1271 *dua_profile = duaProfile; 1272 } else { 1273 free(duaProfile); 1274 } 1275 1276 __s_api_conn_user_free(cu); 1277 DropConnection(sessionId, NS_LDAP_NEW_CONN); 1278 1279 return (NS_LDAP_SUCCESS); 1280 } 1281 1282 /* 1283 * This function obtains the root DSE from a specified server. 1284 * 1285 * INPUT: 1286 * server_addr - an adress of a server to be connected to. 1287 * 1288 * OUTPUT: 1289 * root_dse - a buffer containing the root DSE in the following format: 1290 * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...] 1291 * For example: ( here | used as DOORLINESEP for visual purposes) 1292 * supportedControl=1.1.1.1|supportedSASLmechanisms=EXTERNAL 1293 * Should be free'ed by the caller. 1294 */ 1295 ns_ldap_return_code 1296 __ns_ldap_getRootDSE(const char *server_addr, 1297 char **root_dse, 1298 ns_ldap_error_t **errorp, 1299 int anon_fallback) 1300 { 1301 char errmsg[MAXERROR]; 1302 ns_ldap_return_code ret_code; 1303 1304 ConnectionID sessionId = 0; 1305 Connection *session = NULL; 1306 1307 struct timeval tv = {NS_DEFAULT_SEARCH_TIMEOUT, 0}; 1308 char *attrs[3]; 1309 int ldap_rc, ldaperrno = 0; 1310 LDAPMessage *resultMsg = NULL; 1311 void **paramVal = NULL; 1312 1313 ns_cred_t anon; 1314 ns_conn_user_t *cu = NULL; 1315 1316 if (errorp == NULL) { 1317 return (NS_LDAP_INVALID_PARAM); 1318 } 1319 1320 *errorp = NULL; 1321 1322 if (!root_dse) { 1323 return (NS_LDAP_INVALID_PARAM); 1324 } 1325 1326 if (!server_addr) { 1327 return (NS_LDAP_INVALID_PARAM); 1328 } 1329 1330 __s_api_setInitMode(); 1331 1332 cu = __s_api_conn_user_init(NS_CONN_USER_SEARCH, NULL, B_FALSE); 1333 if (cu == NULL) { 1334 return (NS_LDAP_INTERNAL); 1335 } 1336 1337 /* 1338 * All the credentials will be taken from the current 1339 * libsldap configuration. 1340 */ 1341 if ((ret_code = __s_api_getConnection(server_addr, 1342 NS_LDAP_NEW_CONN, 1343 NULL, 1344 &sessionId, 1345 &session, 1346 errorp, 1347 0, 1348 0, 1349 cu)) != NS_LDAP_SUCCESS) { 1350 /* Fallback to anonymous mode is disabled. Stop. */ 1351 if (anon_fallback == 0) { 1352 syslog(LOG_WARNING, 1353 gettext("libsldap: can not get the root DSE from " 1354 " the %s server: %s. " 1355 "Falling back to anonymous disabled.\n"), 1356 server_addr, 1357 errorp && *errorp && (*errorp)->message ? 1358 (*errorp)->message : ""); 1359 if (errorp != NULL && *errorp != NULL) { 1360 (void) __ns_ldap_freeError(errorp); 1361 } 1362 __s_api_unsetInitMode(); 1363 return (ret_code); 1364 } 1365 1366 /* 1367 * Fallback to anonymous, non-SSL mode for backward 1368 * compatibility reasons. This mode should only be used when 1369 * this function (__ns_ldap_getRootDSE) is called from 1370 * ldap_cachemgr(8). 1371 */ 1372 syslog(LOG_WARNING, 1373 gettext("libsldap: Falling back to anonymous, non-SSL" 1374 " mode for __ns_ldap_getRootDSE. %s\n"), 1375 errorp && *errorp && (*errorp)->message ? 1376 (*errorp)->message : ""); 1377 1378 /* Setup the anon credential for anonymous connection. */ 1379 (void) memset(&anon, 0, sizeof (ns_cred_t)); 1380 anon.auth.type = NS_LDAP_AUTH_NONE; 1381 1382 if (*errorp != NULL) { 1383 (void) __ns_ldap_freeError(errorp); 1384 } 1385 *errorp = NULL; 1386 1387 ret_code = __s_api_getConnection(server_addr, 1388 NS_LDAP_NEW_CONN, 1389 &anon, 1390 &sessionId, 1391 &session, 1392 errorp, 1393 0, 1394 0, 1395 cu); 1396 1397 if (ret_code != NS_LDAP_SUCCESS) { 1398 __s_api_conn_user_free(cu); 1399 __s_api_unsetInitMode(); 1400 return (ret_code); 1401 } 1402 } 1403 1404 __s_api_unsetInitMode(); 1405 1406 /* get search timeout value */ 1407 (void) __ns_ldap_getParam(NS_LDAP_SEARCH_TIME_P, ¶mVal, errorp); 1408 if (paramVal != NULL && *paramVal != NULL) { 1409 tv.tv_sec = **((int **)paramVal); 1410 (void) __ns_ldap_freeParam(¶mVal); 1411 } 1412 if (*errorp != NULL) { 1413 (void) __ns_ldap_freeError(errorp); 1414 } 1415 1416 /* Get root DSE from the server specified by the caller. */ 1417 attrs[0] = "supportedControl"; 1418 attrs[1] = "supportedsaslmechanisms"; 1419 attrs[2] = NULL; 1420 ldap_rc = ldap_search_ext_s(session->ld, 1421 "", 1422 LDAP_SCOPE_BASE, 1423 "(objectclass=*)", 1424 attrs, 1425 0, 1426 NULL, 1427 NULL, 1428 &tv, 1429 0, 1430 &resultMsg); 1431 1432 if (ldap_rc != LDAP_SUCCESS) { 1433 /* 1434 * If the root DSE was not found, the server does 1435 * not comply with the LDAP v3 protocol. 1436 */ 1437 (void) ldap_get_option(session->ld, 1438 LDAP_OPT_ERROR_NUMBER, 1439 &ldaperrno); 1440 (void) snprintf(errmsg, 1441 sizeof (errmsg), 1442 gettext(ldap_err2string(ldaperrno))); 1443 MKERROR(LOG_ERR, 1444 *errorp, 1445 NS_LDAP_OP_FAILED, 1446 strdup(errmsg), 1447 NS_LDAP_MEMORY); 1448 1449 if (resultMsg) { 1450 (void) ldap_msgfree(resultMsg); 1451 resultMsg = NULL; 1452 } 1453 1454 __s_api_conn_user_free(cu); 1455 DropConnection(sessionId, NS_LDAP_NEW_CONN); 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 case ENOMEM: 1811 (*new_list)->nsServers[i]->status = 1812 INFO_SERVER_ERROR; 1813 continue; 1814 default: 1815 thrPool[i] = thrID; 1816 continue; 1817 } 1818 } 1819 1820 for (i = 0; i < srvListLength; ++i) { 1821 if (thrPool[i] != 0 && 1822 thr_join(thrPool[i], NULL, &status) == 0) { 1823 if (status == NULL) { 1824 /* 1825 * Some memory allocation problems occured. Just 1826 * ignore the server and hope there will be some 1827 * other good ones. 1828 */ 1829 (*new_list)->nsServers[i]->status = 1830 INFO_SERVER_ERROR; 1831 } 1832 free(status); 1833 } 1834 } 1835 1836 __s_api_free2dArray(serverList); 1837 free(thrPool); 1838 1839 if (retCode == NS_LDAP_MEMORY) { 1840 (void) disposeOfOldList(*new_list); 1841 return (NS_LDAP_MEMORY); 1842 } 1843 1844 return (NS_LDAP_SUCCESS); 1845 } 1846 1847 /* 1848 * This functions replaces the local list of root DSEs with a new one and starts 1849 * a thread destroying the old list. There is no need for other threads to wait 1850 * until the old list will be destroyed. 1851 * Since it is possible that more than one thread can start creating the list, 1852 * this function should be protected by mutexes to be sure that only one thread 1853 * performs the initialization. 1854 */ 1855 static 1856 ns_ldap_return_code 1857 initGlobalList(ns_ldap_error_t **error) 1858 { 1859 dir_server_list_t *new_list, *old_list; 1860 ns_ldap_return_code ret_code; 1861 thread_t tid; 1862 1863 ret_code = createDirServerList(&new_list, error); 1864 if (ret_code != NS_LDAP_SUCCESS) { 1865 return (ret_code); 1866 } 1867 1868 old_list = dir_servers.list; 1869 dir_servers.list = new_list; 1870 1871 if (old_list) { 1872 (void) thr_create(NULL, 1873 0, 1874 disposeOfOldList, 1875 old_list, 1876 THR_DETACHED, 1877 &tid); 1878 } 1879 1880 return (NS_LDAP_SUCCESS); 1881 } 1882 1883 static 1884 struct { 1885 char *authMech; 1886 ns_auth_t auth; 1887 } authArray[] = {{"none", {NS_LDAP_AUTH_NONE, 1888 NS_LDAP_TLS_NONE, 1889 NS_LDAP_SASL_NONE, 1890 NS_LDAP_SASLOPT_NONE}}, 1891 {"simple", {NS_LDAP_AUTH_SIMPLE, 1892 NS_LDAP_TLS_NONE, 1893 NS_LDAP_SASL_NONE, 1894 NS_LDAP_SASLOPT_NONE}}, 1895 {"tls:simple", {NS_LDAP_AUTH_TLS, 1896 NS_LDAP_TLS_SIMPLE, 1897 NS_LDAP_SASL_NONE, 1898 NS_LDAP_SASLOPT_NONE}}, 1899 {"tls:sasl/CRAM-MD5", {NS_LDAP_AUTH_TLS, 1900 NS_LDAP_TLS_SASL, 1901 NS_LDAP_SASL_CRAM_MD5, 1902 NS_LDAP_SASLOPT_NONE}}, 1903 {"tls:sasl/DIGEST-MD5", {NS_LDAP_AUTH_TLS, 1904 NS_LDAP_TLS_SASL, 1905 NS_LDAP_SASL_DIGEST_MD5, 1906 NS_LDAP_SASLOPT_NONE}}, 1907 {"sasl/CRAM-MD5", {NS_LDAP_AUTH_SASL, 1908 NS_LDAP_TLS_SASL, 1909 NS_LDAP_SASL_CRAM_MD5, 1910 NS_LDAP_SASLOPT_NONE}}, 1911 {"sasl/DIGEST-MD5", {NS_LDAP_AUTH_SASL, 1912 NS_LDAP_TLS_SASL, 1913 NS_LDAP_SASL_DIGEST_MD5, 1914 NS_LDAP_SASLOPT_NONE}}, 1915 {"sasl/GSSAPI", {NS_LDAP_AUTH_SASL, 1916 NS_LDAP_TLS_SASL, 1917 NS_LDAP_SASL_GSSAPI, 1918 NS_LDAP_SASLOPT_PRIV | NS_LDAP_SASLOPT_INT}}, 1919 {NULL, {NS_LDAP_AUTH_NONE, 1920 NS_LDAP_TLS_NONE, 1921 NS_LDAP_SASL_NONE, 1922 NS_LDAP_SASLOPT_NONE}}}; 1923 1924 ns_ldap_return_code 1925 __ns_ldap_initAuth(const char *auth_mech, 1926 ns_auth_t *auth, 1927 ns_ldap_error_t **errorp) 1928 { 1929 uint32_t i; 1930 char errmsg[MAXERROR]; 1931 1932 if (auth_mech == NULL) { 1933 (void) snprintf(errmsg, 1934 sizeof (errmsg), 1935 gettext("Invalid authentication method specified\n")); 1936 MKERROR(LOG_WARNING, 1937 *errorp, 1938 NS_LDAP_INTERNAL, 1939 strdup(errmsg), 1940 NS_LDAP_MEMORY); 1941 return (NS_LDAP_INTERNAL); 1942 } 1943 1944 for (i = 0; authArray[i].authMech != NULL; ++i) { 1945 if (strcasecmp(auth_mech, authArray[i].authMech) == 0) { 1946 *auth = authArray[i].auth; 1947 return (NS_LDAP_SUCCESS); 1948 } 1949 } 1950 1951 (void) snprintf(errmsg, 1952 sizeof (errmsg), 1953 gettext("Invalid authentication method specified\n")); 1954 MKERROR(LOG_WARNING, 1955 *errorp, 1956 NS_LDAP_INTERNAL, 1957 strdup(errmsg), 1958 NS_LDAP_MEMORY); 1959 return (NS_LDAP_INTERNAL); 1960 } 1961 1962 /* 1963 * This function "informs" libsldap that a client application has specified 1964 * a directory to use. The function obtains a DUAProfile, credentials, 1965 * and naming context. During all further operations on behalf 1966 * of the application requested a standalone schema libsldap will use 1967 * the information obtained by __ns_ldap_initStandalone() instead of 1968 * door_call(3C)ing ldap_cachemgr(8). 1969 * 1970 * INPUT: 1971 * sa_conf - a structure describing where and in which way to obtain all 1972 * the configuration describing how to communicate to 1973 * a choosen LDAP directory, 1974 * errorp - an error object describing an error occured. 1975 */ 1976 ns_ldap_return_code 1977 __ns_ldap_initStandalone(const ns_standalone_conf_t *sa_conf, 1978 ns_ldap_error_t **errorp) { 1979 1980 ns_cred_t user_cred = {{NS_LDAP_AUTH_NONE, 1981 NS_LDAP_TLS_NONE, 1982 NS_LDAP_SASL_NONE, 1983 NS_LDAP_SASLOPT_NONE}, 1984 NULL, 1985 {NULL, NULL}}; 1986 char *dua_profile = NULL; 1987 char errmsg[MAXERROR]; 1988 ns_config_t *cfg; 1989 int ret_code; 1990 1991 if (sa_conf->SA_BIND_DN == NULL && sa_conf->SA_BIND_PWD != NULL || 1992 sa_conf->SA_BIND_DN != NULL && sa_conf->SA_BIND_PWD == NULL) { 1993 (void) snprintf(errmsg, 1994 sizeof (errmsg), 1995 gettext("Bind DN and bind password" 1996 " must both be provided\n")); 1997 MKERROR(LOG_ERR, 1998 *errorp, 1999 NS_CONFIG_NOTLOADED, 2000 strdup(errmsg), 2001 NS_LDAP_MEMORY); 2002 return (NS_LDAP_INTERNAL); 2003 } 2004 2005 switch (sa_conf->type) { 2006 case NS_LDAP_SERVER: 2007 if (sa_conf->SA_BIND_DN != NULL) { 2008 user_cred.cred.unix_cred.userID = sa_conf->SA_BIND_DN; 2009 user_cred.auth.type = NS_LDAP_AUTH_SIMPLE; 2010 } 2011 2012 if (sa_conf->SA_BIND_PWD != NULL) { 2013 user_cred.cred.unix_cred.passwd = sa_conf->SA_BIND_PWD; 2014 } 2015 2016 if (sa_conf->SA_AUTH != NULL) { 2017 user_cred.auth.type = sa_conf->SA_AUTH->type; 2018 user_cred.auth.tlstype = sa_conf->SA_AUTH->tlstype; 2019 user_cred.auth.saslmech = sa_conf->SA_AUTH->saslmech; 2020 user_cred.auth.saslopt = sa_conf->SA_AUTH->saslopt; 2021 } 2022 2023 if (sa_conf->SA_CERT_PATH != NULL) { 2024 user_cred.hostcertpath = sa_conf->SA_CERT_PATH; 2025 } 2026 2027 ret_code = __ns_ldap_getConnectionInfoFromDUA( 2028 &sa_conf->ds_profile.server, 2029 &user_cred, 2030 &dua_profile, 2031 NULL, 2032 errorp); 2033 if (ret_code != NS_LDAP_SUCCESS) { 2034 return (ret_code); 2035 } 2036 2037 cfg = __s_api_create_config_door_str(dua_profile, errorp); 2038 if (cfg == NULL) { 2039 free(dua_profile); 2040 return (NS_LDAP_CONFIG); 2041 } 2042 2043 if (sa_conf->SA_CERT_PATH != NULL) { 2044 char *certPathAttr; 2045 ParamIndexType type; 2046 2047 switch (cfg->version) { 2048 case NS_LDAP_V1: 2049 certPathAttr = "NS_LDAP_CERT_PATH"; 2050 break; 2051 default: /* Version 2 */ 2052 certPathAttr = "NS_LDAP_HOST_CERTPATH"; 2053 break; 2054 } 2055 2056 if (__s_api_get_versiontype(cfg, 2057 certPathAttr, 2058 &type) == 0 && 2059 (ret_code = __ns_ldap_setParamValue(cfg, 2060 type, 2061 sa_conf->SA_CERT_PATH, 2062 errorp)) != NS_LDAP_SUCCESS) { 2063 __s_api_destroy_config(cfg); 2064 return (ret_code); 2065 } 2066 } 2067 2068 if (sa_conf->SA_BIND_DN != NULL && 2069 sa_conf->SA_BIND_PWD != NULL) { 2070 char *authMethods; 2071 2072 authMethods = __s_api_strValue(cfg, NS_LDAP_AUTH_P, 2073 NS_FILE_FMT); 2074 if (authMethods != NULL && 2075 strstr(authMethods, "sasl/GSSAPI") != NULL) { 2076 /* 2077 * The received DUAProfile specifies 2078 * sasl/GSSAPI as an auth. mechanism. 2079 * The bind DN and password will be 2080 * ignored. 2081 */ 2082 syslog(LOG_INFO, gettext("sasl/GSSAPI will be " 2083 "used as an authentication method. " 2084 "The bind DN and password will " 2085 "be ignored.\n")); 2086 free(authMethods); 2087 break; 2088 } 2089 2090 if (authMethods != NULL) 2091 free(authMethods); 2092 2093 if (__ns_ldap_setParamValue(cfg, 2094 NS_LDAP_BINDDN_P, 2095 sa_conf->SA_BIND_DN, 2096 errorp) != NS_LDAP_SUCCESS) { 2097 __s_api_destroy_config(cfg); 2098 return (NS_LDAP_CONFIG); 2099 } 2100 2101 if (__ns_ldap_setParamValue(cfg, 2102 NS_LDAP_BINDPASSWD_P, 2103 sa_conf->SA_BIND_PWD, 2104 errorp) != NS_LDAP_SUCCESS) { 2105 __s_api_destroy_config(cfg); 2106 return (NS_LDAP_CONFIG); 2107 } 2108 } 2109 2110 break; 2111 default: /* NS_CACHEMGR */ 2112 return (NS_LDAP_SUCCESS); 2113 } 2114 2115 __s_api_init_config(cfg); 2116 /* Connection management should use the new config now. */ 2117 __s_api_reinit_conn_mgmt_new_config(cfg); 2118 __ns_ldap_setServer(TRUE); 2119 2120 (void) mutex_lock(&dir_servers.listReplaceLock); 2121 if ((ret_code = initGlobalList(errorp)) != NS_SUCCESS) { 2122 (void) mutex_unlock(&dir_servers.listReplaceLock); 2123 return (ret_code); 2124 } 2125 dir_servers.standalone = 1; 2126 (void) mutex_unlock(&dir_servers.listReplaceLock); 2127 2128 return (NS_LDAP_SUCCESS); 2129 } 2130 2131 /* 2132 * INPUT: 2133 * serverAddr is the address of a server and 2134 * request is one of the following: 2135 * NS_CACHE_NEW: get a new server address, addr is ignored. 2136 * NS_CACHE_NORESP: get the next one, remove addr from list. 2137 * NS_CACHE_NEXT: get the next one, keep addr on list. 2138 * NS_CACHE_WRITE: get a non-replica server, if possible, if not, same 2139 * as NS_CACHE_NEXT. 2140 * addrType: 2141 * NS_CACHE_ADDR_IP: return server address as is, this is default. 2142 * NS_CACHE_ADDR_HOSTNAME: return server addess as FQDN format, only 2143 * self credential case requires such format. 2144 * OUTPUT: 2145 * ret 2146 * 2147 * a structure of type ns_server_info_t containing the server address 2148 * or name, server controls and supported SASL mechanisms. 2149 * NOTE: Caller should allocate space for the structure and free 2150 * all the space allocated by the function for the information contained 2151 * in the structure. 2152 * 2153 * error - an error object describing an error, if any. 2154 */ 2155 ns_ldap_return_code 2156 __s_api_findRootDSE(const char *request, 2157 const char *serverAddr, 2158 const char *addrType, 2159 ns_server_info_t *ret, 2160 ns_ldap_error_t **error) 2161 { 2162 dir_server_list_t *current_list = NULL; 2163 ns_ldap_return_code ret_code; 2164 long i = 0; 2165 int matched = FALSE; 2166 dir_server_t *server = NULL; 2167 char errmsg[MAXERROR]; 2168 2169 (void) mutex_lock(&dir_servers.listReplaceLock); 2170 if (dir_servers.list == NULL) { 2171 (void) mutex_unlock(&dir_servers.listReplaceLock); 2172 (void) snprintf(errmsg, 2173 sizeof (errmsg), 2174 gettext("The list of root DSEs is empty: " 2175 "the Standalone mode was not properly initialized")); 2176 MKERROR(LOG_ERR, 2177 *error, 2178 NS_CONFIG_NOTLOADED, 2179 strdup(errmsg), 2180 NS_LDAP_MEMORY); 2181 return (NS_LDAP_INTERNAL); 2182 } 2183 2184 current_list = dir_servers.list; 2185 (void) rw_rdlock(¤t_list->listDestroyLock); 2186 (void) mutex_unlock(&dir_servers.listReplaceLock); 2187 2188 /* 2189 * The code below is mostly the clone of the 2190 * ldap_cachemgr::cachemgr_getldap.c::getldap_get_serverInfo() function. 2191 * Currently we have two different server lists: one is maintained 2192 * by libsldap ('standalone' mode), the other is in ldap_cachemgr 2193 * (a part of its standard functionality). 2194 */ 2195 2196 /* 2197 * If NS_CACHE_NEW, or the server info is new, 2198 * starts from the beginning of the list. 2199 */ 2200 (void) mutex_lock(¤t_list->nsServers[0]->updateStatus); 2201 if (strcmp(request, NS_CACHE_NEW) == 0 || 2202 current_list->nsServers[0]->info == INFO_STATUS_NEW) { 2203 matched = TRUE; 2204 } 2205 (void) mutex_unlock(¤t_list->nsServers[i]->updateStatus); 2206 2207 for (i = 0; current_list->nsServers[i]; ++i) { 2208 /* 2209 * Lock the updateStatus mutex to 2210 * make sure the server status stays the same 2211 * while the data is being processed. 2212 */ 2213 if (matched == FALSE && 2214 strcmp(current_list->nsServers[i]->ip, 2215 serverAddr) == 0) { 2216 matched = TRUE; 2217 if (strcmp(request, NS_CACHE_NORESP) == 0) { 2218 2219 /* 2220 * if the server has already been removed, 2221 * don't bother. 2222 */ 2223 (void) mutex_lock(¤t_list-> 2224 nsServers[i]->updateStatus); 2225 if (current_list->nsServers[i]->status == 2226 INFO_SERVER_REMOVED) { 2227 (void) mutex_unlock(¤t_list-> 2228 nsServers[i]-> 2229 updateStatus); 2230 continue; 2231 } 2232 (void) mutex_unlock(¤t_list-> 2233 nsServers[i]-> 2234 updateStatus); 2235 2236 /* 2237 * if the information is new, 2238 * give this server one more chance. 2239 */ 2240 (void) mutex_lock(¤t_list-> 2241 nsServers[i]-> 2242 updateStatus); 2243 if (current_list->nsServers[i]->info == 2244 INFO_STATUS_NEW && 2245 current_list->nsServers[i]->status == 2246 INFO_SERVER_UP) { 2247 server = current_list->nsServers[i]; 2248 (void) mutex_unlock(¤t_list-> 2249 nsServers[i]-> 2250 updateStatus); 2251 break; 2252 } else { 2253 /* 2254 * it is recommended that 2255 * before removing the 2256 * server from the list, 2257 * the server should be 2258 * contacted one more time 2259 * to make sure that it is 2260 * really unavailable. 2261 * For now, just trust the client 2262 * (i.e., the sldap library) 2263 * that it knows what it is 2264 * doing and would not try 2265 * to mess up the server 2266 * list. 2267 */ 2268 current_list->nsServers[i]->status = 2269 INFO_SERVER_REMOVED; 2270 (void) mutex_unlock(¤t_list-> 2271 nsServers[i]-> 2272 updateStatus); 2273 continue; 2274 } 2275 } else { 2276 /* 2277 * req == NS_CACHE_NEXT or NS_CACHE_WRITE 2278 */ 2279 continue; 2280 } 2281 } 2282 2283 if (matched) { 2284 if (strcmp(request, NS_CACHE_WRITE) == 0) { 2285 /* 2286 * ldap_cachemgr checks here if the server 2287 * is not a non-replica server (a server 2288 * of type INFO_RW_WRITEABLE). But currently 2289 * it considers all the servers in its list 2290 * as those. 2291 */ 2292 (void) mutex_lock(¤t_list-> 2293 nsServers[i]-> 2294 updateStatus); 2295 if (current_list->nsServers[i]->status == 2296 INFO_SERVER_UP) { 2297 (void) mutex_unlock(¤t_list-> 2298 nsServers[i]-> 2299 updateStatus); 2300 server = current_list->nsServers[i]; 2301 break; 2302 } 2303 } else { 2304 (void) mutex_lock(¤t_list-> 2305 nsServers[i]-> 2306 updateStatus); 2307 if (current_list->nsServers[i]->status == 2308 INFO_SERVER_UP) { 2309 (void) mutex_unlock(¤t_list-> 2310 nsServers[i]-> 2311 updateStatus); 2312 server = current_list->nsServers[i]; 2313 break; 2314 } 2315 } 2316 2317 (void) mutex_unlock(¤t_list-> 2318 nsServers[i]-> 2319 updateStatus); 2320 } 2321 } 2322 2323 if (server == NULL) { 2324 (void) rw_unlock(¤t_list->listDestroyLock); 2325 (void) snprintf(errmsg, 2326 sizeof (errmsg), 2327 gettext("No servers are available")); 2328 MKERROR(LOG_ERR, 2329 *error, 2330 NS_CONFIG_NOTLOADED, 2331 strdup(errmsg), 2332 NS_LDAP_MEMORY); 2333 return (NS_LDAP_NOTFOUND); 2334 } 2335 2336 (void) mutex_lock(&server->updateStatus); 2337 server->info = INFO_STATUS_OLD; 2338 (void) mutex_unlock(&server->updateStatus); 2339 2340 if (ret == NULL) { 2341 (void) rw_unlock(¤t_list->listDestroyLock); 2342 return (NS_LDAP_SUCCESS); 2343 } 2344 2345 if (strcmp(addrType, NS_CACHE_ADDR_HOSTNAME) == 0) { 2346 ret_code = __s_api_ip2hostname(server->ip, &ret->serverFQDN); 2347 if (ret_code != NS_LDAP_SUCCESS) { 2348 (void) snprintf(errmsg, 2349 sizeof (errmsg), 2350 gettext("The %s address " 2351 "can not be resolved into " 2352 "a host name. Returning " 2353 "the address as it is."), 2354 server->ip); 2355 MKERROR(LOG_ERR, 2356 *error, 2357 NS_CONFIG_NOTLOADED, 2358 strdup(errmsg), 2359 NS_LDAP_MEMORY); 2360 return (NS_LDAP_INTERNAL); 2361 } 2362 } 2363 2364 ret->server = strdup(server->ip); 2365 2366 ret->controls = __s_api_cp2dArray(server->controls); 2367 ret->saslMechanisms = __s_api_cp2dArray(server->saslMech); 2368 2369 (void) rw_unlock(¤t_list->listDestroyLock); 2370 2371 return (NS_LDAP_SUCCESS); 2372 } 2373 2374 /* 2375 * This function iterates through the list of the configured LDAP servers 2376 * and "pings" those which are marked as removed or if any error occurred 2377 * during the previous receiving of the server's root DSE. If the 2378 * function is able to reach such a server and get its root DSE, it 2379 * marks the server as on-line. Otherwise, the server's status is set 2380 * to "Error". 2381 * For each server the function tries to connect to, it fires up 2382 * a separate thread and then waits until all the treads finish. 2383 * The function returns NS_LDAP_INTERNAL if the Standalone mode was not 2384 * initialized or was canceled prior to an invocation of 2385 * __ns_ldap_pingOfflineServers(). 2386 */ 2387 ns_ldap_return_code 2388 __ns_ldap_pingOfflineServers(void) 2389 { 2390 dir_server_list_t *current_list = NULL; 2391 ns_ldap_return_code retCode = NS_LDAP_SUCCESS; 2392 long srvListLength, i = 0; 2393 thread_t *thrPool, thrID; 2394 void *status = NULL; 2395 2396 (void) mutex_lock(&dir_servers.listReplaceLock); 2397 if (dir_servers.list == NULL) { 2398 (void) mutex_unlock(&dir_servers.listReplaceLock); 2399 return (NS_LDAP_INTERNAL); 2400 } 2401 2402 current_list = dir_servers.list; 2403 (void) rw_wrlock(¤t_list->listDestroyLock); 2404 (void) mutex_unlock(&dir_servers.listReplaceLock); 2405 2406 while (current_list->nsServers[i] != NULL) { 2407 ++i; 2408 } 2409 srvListLength = i; 2410 2411 thrPool = calloc(srvListLength, sizeof (thread_t)); 2412 if (thrPool == NULL) { 2413 (void) rw_unlock(¤t_list->listDestroyLock); 2414 return (NS_LDAP_MEMORY); 2415 } 2416 2417 for (i = 0; i < srvListLength; ++i) { 2418 if (current_list->nsServers[i]->status != INFO_SERVER_REMOVED && 2419 current_list->nsServers[i]->status != INFO_SERVER_ERROR) { 2420 continue; 2421 } 2422 current_list->nsServers[i]->status = INFO_SERVER_CONNECTING; 2423 current_list->nsServers[i]->info = INFO_STATUS_NEW; 2424 2425 __s_api_free2dArray(current_list->nsServers[i]->controls); 2426 current_list->nsServers[i]->controls = NULL; 2427 __s_api_free2dArray(current_list->nsServers[i]->saslMech); 2428 current_list->nsServers[i]->saslMech = NULL; 2429 2430 switch (thr_create(NULL, 2431 0, 2432 create_ns_servers_entry, 2433 current_list->nsServers[i], 2434 0, 2435 &thrID)) { 2436 case EAGAIN: 2437 current_list->nsServers[i]->status = INFO_SERVER_ERROR; 2438 continue; 2439 case ENOMEM: 2440 current_list->nsServers[i]->status = INFO_SERVER_ERROR; 2441 retCode = NS_LDAP_MEMORY; 2442 break; 2443 default: 2444 thrPool[i] = thrID; 2445 continue; 2446 } 2447 /* A memory allocation error has occured */ 2448 break; 2449 2450 } 2451 2452 for (i = 0; i < srvListLength; ++i) { 2453 if (thrPool[i] != 0 && 2454 thr_join(thrPool[i], NULL, &status) == 0) { 2455 if (status == NULL) { 2456 current_list->nsServers[i]->status = 2457 INFO_SERVER_ERROR; 2458 retCode = NS_LDAP_MEMORY; 2459 } 2460 free(status); 2461 } 2462 } 2463 2464 (void) rw_unlock(¤t_list->listDestroyLock); 2465 2466 free(thrPool); 2467 2468 return (retCode); 2469 } 2470