/*
* 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
*/
/*
*/
#include <strings.h>
#include <errno.h>
#include <stdio.h>
#include "ldap_util.h"
#include "ldap_attr.h"
#include "ldap_ruleval.h"
#include "ldap_op.h"
#include "ldap_map.h"
#include "ldap_glob.h"
#include "ldap_xdr.h"
#include "ldap_val.h"
/* From yptol/dit_access_utils.h */
extern int yp2ldap;
int
char **col;
if (t == 0)
return (0);
t->objType = NIS_BOGUS_OBJ;
t->obj = 0;
/*
* If it's a table object, but there are no translation rules,
* this mapping is for the table object itself. In that case,
* we throw away the column names (if any).
*/
t->numRulesToLDAP == 0) {
for (i = 0; i < t->numColumns; i++)
t->column = 0;
t->numColumns = 0;
noc = 0;
}
/*
* Verify that all column names found by the parser
* are present in the actual column list.
*/
if (verbose) {
int found = 0;
if (col[i] == 0)
continue;
/* Skip the 'zo_*' special column names */
if (isObjAttrString(col[i]))
continue;
for (j = 0; j < t->numColumns; j++) {
noc++;
found = 1;
break;
}
}
if (!found) {
"%s: No column \"%s\" in \"%s\"",
}
}
}
/* Remove any setup by the parser */
for (i = 0; i < nic; i++) {
}
return (0);
}
void
if (attr == 0)
return;
}
void
int i;
if (attr == 0)
return;
for (i = 0; i < numAttr; i++) {
freeSingleObjAttr(attr[i]);
}
}
if (old == 0)
return (0);
if (new == 0)
return (0);
goto cleanup;
goto cleanup;
goto cleanup;
return (new);
return (0);
}
/*
* Obtain NIS+ entries (in the form of db_query's) from the supplied table
* mapping and db_query.
*
* If 'qin' is NULL, enumeration is desired.
*
* On exit, '*numQueries' contains the number of (db_query *)'s in the
* return array, '*ldapStat' the LDAP operation status, and '*objAttr'
* a pointer to an array (of '*numQueries elements) of object attributes
* (zo_owner, etc.). If no object attributes were retrieved, '*objAttr'
* is NULL; any and all of the (*objAttr)[i]'s may be NULL.
*/
db_query **
db_query **q;
int stat;
if (ldapStat == 0)
if (t == 0 || numQueries == 0) {
return (0);
}
/* Select the correct table mapping(s) */
if (tp == 0 || numMatches <= 0) {
/*
* Not really an error; just no matching mapping
* for the query.
*/
*ldapStat = LDAP_SUCCESS;
return (0);
}
q = 0;
attr = 0;
/* For each mapping */
for (numVals = 0, n = 0; n < numMatches; n++) {
t = tp[n];
if (qin != 0) {
if (rv != 0) {
/*
* Depending on the value of res, we shall
* proceed to next table mapping.
*/
}
else
ls = 0;
} else {
/* Build enumeration request */
rv = 0;
}
if (ls == 0) {
/*
* if the res is NP_LDAP_RULES_NO_VALUE, that means we
* have enough NIS+ columns for the rules to produce
* values, but none of them did, so continue to the
* next table mapping. Otherwise do cleanup and return
* error.
*/
if (res == NP_LDAP_RULES_NO_VALUE)
continue;
for (i = 0; i < numVals; i++)
freeQuery(q[i]);
sfree(q);
return (0);
}
/* Query LDAP */
/*
* If qin != 0, then we need to make sure that the
* LDAP search is filtered so that only entries that
* are compatible with 'qin' are retained. This will
* happen automatically if we do a DN search (in which
* case, no need to filter on 'qin').
*/
filterOnQin = 0;
else
filterOnQin = 1;
/* Convert rule-values to db_query's */
if (qt != 0 && q == 0) {
q = qt;
} else if (qt != 0) {
/* Extend the 'q' array */
/* ... and the 'attr' array */
"%s: realloc(%d) => NULL",
for (i = 0; i < numVals; i++)
freeQuery(q[i]);
for (i = 0; i < nqt; i++)
sfree(q);
return (0);
}
q = tmp;
/* Add the results for this 't' */
}
}
}
*numQueries = numVals;
if (objAttr != 0)
else
return (q);
}
/*
* Add the object attributes (zo_owner, etc.) to the rule-value 'rv'.
* Returns a pointer to the (possibly newly allocated) rule-value,
* or NULL in case of failure. If not returning 'rvIn', the latter
* will have been freed.
*/
if (obj == 0)
return (0);
if (rvIn != 0) {
} else {
if (rv == 0)
return (0);
}
return (0);
}
}
return (0);
}
}
return (0);
}
}
return (0);
}
return (0);
}
return (rv);
}
/*
* Returns a pointer to (NOT a copy of) the value for the specified
* column 'col' in the rule-value 'rv'.
*/
int i;
return (0);
for (i = 0; i < rv->numColumns; i++) {
}
return (0);
}
/*
* Return the NIS+ object attributes (if any) in the rule-value 'rv'.
*/
return (0);
return (0);
}
}
return (0);
}
}
return (0);
}
}
return (0);
}
}
return (0);
}
}
return (attr);
}
/*
* If the supplied string is one of the object attributes, return one.
* Otherwise, return zero.
*/
int
if (str == 0)
return (0);
return (1);
else
return (0);
}
/*
* If the supplied value is one of the object attribute strings, return
* a pointer to the string. Otherwise, return NULL.
*/
char *
return (0);
else
return (0);
}
int
__nis_obj_attr_t **objAttr) {
return (-1);
if (*objAttr != 0) {
} else {
if (attr == 0)
return (-2);
}
return (-11);
}
return (-12);
}
return (-13);
}
return (-14);
}
return (-15);
}
}
return (0);
}
/*
* Return a DN and rule-value for the supplied mapping, db_query's, and
* input rule-value. This function only works on a single mapping. See
* mapToLDAP() below for a description of the action depending on the
* values of 'old' and 'new'.
*
* If both 'old' and 'new' are supplied, and the modify would result
* in a change to the DN, '*oldDN' will contain the old DN. Otherwise
* (and normally), '*oldDN' will be NULL.
*/
char *
char **oldDnP) {
return (0);
/*
* If entry should be deleted, we look at the delete
* policy in the table mapping. Should it specify a
* rule set, we use that rule set to build a rule-
* value, and the delete actually becomes a modify
* operation.
*/
/*
* The functions that build a rule-value from a
* rule set expect a __nis_table_mapping_t, but the
* rule set in the __nis_object_dn_t isn't of that
* form. So, build a pseudo-__nis_table_mapping_t that
* borrows heavily from 't'.
*/
del = *t;
/*
* Do a modify with the pseudo-table
* mapping, and the 'old' db_query
* supplying input to the delete rule
* set.
*/
t = &del;
/* Nothing to do here; all handled below */
return (0);
} else {
"%s: Invalid delete disposition %d for \"%s\"",
return (0);
}
}
/* Make a copy of the input rule-value */
if (rvIn != 0) {
if (rv == 0)
return (0);
} else {
rv = 0;
}
/* First get a rule-value from the supplied NIS+ entry. */
if (rv == 0) {
"%s: No in-query rule-value derived for \"%s\"",
return (0);
}
/*
* Create a request (really only care about the DN) from the
* supplied NIS+ entry data.
*/
"%s: Unable to create LDAP request for %s: %s",
return (0);
}
if (new != 0) {
/*
* Create a rule-value from the new NIS+ entry.
* Don't want to mix in the rule-value derived
* from 'old', so delete it. However, we still
* want the owner, group, etc., from 'rvIn'.
*/
if (old != 0) {
if (rvIn != 0) {
if (rv == 0) {
return (0);
}
} else {
rv = 0;
}
}
if (rv == 0) {
"%s: No new rule-value derived for \"%s: %s\"",
return (0);
}
/*
* Check if the proposed modification would result in a
* a change to the DN.
*/
if (old != 0) {
dn = 0;
"%s: Unable to create new DN for \"%s: %s\"",
return (0);
}
oldDn = 0;
}
}
}
if (oldDnP != 0)
return (dn);
}
/*
* Since the DN hash list is an automatic variable, there's no need for
* locking, and we remove the locking overhead by using the libnsl
* hash functions.
*/
typedef struct {
int index;
char *oldDn;
} __dn_item_t;
/*
* Update LDAP per the supplied table mapping and db_query's.
*
* 'nq' is the number of elements in the 'old', 'new', and 'rvIn'
* arrays. mapToLDAP() generally performs one update for each
* element; however, if one or more of the individual queries
* produce the same DN, they're merged into a single update.
*
* There are four cases, depending on the values of 'old[iq]' and
* 'new[iq]':
*
* (1) old[iq] == 0 && new[iq] == 0
* No action; skip to next query
*
* (2) old[iq] == 0 && new[iq] != 0
* Attempt to use the 'new' db_query to get a DN, and try to create
* the corresponding LDAP entry.
*
* (3) old[iq] != 0 && new[iq] == 0
* Use the 'old' db_query to get a DN, and try to delete the LDAP
* entry per the table mapping.
*
* (4) old[iq] != 0 && new[iq] != 0
* Use the 'old' db_query to get a DN, and update (possibly create)
* the corresponding LDAP entry per the 'new' db_query.
*
* If 'rvIn' is non-NULL, it is expected to contain the object attributes
* (zo_owner, etc.) to be written to LDAP. 'rvIn' is an array with 'nq'
* elements.
*
* If 'firstOnly' is set, only the first old[iq]/new[iq] pair is used
* to perform the actual update. Any additional queries specified will
* have their values folded in, but are not used to derive update targets.
* This mode is inteded to support the case where multiple NIS+ entries
* map to one and the same LDAP entry. Note that 'rvIn' must still be
* an array of 'nq' elements, though if 'firstOnly' is set, it should be
* OK to leave all but 'rvIn[0]' empty.
*
* 'dbId' is used to further narow down the selection of mapping candidates
* to those matching the 'dbId' value.
*/
int
return (LDAP_PARAM_ERROR);
/* Determine maximum number of table mapping matches */
if (nq == 1) {
dbId, &maxMatches);
} else {
}
/*
* If no matching mapping, we're not mapping to LDAP in this
* particular case.
*/
if (tp == 0 || maxMatches == 0) {
return (LDAP_SUCCESS);
}
/*
* Allocate the 'rv', 'dn', and 'tpa' arrays. Worst case is that
* we need nq * maxMatches elements in each array. However, if
* 'firstOnly' is set, we only need one element per matching
* mapping in each.
*/
return (LDAP_NO_MEMORY);
}
/* Unless nq == 1, we don't need the 'tp' value */
if (nq != 1)
#ifdef NISDB_LDAP_DEBUG
#else
#endif /* NISDB_LDAP_DEBUG */
"%s: %s: %d * %d potential updates",
/*
* Create DNs, column and attribute values, and merge duplicate DNs.
*/
int idx;
continue;
/*
* Select matching table mappings; if nq == 1, we've already
* got the 'tp' array from above. We expect this to be the
* most common case, so it's worth special treatment.
*/
if (nq != 1)
dbId, &numMatches);
if (tp == 0)
continue;
else if (numMatches <= 0) {
continue;
}
for (n = 0; n < numMatches; n++) {
if (tp[n] == 0)
continue;
if (dnt == 0)
continue;
if (rvt == 0) {
#ifdef NISDB_LDAP_DEBUG
abort();
#else
continue;
#endif /* NISDB_LDAP_DEBUG */
}
/*
* Create a request to get a rule-value with
* NIS+ data translated to LDAP equivalents.
*/
if (ls == 0) {
if (ret == LDAP_SUCCESS)
"%s: Unable to map to LDAP attrs for %s:dn=%s",
continue;
}
/*
* If the DN is the same as one we already know
* about, merge the rule-values.
*/
if (dni != 0) {
} else {
"%s: Skipping update for dn=\"%s\"",
dnt = 0;
}
if (dnt != 0) {
rnq++;
} else {
rvt = 0;
}
} else if (dnt != 0) {
}
}
}
#ifdef NISDB_LDAP_DEBUG
#else
#endif /* NISDB_LDAP_DEBUG */
"%s: %s: %d update%s requested",
/* Perform the updates */
int delPerDbId;
if (dn[i] == 0)
continue;
#ifdef NISDB_LDAP_DEBUG
"%s: %s %s:dn=%s",
"modify" : "delete",
#endif /* NISDB_LDAP_DEBUG */
/*
* however, if the update changes the DN, make
* that change.
*/
LDAP_SUCCESS) {
int addFirst;
new[i/maxMatches] != 0 &&
!delPerDbId);
addFirst);
}
} else {
/* Try to delete the specified DN */
r = ldapModify(dn[i], 0,
}
if (r == LDAP_SUCCESS) {
rnq++;
} else {
if (ret == LDAP_SUCCESS)
ret = r;
"%s: LDAP %s request error %d for %s:dn=%s",
"modify" : "delete",
}
dn[i] = 0;
rv[i] = 0;
}
#ifdef NISDB_LDAP_DEBUG
#else
#endif /* NISDB_LDAP_DEBUG */
"%s: %s: %d update%s performed",
return (ret);
}
/*
* In nis2ldap, if 'name' is provided then check if its value in 'val'
* matches the selector index. If 'name' is NULL, then check if rule-value 'rv'
* matches the index.
* To match the selector index, all fieldspecs in the indexlist should match
* (AND). In nis2ldap, an exception is, if there are multiple fieldspecs with
* the same fieldname then only one of them needs to match (OR).
* Example:
* Indexlist = [host="H*", host="I*", user="U*", domain="D*"]
* Then,
* host = "H1", user="U1", domain="D1" ==> pass
* host = "I1", user="U1", domain="D1" ==> pass
* host = "X1", user="U1", domain="D1" ==> fail
* host = "H1", user="X1", domain="D1" ==> fail
* host = "H1", user="U1" ==> fail
*
* Return 1 in case of a match, 0 otherwise.
*/
int
/*
* The pass and fail arrays are used by N2L to keep track of
* index matches. This saves us from having matches in a
* nested loop to decide OR or AND.
*/
if (x == 0)
return (0);
/* Trivial match */
return (1);
if (yp2ldap) {
return (0);
x->index.numIndexes * sizeof (char *)))) {
return (0);
}
}
/* Check each index */
for (i = 0; i < x->index.numIndexes; i++) {
int len = 0;
char *value = 0;
/* Skip NULL index names */
continue;
/* Check N2L values */
if (yp2ldap) {
if (name) {
else
continue;
} else if (rv) {
== 0)
continue;
mit_nis);
}
value))
else
continue;
}
/* Is the index name a known column ? */
for (j = 0; j < x->numColumns; j++) {
/*
* Do we have a value for the column ?
*/
for (k = 0; k < q->components.components_len;
k++) {
if (q->components.components_val[k].
which_index == j) {
value = q->components.
components_val[k].
len = q->components.
components_val[k].
break;
}
}
if (value != 0)
break;
}
}
/*
* If we found a value, check if it matches the
* format. If no value found or no match, this
* mapping is _not_ an alternative. Otherwise,
* we continue checking any other indexes.
*/
if (value == 0 ||
value)) {
match = 0;
break;
}
}
if (yp2ldap) {
for (i = 0; i < ppos; i++) {
break;
}
if (i == ppos) {
match = 0;
break;
}
}
}
return (match);
}
/*
* Return all table mappings that match the column values in 'q'.
* If there's no match, return those alternative mappings that don't
* have an index; if no such mapping exists, return NULL.
*
* If 'wantWrite' is set, we want mappings for writing (i.e., data
* to LDAP); otherwise, we want mappings for reading.
*
* If 'wantObj' is set, we want object mappings only (i.e., _not_
* those used to map entries in tables).
*
* If 'dbId' is non-NULL, we select mappings with a matching dbId field.
*/
int *numMatches) {
if (numMatches == 0)
numMatches = &nm;
/*
* Count the number of possible mappings, so that we can
* allocate the 'tp' array up front.
*/
if (numap == 0) {
*numMatches = 0;
return (0);
}
if (tp == 0) {
*numMatches = -1;
return (0);
}
/*
* Special cases:
*
* q == 0 trivially matches any 't' of the correct object type
*
* wantObj != 0 means we ignore 'q'
*/
if (q == 0 || wantObj) {
if (x->objectDN == 0)
continue;
if (wantWrite) {
continue;
} else {
continue;
}
if (wantObj) {
if (x->numColumns > 0)
continue;
} else {
if (x->numColumns <= 0)
continue;
}
continue;
nm++;
}
*numMatches = nm;
if (nm == 0) {
tp = 0;
}
return (tp);
}
/* Scan all mappings, and collect candidates */
if (x->objectDN == 0)
continue;
if (wantWrite) {
continue;
} else {
continue;
}
if (x->numColumns <= 0)
continue;
continue;
/*
* It's a match if: there are no indexes, or we actually
* match the query with the indexes.
*/
if (x->index.numIndexes <= 0 ||
verifyIndexMatch(x, q, 0, 0, 0)) {
nm++;
}
}
if (nm == 0) {
tp = 0;
}
*numMatches = nm;
return (tp);
}
/*
* Return 1 if there's an indexed mapping, 0 otherwise.
*/
int
for (x = t; x != 0; x = x->next) {
if (x->index.numIndexes > 0)
return (1);
}
return (0);
}
/*
* Given an input string 'attrs' of the form "attr1=val1,attr2=val2,...",
* or a filter, return the value associated with the attribute 'attrName'.
* If no instance of 'attrName' is found, return 'default'. In all cases,
* the return value is a copy, and must be freed by the caller.
*
* Of course, return NULL in case of failure.
*/
static char *
int i, nfc;
return (0);
if (msg == 0)
nfc > 0) {
for (i = 0; i < nfc; i++) {
/* Skip if not of attr=value form */
continue;
*value = '\0';
value++;
break;
}
}
}
if (val != 0)
return (val);
}
/*
* Copy an XDR:ed version of the NIS+ object 'o' (or the one indicated
* by 't->objName' if 'o' is NULL) to the place indicated by
* 't->objectDN->write'. Return an appropriate LDAP status code.
*/
int
char *objName;
void *buf;
if (t == 0)
return (LDAP_PARAM_ERROR);
#ifdef NISDB_LDAP_DEBUG
#else
#endif /* NISDB_LDAP_DEBUG */
if (tp == 0 || numMatches <= 0) {
#ifdef NISDB_LDAP_DEBUG
#else
#endif /* NISDB_LDAP_DEBUG */
return (LDAP_SUCCESS);
}
for (n = 0; n < numMatches; n++) {
t = tp[n];
if (o == 0) {
return (LDAP_OPERATIONS_ERROR);
}
if (buf == 0) {
return (LDAP_OPERATIONS_ERROR);
}
/*
* Prepare to build a rule-value containing the XDR:ed
* object
*/
if (attrName != 0)
"nisObject",
attrName[0] == 0) {
return (LDAP_NO_MEMORY);
}
/* 'vt_ber' just means "not a NUL-terminated string" */
/*
* The 'write.base' is the actual DN of the entry (and the
* scope had better be 'base', but we don't check that).
*/
#ifdef NISDB_LDAP_DEBUG
#else
#endif /* NISDB_LDAP_DEBUG */
if (stat != LDAP_SUCCESS)
break;
}
return (stat);
}
/*
* Retrieve a copy of the 't->objName' object from LDAP, where it's
* stored in XDR:ed form in the place indicated by 't->objectDN->read'.
* Un-XDR the object, and return a pointer to it in '*obj'; it's the
* responsibility of the caller to free the object when it's no
* longer needed.
*
* Returns an appropriate LDAP status.
*/
int
nis_object *o;
void *buf;
int n, numMatches;
if (t == 0)
return (LDAP_PARAM_ERROR);
/*
* If there's nowhere to store the result, we might as
* well pretend all went well, and return right away.
*/
if (obj == 0)
return (LDAP_SUCCESS);
/* Prepare for the worst */
*obj = 0;
#ifdef NISDB_LDAP_DEBUG
#else
#endif /* NISDB_LDAP_DEBUG */
if (tp == 0 || numMatches <= 0) {
#ifdef NISDB_LDAP_DEBUG
#else
#endif /* NISDB_LDAP_DEBUG */
return (LDAP_SUCCESS);
}
for (n = 0; n < numMatches; n++) {
t = tp[n];
nfc <= 0) {
}
/* Don't need the filter, just the components */
/*
* Look for a "nisObject" attribute, and (if found) copy
* the value to attrs[0]. Also remove the "nisObject"
* attribute and value from the filter components.
*/
if (attrs[0] == 0) {
return (LDAP_NO_MEMORY);
}
attrs[1] = 0;
for (i = 0; i < nfc; i++) {
int compare;
/* Skip if not of attr=value form */
continue;
/* Temporarily overWrite the '=' with a '\0' */
*value = '\0';
/* Compare with our target attribute name */
/* Put back the '=' */
*value = '=';
/* Is it the name we're looking for ? */
if (compare == 0) {
if (attrs[0] == 0) {
return (LDAP_NO_MEMORY);
}
if (i < nfc-1)
nfc--;
break;
}
}
if (ls == 0) {
return (LDAP_OPERATIONS_ERROR);
}
nrv = 0;
if (rv == 0) {
return (stat);
}
continue;
break;
}
}
}
if (buf != 0) {
if (o == 0) {
return (LDAP_OPERATIONS_ERROR);
}
stat = LDAP_SUCCESS;
*obj = o;
} else {
}
#ifdef NISDB_LDAP_DEBUG
#else
#endif /* NISDB_LDAP_DEBUG */
if (stat != LDAP_SUCCESS)
break;
}
return (stat);
}
int
if (t == 0)
return (LDAP_PARAM_ERROR);
#ifdef NISDB_LDAP_DEBUG
#else
#endif /* NISDB_LDAP_DEBUG */
if (tp == 0 || numMatches <= 0) {
#ifdef NISDB_LDAP_DEBUG
#else
#endif /* NISDB_LDAP_DEBUG */
return (LDAP_SUCCESS);
}
for (n = 0; n < numMatches; n++) {
t = tp[n];
/* Delete entire entry */
/*
* Delete the attribute holding the object.
* First, determine what that attribute is called.
*/
char *attrName =
"nisObject",
"nisObject",
if (attrName == 0) {
return (LDAP_NO_MEMORY);
}
/*
* Build a __nis_value_t with 'numVals' < 0 to
* indicate deletion.
*/
/*
* Build a rule-value with the name we determined
* above, and the deletion value.
*/
/* Nothing to do, so we're trivially successful */
stat = LDAP_SUCCESS;
} else {
}
#ifdef NISDB_LDAP_DEBUG
#else
#endif /* NISDB_LDAP_DEBUG */
/* If there were no such object, we've trivially succeeded */
if (stat == LDAP_NO_SUCH_OBJECT)
stat = LDAP_SUCCESS;
if (stat != LDAP_SUCCESS)
break;
}
return (stat);
}