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