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