/*
* 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 2015 Gary Mills
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <strings.h>
#include <string.h>
#include <lber.h>
#include <ldap.h>
#include "db_item_c.h"
#include "nisdb_mt.h"
#include "ldap_util.h"
#include "ldap_structs.h"
#include "ldap_val.h"
#include "ldap_ruleval.h"
#include "ldap_op.h"
#include "ldap_nisdbquery.h"
#include "ldap_attr.h"
#include "ldap_xdr.h"
#include "ldap_ldap.h"
item *
if (i == 0)
return (0);
/*
* To this function, a NULL value, or a length less than or equal
* zero means an item with no value. Hence, buildItem(0, 0) is
* _not_ the right way to create index_value == 0 to indicate
* deletion.
*/
i->itemvalue.itemvalue_len = 0;
i->itemvalue.itemvalue_val = 0;
return (i);
}
/*
* NIS+ usually stores the terminating NUL for strings, so we add
* it here just in case. This means we usually waste a byte for
* binary column values...
*/
mlen++;
free(i);
return (0);
}
return (i);
}
void
if (i != 0) {
free(i);
}
}
void
if (qc == 0)
return;
if (doFree)
}
db_query *
if (q == 0)
return (0);
return (q);
}
/*
* Clone a db_query. The 'numComps' parameter can be used to specify
* the number of db_qcomp's to allocate (in the 'components.components_val'
* array), if 'components.components_len' hasn't yet reached its expected
* maximum value.
*/
db_query *
int i;
if (old == 0)
return (0);
if (new == 0)
return (0);
numComps);
return (0);
}
continue;
}
if (it == 0) {
return (0);
}
}
return (new);
}
void
int i;
if (q == 0)
return;
for (i = 0; i < q->components.components_len; i++) {
}
sfree(q);
}
void
int i;
if (q == 0)
return;
for (i = 0; i < numQ; i++)
freeQuery(q[i]);
sfree(q);
}
/*
* Given an array index[0..num-1] of pointers to strings of the form
* "name=value", create the corresponding db_queries. "name=" indicates
* deletion, which results in a db_query component where index_value == 0.
*
* The __nis_table_mapping_t structure is used to translate column
* names to indices.
*
* If 'rvP' is non-NULL, the searchable columns from the 'index'
* entries, and '*rvP' is initialized with the current entry values
* those from existing NIS+ entries.
*/
db_query **
db_query **q;
__nis_buffer_t b = {0, 0};
char *table = 0;
if (rvq == 0)
return (0);
if (numVals == 0)
*numVals = 0;
if (rvP != 0) {
/*
* Try to obtain a copy of the table object, in order to
* determine the searchable columns. A failure isn't
* necessarily fatal; we just try to compose the entire
* LDAP data from the col=val pairs.
*/
if (table == 0) {
"%s: Error converting \"%s\" to FQ object name",
return (0);
}
}
/* Create a rule-value from the col=val pairs */
for (n = 0; n < num; n++) {
char *value;
"%s: no '=' in \"%s\"",
continue;
}
*value = '\0';
value++;
for (a = 0; a < t->numColumns; a++) {
/* Add col=val pair to 'rvq' */
return (0);
}
break;
}
}
if (a >= t->numColumns) {
"%s: Ignoring unknown column \"%s\"",
}
}
/*
* Find out if any of the columns specified via the 'index'
* array are multi-valued.
*/
}
*numVals = 1;
if (rvq->numColumns <= 0) {
*numVals = 0;
return (0);
}
/*
* If any column name was repeated in the col=val pairs (but with
* different values), 'rvq' will have one or more multi-valued
* column values. We now convert those into an array of rule-values
* where every column is single-valued.
*
* Since we want all combinations of column values, the number
* of array elements is the product of all column value counts.
*
* There are four possible combinations of 'index' and NIS+ data:
*
* (1) Only single-valued 'index' columns, and at most one NIS+
* entry, so 'rvq' is complete, and '*numVals' == 1.
*
* (2) Single-valued 'index' columns, but multiple NIS+ entries.
* '*numVals' reflects the number of NIS+ entries, and no
* expansion of 'index' column values to array elements is
* needed.
*
* (3) At least one multi-valued 'index', and multiple NIS+
* entries. We already rejected the NIS+ data for this case
* above, so it is in fact equivalent to case (4).
*
* (4) At least one multi-valued 'index', but at most one NIS+
* entry. This is the case where we must expand the multi-valued
* columns to multiple array elements.
*/
int repeat;
/*
* By using initRuleValue() to create 'rv', and make each
* element a clone of 'rvq', we save a lot of code. The
* down side is that 'rv' only really needs one element
* for each rv[].colVal[].val array, but we know that at
* least one rvq->colVal[].val array has more than one
* element. Hence, making 'rv' a clone of 'rvq' will waste
* memory.
*
* However, we believe this waste is acceptable, because
* we expect that 'niv' will be small. Also, we are executing
* in the context of a utility command, not in a daemon.
*/
if (rv == 0) {
*numVals = 0;
return (0);
}
/*
* For each column value in 'rvq', copy to the appropriate
* place in 'rv', so that the end result is that all
* combinations of values are enumerated, and each
* 'rv[n].colVal[i]' is single-valued.
*
* We do this by traversing the rv[] array 'rvq->numColumns'
* times, where each traversal 'i' works on the values
* for rvq->colVal[i]. A repeat factor 'repeat' starts out
* at '1', and is multiplied by 'rvq->colVal[i].numVals'
* at the end of each traversal. Every value
* rvq->colVal[i].val[j] is repeated 'repeat' times.
*
* This algorithm works by regarding the rv[] array as
* an I-dimensional array (I = rvq->numColumns), where
* each dimension 'i' corresponds to the values for
* rvq->colVal[i]. The I-dimensional array is stored
* in column-major order.
*
* Since the 'rv' elements start out as copies of 'rvq',
* we achieve the "copy" of the 'rvq' column values by
* deleting those we don't want from the 'rv' elements.
*/
int r, k;
for (n = 0, j = 0, r = 0; n < niv; n++) {
/*
* Free all but element 'j' of the
* rv[n].colVal[i].val array.
*/
/* Leave element 'j' in place */
if (k == j)
continue;
value);
}
/* Move element 'j' to zero */
if (j != 0)
/*
* Increment the repeat index 'r'. If >=
* 'repeat', reset 'r' and increment the
* value index 'j'. If 'j' >=
* rvq->colVal[i].numVals, start over on
* the column values for column 'i' (i.e.,
* reset 'j' to zero).
*/
r += 1;
if (r >= repeat) {
r = 0;
j += 1;
j = 0;
}
}
}
rv = 0;
}
if (q == 0) {
return (0);
}
/*
* Create queries from the rvq[] array.
*/
for (a = 0; a < *numVals; a++) {
if (qc != 0) {
for (j = 0; j < t->numColumns; j++) {
t->column[j]) == 0) {
break;
}
}
if (j >= t->numColumns)
continue;
err++;
} else {
"%s: No values for [%d]%s",
err++;
}
nn++;
}
if (err == 0)
}
if (err > 0 || q[a] == 0) {
freeQueries(q, a);
for (a = 0; a < nn; a++)
return (0);
}
}
if (rvP != 0) {
} else {
*numVals = 0;
}
return (q);
}
void
if (q == 0)
return;
/*
* Collect the values, which may be out of order in 'q'.
* Remember the largest index.
*/
for (i = 0; i < q->components.components_len; i++) {
if (ix >= NIS_MAXCOLUMNS ||
(t != 0 && ix >= t->numColumns))
continue;
}
/* Print the values we collected */
for (i = 0; i <= mc; i++) {
}
/* If we printed anything, add a newline */
if (mc >= 0)
}
/*
* Verify that the db_query's 'q' and 'fq' match, in the sense that if
* they both have a value for a certain index, the values are the same.
*/
int
int i, j, match;
if (fq == 0)
return (1);
if (q == 0)
return ((fq == 0) ? 1 : 0);
i++) {
/* Same index ? */
continue;
/*
* If one 'index_value' is NULL, the other one must
* be NULL as well.
*/
index_value == 0)
continue;
else {
match = 0;
break;
}
}
0) {
match = 0;
break;
}
/* Same value lengths ? */
/*
* There's a twist here: the input query
* may well _not_ count a concluding NUL
* in a string value, while the output
* usually will. So, if the difference in
* length is one, and the "extra" byte is
* a zero-valued one, we accept equality.
* 'q' is assumed to be the output, and
* 'fq' the input.
*/
q->components.components_val[i].
match = 0;
break;
}
}
/* Same value ? */
flen) != 0) {
match = 0;
break;
}
}
}
return (match);
}
/*
* Remove those queries in 'q' that don't match t->index.
* Returns a pointer to the filtered array, which could be
* a compacted version of the original, or a new copy; in
* the latter case, the original will have been freed.
*
*/
db_query **
if ((t == 0 && qin == 0) || q == 0 ||
numQueries == 0 || *numQueries <= 0)
return (q);
nq = *numQueries;
if (objAttr != 0)
else
attr = 0;
freeQueries(q, nq);
if (objAttr != 0) {
*objAttr = 0;
}
*numQueries = -1;
return (0);
}
if (t != 0)
retain = verifyIndexMatch(t, q[i], 0, 0, 0);
if (retain) {
if (objAttr != 0)
nn++;
} else {
freeQuery(q[i]);
q[i] = 0;
if (objAttr != 0) {
freeSingleObjAttr((*objAttr)[i]);
(*objAttr)[i] = 0;
}
}
}
/* All q[i]'s are either in 'new', or have been deleted */
free(q);
if (objAttr != 0) {
}
*numQueries = nn;
return (new);
}
db_query **
int *numQueries) {
int r, i, j, ir;
int numItems;
int nq;
char **dn = 0;
int numDN = 0;
return (0);
/* Establish default, per-thread, search base */
for (r = 0, nq = 0; r < t->numRulesFromLDAP; r++) {
if (rval == 0)
continue;
&numItems);
if (litem == 0) {
/* XXX Should this be a fatal error ? */
continue;
}
lval = 0;
for (i = 0; i < numItems; i++) {
mit_nisplus, 0, 0, NULL);
/*
* If the LHS specifies an out-of-context LDAP or
* NIS+ item, we do the update right here. We
* don't add any values to 'lval'; instead, we
* skip to the next item. (However, we still
* get a string representation of the LHS in case
* we need to report an error.)
*/
int stat;
if (dn == 0)
&numDN);
if (stat != LDAP_SUCCESS) {
if (tmpval != 0 &&
"%s: LDAP store \"%s\": %s",
}
continue;
}
}
continue;
}
/*
* We now have a number of possible cases. The notation
* used in the table is:
*
* single A single value (numVals == 1)
* multi[N] N values
* multi[N]/rep M values with repeat == 1
* (M) M resulting db_query's
*
* single (1) (1) (1) (1)
* multi[M] (1) (1) (1) 1+(N-1)/M
* multi[M]/rep (1) (1) (1) 1+(N-1)/M
*
* Of course, we already have 'nq' db_query's from previous
* rules, so the resulting number of queries is max(1,nq)
* times the numbers in the table above.
*/
/* The number of queries resulting from the current rule */
else
nrq = 1;
} else {
nrq = 1;
}
/* Total number of queries after adding the current rule */
if (nq <= 0)
else
"%s: realloc(%d) => NULL",
return (0);
}
}
/*
* remembering that realloc() has done the first 'nq'
* ones.
*
* If there's an error (probably memory allocation), we
* still go through the rest of the array, so that it's
* simple to free the elements when we clean up.
*/
for (j = 0; j < nq; j++) {
t->numColumns);
query[j] != 0)
err++;
attr[j] != 0)
err++;
}
}
if (err > 0) {
return (0);
}
/*
* Special case if nq == 0 (i.e., the first time we
* allocated db_query's). If so, we now allocate empty
* db_qcomp arrays, which simplifies subsequent
* copying of values.
*/
if (nq <= 0) {
if (query[i] == 0) {
err++;
break;
}
err++;
break;
}
}
if (err > 0) {
return (0);
}
}
/* Now we're ready to add the new values */
char *oaName = 0;
int index;
/* Find column index */
index++) {
break;
}
if (index >= t->numColumns) {
/*
* Could be one of the special object
* attributes.
*/
if (oaName == 0)
continue;
}
int k;
/* If we're out of values, repeat last one */
/*
* Step through the query array, adding
* the new value every 'nrq' queries, and
* starting at 'query[j % nrq]'.
*/
int ic, c;
if (oaName != 0) {
&attr[k]);
if (fail) {
err++;
break;
}
continue;
}
/*
* If we've already filled this
* query, the new value is a dup
* which we'll ignore.
*/
if (ic >= t->numColumns)
continue;
/*
* Do we already have a value for
* this 'index' ?
*/
for (c = 0; c < ic; c++) {
if (query[k]->components.
components_val[c].
which_index == index)
break;
}
/* If no previous value, add it */
if (c >= ic) {
int l;
char *v;
query[k]->components.
which_index = index;
l > 0 &&
v[l-1] != '\0' &&
v[l] == '\0')
l++;
query[k]->components.
buildItem(l, v);
if (query[k]->
index_value == 0) {
err++;
break;
}
query[k]->components.
}
}
if (err > 0) {
return (0);
}
}
}
}
if (nq <= 0) {
query = 0;
}
/* Should we filter on index or input query ? */
if (query != 0) {
if (t->index.numIndexes > 0)
else if (qin != 0)
}
if (query != 0 && numQueries != 0)
*numQueries = nq;
if (objAttr != 0)
else
return (query);
}
/*
* Given a table mapping and a rule-value, convert to an array of
* (db_query *), using the fromLDAP ruleset.
*
* On entry, '*numQueries' holds the number of elements in the 'rv'
* array. On exit, it holds the number of (db_query *)'s in the return
* value array.
*/
db_query **
if (t == 0 || rv == 0 || numQueries == 0)
return (0);
nv = *numQueries;
if (nv <= 0)
return (0);
/*
* 'qp' is an array of (db_query **), and we get one element for
* each call to createNisPlusEntry(); i.e., one for each rule-value.
*
* 'nnp[i]' is the count of (db_query *) in each 'qp[i]'.
*/
return (0);
}
/* If we fail, abort (XXX??? or continue ???) */
goto cleanup;
nqp++;
}
/* If we didn't get any (db_query **)'s, return failure */
goto cleanup;
if (q == 0 || attr == 0) {
nq = 0;
goto cleanup;
}
/* Convert 'qp' to an array of (db_query *)'s */
}
*numQueries = nq;
if (objAttr != 0)
else
/* Make sure 'cleanup' doesn't free the db_query pointers */
nqp = 0;
for (i = 0; i < nqp; i++) {
}
return (q);
}
db_query *
int nc, i;
return (0);
/*
* If 'rvP' is non-NULL, build a rule value from the pseudo-
* nis_object in e->en_cols.en_cols_val[0].
*/
if (rv != 0) {
nis_object *o;
o = unmakePseudoEntryObj(e, tobj);
if (o == 0)
return (0);
rvt = addObjAttr2RuleValue(o, 0);
if (rvt == 0)
return (0);
}
/*
* If there are no columns (other than the pseudo-entry object),
* we're done.
*/
if (nc == 0)
return (qbuf);
if (qcbuf == 0) {
if (rvt != 0)
return (0);
}
/*
* Build the db_query, remembering that e->en_cols.en_cols_val[0]
* is the pseudo-nis_object.
*/
for (i = 0; i < nc; i++) {
qcbuf[i].which_index = i;
if (qcbuf[i].index_value == 0) {
if (rvt != 0)
return (0);
}
}
if (rvt != 0) {
}
return (qbuf);
}
/*
* Given an input query 'q', and a db_query work buffer 'qbuf', return
* a pointer to a query with one component corresponding to component
* 'index' in 'q'.
*
* Note that no memory is allocated, and that the returned query has
* pointers into 'q'.
*/
db_query *
qbuf == 0)
return (0);
return (qbuf);
}