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 ldap_memfree(nm); 1668 if (val != 0) 1669 ldap_value_free_len(val); 1670 } 1671 if (ber != 0) 1672 ber_free(ber, 0); 1673 } 1674 1675 if (numVals != tnv) { 1676 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 1677 "%s: Inconsistent LDAP entry count, found = %d, expected %d", 1678 myself, numVals, tnv); 1679 } 1680 1681 if (doVLV) { 1682 stat = ldap_parse_result(lc->ld, msg, &lprEc, 0, 0, 0, 1683 &retCtrls, 0); 1684 if (stat != LDAP_SUCCESS) { 1685 ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, 1686 &stat); 1687 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1688 "%s: VLV parse result error: %s", 1689 myself, ldap_err2string(stat)); 1690 *ldapStat = stat; 1691 freeRuleValue(rv, tnv); 1692 rv = 0; 1693 goto cleanup; 1694 } 1695 if (retCtrls != 0) { 1696 unsigned long targetPosP = 0; 1697 unsigned long listSize = 0; 1698 1699 stat = ldap_parse_virtuallist_control(lc->ld, 1700 retCtrls, &targetPosP, &listSize, 1701 &lprEc); 1702 if (stat == LDAP_SUCCESS) { 1703 index = targetPosP + lc->batchFrom; 1704 if (index >= listSize) 1705 done = 1; 1706 } 1707 ldap_controls_free(retCtrls); 1708 retCtrls = 0; 1709 } else { 1710 done = 1; 1711 } 1712 } else if (doSP) { 1713 stat = ldap_parse_result(lc->ld, msg, &lprEc, 0, 0, 0, 1714 &retCtrls, 0); 1715 if (stat != LDAP_SUCCESS) { 1716 ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, 1717 &stat); 1718 logmsg(MSG_NOTIMECHECK, LOG_ERR, 1719 "%s: Simple page parse result error: %s", 1720 myself, ldap_err2string(stat)); 1721 *ldapStat = stat; 1722 freeRuleValue(rv, tnv); 1723 rv = 0; 1724 goto cleanup; 1725 } 1726 if (retCtrls != 0) { 1727 unsigned int count; 1728 1729 if (spCookie != 0) { 1730 ber_bvfree(spCookie); 1731 spCookie = 0; 1732 } 1733 stat = ldap_parse_page_control(lc->ld, 1734 retCtrls, &count, &spCookie); 1735 if (stat == LDAP_SUCCESS) { 1736 if (spCookie == 0 || 1737 spCookie->bv_val == 0 || 1738 spCookie->bv_len == 0) 1739 done = 1; 1740 } 1741 ldap_controls_free(retCtrls); 1742 retCtrls = 0; 1743 } else { 1744 done = 1; 1745 } 1746 } else { 1747 done = 1; 1748 } 1749 1750 (void) ldap_msgfree(msg); 1751 msg = 0; 1752 1753 /* 1754 * If we're using VLV or SP, the timeout should apply 1755 * to all calls as an aggregate, so we need to reduce 1756 * 'tv' with the time spent on this chunk of data. 1757 */ 1758 if (!done) { 1759 struct timeval tmp; 1760 1761 (void) gettimeofday(&now, 0); 1762 tmp = now; 1763 now.tv_sec -= start.tv_sec; 1764 now.tv_usec -= start.tv_usec; 1765 if (now.tv_usec < 0) { 1766 now.tv_usec += 1000000; 1767 now.tv_sec -= 1; 1768 } 1769 tv.tv_sec -= now.tv_sec; 1770 tv.tv_usec -= now.tv_usec; 1771 if (tv.tv_usec < 0) { 1772 tv.tv_usec += 1000000; 1773 tv.tv_sec -= 1; 1774 } 1775 if (tv.tv_sec < 0) { 1776 *ldapStat = LDAP_TIMEOUT; 1777 freeRuleValue(rv, tnv); 1778 rv = 0; 1779 goto cleanup; 1780 } 1781 start = tmp; 1782 } 1783 1784 } while (!done); 1785 1786 if (numValues != 0) 1787 *numValues = numVals; 1788 1789 cleanup: 1790 if (NULL != lc) { 1791 if (yp2ldap && ls->useCon == 0) { 1792 /* Disconnect and free the connection */ 1793 lc->doDis++; 1794 lc->doDel++; 1795 releaseCon(lc, stat); 1796 releaseLC(lc); 1797 1798 } else { 1799 releaseCon(lc, stat); 1800 } 1801 } 1802 if (msg != 0) 1803 (void) ldap_msgfree(msg); 1804 if (ctrls[0] != 0) 1805 ldap_control_free(ctrls[0]); 1806 if (ctrls[1] != 0) 1807 ldap_control_free(ctrls[1]); 1808 if (spCookie != 0) 1809 ber_bvfree(spCookie); 1810 if (sortKeyList != 0) 1811 ldap_free_sort_keylist(sortKeyList); 1812 1813 return (rv); 1814 } 1815 1816 static void 1817 freeLdapModEntry(LDAPMod *m) { 1818 1819 if (m == 0) 1820 return; 1821 1822 sfree(m->mod_type); 1823 if ((m->mod_op & LDAP_MOD_BVALUES) == 0) { 1824 char **v = m->mod_values; 1825 1826 if (v != 0) { 1827 while (*v != 0) { 1828 sfree(*v); 1829 v++; 1830 } 1831 free(m->mod_values); 1832 } 1833 } else { 1834 struct berval **b = m->mod_bvalues; 1835 1836 if (b != 0) { 1837 while (*b != 0) { 1838 sfree((*b)->bv_val); 1839 free(*b); 1840 b++; 1841 } 1842 free(m->mod_bvalues); 1843 } 1844 } 1845 1846 free(m); 1847 } 1848 1849 static void 1850 freeLdapMod(LDAPMod **mods) { 1851 LDAPMod *m, **org = mods; 1852 1853 if (mods == 0) 1854 return; 1855 1856 while ((m = *mods) != 0) { 1857 freeLdapModEntry(m); 1858 mods++; 1859 } 1860 1861 free(org); 1862 } 1863 1864 /* 1865 * Convert a rule-value structure to the corresponding LDAPMod. 1866 * If 'add' is set, attributes/values are added; object classes 1867 * are also added. If 'add' is cleared, attributes/values are modified, 1868 * and 'oc' controls whether or not object classes are added. 1869 */ 1870 LDAPMod ** 1871 search2LdapMod(__nis_rule_value_t *rv, int add, int oc) { 1872 LDAPMod **mods; 1873 int i, j, nm; 1874 char *myself = "search2LdapMod"; 1875 1876 if (rv == 0 || rv->numAttrs <= 0) 1877 return (0); 1878 1879 mods = am(myself, (rv->numAttrs + 1) * sizeof (mods[0])); 1880 if (mods == 0) 1881 return (0); 1882 1883 for (i = 0, nm = 0; i < rv->numAttrs; i++) { 1884 int isOc; 1885 /* 1886 * If we're creating an LDAPMod array for an add operation, 1887 * just skip attributes that should be deleted. 1888 */ 1889 if (add && rv->attrVal[i].numVals < 0) 1890 continue; 1891 1892 /* 1893 * Skip DN; it's specified separately to ldap_modify() 1894 * and ldap_add(), and mustn't appear among the 1895 * attributes to be modified/added. 1896 */ 1897 if (strcasecmp("dn", rv->attrName[i]) == 0) 1898 continue; 1899 1900 /* 1901 * If modifying, and 'oc' is off, skip object class 1902 * attributes. 1903 */ 1904 isOc = (strcasecmp("objectclass", rv->attrName[i]) == 0); 1905 if (!add && !oc && isOc) 1906 continue; 1907 1908 mods[nm] = am(myself, sizeof (*mods[nm])); 1909 if (mods[nm] == 0) { 1910 freeLdapMod(mods); 1911 return (0); 1912 } 1913 1914 /* 'mod_type' is the attribute name */ 1915 mods[nm]->mod_type = sdup(myself, T, rv->attrName[i]); 1916 if (mods[nm]->mod_type == 0) { 1917 freeLdapMod(mods); 1918 return (0); 1919 } 1920 1921 /* 1922 * numVals < 0 means attribute and all values should 1923 * be deleted. 1924 */ 1925 if (rv->attrVal[i].numVals < 0) { 1926 mods[nm]->mod_op = LDAP_MOD_DELETE; 1927 mods[nm]->mod_values = 0; 1928 nm++; 1929 continue; 1930 } 1931 1932 /* objectClass attributes always added */ 1933 mods[nm]->mod_op = (add) ? 0 : ((isOc) ? 0 : LDAP_MOD_REPLACE); 1934 1935 if (rv->attrVal[i].type == vt_string) { 1936 /* 1937 * mods[]->mod_values is a NULL-terminated array 1938 * of (char *)'s. 1939 */ 1940 mods[nm]->mod_values = am(myself, 1941 (rv->attrVal[i].numVals + 1) * 1942 sizeof (mods[nm]->mod_values[0])); 1943 if (mods[nm]->mod_values == 0) { 1944 freeLdapMod(mods); 1945 return (0); 1946 } 1947 for (j = 0; j < rv->attrVal[i].numVals; j++) { 1948 /* 1949 * Just in case the string isn't NUL 1950 * terminated, add one byte to the 1951 * allocated length; am() will initialize 1952 * the buffer to zero. 1953 */ 1954 mods[nm]->mod_values[j] = am(myself, 1955 rv->attrVal[i].val[j].length + 1); 1956 if (mods[nm]->mod_values[j] == 0) { 1957 freeLdapMod(mods); 1958 return (0); 1959 } 1960 memcpy(mods[nm]->mod_values[j], 1961 rv->attrVal[i].val[j].value, 1962 rv->attrVal[i].val[j].length); 1963 } 1964 } else { 1965 mods[nm]->mod_op |= LDAP_MOD_BVALUES; 1966 mods[nm]->mod_bvalues = am(myself, 1967 (rv->attrVal[i].numVals+1) * 1968 sizeof (mods[nm]->mod_bvalues[0])); 1969 if (mods[nm]->mod_bvalues == 0) { 1970 freeLdapMod(mods); 1971 return (0); 1972 } 1973 for (j = 0; j < rv->attrVal[i].numVals; j++) { 1974 mods[nm]->mod_bvalues[j] = am(myself, 1975 sizeof (*mods[nm]->mod_bvalues[j])); 1976 if (mods[nm]->mod_bvalues[j] == 0) { 1977 freeLdapMod(mods); 1978 return (0); 1979 } 1980 mods[nm]->mod_bvalues[j]->bv_val = am(myself, 1981 rv->attrVal[i].val[j].length); 1982 if (mods[nm]->mod_bvalues[j]->bv_val == 0) { 1983 freeLdapMod(mods); 1984 return (0); 1985 } 1986 mods[nm]->mod_bvalues[j]->bv_len = 1987 rv->attrVal[i].val[j].length; 1988 memcpy(mods[nm]->mod_bvalues[j]->bv_val, 1989 rv->attrVal[i].val[j].value, 1990 mods[nm]->mod_bvalues[j]->bv_len); 1991 } 1992 } 1993 nm++; 1994 } 1995 1996 return (mods); 1997 } 1998 1999 /* 2000 * Remove 'value' from 'val'. If value==0, remove the entire 2001 * __nis_single_value_t array from 'val'. 2002 */ 2003 static void 2004 removeSingleValue(__nis_value_t *val, void *value, int length) { 2005 int i; 2006 2007 if (val == 0) 2008 return; 2009 2010 if (value == 0) { 2011 for (i = 0; i < val->numVals; i++) { 2012 sfree(val->val[i].value); 2013 } 2014 sfree(val->val); 2015 val->val = 0; 2016 val->numVals = 0; 2017 return; 2018 } 2019 2020 for (i = 0; i < val->numVals; i++) { 2021 if (val->val[i].value == 0 || (val->val[i].length != length)) 2022 continue; 2023 if (memcmp(val->val[i].value, value, length) != 0) 2024 continue; 2025 sfree(val->val[i].value); 2026 if (i != (val->numVals - 1)) { 2027 (void) memmove(&val->val[i], &val->val[i+1], 2028 (val->numVals - 1 - i) * sizeof (val->val[0])); 2029 } 2030 val->numVals -= 1; 2031 break; 2032 } 2033 } 2034 2035 /* 2036 * Helper function for LdapModify 2037 * When a modify operation fails with an object class violation, 2038 * the most probable reason is that the attributes we're modifying are new, 2039 * and the needed object class are not present. So, try the modify again, 2040 * but add the object classes this time. 2041 */ 2042 2043 static int 2044 ldapModifyObjectClass(__nis_ldap_conn_t **lc, char *dn, 2045 __nis_rule_value_t *rvIn, char *objClassAttrs) 2046 { 2047 LDAPMod **mods = 0; 2048 int msgid; 2049 int lderr; 2050 struct timeval tv; 2051 int stat; 2052 LDAPMessage *msg = 0; 2053 char **referralsp = NULL; 2054 __nis_rule_value_t *rv, *rvldap; 2055 __nis_ldap_search_t *ls; 2056 int i, ocrv, ocrvldap, nv; 2057 char *oc[2] = { "objectClass", 0}; 2058 char *myself = "ldapModifyObjectClass"; 2059 2060 rv = initRuleValue(1, rvIn); 2061 if (rv == 0) 2062 return (LDAP_NO_MEMORY); 2063 2064 delAttrFromRuleValue(rv, "objectClass"); 2065 rv = addObjectClasses(rv, objClassAttrs); 2066 if (rv == 0) { 2067 stat = LDAP_OPERATIONS_ERROR; 2068 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 2069 "%s: addObjectClasses failed for %s", 2070 myself, NIL(dn)); 2071 goto cleanup; 2072 } 2073 2074 /* 2075 * Before adding the object classes whole-sale, try retrieving 2076 * the entry specified by the 'dn'. If it exists, we filter out 2077 * those object classes that already are present in LDAP from our 2078 * update. 2079 */ 2080 ls = buildLdapSearch(dn, LDAP_SCOPE_BASE, 0, 0, "objectClass=*", 2081 oc, 0, 1); 2082 if (ls == 0) { 2083 logmsg(MSG_NOTIMECHECK, LOG_INFO, 2084 "%s: Unable to build DN search for \"%s\"", 2085 myself, NIL(dn)); 2086 /* Fall through to try just adding the object classes */ 2087 goto addObjectClasses; 2088 } 2089 2090 nv = 0; 2091 rvldap = ldapSearch(ls, &nv, 0, &lderr); 2092 freeLdapSearch(ls); 2093 if (rvldap == 0) { 2094 logmsg(MSG_NOTIMECHECK, LOG_INFO, 2095 "%s: No data for DN search (\"%s\"); LDAP status %d", 2096 myself, NIL(dn), lderr); 2097 /* Fall through to try just adding the object classes */ 2098 goto addObjectClasses; 2099 } 2100 2101 /* 2102 * Find the indices of the 'objectClass' attribute 2103 * in 'rvldap' and 'rv'. 2104 */ 2105 for (i = 0, ocrvldap = -1; i < rvldap->numAttrs; i++) { 2106 if (rvldap->attrName[i] != 0 && 2107 strcasecmp("objectClass", rvldap->attrName[i]) == 0) { 2108 ocrvldap = i; 2109 break; 2110 } 2111 } 2112 for (i = 0, ocrv = -1; i < rv->numAttrs; i++) { 2113 if (rv->attrName[i] != 0 && 2114 strcasecmp("objectClass", rv->attrName[i]) == 0) { 2115 ocrv = i; 2116 break; 2117 } 2118 } 2119 2120 /* 2121 * Remove those object classes that already exist 2122 * in LDAP (i.e., in 'rvldap') from 'rv'. 2123 */ 2124 if (ocrv >= 0 && ocrvldap >= 0) { 2125 for (i = 0; i < rvldap->attrVal[ocrvldap].numVals; i++) { 2126 removeSingleValue(&rv->attrVal[ocrv], 2127 rvldap->attrVal[ocrvldap].val[i].value, 2128 rvldap->attrVal[ocrvldap].val[i].length); 2129 } 2130 /* 2131 * If no 'objectClass' values left in 'rv', delete 2132 * 'objectClass' from 'rv'. 2133 */ 2134 if (rv->attrVal[ocrv].numVals == 0) 2135 delAttrFromRuleValue(rv, "objectClass"); 2136 } 2137 2138 /* 2139 * 'rv' now contains the update we want to make, with just the 2140 * object class(es) that need to be added. Fall through to the 2141 * actual LDAP modify operation. 2142 */ 2143 freeRuleValue(rvldap, 1); 2144 2145 addObjectClasses: 2146 2147 mods = search2LdapMod(rv, 0, 1); 2148 if (mods == 0) { 2149 stat = LDAP_OPERATIONS_ERROR; 2150 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 2151 "%s: Unable to create LDAP modify changes with object classes for %s", 2152 myself, NIL(dn)); 2153 goto cleanup; 2154 } 2155 msgid = ldap_modify((*lc)->ld, dn, mods); 2156 if (msgid != -1) { 2157 tv = (*lc)->modifyTimeout; 2158 stat = ldap_result((*lc)->ld, msgid, 0, &tv, &msg); 2159 if (stat == 0) { 2160 stat = LDAP_TIMEOUT; 2161 } else if (stat == -1) { 2162 (void) ldap_get_option((*lc)->ld, 2163 LDAP_OPT_ERROR_NUMBER, &stat); 2164 } else { 2165 stat = ldap_parse_result((*lc)->ld, msg, &lderr, NULL, 2166 NULL, &referralsp, NULL, 0); 2167 if (stat == LDAP_SUCCESS) 2168 stat = lderr; 2169 stat = ldap_result2error((*lc)->ld, msg, 0); 2170 } 2171 } else { 2172 (void) ldap_get_option((*lc)->ld, LDAP_OPT_ERROR_NUMBER, 2173 &stat); 2174 } 2175 if (proxyInfo.follow_referral == follow && 2176 stat == LDAP_REFERRAL && referralsp != NULL) { 2177 releaseCon(*lc, stat); 2178 if (msg != NULL) 2179 (void) ldap_msgfree(msg); 2180 msg = NULL; 2181 *lc = findReferralCon(referralsp, &stat); 2182 ldap_value_free(referralsp); 2183 referralsp = NULL; 2184 if (*lc == NULL) 2185 goto cleanup; 2186 msgid = ldap_modify((*lc)->ld, dn, mods); 2187 if (msgid == -1) { 2188 (void) ldap_get_option((*lc)->ld, 2189 LDAP_OPT_ERROR_NUMBER, &stat); 2190 goto cleanup; 2191 } 2192 stat = ldap_result((*lc)->ld, msgid, 0, &tv, &msg); 2193 if (stat == 0) { 2194 stat = LDAP_TIMEOUT; 2195 } else if (stat == -1) { 2196 (void) ldap_get_option((*lc)->ld, 2197 LDAP_OPT_ERROR_NUMBER, &stat); 2198 } else { 2199 stat = ldap_parse_result((*lc)->ld, msg, &lderr, 2200 NULL, NULL, NULL, NULL, 0); 2201 if (stat == LDAP_SUCCESS) 2202 stat = lderr; 2203 } 2204 } 2205 cleanup: 2206 if (mods != 0) 2207 freeLdapMod(mods); 2208 freeRuleValue(rv, 1); 2209 return (stat); 2210 } 2211 2212 /* 2213 * Modify the specified 'dn' per the attribute names/values in 'rv'. 2214 * If 'rv' is NULL, we attempt to delete the entire entry. 2215 * 2216 * The 'objClassAttrs' parameter is needed if the entry must be added 2217 * (i.e., created), or a modify fails with an object class violation. 2218 * 2219 * If 'addFirst' is set, we try an add before a modify; modify before 2220 * add otherwise (ignored if we're deleting). 2221 */ 2222 int 2223 ldapModify(char *dn, __nis_rule_value_t *rv, char *objClassAttrs, 2224 int addFirst) { 2225 int stat, add = 0; 2226 LDAPMod **mods = 0; 2227 __nis_ldap_conn_t *lc; 2228 struct timeval tv; 2229 LDAPMessage *msg = 0; 2230 char *myself = "ldapModify"; 2231 int msgid; 2232 int lderr; 2233 char **referralsp = NULL; 2234 bool_t delete = FALSE; 2235 2236 if (dn == 0) 2237 return (LDAP_PARAM_ERROR); 2238 2239 if ((lc = findCon(&stat)) == 0) 2240 return (stat); 2241 2242 if (rv == 0) { 2243 delete = TRUE; 2244 /* Simple case: if rv == 0, try to delete the entire entry */ 2245 msgid = ldap_delete(lc->ld, dn); 2246 if (msgid == -1) { 2247 (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, 2248 &stat); 2249 goto cleanup; 2250 } 2251 tv = lc->deleteTimeout; 2252 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg); 2253 2254 if (stat == 0) { 2255 stat = LDAP_TIMEOUT; 2256 } else if (stat == -1) { 2257 (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, 2258 &stat); 2259 } else { 2260 stat = ldap_parse_result(lc->ld, msg, &lderr, NULL, 2261 NULL, &referralsp, NULL, 0); 2262 if (stat == LDAP_SUCCESS) 2263 stat = lderr; 2264 } 2265 if (proxyInfo.follow_referral == follow && 2266 stat == LDAP_REFERRAL && referralsp != NULL) { 2267 releaseCon(lc, stat); 2268 if (msg != NULL) 2269 (void) ldap_msgfree(msg); 2270 msg = NULL; 2271 lc = findReferralCon(referralsp, &stat); 2272 ldap_value_free(referralsp); 2273 if (lc == NULL) 2274 goto cleanup; 2275 msgid = ldap_delete(lc->ld, dn); 2276 if (msgid == -1) { 2277 (void) ldap_get_option(lc->ld, 2278 LDAP_OPT_ERROR_NUMBER, &stat); 2279 goto cleanup; 2280 } 2281 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg); 2282 if (stat == 0) { 2283 stat = LDAP_TIMEOUT; 2284 } else if (stat == -1) { 2285 (void) ldap_get_option(lc->ld, 2286 LDAP_OPT_ERROR_NUMBER, &stat); 2287 } else { 2288 stat = ldap_parse_result(lc->ld, msg, &lderr, 2289 NULL, NULL, NULL, NULL, 0); 2290 if (stat == LDAP_SUCCESS) 2291 stat = lderr; 2292 } 2293 } 2294 /* No such object means someone else has done our job */ 2295 if (stat == LDAP_NO_SUCH_OBJECT) 2296 stat = LDAP_SUCCESS; 2297 } else { 2298 if (addFirst) { 2299 stat = ldapAdd(dn, rv, objClassAttrs, lc); 2300 lc = NULL; 2301 if (stat != LDAP_ALREADY_EXISTS) 2302 goto cleanup; 2303 if ((lc = findCon(&stat)) == 0) 2304 return (stat); 2305 } 2306 2307 /* 2308 * First try the modify without specifying object classes 2309 * (i.e., assume they're already present). 2310 */ 2311 mods = search2LdapMod(rv, 0, 0); 2312 if (mods == 0) { 2313 stat = LDAP_PARAM_ERROR; 2314 goto cleanup; 2315 } 2316 2317 msgid = ldap_modify(lc->ld, dn, mods); 2318 if (msgid == -1) { 2319 (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, 2320 &stat); 2321 goto cleanup; 2322 } 2323 tv = lc->modifyTimeout; 2324 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg); 2325 if (stat == 0) { 2326 stat = LDAP_TIMEOUT; 2327 } else if (stat == -1) { 2328 (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, 2329 &stat); 2330 } else { 2331 stat = ldap_parse_result(lc->ld, msg, &lderr, NULL, 2332 NULL, &referralsp, NULL, 0); 2333 if (stat == LDAP_SUCCESS) 2334 stat = lderr; 2335 } 2336 if (proxyInfo.follow_referral == follow && 2337 stat == LDAP_REFERRAL && referralsp != NULL) { 2338 releaseCon(lc, stat); 2339 if (msg != NULL) 2340 (void) ldap_msgfree(msg); 2341 msg = NULL; 2342 lc = findReferralCon(referralsp, &stat); 2343 ldap_value_free(referralsp); 2344 referralsp = NULL; 2345 if (lc == NULL) 2346 goto cleanup; 2347 msgid = ldap_modify(lc->ld, dn, mods); 2348 if (msgid == -1) { 2349 (void) ldap_get_option(lc->ld, 2350 LDAP_OPT_ERROR_NUMBER, &stat); 2351 goto cleanup; 2352 } 2353 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg); 2354 if (stat == 0) { 2355 stat = LDAP_TIMEOUT; 2356 } else if (stat == -1) { 2357 (void) ldap_get_option(lc->ld, 2358 LDAP_OPT_ERROR_NUMBER, &stat); 2359 } else { 2360 stat = ldap_parse_result(lc->ld, msg, &lderr, 2361 NULL, NULL, NULL, NULL, 0); 2362 if (stat == LDAP_SUCCESS) 2363 stat = lderr; 2364 } 2365 } 2366 2367 /* 2368 * If the modify failed with an object class violation, 2369 * the most probable reason is that at least on of the 2370 * attributes we're modifying didn't exist before, and 2371 * neither did its object class. So, try the modify again, 2372 * but add the object classes this time. 2373 */ 2374 if (stat == LDAP_OBJECT_CLASS_VIOLATION && 2375 objClassAttrs != 0) { 2376 freeLdapMod(mods); 2377 mods = 0; 2378 stat = ldapModifyObjectClass(&lc, dn, rv, 2379 objClassAttrs); 2380 } 2381 2382 if (stat == LDAP_NO_SUCH_ATTRIBUTE) { 2383 /* 2384 * If there was at least one attribute delete, then 2385 * the cause of this error could be that said attribute 2386 * didn't exist in LDAP. So, do things the slow way, 2387 * and try to delete one attribute at a time. 2388 */ 2389 int d, numDelete, st; 2390 __nis_rule_value_t *rvt; 2391 2392 for (d = 0, numDelete = 0; d < rv->numAttrs; d++) { 2393 if (rv->attrVal[d].numVals < 0) 2394 numDelete++; 2395 } 2396 2397 /* If there's just one, we've already tried */ 2398 if (numDelete <= 1) 2399 goto cleanup; 2400 2401 /* Make a copy of the rule value */ 2402 rvt = initRuleValue(1, rv); 2403 if (rvt == 0) 2404 goto cleanup; 2405 2406 /* 2407 * Remove all delete attributes from the tmp 2408 * rule value. 2409 */ 2410 for (d = 0; d < rv->numAttrs; d++) { 2411 if (rv->attrVal[d].numVals < 0) { 2412 delAttrFromRuleValue(rvt, 2413 rv->attrName[d]); 2414 } 2415 } 2416 2417 /* 2418 * Now put the attributes back in one by one, and 2419 * invoke ourselves. 2420 */ 2421 for (d = 0; d < rv->numAttrs; d++) { 2422 if (rv->attrVal[d].numVals >= 0) 2423 continue; 2424 st = addAttr2RuleValue(rv->attrVal[d].type, 2425 rv->attrName[d], 0, 0, rvt); 2426 if (st != 0) { 2427 logmsg(MSG_NOMEM, LOG_ERR, 2428 "%s: Error deleting \"%s\" for \"%s\"", 2429 NIL(rv->attrName[d]), NIL(dn)); 2430 stat = LDAP_NO_MEMORY; 2431 freeRuleValue(rvt, 1); 2432 goto cleanup; 2433 } 2434 stat = ldapModify(dn, rvt, objClassAttrs, 0); 2435 if (stat != LDAP_SUCCESS && 2436 stat != LDAP_NO_SUCH_ATTRIBUTE) { 2437 freeRuleValue(rvt, 1); 2438 goto cleanup; 2439 } 2440 delAttrFromRuleValue(rvt, rv->attrName[d]); 2441 } 2442 2443 /* 2444 * If we got here, then all attributes that should 2445 * be deleted either have been, or didn't exist. For 2446 * our purposes, the latter is as good as the former. 2447 */ 2448 stat = LDAP_SUCCESS; 2449 freeRuleValue(rvt, 1); 2450 } 2451 2452 if (stat == LDAP_NO_SUCH_OBJECT && !addFirst) { 2453 /* 2454 * Entry doesn't exist, so try an ldap_add(). If the 2455 * ldap_add() also fails, that could be because someone 2456 * else added it between our modify and add operations. 2457 * If so, we consider that foreign add to be 2458 * authoritative (meaning we don't retry our modify). 2459 * 2460 * Also, if all modify operations specified by 'mods' 2461 * are deletes, LDAP_NO_SUCH_OBJECT is a kind of 2462 * success; we certainly don't want to create the 2463 * entry. 2464 */ 2465 int allDelete; 2466 LDAPMod **m; 2467 2468 for (m = mods, allDelete = 1; *m != 0 && allDelete; 2469 m++) { 2470 if (((*m)->mod_op & LDAP_MOD_DELETE) == 0) 2471 allDelete = 0; 2472 } 2473 2474 add = 1; 2475 2476 if (allDelete) { 2477 stat = LDAP_SUCCESS; 2478 } else if (objClassAttrs == 0) { 2479 /* Now we need it, so this is fatal */ 2480 stat = LDAP_PARAM_ERROR; 2481 } else { 2482 stat = ldapAdd(dn, rv, objClassAttrs, lc); 2483 lc = NULL; 2484 } 2485 } 2486 } 2487 2488 cleanup: 2489 if (stat != LDAP_SUCCESS) { 2490 logmsg(MSG_NOTIMECHECK, LOG_INFO, 2491 "%s(0x%x (%s), \"%s\") => %d (%s)\n", 2492 !delete ? (add ? "ldap_add" : "ldap_modify") : 2493 "ldap_delete", 2494 lc != NULL ? lc->ld : 0, 2495 lc != NULL ? NIL(lc->sp) : "nil", 2496 dn, stat, ldap_err2string(stat)); 2497 } 2498 2499 releaseCon(lc, stat); 2500 freeLdapMod(mods); 2501 if (msg != 0) 2502 (void) ldap_msgfree(msg); 2503 2504 return (stat); 2505 } 2506 2507 /* 2508 * Create the entry specified by 'dn' to have the values per 'rv'. 2509 * The 'objClassAttrs' are the extra object classes we need when 2510 * creating an entry. 2511 * 2512 * If 'lc' is non-NULL, we use that connection; otherwise, we find 2513 * our own. CAUTION: This connection will be released on return. Regardless 2514 * of return value, this connection should not subsequently used by the 2515 * caller. 2516 * 2517 * Returns an LDAP status. 2518 */ 2519 int 2520 ldapAdd(char *dn, __nis_rule_value_t *rv, char *objClassAttrs, void *lcv) { 2521 int stat; 2522 LDAPMod **mods = 0; 2523 struct timeval tv; 2524 LDAPMessage *msg = 0; 2525 __nis_ldap_conn_t *lc = lcv; 2526 int msgid; 2527 int lderr; 2528 char **referralsp = NULL; 2529 2530 if (dn == 0 || rv == 0 || objClassAttrs == 0) { 2531 releaseCon(lc, LDAP_SUCCESS); 2532 return (LDAP_PARAM_ERROR); 2533 } 2534 2535 if (lc == 0) { 2536 if ((lc = findCon(&stat)) == 0) 2537 return (stat); 2538 } 2539 2540 rv = addObjectClasses(rv, objClassAttrs); 2541 if (rv == 0) { 2542 stat = LDAP_OPERATIONS_ERROR; 2543 goto cleanup; 2544 } 2545 2546 mods = search2LdapMod(rv, 1, 0); 2547 if (mods == 0) { 2548 stat = LDAP_OPERATIONS_ERROR; 2549 goto cleanup; 2550 } 2551 2552 msgid = ldap_add(lc->ld, dn, mods); 2553 if (msgid == -1) { 2554 (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, &stat); 2555 goto cleanup; 2556 } 2557 tv = lc->addTimeout; 2558 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg); 2559 if (stat == 0) { 2560 stat = LDAP_TIMEOUT; 2561 } else if (stat == -1) { 2562 (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, &stat); 2563 } else { 2564 stat = ldap_parse_result(lc->ld, msg, &lderr, NULL, NULL, 2565 &referralsp, NULL, 0); 2566 if (stat == LDAP_SUCCESS) 2567 stat = lderr; 2568 } 2569 if (proxyInfo.follow_referral == follow && stat == LDAP_REFERRAL && 2570 referralsp != NULL) { 2571 releaseCon(lc, stat); 2572 if (msg != NULL) 2573 (void) ldap_msgfree(msg); 2574 msg = NULL; 2575 lc = findReferralCon(referralsp, &stat); 2576 ldap_value_free(referralsp); 2577 if (lc == NULL) 2578 goto cleanup; 2579 msgid = ldap_add(lc->ld, dn, mods); 2580 if (msgid == -1) { 2581 (void) ldap_get_option(lc->ld, 2582 LDAP_OPT_ERROR_NUMBER, &stat); 2583 goto cleanup; 2584 } 2585 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg); 2586 if (stat == 0) { 2587 stat = LDAP_TIMEOUT; 2588 } else if (stat == -1) { 2589 (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, 2590 &stat); 2591 } else { 2592 stat = ldap_parse_result(lc->ld, msg, &lderr, NULL, 2593 NULL, NULL, NULL, 0); 2594 if (stat == LDAP_SUCCESS) 2595 stat = lderr; 2596 } 2597 } 2598 2599 cleanup: 2600 if (stat != LDAP_SUCCESS) { 2601 logmsg(MSG_NOTIMECHECK, LOG_INFO, 2602 "ldap_add(0x%x (%s), \"%s\") => %d (%s)\n", 2603 lc != NULL ? lc->ld : 0, 2604 lc != NULL ? NIL(lc->sp) : "nil", 2605 dn, stat, ldap_err2string(stat)); 2606 } 2607 2608 releaseCon(lc, stat); 2609 freeLdapMod(mods); 2610 if (msg != 0) 2611 (void) ldap_msgfree(msg); 2612 2613 return (stat); 2614 } 2615 2616 /* 2617 * Change the entry at 'oldDn' to have the new DN (not RDN) 'dn'. 2618 * Returns an LDAP error status. 2619 */ 2620 int 2621 ldapChangeDN(char *oldDn, char *dn) { 2622 int stat; 2623 __nis_ldap_conn_t *lc; 2624 int i, j, lo, ln; 2625 char *rdn; 2626 int msgid; 2627 int lderr; 2628 struct timeval tv; 2629 LDAPMessage *msg = 0; 2630 char **referralsp = NULL; 2631 char *myself = "ldapChangeDN"; 2632 2633 if ((lo = slen(oldDn)) <= 0 || (ln = slen(dn)) <= 0) 2634 return (LDAP_PARAM_ERROR); 2635 2636 if (strcasecmp(oldDn, dn) == 0) 2637 return (LDAP_SUCCESS); 2638 2639 if ((lc = findCon(&stat)) == 0) 2640 return (stat); 2641 2642 rdn = sdup(myself, T, dn); 2643 if (rdn == 0) { 2644 releaseCon(lc, LDAP_SUCCESS); 2645 return (LDAP_NO_MEMORY); 2646 } 2647 2648 /* Compare old and new DN from the end */ 2649 for (i = lo-1, j = ln-1; i >= 0 && j >= 0; i--, j--) { 2650 if (tolower(oldDn[i]) != tolower(rdn[j])) { 2651 /* 2652 * Terminate 'rdn' after this character in order 2653 * to snip off the portion of the new DN that is 2654 * the same as the old DN. What remains in 'rdn' 2655 * is the relative DN. 2656 */ 2657 rdn[j+1] = '\0'; 2658 break; 2659 } 2660 } 2661 2662 stat = ldap_rename(lc->ld, oldDn, rdn, NULL, 1, NULL, NULL, &msgid); 2663 2664 if (msgid != -1) { 2665 tv = lc->modifyTimeout; 2666 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg); 2667 if (stat == 0) { 2668 stat = LDAP_TIMEOUT; 2669 } else if (stat == -1) { 2670 (void) ldap_get_option(lc->ld, 2671 LDAP_OPT_ERROR_NUMBER, &stat); 2672 } else { 2673 stat = ldap_parse_result(lc->ld, msg, &lderr, NULL, 2674 NULL, &referralsp, NULL, 0); 2675 if (stat == LDAP_SUCCESS) 2676 stat = lderr; 2677 stat = ldap_result2error(lc->ld, msg, 0); 2678 } 2679 } else { 2680 (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, 2681 &stat); 2682 } 2683 if (proxyInfo.follow_referral == follow && 2684 stat == LDAP_REFERRAL && referralsp != NULL) { 2685 releaseCon(lc, stat); 2686 if (msg != NULL) 2687 (void) ldap_msgfree(msg); 2688 msg = NULL; 2689 lc = findReferralCon(referralsp, &stat); 2690 ldap_value_free(referralsp); 2691 referralsp = NULL; 2692 if (lc == NULL) 2693 goto cleanup; 2694 msgid = ldap_rename(lc->ld, oldDn, rdn, NULL, 1, NULL, NULL, 2695 &msgid); 2696 if (msgid == -1) { 2697 (void) ldap_get_option(lc->ld, 2698 LDAP_OPT_ERROR_NUMBER, &stat); 2699 goto cleanup; 2700 } 2701 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg); 2702 if (stat == 0) { 2703 stat = LDAP_TIMEOUT; 2704 } else if (stat == -1) { 2705 (void) ldap_get_option(lc->ld, 2706 LDAP_OPT_ERROR_NUMBER, &stat); 2707 } else { 2708 stat = ldap_parse_result(lc->ld, msg, &lderr, 2709 NULL, NULL, NULL, NULL, 0); 2710 if (stat == LDAP_SUCCESS) 2711 stat = lderr; 2712 } 2713 } 2714 2715 cleanup: 2716 if (msg != NULL) 2717 (void) ldap_msgfree(msg); 2718 2719 #if 1 2720 fprintf(stderr, "%s: ldap_modrdn_s(0x%x, %s, %s, 1) => %s\n", 2721 myself, lc == NULL ? 0: lc->ld, NIL(oldDn), NIL(rdn), 2722 ldap_err2string(stat)); 2723 logmsg(MSG_NOTIMECHECK, LOG_WARNING, 2724 "%s: ldap_modrdn_s(0x%x, %s, %s, 1) => %s", 2725 myself, lc == NULL ? 0: lc->ld, NIL(oldDn), NIL(rdn), 2726 ldap_err2string(stat)); 2727 #endif 2728 2729 if (stat == LDAP_NO_SUCH_OBJECT) { 2730 /* 2731 * Fine from our point of view, since all we want to do 2732 * is to make sure that an update to the new DN doesn't 2733 * leave the old entry around. 2734 */ 2735 stat = LDAP_SUCCESS; 2736 } 2737 2738 releaseCon(lc, stat); 2739 sfree(rdn); 2740 2741 return (stat); 2742 } 2743