Merge branch 'cleanups'
[unleashed.git] / usr / src / lib / libnisdb / ldap_op.c
blob935dea8114c45fc0e450c5e802501d7202424874
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2015 Gary Mills
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <synch.h>
28 #include <strings.h>
29 #include <sys/time.h>
30 #include <ctype.h>
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"
40 #include "nis_parse_ldap_conf.h"
42 #ifndef LDAPS_PORT
43 #define LDAPS_PORT 636
44 #endif
46 static int setupConList(char *serverList, char *who,
47 char *cred, auth_method_t method);
51 * Build one of our internal LDAP search structures, containing copies of
52 * the supplied input. return NULL in case of error.
54 * If 'filter' is NULL, build an AND-filter using the filter components.
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";
64 ls = am(myself, sizeof (*ls));
65 if (ls == 0)
66 return (0);
68 ls->base = sdup(myself, T, base);
69 if (ls->base == 0 && base != 0)
70 err++;
71 ls->scope = scope;
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;
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++;
85 ls->numFilterComps = numFilterComps;
86 if (filter == 0) {
87 ls->filter = concatenateFilterComps(ls->numFilterComps,
88 ls->filterComp);
89 if (ls->filter == 0)
90 err++;
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++;
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++;
109 ls->attrs[na] = 0;
110 ls->numAttrs = na;
111 } else {
112 err++;
114 } else {
115 ls->attrs = 0;
116 ls->numAttrs = 0;
119 ls->attrsonly = attrsonly;
120 ls->isDN = isDN;
122 if (err > 0) {
123 freeLdapSearch(ls);
124 ls = 0;
127 return (ls);
130 void
131 freeLdapSearch(__nis_ldap_search_t *ls) {
132 int i;
134 if (ls == 0)
135 return;
137 sfree(ls->base);
138 if (ls->filterComp != 0) {
139 for (i = 0; i < ls->numFilterComps; i++) {
140 sfree(ls->filterComp[i]);
142 sfree(ls->filterComp);
144 sfree(ls->filter);
145 if (ls->attrs != 0) {
146 for (i = 0; i < ls->numAttrs; i++) {
147 sfree(ls->attrs[i]);
149 sfree(ls->attrs);
152 free(ls);
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.
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.
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.
169 * If 'fromLDAP' is set, the caller wants base/scope/filter from
170 * t->objectDN->read; otherwise, from t->objectDN->write.
172 * If 'rv' is NULL, the caller wants an enumeration of the container.
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.
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;
189 if (t == 0)
190 return (0);
192 if (obj_dn == NULL)
193 objectDN = t->objectDN;
194 else
195 objectDN = obj_dn;
197 if (rv == 0) {
198 char *base;
199 char *filter;
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);
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);
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;
227 * If none of the rules produced a value despite
228 * having enough NIS+ columns, return error.
230 if (rv->numAttrs == 0 && count > 0) {
231 *res = NP_LDAP_RULES_NO_VALUE;
232 return (0);
236 * 'rv' now contains everything we know about the attributes and
237 * values. Build an LDAP search structure from it.
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;
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);
261 } else {
262 freeDNs(locDN, numLocDN);
265 if (ls != 0) {
266 ls->useCon = 1;
267 return (ls);
271 * No DN, or caller wanted a search structure with the non-DN
272 * attributes.
275 /* Initialize search structure */
277 char *filter = (fromLDAP) ?
278 makeFilter(objectDN->read.attrs) :
279 makeFilter(objectDN->write.attrs);
280 char **ofc;
281 int nofc = 0;
283 ofc = makeFilterComp(filter, &nofc);
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);
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);
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;
313 /* Skip vt_ber values */
314 if (rv->attrVal[i].type == vt_ber)
315 continue;
317 for (j = 0; j < rv->attrVal[i].numVals; j++) {
318 __nis_buffer_t b = {0, 0};
319 char **tmpComp;
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);
333 ls->filterComp = tmpComp;
334 sfree(b.buf);
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);
351 if (dn != 0 && *dn == 0) {
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.
357 __nis_rule_value_t *rvtmp;
358 char **locDN;
359 int nv = 0, numLocDN;
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);
369 freeRuleValue(rvtmp, nv);
372 ls->useCon = 1;
373 return (ls);
376 int ldapConnAttemptRetryTimeout = 60; /* seconds */
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;
407 * List of connections, 'ldapCon', protected by an RW lock.
409 * The following locking scheme is used:
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
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;
441 void
442 exclusiveLC(__nis_ldap_conn_t *lc) {
443 pthread_t me = pthread_self();
444 int stat;
446 if (lc == 0)
447 return;
449 stat = mutex_trylock(&lc->mutex);
450 if (stat == EBUSY && lc->owner != me)
451 mutex_lock(&lc->mutex);
453 lc->owner = me;
456 /* Return 1 if mutex held by this thread, 0 otherwise */
458 assertExclusive(__nis_ldap_conn_t *lc) {
459 pthread_t me;
460 int stat;
462 if (lc == 0)
463 return (0);
465 stat = mutex_trylock(&lc->mutex);
467 if (stat == 0) {
468 mutex_unlock(&lc->mutex);
469 return (0);
472 me = pthread_self();
473 if (stat != EBUSY || lc->owner != me)
474 return (0);
476 return (1);
479 void
480 releaseLC(__nis_ldap_conn_t *lc) {
481 pthread_t me = pthread_self();
483 if (lc == 0 || lc->owner != me)
484 return;
486 lc->owner = 0;
487 (void) mutex_unlock(&lc->mutex);
490 void
491 incrementRC(__nis_ldap_conn_t *lc) {
492 if (lc == 0)
493 return;
495 (void) mutex_lock(&lc->rcMutex);
496 lc->refCount++;
497 (void) mutex_unlock(&lc->rcMutex);
500 void
501 decrementRC(__nis_ldap_conn_t *lc) {
502 if (lc == 0)
503 return;
505 (void) mutex_lock(&lc->rcMutex);
506 if (lc->refCount > 0)
507 lc->refCount--;
508 (void) mutex_unlock(&lc->rcMutex);
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;
520 if (srv == 0)
521 return (0);
523 if (use_ssl) {
524 ld = ldapssl_init(srv, port, 1);
525 } else {
526 ld = ldap_init(srv, port);
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);
539 return (ld);
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.
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";
555 if (ldP == 0 || (ld = *ldP) == 0)
556 return (LDAP_PARAM_ERROR);
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;
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);
578 if (msg != 0)
579 (void) ldap_msgfree(msg);
580 } else {
581 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
582 &ret);
584 } else if (method == cram_md5) {
585 /* Note: there is only a synchronous call for cram-md5 */
586 struct berval ber_cred;
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;
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;
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));
611 return (ret);
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.
619 static int
620 freeCon(__nis_ldap_conn_t *lc) {
622 if (!assertExclusive(lc))
623 return (LDAP_PARAM_ERROR);
625 incrementRC(lc);
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);
634 sfree(lc->sp);
635 sfree(lc->who);
636 sfree(lc->cred);
638 /* Delete structure with both mutex:es held */
640 free(lc);
642 return (LDAP_UNAVAILABLE);
646 * Disconnect the specified LDAP connection. Caller must have acquired 'mutex'.
648 * On return, if the status is LDAP_UNAVAILABLE, the caller must not touch
649 * the structure in any way.
651 static int
652 disconnectCon(__nis_ldap_conn_t *lc) {
653 int stat;
654 char *myself = "disconnectCon";
656 if (lc == 0)
657 return (LDAP_SUCCESS);
659 if (!assertExclusive(lc))
660 return (LDAP_UNAVAILABLE);
662 if (lc->doDis) {
664 /* Increment refCount to protect against interference */
665 incrementRC(lc);
666 /* refCount must be one (i.e., just us) */
667 if (lc->refCount != 1) {
669 * In use; already marked for disconnect,
670 * so do nothing.
672 decrementRC(lc);
673 return (LDAP_BUSY);
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));
690 decrementRC(lc);
693 if (lc->doDel) {
694 if (LDAP_UNAVAILABLE == freeCon(lc))
695 stat = LDAP_UNAVAILABLE;
698 return (stat);
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.
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";
721 attr[0] = "supportedControl";
722 attr[1] = 0;
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);
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);
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);
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;
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++;
769 ldap_value_free(val);
770 ldap_memfree(a);
771 if (ber != 0)
772 ber_free(ber, 0);
773 ldap_msgfree(res);
775 return (stat);
779 * Connect the LDAP connection 'lc'. Caller must have acquired the 'mutex',
780 * and the refCount must be zero.
782 * On return, if the status is LDAP_UNAVAILABLE, the caller must not touch
783 * the structure in any way.
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};
794 if (lc == 0)
795 return (LDAP_SUCCESS);
797 if (!assertExclusive(lc))
798 return (LDAP_PARAM_ERROR);
800 incrementRC(lc);
801 if (lc->refCount != 1) {
803 * Don't want to step on structure when it's used by someone
804 * else.
806 decrementRC(lc);
807 return (LDAP_BUSY);
810 (void) gettimeofday(&tp, 0);
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;
826 } else if (tp.tv_sec < lc->retryTime) {
827 /* Too early to retry connect */
828 decrementRC(lc);
829 return (LDAP_SERVER_DOWN);
832 /* Set new retry time in case we fail below */
833 lc->retryTime = tp.tv_sec + ldapConnAttemptRetryTimeout;
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);
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;
854 decrementRC(lc);
856 return (stat);
860 * Find and return a connection believed to be OK.
862 static __nis_ldap_conn_t *
863 findCon(int *stat) {
864 __nis_ldap_conn_t *lc;
865 int ldapStat;
866 char *myself = "findCon";
868 if (stat == 0)
869 stat = &ldapStat;
871 (void) rw_rdlock(&ldapConLock);
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);
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);
897 continue;
899 } else if (lc->doDis || lc->doDel) {
900 *stat = disconnectCon(lc);
901 if (*stat != LDAP_UNAVAILABLE)
902 releaseLC(lc);
903 continue;
905 incrementRC(lc);
906 releaseLC(lc);
907 break;
910 (void) rw_unlock(&ldapConLock);
912 return (lc);
915 /* Release connection; decrements ref count for the connection */
916 static void
917 releaseCon(__nis_ldap_conn_t *lc, int status) {
918 int stat;
920 if (lc == 0)
921 return;
923 exclusiveLC(lc);
925 lc->status = status;
927 decrementRC(lc);
929 if (lc->doDis)
930 stat = disconnectCon(lc);
931 else
932 stat = LDAP_SUCCESS;
934 if (stat != LDAP_UNAVAILABLE)
935 releaseLC(lc);
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;
944 if (sp == 0)
945 return (0);
947 lc = am(myself, sizeof (*lc));
948 if (lc == 0)
949 return (0);
951 (void) mutex_init(&lc->mutex, 0, 0);
952 (void) mutex_init(&lc->rcMutex, 0, 0);
954 /* If we need to delete 'lc', freeCon() wants the mutex held */
955 exclusiveLC(lc);
957 lc->sp = sdup(myself, T, sp);
958 if (lc->sp == 0) {
959 (void) freeCon(lc);
960 return (0);
963 if ((r = strchr(lc->sp, ']')) != 0) {
965 * IPv6 address. Does libldap want this with the
966 * '[' and ']' left in place ? Assume so for now.
968 r = strchr(r, ':');
969 } else {
970 r = strchr(lc->sp, ':');
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;
979 if (who != 0) {
980 lc->who = sdup(myself, T, who);
981 if (lc->who == 0) {
982 (void) freeCon(lc);
983 return (0);
987 if (cred != 0) {
988 lc->cred = sdup(myself, T, cred);
989 if (lc->cred == 0) {
990 (void) freeCon(lc);
991 return (0);
995 lc->method = method;
996 lc->port = port;
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;
1004 /* All other fields OK at zero */
1006 releaseLC(lc);
1008 return (lc);
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";
1017 if (serverList == 0)
1018 return (LDAP_PARAM_ERROR);
1020 (void) rw_wrlock(&ldapConLock);
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);
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);
1035 /* Remove leading white space */
1036 for (; *sl == ' ' || *sl == '\t'; sl++);
1038 /* Create connection for each server on the list */
1039 for (s = sl; *s != '\0'; s = e+1) {
1040 int l;
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);
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);
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;
1069 free(sls);
1071 (void) rw_unlock(&ldapConLock);
1073 return (LDAP_SUCCESS);
1076 static bool_t
1077 is_same_connection(__nis_ldap_conn_t *lc, LDAPURLDesc *ludpp)
1079 return (strcasecmp(ludpp->lud_host, lc->sp) == 0 &&
1080 ludpp->lud_port == lc->port);
1083 static __nis_ldap_conn_t *
1084 find_connection_from_list(__nis_ldap_conn_t *list,
1085 LDAPURLDesc *ludpp, int *stat)
1087 int ldapStat;
1088 __nis_ldap_conn_t *lc = NULL;
1089 if (stat == 0)
1090 stat = &ldapStat;
1092 *stat = LDAP_SUCCESS;
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;
1103 } else if (lc->doDis || lc->doDel) {
1104 (void) disconnectCon(lc);
1105 releaseLC(lc);
1106 continue;
1108 incrementRC(lc);
1109 releaseLC(lc);
1110 break;
1112 releaseLC(lc);
1114 return (lc);
1117 static __nis_ldap_conn_t *
1118 findReferralCon(char **referralsp, int *stat)
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";
1127 if (stat == 0)
1128 stat = &ldapStat;
1130 *stat = LDAP_SUCCESS;
1133 * We have the referral lock - to prevent multiple
1134 * threads from creating a referred connection simultaneously
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.
1143 (void) rw_rdlock(&referralConLock);
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;
1155 } else {
1156 if (proxyInfo.tls_method != no_tls) {
1157 ldap_free_urldesc(ludpp);
1158 continue;
1161 #endif
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);
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;
1184 } else {
1185 if (proxyInfo.tls_method != no_tls) {
1186 ldap_free_urldesc(ludpp);
1187 continue;
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);
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;
1214 lc = find_connection_from_list(ldapReferralCon, ludpp, stat);
1215 ldap_free_urldesc(ludpp);
1216 if (lc != NULL)
1217 break;
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]));
1226 return (lc);
1230 * Find and return a connection believed to be OK and ensure children
1231 * will never use parent's connection.
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";
1239 if (stat == 0)
1240 stat = &ldapStat;
1242 (void) rw_rdlock(&ldapConLock);
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);
1256 for (lc = ldapCon; lc != 0; lc = lc->next) {
1257 exclusiveLC(lc);
1259 if (lc->isBound && (lc->doDis || lc->doDel)) {
1260 *stat = disconnectCon(lc);
1261 if (*stat != LDAP_UNAVAILABLE)
1262 releaseLC(lc);
1263 continue;
1267 * Use a new connection for all cases except when
1268 * requested by the main thread in the parent ypserv
1269 * process.
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;
1278 if (lc->ld != 0) {
1279 newlc->simplePage = lc->simplePage;
1280 newlc->vlv = lc->vlv;
1281 newlc->batchFrom = lc->batchFrom;
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));
1293 (void) freeCon(newlc);
1294 newlc = 0;
1295 continue;
1299 * No need to put newlc on the ldapCon list as this
1300 * connection will be freed after use.
1302 newlc->onList = 0;
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);
1315 continue;
1319 incrementRC(lc);
1320 releaseLC(lc);
1321 break;
1324 (void) rw_unlock(&ldapConLock);
1326 return (lc);
1329 #define SORTKEYLIST "cn uid"
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.
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.
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.
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;
1367 ctrls[0] = ctrls[1] = ctrls[2] = 0;
1369 if (ldapStat == 0)
1370 ldapStat = &stat;
1372 if (ls == 0) {
1373 *ldapStat = LDAP_PARAM_ERROR;
1374 return (0);
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);
1383 } else {
1384 if ((lc = findCon(ldapStat)) == 0) {
1385 *ldapStat = LDAP_SERVER_DOWN;
1386 return (0);
1390 if (numValues != 0 && (*numValues == 0 || *numValues == 1))
1391 doIndex = 0;
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;
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;
1426 retry_noVLV:
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;
1445 if (!doVLV && !doSP)
1446 ctrls[0] = ctrls[1] = 0;
1448 numVals = 0;
1449 done = 0;
1451 if (ls->timeout.tv_sec || ls->timeout.tv_usec) {
1452 tv = ls->timeout;
1453 } else {
1454 tv = lc->searchTimeout;
1456 (void) gettimeofday(&start, 0);
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;
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;
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);
1514 if (stat == LDAP_SUCCESS)
1515 ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, &stat);
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;
1527 goto retry_new_conn;
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;
1545 stat = LDAP_SUCCESS;
1546 goto retry_new_conn;
1549 *ldapStat = stat;
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) {
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.
1562 doVLV = 0;
1563 if (msg != 0) {
1564 (void) ldap_msgfree(msg);
1565 msg = 0;
1567 if (ctrls[0] != 0) {
1568 ldap_control_free(ctrls[0]);
1569 ctrls[0] = 0;
1571 if (ctrls[1] != 0) {
1572 ldap_control_free(ctrls[1]);
1573 ctrls[1] = 0;
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;
1592 numEntries = ldap_count_entries(lc->ld, msg);
1593 if (numEntries == 0 && *ldapStat == LDAP_SUCCESS) {
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.
1601 freeRuleValue(rv, numVals);
1602 rv = 0;
1603 *ldapStat = LDAP_NO_SUCH_OBJECT;
1604 goto cleanup;
1607 tnv = numVals + numEntries;
1608 if ((rv = growRuleValue(numVals, tnv, rv, rvIn)) == 0) {
1609 *ldapStat = LDAP_NO_MEMORY;
1610 goto cleanup;
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;
1618 if (numVals > tnv) {
1619 logmsg(MSG_NOTIMECHECK, LOG_INFO,
1620 "%s: Inconsistent LDAP entry count > %d",
1621 myself, numEntries);
1622 break;
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;
1634 sfree(nm);
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;
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++) {
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.
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;
1666 ldap_memfree(nm);
1667 if (val != 0)
1668 ldap_value_free_len(val);
1670 if (ber != 0)
1671 ber_free(ber, 0);
1674 if (numVals != tnv) {
1675 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1676 "%s: Inconsistent LDAP entry count, found = %d, expected %d",
1677 myself, numVals, tnv);
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;
1694 if (retCtrls != 0) {
1695 unsigned long targetPosP = 0;
1696 unsigned long listSize = 0;
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;
1706 ldap_controls_free(retCtrls);
1707 retCtrls = 0;
1708 } else {
1709 done = 1;
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;
1725 if (retCtrls != 0) {
1726 unsigned int count;
1728 if (spCookie != 0) {
1729 ber_bvfree(spCookie);
1730 spCookie = 0;
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;
1740 ldap_controls_free(retCtrls);
1741 retCtrls = 0;
1742 } else {
1743 done = 1;
1745 } else {
1746 done = 1;
1749 (void) ldap_msgfree(msg);
1750 msg = 0;
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.
1757 if (!done) {
1758 struct timeval tmp;
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;
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;
1774 if (tv.tv_sec < 0) {
1775 *ldapStat = LDAP_TIMEOUT;
1776 freeRuleValue(rv, tnv);
1777 rv = 0;
1778 goto cleanup;
1780 start = tmp;
1783 } while (!done);
1785 if (numValues != 0)
1786 *numValues = numVals;
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);
1797 } else {
1798 releaseCon(lc, stat);
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);
1812 return (rv);
1815 static void
1816 freeLdapModEntry(LDAPMod *m) {
1818 if (m == 0)
1819 return;
1821 sfree(m->mod_type);
1822 if ((m->mod_op & LDAP_MOD_BVALUES) == 0) {
1823 char **v = m->mod_values;
1825 if (v != 0) {
1826 while (*v != 0) {
1827 sfree(*v);
1828 v++;
1830 free(m->mod_values);
1832 } else {
1833 struct berval **b = m->mod_bvalues;
1835 if (b != 0) {
1836 while (*b != 0) {
1837 sfree((*b)->bv_val);
1838 free(*b);
1839 b++;
1841 free(m->mod_bvalues);
1845 free(m);
1848 static void
1849 freeLdapMod(LDAPMod **mods) {
1850 LDAPMod *m, **org = mods;
1852 if (mods == 0)
1853 return;
1855 while ((m = *mods) != 0) {
1856 freeLdapModEntry(m);
1857 mods++;
1860 free(org);
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.
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";
1875 if (rv == 0 || rv->numAttrs <= 0)
1876 return (0);
1878 mods = am(myself, (rv->numAttrs + 1) * sizeof (mods[0]));
1879 if (mods == 0)
1880 return (0);
1882 for (i = 0, nm = 0; i < rv->numAttrs; i++) {
1883 int isOc;
1885 * If we're creating an LDAPMod array for an add operation,
1886 * just skip attributes that should be deleted.
1888 if (add && rv->attrVal[i].numVals < 0)
1889 continue;
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.
1896 if (strcasecmp("dn", rv->attrName[i]) == 0)
1897 continue;
1900 * If modifying, and 'oc' is off, skip object class
1901 * attributes.
1903 isOc = (strcasecmp("objectclass", rv->attrName[i]) == 0);
1904 if (!add && !oc && isOc)
1905 continue;
1907 mods[nm] = am(myself, sizeof (*mods[nm]));
1908 if (mods[nm] == 0) {
1909 freeLdapMod(mods);
1910 return (0);
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);
1921 * numVals < 0 means attribute and all values should
1922 * be deleted.
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;
1931 /* objectClass attributes always added */
1932 mods[nm]->mod_op = (add) ? 0 : ((isOc) ? 0 : LDAP_MOD_REPLACE);
1934 if (rv->attrVal[i].type == vt_string) {
1936 * mods[]->mod_values is a NULL-terminated array
1937 * of (char *)'s.
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);
1946 for (j = 0; j < rv->attrVal[i].numVals; j++) {
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.
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);
1959 memcpy(mods[nm]->mod_values[j],
1960 rv->attrVal[i].val[j].value,
1961 rv->attrVal[i].val[j].length);
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);
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);
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);
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);
1992 nm++;
1995 return (mods);
1999 * Remove 'value' from 'val'. If value==0, remove the entire
2000 * __nis_single_value_t array from 'val'.
2002 static void
2003 removeSingleValue(__nis_value_t *val, void *value, int length) {
2004 int i;
2006 if (val == 0)
2007 return;
2009 if (value == 0) {
2010 for (i = 0; i < val->numVals; i++) {
2011 sfree(val->val[i].value);
2013 sfree(val->val);
2014 val->val = 0;
2015 val->numVals = 0;
2016 return;
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]));
2029 val->numVals -= 1;
2030 break;
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.
2042 static int
2043 ldapModifyObjectClass(__nis_ldap_conn_t **lc, char *dn,
2044 __nis_rule_value_t *rvIn, char *objClassAttrs)
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";
2059 rv = initRuleValue(1, rvIn);
2060 if (rv == 0)
2061 return (LDAP_NO_MEMORY);
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;
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.
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;
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;
2101 * Find the indices of the 'objectClass' attribute
2102 * in 'rvldap' and 'rv'.
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;
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;
2120 * Remove those object classes that already exist
2121 * in LDAP (i.e., in 'rvldap') from 'rv'.
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);
2130 * If no 'objectClass' values left in 'rv', delete
2131 * 'objectClass' from 'rv'.
2133 if (rv->attrVal[ocrv].numVals == 0)
2134 delAttrFromRuleValue(rv, "objectClass");
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.
2142 freeRuleValue(rvldap, 1);
2144 addObjectClasses:
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;
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);
2170 } else {
2171 (void) ldap_get_option((*lc)->ld, LDAP_OPT_ERROR_NUMBER,
2172 &stat);
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;
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;
2204 cleanup:
2205 if (mods != 0)
2206 freeLdapMod(mods);
2207 freeRuleValue(rv, 1);
2208 return (stat);
2212 * Modify the specified 'dn' per the attribute names/values in 'rv'.
2213 * If 'rv' is NULL, we attempt to delete the entire entry.
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.
2218 * If 'addFirst' is set, we try an add before a modify; modify before
2219 * add otherwise (ignored if we're deleting).
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;
2234 if (dn == 0)
2235 return (LDAP_PARAM_ERROR);
2237 if ((lc = findCon(&stat)) == 0)
2238 return (stat);
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;
2249 tv = lc->deleteTimeout;
2250 stat = ldap_result(lc->ld, msgid, 0, &tv, &msg);
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;
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;
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;
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);
2306 * First try the modify without specifying object classes
2307 * (i.e., assume they're already present).
2309 mods = search2LdapMod(rv, 0, 0);
2310 if (mods == 0) {
2311 stat = LDAP_PARAM_ERROR;
2312 goto cleanup;
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;
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;
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;
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;
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.
2372 if (stat == LDAP_OBJECT_CLASS_VIOLATION &&
2373 objClassAttrs != 0) {
2374 freeLdapMod(mods);
2375 mods = 0;
2376 stat = ldapModifyObjectClass(&lc, dn, rv,
2377 objClassAttrs);
2380 if (stat == LDAP_NO_SUCH_ATTRIBUTE) {
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.
2387 int d, numDelete, st;
2388 __nis_rule_value_t *rvt;
2390 for (d = 0, numDelete = 0; d < rv->numAttrs; d++) {
2391 if (rv->attrVal[d].numVals < 0)
2392 numDelete++;
2395 /* If there's just one, we've already tried */
2396 if (numDelete <= 1)
2397 goto cleanup;
2399 /* Make a copy of the rule value */
2400 rvt = initRuleValue(1, rv);
2401 if (rvt == 0)
2402 goto cleanup;
2405 * Remove all delete attributes from the tmp
2406 * rule value.
2408 for (d = 0; d < rv->numAttrs; d++) {
2409 if (rv->attrVal[d].numVals < 0) {
2410 delAttrFromRuleValue(rvt,
2411 rv->attrName[d]);
2416 * Now put the attributes back in one by one, and
2417 * invoke ourselves.
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;
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;
2438 delAttrFromRuleValue(rvt, rv->attrName[d]);
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.
2446 stat = LDAP_SUCCESS;
2447 freeRuleValue(rvt, 1);
2450 if (stat == LDAP_NO_SUCH_OBJECT && !addFirst) {
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).
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.
2463 int allDelete;
2464 LDAPMod **m;
2466 for (m = mods, allDelete = 1; *m != 0 && allDelete;
2467 m++) {
2468 if (((*m)->mod_op & LDAP_MOD_DELETE) == 0)
2469 allDelete = 0;
2472 add = 1;
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;
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));
2497 releaseCon(lc, stat);
2498 freeLdapMod(mods);
2499 if (msg != 0)
2500 (void) ldap_msgfree(msg);
2502 return (stat);
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.
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.
2515 * Returns an LDAP status.
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;
2528 if (dn == 0 || rv == 0 || objClassAttrs == 0) {
2529 releaseCon(lc, LDAP_SUCCESS);
2530 return (LDAP_PARAM_ERROR);
2533 if (lc == 0) {
2534 if ((lc = findCon(&stat)) == 0)
2535 return (stat);
2538 rv = addObjectClasses(rv, objClassAttrs);
2539 if (rv == 0) {
2540 stat = LDAP_OPERATIONS_ERROR;
2541 goto cleanup;
2544 mods = search2LdapMod(rv, 1, 0);
2545 if (mods == 0) {
2546 stat = LDAP_OPERATIONS_ERROR;
2547 goto cleanup;
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;
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;
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;
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;
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));
2606 releaseCon(lc, stat);
2607 freeLdapMod(mods);
2608 if (msg != 0)
2609 (void) ldap_msgfree(msg);
2611 return (stat);
2615 * Change the entry at 'oldDn' to have the new DN (not RDN) 'dn'.
2616 * Returns an LDAP error status.
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";
2631 if ((lo = slen(oldDn)) <= 0 || (ln = slen(dn)) <= 0)
2632 return (LDAP_PARAM_ERROR);
2634 if (strcasecmp(oldDn, dn) == 0)
2635 return (LDAP_SUCCESS);
2637 if ((lc = findCon(&stat)) == 0)
2638 return (stat);
2640 rdn = sdup(myself, T, dn);
2641 if (rdn == 0) {
2642 releaseCon(lc, LDAP_SUCCESS);
2643 return (LDAP_NO_MEMORY);
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])) {
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.
2655 rdn[j+1] = '\0';
2656 break;
2660 stat = ldap_rename(lc->ld, oldDn, rdn, NULL, 1, NULL, NULL, &msgid);
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);
2677 } else {
2678 (void) ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER,
2679 &stat);
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;
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;
2713 cleanup:
2714 if (msg != NULL)
2715 (void) ldap_msgfree(msg);
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
2727 if (stat == LDAP_NO_SUCH_OBJECT) {
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.
2733 stat = LDAP_SUCCESS;
2736 releaseCon(lc, stat);
2737 sfree(rdn);
2739 return (stat);