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