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