ldap_op.c revision d387ac4c164917d885cd84bd1b62647d989033ac
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <synch.h>
#include <strings.h>
#include <ctype.h>
#include "ldap_op.h"
#include "ldap_util.h"
#include "ldap_structs.h"
#include "ldap_ruleval.h"
#include "ldap_attr.h"
#include "ldap_print.h"
#include "ldap_glob.h"
#include "nis_parse_ldap_conf.h"
#ifndef LDAPS_PORT
#define LDAPS_PORT 636
#endif
/*
* Build one of our internal LDAP search structures, containing copies of
* the supplied input. return NULL in case of error.
*
* If 'filter' is NULL, build an AND-filter using the filter components.
*/
char **a;
char *myself = "buildLdapSearch";
if (ls == 0)
return (0);
err++;
if (filterComp != 0 && numFilterComps > 0) {
sizeof (ls->filterComp[0]));
if (ls->filterComp == 0) {
err++;
numFilterComps = 0;
}
for (i = 0; i < numFilterComps; i++) {
err++;
}
if (filter == 0) {
ls->filterComp);
err++;
}
} else {
ls->filterComp = 0;
ls->numFilterComps = 0;
err++;
}
if (attrs != 0) {
for (i = 0; i < na; i++) {
err++;
}
} else {
err++;
}
} else {
}
if (err > 0) {
ls = 0;
}
return (ls);
}
void
int i;
if (ls == 0)
return;
if (ls->filterComp != 0) {
for (i = 0; i < ls->numFilterComps; i++) {
}
}
}
}
}
/*
* return an LDAP search structure with values suitable for use
* may be modified.
*
* If dn != 0 and *dn == 0, the function attemps to return a pointer
* to the DN. This may necessitate an ldapSearch, if the rule set doesn't
* produce a DN directly.
*
* if dn == 0, and the rule set produces a DN as well as other attribute/
* value pairs, the function returns an LDAP search structure with the
* DN only.
*
* t->objectDN->read; otherwise, from t->objectDN->write.
*
* If 'rv' is NULL, the caller wants an enumeration of the container.
*
* Note that this function only creates a search structure for 't' itself;
* if there are alternative mappings for the table, those must be handled
* by our caller.
*/
int i, j;
__nis_ldap_search_t *ls = 0;
char **locDN;
char *myself = "createLdapRequest";
if (t == 0)
return (0);
else
if (rv == 0) {
char *base;
char *filter;
if (fromLDAP) {
} else {
}
/* Create request to enumerate container */
0, 0, 0);
return (ls);
}
for (i = 0; i < t->numRulesToLDAP; i++) {
if (rv == 0)
return (0);
if (stat == NP_LDAP_RULES_NO_VALUE)
count++;
stat = 0;
}
/*
* If none of the rules produced a value despite
* having enough NIS+ columns, return error.
*/
return (0);
}
/*
* 'rv' now contains everything we know about the attributes and
* values. Build an LDAP search structure from it.
*/
/* Look for a single-valued DN */
&numLocDN);
} else {
char *filter;
if (fromLDAP)
else
filter, 0, 0, 1);
}
} else {
}
if (ls != 0) {
return (ls);
}
/*
* No DN, or caller wanted a search structure with the non-DN
* attributes.
*/
/* Initialize search structure */
{
char **ofc;
int nofc = 0;
"%s: Unable to break filter into components: \"%s\"",
return (0);
}
if (fromLDAP)
else
if (ls == 0)
return (0);
}
/* Build and add the filter components */
/* Skip DN */
continue;
/* Skip vt_ber values */
continue;
__nis_buffer_t b = {0, 0};
char **tmpComp;
&ls->numFilterComps);
if (tmpComp == 0) {
"%s: Unable to add filter component \"%s\"",
return (0);
}
}
}
if (ls->numFilterComps > 0) {
ls->filterComp);
"%s: Unable to concatenate filter components",
myself);
return (0);
}
}
/*
* The caller wants a DN, but we didn't get one from the
* the rule set. We have an 'ls', so use it to ldapSearch()
* for an entry from which we can extract the DN.
*/
char **locDN;
} else {
}
}
return (ls);
}
typedef struct {
int refCount; /* Reference count */
int isBound; /* Is connection open and usable ? */
int status; /* Status of last operation */
int doDis; /* To be disconnected if refCount==0 */
int doDel; /* To be deleted if refCount zero */
int onList; /* True if on the 'ldapCon' list */
char *sp; /* server string */
char *who;
char *cred;
int port;
struct timeval bindTimeout;
struct timeval searchTimeout;
struct timeval modifyTimeout;
struct timeval addTimeout;
struct timeval deleteTimeout;
int simplePage; /* Can do simple-page */
int vlv; /* Can do VLV */
void *next;
/*
* List of connections, 'ldapCon', protected by an RW lock.
*
* The following locking scheme is used:
*
* (1) Find a connection structure to use to talk to LDAP
* Rlock list
* Locate structure
* Acquire 'mutex'
* Acquire 'rcMutex'
* update refCount
* Release 'rcMutex'
* release 'mutex'
* Unlock list
* Use structure
* Release structure when done
* Wlock list
* acquire 'mutex', and 'rcMutex' (in that order),
* and 'refCount' must be zero.
* Unlock list
* (3) Modify structure
* Find structure
* Acquire 'mutex'
* Modify (except refCount)
* Release 'mutex'
* Release structure
*/
__nis_ldap_conn_t *ldapCon = 0;
void
int stat;
if (lc == 0)
return;
}
/* Return 1 if mutex held by this thread, 0 otherwise */
int
int stat;
if (lc == 0)
return (0);
if (stat == 0) {
return (0);
}
me = pthread_self();
return (0);
return (1);
}
void
return;
}
void
if (lc == 0)
return;
}
void
if (lc == 0)
return;
}
static LDAP *
int ldapVersion = LDAP_VERSION3;
int derefOption = LDAP_DEREF_ALWAYS;
char *myself = "ldapInit";
if (srv == 0)
return (0);
if (use_ssl) {
} else {
}
if (ld != 0) {
&ldapVersion);
}
return (ld);
}
/*
* Bind the specified LDAP structure per the supplied authentication.
* Note: tested with none, simple, and digest_md5. May or may not
* work with other authentication methods, mostly depending on whether
* or not 'who' and 'cred' contain sufficient information.
*/
static int
int ret;
char *myself = "ldapBind";
return (LDAP_PARAM_ERROR);
/* No ldap_bind() required (or even possible) */
ret = LDAP_SUCCESS;
LDAPMessage *msg = 0;
if (ret != -1) {
if (ret == 0) {
ret = LDAP_TIMEOUT;
} else if (ret == -1) {
(void) ldap_get_option(ld,
&ret);
} else {
}
if (msg != 0)
(void) ldap_msgfree(msg);
} else {
&ret);
}
/* Note: there is only a synchronous call for cram-md5 */
} else if (method == digest_md5) {
/* Note: there is only a synchronous call for digest-md5 */
NULL);
} else {
}
if (ret != LDAP_SUCCESS) {
(void) ldap_unbind_s(ld);
*ldP = 0;
"%s: Unable to bind as: %s: %s",
}
return (ret);
}
/*
* Free 'lc' and all related memory. Caller must hold the exclusive lock.
* Return LDAP_UNAVAILABLE upon success, in which case the caller mustn't
* try to use the structure pointer in any way.
*/
static int
char *myself = "freeCon";
if (!assertExclusive(lc))
return (LDAP_PARAM_ERROR);
/* Must be unused, unbound, and not on the 'ldapCon' list */
return (LDAP_BUSY);
}
/* Delete structure with both mutex:es held */
return (LDAP_UNAVAILABLE);
}
/*
* Disconnect the specified LDAP connection. Caller must have acquired 'mutex'.
*
* On return, if the status is LDAP_UNAVAILABLE, the caller must not touch
* the structure in any way.
*/
static int
int stat;
char *myself = "disconnectCon";
if (lc == 0)
return (LDAP_SUCCESS);
if (!assertExclusive(lc))
return (LDAP_UNAVAILABLE);
/* Increment refCount to protect against interference */
/* refCount must be one (i.e., just us) */
/*
* In use; already marked for disconnect,
* so do nothing.
*/
return (LDAP_BUSY);
}
if (stat == LDAP_SUCCESS) {
/* Reset simple page and vlv indication */
lc->simplePage = 0;
} else if (verbose) {
"%s: ldap_unbind_s() => %d (%s)",
}
}
}
return (stat);
}
/*
* controlSupported will determine for a given connection whether a set
* of controls is supported or not. The input parameters:
* lc The connection
* ctrl A an array of OID strings, the terminal string should be NULL
* The returned values if LDAP_SUCCESS is returned:
* supported A caller supplied array which will be set to TRUE or
* FALSE depending on whether the corresponding control
* is reported as supported.
* Returns LDAP_SUCCESS if the supportedControl attribute is read.
*/
static int
LDAPMessage *res, *e;
int stat, i;
BerElement *ber = 0;
char *myself = "controlSupported";
attr[0] = "supportedControl";
attr[1] = 0;
if (stat != LDAP_SUCCESS) {
"%s: Unable to retrieve supported control information for %s: %s",
return (stat);
}
if (e != 0) {
if (a != 0) {
if (val == 0) {
ldap_memfree(a);
if (ber != 0)
}
}
}
if (e == 0 || a == 0 || val == 0) {
"%s: Unable to get root DSE for %s",
return (LDAP_OPERATIONS_ERROR);
}
for (i = 0; val[i] != 0; i++) {
break;
}
}
"%s: %s: %s: %s",
ctrl++;
supported++;
}
ldap_memfree(a);
if (ber != 0)
return (stat);
}
/*
* Connect the LDAP connection 'lc'. Caller must have acquired the 'mutex',
* and the refCount must be zero.
*
* On return, if the status is LDAP_UNAVAILABLE, the caller must not touch
* the structure in any way.
*/
static int
int stat;
NULL};
if (lc == 0)
return (LDAP_SUCCESS);
if (!assertExclusive(lc))
return (LDAP_PARAM_ERROR);
/*
* Don't want to step on structure when it's used by someone
* else.
*/
return (LDAP_BUSY);
}
(void) gettimeofday(&tp, 0);
/* Try to disconnect */
/* disconnctCon() will do the delete if required */
if (stat != LDAP_SUCCESS)
return (stat);
}
/* Too early to retry connect */
return (LDAP_SERVER_DOWN);
}
/* Set new retry time in case we fail below */
return (LDAP_LOCAL_ERROR);
}
lc->bindTimeout);
if (check_ctrl) {
}
}
return (stat);
}
/*
* Find and return a connection believed to be OK.
*/
static __nis_ldap_conn_t *
int ldapStat;
char *myself = "findCon";
if (stat == 0)
(void) rw_rdlock(&ldapConLock);
if (ldapCon == 0) {
/* Probably first call; try to set up the connection list */
(void) rw_unlock(&ldapConLock);
proxyInfo.auth_method)) !=
return (0);
(void) rw_rdlock(&ldapConLock);
}
if (*stat != LDAP_SUCCESS) {
if (*stat != LDAP_UNAVAILABLE) {
"%s: Cannot open connection to LDAP server (%s): %s",
ldap_err2string(*stat));
}
continue;
}
if (*stat != LDAP_UNAVAILABLE)
continue;
}
break;
}
(void) rw_unlock(&ldapConLock);
return (lc);
}
/* Release connection; decrements ref count for the connection */
static void
int stat;
if (lc == 0)
return;
else
stat = LDAP_SUCCESS;
if (stat != LDAP_UNAVAILABLE)
}
static __nis_ldap_conn_t *
char *myself = "createCon";
char *r;
if (sp == 0)
return (0);
if (lc == 0)
return (0);
/* If we need to delete 'lc', freeCon() wants the mutex held */
return (0);
}
/*
* IPv6 address. Does libldap want this with the
* '[' and ']' left in place ? Assume so for now.
*/
r = strchr(r, ':');
} else {
}
if (r != NULL) {
*r++ = '\0';
} else if (port == 0)
if (who != 0) {
return (0);
}
}
if (cred != 0) {
return (0);
}
}
/* All other fields OK at zero */
return (lc);
}
static int
char *myself = "setupConList";
if (serverList == 0)
return (LDAP_PARAM_ERROR);
(void) rw_wrlock(&ldapConLock);
if (ldapCon != 0) {
/* Assume we've already been called and done the set-up */
(void) rw_unlock(&ldapConLock);
return (LDAP_SUCCESS);
}
/* Work on a copy of 'serverList' */
if (sl == 0) {
(void) rw_unlock(&ldapConLock);
return (LDAP_NO_MEMORY);
}
/* Remove leading white space */
/* Create connection for each server on the list */
int l;
for (e = s; *e != ' ' && *e != '\t' && *e != '\0'; e++);
if (*e != '\0')
*e = '\0';
else
e--;
l = slen(s);
if (l > 0) {
if (lc == 0) {
(void) rw_unlock(&ldapConLock);
return (LDAP_NO_MEMORY);
}
if (ldapCon == 0) {
} else {
/* Insert at end of list */
}
}
}
(void) rw_unlock(&ldapConLock);
return (LDAP_SUCCESS);
}
static bool_t
{
}
static __nis_ldap_conn_t *
{
int ldapStat;
if (stat == 0)
*stat = LDAP_SUCCESS;
if (*stat != LDAP_SUCCESS) {
continue;
}
(void) disconnectCon(lc);
continue;
}
break;
}
}
return (lc);
}
static __nis_ldap_conn_t *
{
int ldapStat;
int i;
char *myself = "findReferralCon";
if (stat == 0)
*stat = LDAP_SUCCESS;
/*
* We have the referral lock - to prevent multiple
* threads from creating a referred connection simultaneously
*
* Note that this code assumes that the ldapCon list is a
* static list - that it has previously been created
* (otherwise we wouldn't have gotten a referral) and that
* it will neither grow or shrink - elements may have new
* connections or unbound. If this assumption is no longer valid,
* the locking needs to be reworked.
*/
(void) rw_rdlock(&referralConLock);
for (i = 0; referralsp[i] != NULL; i++) {
continue;
/* Ignore referrals if not at the appropriate tls level */
#ifdef LDAP_URL_OPT_SECURE
continue;
}
} else {
continue;
}
}
#endif
/* Determine if we already have a connection to the server */
(void) rw_unlock(&referralConLock);
return (lc);
}
}
for (i = 0; referralsp[i] != NULL; i++) {
continue;
/* Ignore referrals if not at the appropriate tls level */
#ifdef LDAP_URL_OPT_SECURE
continue;
}
} else {
continue;
}
}
#endif
if (lc == 0) {
(void) rw_unlock(&referralConLock);
*stat = LDAP_NO_MEMORY;
"%s: Could not connect to host: %s",
return (NULL);
}
if (ldapReferralCon == 0) {
} else {
/* Insert at end of list */
}
break;
}
(void) rw_unlock(&referralConLock);
"%s: Could not find a connection to %s, ...",
}
return (lc);
}
/*
* Find and return a connection believed to be OK and ensure children
* will never use parent's connection.
*/
static __nis_ldap_conn_t *
char *myself = "findYPCon";
if (stat == 0)
(void) rw_rdlock(&ldapConLock);
if (ldapCon == 0) {
/* Probably first call; try to set up the connection list */
(void) rw_unlock(&ldapConLock);
proxyInfo.auth_method)) !=
return (0);
(void) rw_rdlock(&ldapConLock);
}
if (*stat != LDAP_UNAVAILABLE)
continue;
}
/*
* Use a new connection for all cases except when
* requested by the main thread in the parent ypserv
* process.
*/
if (!newlc) {
continue;
}
}
if (newstat != LDAP_SUCCESS) {
if (newstat != LDAP_UNAVAILABLE) {
"%s: Cannot open connection to LDAP server (%s): %s",
ldap_err2string(*stat));
}
newlc = 0;
continue;
}
/*
* No need to put newlc on the ldapCon list as this
* connection will be freed after use.
*/
if (*stat != LDAP_SUCCESS) {
if (*stat != LDAP_UNAVAILABLE) {
"%s: Cannot open connection to LDAP server (%s): %s",
ldap_err2string(*stat));
}
continue;
}
}
break;
}
(void) rw_unlock(&ldapConLock);
return (lc);
}
#define SORTKEYLIST "cn uid"
/*
* Perform an LDAP search operation per 'ls', adding the result(s) to
* a copy of the 'rvIn' structure; the copy becomes the return value.
* The caller must deallocate both 'rvIn' and the result, if any.
*
* On entry, '*numValues' contains a hint regarding the expected
* number of entries. Zero is the same as one, and negative values
* imply no information. This is used to decide whether or not to
* try an indexed search.
*
* On successful (non-NULL) return, '*numValues' contains the number
* of __nis_rule_value_t elements in the returned array, and '*stat'
* the LDAP operations status.
*/
int *ldapStat) {
__nis_rule_value_t *rv = 0;
LDAPMessage *msg = 0, *m;
LDAPsortkey **sortKeyList = 0;
LDAPControl **retCtrls = 0;
int doVLV = 0;
int doSP = 0;
long index;
char *myself = "ldapSearch";
int doIndex = 1;
char **referralsp = NULL;
if (ldapStat == 0)
if (ls == 0) {
return (0);
}
if (yp2ldap) {
/* make sure the parent's connection is not used by child */
return (0);
}
} else {
return (0);
}
}
doIndex = 0;
/* Prefer VLV over simple page, and SP over nothing */
if (stat != LDAP_SUCCESS) {
"%s: Error creating sort keylist: %s",
rv = 0;
goto retry_noVLV;
}
&sortCtrl);
if (stat == LDAP_SUCCESS) {
vList.ldvlist_attrvalue = 0;
vList.ldvlist_extradata = 0;
index = 1;
doVLV = 1;
} else {
"%s: Error creating VLV sort control: %s",
rv = 0;
}
}
if (spCookie != 0 &&
doSP = 1;
} else {
"%s: No memory for simple page cookie; using un-paged LDAP search",
myself);
rv = 0;
goto cleanup;
}
}
numVals = 0;
done = 0;
} else {
}
(void) gettimeofday(&start, 0);
do {
/* don't do vlv or simple page for base level searches */
vList.ldvlist_size = 0;
if (vlvCtrl != 0)
if (stat != LDAP_SUCCESS) {
&stat);
"%s: Error creating VLV at index %ld: %s",
rv = 0;
goto cleanup;
}
ctrls[2] = 0;
/* don't do vlv or simple page for base level searches */
if (ctrls[0] != 0)
ldap_control_free(ctrls[0]);
if (stat != LDAP_SUCCESS) {
spCookie = 0;
&stat);
"%s: Simple page error: %s",
rv = 0;
goto cleanup;
}
ctrls[1] = 0;
} else {
}
if (stat == LDAP_SUCCESS)
if (stat == LDAP_SERVER_DOWN) {
if (lc == 0) {
rv = 0;
goto cleanup;
}
goto retry_new_conn;
}
&referralsp, NULL, 0);
if (referralsp != NULL) {
/* We support at most one level of referrals */
rv = 0;
goto cleanup;
}
stat = LDAP_SUCCESS;
goto retry_new_conn;
}
}
if (*ldapStat == LDAP_NO_SUCH_OBJECT) {
rv = 0;
goto cleanup;
/*
* The LDAP server (at least Netscape 4.x) can return
* LDAP_INSUFFICIENT_ACCESS when VLV is supported,
* but not for the bind DN specified. So, just in
* case, we clean up, and try again without VLV.
*/
doVLV = 0;
if (msg != 0) {
(void) ldap_msgfree(msg);
msg = 0;
}
if (ctrls[0] != 0) {
ldap_control_free(ctrls[0]);
ctrls[0] = 0;
}
if (ctrls[1] != 0) {
ctrls[1] = 0;
}
"%s: VLV insufficient access from server %s; retrying without VLV",
goto retry_noVLV;
} else if (*ldapStat != LDAP_SUCCESS) {
"ldap_search(0x%x,\n\t\"%s\",\n\t %d,",
"\t\"%s\",\n\t0x%x,\n\t%d) => %d (%s)",
rv = 0;
goto cleanup;
}
/*
* This is a bit weird, but the server (or, at least,
* ldap_search_ext()) can sometimes return
* LDAP_SUCCESS and no entries when it didn't
* find what we were looking for. Seems it ought to
* return LDAP_NO_SUCH_OBJECT or some such.
*/
rv = 0;
goto cleanup;
}
goto cleanup;
}
char *nm;
BerElement *ber = 0;
"%s: Inconsistent LDAP entry count > %d",
myself, numEntries);
break;
}
rv = 0;
goto cleanup;
}
nm != 0;
int i, nv;
for (i = 0; i < nv; i++) {
/*
* Since we don't know if the value is
* BER-encoded or not, we mark it as a
* string. All is well as long as we
* don't insist on 'vt_ber' when
* interpreting.
*/
if (ber != 0)
rv = 0;
goto cleanup;
}
}
if (val != 0)
}
if (ber != 0)
}
"%s: Inconsistent LDAP entry count, found = %d, expected %d",
}
if (doVLV) {
&retCtrls, 0);
if (stat != LDAP_SUCCESS) {
&stat);
"%s: VLV parse result error: %s",
rv = 0;
goto cleanup;
}
if (retCtrls != 0) {
unsigned long targetPosP = 0;
unsigned long listSize = 0;
&lprEc);
if (stat == LDAP_SUCCESS) {
done = 1;
}
retCtrls = 0;
} else {
done = 1;
}
} else if (doSP) {
&retCtrls, 0);
if (stat != LDAP_SUCCESS) {
&stat);
"%s: Simple page parse result error: %s",
rv = 0;
goto cleanup;
}
if (retCtrls != 0) {
unsigned int count;
if (spCookie != 0) {
spCookie = 0;
}
if (stat == LDAP_SUCCESS) {
if (spCookie == 0 ||
done = 1;
}
retCtrls = 0;
} else {
done = 1;
}
} else {
done = 1;
}
(void) ldap_msgfree(msg);
msg = 0;
/*
* If we're using VLV or SP, the timeout should apply
* to all calls as an aggregate, so we need to reduce
* 'tv' with the time spent on this chunk of data.
*/
if (!done) {
(void) gettimeofday(&now, 0);
}
}
*ldapStat = LDAP_TIMEOUT;
rv = 0;
goto cleanup;
}
}
} while (!done);
if (numValues != 0)
/* Disconnect and free the connection */
} else {
}
}
if (msg != 0)
(void) ldap_msgfree(msg);
if (ctrls[0] != 0)
ldap_control_free(ctrls[0]);
if (ctrls[1] != 0)
if (spCookie != 0)
if (sortKeyList != 0)
return (rv);
}
static void
freeLdapModEntry(LDAPMod *m) {
if (m == 0)
return;
if ((m->mod_op & LDAP_MOD_BVALUES) == 0) {
char **v = m->mod_values;
if (v != 0) {
while (*v != 0) {
sfree(*v);
v++;
}
free(m->mod_values);
}
} else {
struct berval **b = m->mod_bvalues;
if (b != 0) {
while (*b != 0) {
free(*b);
b++;
}
free(m->mod_bvalues);
}
}
free(m);
}
static void
if (mods == 0)
return;
while ((m = *mods) != 0) {
freeLdapModEntry(m);
mods++;
}
}
/*
* Convert a rule-value structure to the corresponding LDAPMod.
* If 'add' is set, attributes/values are added; object classes
* are also added. If 'add' is cleared, attributes/values are modified,
* and 'oc' controls whether or not object classes are added.
*/
LDAPMod **
int i, j, nm;
char *myself = "search2LdapMod";
return (0);
if (mods == 0)
return (0);
int isOc;
/*
* If we're creating an LDAPMod array for an add operation,
* just skip attributes that should be deleted.
*/
continue;
/*
* Skip DN; it's specified separately to ldap_modify()
* and ldap_add(), and mustn't appear among the
*/
continue;
/*
* If modifying, and 'oc' is off, skip object class
* attributes.
*/
continue;
return (0);
}
/* 'mod_type' is the attribute name */
return (0);
}
/*
* numVals < 0 means attribute and all values should
* be deleted.
*/
nm++;
continue;
}
/* objectClass attributes always added */
/*
* mods[]->mod_values is a NULL-terminated array
* of (char *)'s.
*/
return (0);
}
/*
* Just in case the string isn't NUL
* terminated, add one byte to the
* allocated length; am() will initialize
* the buffer to zero.
*/
return (0);
}
}
} else {
return (0);
}
return (0);
}
return (0);
}
}
}
nm++;
}
return (mods);
}
/*
* Remove 'value' from 'val'. If value==0, remove the entire
* __nis_single_value_t array from 'val'.
*/
static void
int i;
if (val == 0)
return;
if (value == 0) {
}
return;
}
continue;
continue;
}
break;
}
}
/*
* Helper function for LdapModify
* When a modify operation fails with an object class violation,
* the most probable reason is that the attributes we're modifying are new,
* and the needed object class are not present. So, try the modify again,
* but add the object classes this time.
*/
static int
{
int msgid;
int lderr;
int stat;
LDAPMessage *msg = 0;
char **referralsp = NULL;
char *myself = "ldapModifyObjectClass";
if (rv == 0)
return (LDAP_NO_MEMORY);
if (rv == 0) {
"%s: addObjectClasses failed for %s",
goto cleanup;
}
/*
* Before adding the object classes whole-sale, try retrieving
* the entry specified by the 'dn'. If it exists, we filter out
* those object classes that already are present in LDAP from our
* update.
*/
oc, 0, 1);
if (ls == 0) {
"%s: Unable to build DN search for \"%s\"",
/* Fall through to try just adding the object classes */
goto addObjectClasses;
}
nv = 0;
if (rvldap == 0) {
"%s: No data for DN search (\"%s\"); LDAP status %d",
/* Fall through to try just adding the object classes */
goto addObjectClasses;
}
/*
* Find the indices of the 'objectClass' attribute
* in 'rvldap' and 'rv'.
*/
ocrvldap = i;
break;
}
}
ocrv = i;
break;
}
}
/*
* Remove those object classes that already exist
* in LDAP (i.e., in 'rvldap') from 'rv'.
*/
}
/*
* If no 'objectClass' values left in 'rv', delete
* 'objectClass' from 'rv'.
*/
}
/*
* 'rv' now contains the update we want to make, with just the
* object class(es) that need to be added. Fall through to the
* actual LDAP modify operation.
*/
if (mods == 0) {
"%s: Unable to create LDAP modify changes with object classes for %s",
goto cleanup;
}
if (msgid != -1) {
if (stat == 0) {
stat = LDAP_TIMEOUT;
} else if (stat == -1) {
} else {
if (stat == LDAP_SUCCESS)
}
} else {
&stat);
}
(void) ldap_msgfree(msg);
referralsp = NULL;
goto cleanup;
if (msgid == -1) {
goto cleanup;
}
if (stat == 0) {
stat = LDAP_TIMEOUT;
} else if (stat == -1) {
} else {
if (stat == LDAP_SUCCESS)
}
}
if (mods != 0)
return (stat);
}
/*
* If 'rv' is NULL, we attempt to delete the entire entry.
*
* The 'objClassAttrs' parameter is needed if the entry must be added
* (i.e., created), or a modify fails with an object class violation.
*
* If 'addFirst' is set, we try an add before a modify; modify before
* add otherwise (ignored if we're deleting).
*/
int
int addFirst) {
LDAPMessage *msg = 0;
char *myself = "ldapModify";
int msgid;
int lderr;
char **referralsp = NULL;
if (dn == 0)
return (LDAP_PARAM_ERROR);
return (stat);
if (rv == 0) {
/* Simple case: if rv == 0, try to delete the entire entry */
if (msgid == -1) {
&stat);
goto cleanup;
}
if (stat == 0) {
stat = LDAP_TIMEOUT;
} else if (stat == -1) {
&stat);
} else {
if (stat == LDAP_SUCCESS)
}
(void) ldap_msgfree(msg);
goto cleanup;
if (msgid == -1) {
goto cleanup;
}
if (stat == 0) {
stat = LDAP_TIMEOUT;
} else if (stat == -1) {
} else {
if (stat == LDAP_SUCCESS)
}
}
/* No such object means someone else has done our job */
if (stat == LDAP_NO_SUCH_OBJECT)
stat = LDAP_SUCCESS;
} else {
if (addFirst) {
if (stat != LDAP_ALREADY_EXISTS)
goto cleanup;
return (stat);
}
/*
* First try the modify without specifying object classes
* (i.e., assume they're already present).
*/
if (mods == 0) {
goto cleanup;
}
if (msgid == -1) {
&stat);
goto cleanup;
}
if (stat == 0) {
stat = LDAP_TIMEOUT;
} else if (stat == -1) {
&stat);
} else {
if (stat == LDAP_SUCCESS)
}
(void) ldap_msgfree(msg);
referralsp = NULL;
goto cleanup;
if (msgid == -1) {
goto cleanup;
}
if (stat == 0) {
stat = LDAP_TIMEOUT;
} else if (stat == -1) {
} else {
if (stat == LDAP_SUCCESS)
}
}
/*
* If the modify failed with an object class violation,
* the most probable reason is that at least on of the
* attributes we're modifying didn't exist before, and
* neither did its object class. So, try the modify again,
* but add the object classes this time.
*/
if (stat == LDAP_OBJECT_CLASS_VIOLATION &&
objClassAttrs != 0) {
mods = 0;
}
if (stat == LDAP_NO_SUCH_ATTRIBUTE) {
/*
* If there was at least one attribute delete, then
* the cause of this error could be that said attribute
* didn't exist in LDAP. So, do things the slow way,
* and try to delete one attribute at a time.
*/
numDelete++;
}
/* If there's just one, we've already tried */
if (numDelete <= 1)
goto cleanup;
/* Make a copy of the rule value */
if (rvt == 0)
goto cleanup;
/*
* Remove all delete attributes from the tmp
* rule value.
*/
}
}
/*
* Now put the attributes back in one by one, and
* invoke ourselves.
*/
continue;
if (st != 0) {
"%s: Error deleting \"%s\" for \"%s\"",
goto cleanup;
}
if (stat != LDAP_SUCCESS &&
stat != LDAP_NO_SUCH_ATTRIBUTE) {
goto cleanup;
}
}
/*
* If we got here, then all attributes that should
* be deleted either have been, or didn't exist. For
* our purposes, the latter is as good as the former.
*/
stat = LDAP_SUCCESS;
}
/*
* Entry doesn't exist, so try an ldap_add(). If the
* ldap_add() also fails, that could be because someone
* else added it between our modify and add operations.
* If so, we consider that foreign add to be
* authoritative (meaning we don't retry our modify).
*
* Also, if all modify operations specified by 'mods'
* are deletes, LDAP_NO_SUCH_OBJECT is a kind of
* success; we certainly don't want to create the
* entry.
*/
int allDelete;
LDAPMod **m;
m++) {
if (((*m)->mod_op & LDAP_MOD_DELETE) == 0)
allDelete = 0;
}
add = 1;
if (allDelete) {
stat = LDAP_SUCCESS;
} else if (objClassAttrs == 0) {
/* Now we need it, so this is fatal */
} else {
}
}
}
if (stat != LDAP_SUCCESS) {
"%s(0x%x (%s), \"%s\") => %d (%s)\n",
"ldap_delete",
}
if (msg != 0)
(void) ldap_msgfree(msg);
return (stat);
}
/*
* Create the entry specified by 'dn' to have the values per 'rv'.
* The 'objClassAttrs' are the extra object classes we need when
* creating an entry.
*
* If 'lc' is non-NULL, we use that connection; otherwise, we find
* our own. CAUTION: This connection will be released on return. Regardless
* of return value, this connection should not subsequently used by the
* caller.
*
* Returns an LDAP status.
*/
int
int stat;
LDAPMessage *msg = 0;
int msgid;
int lderr;
char **referralsp = NULL;
return (LDAP_PARAM_ERROR);
}
if (lc == 0) {
return (stat);
}
if (rv == 0) {
goto cleanup;
}
if (mods == 0) {
goto cleanup;
}
if (msgid == -1) {
goto cleanup;
}
if (stat == 0) {
stat = LDAP_TIMEOUT;
} else if (stat == -1) {
} else {
&referralsp, NULL, 0);
if (stat == LDAP_SUCCESS)
}
referralsp != NULL) {
(void) ldap_msgfree(msg);
goto cleanup;
if (msgid == -1) {
goto cleanup;
}
if (stat == 0) {
stat = LDAP_TIMEOUT;
} else if (stat == -1) {
&stat);
} else {
if (stat == LDAP_SUCCESS)
}
}
if (stat != LDAP_SUCCESS) {
"ldap_add(0x%x (%s), \"%s\") => %d (%s)\n",
}
if (msg != 0)
(void) ldap_msgfree(msg);
return (stat);
}
/*
* Change the entry at 'oldDn' to have the new DN (not RDN) 'dn'.
* Returns an LDAP error status.
*/
int
int stat;
char *rdn;
int msgid;
int lderr;
LDAPMessage *msg = 0;
char **referralsp = NULL;
char *myself = "ldapChangeDN";
return (LDAP_PARAM_ERROR);
return (LDAP_SUCCESS);
return (stat);
if (rdn == 0) {
return (LDAP_NO_MEMORY);
}
/* Compare old and new DN from the end */
/*
* Terminate 'rdn' after this character in order
* to snip off the portion of the new DN that is
* the same as the old DN. What remains in 'rdn'
* is the relative DN.
*/
break;
}
}
if (msgid != -1) {
if (stat == 0) {
stat = LDAP_TIMEOUT;
} else if (stat == -1) {
} else {
if (stat == LDAP_SUCCESS)
}
} else {
&stat);
}
(void) ldap_msgfree(msg);
referralsp = NULL;
goto cleanup;
&msgid);
if (msgid == -1) {
goto cleanup;
}
if (stat == 0) {
stat = LDAP_TIMEOUT;
} else if (stat == -1) {
} else {
if (stat == LDAP_SUCCESS)
}
}
(void) ldap_msgfree(msg);
#if 1
"%s: ldap_modrdn_s(0x%x, %s, %s, 1) => %s",
#endif
if (stat == LDAP_NO_SUCH_OBJECT) {
/*
* Fine from our point of view, since all we want to do
* is to make sure that an update to the new DN doesn't
* leave the old entry around.
*/
stat = LDAP_SUCCESS;
}
return (stat);
}