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 2004 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 /* 49 * Build one of our internal LDAP search structures, containing copies of 50 * the supplied input. return NULL in case of error. 51 * 52 * If 'filter' is NULL, build an AND-filter using the filter components. 53 */ 54 __nis_ldap_search_t * 55 buildLdapSearch(char *base, int scope, int numFilterComps, char **filterComp, 56 char *filter, char **attrs, int attrsonly, int isDN) { 57 __nis_ldap_search_t *ls; 58 char **a; 59 int i, na, err = 0; 60 char *myself = "buildLdapSearch"; 61 62 ls = am(myself, sizeof (*ls)); 63 if (ls == 0) 64 return (0); 65 66 ls->base = sdup(myself, T, base); 67 if (ls->base == 0 && base != 0) 68 err++; 69 ls->scope = scope; 70 71 if (filterComp != 0 && numFilterComps > 0) { 72 ls->filterComp = am(myself, numFilterComps * 73 sizeof (ls->filterComp[0])); 74 if (ls->filterComp == 0) { 75 err++; 76 numFilterComps = 0; 77 } 78 for (i = 0; i < numFilterComps; i++) { 79 ls->filterComp[i] = sdup(myself, T, filterComp[i]); 80 if (ls->filterComp[i] == 0 && filterComp[i] != 0) 81 err++; 82 } 83 ls->numFilterComps = numFilterComps; 84 if (filter == 0) { 85 ls->filter = concatenateFilterComps(ls->numFilterComps, 86 ls->filterComp); 87 if (ls->filter == 0) 88 err++; 89 } 90 } else { 91 ls->filterComp = 0; 92 ls->numFilterComps = 0; 93 ls->filter = sdup(myself, T, filter); 94 if (ls->filter == 0 && filter != 0) 95 err++; 96 } 97 98 if (attrs != 0) { 99 for (na = 0, a = attrs; *a != 0; a++, na++); 100 ls->attrs = am(myself, (na + 1) * sizeof (ls->attrs[0])); 101 if (ls->attrs != 0) { 102 for (i = 0; i < na; i++) { 103 ls->attrs[i] = sdup(myself, T, attrs[i]); 104 if (ls->attrs[i] == 0 && attrs[i] != 0) 105 err++; 106 } 107 ls->attrs[na] = 0; 108 ls->numAttrs = na; 109 } else { 110 err++; 111 } 112 } else { 113 ls->attrs = 0; 114 ls->numAttrs = 0; 115 } 116 117 ls->attrsonly = attrsonly; 118 ls->isDN = isDN; 119 120 if (err > 0) { 121 freeLdapSearch(ls); 122 ls = 0; 123 } 124 125 return (ls); 126 } 127 128 void 129 freeLdapSearch(__nis_ldap_search_t *ls) { 130 int i; 131 132 if (ls == 0) 133 return; 134 135 sfree(ls->base); 136 if (ls->filterComp != 0) { 137 for (i = 0; i < ls->numFilterComps; i++) { 138 sfree(ls->filterComp[i]); 139 } 140 sfree(ls->filterComp); 141 } 142 sfree(ls->filter); 143 if (ls->attrs != 0) { 144 for (i = 0; i < ls->numAttrs; i++) { 145 sfree(ls->attrs[i]); 146 } 147 sfree(ls->attrs); 148 } 149 150 free(ls); 151 } 152 153 /* 154 * Given a table mapping, and a rule/value pointer, 155 * return an LDAP search structure with values suitable for use 156 * by ldap_search() or (if dn != 0) ldap_modify(). The rule/value 157 * may be modified. 158 * 159 * If dn != 0 and *dn == 0, the function attemps to return a pointer 160 * to the DN. This may necessitate an ldapSearch, if the rule set doesn't 161 * produce a DN directly. 162 * 163 * if dn == 0, and the rule set produces a DN as well as other attribute/ 164 * value pairs, the function returns an LDAP search structure with the 165 * DN only. 166 * 167 * If 'fromLDAP' is set, the caller wants base/scope/filter from 168 * t->objectDN->read; otherwise, from t->objectDN->write. 169 * 170 * If 'rv' is NULL, the caller wants an enumeration of the container. 171 * 172 * Note that this function only creates a search structure for 't' itself; 173 * if there are alternative mappings for the table, those must be handled 174 * by our caller. 175 */ 176 __nis_ldap_search_t * 177 createLdapRequest(__nis_table_mapping_t *t, 178 __nis_rule_value_t *rv, char **dn, int fromLDAP, 179 int *res, __nis_object_dn_t *obj_dn) { 180 int i, j; 181 __nis_ldap_search_t *ls = 0; 182 char **locDN; 183 int numLocDN, stat = 0, count = 0; 184 char *myself = "createLdapRequest"; 185 __nis_object_dn_t *objectDN = NULL; 186 187 if (t == 0) 188 return (0); 189 190 if (obj_dn == NULL) 191 objectDN = t->objectDN; 192 else 193 objectDN = obj_dn; 194 195 if (rv == 0) { 196 char *base; 197 char *filter; 198 199 if (fromLDAP) { 200 base = objectDN->read.base; 201 filter = makeFilter(objectDN->read.attrs); 202 } else { 203 base = objectDN->write.base; 204 filter = makeFilter(objectDN->write.attrs); 205 } 206 207 /* Create request to enumerate container */ 208 ls = buildLdapSearch(base, objectDN->read.scope, 0, 0, filter, 209 0, 0, 0); 210 sfree(filter); 211 return (ls); 212 } 213 214 for (i = 0; i < t->numRulesToLDAP; i++) { 215 rv = addLdapRuleValue(t, t->ruleToLDAP[i], 216 mit_ldap, mit_nisplus, rv, !fromLDAP, &stat); 217 if (rv == 0) 218 return (0); 219 if (stat == NP_LDAP_RULES_NO_VALUE) 220 count++; 221 stat = 0; 222 } 223 224 /* 225 * If none of the rules produced a value despite 226 * having enough NIS+ columns, return error. 227 */ 228 if (rv->numAttrs == 0 && count > 0) { 229 *res = NP_LDAP_RULES_NO_VALUE; 230 return (0); 231 } 232 233 /* 234 * 'rv' now contains everything we know about the attributes and 235 * values. Build an LDAP search structure from it. 236 */ 237 238 /* Look for a single-valued DN */ 239 locDN = findDNs(myself, rv, 1, 240 fromLDAP ? objectDN->read.base : 241 objectDN->write.base, 242 &numLocDN); 243 if (locDN != 0 && numLocDN == 1) { 244 if (dn != 0 && *dn == 0) { 245 *dn = locDN[0]; 246 sfree(locDN); 247 } else { 248 char *filter; 249 250 if (fromLDAP) 251 filter = makeFilter(objectDN->read.attrs); 252 else 253 filter = makeFilter(objectDN->write.attrs); 254 ls = buildLdapSearch(locDN[0], LDAP_SCOPE_BASE, 0, 0, 255 filter, 0, 0, 1); 256 sfree(filter); 257 freeDNs(locDN, numLocDN); 258 } 259 } else { 260 freeDNs(locDN, numLocDN); 261 } 262 263 if (ls != 0) { 264 ls->useCon = 1; 265 return (ls); 266 } 267 268 /* 269 * No DN, or caller wanted a search structure with the non-DN 270 * attributes. 271 */ 272 273 /* Initialize search structure */ 274 { 275 char *filter = (fromLDAP) ? 276 makeFilter(objectDN->read.attrs) : 277 makeFilter(objectDN->write.attrs); 278 char **ofc; 279 int nofc = 0; 280 281 ofc = makeFilterComp(filter, &nofc); 282 283 if (filter != 0 && ofc == 0) { 284 logmsg(MSG_NOTIMECHECK, LOG_ERR, 285 "%s: Unable to break filter into components: \"%s\"", 286 myself, NIL(filter)); 287 sfree(filter); 288 return (0); 289 } 290 291 if (fromLDAP) 292 ls = buildLdapSearch(objectDN->read.base, 293 objectDN->read.scope, 294 nofc, ofc, 0, 0, 0, 0); 295 else 296 ls = buildLdapSearch(objectDN->write.base, 297 objectDN->write.scope, 298 nofc, ofc, 0, 0, 0, 0); 299 sfree(filter); 300 freeFilterComp(ofc, nofc); 301 if (ls == 0) 302 return (0); 303 } 304 305 /* Build and add the filter components */ 306 for (i = 0; i < rv->numAttrs; i++) { 307 /* Skip DN */ 308 if (strcasecmp("dn", rv->attrName[i]) == 0) 309 continue; 310 311 /* Skip vt_ber values */ 312 if (rv->attrVal[i].type == vt_ber) 313 continue; 314 315 for (j = 0; j < rv->attrVal[i].numVals; j++) { 316 __nis_buffer_t b = {0, 0}; 317 char **tmpComp; 318 319 bp2buf(myself, &b, "%s=%s", 320 rv->attrName[i], rv->attrVal[i].val[j].value); 321 tmpComp = addFilterComp(b.buf, ls->filterComp, 322 &ls->numFilterComps); 323 if (tmpComp == 0) { 324 logmsg(MSG_NOTIMECHECK, LOG_ERR, 325 "%s: Unable to add filter component \"%s\"", 326 myself, NIL(b.buf)); 327 sfree(b.buf); 328 freeLdapSearch(ls); 329 return (0); 330 } 331 ls->filterComp = tmpComp; 332 sfree(b.buf); 333 } 334 } 335 336 if (ls->numFilterComps > 0) { 337 sfree(ls->filter); 338 ls->filter = concatenateFilterComps(ls->numFilterComps, 339 ls->filterComp); 340 if (ls->filter == 0) { 341 logmsg(MSG_NOTIMECHECK, LOG_ERR, 342 "%s: Unable to concatenate filter components", 343 myself); 344 freeLdapSearch(ls); 345 return (0); 346 } 347 } 348 349 if (dn != 0 && *dn == 0) { 350 /* 351 * The caller wants a DN, but we didn't get one from the 352 * the rule set. We have an 'ls', so use it to ldapSearch() 353 * for an entry from which we can extract the DN. 354 */ 355 __nis_rule_value_t *rvtmp; 356 char **locDN; 357 int nv = 0, numLocDN; 358 359 rvtmp = ldapSearch(ls, &nv, 0, 0); 360 locDN = findDNs(myself, rvtmp, nv, 0, &numLocDN); 361 if (locDN != 0 && numLocDN == 1) { 362 *dn = locDN[0]; 363 sfree(locDN); 364 } else { 365 freeDNs(locDN, numLocDN); 366 } 367 freeRuleValue(rvtmp, nv); 368 } 369 370 ls->useCon = 1; 371 return (ls); 372 } 373 374 int ldapConnAttemptRetryTimeout = 60; /* seconds */ 375 376 typedef struct { 377 LDAP *ld; 378 mutex_t mutex; /* Mutex for update of structure */ 379 pthread_t owner; /* Thread holding mutex */ 380 mutex_t rcMutex; /* Mutex for refCount */ 381 int refCount; /* Reference count */ 382 int isBound; /* Is connection open and usable ? */ 383 time_t retryTime; /* When should open be retried */ 384 int status; /* Status of last operation */ 385 int doDis; /* To be disconnected if refCount==0 */ 386 int doDel; /* To be deleted if refCount zero */ 387 int onList; /* True if on the 'ldapCon' list */ 388 char *sp; /* server string */ 389 char *who; 390 char *cred; 391 auth_method_t method; 392 int port; 393 struct timeval bindTimeout; 394 struct timeval searchTimeout; 395 struct timeval modifyTimeout; 396 struct timeval addTimeout; 397 struct timeval deleteTimeout; 398 int simplePage; /* Can do simple-page */ 399 int vlv; /* Can do VLV */ 400 uint_t batchFrom; /* # entries read in one operation */ 401 void *next; 402 } __nis_ldap_conn_t; 403 404 /* 405 * List of connections, 'ldapCon', protected by an RW lock. 406 * 407 * The following locking scheme is used: 408 * 409 * (1) Find a connection structure to use to talk to LDAP 410 * Rlock list 411 * Locate structure 412 * Acquire 'mutex' 413 * Acquire 'rcMutex' 414 * update refCount 415 * Release 'rcMutex' 416 * release 'mutex' 417 * Unlock list 418 * Use structure 419 * Release structure when done 420 * (2) Insert/delete structure(s) on/from list 421 * Wlock list 422 * Insert/delete structure; if deleting, must 423 * acquire 'mutex', and 'rcMutex' (in that order), 424 * and 'refCount' must be zero. 425 * Unlock list 426 * (3) Modify structure 427 * Find structure 428 * Acquire 'mutex' 429 * Modify (except refCount) 430 * Release 'mutex' 431 * Release structure 432 */ 433 434 __nis_ldap_conn_t *ldapCon = 0; 435 __nis_ldap_conn_t *ldapReferralCon = 0; 436 static rwlock_t ldapConLock = DEFAULTRWLOCK; 437 static rwlock_t referralConLock = DEFAULTRWLOCK; 438 439 void 440 exclusiveLC(__nis_ldap_conn_t *lc) { 441 pthread_t me = pthread_self(); 442 int stat; 443 444 if (lc == 0) 445 return; 446 447 stat = mutex_trylock(&lc->mutex); 448 if (stat == EBUSY && lc->owner != me) 449 mutex_lock(&lc->mutex); 450 451 lc->owner = me; 452 } 453 454 /* Return 1 if mutex held by this thread, 0 otherwise */ 455 int 456 assertExclusive(__nis_ldap_conn_t *lc) { 457 pthread_t me; 458 int stat; 459 460 if (lc == 0) 461 return (0); 462 463 stat = mutex_trylock(&lc->mutex); 464 465 if (stat == 0) { 466 mutex_unlock(&lc->mutex); 467 return (0); 468 } 469 470 me = pthread_self(); 471 if (stat != EBUSY || lc->owner != me) 472 return (0); 473 474 return (1); 475 } 476 477 void 478 releaseLC(__nis_ldap_conn_t *lc) { 479 pthread_t me = pthread_self(); 480 481 if (lc == 0 || lc->owner != me) 482 return; 483 484 lc->owner = 0; 485 (void) mutex_unlock(&lc->mutex); 486 } 487 488 void 489 incrementRC(__nis_ldap_conn_t *lc) { 490 if (lc == 0) 491 return; 492 493 (void) mutex_lock(&lc->rcMutex); 494 lc->refCount++; 495 (void) mutex_unlock(&lc->rcMutex); 496 } 497 498 void 499 decrementRC(__nis_ldap_conn_t *lc) { 500 if (lc == 0) 501 return; 502 503 (void) mutex_lock(&lc->rcMutex); 504 if (lc->refCount > 0) 505 lc->refCount--; 506 (void) mutex_unlock(&lc->rcMutex); 507 } 508 509 /* Accept a server/port indication, and call ldap_init() */ 510 static LDAP * 511 ldapInit(char *srv, int port, bool_t use_ssl) { 512 LDAP *ld; 513 int ldapVersion = LDAP_VERSION3; 514 int derefOption = LDAP_DEREF_ALWAYS; 515 int timelimit = proxyInfo.search_time_limit; 516 int sizelimit = proxyInfo.search_size_limit; 517 char *myself = "ldapInit"; 518 519 if (srv == 0) 520 return (0); 521 522 if (use_ssl) { 523 ld = ldapssl_init(srv, port, 1); 524 } else { 525 ld = ldap_init(srv, port); 526 } 527 528 if (ld != 0) { 529 (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, 530 &ldapVersion); 531 (void) ldap_set_option(ld, LDAP_OPT_DEREF, &derefOption); 532 (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 533 (void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &timelimit); 534 (void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &sizelimit); 535 (void) ldap_set_option(ld, LDAP_OPT_REBIND_ARG, 0); 536 } 537 538 return (ld); 539 } 540 541 /* 542 * Bind the specified LDAP structure per the supplied authentication. 543 * Note: tested with none, simple, and digest_md5. May or may not 544 * work with other authentication methods, mostly depending on whether 545 * or not 'who' and 'cred' contain sufficient information. 546 */ 547 static int 548 ldapBind(LDAP **ldP, char *who, char *cred, auth_method_t method, 549 struct timeval timeout) { 550 int ret; 551 LDAP *ld; 552 char *myself = "ldapBind"; 553 554 if (ldP == 0 || (ld = *ldP) == 0) 555 return (LDAP_PARAM_ERROR); 556 557 if (method == none) { 558 /* No ldap_bind() required (or even possible) */ 559 ret = LDAP_SUCCESS; 560 } else if (method == simple) { 561 struct timeval tv; 562 LDAPMessage *msg = 0; 563 564 tv = timeout; 565 ret = ldap_bind(ld, who, cred, LDAP_AUTH_SIMPLE); 566 if (ret != -1) { 567 ret = ldap_result(ld, ret, 0, &tv, &msg); 568 if (ret == 0) { 569 ret = LDAP_TIMEOUT; 570 } else if (ret == -1) { 571 (void) ldap_get_option(ld, 572 LDAP_OPT_ERROR_NUMBER, 573 &ret); 574 } else { 575 ret = ldap_result2error(ld, msg, 0); 576 } 577 if (msg != 0) 578 (void) ldap_msgfree(msg); 579 } else { 580 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, 581 &ret); 582 } 583 } else if (method == cram_md5) { 584 /* Note: there is only a synchronous call for cram-md5 */ 585 struct berval ber_cred; 586 587 ber_cred.bv_len = strlen(cred); 588 ber_cred.bv_val = cred; 589 ret = ldap_sasl_cram_md5_bind_s(ld, who, &ber_cred, NULL, NULL); 590 } else if (method == digest_md5) { 591 /* Note: there is only a synchronous call for digest-md5 */ 592 struct berval ber_cred; 593 594 ber_cred.bv_len = strlen(cred); 595 ber_cred.bv_val = cred; 596 ret = ldap_x_sasl_digest_md5_bind_s(ld, who, &ber_cred, NULL, 597 NULL); 598 } else { 599 ret = LDAP_AUTH_METHOD_NOT_SUPPORTED; 600 } 601 602 if (ret != LDAP_SUCCESS) { 603 (void) ldap_unbind_s(ld); 604 *ldP = 0; 605 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 606 "%s: Unable to bind as: %s: %s", 607 myself, who, ldap_err2string(ret)); 608 } 609 610 return (ret); 611 } 612 613 /* 614 * Free 'lc' and all related memory. Caller must hold the exclusive lock. 615 * Return LDAP_UNAVAILABLE upon success, in which case the caller mustn't 616 * try to use the structure pointer in any way. 617 */ 618 static int 619 freeCon(__nis_ldap_conn_t *lc) { 620 char *myself = "freeCon"; 621 622 if (!assertExclusive(lc)) 623 return (LDAP_PARAM_ERROR); 624 625 incrementRC(lc); 626 627 /* Must be unused, unbound, and not on the 'ldapCon' list */ 628 if (lc->onList || lc->refCount != 1 || lc->isBound) { 629 lc->doDel++; 630 decrementRC(lc); 631 return (LDAP_BUSY); 632 } 633 634 sfree(lc->sp); 635 sfree(lc->who); 636 sfree(lc->cred); 637 638 /* Delete structure with both mutex:es held */ 639 640 free(lc); 641 642 return (LDAP_UNAVAILABLE); 643 } 644 645 /* 646 * Disconnect the specified LDAP connection. Caller must have acquired 'mutex'. 647 * 648 * On return, if the status is LDAP_UNAVAILABLE, the caller must not touch 649 * the structure in any way. 650 */ 651 static int 652 disconnectCon(__nis_ldap_conn_t *lc) { 653 int stat; 654 char *myself = "disconnectCon"; 655 656 if (lc == 0) 657 return (LDAP_SUCCESS); 658 659 if (!assertExclusive(lc)) 660 return (LDAP_UNAVAILABLE); 661 662 if (lc->doDis) { 663 664 /* Increment refCount to protect against interference */ 665 incrementRC(lc); 666 /* refCount must be one (i.e., just us) */ 667 if (lc->refCount != 1) { 668 /* 669 * In use; already marked for disconnect, 670 * so do nothing. 671 */ 672 decrementRC(lc); 673 return (LDAP_BUSY); 674 } 675 676 stat = ldap_unbind_s(lc->ld); 677 if (stat == LDAP_SUCCESS) { 678 lc->ld = 0; 679 lc->isBound = 0; 680 lc->doDis = 0; 681 /* Reset simple page and vlv indication */ 682 lc->simplePage = 0; 683 lc->vlv = 0; 684 } else if (verbose) { 685 logmsg(MSG_NOTIMECHECK, LOG_ERR, 686 "%s: ldap_unbind_s() => %d (%s)", 687 myself, stat, ldap_err2string(stat)); 688 } 689 690 decrementRC(lc); 691 } 692 693 if (lc->doDel) { 694 if (LDAP_UNAVAILABLE == freeCon(lc)) 695 stat = LDAP_UNAVAILABLE; 696 } 697 698 return (stat); 699 } 700 701 /* 702 * controlSupported will determine for a given connection whether a set 703 * of controls is supported or not. The input parameters: 704 * lc The connection 705 * ctrl A an array of OID strings, the terminal string should be NULL 706 * The returned values if LDAP_SUCCESS is returned: 707 * supported A caller supplied array which will be set to TRUE or 708 * FALSE depending on whether the corresponding control 709 * is reported as supported. 710 * Returns LDAP_SUCCESS if the supportedControl attribute is read. 711 */ 712 713 static int 714 controlSupported(__nis_ldap_conn_t *lc, char **ctrl, bool_t *supported) { 715 LDAPMessage *res, *e; 716 char *attr[2], *a, **val; 717 int stat, i; 718 BerElement *ber = 0; 719 char *myself = "controlSupported"; 720 721 attr[0] = "supportedControl"; 722 attr[1] = 0; 723 724 stat = ldap_search_st(lc->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)", 725 attr, 0, &lc->searchTimeout, &res); 726 if (stat != LDAP_SUCCESS) { 727 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 728 "%s: Unable to retrieve supported control information for %s: %s", 729 myself, NIL(lc->sp), ldap_err2string(stat)); 730 return (stat); 731 } 732 733 e = ldap_first_entry(lc->ld, res); 734 if (e != 0) { 735 a = ldap_first_attribute(lc->ld, e, &ber); 736 if (a != 0) { 737 val = ldap_get_values(lc->ld, e, a); 738 if (val == 0) { 739 ldap_memfree(a); 740 if (ber != 0) 741 ber_free(ber, 0); 742 } 743 } 744 } 745 if (e == 0 || a == 0 || val == 0) { 746 ldap_msgfree(res); 747 logmsg(MSG_NOTIMECHECK, LOG_INFO, 748 "%s: Unable to get root DSE for %s", 749 myself, NIL(lc->sp)); 750 return (LDAP_OPERATIONS_ERROR); 751 } 752 753 while (*ctrl != NULL) { 754 *supported = FALSE; 755 for (i = 0; val[i] != 0; i++) { 756 if (strstr(val[i], *ctrl) != 0) { 757 *supported = TRUE; 758 break; 759 } 760 } 761 logmsg(MSG_NOTIMECHECK, LOG_INFO, 762 "%s: %s: %s: %s", 763 myself, NIL(lc->sp), NIL(*ctrl), 764 *supported ? "enabled" : "disabled"); 765 ctrl++; 766 supported++; 767 } 768 769 ldap_value_free(val); 770 ldap_memfree(a); 771 if (ber != 0) 772 ber_free(ber, 0); 773 ldap_msgfree(res); 774 775 return (stat); 776 } 777 778 /* 779 * Connect the LDAP connection 'lc'. Caller must have acquired the 'mutex', 780 * and the refCount must be zero. 781 * 782 * On return, if the status is LDAP_UNAVAILABLE, the caller must not touch 783 * the structure in any way. 784 */ 785 static int 786 connectCon(__nis_ldap_conn_t *lc, int check_ctrl) { 787 struct timeval tp; 788 int stat; 789 bool_t supported[2] = {FALSE, FALSE}; 790 char *ctrl[3] = {LDAP_CONTROL_SIMPLE_PAGE, 791 LDAP_CONTROL_VLVREQUEST, 792 NULL}; 793 794 if (lc == 0) 795 return (LDAP_SUCCESS); 796 797 if (!assertExclusive(lc)) 798 return (LDAP_PARAM_ERROR); 799 800 incrementRC(lc); 801 if (lc->refCount != 1) { 802 /* 803 * Don't want to step on structure when it's used by someone 804 * else. 805 */ 806 decrementRC(lc); 807 return (LDAP_BUSY); 808 } 809 810 (void) gettimeofday(&tp, 0); 811 812 if (lc->ld != 0) { 813 /* Try to disconnect */ 814 lc->doDis++; 815 decrementRC(lc); 816 /* disconnctCon() will do the delete if required */ 817 stat = disconnectCon(lc); 818 if (stat != LDAP_SUCCESS) 819 return (stat); 820 incrementRC(lc); 821 if (lc->refCount != 1 || lc->ld != 0) { 822 decrementRC(lc); 823 return (lc->ld != 0) ? LDAP_SUCCESS : 824 LDAP_BUSY; 825 } 826 } else if (tp.tv_sec < lc->retryTime) { 827 /* Too early to retry connect */ 828 decrementRC(lc); 829 return (LDAP_SERVER_DOWN); 830 } 831 832 /* Set new retry time in case we fail below */ 833 lc->retryTime = tp.tv_sec + ldapConnAttemptRetryTimeout; 834 835 lc->ld = ldapInit(lc->sp, lc->port, proxyInfo.tls_method != no_tls); 836 if (lc->ld == 0) { 837 decrementRC(lc); 838 return (LDAP_LOCAL_ERROR); 839 } 840 841 stat = lc->status = ldapBind(&lc->ld, lc->who, lc->cred, lc->method, 842 lc->bindTimeout); 843 if (lc->status == LDAP_SUCCESS) { 844 lc->isBound = 1; 845 lc->retryTime = 0; 846 if (check_ctrl) { 847 (void) controlSupported(lc, ctrl, supported); 848 lc->simplePage = supported[0]; 849 lc->vlv = supported[1]; 850 lc->batchFrom = 50000; 851 } 852 } 853 854 decrementRC(lc); 855 856 return (stat); 857 } 858 859 /* 860 * Find and return a connection believed to be OK. 861 */ 862 static __nis_ldap_conn_t * 863 findCon(int *stat) { 864 __nis_ldap_conn_t *lc; 865 int ldapStat; 866 char *myself = "findCon"; 867 868 if (stat == 0) 869 stat = &ldapStat; 870 871 (void) rw_rdlock(&ldapConLock); 872 873 if (ldapCon == 0) { 874 /* Probably first call; try to set up the connection list */ 875 (void) rw_unlock(&ldapConLock); 876 if ((*stat = setupConList(proxyInfo.default_servers, 877 proxyInfo.proxy_dn, 878 proxyInfo.proxy_passwd, 879 proxyInfo.auth_method)) != 880 LDAP_SUCCESS) 881 return (0); 882 (void) rw_rdlock(&ldapConLock); 883 } 884 885 for (lc = ldapCon; lc != 0; lc = lc->next) { 886 exclusiveLC(lc); 887 if (!lc->isBound) { 888 *stat = connectCon(lc, 1); 889 if (*stat != LDAP_SUCCESS) { 890 if (*stat != LDAP_UNAVAILABLE) { 891 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 892 "%s: Cannot open connection to LDAP server (%s): %s", 893 myself, NIL(lc->sp), 894 ldap_err2string(*stat)); 895 releaseLC(lc); 896 } 897 continue; 898 } 899 } else if (lc->doDis || lc->doDel) { 900 *stat = disconnectCon(lc); 901 if (*stat != LDAP_UNAVAILABLE) 902 releaseLC(lc); 903 continue; 904 } 905 incrementRC(lc); 906 releaseLC(lc); 907 break; 908 } 909 910 (void) rw_unlock(&ldapConLock); 911 912 return (lc); 913 } 914 915 /* Release connection; decrements ref count for the connection */ 916 static void 917 releaseCon(__nis_ldap_conn_t *lc, int status) { 918 int stat; 919 920 if (lc == 0) 921 return; 922 923 exclusiveLC(lc); 924 925 lc->status = status; 926 927 decrementRC(lc); 928 929 if (lc->doDis) 930 stat = disconnectCon(lc); 931 else 932 stat = LDAP_SUCCESS; 933 934 if (stat != LDAP_UNAVAILABLE) 935 releaseLC(lc); 936 } 937 938 static __nis_ldap_conn_t * 939 createCon(char *sp, char *who, char *cred, auth_method_t method, int port) { 940 __nis_ldap_conn_t *lc; 941 char *myself = "createCon"; 942 char *r; 943 944 if (sp == 0) 945 return (0); 946 947 lc = am(myself, sizeof (*lc)); 948 if (lc == 0) 949 return (0); 950 951 (void) mutex_init(&lc->mutex, 0, 0); 952 (void) mutex_init(&lc->rcMutex, 0, 0); 953 954 /* If we need to delete 'lc', freeCon() wants the mutex held */ 955 exclusiveLC(lc); 956 957 lc->sp = sdup(myself, T, sp); 958 if (lc->sp == 0) { 959 (void) freeCon(lc); 960 return (0); 961 } 962 963 if ((r = strchr(lc->sp, ']')) != 0) { 964 /* 965 * IPv6 address. Does libldap want this with the 966 * '[' and ']' left in place ? Assume so for now. 967 */ 968 r = strchr(r, ':'); 969 } else { 970 r = strchr(lc->sp, ':'); 971 } 972 973 if (r != NULL) { 974 *r++ = '\0'; 975 port = atoi(r); 976 } else if (port == 0) 977 port = proxyInfo.tls_method == ssl_tls ? LDAPS_PORT : LDAP_PORT; 978 979 if (who != 0) { 980 lc->who = sdup(myself, T, who); 981 if (lc->who == 0) { 982 (void) freeCon(lc); 983 return (0); 984 } 985 } 986 987 if (cred != 0) { 988 lc->cred = sdup(myself, T, cred); 989 if (lc->cred == 0) { 990 (void) freeCon(lc); 991 return (0); 992 } 993 } 994 995 lc->method = method; 996 lc->port = port; 997 998 lc->bindTimeout = proxyInfo.bind_timeout; 999 lc->searchTimeout = proxyInfo.search_timeout; 1000 lc->modifyTimeout = proxyInfo.modify_timeout; 1001 lc->addTimeout = proxyInfo.add_timeout; 1002 lc->deleteTimeout = proxyInfo.delete_timeout; 1003 1004 /* All other fields OK at zero */ 1005 1006 releaseLC(lc); 1007 1008 return (lc); 1009 } 1010 1011 static int 1012 setupConList(char *serverList, char *who, char *cred, auth_method_t method) { 1013 char *sls, *sl, *s, *e; 1014 __nis_ldap_conn_t *lc, *tmp; 1015 char *myself = "setupConList"; 1016 1017 if (serverList == 0) 1018 return (LDAP_PARAM_ERROR); 1019 1020 (void) rw_wrlock(&ldapConLock); 1021 1022 if (ldapCon != 0) { 1023 /* Assume we've already been called and done the set-up */ 1024 (void) rw_unlock(&ldapConLock); 1025 return (LDAP_SUCCESS); 1026 } 1027 1028 /* Work on a copy of 'serverList' */ 1029 sl = sls = sdup(myself, T, serverList); 1030 if (sl == 0) { 1031 (void) rw_unlock(&ldapConLock); 1032 return (LDAP_NO_MEMORY); 1033 } 1034 1035 /* Remove leading white space */ 1036 for (0; *sl == ' ' || *sl == '\t'; sl++); 1037 1038 /* Create connection for each server on the list */ 1039 for (s = sl; *s != '\0'; s = e+1) { 1040 int l; 1041 1042 /* Find end of server/port token */ 1043 for (e = s; *e != ' ' && *e != '\t' && *e != '\0'; e++); 1044 if (*e != '\0') 1045 *e = '\0'; 1046 else 1047 e--; 1048 l = slen(s); 1049 1050 if (l > 0) { 1051 lc = createCon(s, who, cred, method, 0); 1052 if (lc == 0) { 1053 free(sls); 1054 (void) rw_unlock(&ldapConLock); 1055 return (LDAP_NO_MEMORY); 1056 } 1057 lc->onList = 1; 1058 if (ldapCon == 0) { 1059 ldapCon = lc; 1060 } else { 1061 /* Insert at end of list */ 1062 for (tmp = ldapCon; tmp->next != 0; 1063 tmp = tmp->next); 1064 tmp->next = lc; 1065 } 1066 } 1067 } 1068 1069 free(sls); 1070 1071 (void) rw_unlock(&ldapConLock); 1072 1073 return (LDAP_SUCCESS); 1074 } 1075 1076 static bool_t 1077 is_same_connection(__nis_ldap_conn_t *lc, LDAPURLDesc *ludpp) 1078 { 1079 return (strcasecmp(ludpp->lud_host, lc->sp) == 0 && 1080 ludpp->lud_port == lc->port); 1081 } 1082 1083 static __nis_ldap_conn_t * 1084 find_connection_from_list(__nis_ldap_conn_t *list, 1085 LDAPURLDesc *ludpp, int *stat) 1086 { 1087 int ldapStat; 1088 __nis_ldap_conn_t *lc = NULL; 1089 if (stat == 0) 1090 stat = &ldapStat; 1091 1092 *stat = LDAP_SUCCESS; 1093 1094 for (lc = list; lc != 0; lc = lc->next) { 1095 exclusiveLC(lc); 1096 if (is_same_connection(lc, ludpp)) { 1097 if (!lc->isBound) { 1098 *stat = connectCon(lc, 1); 1099 if (*stat != LDAP_SUCCESS) { 1100 releaseLC(lc); 1101 continue; 1102 } 1103 } else if (lc->doDis || lc->doDel) { 1104 (void) disconnectCon(lc); 1105 releaseLC(lc); 1106 continue; 1107 } 1108 incrementRC(lc); 1109 releaseLC(lc); 1110 break; 1111 } 1112 releaseLC(lc); 1113 } 1114 return (lc); 1115 } 1116 1117 static __nis_ldap_conn_t * 1118 findReferralCon(char **referralsp, int *stat) 1119 { 1120 __nis_ldap_conn_t *lc = NULL; 1121 __nis_ldap_conn_t *tmp; 1122 int ldapStat; 1123 int i; 1124 LDAPURLDesc *ludpp = NULL; 1125 char *myself = "findReferralCon"; 1126 1127 if (stat == 0) 1128 stat = &ldapStat; 1129 1130 *stat = LDAP_SUCCESS; 1131 1132 /* 1133 * We have the referral lock - to prevent multiple 1134 * threads from creating a referred connection simultaneously 1135 * 1136 * Note that this code assumes that the ldapCon list is a 1137 * static list - that it has previously been created 1138 * (otherwise we wouldn't have gotten a referral) and that 1139 * it will neither grow or shrink - elements may have new 1140 * connections or unbound. If this assumption is no longer valid, 1141 * the locking needs to be reworked. 1142 */ 1143 (void) rw_rdlock(&referralConLock); 1144 1145 for (i = 0; referralsp[i] != NULL; i++) { 1146 if (ldap_url_parse(referralsp[i], &ludpp) != LDAP_SUCCESS) 1147 continue; 1148 /* Ignore referrals if not at the appropriate tls level */ 1149 #ifdef LDAP_URL_OPT_SECURE 1150 if (ludpp->lud_options & LDAP_URL_OPT_SECURE) { 1151 if (proxyInfo.tls_method != ssl_tls) { 1152 ldap_free_urldesc(ludpp); 1153 continue; 1154 } 1155 } else { 1156 if (proxyInfo.tls_method != no_tls) { 1157 ldap_free_urldesc(ludpp); 1158 continue; 1159 } 1160 } 1161 #endif 1162 1163 /* Determine if we already have a connection to the server */ 1164 lc = find_connection_from_list(ldapReferralCon, ludpp, stat); 1165 if (lc == NULL) 1166 lc = find_connection_from_list(ldapCon, ludpp, stat); 1167 ldap_free_urldesc(ludpp); 1168 if (lc != NULL) { 1169 (void) rw_unlock(&referralConLock); 1170 return (lc); 1171 } 1172 } 1173 1174 for (i = 0; referralsp[i] != NULL; i++) { 1175 if (ldap_url_parse(referralsp[i], &ludpp) != LDAP_SUCCESS) 1176 continue; 1177 /* Ignore referrals if not at the appropriate tls level */ 1178 #ifdef LDAP_URL_OPT_SECURE 1179 if (ludpp->lud_options & LDAP_URL_OPT_SECURE) { 1180 if (proxyInfo.tls_method != ssl_tls) { 1181 ldap_free_urldesc(ludpp); 1182 continue; 1183 } 1184 } else { 1185 if (proxyInfo.tls_method != no_tls) { 1186 ldap_free_urldesc(ludpp); 1187 continue; 1188 } 1189 } 1190 #endif 1191 lc = createCon(ludpp->lud_host, proxyInfo.proxy_dn, 1192 proxyInfo.proxy_passwd, 1193 proxyInfo.auth_method, 1194 ludpp->lud_port); 1195 if (lc == 0) { 1196 ldap_free_urldesc(ludpp); 1197 (void) rw_unlock(&referralConLock); 1198 *stat = LDAP_NO_MEMORY; 1199 logmsg(MSG_NOTIMECHECK, LOG_INFO, 1200 "%s: Could not connect to host: %s", 1201 myself, NIL(ludpp->lud_host)); 1202 return (NULL); 1203 } 1204 1205 lc->onList = 1; 1206 if (ldapReferralCon == 0) { 1207 ldapReferralCon = lc; 1208 } else { 1209 /* Insert at end of list */ 1210 for (tmp = ldapReferralCon; tmp->next != 0; 1211 tmp = tmp->next) {} 1212 tmp->next = lc; 1213 } 1214 lc = find_connection_from_list(ldapReferralCon, ludpp, stat); 1215 ldap_free_urldesc(ludpp); 1216 if (lc != NULL) 1217 break; 1218 } 1219 (void) rw_unlock(&referralConLock); 1220 if (lc == NULL) { 1221 logmsg(MSG_NOTIMECHECK, LOG_INFO, 1222 "%s: Could not find a connection to %s, ...", 1223 myself, NIL(referralsp[0])); 1224 } 1225 1226 return (lc); 1227 } 1228 1229 /* 1230 * Find and return a connection believed to be OK and ensure children 1231 * will never use parent's connection. 1232 */ 1233 static __nis_ldap_conn_t * 1234 findYPCon(__nis_ldap_search_t *ls, int *stat) { 1235 __nis_ldap_conn_t *lc, *newlc; 1236 int ldapStat, newstat; 1237 char *myself = "findYPCon"; 1238 1239 if (stat == 0) 1240 stat = &ldapStat; 1241 1242 (void) rw_rdlock(&ldapConLock); 1243 1244 if (ldapCon == 0) { 1245 /* Probably first call; try to set up the connection list */ 1246 (void) rw_unlock(&ldapConLock); 1247 if ((*stat = setupConList(proxyInfo.default_servers, 1248 proxyInfo.proxy_dn, 1249 proxyInfo.proxy_passwd, 1250 proxyInfo.auth_method)) != 1251 LDAP_SUCCESS) 1252 return (0); 1253 (void) rw_rdlock(&ldapConLock); 1254 } 1255 1256 for (lc = ldapCon; lc != 0; lc = lc->next) { 1257 exclusiveLC(lc); 1258 1259 if (lc->isBound && (lc->doDis || lc->doDel)) { 1260 *stat = disconnectCon(lc); 1261 if (*stat != LDAP_UNAVAILABLE) 1262 releaseLC(lc); 1263 continue; 1264 } 1265 1266 /* 1267 * Use a new connection for all cases except when 1268 * requested by the main thread in the parent ypserv 1269 * process. 1270 */ 1271 if (ls->useCon == 0) { 1272 newlc = createCon(lc->sp, lc->who, lc->cred, 1273 lc->method, lc->port); 1274 if (!newlc) { 1275 releaseLC(lc); 1276 continue; 1277 } 1278 if (lc->ld != 0) { 1279 newlc->simplePage = lc->simplePage; 1280 newlc->vlv = lc->vlv; 1281 newlc->batchFrom = lc->batchFrom; 1282 } 1283 releaseLC(lc); 1284 exclusiveLC(newlc); 1285 newstat = connectCon(newlc, 0); 1286 if (newstat != LDAP_SUCCESS) { 1287 if (newstat != LDAP_UNAVAILABLE) { 1288 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 1289 "%s: Cannot open connection to LDAP server (%s): %s", 1290 myself, NIL(newlc->sp), 1291 ldap_err2string(*stat)); 1292 } 1293 (void) freeCon(newlc); 1294 newlc = 0; 1295 continue; 1296 } 1297 1298 /* 1299 * No need to put newlc on the ldapCon list as this 1300 * connection will be freed after use. 1301 */ 1302 newlc->onList = 0; 1303 1304 lc = newlc; 1305 } else if (!lc->isBound) { 1306 *stat = connectCon(lc, 1); 1307 if (*stat != LDAP_SUCCESS) { 1308 if (*stat != LDAP_UNAVAILABLE) { 1309 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 1310 "%s: Cannot open connection to LDAP server (%s): %s", 1311 myself, NIL(lc->sp), 1312 ldap_err2string(*stat)); 1313 releaseLC(lc); 1314 } 1315 continue; 1316 } 1317 } 1318 1319 incrementRC(lc); 1320 releaseLC(lc); 1321 break; 1322 } 1323 1324 (void) rw_unlock(&ldapConLock); 1325 1326 return (lc); 1327 } 1328 1329 #define SORTKEYLIST "cn uid" 1330 1331 /* 1332 * Perform an LDAP search operation per 'ls', adding the result(s) to 1333 * a copy of the 'rvIn' structure; the copy becomes the return value. 1334 * The caller must deallocate both 'rvIn' and the result, if any. 1335 * 1336 * On entry, '*numValues' contains a hint regarding the expected 1337 * number of entries. Zero is the same as one, and negative values 1338 * imply no information. This is used to decide whether or not to 1339 * try an indexed search. 1340 * 1341 * On successful (non-NULL) return, '*numValues' contains the number 1342 * of __nis_rule_value_t elements in the returned array, and '*stat' 1343 * the LDAP operations status. 1344 */ 1345 __nis_rule_value_t * 1346 ldapSearch(__nis_ldap_search_t *ls, int *numValues, __nis_rule_value_t *rvIn, 1347 int *ldapStat) { 1348 __nis_rule_value_t *rv = 0; 1349 int stat, numEntries, numVals, tnv, done, lprEc; 1350 LDAPMessage *msg = 0, *m; 1351 __nis_ldap_conn_t *lc; 1352 struct timeval tv, start, now; 1353 LDAPsortkey **sortKeyList = 0; 1354 LDAPControl *ctrls[3], *sortCtrl = 0, *vlvCtrl = 0; 1355 LDAPControl **retCtrls = 0; 1356 LDAPVirtualList vList; 1357 struct berval *spCookie = 0; 1358 int doVLV = 0; 1359 int doSP = 0; 1360 long index; 1361 char *myself = "ldapSearch"; 1362 bool_t follow_referral = 1363 proxyInfo.follow_referral == follow; 1364 int doIndex = 1; 1365 char **referralsp = NULL; 1366 1367 if (ldapStat == 0) 1368 ldapStat = &stat; 1369 1370 if (ls == 0) { 1371 *ldapStat = LDAP_PARAM_ERROR; 1372 return (0); 1373 } 1374 1375 if (yp2ldap) { 1376 /* make sure the parent's connection is not used by child */ 1377 if ((lc = findYPCon(ls, ldapStat)) == 0) { 1378 *ldapStat = LDAP_SERVER_DOWN; 1379 return (0); 1380 } 1381 } else { 1382 if ((lc = findCon(ldapStat)) == 0) { 1383 *ldapStat = LDAP_SERVER_DOWN; 1384 return (0); 1385 } 1386 } 1387 1388 if (numValues != 0 && (*numValues == 0 || *numValues == 1)) 1389 doIndex = 0; 1390 1391 retry_new_conn: 1392 /* Prefer VLV over simple page, and SP over nothing */ 1393 if (doIndex && lc->vlv) { 1394 stat = ldap_create_sort_keylist(&sortKeyList, SORTKEYLIST); 1395 if (stat != LDAP_SUCCESS) { 1396 logmsg(MSG_NOTIMECHECK, LOG_INFO, 1397 "%s: Error creating sort keylist: %s", 1398 myself, ldap_err2string(stat)); 1399 freeRuleValue(rv, numVals); 1400 *ldapStat = stat; 1401 rv = 0; 1402 goto retry_noVLV; 1403 } 1404 stat = ldap_create_sort_control(lc->ld, sortKeyList, 1, 1405 &sortCtrl); 1406 if (stat == LDAP_SUCCESS) { 1407 vList.ldvlist_before_count = 0; 1408 vList.ldvlist_after_count = lc->batchFrom - 1; 1409 vList.ldvlist_attrvalue = 0; 1410 vList.ldvlist_extradata = 0; 1411 index = 1; 1412 doVLV = 1; 1413 } else { 1414 ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, &stat); 1415 logmsg(MSG_NOTIMECHECK, LOG_INFO, 1416 "%s: Error creating VLV sort control: %s", 1417 myself, ldap_err2string(stat)); 1418 freeRuleValue(rv, numVals); 1419 *ldapStat = stat; 1420 rv = 0; 1421 } 1422 } 1423 1424 retry_noVLV: 1425 1426 if (doIndex && !doVLV && lc->simplePage) { 1427 spCookie = am(myself, sizeof (*spCookie)); 1428 if (spCookie != 0 && 1429 (spCookie->bv_val = sdup(myself, T, "")) != 0) { 1430 spCookie->bv_len = 0; 1431 doSP = 1; 1432 } else { 1433 logmsg(MSG_NOTIMECHECK, LOG_INFO, 1434 "%s: No memory for simple page cookie; using un-paged LDAP search", 1435 myself); 1436 freeRuleValue(rv, numVals); 1437 *ldapStat = stat; 1438 rv = 0; 1439 goto cleanup; 1440 } 1441 } 1442 1443 if (!doVLV && !doSP) 1444 ctrls[0] = ctrls[1] = 0; 1445 1446 numVals = 0; 1447 done = 0; 1448 1449 if (ls->timeout.tv_sec || ls->timeout.tv_usec) { 1450 tv = ls->timeout; 1451 } else { 1452 tv = lc->searchTimeout; 1453 } 1454 (void) gettimeofday(&start, 0); 1455 1456 do { 1457 /* don't do vlv or simple page for base level searches */ 1458 if (doVLV && ls->base != LDAP_SCOPE_BASE) { 1459 vList.ldvlist_index = index; 1460 vList.ldvlist_size = 0; 1461 if (vlvCtrl != 0) 1462 ldap_control_free(vlvCtrl); 1463 stat = ldap_create_virtuallist_control(lc->ld, 1464 &vList, &vlvCtrl); 1465 if (stat != LDAP_SUCCESS) { 1466 ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, 1467 &stat); 1468 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1469 "%s: Error creating VLV at index %ld: %s", 1470 myself, index, ldap_err2string(stat)); 1471 *ldapStat = stat; 1472 freeRuleValue(rv, numVals); 1473 rv = 0; 1474 goto cleanup; 1475 } 1476 ctrls[0] = sortCtrl; 1477 ctrls[1] = vlvCtrl; 1478 ctrls[2] = 0; 1479 stat = ldap_search_ext_s(lc->ld, ls->base, 1480 ls->scope, ls->filter, ls->attrs, 1481 ls->attrsonly, ctrls, 0, &tv, 1482 proxyInfo.search_size_limit, &msg); 1483 /* don't do vlv or simple page for base level searches */ 1484 } else if (doSP && ls->base != LDAP_SCOPE_BASE) { 1485 if (ctrls[0] != 0) 1486 ldap_control_free(ctrls[0]); 1487 stat = ldap_create_page_control(lc->ld, 1488 lc->batchFrom, spCookie, 0, &ctrls[0]); 1489 if (stat != LDAP_SUCCESS) { 1490 ber_bvfree(spCookie); 1491 spCookie = 0; 1492 ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, 1493 &stat); 1494 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1495 "%s: Simple page error: %s", 1496 myself, ldap_err2string(stat)); 1497 freeRuleValue(rv, numVals); 1498 *ldapStat = stat; 1499 rv = 0; 1500 goto cleanup; 1501 } 1502 ctrls[1] = 0; 1503 stat = ldap_search_ext_s(lc->ld, ls->base, 1504 ls->scope, ls->filter, ls->attrs, 1505 ls->attrsonly, ctrls, 0, &tv, 1506 proxyInfo.search_size_limit, &msg); 1507 } else { 1508 stat = ldap_search_st(lc->ld, ls->base, ls->scope, 1509 ls->filter, ls->attrs, ls->attrsonly, 1510 &tv, &msg); 1511 } 1512 if (stat == LDAP_SUCCESS) 1513 ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, &stat); 1514 1515 if (stat == LDAP_SERVER_DOWN) { 1516 lc->doDis++; 1517 releaseCon(lc, stat); 1518 lc = (yp2ldap)?findYPCon(ls, ldapStat): 1519 findCon(ldapStat); 1520 if (lc == 0) { 1521 *ldapStat = LDAP_SERVER_DOWN; 1522 rv = 0; 1523 goto cleanup; 1524 } 1525 goto retry_new_conn; 1526 } 1527 1528 if (stat == LDAP_REFERRAL && follow_referral) { 1529 (void) ldap_parse_result(lc->ld, msg, NULL, NULL, NULL, 1530 &referralsp, NULL, 0); 1531 if (referralsp != NULL) { 1532 /* We support at most one level of referrals */ 1533 follow_referral = FALSE; 1534 releaseCon(lc, stat); 1535 lc = findReferralCon(referralsp, &stat); 1536 ldap_value_free(referralsp); 1537 if (lc == NULL) { 1538 freeRuleValue(rv, numVals); 1539 rv = 0; 1540 *ldapStat = stat; 1541 goto cleanup; 1542 } 1543 stat = LDAP_SUCCESS; 1544 goto retry_new_conn; 1545 } 1546 } 1547 *ldapStat = stat; 1548 1549 if (*ldapStat == LDAP_NO_SUCH_OBJECT) { 1550 freeRuleValue(rv, numVals); 1551 rv = 0; 1552 goto cleanup; 1553 } else if (doVLV && *ldapStat == LDAP_INSUFFICIENT_ACCESS) { 1554 /* 1555 * The LDAP server (at least Netscape 4.x) can return 1556 * LDAP_INSUFFICIENT_ACCESS when VLV is supported, 1557 * but not for the bind DN specified. So, just in 1558 * case, we clean up, and try again without VLV. 1559 */ 1560 doVLV = 0; 1561 if (msg != 0) { 1562 (void) ldap_msgfree(msg); 1563 msg = 0; 1564 } 1565 if (ctrls[0] != 0) { 1566 ldap_control_free(ctrls[0]); 1567 ctrls[0] = 0; 1568 } 1569 if (ctrls[1] != 0) { 1570 ldap_control_free(ctrls[1]); 1571 ctrls[1] = 0; 1572 } 1573 logmsg(MSG_VLV_INSUFF_ACC, LOG_WARNING, 1574 "%s: VLV insufficient access from server %s; retrying without VLV", 1575 myself, NIL(lc->sp)); 1576 goto retry_noVLV; 1577 } else if (*ldapStat != LDAP_SUCCESS) { 1578 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 1579 "ldap_search(0x%x,\n\t\"%s\",\n\t %d,", 1580 lc->ld, NIL(ls->base), ls->scope); 1581 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 1582 "\t\"%s\",\n\t0x%x,\n\t%d) => %d (%s)", 1583 NIL(ls->filter), ls->attrs, ls->attrsonly, 1584 *ldapStat, ldap_err2string(stat)); 1585 freeRuleValue(rv, numVals); 1586 rv = 0; 1587 goto cleanup; 1588 } 1589 1590 numEntries = ldap_count_entries(lc->ld, msg); 1591 if (numEntries == 0 && *ldapStat == LDAP_SUCCESS) { 1592 /* 1593 * This is a bit weird, but the server (or, at least, 1594 * ldap_search_ext()) can sometimes return 1595 * LDAP_SUCCESS and no entries when it didn't 1596 * find what we were looking for. Seems it ought to 1597 * return LDAP_NO_SUCH_OBJECT or some such. 1598 */ 1599 freeRuleValue(rv, numVals); 1600 rv = 0; 1601 *ldapStat = LDAP_NO_SUCH_OBJECT; 1602 goto cleanup; 1603 } 1604 1605 tnv = numVals + numEntries; 1606 if ((rv = growRuleValue(numVals, tnv, rv, rvIn)) == 0) { 1607 *ldapStat = LDAP_NO_MEMORY; 1608 goto cleanup; 1609 } 1610 1611 for (m = ldap_first_entry(lc->ld, msg); m != 0; 1612 m = ldap_next_entry(lc->ld, m), numVals++) { 1613 char *nm; 1614 BerElement *ber = 0; 1615 1616 if (numVals > tnv) { 1617 logmsg(MSG_NOTIMECHECK, LOG_INFO, 1618 "%s: Inconsistent LDAP entry count > %d", 1619 myself, numEntries); 1620 break; 1621 } 1622 1623 nm = ldap_get_dn(lc->ld, m); 1624 if (nm == 0 || addSAttr2RuleValue("dn", nm, 1625 &rv[numVals])) { 1626 sfree(nm); 1627 *ldapStat = LDAP_NO_MEMORY; 1628 freeRuleValue(rv, tnv); 1629 rv = 0; 1630 goto cleanup; 1631 } 1632 sfree(nm); 1633 1634 for (nm = ldap_first_attribute(lc->ld, m, &ber); 1635 nm != 0; 1636 nm = ldap_next_attribute(lc->ld, m, ber)) { 1637 struct berval **val; 1638 int i, nv; 1639 1640 val = ldap_get_values_len(lc->ld, m, nm); 1641 nv = (val == 0) ? 0 : 1642 ldap_count_values_len(val); 1643 for (i = 0; i < nv; i++) { 1644 /* 1645 * Since we don't know if the value is 1646 * BER-encoded or not, we mark it as a 1647 * string. All is well as long as we 1648 * don't insist on 'vt_ber' when 1649 * interpreting. 1650 */ 1651 if (addAttr2RuleValue(vt_string, nm, 1652 val[i]->bv_val, 1653 val[i]->bv_len, 1654 &rv[numVals])) { 1655 if (ber != 0) 1656 ber_free(ber, 0); 1657 ldap_value_free_len(val); 1658 *ldapStat = LDAP_NO_MEMORY; 1659 freeRuleValue(rv, tnv); 1660 rv = 0; 1661 goto cleanup; 1662 } 1663 } 1664 /* 1665 * XXX the ldap_first_attribute(3LDAP) man 1666 * page says that the ldap_first_attribute/ 1667 * ldap_next_attribute should be treated as 1668 * static, but the libldap.so.4 code mallocs 1669 * (and it's not TSD). So, in order to avoid 1670 * a leak, we free the return value. 1671 */ 1672 ldap_memfree(nm); 1673 if (val != 0) 1674 ldap_value_free_len(val); 1675 } 1676 /* 1677 * XXX ldap_next_attribute(3LDAP) says that the 'ber' 1678 * pointer is freed when it returns NULL, but that's 1679 * not implemented in the libldap.so.4 code, so we 1680 * free it here in order to avoid a memory leak. 1681 */ 1682 if (ber != 0) 1683 ber_free(ber, 0); 1684 } 1685 1686 if (numVals != tnv) { 1687 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 1688 "%s: Inconsistent LDAP entry count, found = %d, expected %d", 1689 myself, numVals, tnv); 1690 } 1691 1692 if (doVLV) { 1693 stat = ldap_parse_result(lc->ld, msg, &lprEc, 0, 0, 0, 1694 &retCtrls, 0); 1695 if (stat != LDAP_SUCCESS) { 1696 ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, 1697 &stat); 1698 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1699 "%s: VLV parse result error: %s", 1700 myself, ldap_err2string(stat)); 1701 *ldapStat = stat; 1702 freeRuleValue(rv, tnv); 1703 rv = 0; 1704 goto cleanup; 1705 } 1706 if (retCtrls != 0) { 1707 unsigned long targetPosP = 0; 1708 unsigned long listSize = 0; 1709 1710 stat = ldap_parse_virtuallist_control(lc->ld, 1711 retCtrls, &targetPosP, &listSize, 1712 &lprEc); 1713 if (stat == LDAP_SUCCESS) { 1714 index = targetPosP + lc->batchFrom; 1715 if (index >= listSize) 1716 done = 1; 1717 } 1718 ldap_controls_free(retCtrls); 1719 retCtrls = 0; 1720 } else { 1721 done = 1; 1722 } 1723 } else if (doSP) { 1724 stat = ldap_parse_result(lc->ld, msg, &lprEc, 0, 0, 0, 1725 &retCtrls, 0); 1726 if (stat != LDAP_SUCCESS) { 1727 ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, 1728 &stat); 1729 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1730 "%s: Simple page parse result error: %s", 1731 myself, ldap_err2string(stat)); 1732 *ldapStat = stat; 1733 freeRuleValue(rv, tnv); 1734 rv = 0; 1735 goto cleanup; 1736 } 1737 if (retCtrls != 0) { 1738 unsigned int count; 1739 1740 if (spCookie != 0) { 1741 ber_bvfree(spCookie); 1742 spCookie = 0; 1743 } 1744 stat = ldap_parse_page_control(lc->ld, 1745 retCtrls, &count, &spCookie); 1746 if (stat == LDAP_SUCCESS) { 1747 if (spCookie == 0 || 1748 spCookie->bv_val == 0 || 1749 spCookie->bv_len == 0) 1750 done = 1; 1751 } 1752 ldap_controls_free(retCtrls); 1753 retCtrls = 0; 1754 } else { 1755 done = 1; 1756 } 1757 } else { 1758 done = 1; 1759 } 1760 1761 (void) ldap_msgfree(msg); 1762 msg = 0; 1763 1764 /* 1765 * If we're using VLV or SP, the timeout should apply 1766 * to all calls as an aggregate, so we need to reduce 1767 * 'tv' with the time spent on this chunk of data. 1768 */ 1769 if (!done) { 1770 struct timeval tmp; 1771 1772 (void) gettimeofday(&now, 0); 1773 tmp = now; 1774 now.tv_sec -= start.tv_sec; 1775 now.tv_usec -= start.tv_usec; 1776 if (now.tv_usec < 0) { 1777 now.tv_usec += 1000000; 1778 now.tv_sec -= 1; 1779 } 1780 tv.tv_sec -= now.tv_sec; 1781 tv.tv_usec -= now.tv_usec; 1782 if (tv.tv_usec < 0) { 1783 tv.tv_usec += 1000000; 1784 tv.tv_sec -= 1; 1785 } 1786 if (tv.tv_sec < 0) { 1787 *ldapStat = LDAP_TIMEOUT; 1788 freeRuleValue(rv, tnv); 1789 rv = 0; 1790 goto cleanup; 1791 } 1792 start = tmp; 1793 } 1794 1795 } while (!done); 1796 1797 if (numValues != 0) 1798 *numValues = numVals; 1799 1800 cleanup: 1801 if (NULL != lc) { 1802 if (yp2ldap && ls->useCon == 0) { 1803 /* Disconnect and free the connection */ 1804 lc->doDis++; 1805 lc->doDel++; 1806 releaseCon(lc, stat); 1807 releaseLC(lc); 1808 1809 } else { 1810 releaseCon(lc, stat); 1811 } 1812 } 1813 if (msg != 0) 1814 (void) ldap_msgfree(msg); 1815 if (ctrls[0] != 0) 1816 ldap_control_free(ctrls[0]); 1817 if (ctrls[1] != 0) 1818 ldap_control_free(ctrls[1]); 1819 if (spCookie != 0) 1820 ber_bvfree(spCookie); 1821 if (sortKeyList != 0) 1822 ldap_free_sort_keylist(sortKeyList); 1823 1824 return (rv); 1825 } 1826 1827 static void 1828 freeLdapModEntry(LDAPMod *m) { 1829 1830 if (m == 0) 1831 return; 1832 1833 sfree(m->mod_type); 1834 if ((m->mod_op & LDAP_MOD_BVALUES) == 0) { 1835 char **v = m->mod_values; 1836 1837 if (v != 0) { 1838 while (*v != 0) { 1839 sfree(*v); 1840 v++; 1841 } 1842 free(m->mod_values); 1843 } 1844 } else { 1845 struct berval **b = m->mod_bvalues; 1846 1847 if (b != 0) { 1848 while (*b != 0) { 1849 sfree((*b)->bv_val); 1850 free(*b); 1851 b++; 1852 } 1853 free(m->mod_bvalues); 1854 } 1855 } 1856 1857 free(m); 1858 } 1859 1860 static void 1861 freeLdapMod(LDAPMod **mods) { 1862 LDAPMod *m, **org = mods; 1863 1864 if (mods == 0) 1865 return; 1866 1867 while ((m = *mods) != 0) { 1868 freeLdapModEntry(m); 1869 mods++; 1870 } 1871 1872 free(org); 1873 } 1874 1875 /* 1876 * Convert a rule-value structure to the corresponding LDAPMod. 1877 * If 'add' is set, attributes/values are added; object classes 1878 * are also added. If 'add' is cleared, attributes/values are modified, 1879 * and 'oc' controls whether or not object classes are added. 1880 */ 1881 LDAPMod ** 1882 search2LdapMod(__nis_rule_value_t *rv, int add, int oc) { 1883 LDAPMod **mods; 1884 int i, j, nm; 1885 char *myself = "search2LdapMod"; 1886 1887 if (rv == 0 || rv->numAttrs <= 0) 1888 return (0); 1889 1890 mods = am(myself, (rv->numAttrs + 1) * sizeof (mods[0])); 1891 if (mods == 0) 1892 return (0); 1893 1894 for (i = 0, nm = 0; i < rv->numAttrs; i++) { 1895 int isOc; 1896 /* 1897 * If we're creating an LDAPMod array for an add operation, 1898 * just skip attributes that should be deleted. 1899 */ 1900 if (add && rv->attrVal[i].numVals < 0) 1901 continue; 1902 1903 /* 1904 * Skip DN; it's specified separately to ldap_modify() 1905 * and ldap_add(), and mustn't appear among the 1906 * attributes to be modified/added. 1907 */ 1908 if (strcasecmp("dn", rv->attrName[i]) == 0) 1909 continue; 1910 1911 /* 1912 * If modifying, and 'oc' is off, skip object class 1913 * attributes. 1914 */ 1915 isOc = (strcasecmp("objectclass", rv->attrName[i]) == 0); 1916 if (!add && !oc && isOc) 1917 continue; 1918 1919 mods[nm] = am(myself, sizeof (*mods[nm])); 1920 if (mods[nm] == 0) { 1921 freeLdapMod(mods); 1922 return (0); 1923 } 1924 1925 /* 'mod_type' is the attribute name */ 1926 mods[nm]->mod_type = sdup(myself, T, rv->attrName[i]); 1927 if (mods[nm]->mod_type == 0) { 1928 freeLdapMod(mods); 1929 return (0); 1930 } 1931 1932 /* 1933 * numVals < 0 means attribute and all values should 1934 * be deleted. 1935 */ 1936 if (rv->attrVal[i].numVals < 0) { 1937 mods[nm]->mod_op = LDAP_MOD_DELETE; 1938 mods[nm]->mod_values = 0; 1939 nm++; 1940 continue; 1941 } 1942 1943 /* objectClass attributes always added */ 1944 mods[nm]->mod_op = (add) ? 0 : ((isOc) ? 0 : LDAP_MOD_REPLACE); 1945 1946 if (rv->attrVal[i].type == vt_string) { 1947 /* 1948 * mods[]->mod_values is a NULL-terminated array 1949 * of (char *)'s. 1950 */ 1951 mods[nm]->mod_values = am(myself, 1952 (rv->attrVal[i].numVals + 1) * 1953 sizeof (mods[nm]->mod_values[0])); 1954 if (mods[nm]->mod_values == 0) { 1955 freeLdapMod(mods); 1956 return (0); 1957 } 1958 for (j = 0; j < rv->attrVal[i].numVals; j++) { 1959 /* 1960 * Just in case the string isn't NUL 1961 * terminated, add one byte to the 1962 * allocated length; am() will initialize 1963 * the buffer to zero. 1964 */ 1965 mods[nm]->mod_values[j] = am(myself, 1966 rv->attrVal[i].val[j].length + 1); 1967 if (mods[nm]->mod_values[j] == 0) { 1968 freeLdapMod(mods); 1969 return (0); 1970 } 1971 memcpy(mods[nm]->mod_values[j], 1972 rv->attrVal[i].val[j].value, 1973 rv->attrVal[i].val[j].length); 1974 } 1975 } else { 1976 mods[nm]->mod_op |= LDAP_MOD_BVALUES; 1977 mods[nm]->mod_bvalues = am(myself, 1978 (rv->attrVal[i].numVals+1) * 1979 sizeof (mods[nm]->mod_bvalues[0])); 1980 if (mods[nm]->mod_bvalues == 0) { 1981 freeLdapMod(mods); 1982 return (0); 1983 } 1984 for (j = 0; j < rv->attrVal[i].numVals; j++) { 1985 mods[nm]->mod_bvalues[j] = am(myself, 1986 sizeof (*mods[nm]->mod_bvalues[j])); 1987 if (mods[nm]->mod_bvalues[j] == 0) { 1988 freeLdapMod(mods); 1989 return (0); 1990 } 1991 mods[nm]->mod_bvalues[j]->bv_val = am(myself, 1992 rv->attrVal[i].val[j].length); 1993 if (mods[nm]->mod_bvalues[j]->bv_val == 0) { 1994 freeLdapMod(mods); 1995 return (0); 1996 } 1997 mods[nm]->mod_bvalues[j]->bv_len = 1998 rv->attrVal[i].val[j].length; 1999 memcpy(mods[nm]->mod_bvalues[j]->bv_val, 2000 rv->attrVal[i].val[j].value, 2001 mods[nm]->mod_bvalues[j]->bv_len); 2002 } 2003 } 2004 nm++; 2005 } 2006 2007 return (mods); 2008 } 2009 2010 /* 2011 * Remove 'value' from 'val'. If value==0, remove the entire 2012 * __nis_single_value_t array from 'val'. 2013 */ 2014 static void 2015 removeSingleValue(__nis_value_t *val, void *value, int length) { 2016 int i; 2017 2018 if (val == 0) 2019 return; 2020 2021 if (value == 0) { 2022 for (i = 0; i < val->numVals; i++) { 2023 sfree(val->val[i].value); 2024 } 2025 sfree(val->val); 2026 val->val = 0; 2027 val->numVals = 0; 2028 return; 2029 } 2030 2031 for (i = 0; i < val->numVals; i++) { 2032 if (val->val[i].value == 0 || (val->val[i].length != length)) 2033 continue; 2034 if (memcmp(val->val[i].value, value, length) != 0) 2035 continue; 2036 sfree(val->val[i].value); 2037 if (i != (val->numVals - 1)) { 2038 (void) memmove(&val->val[i], &val->val[i+1], 2039 (val->numVals - 1 - i) * sizeof (val->val[0])); 2040 } 2041 val->numVals -= 1; 2042 break; 2043 } 2044 } 2045 2046 /* 2047 * Helper function for LdapModify 2048 * When a modify operation fails with an object class violation, 2049 * the most probable reason is that the attributes we're modifying are new, 2050 * and the needed object class are not present. So, try the modify again, 2051 * but add the object classes this time. 2052 */ 2053 2054 static int 2055 ldapModifyObjectClass(__nis_ldap_conn_t **lc, char *dn, 2056 __nis_rule_value_t *rvIn, char *objClassAttrs) 2057 { 2058 LDAPMod **mods = 0; 2059 int msgid; 2060 int lderr; 2061 struct timeval tv; 2062 int stat; 2063 LDAPMessage *msg = 0; 2064 char **referralsp = NULL; 2065 __nis_rule_value_t *rv, *rvldap; 2066 __nis_ldap_search_t *ls; 2067 int i, ocrv, ocrvldap, nv; 2068 char *oc[2] = { "objectClass", 0}; 2069 char *myself = "ldapModifyObjectClass"; 2070 2071 rv = initRuleValue(1, rvIn); 2072 if (rv == 0) 2073 return (LDAP_NO_MEMORY); 2074 2075 delAttrFromRuleValue(rv, "objectClass"); 2076 rv = addObjectClasses(rv, objClassAttrs); 2077 if (rv == 0) { 2078 stat = LDAP_OPERATIONS_ERROR; 2079 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 2080 "%s: addObjectClasses failed for %s", 2081 myself, NIL(dn)); 2082 goto cleanup; 2083 } 2084 2085 /* 2086 * Before adding the object classes whole-sale, try retrieving 2087 * the entry specified by the 'dn'. If it exists, we filter out 2088 * those object classes that already are present in LDAP from our 2089 * update. 2090 */ 2091 ls = buildLdapSearch(dn, LDAP_SCOPE_BASE, 0, 0, "objectClass=*", 2092 oc, 0, 1); 2093 if (ls == 0) { 2094 logmsg(MSG_NOTIMECHECK, LOG_INFO, 2095 "%s: Unable to build DN search for \"%s\"", 2096 myself, NIL(dn)); 2097 /* Fall through to try just adding the object classes */ 2098 goto addObjectClasses; 2099 } 2100 2101 nv = 0; 2102 rvldap = ldapSearch(ls, &nv, 0, &lderr); 2103 freeLdapSearch(ls); 2104 if (rvldap == 0) { 2105 logmsg(MSG_NOTIMECHECK, LOG_INFO, 2106 "%s: No data for DN search (\"%s\"); LDAP status %d", 2107 myself, NIL(dn), lderr); 2108 /* Fall through to try just adding the object classes */ 2109 goto addObjectClasses; 2110 } 2111 2112 /* 2113 * Find the indices of the 'objectClass' attribute 2114 * in 'rvldap' and 'rv'. 2115 */ 2116 for (i = 0, ocrvldap = -1; i < rvldap->numAttrs; i++) { 2117 if (rvldap->attrName[i] != 0 && 2118 strcasecmp("objectClass", rvldap->attrName[i]) == 0) { 2119 ocrvldap = i; 2120 break; 2121 } 2122 } 2123 for (i = 0, ocrv = -1; i < rv->numAttrs; i++) { 2124 if (rv->attrName[i] != 0 && 2125 strcasecmp("objectClass", rv->attrName[i]) == 0) { 2126 ocrv = i; 2127 break; 2128 } 2129 } 2130 2131 /* 2132 * Remove those object classes that already exist 2133 * in LDAP (i.e., in 'rvldap') from 'rv'. 2134 */ 2135 if (ocrv >= 0 && ocrvldap >= 0) { 2136 for (i = 0; i < rvldap->attrVal[ocrvldap].numVals; i++) { 2137 removeSingleValue(&rv->attrVal[ocrv], 2138 rvldap->attrVal[ocrvldap].val[i].value, 2139 rvldap->attrVal[ocrvldap].val[i].length); 2140 } 2141 /* 2142 * If no 'objectClass' values left in 'rv', delete 2143 * 'objectClass' from 'rv'. 2144 */ 2145 if (rv->attrVal[ocrv].numVals == 0) 2146 delAttrFromRuleValue(rv, "objectClass"); 2147 } 2148 2149 /* 2150 * 'rv' now contains the update we want to make, with just the 2151 * object class(es) that need to be added. Fall through to the 2152 * actual LDAP modify operation. 2153 */ 2154 freeRuleValue(rvldap, 1); 2155 2156 addObjectClasses: 2157 2158 mods = search2LdapMod(rv, 0, 1); 2159 if (mods == 0) { 2160 stat = LDAP_OPERATIONS_ERROR; 2161 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 2162 "%s: Unable to create LDAP modify changes with object classes for %s", 2163 myself, NIL(dn)); 2164 goto cleanup; 2165 } 2166 msgid = ldap_modify((*lc)->ld, dn, mods); 2167 if (msgid != -1) { 2168 tv = (*lc)->modifyTimeout; 2169 stat = ldap_result((*lc)->ld, msgid, 0, &tv, &msg); 2170 if (stat == 0) { 2171 stat = LDAP_TIMEOUT; 2172 } else if (stat == -1) { 2173 (void) ldap_get_option((*lc)->ld, 2174 LDAP_OPT_ERROR_NUMBER, &stat); 2175 } else { 2176 stat = ldap_parse_result((*lc)->ld, msg, &lderr, NULL, 2177 NULL, &referralsp, NULL, 0); 2178 if (stat == LDAP_SUCCESS) 2179 stat = lderr; 2180 stat = ldap_result2error((*lc)->ld, msg, 0); 2181 } 2182 } else { 2183 (void) ldap_get_option((*lc)->ld, LDAP_OPT_ERROR_NUMBER, 2184 &stat); 2185 } 2186 if (proxyInfo.follow_referral == follow && 2187 stat == LDAP_REFERRAL && referralsp != NULL) { 2188 releaseCon(*lc, stat); 2189 if (msg != NULL) 2190 (void) ldap_msgfree(msg); 2191 msg = NULL; 2192 *lc = findReferralCon(referralsp, &stat); 2193 ldap_value_free(referralsp); 2194 referralsp = NULL; 2195 if (*lc == NULL) 2196 goto cleanup; 2197 msgid = ldap_modify((*lc)->ld, dn, mods); 2198 if (msgid == -1) { 2199 (void) ldap_get_option((*lc)->ld, 2200 LDAP_OPT_ERROR_NUMBER, &stat); 2201 goto cleanup; 2202 } 2203 stat = ldap_result((*lc)->ld, msgid, 0, &tv, &msg); 2204 if (stat == 0) { 2205 stat = LDAP_TIMEOUT; 2206 } else if (stat == -1) { 2207 (void) ldap_get_option((*lc)->ld, 2208 LDAP_OPT_ERROR_NUMBER, &stat); 2209 } else { 2210 stat = ldap_parse_result((*lc)->ld, msg, &lderr, 2211 NULL, NULL, NULL, NULL, 0); 2212 if (stat == LDAP_SUCCESS) 2213 stat = lderr; 2214 } 2215 } 2216 cleanup: 2217 if (mods != 0) 2218 freeLdapMod(mods); 2219 freeRuleValue(rv, 1); 2220 return (stat); 2221 } 2222 2223 /* 2224 * Modify the specified 'dn' per the attribute names/values in 'rv'. 2225 * If 'rv' is NULL, we attempt to delete the entire entry. 2226 * 2227 * The 'objClassAttrs' parameter is needed if the entry must be added 2228 * (i.e., created), or a modify fails with an object class violation. 2229 * 2230 * If 'addFirst' is set, we try an add before a modify; modify before 2231 * add otherwise (ignored if we're deleting). 2232 */ 2233 int 2234 ldapModify(char *dn, __nis_rule_value_t *rv, char *objClassAttrs, 2235 int addFirst) { 2236 int stat, add = 0; 2237 LDAPMod **mods = 0; 2238 __nis_ldap_conn_t *lc; 2239 struct timeval tv; 2240 LDAPMessage *msg = 0; 2241 char *myself = "ldapModify"; 2242 int msgid; 2243 int lderr; 2244 char **referralsp = NULL; 2245 bool_t delete = FALSE; 2246 2247 if (dn == 0) 2248 return (LDAP_PARAM_ERROR); 2249 2250 if ((lc = findCon(&stat)) == 0) 2251 return (stat); 2252 2253 if (rv == 0) { 2254 delete = TRUE; 2255 /* Simple case: if rv == 0, try to delete the entire entry */ 2256 msgid = ldap_delete(lc->ld, dn); 2257 if (msgid == -1) { 2258 (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, 2259 &stat); 2260 goto cleanup; 2261 } 2262 tv = lc->deleteTimeout; 2263 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg); 2264 2265 if (stat == 0) { 2266 stat = LDAP_TIMEOUT; 2267 } else if (stat == -1) { 2268 (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, 2269 &stat); 2270 } else { 2271 stat = ldap_parse_result(lc->ld, msg, &lderr, NULL, 2272 NULL, &referralsp, NULL, 0); 2273 if (stat == LDAP_SUCCESS) 2274 stat = lderr; 2275 } 2276 if (proxyInfo.follow_referral == follow && 2277 stat == LDAP_REFERRAL && referralsp != NULL) { 2278 releaseCon(lc, stat); 2279 if (msg != NULL) 2280 (void) ldap_msgfree(msg); 2281 msg = NULL; 2282 lc = findReferralCon(referralsp, &stat); 2283 ldap_value_free(referralsp); 2284 if (lc == NULL) 2285 goto cleanup; 2286 msgid = ldap_delete(lc->ld, dn); 2287 if (msgid == -1) { 2288 (void) ldap_get_option(lc->ld, 2289 LDAP_OPT_ERROR_NUMBER, &stat); 2290 goto cleanup; 2291 } 2292 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg); 2293 if (stat == 0) { 2294 stat = LDAP_TIMEOUT; 2295 } else if (stat == -1) { 2296 (void) ldap_get_option(lc->ld, 2297 LDAP_OPT_ERROR_NUMBER, &stat); 2298 } else { 2299 stat = ldap_parse_result(lc->ld, msg, &lderr, 2300 NULL, NULL, NULL, NULL, 0); 2301 if (stat == LDAP_SUCCESS) 2302 stat = lderr; 2303 } 2304 } 2305 /* No such object means someone else has done our job */ 2306 if (stat == LDAP_NO_SUCH_OBJECT) 2307 stat = LDAP_SUCCESS; 2308 } else { 2309 if (addFirst) { 2310 stat = ldapAdd(dn, rv, objClassAttrs, lc); 2311 lc = NULL; 2312 if (stat != LDAP_ALREADY_EXISTS) 2313 goto cleanup; 2314 if ((lc = findCon(&stat)) == 0) 2315 return (stat); 2316 } 2317 2318 /* 2319 * First try the modify without specifying object classes 2320 * (i.e., assume they're already present). 2321 */ 2322 mods = search2LdapMod(rv, 0, 0); 2323 if (mods == 0) { 2324 stat = LDAP_PARAM_ERROR; 2325 goto cleanup; 2326 } 2327 2328 msgid = ldap_modify(lc->ld, dn, mods); 2329 if (msgid == -1) { 2330 (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, 2331 &stat); 2332 goto cleanup; 2333 } 2334 tv = lc->modifyTimeout; 2335 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg); 2336 if (stat == 0) { 2337 stat = LDAP_TIMEOUT; 2338 } else if (stat == -1) { 2339 (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, 2340 &stat); 2341 } else { 2342 stat = ldap_parse_result(lc->ld, msg, &lderr, NULL, 2343 NULL, &referralsp, NULL, 0); 2344 if (stat == LDAP_SUCCESS) 2345 stat = lderr; 2346 } 2347 if (proxyInfo.follow_referral == follow && 2348 stat == LDAP_REFERRAL && referralsp != NULL) { 2349 releaseCon(lc, stat); 2350 if (msg != NULL) 2351 (void) ldap_msgfree(msg); 2352 msg = NULL; 2353 lc = findReferralCon(referralsp, &stat); 2354 ldap_value_free(referralsp); 2355 referralsp = NULL; 2356 if (lc == NULL) 2357 goto cleanup; 2358 msgid = ldap_modify(lc->ld, dn, mods); 2359 if (msgid == -1) { 2360 (void) ldap_get_option(lc->ld, 2361 LDAP_OPT_ERROR_NUMBER, &stat); 2362 goto cleanup; 2363 } 2364 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg); 2365 if (stat == 0) { 2366 stat = LDAP_TIMEOUT; 2367 } else if (stat == -1) { 2368 (void) ldap_get_option(lc->ld, 2369 LDAP_OPT_ERROR_NUMBER, &stat); 2370 } else { 2371 stat = ldap_parse_result(lc->ld, msg, &lderr, 2372 NULL, NULL, NULL, NULL, 0); 2373 if (stat == LDAP_SUCCESS) 2374 stat = lderr; 2375 } 2376 } 2377 2378 /* 2379 * If the modify failed with an object class violation, 2380 * the most probable reason is that at least on of the 2381 * attributes we're modifying didn't exist before, and 2382 * neither did its object class. So, try the modify again, 2383 * but add the object classes this time. 2384 */ 2385 if (stat == LDAP_OBJECT_CLASS_VIOLATION && 2386 objClassAttrs != 0) { 2387 freeLdapMod(mods); 2388 mods = 0; 2389 stat = ldapModifyObjectClass(&lc, dn, rv, 2390 objClassAttrs); 2391 } 2392 2393 if (stat == LDAP_NO_SUCH_ATTRIBUTE) { 2394 /* 2395 * If there was at least one attribute delete, then 2396 * the cause of this error could be that said attribute 2397 * didn't exist in LDAP. So, do things the slow way, 2398 * and try to delete one attribute at a time. 2399 */ 2400 int d, numDelete, st; 2401 __nis_rule_value_t *rvt; 2402 2403 for (d = 0, numDelete = 0; d < rv->numAttrs; d++) { 2404 if (rv->attrVal[d].numVals < 0) 2405 numDelete++; 2406 } 2407 2408 /* If there's just one, we've already tried */ 2409 if (numDelete <= 1) 2410 goto cleanup; 2411 2412 /* Make a copy of the rule value */ 2413 rvt = initRuleValue(1, rv); 2414 if (rvt == 0) 2415 goto cleanup; 2416 2417 /* 2418 * Remove all delete attributes from the tmp 2419 * rule value. 2420 */ 2421 for (d = 0; d < rv->numAttrs; d++) { 2422 if (rv->attrVal[d].numVals < 0) { 2423 delAttrFromRuleValue(rvt, 2424 rv->attrName[d]); 2425 } 2426 } 2427 2428 /* 2429 * Now put the attributes back in one by one, and 2430 * invoke ourselves. 2431 */ 2432 for (d = 0; d < rv->numAttrs; d++) { 2433 if (rv->attrVal[d].numVals >= 0) 2434 continue; 2435 st = addAttr2RuleValue(rv->attrVal[d].type, 2436 rv->attrName[d], 0, 0, rvt); 2437 if (st != 0) { 2438 logmsg(MSG_NOMEM, LOG_ERR, 2439 "%s: Error deleting \"%s\" for \"%s\"", 2440 NIL(rv->attrName[d]), NIL(dn)); 2441 stat = LDAP_NO_MEMORY; 2442 freeRuleValue(rvt, 1); 2443 goto cleanup; 2444 } 2445 stat = ldapModify(dn, rvt, objClassAttrs, 0); 2446 if (stat != LDAP_SUCCESS && 2447 stat != LDAP_NO_SUCH_ATTRIBUTE) { 2448 freeRuleValue(rvt, 1); 2449 goto cleanup; 2450 } 2451 delAttrFromRuleValue(rvt, rv->attrName[d]); 2452 } 2453 2454 /* 2455 * If we got here, then all attributes that should 2456 * be deleted either have been, or didn't exist. For 2457 * our purposes, the latter is as good as the former. 2458 */ 2459 stat = LDAP_SUCCESS; 2460 freeRuleValue(rvt, 1); 2461 } 2462 2463 if (stat == LDAP_NO_SUCH_OBJECT && !addFirst) { 2464 /* 2465 * Entry doesn't exist, so try an ldap_add(). If the 2466 * ldap_add() also fails, that could be because someone 2467 * else added it between our modify and add operations. 2468 * If so, we consider that foreign add to be 2469 * authoritative (meaning we don't retry our modify). 2470 * 2471 * Also, if all modify operations specified by 'mods' 2472 * are deletes, LDAP_NO_SUCH_OBJECT is a kind of 2473 * success; we certainly don't want to create the 2474 * entry. 2475 */ 2476 int allDelete; 2477 LDAPMod **m; 2478 2479 for (m = mods, allDelete = 1; *m != 0 && allDelete; 2480 m++) { 2481 if (((*m)->mod_op & LDAP_MOD_DELETE) == 0) 2482 allDelete = 0; 2483 } 2484 2485 add = 1; 2486 2487 if (allDelete) { 2488 stat = LDAP_SUCCESS; 2489 } else if (objClassAttrs == 0) { 2490 /* Now we need it, so this is fatal */ 2491 stat = LDAP_PARAM_ERROR; 2492 } else { 2493 stat = ldapAdd(dn, rv, objClassAttrs, lc); 2494 lc = NULL; 2495 } 2496 } 2497 } 2498 2499 cleanup: 2500 if (stat != LDAP_SUCCESS) { 2501 logmsg(MSG_NOTIMECHECK, LOG_INFO, 2502 "%s(0x%x (%s), \"%s\") => %d (%s)\n", 2503 !delete ? (add ? "ldap_add" : "ldap_modify") : 2504 "ldap_delete", 2505 lc != NULL ? lc->ld : 0, 2506 lc != NULL ? NIL(lc->sp) : "nil", 2507 dn, stat, ldap_err2string(stat)); 2508 } 2509 2510 releaseCon(lc, stat); 2511 freeLdapMod(mods); 2512 if (msg != 0) 2513 (void) ldap_msgfree(msg); 2514 2515 return (stat); 2516 } 2517 2518 /* 2519 * Create the entry specified by 'dn' to have the values per 'rv'. 2520 * The 'objClassAttrs' are the extra object classes we need when 2521 * creating an entry. 2522 * 2523 * If 'lc' is non-NULL, we use that connection; otherwise, we find 2524 * our own. CAUTION: This connection will be released on return. Regardless 2525 * of return value, this connection should not subsequently used by the 2526 * caller. 2527 * 2528 * Returns an LDAP status. 2529 */ 2530 int 2531 ldapAdd(char *dn, __nis_rule_value_t *rv, char *objClassAttrs, void *lcv) { 2532 int stat; 2533 LDAPMod **mods = 0; 2534 struct timeval tv; 2535 LDAPMessage *msg = 0; 2536 __nis_ldap_conn_t *lc = lcv; 2537 int msgid; 2538 int lderr; 2539 char **referralsp = NULL; 2540 2541 if (dn == 0 || rv == 0 || objClassAttrs == 0) { 2542 releaseCon(lc, LDAP_SUCCESS); 2543 return (LDAP_PARAM_ERROR); 2544 } 2545 2546 if (lc == 0) { 2547 if ((lc = findCon(&stat)) == 0) 2548 return (stat); 2549 } 2550 2551 rv = addObjectClasses(rv, objClassAttrs); 2552 if (rv == 0) { 2553 stat = LDAP_OPERATIONS_ERROR; 2554 goto cleanup; 2555 } 2556 2557 mods = search2LdapMod(rv, 1, 0); 2558 if (mods == 0) { 2559 stat = LDAP_OPERATIONS_ERROR; 2560 goto cleanup; 2561 } 2562 2563 msgid = ldap_add(lc->ld, dn, mods); 2564 if (msgid == -1) { 2565 (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, &stat); 2566 goto cleanup; 2567 } 2568 tv = lc->addTimeout; 2569 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg); 2570 if (stat == 0) { 2571 stat = LDAP_TIMEOUT; 2572 } else if (stat == -1) { 2573 (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, &stat); 2574 } else { 2575 stat = ldap_parse_result(lc->ld, msg, &lderr, NULL, NULL, 2576 &referralsp, NULL, 0); 2577 if (stat == LDAP_SUCCESS) 2578 stat = lderr; 2579 } 2580 if (proxyInfo.follow_referral == follow && stat == LDAP_REFERRAL && 2581 referralsp != NULL) { 2582 releaseCon(lc, stat); 2583 if (msg != NULL) 2584 (void) ldap_msgfree(msg); 2585 msg = NULL; 2586 lc = findReferralCon(referralsp, &stat); 2587 ldap_value_free(referralsp); 2588 if (lc == NULL) 2589 goto cleanup; 2590 msgid = ldap_add(lc->ld, dn, mods); 2591 if (msgid == -1) { 2592 (void) ldap_get_option(lc->ld, 2593 LDAP_OPT_ERROR_NUMBER, &stat); 2594 goto cleanup; 2595 } 2596 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg); 2597 if (stat == 0) { 2598 stat = LDAP_TIMEOUT; 2599 } else if (stat == -1) { 2600 (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, 2601 &stat); 2602 } else { 2603 stat = ldap_parse_result(lc->ld, msg, &lderr, NULL, 2604 NULL, NULL, NULL, 0); 2605 if (stat == LDAP_SUCCESS) 2606 stat = lderr; 2607 } 2608 } 2609 2610 cleanup: 2611 if (stat != LDAP_SUCCESS) { 2612 logmsg(MSG_NOTIMECHECK, LOG_INFO, 2613 "ldap_add(0x%x (%s), \"%s\") => %d (%s)\n", 2614 lc != NULL ? lc->ld : 0, 2615 lc != NULL ? NIL(lc->sp) : "nil", 2616 dn, stat, ldap_err2string(stat)); 2617 } 2618 2619 releaseCon(lc, stat); 2620 freeLdapMod(mods); 2621 if (msg != 0) 2622 (void) ldap_msgfree(msg); 2623 2624 return (stat); 2625 } 2626 2627 /* 2628 * Change the entry at 'oldDn' to have the new DN (not RDN) 'dn'. 2629 * Returns an LDAP error status. 2630 */ 2631 int 2632 ldapChangeDN(char *oldDn, char *dn) { 2633 int stat; 2634 __nis_ldap_conn_t *lc; 2635 int i, j, lo, ln; 2636 char *rdn; 2637 int msgid; 2638 int lderr; 2639 struct timeval tv; 2640 LDAPMessage *msg = 0; 2641 char **referralsp = NULL; 2642 char *myself = "ldapChangeDN"; 2643 2644 if ((lo = slen(oldDn)) <= 0 || (ln = slen(dn)) <= 0) 2645 return (LDAP_PARAM_ERROR); 2646 2647 if (strcasecmp(oldDn, dn) == 0) 2648 return (LDAP_SUCCESS); 2649 2650 if ((lc = findCon(&stat)) == 0) 2651 return (stat); 2652 2653 rdn = sdup(myself, T, dn); 2654 if (rdn == 0) { 2655 releaseCon(lc, LDAP_SUCCESS); 2656 return (LDAP_NO_MEMORY); 2657 } 2658 2659 /* Compare old and new DN from the end */ 2660 for (i = lo-1, j = ln-1; i >= 0 && j >= 0; i--, j--) { 2661 if (tolower(oldDn[i]) != tolower(rdn[j])) { 2662 /* 2663 * Terminate 'rdn' after this character in order 2664 * to snip off the portion of the new DN that is 2665 * the same as the old DN. What remains in 'rdn' 2666 * is the relative DN. 2667 */ 2668 rdn[j+1] = '\0'; 2669 break; 2670 } 2671 } 2672 2673 stat = ldap_rename(lc->ld, oldDn, rdn, NULL, 1, NULL, NULL, &msgid); 2674 2675 if (msgid != -1) { 2676 tv = lc->modifyTimeout; 2677 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg); 2678 if (stat == 0) { 2679 stat = LDAP_TIMEOUT; 2680 } else if (stat == -1) { 2681 (void) ldap_get_option(lc->ld, 2682 LDAP_OPT_ERROR_NUMBER, &stat); 2683 } else { 2684 stat = ldap_parse_result(lc->ld, msg, &lderr, NULL, 2685 NULL, &referralsp, NULL, 0); 2686 if (stat == LDAP_SUCCESS) 2687 stat = lderr; 2688 stat = ldap_result2error(lc->ld, msg, 0); 2689 } 2690 } else { 2691 (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, 2692 &stat); 2693 } 2694 if (proxyInfo.follow_referral == follow && 2695 stat == LDAP_REFERRAL && referralsp != NULL) { 2696 releaseCon(lc, stat); 2697 if (msg != NULL) 2698 (void) ldap_msgfree(msg); 2699 msg = NULL; 2700 lc = findReferralCon(referralsp, &stat); 2701 ldap_value_free(referralsp); 2702 referralsp = NULL; 2703 if (lc == NULL) 2704 goto cleanup; 2705 msgid = ldap_rename(lc->ld, oldDn, rdn, NULL, 1, NULL, NULL, 2706 &msgid); 2707 if (msgid == -1) { 2708 (void) ldap_get_option(lc->ld, 2709 LDAP_OPT_ERROR_NUMBER, &stat); 2710 goto cleanup; 2711 } 2712 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg); 2713 if (stat == 0) { 2714 stat = LDAP_TIMEOUT; 2715 } else if (stat == -1) { 2716 (void) ldap_get_option(lc->ld, 2717 LDAP_OPT_ERROR_NUMBER, &stat); 2718 } else { 2719 stat = ldap_parse_result(lc->ld, msg, &lderr, 2720 NULL, NULL, NULL, NULL, 0); 2721 if (stat == LDAP_SUCCESS) 2722 stat = lderr; 2723 } 2724 } 2725 2726 cleanup: 2727 if (msg != NULL) 2728 (void) ldap_msgfree(msg); 2729 2730 #if 1 2731 fprintf(stderr, "%s: ldap_modrdn_s(0x%x, %s, %s, 1) => %s\n", 2732 myself, lc == NULL ? 0: lc->ld, NIL(oldDn), NIL(rdn), 2733 ldap_err2string(stat)); 2734 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 2735 "%s: ldap_modrdn_s(0x%x, %s, %s, 1) => %s", 2736 myself, lc == NULL ? 0: lc->ld, NIL(oldDn), NIL(rdn), 2737 ldap_err2string(stat)); 2738 #endif 2739 2740 if (stat == LDAP_NO_SUCH_OBJECT) { 2741 /* 2742 * Fine from our point of view, since all we want to do 2743 * is to make sure that an update to the new DN doesn't 2744 * leave the old entry around. 2745 */ 2746 stat = LDAP_SUCCESS; 2747 } 2748 2749 releaseCon(lc, stat); 2750 sfree(rdn); 2751 2752 return (stat); 2753 } 2754