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 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27 #include <lber.h>
28 #include <ldap.h>
29 #include <strings.h>
30
31 #include "nisdb_mt.h"
32
33 #include "ldap_util.h"
34 #include "ldap_val.h"
35 #include "ldap_attr.h"
36 #include "ldap_ldap.h"
37 #include "ldap_ruleval.h"
38
39
40 /*
41 * Free an array of 'count' rule-value elements.
42 */
43 void
freeRuleValue(__nis_rule_value_t * rv,int count)44 freeRuleValue(__nis_rule_value_t *rv, int count) {
45 int n, i, j;
46
47 if (rv == 0)
48 return;
49
50 for (n = 0; n < count; n++) {
51
52 if (rv[n].colName != 0) {
53 for (i = 0; i < rv[n].numColumns; i++) {
54 sfree(rv[n].colName[i]);
55 }
56 free(rv[n].colName);
57 }
58 if (rv[n].colVal != 0) {
59 for (i = 0; i < rv[n].numColumns; i++) {
60 for (j = 0; j < rv[n].colVal[i].numVals; j++) {
61 sfree(rv[n].colVal[i].val[j].value);
62 }
63 if (rv[n].colVal[i].numVals > 0)
64 sfree(rv[n].colVal[i].val);
65 }
66 free(rv[n].colVal);
67 }
68
69 if (rv[n].attrName != 0) {
70 for (i = 0; i < rv[n].numAttrs; i++) {
71 sfree(rv[n].attrName[i]);
72 }
73 free(rv[n].attrName);
74 }
75 if (rv[n].attrVal != 0) {
76 for (i = 0; i < rv[n].numAttrs; i++) {
77 for (j = 0; j < rv[n].attrVal[i].numVals;
78 j++) {
79 sfree(rv[n].attrVal[i].val[j].value);
80 }
81 if (rv[n].attrVal[i].numVals > 0)
82 sfree(rv[n].attrVal[i].val);
83 }
84 free(rv[n].attrVal);
85 }
86
87 }
88 sfree(rv);
89 }
90
91 /*
92 * Return an array of 'count' __nis_rule_value_t elements, initialized
93 * to be copies of 'rvIn' if supplied; empty otherwise.
94 */
95 __nis_rule_value_t *
initRuleValue(int count,__nis_rule_value_t * rvIn)96 initRuleValue(int count, __nis_rule_value_t *rvIn) {
97 return (growRuleValue(0, count, 0, rvIn));
98 }
99
100 static const __nis_rule_value_t rvZero = {0};
101
102 /*
103 * Grow 'old' from 'oldCount' to 'newCount' elements, initialize the
104 * new portion to 'rvIn' (empty if not supplied), and return a pointer
105 * to the result. Following a call to this function, the caller must
106 * refer only to the returned array, not to 'old'.
107 */
108 __nis_rule_value_t *
growRuleValue(int oldCount,int newCount,__nis_rule_value_t * old,__nis_rule_value_t * rvIn)109 growRuleValue(int oldCount, int newCount, __nis_rule_value_t *old,
110 __nis_rule_value_t *rvIn) {
111 __nis_rule_value_t *rv;
112 int i, j;
113 char *myself = "growRuleValue";
114
115 if (newCount <= 0 || newCount <= oldCount)
116 return (old);
117
118 if (oldCount <= 0) {
119 oldCount = 0;
120 old = 0;
121 }
122
123 if (rvIn == 0)
124 rvIn = (__nis_rule_value_t *)&rvZero;
125
126 rv = realloc(old, newCount * sizeof (rv[0]));
127 if (rv == 0) {
128 logmsg(MSG_NOMEM, LOG_ERR,
129 "%s: realloc(%d ((%d+%d)*%d)) => 0",
130 myself, (oldCount+newCount) * sizeof (rv[0]),
131 oldCount, newCount, sizeof (rv[0]));
132 freeRuleValue(old, oldCount);
133 return (0);
134 }
135
136 (void) memset(&rv[oldCount], 0, (newCount-oldCount)*sizeof (rv[0]));
137
138 for (i = oldCount; i < newCount; i++) {
139 rv[i].numColumns = rvIn->numColumns;
140 if (rv[i].numColumns > 0) {
141 rv[i].colName = cloneName(rvIn->colName,
142 rv[i].numColumns);
143 rv[i].colVal = cloneValue(rvIn->colVal,
144 rv[i].numColumns);
145 }
146 if (rv[i].numColumns > 0 &&
147 (rv[i].colName == 0 || rv[i].colVal == 0)) {
148 freeRuleValue(rv, i);
149 return (0);
150 }
151 rv[i].numAttrs = rvIn->numAttrs;
152 rv[i].attrName = cloneName(rvIn->attrName, rv[i].numAttrs);
153 rv[i].attrVal = cloneValue(rvIn->attrVal, rv[i].numAttrs);
154 if (rv[i].numAttrs > 0 &&
155 (rv[i].attrName == 0 || rv[i].attrVal == 0)) {
156 freeRuleValue(rv, i);
157 return (0);
158 }
159 }
160
161 return (rv);
162 }
163
164 /*
165 * Merge the source rule-value 's' into the target rule-value 't'.
166 * If successful, unless 's' is a sub-set of 't', 't' will be changed
167 * on exit, and will contain the values from 's' as well.
168 */
169 int
mergeRuleValue(__nis_rule_value_t * t,__nis_rule_value_t * s)170 mergeRuleValue(__nis_rule_value_t *t, __nis_rule_value_t *s) {
171 int i, j;
172
173 if (s == 0)
174 return (0);
175 else if (t == 0)
176 return (-1);
177
178 for (i = 0; i < s->numColumns; i++) {
179 for (j = 0; j < s->colVal[i].numVals; j++) {
180 if (addCol2RuleValue(s->colVal[i].type, s->colName[i],
181 s->colVal[i].val[j].value,
182 s->colVal[i].val[j].length,
183 t))
184 return (-1);
185 }
186 }
187
188 for (i = 0; i < s->numAttrs; i++) {
189 for (j = 0; j < s->attrVal[i].numVals; j++) {
190 if (addAttr2RuleValue(s->attrVal[i].type,
191 s->attrName[i],
192 s->attrVal[i].val[j].value,
193 s->attrVal[i].val[j].length,
194 t))
195 return (-1);
196 }
197 }
198
199 return (0);
200 }
201
202 static int
addVal2RuleValue(char * msg,int caseSens,int snipNul,__nis_value_type_t type,char * name,void * value,int valueLen,int * numP,char *** inNameP,__nis_value_t ** inValP)203 addVal2RuleValue(char *msg, int caseSens, int snipNul, __nis_value_type_t type,
204 char *name, void *value, int valueLen,
205 int *numP, char ***inNameP, __nis_value_t **inValP) {
206 int i, j, copyLen = valueLen;
207 __nis_single_value_t *v;
208 char **inName = *inNameP;
209 __nis_value_t *inVal = *inValP;
210 int num = *numP;
211 int (*comp)(const char *s1, const char *s2);
212 char *myself = "addVal2RuleValue";
213
214 /* Internal function, so assume arguments OK */
215
216 if (msg == 0)
217 msg = myself;
218
219 /* Should we match the 'inName' value case sensitive or not ? */
220 if (caseSens)
221 comp = strcmp;
222 else
223 comp = strcasecmp;
224
225 /*
226 * String-valued NIS+ entries count the concluding NUL in the
227 * length, while LDAP entries don't. In order to support this,
228 * we implement the following for vt_string value types:
229 *
230 * If the last byte of the value isn't a NUL, add one to the
231 * allocated length, so that there always is a NUL after the
232 * value, making it safe to pass to strcmp() etc.
233 *
234 * If 'snipNul' is set (presumably meaning we're inserting a
235 * value derived from a NIS+ entry), and the last byte of the
236 * value already is a NUL, decrement the length to be copied by
237 * one. This (a) doesn't count the NUL in the value length, but
238 * (b) still leaves a NUL following the value.
239 *
240 * In N2L, for all cases we set 'copyLen' to the number of non-0
241 * characters in 'value'.
242 */
243 if (type == vt_string && valueLen > 0) {
244 char *charval = value;
245
246 if (charval[valueLen-1] != '\0')
247 valueLen += 1;
248 else if (yp2ldap || snipNul)
249 copyLen -= 1;
250 } else if (valueLen == 0) {
251 /*
252 * If the 'value' pointer is non-NULL, we create a zero-
253 * length value with one byte allocated. This takes care
254 * of empty strings.
255 */
256 valueLen += 1;
257 }
258
259 /* If we already have values for this attribute, add another one */
260 for (i = 0; i < num; i++) {
261 if ((*comp)(inName[i], name) == 0) {
262
263 /*
264 * Our caller often doesn't know the type of the
265 * value; this happens because the type (vt_string
266 * or vt_ber) is determined by the format in the
267 * rule sets, and we may be invoked as a preparation
268 * for evaluating the rules. Hence, we only use the
269 * supplied 'type' if we need to create a value.
270 * Otherwise, we accept mixed types.
271 *
272 * Strings are OK in any case, since we always make
273 * sure to have a zero byte at the end of any value,
274 * whatever the type.
275 */
276
277 if (inVal[i].numVals < 0) {
278 /*
279 * Used to indicate deletion of attribute,
280 * so we honor that and don't add a value.
281 */
282 return (0);
283 }
284
285 /*
286 * If 'value' is NULL, we should delete, so
287 * remove any existing values, and set the
288 * 'numVals' field to -1.
289 */
290 if (value == 0) {
291 for (j = 0; j < inVal[i].numVals; j++) {
292 sfree(inVal[i].val[j].value);
293 }
294 sfree(inVal[i].val);
295 inVal[i].val = 0;
296 inVal[i].numVals = -1;
297 return (0);
298 }
299
300 /* Is the value a duplicate ? */
301 for (j = 0; j < inVal[i].numVals; j++) {
302 if (copyLen == inVal[i].val[j].length &&
303 memcmp(value, inVal[i].val[j].value,
304 copyLen) == 0) {
305 break;
306 }
307 }
308 if (j < inVal[i].numVals)
309 return (0);
310
311 /* Not a duplicate, so add the name/value pair */
312 v = realloc(inVal[i].val,
313 (inVal[i].numVals+1) *
314 sizeof (inVal[i].val[0]));
315 if (v == 0)
316 return (-1);
317 inVal[i].val = v;
318 v[inVal[i].numVals].length = copyLen;
319 v[inVal[i].numVals].value = am(msg, valueLen);
320 if (v[inVal[i].numVals].value == 0 &&
321 value != 0) {
322 sfree(v);
323 return (-1);
324 }
325 memcpy(v[inVal[i].numVals].value, value, copyLen);
326 inVal[i].numVals++;
327
328 return (0);
329 }
330 }
331
332 /* No previous value for this attribute */
333
334 /*
335 * value == 0 means deletion, in which case we create a
336 * __nis_value_t with the numVals field set to -1.
337 */
338 if (value != 0) {
339 if ((v = am(msg, sizeof (*v))) == 0)
340 return (-1);
341 v->length = copyLen;
342 v->value = am(msg, valueLen);
343 if (v->value == 0 && value != 0) {
344 sfree(v);
345 return (-1);
346 }
347 memcpy(v->value, value, copyLen);
348 }
349
350 inVal = realloc(inVal, (num+1)*sizeof (inVal[0]));
351 if (inVal == 0) {
352 if (value != 0) {
353 sfree(v->value);
354 sfree(v);
355 }
356 return (-1);
357 }
358 *inValP = inVal;
359
360 inName = realloc(inName,
361 (num+1)*sizeof (inName[0]));
362 if (inName == 0 || (inName[num] =
363 sdup(msg, T, name)) == 0) {
364 sfree(v->value);
365 sfree(v);
366 return (-1);
367 }
368 *inNameP = inName;
369
370 inVal[num].type = type;
371 inVal[num].repeat = 0;
372 if (value != 0) {
373 inVal[num].numVals = 1;
374 inVal[num].val = v;
375 } else {
376 inVal[num].numVals = -1;
377 inVal[num].val = 0;
378 }
379
380 *numP += 1;
381
382 return (0);
383 }
384
385 int
addAttr2RuleValue(__nis_value_type_t type,char * name,void * value,int valueLen,__nis_rule_value_t * rv)386 addAttr2RuleValue(__nis_value_type_t type, char *name, void *value,
387 int valueLen, __nis_rule_value_t *rv) {
388 char *myself = "addAttr2RuleValue";
389
390 if (name == 0 || rv == 0)
391 return (-1);
392
393 return (addVal2RuleValue(myself, 0, 0, type, name, value, valueLen,
394 &rv->numAttrs, &rv->attrName, &rv->attrVal));
395 }
396
397 int
addSAttr2RuleValue(char * name,char * value,__nis_rule_value_t * rv)398 addSAttr2RuleValue(char *name, char *value, __nis_rule_value_t *rv) {
399 return (addAttr2RuleValue(vt_string, name, value, slen(value), rv));
400 }
401
402 int
addCol2RuleValue(__nis_value_type_t type,char * name,void * value,int valueLen,__nis_rule_value_t * rv)403 addCol2RuleValue(__nis_value_type_t type, char *name, void *value,
404 int valueLen, __nis_rule_value_t *rv) {
405 char *myself = "addCol2RuleValue";
406
407 if (name == 0 || rv == 0)
408 return (-1);
409
410 return (addVal2RuleValue(myself, 1, 1, type, name, value, valueLen,
411 &rv->numColumns, &rv->colName, &rv->colVal));
412 }
413
414 int
addSCol2RuleValue(char * name,char * value,__nis_rule_value_t * rv)415 addSCol2RuleValue(char *name, char *value, __nis_rule_value_t *rv) {
416 return (addCol2RuleValue(vt_string, name, value, slen(value), rv));
417 }
418
419 /*
420 * Given a table mapping, a NIS+ DB query, and (optionally) an existing
421 * and compatible __nis_rule_value_t, return a new __nis_rule_value_t
422 * with the values from the query added.
423 */
424 __nis_rule_value_t *
buildNisPlusRuleValue(__nis_table_mapping_t * t,db_query * q,__nis_rule_value_t * rv)425 buildNisPlusRuleValue(__nis_table_mapping_t *t, db_query *q,
426 __nis_rule_value_t *rv) {
427 int i;
428 __nis_single_value_t *sv;
429 char *myself = "buildNisPlusRuleValue";
430
431 if (t == 0 || q == 0)
432 return (0);
433
434 rv = initRuleValue(1, rv);
435 if (rv == 0)
436 return (0);
437
438 for (i = 0; i < q->components.components_len; i++) {
439 int ic;
440 int iv, v, dup;
441 int len;
442
443 /* Ignore out-of-range column index */
444 if (q->components.components_val[i].which_index >=
445 t->numColumns)
446 continue;
447
448 /*
449 * Add the query value. A NULL value indicates deletion,
450 * but addCol2RuleValue() takes care of that for us.
451 */
452 if (addCol2RuleValue(vt_string,
453 t->column[q->components.components_val[i].
454 which_index],
455 q->components.components_val[i].index_value->
456 itemvalue.itemvalue_val,
457 q->components.components_val[i].index_value->
458 itemvalue.itemvalue_len, rv) != 0) {
459 freeRuleValue(rv, 1);
460 rv = 0;
461 break;
462 }
463 }
464
465 return (rv);
466 }
467
468
469 /*
470 * Given a LHS rule 'rl', return an array containing the item names,
471 * and the number of elements in the array in '*numItems'.
472 *
473 * If there are 'me_match' __nis_mapping_element_t's, we use the
474 * supplied '*rval' (if any) to derive values for the items in
475 * the 'me_match', and add the values thus derived to '*rval' (in
476 * which case the '*rval' pointer will change; the old '*rval'
477 * is deleted).
478 */
479 __nis_mapping_item_t *
buildLvalue(__nis_mapping_rlhs_t * rl,__nis_value_t ** rval,int * numItems)480 buildLvalue(__nis_mapping_rlhs_t *rl, __nis_value_t **rval, int *numItems) {
481 __nis_value_t *val, *r;
482 __nis_mapping_item_t *item = 0;
483 int i, n, ni = 0, nv = 0;
484 int repeat = 0;
485
486 if (rl == 0)
487 return (0);
488
489 if (rval != 0) {
490 r = *rval;
491 repeat = r->repeat;
492 } else
493 r = 0;
494
495 /* If there is more than one element, we concatenate the items */
496 for (i = 0; i < rl->numElements; i++) {
497 __nis_mapping_element_t *e = &rl->element[i];
498 __nis_mapping_item_t *olditem, *tmpitem = 0;
499 __nis_value_t **tmp;
500
501 switch (e->type) {
502 case me_item:
503 tmpitem = cloneItem(&e->element.item);
504 break;
505 case me_match:
506 /*
507 * Obtain values for the items in the 'me_match'
508 * element.
509 */
510 tmp = matchMappingItem(e->element.match.fmt, r, &nv,
511 0, 0);
512 if (tmp != 0) {
513 freeValue(r, 1);
514 val = 0;
515 for (n = 0; n < nv; n++) {
516 r = concatenateValues(val, tmp[n]);
517 freeValue(val, 1);
518 freeValue(tmp[n], 1);
519 val = r;
520 if (val == 0) {
521 for (n++; n < nv; n++) {
522 freeValue(tmp[n], 1);
523 }
524 break;
525 }
526 }
527 free(tmp);
528 if (rval != 0) {
529 if (repeat && val != 0)
530 val->repeat = repeat;
531 *rval = val;
532 }
533 for (n = 0; n < e->element.match.numItems;
534 n++) {
535 olditem = item;
536 item = concatenateMappingItem(item, ni,
537 &e->element.match.item[n]);
538 freeMappingItem(olditem, ni);
539 if (item == 0) {
540 ni = 0;
541 break;
542 }
543 ni++;
544 }
545 }
546 break;
547 case me_print:
548 case me_split:
549 case me_extract:
550 default:
551 /* These shouldn't show up on the LHS; ignore */
552 break;
553 }
554
555 if (tmpitem != 0) {
556 olditem = item;
557 item = concatenateMappingItem(item, ni, tmpitem);
558 freeMappingItem(olditem, ni);
559 freeMappingItem(tmpitem, 1);
560 ni++;
561 if (item == 0) {
562 ni = 0;
563 break;
564 }
565 }
566 }
567
568 if (numItems != 0)
569 *numItems = ni;
570
571 return (item);
572 }
573
574 __nis_value_t *
buildRvalue(__nis_mapping_rlhs_t * rl,__nis_mapping_item_type_t native,__nis_rule_value_t * rv,int * stat)575 buildRvalue(__nis_mapping_rlhs_t *rl, __nis_mapping_item_type_t native,
576 __nis_rule_value_t *rv, int *stat) {
577 __nis_value_t *val, *vold = 0, *vnew;
578 int i;
579 char *myself = "buildRvalue";
580
581 if (rl == 0 || rl->numElements <= 0) {
582 /*
583 * No RHS indicates deletion, as does a __nis_value_t
584 * with numVals == -1, so we return such a creature.
585 */
586 val = am(myself, sizeof (*val));
587 if (val != 0) {
588 val->type = vt_string;
589 val->numVals = -1;
590 }
591 return (val);
592 }
593
594 /* If there is more than one element, we concatenate the values */
595 for (i = 0; i < rl->numElements; i++) {
596 vnew = getMappingElement(&rl->element[i], native, rv, stat);
597 val = concatenateValues(vold, vnew);
598 freeValue(vnew, 1);
599 freeValue(vold, 1);
600 vold = val;
601 }
602 return (val);
603 }
604
605 /*
606 * Derive values for the LDAP attributes specified by the rule 'r',
607 * and add them to the rule-value 'rv'.
608 *
609 * If 'doAssign' is set, out-of-context assignments are performed,
610 * otherwise not.
611 */
612 __nis_rule_value_t *
addLdapRuleValue(__nis_table_mapping_t * t,__nis_mapping_rule_t * r,__nis_mapping_item_type_t lnative,__nis_mapping_item_type_t rnative,__nis_rule_value_t * rv,int doAssign,int * stat)613 addLdapRuleValue(__nis_table_mapping_t *t,
614 __nis_mapping_rule_t *r,
615 __nis_mapping_item_type_t lnative,
616 __nis_mapping_item_type_t rnative,
617 __nis_rule_value_t *rv,
618 int doAssign, int *stat) {
619 int i, j;
620 char **new;
621 __nis_value_t *rval, *lval;
622 __nis_buffer_t b = {0, 0};
623 __nis_mapping_item_t *litem;
624 int numItems;
625 char **dn = 0;
626 int numDN = 0;
627 char *myself = "addLdapRuleValue";
628
629
630 /* Do we have the required values ? */
631 if (rv == 0)
632 return (0);
633
634 /*
635 * Establish appropriate search base. For rnative == mit_nisplus,
636 * we're deriving LDAP attribute values from NIS+ columns; in other
637 * words, we're writing to LDAP, and should use the write.base value.
638 */
639 __nisdb_get_tsd()->searchBase = (rnative == mit_nisplus) ?
640 t->objectDN->write.base : t->objectDN->read.base;
641
642 /* Set escapeFlag if LHS is "dn" to escape special chars */
643 if (yp2ldap && r->lhs.numElements == 1 &&
644 r->lhs.element->type == me_item &&
645 r->lhs.element->element.item.type == mit_ldap &&
646 strcasecmp(r->lhs.element->element.item.name, "dn") == 0) {
647 __nisdb_get_tsd()->escapeFlag = '1';
648 }
649
650 /* Build the RHS value */
651 rval = buildRvalue(&r->rhs, rnative, rv, stat);
652
653 /* Reset escapeFlag */
654 __nisdb_get_tsd()->escapeFlag = '\0';
655
656 if (rval == 0)
657 return (rv);
658
659 /*
660 * Special case: If we got no value for the RHS (presumably because
661 * we're missing one or more item values), we don't produce an lval.
662 * Note that this isn't the same thing as an empty value, which we
663 * faithfully try to transmit to LDAP.
664 */
665 if (rval->numVals == 1 && rval->val[0].value == 0) {
666 freeValue(rval, 1);
667 return (rv);
668 }
669
670 /* Obtain the LHS item names */
671 litem = buildLvalue(&r->lhs, &rval, &numItems);
672 if (litem == 0) {
673 freeValue(rval, 1);
674 return (rv);
675 }
676
677 /* Get string representations of the LHS item names */
678 lval = 0;
679 for (i = 0; i < numItems; i++) {
680 __nis_value_t *tmpval, *old;
681
682 tmpval = getMappingItem(&litem[i], lnative, 0, 0, NULL);
683
684 /*
685 * If the LHS item is out-of-context, we do the
686 * assignment right here.
687 */
688 if (doAssign && litem[i].type == mit_ldap &&
689 litem[i].searchSpec.triple.scope !=
690 LDAP_SCOPE_UNKNOWN &&
691 slen(litem[i].searchSpec.triple.base) > 0 &&
692 (slen(litem[i].searchSpec.triple.attrs) > 0 ||
693 litem[i].searchSpec.triple.element != 0)) {
694 int stat;
695
696 if (dn == 0)
697 dn = findDNs(myself, rv, 1,
698 t->objectDN->write.base,
699 &numDN);
700
701 stat = storeLDAP(&litem[i], i, numItems, rval,
702 t->objectDN, dn, numDN);
703 if (stat != LDAP_SUCCESS) {
704 char *iname = "<unknown>";
705
706 if (tmpval != 0 &&
707 tmpval->numVals == 1)
708 iname = tmpval->val[0].value;
709 logmsg(MSG_NOTIMECHECK, LOG_ERR,
710 "%s: LDAP store \"%s\": %s",
711 myself, iname,
712 ldap_err2string(stat));
713 }
714
715 freeValue(tmpval, 1);
716 continue;
717 }
718
719 old = lval;
720 lval = concatenateValues(old, tmpval);
721 freeValue(tmpval, 1);
722 freeValue(old, 1);
723 }
724
725 /* Don't need the LHS items themselves anymore */
726 freeMappingItem(litem, numItems);
727
728 /*
729 * If we don't have an 'lval' (probably because all litem[i]:s
730 * were out-of-context assignments), we're done.
731 */
732 if (lval == 0 || lval->numVals <= 0) {
733 freeValue(lval, 1);
734 freeValue(rval, 1);
735 return (rv);
736 }
737
738 for (i = 0, j = 0; i < lval->numVals; i++) {
739 /* Special case: rval->numVals < 0 means deletion */
740 if (rval->numVals < 0) {
741 (void) addAttr2RuleValue(rval->type,
742 lval->val[i].value, 0, 0, rv);
743 continue;
744 }
745 /* If we're out of values, repeat the last one */
746 if (j >= rval->numVals)
747 j = (rval->numVals > 0) ? rval->numVals-1 : 0;
748 for (0; j < rval->numVals; j++) {
749 /*
750 * If this is the 'dn', and the value ends in a
751 * comma, append the appropriate search base.
752 */
753 if (strcasecmp("dn", lval->val[i].value) == 0 &&
754 lastChar(&rval->val[j]) == ',' &&
755 t->objectDN->write.scope !=
756 LDAP_SCOPE_UNKNOWN) {
757 void *nval;
758 int nlen = -1;
759
760 nval = appendString2SingleVal(
761 t->objectDN->write.base, &rval->val[j],
762 &nlen);
763 if (nval != 0 && nlen >= 0) {
764 sfree(rval->val[j].value);
765 rval->val[j].value = nval;
766 rval->val[j].length = nlen;
767 }
768 }
769 (void) addAttr2RuleValue(rval->type,
770 lval->val[i].value, rval->val[j].value,
771 rval->val[j].length, rv);
772 /*
773 * If the lval is multi-valued, go on to the
774 * other values; otherwise, quit (but increment
775 * the 'rval' value index).
776 */
777 if (!lval->repeat) {
778 j++;
779 break;
780 }
781 }
782 }
783
784 /* Clean up */
785 freeValue(lval, 1);
786 freeValue(rval, 1);
787
788 return (rv);
789 }
790
791 /*
792 * Remove the indicated attribute, and any values for it, from the
793 * rule-value.
794 */
795 void
delAttrFromRuleValue(__nis_rule_value_t * rv,char * attrName)796 delAttrFromRuleValue(__nis_rule_value_t *rv, char *attrName) {
797 int i;
798
799 if (rv == 0 || attrName == 0)
800 return;
801
802 for (i = 0; i < rv->numAttrs; i++) {
803 if (strcasecmp(attrName, rv->attrName[i]) == 0) {
804 int j;
805
806 for (j = 0; j < rv->attrVal[i].numVals; j++)
807 sfree(rv->attrVal[i].val[j].value);
808 if (rv->attrVal[i].numVals > 0)
809 sfree(rv->attrVal[i].val);
810
811 sfree(rv->attrName[i]);
812
813 /* Move up the rest of the attribute names/values */
814 for (j = i+1; j < rv->numAttrs; j++) {
815 rv->attrName[j-1] = rv->attrName[j];
816 rv->attrVal[j-1] = rv->attrVal[j];
817 }
818
819 rv->numAttrs -= 1;
820
821 break;
822 }
823 }
824 }
825
826 /*
827 * Remove the indicated column, and any values for it, from the
828 * rule-value.
829 */
830 void
delColFromRuleValue(__nis_rule_value_t * rv,char * colName)831 delColFromRuleValue(__nis_rule_value_t *rv, char *colName) {
832 int i;
833
834 if (rv == 0 || colName == 0)
835 return;
836
837 for (i = 0; i < rv->numColumns; i++) {
838 if (strcmp(colName, rv->colName[i]) == 0) {
839 int j;
840
841 for (j = 0; j < rv->colVal[i].numVals; j++)
842 sfree(rv->colVal[i].val[j].value);
843 if (rv->colVal[i].numVals > 0)
844 sfree(rv->colVal[i].val);
845
846 sfree(rv->colName[i]);
847
848 /* Move up the rest of the column names/values */
849 for (j = i+1; j < rv->numColumns; j++) {
850 rv->colName[j-1] = rv->colName[j];
851 rv->colVal[j-1] = rv->colVal[j];
852 }
853
854 rv->numColumns -= 1;
855
856 break;
857 }
858 }
859 }
860
861 /*
862 * Add the write-mode object classes specified by 'objClassAttrs' to the
863 * rule-value 'rv'.
864 * If there's an error, 'rv' is deleted, and NULL returned.
865 */
866 __nis_rule_value_t *
addObjectClasses(__nis_rule_value_t * rv,char * objClassAttrs)867 addObjectClasses(__nis_rule_value_t *rv, char *objClassAttrs) {
868 char *filter = 0, **fc = 0;
869 int i, nfc = 0;
870
871 /*
872 * Expect to only use this for existing rule-values, so rv == 0 is
873 * an error.
874 */
875 if (rv == 0)
876 return (0);
877
878 /*
879 * If 'objClassAttrs' is NULL, we trivially have nothing to do.
880 * Assume the caller knows what it's doing, and return success.
881 */
882 if (objClassAttrs == 0)
883 return (rv);
884
885 /*
886 * Make an AND-filter of the object classes, and split into
887 * components. (Yes, this is a bit round-about, but leverages
888 * existing functions.)
889 */
890 filter = makeFilter(objClassAttrs);
891 if (filter == 0) {
892 freeRuleValue(rv, 1);
893 return (0);
894 }
895
896 fc = makeFilterComp(filter, &nfc);
897 if (fc == 0 || nfc <= 0) {
898 free(filter);
899 freeRuleValue(rv, 1);
900 return (0);
901 }
902
903 /* Add the objectClass attributes to the rule-value */
904 for (i = 0; i < nfc; i++) {
905 char *name, *value;
906
907 name = fc[i];
908 /* Skip if not of the "name=value" form */
909 if ((value = strchr(name, '=')) == 0)
910 continue;
911
912 *value = '\0';
913 value++;
914
915 /* Skip if the attribute name isn't "objectClass" */
916 if (strcasecmp("objectClass", name) != 0)
917 continue;
918
919 if (addSAttr2RuleValue(name, value, rv) != 0) {
920 free(filter);
921 freeFilterComp(fc, nfc);
922 freeRuleValue(rv, 1);
923 return (0);
924 }
925 }
926
927 free(filter);
928 freeFilterComp(fc, nfc);
929
930 return (rv);
931 }
932
933
934 static char *
valString(__nis_value_t * val)935 valString(__nis_value_t *val) {
936 int i;
937
938 if (val == 0 || val->type != vt_string)
939 return (0);
940
941 for (i = 0; i < val->numVals; i++) {
942 /* Look for a non-NULL, non-zero length value */
943 if (val->val[i].value != 0 && val->val[i].length > 0) {
944 char *v = val->val[i].value;
945
946 /*
947 * Check that there's a NUL at the end. True,
948 * if there isn't, we may be looking beyond
949 * allocated memory. However, we would have done
950 * so in any case when the supposed string was
951 * traversed (printed, etc.), very possibly by
952 * a lot more than one byte. So, it's better to
953 * take a small risk here than a large one later.
954 */
955 if (v[val->val[i].length-1] == '\0' ||
956 v[val->val[i].length] == '\0')
957 return (v);
958 }
959 }
960
961 return (0);
962 }
963
964 char *
findVal(char * name,__nis_rule_value_t * rv,__nis_mapping_item_type_t type)965 findVal(char *name, __nis_rule_value_t *rv, __nis_mapping_item_type_t type) {
966 int i;
967
968 if (type == mit_nisplus) {
969 for (i = 0; i < rv->numColumns; i++) {
970 if (rv->colName[i] == 0)
971 continue;
972 if (strcmp(name, rv->colName[i]) == 0) {
973 return (valString(&rv->colVal[i]));
974 }
975 }
976 } else if (type == mit_ldap) {
977 for (i = 0; i < rv->numAttrs; i++) {
978 if (rv->attrName[i] == 0)
979 continue;
980 if (strcasecmp(name, rv->attrName[i]) == 0) {
981 return (valString(&rv->attrVal[i]));
982 }
983 }
984 }
985
986 return (0);
987 }
988
989 static char *norv = "<NIL>";
990 static char *unknown = "<unknown>";
991
992 /*
993 * Attempt to derive a string identifying the rule-value 'rv'. The
994 * returned string is a pointer, either into 'rv', or to static
995 * storage, and must not be freed.
996 */
997 char *
rvId(__nis_rule_value_t * rv,__nis_mapping_item_type_t type)998 rvId(__nis_rule_value_t *rv, __nis_mapping_item_type_t type) {
999 char *v;
1000
1001 if (rv == 0)
1002 return (norv);
1003
1004 if (rv->numColumns > 0 && type == mit_nisplus) {
1005 /*
1006 * Look for a column called "cname" or "name".
1007 * If that fails, try "key" or "alias".
1008 */
1009 if ((v = findVal("cname", rv, type)) != 0)
1010 return (v);
1011 else if ((v = findVal("name", rv, type)) != 0)
1012 return (v);
1013 else if ((v = findVal("key", rv, type)) != 0)
1014 return (v);
1015 else if ((v = findVal("alias", rv, type)) != 0)
1016 return (v);
1017 } else if (rv->numAttrs > 0 && type == mit_ldap) {
1018 /*
1019 * Look for "dn", or "cn".
1020 */
1021 if ((v = findVal("dn", rv, type)) != 0)
1022 return (v);
1023 else if ((v = findVal("cn", rv, type)) != 0)
1024 return (v);
1025 }
1026
1027 return (unknown);
1028 }
1029
1030 /*
1031 * Merge the rule-values with the same DN into one. Each rule-value
1032 * in the returned array will have unique 'dn'. On entry, *numVals
1033 * contains the number of rule-values in 'rv'. On exit, it contains
1034 * the number of rule-values in the returned array or -1 on error.
1035 */
1036 __nis_rule_value_t *
mergeRuleValueWithSameDN(__nis_rule_value_t * rv,int * numVals)1037 mergeRuleValueWithSameDN(__nis_rule_value_t *rv, int *numVals) {
1038 __nis_rule_value_t *rvq = 0;
1039 char *dn, *odn;
1040 int count = 0;
1041 int i, j;
1042
1043 if (numVals == 0)
1044 return (0);
1045
1046 for (i = 0; i < *numVals; i++) {
1047 if ((dn = findVal("dn", &rv[i], mit_ldap)) != 0) {
1048 for (j = 0; j < count; j++) {
1049 if ((odn = findVal("dn", &rvq[j],
1050 mit_ldap)) != 0) {
1051 /* case sensitive compare */
1052 if (strcmp(dn, odn) != 0)
1053 continue;
1054 if (mergeRuleValue(&rvq[j],
1055 &rv[i]) == -1) {
1056 freeRuleValue(rvq, count);
1057 *numVals = -1;
1058 return (0);
1059 }
1060 break;
1061 } else {
1062 freeRuleValue(rvq, count);
1063 *numVals = -1;
1064 return (0);
1065 }
1066 }
1067 /* if no match, then add it to the rulevalue array */
1068 if (j == count) {
1069 rvq = growRuleValue(count, count + 1, rvq,
1070 &rv[i]);
1071 if (rvq == 0) {
1072 *numVals = -1;
1073 return (0);
1074 }
1075 count++;
1076 }
1077 }
1078 }
1079
1080 *numVals = count;
1081 return (rvq);
1082 }
1083