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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <synch.h> 30 #include <strings.h> 31 #include <sys/time.h> 32 #include <ctype.h> 33 34 #include "ldap_op.h" 35 #include "ldap_util.h" 36 #include "ldap_structs.h" 37 #include "ldap_ruleval.h" 38 #include "ldap_attr.h" 39 #include "ldap_print.h" 40 #include "ldap_glob.h" 41 42 #include "nis_parse_ldap_conf.h" 43 44 #ifndef LDAPS_PORT 45 #define LDAPS_PORT 636 46 #endif 47 48 static int setupConList(char *serverList, char *who, 49 char *cred, auth_method_t method); 50 51 52 /* 53 * Build one of our internal LDAP search structures, containing copies of 54 * the supplied input. return NULL in case of error. 55 * 56 * If 'filter' is NULL, build an AND-filter using the filter components. 57 */ 58 __nis_ldap_search_t * 59 buildLdapSearch(char *base, int scope, int numFilterComps, char **filterComp, 60 char *filter, char **attrs, int attrsonly, int isDN) { 61 __nis_ldap_search_t *ls; 62 char **a; 63 int i, na, err = 0; 64 char *myself = "buildLdapSearch"; 65 66 ls = am(myself, sizeof (*ls)); 67 if (ls == 0) 68 return (0); 69 70 ls->base = sdup(myself, T, base); 71 if (ls->base == 0 && base != 0) 72 err++; 73 ls->scope = scope; 74 75 if (filterComp != 0 && numFilterComps > 0) { 76 ls->filterComp = am(myself, numFilterComps * 77 sizeof (ls->filterComp[0])); 78 if (ls->filterComp == 0) { 79 err++; 80 numFilterComps = 0; 81 } 82 for (i = 0; i < numFilterComps; i++) { 83 ls->filterComp[i] = sdup(myself, T, filterComp[i]); 84 if (ls->filterComp[i] == 0 && filterComp[i] != 0) 85 err++; 86 } 87 ls->numFilterComps = numFilterComps; 88 if (filter == 0) { 89 ls->filter = concatenateFilterComps(ls->numFilterComps, 90 ls->filterComp); 91 if (ls->filter == 0) 92 err++; 93 } 94 } else { 95 ls->filterComp = 0; 96 ls->numFilterComps = 0; 97 ls->filter = sdup(myself, T, filter); 98 if (ls->filter == 0 && filter != 0) 99 err++; 100 } 101 102 if (attrs != 0) { 103 for (na = 0, a = attrs; *a != 0; a++, na++); 104 ls->attrs = am(myself, (na + 1) * sizeof (ls->attrs[0])); 105 if (ls->attrs != 0) { 106 for (i = 0; i < na; i++) { 107 ls->attrs[i] = sdup(myself, T, attrs[i]); 108 if (ls->attrs[i] == 0 && attrs[i] != 0) 109 err++; 110 } 111 ls->attrs[na] = 0; 112 ls->numAttrs = na; 113 } else { 114 err++; 115 } 116 } else { 117 ls->attrs = 0; 118 ls->numAttrs = 0; 119 } 120 121 ls->attrsonly = attrsonly; 122 ls->isDN = isDN; 123 124 if (err > 0) { 125 freeLdapSearch(ls); 126 ls = 0; 127 } 128 129 return (ls); 130 } 131 132 void 133 freeLdapSearch(__nis_ldap_search_t *ls) { 134 int i; 135 136 if (ls == 0) 137 return; 138 139 sfree(ls->base); 140 if (ls->filterComp != 0) { 141 for (i = 0; i < ls->numFilterComps; i++) { 142 sfree(ls->filterComp[i]); 143 } 144 sfree(ls->filterComp); 145 } 146 sfree(ls->filter); 147 if (ls->attrs != 0) { 148 for (i = 0; i < ls->numAttrs; i++) { 149 sfree(ls->attrs[i]); 150 } 151 sfree(ls->attrs); 152 } 153 154 free(ls); 155 } 156 157 /* 158 * Given a table mapping, and a rule/value pointer, 159 * return an LDAP search structure with values suitable for use 160 * by ldap_search() or (if dn != 0) ldap_modify(). The rule/value 161 * may be modified. 162 * 163 * If dn != 0 and *dn == 0, the function attemps to return a pointer 164 * to the DN. This may necessitate an ldapSearch, if the rule set doesn't 165 * produce a DN directly. 166 * 167 * if dn == 0, and the rule set produces a DN as well as other attribute/ 168 * value pairs, the function returns an LDAP search structure with the 169 * DN only. 170 * 171 * If 'fromLDAP' is set, the caller wants base/scope/filter from 172 * t->objectDN->read; otherwise, from t->objectDN->write. 173 * 174 * If 'rv' is NULL, the caller wants an enumeration of the container. 175 * 176 * Note that this function only creates a search structure for 't' itself; 177 * if there are alternative mappings for the table, those must be handled 178 * by our caller. 179 */ 180 __nis_ldap_search_t * 181 createLdapRequest(__nis_table_mapping_t *t, 182 __nis_rule_value_t *rv, char **dn, int fromLDAP, 183 int *res, __nis_object_dn_t *obj_dn) { 184 int i, j; 185 __nis_ldap_search_t *ls = 0; 186 char **locDN; 187 int numLocDN, stat = 0, count = 0; 188 char *myself = "createLdapRequest"; 189 __nis_object_dn_t *objectDN = NULL; 190 191 if (t == 0) 192 return (0); 193 194 if (obj_dn == NULL) 195 objectDN = t->objectDN; 196 else 197 objectDN = obj_dn; 198 199 if (rv == 0) { 200 char *base; 201 char *filter; 202 203 if (fromLDAP) { 204 base = objectDN->read.base; 205 filter = makeFilter(objectDN->read.attrs); 206 } else { 207 base = objectDN->write.base; 208 filter = makeFilter(objectDN->write.attrs); 209 } 210 211 /* Create request to enumerate container */ 212 ls = buildLdapSearch(base, objectDN->read.scope, 0, 0, filter, 213 0, 0, 0); 214 sfree(filter); 215 return (ls); 216 } 217 218 for (i = 0; i < t->numRulesToLDAP; i++) { 219 rv = addLdapRuleValue(t, t->ruleToLDAP[i], 220 mit_ldap, mit_nisplus, rv, !fromLDAP, &stat); 221 if (rv == 0) 222 return (0); 223 if (stat == NP_LDAP_RULES_NO_VALUE) 224 count++; 225 stat = 0; 226 } 227 228 /* 229 * If none of the rules produced a value despite 230 * having enough NIS+ columns, return error. 231 */ 232 if (rv->numAttrs == 0 && count > 0) { 233 *res = NP_LDAP_RULES_NO_VALUE; 234 return (0); 235 } 236 237 /* 238 * 'rv' now contains everything we know about the attributes and 239 * values. Build an LDAP search structure from it. 240 */ 241 242 /* Look for a single-valued DN */ 243 locDN = findDNs(myself, rv, 1, 244 fromLDAP ? objectDN->read.base : 245 objectDN->write.base, 246 &numLocDN); 247 if (locDN != 0 && numLocDN == 1) { 248 if (dn != 0 && *dn == 0) { 249 *dn = locDN[0]; 250 sfree(locDN); 251 } else { 252 char *filter; 253 254 if (fromLDAP) 255 filter = makeFilter(objectDN->read.attrs); 256 else 257 filter = makeFilter(objectDN->write.attrs); 258 ls = buildLdapSearch(locDN[0], LDAP_SCOPE_BASE, 0, 0, 259 filter, 0, 0, 1); 260 sfree(filter); 261 freeDNs(locDN, numLocDN); 262 } 263 } else { 264 freeDNs(locDN, numLocDN); 265 } 266 267 if (ls != 0) { 268 ls->useCon = 1; 269 return (ls); 270 } 271 272 /* 273 * No DN, or caller wanted a search structure with the non-DN 274 * attributes. 275 */ 276 277 /* Initialize search structure */ 278 { 279 char *filter = (fromLDAP) ? 280 makeFilter(objectDN->read.attrs) : 281 makeFilter(objectDN->write.attrs); 282 char **ofc; 283 int nofc = 0; 284 285 ofc = makeFilterComp(filter, &nofc); 286 287 if (filter != 0 && ofc == 0) { 288 logmsg(MSG_NOTIMECHECK, LOG_ERR, 289 "%s: Unable to break filter into components: \"%s\"", 290 myself, NIL(filter)); 291 sfree(filter); 292 return (0); 293 } 294 295 if (fromLDAP) 296 ls = buildLdapSearch(objectDN->read.base, 297 objectDN->read.scope, 298 nofc, ofc, 0, 0, 0, 0); 299 else 300 ls = buildLdapSearch(objectDN->write.base, 301 objectDN->write.scope, 302 nofc, ofc, 0, 0, 0, 0); 303 sfree(filter); 304 freeFilterComp(ofc, nofc); 305 if (ls == 0) 306 return (0); 307 } 308 309 /* Build and add the filter components */ 310 for (i = 0; i < rv->numAttrs; i++) { 311 /* Skip DN */ 312 if (strcasecmp("dn", rv->attrName[i]) == 0) 313 continue; 314 315 /* Skip vt_ber values */ 316 if (rv->attrVal[i].type == vt_ber) 317 continue; 318 319 for (j = 0; j < rv->attrVal[i].numVals; j++) { 320 __nis_buffer_t b = {0, 0}; 321 char **tmpComp; 322 323 bp2buf(myself, &b, "%s=%s", 324 rv->attrName[i], rv->attrVal[i].val[j].value); 325 tmpComp = addFilterComp(b.buf, ls->filterComp, 326 &ls->numFilterComps); 327 if (tmpComp == 0) { 328 logmsg(MSG_NOTIMECHECK, LOG_ERR, 329 "%s: Unable to add filter component \"%s\"", 330 myself, NIL(b.buf)); 331 sfree(b.buf); 332 freeLdapSearch(ls); 333 return (0); 334 } 335 ls->filterComp = tmpComp; 336 sfree(b.buf); 337 } 338 } 339 340 if (ls->numFilterComps > 0) { 341 sfree(ls->filter); 342 ls->filter = concatenateFilterComps(ls->numFilterComps, 343 ls->filterComp); 344 if (ls->filter == 0) { 345 logmsg(MSG_NOTIMECHECK, LOG_ERR, 346 "%s: Unable to concatenate filter components", 347 myself); 348 freeLdapSearch(ls); 349 return (0); 350 } 351 } 352 353 if (dn != 0 && *dn == 0) { 354 /* 355 * The caller wants a DN, but we didn't get one from the 356 * the rule set. We have an 'ls', so use it to ldapSearch() 357 * for an entry from which we can extract the DN. 358 */ 359 __nis_rule_value_t *rvtmp; 360 char **locDN; 361 int nv = 0, numLocDN; 362 363 rvtmp = ldapSearch(ls, &nv, 0, 0); 364 locDN = findDNs(myself, rvtmp, nv, 0, &numLocDN); 365 if (locDN != 0 && numLocDN == 1) { 366 *dn = locDN[0]; 367 sfree(locDN); 368 } else { 369 freeDNs(locDN, numLocDN); 370 } 371 freeRuleValue(rvtmp, nv); 372 } 373 374 ls->useCon = 1; 375 return (ls); 376 } 377 378 int ldapConnAttemptRetryTimeout = 60; /* seconds */ 379 380 typedef struct { 381 LDAP *ld; 382 mutex_t mutex; /* Mutex for update of structure */ 383 pthread_t owner; /* Thread holding mutex */ 384 mutex_t rcMutex; /* Mutex for refCount */ 385 int refCount; /* Reference count */ 386 int isBound; /* Is connection open and usable ? */ 387 time_t retryTime; /* When should open be retried */ 388 int status; /* Status of last operation */ 389 int doDis; /* To be disconnected if refCount==0 */ 390 int doDel; /* To be deleted if refCount zero */ 391 int onList; /* True if on the 'ldapCon' list */ 392 char *sp; /* server string */ 393 char *who; 394 char *cred; 395 auth_method_t method; 396 int port; 397 struct timeval bindTimeout; 398 struct timeval searchTimeout; 399 struct timeval modifyTimeout; 400 struct timeval addTimeout; 401 struct timeval deleteTimeout; 402 int simplePage; /* Can do simple-page */ 403 int vlv; /* Can do VLV */ 404 uint_t batchFrom; /* # entries read in one operation */ 405 void *next; 406 } __nis_ldap_conn_t; 407 408 /* 409 * List of connections, 'ldapCon', protected by an RW lock. 410 * 411 * The following locking scheme is used: 412 * 413 * (1) Find a connection structure to use to talk to LDAP 414 * Rlock list 415 * Locate structure 416 * Acquire 'mutex' 417 * Acquire 'rcMutex' 418 * update refCount 419 * Release 'rcMutex' 420 * release 'mutex' 421 * Unlock list 422 * Use structure 423 * Release structure when done 424 * (2) Insert/delete structure(s) on/from list 425 * Wlock list 426 * Insert/delete structure; if deleting, must 427 * acquire 'mutex', and 'rcMutex' (in that order), 428 * and 'refCount' must be zero. 429 * Unlock list 430 * (3) Modify structure 431 * Find structure 432 * Acquire 'mutex' 433 * Modify (except refCount) 434 * Release 'mutex' 435 * Release structure 436 */ 437 438 __nis_ldap_conn_t *ldapCon = 0; 439 __nis_ldap_conn_t *ldapReferralCon = 0; 440 static rwlock_t ldapConLock = DEFAULTRWLOCK; 441 static rwlock_t referralConLock = DEFAULTRWLOCK; 442 443 void 444 exclusiveLC(__nis_ldap_conn_t *lc) { 445 pthread_t me = pthread_self(); 446 int stat; 447 448 if (lc == 0) 449 return; 450 451 stat = mutex_trylock(&lc->mutex); 452 if (stat == EBUSY && lc->owner != me) 453 mutex_lock(&lc->mutex); 454 455 lc->owner = me; 456 } 457 458 /* Return 1 if mutex held by this thread, 0 otherwise */ 459 int 460 assertExclusive(__nis_ldap_conn_t *lc) { 461 pthread_t me; 462 int stat; 463 464 if (lc == 0) 465 return (0); 466 467 stat = mutex_trylock(&lc->mutex); 468 469 if (stat == 0) { 470 mutex_unlock(&lc->mutex); 471 return (0); 472 } 473 474 me = pthread_self(); 475 if (stat != EBUSY || lc->owner != me) 476 return (0); 477 478 return (1); 479 } 480 481 void 482 releaseLC(__nis_ldap_conn_t *lc) { 483 pthread_t me = pthread_self(); 484 485 if (lc == 0 || lc->owner != me) 486 return; 487 488 lc->owner = 0; 489 (void) mutex_unlock(&lc->mutex); 490 } 491 492 void 493 incrementRC(__nis_ldap_conn_t *lc) { 494 if (lc == 0) 495 return; 496 497 (void) mutex_lock(&lc->rcMutex); 498 lc->refCount++; 499 (void) mutex_unlock(&lc->rcMutex); 500 } 501 502 void 503 decrementRC(__nis_ldap_conn_t *lc) { 504 if (lc == 0) 505 return; 506 507 (void) mutex_lock(&lc->rcMutex); 508 if (lc->refCount > 0) 509 lc->refCount--; 510 (void) mutex_unlock(&lc->rcMutex); 511 } 512 513 /* Accept a server/port indication, and call ldap_init() */ 514 static LDAP * 515 ldapInit(char *srv, int port, bool_t use_ssl) { 516 LDAP *ld; 517 int ldapVersion = LDAP_VERSION3; 518 int derefOption = LDAP_DEREF_ALWAYS; 519 int timelimit = proxyInfo.search_time_limit; 520 int sizelimit = proxyInfo.search_size_limit; 521 char *myself = "ldapInit"; 522 523 if (srv == 0) 524 return (0); 525 526 if (use_ssl) { 527 ld = ldapssl_init(srv, port, 1); 528 } else { 529 ld = ldap_init(srv, port); 530 } 531 532 if (ld != 0) { 533 (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, 534 &ldapVersion); 535 (void) ldap_set_option(ld, LDAP_OPT_DEREF, &derefOption); 536 (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 537 (void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &timelimit); 538 (void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &sizelimit); 539 (void) ldap_set_option(ld, LDAP_OPT_REBIND_ARG, 0); 540 } 541 542 return (ld); 543 } 544 545 /* 546 * Bind the specified LDAP structure per the supplied authentication. 547 * Note: tested with none, simple, and digest_md5. May or may not 548 * work with other authentication methods, mostly depending on whether 549 * or not 'who' and 'cred' contain sufficient information. 550 */ 551 static int 552 ldapBind(LDAP **ldP, char *who, char *cred, auth_method_t method, 553 struct timeval timeout) { 554 int ret; 555 LDAP *ld; 556 char *myself = "ldapBind"; 557 558 if (ldP == 0 || (ld = *ldP) == 0) 559 return (LDAP_PARAM_ERROR); 560 561 if (method == none) { 562 /* No ldap_bind() required (or even possible) */ 563 ret = LDAP_SUCCESS; 564 } else if (method == simple) { 565 struct timeval tv; 566 LDAPMessage *msg = 0; 567 568 tv = timeout; 569 ret = ldap_bind(ld, who, cred, LDAP_AUTH_SIMPLE); 570 if (ret != -1) { 571 ret = ldap_result(ld, ret, 0, &tv, &msg); 572 if (ret == 0) { 573 ret = LDAP_TIMEOUT; 574 } else if (ret == -1) { 575 (void) ldap_get_option(ld, 576 LDAP_OPT_ERROR_NUMBER, 577 &ret); 578 } else { 579 ret = ldap_result2error(ld, msg, 0); 580 } 581 if (msg != 0) 582 (void) ldap_msgfree(msg); 583 } else { 584 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, 585 &ret); 586 } 587 } else if (method == cram_md5) { 588 /* Note: there is only a synchronous call for cram-md5 */ 589 struct berval ber_cred; 590 591 ber_cred.bv_len = strlen(cred); 592 ber_cred.bv_val = cred; 593 ret = ldap_sasl_cram_md5_bind_s(ld, who, &ber_cred, NULL, NULL); 594 } else if (method == digest_md5) { 595 /* Note: there is only a synchronous call for digest-md5 */ 596 struct berval ber_cred; 597 598 ber_cred.bv_len = strlen(cred); 599 ber_cred.bv_val = cred; 600 ret = ldap_x_sasl_digest_md5_bind_s(ld, who, &ber_cred, NULL, 601 NULL); 602 } else { 603 ret = LDAP_AUTH_METHOD_NOT_SUPPORTED; 604 } 605 606 if (ret != LDAP_SUCCESS) { 607 (void) ldap_unbind_s(ld); 608 *ldP = 0; 609 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 610 "%s: Unable to bind as: %s: %s", 611 myself, who, ldap_err2string(ret)); 612 } 613 614 return (ret); 615 } 616 617 /* 618 * Free 'lc' and all related memory. Caller must hold the exclusive lock. 619 * Return LDAP_UNAVAILABLE upon success, in which case the caller mustn't 620 * try to use the structure pointer in any way. 621 */ 622 static int 623 freeCon(__nis_ldap_conn_t *lc) { 624 char *myself = "freeCon"; 625 626 if (!assertExclusive(lc)) 627 return (LDAP_PARAM_ERROR); 628 629 incrementRC(lc); 630 631 /* Must be unused, unbound, and not on the 'ldapCon' list */ 632 if (lc->onList || lc->refCount != 1 || lc->isBound) { 633 lc->doDel++; 634 decrementRC(lc); 635 return (LDAP_BUSY); 636 } 637 638 sfree(lc->sp); 639 sfree(lc->who); 640 sfree(lc->cred); 641 642 /* Delete structure with both mutex:es held */ 643 644 free(lc); 645 646 return (LDAP_UNAVAILABLE); 647 } 648 649 /* 650 * Disconnect the specified LDAP connection. Caller must have acquired 'mutex'. 651 * 652 * On return, if the status is LDAP_UNAVAILABLE, the caller must not touch 653 * the structure in any way. 654 */ 655 static int 656 disconnectCon(__nis_ldap_conn_t *lc) { 657 int stat; 658 char *myself = "disconnectCon"; 659 660 if (lc == 0) 661 return (LDAP_SUCCESS); 662 663 if (!assertExclusive(lc)) 664 return (LDAP_UNAVAILABLE); 665 666 if (lc->doDis) { 667 668 /* Increment refCount to protect against interference */ 669 incrementRC(lc); 670 /* refCount must be one (i.e., just us) */ 671 if (lc->refCount != 1) { 672 /* 673 * In use; already marked for disconnect, 674 * so do nothing. 675 */ 676 decrementRC(lc); 677 return (LDAP_BUSY); 678 } 679 680 stat = ldap_unbind_s(lc->ld); 681 if (stat == LDAP_SUCCESS) { 682 lc->ld = 0; 683 lc->isBound = 0; 684 lc->doDis = 0; 685 /* Reset simple page and vlv indication */ 686 lc->simplePage = 0; 687 lc->vlv = 0; 688 } else if (verbose) { 689 logmsg(MSG_NOTIMECHECK, LOG_ERR, 690 "%s: ldap_unbind_s() => %d (%s)", 691 myself, stat, ldap_err2string(stat)); 692 } 693 694 decrementRC(lc); 695 } 696 697 if (lc->doDel) { 698 if (LDAP_UNAVAILABLE == freeCon(lc)) 699 stat = LDAP_UNAVAILABLE; 700 } 701 702 return (stat); 703 } 704 705 /* 706 * controlSupported will determine for a given connection whether a set 707 * of controls is supported or not. The input parameters: 708 * lc The connection 709 * ctrl A an array of OID strings, the terminal string should be NULL 710 * The returned values if LDAP_SUCCESS is returned: 711 * supported A caller supplied array which will be set to TRUE or 712 * FALSE depending on whether the corresponding control 713 * is reported as supported. 714 * Returns LDAP_SUCCESS if the supportedControl attribute is read. 715 */ 716 717 static int 718 controlSupported(__nis_ldap_conn_t *lc, char **ctrl, bool_t *supported) { 719 LDAPMessage *res, *e; 720 char *attr[2], *a, **val; 721 int stat, i; 722 BerElement *ber = 0; 723 char *myself = "controlSupported"; 724 725 attr[0] = "supportedControl"; 726 attr[1] = 0; 727 728 stat = ldap_search_st(lc->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)", 729 attr, 0, &lc->searchTimeout, &res); 730 if (stat != LDAP_SUCCESS) { 731 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 732 "%s: Unable to retrieve supported control information for %s: %s", 733 myself, NIL(lc->sp), ldap_err2string(stat)); 734 return (stat); 735 } 736 737 e = ldap_first_entry(lc->ld, res); 738 if (e != 0) { 739 a = ldap_first_attribute(lc->ld, e, &ber); 740 if (a != 0) { 741 val = ldap_get_values(lc->ld, e, a); 742 if (val == 0) { 743 ldap_memfree(a); 744 if (ber != 0) 745 ber_free(ber, 0); 746 } 747 } 748 } 749 if (e == 0 || a == 0 || val == 0) { 750 ldap_msgfree(res); 751 logmsg(MSG_NOTIMECHECK, LOG_INFO, 752 "%s: Unable to get root DSE for %s", 753 myself, NIL(lc->sp)); 754 return (LDAP_OPERATIONS_ERROR); 755 } 756 757 while (*ctrl != NULL) { 758 *supported = FALSE; 759 for (i = 0; val[i] != 0; i++) { 760 if (strstr(val[i], *ctrl) != 0) { 761 *supported = TRUE; 762 break; 763 } 764 } 765 logmsg(MSG_NOTIMECHECK, LOG_INFO, 766 "%s: %s: %s: %s", 767 myself, NIL(lc->sp), NIL(*ctrl), 768 *supported ? "enabled" : "disabled"); 769 ctrl++; 770 supported++; 771 } 772 773 ldap_value_free(val); 774 ldap_memfree(a); 775 if (ber != 0) 776 ber_free(ber, 0); 777 ldap_msgfree(res); 778 779 return (stat); 780 } 781 782 /* 783 * Connect the LDAP connection 'lc'. Caller must have acquired the 'mutex', 784 * and the refCount must be zero. 785 * 786 * On return, if the status is LDAP_UNAVAILABLE, the caller must not touch 787 * the structure in any way. 788 */ 789 static int 790 connectCon(__nis_ldap_conn_t *lc, int check_ctrl) { 791 struct timeval tp; 792 int stat; 793 bool_t supported[2] = {FALSE, FALSE}; 794 char *ctrl[3] = {LDAP_CONTROL_SIMPLE_PAGE, 795 LDAP_CONTROL_VLVREQUEST, 796 NULL}; 797 798 if (lc == 0) 799 return (LDAP_SUCCESS); 800 801 if (!assertExclusive(lc)) 802 return (LDAP_PARAM_ERROR); 803 804 incrementRC(lc); 805 if (lc->refCount != 1) { 806 /* 807 * Don't want to step on structure when it's used by someone 808 * else. 809 */ 810 decrementRC(lc); 811 return (LDAP_BUSY); 812 } 813 814 (void) gettimeofday(&tp, 0); 815 816 if (lc->ld != 0) { 817 /* Try to disconnect */ 818 lc->doDis++; 819 decrementRC(lc); 820 /* disconnctCon() will do the delete if required */ 821 stat = disconnectCon(lc); 822 if (stat != LDAP_SUCCESS) 823 return (stat); 824 incrementRC(lc); 825 if (lc->refCount != 1 || lc->ld != 0) { 826 decrementRC(lc); 827 return (lc->ld != 0) ? LDAP_SUCCESS : 828 LDAP_BUSY; 829 } 830 } else if (tp.tv_sec < lc->retryTime) { 831 /* Too early to retry connect */ 832 decrementRC(lc); 833 return (LDAP_SERVER_DOWN); 834 } 835 836 /* Set new retry time in case we fail below */ 837 lc->retryTime = tp.tv_sec + ldapConnAttemptRetryTimeout; 838 839 lc->ld = ldapInit(lc->sp, lc->port, proxyInfo.tls_method != no_tls); 840 if (lc->ld == 0) { 841 decrementRC(lc); 842 return (LDAP_LOCAL_ERROR); 843 } 844 845 stat = lc->status = ldapBind(&lc->ld, lc->who, lc->cred, lc->method, 846 lc->bindTimeout); 847 if (lc->status == LDAP_SUCCESS) { 848 lc->isBound = 1; 849 lc->retryTime = 0; 850 if (check_ctrl) { 851 (void) controlSupported(lc, ctrl, supported); 852 lc->simplePage = supported[0]; 853 lc->vlv = supported[1]; 854 lc->batchFrom = 50000; 855 } 856 } 857 858 decrementRC(lc); 859 860 return (stat); 861 } 862 863 /* 864 * Find and return a connection believed to be OK. 865 */ 866 static __nis_ldap_conn_t * 867 findCon(int *stat) { 868 __nis_ldap_conn_t *lc; 869 int ldapStat; 870 char *myself = "findCon"; 871 872 if (stat == 0) 873 stat = &ldapStat; 874 875 (void) rw_rdlock(&ldapConLock); 876 877 if (ldapCon == 0) { 878 /* Probably first call; try to set up the connection list */ 879 (void) rw_unlock(&ldapConLock); 880 if ((*stat = setupConList(proxyInfo.default_servers, 881 proxyInfo.proxy_dn, 882 proxyInfo.proxy_passwd, 883 proxyInfo.auth_method)) != 884 LDAP_SUCCESS) 885 return (0); 886 (void) rw_rdlock(&ldapConLock); 887 } 888 889 for (lc = ldapCon; lc != 0; lc = lc->next) { 890 exclusiveLC(lc); 891 if (!lc->isBound) { 892 *stat = connectCon(lc, 1); 893 if (*stat != LDAP_SUCCESS) { 894 if (*stat != LDAP_UNAVAILABLE) { 895 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 896 "%s: Cannot open connection to LDAP server (%s): %s", 897 myself, NIL(lc->sp), 898 ldap_err2string(*stat)); 899 releaseLC(lc); 900 } 901 continue; 902 } 903 } else if (lc->doDis || lc->doDel) { 904 *stat = disconnectCon(lc); 905 if (*stat != LDAP_UNAVAILABLE) 906 releaseLC(lc); 907 continue; 908 } 909 incrementRC(lc); 910 releaseLC(lc); 911 break; 912 } 913 914 (void) rw_unlock(&ldapConLock); 915 916 return (lc); 917 } 918 919 /* Release connection; decrements ref count for the connection */ 920 static void 921 releaseCon(__nis_ldap_conn_t *lc, int status) { 922 int stat; 923 924 if (lc == 0) 925 return; 926 927 exclusiveLC(lc); 928 929 lc->status = status; 930 931 decrementRC(lc); 932 933 if (lc->doDis) 934 stat = disconnectCon(lc); 935 else 936 stat = LDAP_SUCCESS; 937 938 if (stat != LDAP_UNAVAILABLE) 939 releaseLC(lc); 940 } 941 942 static __nis_ldap_conn_t * 943 createCon(char *sp, char *who, char *cred, auth_method_t method, int port) { 944 __nis_ldap_conn_t *lc; 945 char *myself = "createCon"; 946 char *r; 947 948 if (sp == 0) 949 return (0); 950 951 lc = am(myself, sizeof (*lc)); 952 if (lc == 0) 953 return (0); 954 955 (void) mutex_init(&lc->mutex, 0, 0); 956 (void) mutex_init(&lc->rcMutex, 0, 0); 957 958 /* If we need to delete 'lc', freeCon() wants the mutex held */ 959 exclusiveLC(lc); 960 961 lc->sp = sdup(myself, T, sp); 962 if (lc->sp == 0) { 963 (void) freeCon(lc); 964 return (0); 965 } 966 967 if ((r = strchr(lc->sp, ']')) != 0) { 968 /* 969 * IPv6 address. Does libldap want this with the 970 * '[' and ']' left in place ? Assume so for now. 971 */ 972 r = strchr(r, ':'); 973 } else { 974 r = strchr(lc->sp, ':'); 975 } 976 977 if (r != NULL) { 978 *r++ = '\0'; 979 port = atoi(r); 980 } else if (port == 0) 981 port = proxyInfo.tls_method == ssl_tls ? LDAPS_PORT : LDAP_PORT; 982 983 if (who != 0) { 984 lc->who = sdup(myself, T, who); 985 if (lc->who == 0) { 986 (void) freeCon(lc); 987 return (0); 988 } 989 } 990 991 if (cred != 0) { 992 lc->cred = sdup(myself, T, cred); 993 if (lc->cred == 0) { 994 (void) freeCon(lc); 995 return (0); 996 } 997 } 998 999 lc->method = method; 1000 lc->port = port; 1001 1002 lc->bindTimeout = proxyInfo.bind_timeout; 1003 lc->searchTimeout = proxyInfo.search_timeout; 1004 lc->modifyTimeout = proxyInfo.modify_timeout; 1005 lc->addTimeout = proxyInfo.add_timeout; 1006 lc->deleteTimeout = proxyInfo.delete_timeout; 1007 1008 /* All other fields OK at zero */ 1009 1010 releaseLC(lc); 1011 1012 return (lc); 1013 } 1014 1015 static int 1016 setupConList(char *serverList, char *who, char *cred, auth_method_t method) { 1017 char *sls, *sl, *s, *e; 1018 __nis_ldap_conn_t *lc, *tmp; 1019 char *myself = "setupConList"; 1020 1021 if (serverList == 0) 1022 return (LDAP_PARAM_ERROR); 1023 1024 (void) rw_wrlock(&ldapConLock); 1025 1026 if (ldapCon != 0) { 1027 /* Assume we've already been called and done the set-up */ 1028 (void) rw_unlock(&ldapConLock); 1029 return (LDAP_SUCCESS); 1030 } 1031 1032 /* Work on a copy of 'serverList' */ 1033 sl = sls = sdup(myself, T, serverList); 1034 if (sl == 0) { 1035 (void) rw_unlock(&ldapConLock); 1036 return (LDAP_NO_MEMORY); 1037 } 1038 1039 /* Remove leading white space */ 1040 for (0; *sl == ' ' || *sl == '\t'; sl++); 1041 1042 /* Create connection for each server on the list */ 1043 for (s = sl; *s != '\0'; s = e+1) { 1044 int l; 1045 1046 /* Find end of server/port token */ 1047 for (e = s; *e != ' ' && *e != '\t' && *e != '\0'; e++); 1048 if (*e != '\0') 1049 *e = '\0'; 1050 else 1051 e--; 1052 l = slen(s); 1053 1054 if (l > 0) { 1055 lc = createCon(s, who, cred, method, 0); 1056 if (lc == 0) { 1057 free(sls); 1058 (void) rw_unlock(&ldapConLock); 1059 return (LDAP_NO_MEMORY); 1060 } 1061 lc->onList = 1; 1062 if (ldapCon == 0) { 1063 ldapCon = lc; 1064 } else { 1065 /* Insert at end of list */ 1066 for (tmp = ldapCon; tmp->next != 0; 1067 tmp = tmp->next); 1068 tmp->next = lc; 1069 } 1070 } 1071 } 1072 1073 free(sls); 1074 1075 (void) rw_unlock(&ldapConLock); 1076 1077 return (LDAP_SUCCESS); 1078 } 1079 1080 static bool_t 1081 is_same_connection(__nis_ldap_conn_t *lc, LDAPURLDesc *ludpp) 1082 { 1083 return (strcasecmp(ludpp->lud_host, lc->sp) == 0 && 1084 ludpp->lud_port == lc->port); 1085 } 1086 1087 static __nis_ldap_conn_t * 1088 find_connection_from_list(__nis_ldap_conn_t *list, 1089 LDAPURLDesc *ludpp, int *stat) 1090 { 1091 int ldapStat; 1092 __nis_ldap_conn_t *lc = NULL; 1093 if (stat == 0) 1094 stat = &ldapStat; 1095 1096 *stat = LDAP_SUCCESS; 1097 1098 for (lc = list; lc != 0; lc = lc->next) { 1099 exclusiveLC(lc); 1100 if (is_same_connection(lc, ludpp)) { 1101 if (!lc->isBound) { 1102 *stat = connectCon(lc, 1); 1103 if (*stat != LDAP_SUCCESS) { 1104 releaseLC(lc); 1105 continue; 1106 } 1107 } else if (lc->doDis || lc->doDel) { 1108 (void) disconnectCon(lc); 1109 releaseLC(lc); 1110 continue; 1111 } 1112 incrementRC(lc); 1113 releaseLC(lc