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