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