xref: /illumos-gate/usr/src/lib/libnisdb/yptol/dit_access_utils.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * DESCRIPTION:	Contains dit_access interface support functions.
31  */
32 #include <sys/systeminfo.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <sys/systeminfo.h>
38 #include <unistd.h>
39 #include <stdlib.h>
40 #include <syslog.h>
41 #include <ndbm.h>
42 #include <strings.h>
43 #include <errno.h>
44 #include "../ldap_util.h"
45 #include "../ldap_map.h"
46 #include "../ldap_parse.h"
47 #include "../ldap_structs.h"
48 #include "../ldap_val.h"
49 #include "../ldap_ruleval.h"
50 #include "../ldap_op.h"
51 #include "../ldap_attr.h"
52 #include "../ldap_nisdbquery.h"
53 #include "../nisdb_mt.h"
54 #include "shim.h"
55 #include "yptol.h"
56 #include "dit_access_utils.h"
57 
58 /*
59  * Returns 'map,domain.'
60  */
61 char *
62 getFullMapName(char *map, char *domain) {
63 	char	*myself = "getFullMapName";
64 	char	*objPath;
65 	if (map == 0 || domain == 0) {
66 		return (0);
67 	}
68 	objPath =  scat(myself, T, scat(myself, F, map, ","),
69 		scat(myself, F, domain, "."));
70 
71 	return (objPath);
72 }
73 
74 /*
75  * Convert string to __nis_value_t
76  */
77 __nis_value_t *stringToValue(char *dptr, int dsize) {
78 	char		*myself = "stringToValue";
79 	char		*emptystr = "";
80 	__nis_value_t	*val;
81 
82 	if ((val = am(myself, sizeof (*val))) == 0) {
83 		return (0);
84 	}
85 
86 	val->type = vt_string;
87 	val->repeat = 0;
88 	val->numVals = 1;
89 	if ((val->val = am(myself, sizeof (val->val[0]))) == 0) {
90 		sfree(val);
91 		return (0);
92 	}
93 
94 	/*
95 	 * Null strings or strings with length 0 are treated
96 	 * as empty strings with length 1
97 	 */
98 	if (dptr == 0 || dsize <= 0) {
99 		dptr = emptystr;
100 		dsize = 1;
101 	}
102 
103 	val->val->length = dsize;
104 	if (dptr[dsize - 1] != '\0') {
105 		val->val->length = dsize + 1;
106 	}
107 
108 	val->val->value = am(myself, val->val->length);
109 	if (val->val->value == 0) {
110 		freeValue(val, 1);
111 		return (0);
112 	}
113 	(void) memcpy(val->val->value, dptr, dsize);
114 
115 	return (val);
116 }
117 
118 /*
119  * Returns an array of rule-values corresponding to the
120  * splitfields.
121  */
122 __nis_rule_value_t *
123 processSplitField(__nis_table_mapping_t *sf, __nis_value_t *inVal,
124 			int *nv, int *statP) {
125 
126 	char			*sepset;
127 	__nis_rule_value_t	*rvq;
128 	__nis_mapping_format_t  *ftmp;
129 	__nis_value_t		**valA, *tempVal;
130 	int			i, j, res, numVals, oldlen, count;
131 	char			*str, *oldstr;
132 	char			*myself = "processSplitField";
133 
134 	/* sf will be non NULL */
135 
136 	if (inVal == 0 || inVal->type != vt_string) {
137 		*statP =  MAP_PARAM_ERROR;
138 		return (0);
139 	}
140 
141 	/* Get the separator list */
142 	sepset = sf->separatorStr;
143 
144 	/* Initialize rule-value */
145 	rvq = 0;
146 	count = 0;
147 
148 	if ((tempVal = stringToValue(inVal->val->value,
149 			inVal->val->length)) == 0) {
150 		*statP = MAP_NO_MEMORY;
151 		return (0);
152 	}
153 
154 	str = oldstr = tempVal->val->value;
155 	oldlen = tempVal->val->length;
156 
157 	while (str) {
158 		tempVal->val->value = str;
159 		tempVal->val->length = strlen(str) + 1;
160 
161 		/* Loop to check which format matches str */
162 		for (i = 0; i <= sf->numSplits; i++) {
163 			valA = matchMappingItem(sf->e[i].element.match.fmt,
164 				tempVal, &numVals, sepset, &str);
165 			if (valA == 0) {
166 				/* The format didn't match. Try the next one */
167 				continue;
168 			}
169 
170 			/*
171 			 * If we are here means we had a match.
172 			 * Each new set of values obtained from the match is
173 			 * added to a new rule-value. This is to preserve the
174 			 * the distinction between each set.
175 			 */
176 			rvq = growRuleValue(count, count + 1, rvq, 0);
177 			if (rvq == 0) {
178 				*statP = MAP_INTERNAL_ERROR;
179 				for (j = 0; j < numVals; j++)
180 					freeValue(valA[j], 1);
181 				sfree(valA);
182 				tempVal->val->value = oldstr;
183 				tempVal->val->length = oldlen;
184 				freeValue(tempVal, 1);
185 				return (0);
186 			}
187 			count++;
188 
189 			for (j = 0; j < numVals; j++) {
190 				res = addCol2RuleValue(vt_string,
191 					sf->e[i].element.match.item[j].name,
192 					valA[j]->val->value,
193 					valA[j]->val->length,
194 					&rvq[count - 1]);
195 				if (res == -1) {
196 					*statP = MAP_INTERNAL_ERROR;
197 					for (; j < numVals; j++)
198 						freeValue(valA[j], 1);
199 					sfree(valA);
200 					tempVal->val->value = oldstr;
201 					tempVal->val->length = oldlen;
202 					freeValue(tempVal, 1);
203 					freeRuleValue(rvq, count);
204 					return (0);
205 				}
206 				freeValue(valA[j], 1);
207 			}
208 			sfree(valA);
209 
210 			/*
211 			 * Since we had a match, break out of this loop
212 			 * to parse remainder of str
213 			 */
214 			break;
215 		}
216 
217 		/* Didn't find any match, so get out of the loop */
218 		if (i > sf->numSplits) {
219 			str = 0;
220 			break;
221 		}
222 
223 		/* Skip the separators before looping back */
224 		if (str) {
225 			str = str + strspn(str, sepset);
226 			if (*str == '\0')
227 				break;
228 		}
229 	}
230 
231 	tempVal->val->value = oldstr;
232 	tempVal->val->length = oldlen;
233 	freeValue(tempVal, 1);
234 
235 	if (str == 0) {
236 		freeRuleValue(rvq, count);
237 		return (0);
238 	}
239 
240 	if (nv != 0)
241 		*nv = count;
242 
243 	return (rvq);
244 }
245 
246 /*
247  * Convert the datum to an array of RuleValues
248  */
249 __nis_rule_value_t *
250 datumToRuleValue(datum *key, datum *value, __nis_table_mapping_t *t,
251 			int *nv, char *domain, bool_t readonly, int *statP) {
252 
253 	__nis_rule_value_t	*rvq, *subrvq, *newrvq;
254 	__nis_value_t		*val;
255 	__nis_value_t		**valA;
256 	__nis_table_mapping_t	*sf;
257 	int			valueLen, comLen, numVals, nr, count = 1;
258 	int			i, j, k, l, af;
259 	char			*ipaddr, *ipvalue;
260 
261 	/*  At this point, 't' is always non NULL */
262 
263 	/* Initialize rule-value */
264 	if ((rvq = initRuleValue(1, 0)) == 0) {
265 		*statP = MAP_INTERNAL_ERROR;
266 		return (0);
267 	}
268 
269 	/* Add domainname to rule-value */
270 	if (addCol2RuleValue(vt_string, N2LDOMAIN, domain, strlen(domain),
271 						rvq)) {
272 		freeRuleValue(rvq, 1);
273 		*statP = MAP_INTERNAL_ERROR;
274 		return (0);
275 	}
276 
277 	/* Handle key */
278 	if (key != 0) {
279 		/* Add field=value pair for N2LKEY */
280 		i = addCol2RuleValue(vt_string, N2LKEY, key->dptr, key->dsize,
281 						rvq);
282 
283 		/* For readonly, add field=value pair for N2LSEARCHKEY */
284 		if (readonly == TRUE && i == 0) {
285 			i = addCol2RuleValue(vt_string, N2LSEARCHKEY, key->dptr,
286 						key->dsize, rvq);
287 		}
288 		if (i) {
289 			freeRuleValue(rvq, 1);
290 			*statP = MAP_INTERNAL_ERROR;
291 			return (0);
292 		}
293 
294 		/* Add field=value pairs for IP addresses */
295 		if (checkIPaddress(key->dptr, key->dsize, &ipaddr) > 0) {
296 			/* If key is IPaddress, use preferred format */
297 			ipvalue = ipaddr;
298 			valueLen = strlen(ipaddr);
299 			i = addCol2RuleValue(vt_string, N2LIPKEY, ipvalue,
300 						valueLen, rvq);
301 		} else {
302 			/* If not, use original value for N2LSEARCHIPKEY */
303 			ipaddr = 0;
304 			ipvalue = key->dptr;
305 			valueLen = key->dsize;
306 			i = 0;
307 		}
308 
309 		if (readonly == TRUE && i == 0) {
310 			i = addCol2RuleValue(vt_string, N2LSEARCHIPKEY, ipvalue,
311 								valueLen, rvq);
312 		}
313 		sfree(ipaddr);
314 		if (i) {
315 			freeRuleValue(rvq, 1);
316 			*statP = MAP_INTERNAL_ERROR;
317 			return (0);
318 		}
319 	}
320 
321 	/* Handle datum value */
322 	if (value != 0 && t->e) {
323 		valueLen = value->dsize;
324 		/*
325 		 * Extract the comment, if any, and add it to
326 		 * the rule-value.
327 		 */
328 		if (t->commentChar != '\0') {
329 			/*
330 			 * We loop on value->dsize because value->dptr
331 			 * may not be NULL-terminated.
332 			 */
333 			for (i = 0; i < value->dsize; i++) {
334 				if (value->dptr[i] == t->commentChar) {
335 					valueLen = i;
336 					comLen = value->dsize - i - 1;
337 					if (comLen == 0)
338 						break;
339 					if (addCol2RuleValue(vt_string,
340 						N2LCOMMENT, value->dptr + i + 1,
341 						comLen, rvq)) {
342 						freeRuleValue(rvq, 1);
343 						*statP = MAP_INTERNAL_ERROR;
344 						return (0);
345 					}
346 					break;
347 				}
348 			}
349 		}
350 
351 		/* Skip trailing whitespaces */
352 		for (; valueLen > 0 && (value->dptr[valueLen - 1] == ' ' ||
353 			value->dptr[valueLen - 1] == '\t'); valueLen--);
354 
355 		/*
356 		 * At this point valueLen is the effective length of
357 		 * the data. Convert value into __nis_value_t so that
358 		 * we can use the matchMappingItem function to break it
359 		 * into fields.
360 		 */
361 		if ((val = stringToValue(value->dptr, valueLen)) == 0) {
362 			freeRuleValue(rvq, 1);
363 			*statP = MAP_NO_MEMORY;
364 			return (0);
365 		}
366 
367 		/* Perform namefield match */
368 		valA = matchMappingItem(t->e->element.match.fmt, val,
369 							&numVals, 0, 0);
370 		if (valA == 0) {
371 			freeValue(val, 1);
372 			freeRuleValue(rvq, 1);
373 			*statP = MAP_NAMEFIELD_MATCH_ERROR;
374 			return (0);
375 		}
376 
377 		/* We don't need val anymore, so free it */
378 		freeValue(val, 1);
379 
380 		/*
381 		 * Since matchMappingItem only returns us an array of
382 		 * __nis_value_t's, we need to associate each value
383 		 * in the array with the corresponding item name.
384 		 * This code assumes that numVals will be less than or
385 		 * equal to the number of item names associated with
386 		 * the format.
387 		 * These name=value pairs are added to rvq.
388 		 */
389 		for (i = 0, *statP = SUCCESS; i < numVals; i++) {
390 			for (j = 0; j < count; j++) {
391 				if (addCol2RuleValue(vt_string,
392 					t->e->element.match.item[i].name,
393 					valA[i]->val->value,
394 					valA[i]->val->length, &rvq[j])) {
395 					*statP = MAP_INTERNAL_ERROR;
396 					break;
397 				}
398 			}
399 			if (*statP == MAP_INTERNAL_ERROR)
400 				break;
401 
402 			/*
403 			 * Check if splitField exists for the field.
404 			 * Since splitfields are also stored as mapping
405 			 * structures, we need to get the hash table entry
406 			 * corresponding to the splitfield name
407 			 */
408 			sf = mappingFromMap(t->e->element.match.item[i].name,
409 					domain, statP);
410 			if (*statP == MAP_NO_MEMORY)
411 				break;
412 			*statP = SUCCESS;
413 			if (sf == 0)
414 				continue;
415 
416 			/*
417 			 * Process and add splitFields to rule-value rvq
418 			 */
419 			subrvq = processSplitField(sf, valA[i], &nr, statP);
420 
421 			if (subrvq == 0) {
422 				/* statP would have been set */
423 				break;
424 			}
425 
426 			/*
427 			 * We merge 'count' rule-values in rvq with 'nr'
428 			 * rule-values from subrvq to give us a whopping
429 			 * 'count * nr' rule-values
430 			 */
431 
432 			/* Initialize the new rule-value array */
433 			if ((newrvq = initRuleValue(count * nr, 0)) == 0) {
434 				*statP = MAP_INTERNAL_ERROR;
435 				freeRuleValue(subrvq, nr);
436 				break;
437 			}
438 
439 			for (j = 0, l = 0; j < nr; j++) {
440 				for (k = 0; k < count; k++, l++) {
441 					if ((mergeRuleValue(&newrvq[l],
442 							&rvq[k]) == -1) ||
443 							(mergeRuleValue(
444 							&newrvq[l],
445 							&subrvq[j]) == -1)) {
446 						*statP = MAP_INTERNAL_ERROR;
447 						for (i = 0; i < numVals; i++)
448 							freeValue(valA[i], 1);
449 						sfree(valA);
450 						freeRuleValue(rvq, count);
451 						freeRuleValue(newrvq,
452 								count * nr);
453 						freeRuleValue(subrvq, nr);
454 						return (0);
455 					}
456 				}
457 			}
458 
459 			freeRuleValue(rvq, count);
460 			rvq = newrvq;
461 			count = l;
462 			freeRuleValue(subrvq, nr);
463 
464 		}
465 
466 		/* We don't need valA anymore, so free it */
467 		for (i = 0; i < numVals; i++)
468 			freeValue(valA[i], 1);
469 		sfree(valA);
470 
471 		if (*statP != SUCCESS) {
472 			freeRuleValue(rvq, count);
473 			return (0);
474 		}
475 
476 	} /* if value */
477 
478 	if (nv != 0)
479 		*nv = count;
480 	return (rvq);
481 
482 }
483 
484 /*
485  * Generate name=values pairs for splitfield names
486  *
487  * Consider Example:
488  *	nisLDAPnameFields club:
489  *			("%s %s %s", name, code, members)
490  *	nisLDAPsplitField members:
491  *			("(%s,%s,%s)", host, user, domain),
492  *			("%s", group)
493  * On entry,
494  * - rv is an array of numVals rule-values each containing
495  * name=value pairs for names occuring in nisLDAPsplitField.
496  * (i.e host, user, domain, group)
497  * - trv contains name=value pairs for names occuring in
498  * nisLDAPnameFields. (i.e name, code but not members)
499  *
500  * For every name in nisLDAPnamefields that is a splitfield,
501  * this function applies the data in rv to the corresponding
502  * splitfield formats (accessed thru t), to generate a single
503  * string value for the corresponding splitfield (members).
504  * This new name=value pair is then added to trv.
505  * Besides, any uninitialized namefield names are set to empty strings.
506  */
507 suc_code
508 addSplitFieldValues(__nis_table_mapping_t *t, __nis_rule_value_t *rv,
509 			__nis_rule_value_t *trv, int numVals, char *domain) {
510 	__nis_table_mapping_t	*sf;
511 	__nis_value_t		*val;
512 	int			i, j, k, nitems, res, statP;
513 	char			*str, *tempstr;
514 	char			delim[2] = {0, 0};
515 	char			*emptystr = "";
516 	char			*myself = "addSplitFieldValues";
517 
518 	if (trv == 0)
519 		return (MAP_INTERNAL_ERROR);
520 
521 	if (t->e == 0)
522 		return (SUCCESS);
523 
524 	nitems = t->e->element.match.numItems;
525 
526 	/*
527 	 * Procedure:
528 	 * - Check each name in nisLDAPnamefield
529 	 * - if it's a splifield, construct its value and add it to trv
530 	 * - if not, check if it has a value
531 	 * - if not, add empty string
532 	 */
533 	for (i = 0, sf = 0; i < nitems; i++) {
534 		if (rv) {
535 			/*
536 			 * str will eventually contain the single string
537 			 * value for the corresponding  splitfield.
538 			 * No point initializing str if rv == 0 because
539 			 * splitfield cannot be constructed without rv.
540 			 * So, only initialized here.
541 			 */
542 			str = 0;
543 
544 			/* Check if it's a splitfield name */
545 			sf = mappingFromMap(t->e->element.match.item[i].name,
546 				domain, &statP);
547 
548 			/*
549 			 * Return only incase of memory allocation failure.
550 			 * The other error case (MAP_NO_MAPPING_EXISTS),
551 			 * indicates that the item name is not a splitfieldname
552 			 * i.e it's a namefieldname. This case is handled by
553 			 * the following if (sf == 0)
554 			 */
555 			if (statP == MAP_NO_MEMORY)
556 				return (statP);
557 		}
558 
559 		if (sf == 0) {
560 			/*
561 			 * Not a splitfield name. Verify if it has a value
562 			 */
563 			if (findVal(t->e->element.match.item[i].name,
564 				trv, mit_nisplus) == 0) {
565 				/* if not, use empty string */
566 				res = addCol2RuleValue(vt_string,
567 					t->e->element.match.item[i].name,
568 					emptystr, 0, trv);
569 				if (res == -1) {
570 					return (MAP_INTERNAL_ERROR);
571 				}
572 			}
573 			/*
574 			 * If rv == 0 then sf == 0 so we will continue here
575 			 * i.e. does not matter that str is not yet set up.
576 			 */
577 			continue;
578 		}
579 
580 		/* Code to construct a single value */
581 
582 		/* Use the first separator character as the delimiter */
583 		delim[0] = sf->separatorStr[0];
584 
585 		for (j = 0; j < numVals; j++) {
586 			/* sf->numSplits is zero-based */
587 			for (k = 0; k <= sf->numSplits; k++) {
588 				val = getMappingFormatArray(
589 					sf->e[k].element.match.fmt, &rv[j],
590 					fa_item,
591 					sf->e[k].element.match.numItems,
592 					sf->e[k].element.match.item);
593 				if (val == 0)
594 					continue;
595 				if (val->numVals > 0) {
596 					if (str) {
597 						tempstr = scat(myself,
598 							0, str, delim);
599 						sfree(str);
600 						if (tempstr)
601 							str = tempstr;
602 						else {
603 							freeValue(val, 1);
604 							return (MAP_NO_MEMORY);
605 						}
606 					}
607 					tempstr = scat(myself, 0, str,
608 						val->val->value);
609 					sfree(str);
610 					if (tempstr)
611 						str = tempstr;
612 					else {
613 						freeValue(val, 1);
614 						return (MAP_NO_MEMORY);
615 					}
616 				}
617 				freeValue(val, 1);
618 			}
619 		}
620 		if (str == 0)
621 			str = emptystr;
622 
623 		res = addCol2RuleValue(vt_string,
624 				t->e->element.match.item[i].name,
625 				str, strlen(str), trv);
626 
627 		if (str != emptystr)
628 			sfree(str);
629 
630 		if (res == -1) {
631 			return (MAP_INTERNAL_ERROR);
632 		}
633 	}
634 
635 	return (SUCCESS);
636 }
637 
638 /*
639  * Updates 'rv' with NIS name=value pairs suitable to
640  * construct datum from namefield information.
641  * Some part based on createNisPlusEntry (from ldap_nisdbquery.c)
642  * This code assumes that from a given LDAP entry, applying the
643  * mapping rules, would give us one or more NIS entries, differing
644  * only in key.
645  */
646 suc_code
647 buildNISRuleValue(__nis_table_mapping_t *t, __nis_rule_value_t *rv,
648 					char *domain) {
649 	int			r, i, j, k, l, index, nrq, res, len;
650 	int			numItems, splitname, count, statP;
651 	__nis_value_t		*rval;
652 	__nis_mapping_item_t	*litem;
653 	__nis_mapping_rule_t	*rl;
654 	__nis_rule_value_t	*rvq;
655 	char			*value, *emptystr = "";
656 
657 	statP = SUCCESS;
658 
659 	/* Initialize default base */
660 	__nisdb_get_tsd()->searchBase = t->objectDN->read.base;
661 
662 	/* Initialize rule-value rvq */
663 	rvq = 0;
664 	count = 0;
665 
666 	/* Add domainname to rule-value */
667 	if (addCol2RuleValue(vt_string, N2LDOMAIN, domain, strlen(domain),
668 						rv)) {
669 		return (MAP_INTERNAL_ERROR);
670 	}
671 
672 	for (r = 0; r < t->numRulesFromLDAP; r++) {
673 		rl = t->ruleFromLDAP[r];
674 
675 		/* Set escapeFlag if RHS is "dn" to remove escape chars */
676 		if (rl->rhs.numElements == 1 &&
677 			rl->rhs.element->type == me_item &&
678 			rl->rhs.element->element.item.type == mit_ldap &&
679 			strcasecmp(rl->rhs.element->element.item.name, "dn")
680 									== 0) {
681 				__nisdb_get_tsd()->escapeFlag = '2';
682 		}
683 
684 		rval = buildRvalue(&rl->rhs, mit_ldap, rv, NULL);
685 
686 		/* Reset escapeFlag */
687 		__nisdb_get_tsd()->escapeFlag = '\0';
688 
689 		if (rval == 0) {
690 			continue;
691 		}
692 
693 		if (rval->numVals <= 0) {
694 			/* Treat as invalid */
695 			freeValue(rval, 1);
696 			continue;
697 		}
698 
699 		litem = buildLvalue(&rl->lhs, &rval, &numItems);
700 		if (litem == 0) {
701 			/* This will take care of numItems == 0 */
702 			freeValue(rval, 1);
703 			continue;
704 		}
705 
706 		if (rval->numVals > 1) {
707 			if (numItems == 1 && litem->repeat)
708 				nrq = rval->numVals;
709 			else if (numItems > 1 && rval->repeat)
710 				nrq = 1 + ((rval->numVals-1)/numItems);
711 			else
712 				nrq = 1;
713 		} else
714 			nrq = 1;
715 
716 		/* Set splitname if splitfield names are specified */
717 		for (i = 0; i < numItems; i++) {
718 			if (strcasecmp(litem[i].name, N2LKEY) == 0 ||
719 				strcasecmp(litem[i].name, N2LIPKEY) == 0 ||
720 				strcasecmp(litem[i].name, N2LCOMMENT) == 0)
721 				continue;
722 			for (j = 0; j < t->numColumns; j++) {
723 				if (strcmp(litem[i].name, t->column[j]) == 0)
724 					break;
725 			}
726 			if (j == t->numColumns)
727 				break;
728 		}
729 
730 		splitname = (i < numItems)?1:0;
731 
732 		for (j = 0; j < nrq; j++) {
733 			if (splitname == 1) {
734 				/*
735 				 * Put every value of splitfieldname in a new
736 				 * rule-value. Helps generating splitfields.
737 				 */
738 				rvq = growRuleValue(count, count + 1, rvq, 0);
739 				if (rvq == 0) {
740 					freeRuleValue(rvq, count);
741 					freeValue(rval, 1);
742 					freeMappingItem(litem, numItems);
743 					return (MAP_INTERNAL_ERROR);
744 				}
745 				count++;
746 			}
747 
748 			for (k = j % nrq, l = 0; l < numItems; k += nrq, l++) {
749 				/* If we run out of values, use empty strings */
750 				if (k >= rval->numVals) {
751 					value = emptystr;
752 					len = 0;
753 				} else {
754 					value = rval->val[k].value;
755 					len = rval->val[k].length;
756 				}
757 				res = (splitname == 1)?addCol2RuleValue(
758 					vt_string, litem[l].name, value,
759 					len, &rvq[count - 1]):0;
760 				if (res != -1)
761 					res = addCol2RuleValue(vt_string,
762 						litem[l].name, value, len, rv);
763 				if (res == -1) {
764 					freeRuleValue(rvq, count);
765 					freeValue(rval, 1);
766 					freeMappingItem(litem, numItems);
767 					return (MAP_INTERNAL_ERROR);
768 				}
769 			}
770 		}
771 		freeValue(rval, 1);
772 		rval = 0;
773 		freeMappingItem(litem, numItems);
774 		litem = 0;
775 		numItems = 0;
776 	} /* for r < t->numRulesFromLDAP */
777 
778 	statP = addSplitFieldValues(t, rvq, rv, count, domain);
779 
780 	if (rvq)
781 		freeRuleValue(rvq, count);
782 
783 	if (verifyIndexMatch(t, 0, rv, 0, 0) == 0)
784 		return (MAP_INDEXLIST_ERROR);
785 	return (statP);
786 
787 } /* end of buildNISRuleValue */
788 
789 /*
790  * Convert rule-value to datum using namefield information
791  */
792 datum *
793 ruleValueToDatum(__nis_table_mapping_t *t, __nis_rule_value_t *rv, int *statP) {
794 	__nis_value_t	*val;
795 	datum		*value;
796 	char		*str, *cstr, commentSep[3] = {' ', 0, 0};
797 	char		*myself = "ruleValueToDatum";
798 
799 	/* No error yet */
800 	*statP = 0;
801 
802 	/* Return empty datum if no namefield information available */
803 	if (t->e == 0) {
804 		if ((value = am(myself, sizeof (*value))) == 0)
805 			*statP = MAP_NO_MEMORY;
806 		return (value);
807 	}
808 
809 	val = getMappingFormatArray(t->e->element.match.fmt, rv,
810 				fa_item, t->e->element.match.numItems,
811 				t->e->element.match.item);
812 
813 	if (val && val->val && val->val->value) {
814 		if ((value = am(myself, sizeof (*value))) == 0) {
815 			*statP = MAP_NO_MEMORY;
816 			freeValue(val, 1);
817 			return (0);
818 		}
819 
820 		/* Strip trailing whitespaces */
821 		cstr = (char *)val->val->value + val->val->length;
822 		for (; cstr >= (char *)val->val->value &&
823 			(*cstr == ' ' || *cstr == '\t'); *cstr-- = '\0');
824 
825 		if (t->commentChar != '\0' &&
826 		    (str = findVal(N2LCOMMENT, rv, mit_nisplus)) != 0 &&
827 		    *str != '\0') {
828 			commentSep[1] = t->commentChar;
829 			cstr = scat(myself, F, commentSep, str);
830 			if (cstr) {
831 				value->dptr = scat(myself, F,
832 						val->val->value, cstr);
833 				sfree(cstr);
834 			}
835 		} else {
836 			value->dptr = sdup(myself, T, val->val->value);
837 		}
838 		freeValue(val, 1);
839 		if (value->dptr) {
840 			value->dsize = strlen(value->dptr);
841 			return (value);
842 		} else {
843 			*statP = MAP_NO_MEMORY;
844 			sfree(value);
845 			return (0);
846 		}
847 	}
848 
849 	*statP = MAP_NAMEFIELD_MATCH_ERROR;
850 	return (0);
851 }
852 
853 datum *
854 getKeyFromRuleValue(__nis_table_mapping_t *t, __nis_rule_value_t *rv, int *nv,
855 					int *statP) {
856 	int	i, j;
857 	datum	*key = 0;
858 	char	*str;
859 	char	*myself = "getKeyFromRuleValue";
860 
861 	/* No error yet */
862 	*statP = 0;
863 
864 	if (rv == 0 || nv == 0)
865 		return (0);
866 
867 	for (i = 0; i < rv->numColumns; i++) {
868 		if (rv->colName[i] == 0)
869 			continue;
870 		if (strcasecmp(N2LKEY, rv->colName[i]) == 0 ||
871 				strcasecmp(N2LIPKEY, rv->colName[i]) == 0) {
872 			if ((*nv = rv->colVal[i].numVals) == 0)
873 				return (0);
874 			if ((key = am(myself, sizeof (key[0]) * *nv)) == 0) {
875 				*statP = MAP_NO_MEMORY;
876 				return (0);
877 			}
878 			for (j = 0; j < *nv; j++) {
879 				if ((str = rv->colVal[i].val[j].value) == 0) {
880 					key[j].dsize = 0;
881 					key[j].dptr = 0;
882 				} else {
883 					if (verifyIndexMatch(t, 0, 0,
884 							rv->colName[i],
885 								str) == 0) {
886 						key[j].dsize = 0;
887 						key[j].dptr = 0;
888 						continue;
889 					}
890 					key[j].dsize = strlen(str);
891 					key[j].dptr = am(myself,
892 							key[j].dsize + 1);
893 					if (key[j].dptr == 0) {
894 						*statP = MAP_NO_MEMORY;
895 						for (--j; j >= 0; j--)
896 							sfree(key[j].dptr);
897 						sfree(key);
898 						return (0);
899 					}
900 					bcopy(str, key[j].dptr, key[j].dsize);
901 				}
902 			}
903 			return (key);
904 		}
905 	}
906 	return (0);
907 }
908 
909 /*
910  * Get the mapping structure corresponding to `map,domain.'
911  */
912 __nis_table_mapping_t *
913 mappingFromMap(char *map, char *domain, int *statP) {
914 	char			*mapPath;
915 	__nis_table_mapping_t	*t;
916 
917 	/* No error yet */
918 	*statP = 0;
919 
920 	/* Construct map,domain. */
921 	if ((mapPath = getFullMapName(map, domain)) == 0) {
922 		*statP = MAP_NO_MEMORY;
923 		return (0);
924 	}
925 
926 	/* Get the hash table entry for the mapPath */
927 	if ((t = __nis_find_item_mt(mapPath, &ldapMappingList, 1, 0))
928 			== 0) {
929 		*statP = MAP_NO_MAPPING_EXISTS;
930 	}
931 	sfree(mapPath);
932 	return (t);
933 }
934 
935 /*
936  * Verify at least one key value obtained from DIT matches the search key
937  * RETURNS:	 1	MATCH
938  *		 0	NO MATCH
939  *		-1	NO KEY FOUND
940  */
941 static int
942 verifyKey(char *key, __nis_rule_value_t *rv) {
943 	int	i, j;
944 	char	*sipkey, *str;
945 
946 	for (i = 0; i < rv->numColumns; i++) {
947 		if (rv->colName[i] == 0)
948 			continue;
949 		if (strcasecmp(N2LKEY, rv->colName[i]) == 0) {
950 			if (rv->colVal[i].val == 0)
951 				return (0);
952 			for (j = 0; j < rv->colVal[i].numVals; j++) {
953 				str = (char *)rv->colVal[i].val[j].value;
954 				if (str && strcmp(str, key) == 0)
955 					return (1);
956 			}
957 			return (0);
958 		} else if (strcasecmp(N2LIPKEY, rv->colName[i]) == 0) {
959 			if (checkIPaddress(key, strlen(key), &sipkey) > 0) {
960 				if (rv->colVal[i].val == 0)
961 					return (0);
962 				for (j = 0; j < rv->colVal[i].numVals; j++) {
963 					str = rv->colVal[i].val[j].value;
964 					if (str && strcmp(str, sipkey) == 0) {
965 						sfree(sipkey);
966 						return (1);
967 					}
968 				}
969 				sfree(sipkey);
970 			}
971 			return (0);
972 		}
973 	}
974 	return (-1);
975 }
976 
977 /*
978  * Read (i.e get and map) a single NIS entry from the LDAP DIT
979  */
980 bool_t
981 singleReadFromDIT(char *map, char *domain, datum *key, datum *value,
982 							int *statP) {
983 	__nis_table_mapping_t	*t;
984 	__nis_rule_value_t	*rv_request = 0, *rv_result = 0;
985 	__nis_ldap_search_t	*ls;
986 	__nis_object_dn_t	*objectDN = NULL;
987 	int			i, rc, nr = 0, nv = 0;
988 	datum			*datval = 0;
989 	char			*skey, *str, *sipkey;
990 	char			*myself = "singleReadFromDIT";
991 
992 	*statP = SUCCESS;
993 
994 	if (!map || !domain || !key || !value) {
995 		*statP = MAP_PARAM_ERROR;
996 		return (FALSE);
997 	}
998 
999 
1000 	/* Get the mapping information for the map */
1001 	if ((t = mappingFromMap(map, domain, statP)) == 0) {
1002 		/*
1003 		 * No problem. We don't handle this map and domain. Maybe it's
1004 		 * handled by a service other than NIS.
1005 		 */
1006 		return (FALSE);
1007 	}
1008 
1009 	/* NULL-terminated version of datum key for logging */
1010 	if ((skey = am(myself, key->dsize + 1)) == 0) {
1011 		*statP = MAP_NO_MEMORY;
1012 		return (FALSE);
1013 	}
1014 	(void) memcpy(skey, key->dptr, key->dsize);
1015 
1016 	if ((str = getFullMapName(map, domain)) == 0) {
1017 		*statP = MAP_NO_MEMORY;
1018 		return (FALSE);
1019 	}
1020 
1021 	/* For each alternate mapping */
1022 	for (; t != 0; t = t->next) {
1023 		/* Verify objName */
1024 		if (strcmp(str, t->objName) != 0) {
1025 			continue;
1026 		}
1027 
1028 		/* Verify if key matches the index */
1029 		if (verifyIndexMatch(t, 0, 0, N2LKEY, skey) == 0 ||
1030 			verifyIndexMatch(t, 0, 0, N2LIPKEY, skey) == 0)
1031 			continue;
1032 
1033 		/* Check if rulesFromLDAP are provided */
1034 		if (t->numRulesFromLDAP == 0) {
1035 			logmsg(MSG_NOTIMECHECK, LOG_INFO,
1036 				"%s: No rulesFromLDAP information available "
1037 				"for %s (%s)", myself, t->dbId, map);
1038 			continue;
1039 		}
1040 
1041 		/* Convert key into rule-value */
1042 		if ((rv_request = datumToRuleValue(key, 0, t, 0, domain, TRUE,
1043 								statP)) == 0) {
1044 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
1045 				"%s: Conversion error %d (NIS to name=value "
1046 				"pairs) for NIS key (%s) for %s (%s)",
1047 				myself, *statP, skey, t->dbId, map);
1048 			continue;
1049 		}
1050 		/* Convert rule-value into ldap request */
1051 		for (objectDN = t->objectDN; objectDN &&
1052 				objectDN->read.base;
1053 				objectDN = objectDN->next) {
1054 			ls = createLdapRequest(t, rv_request, 0, 1, NULL,
1055 								objectDN);
1056 			if (ls == 0) {
1057 				*statP = MAP_CREATE_LDAP_REQUEST_ERROR;
1058 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
1059 					"%s: Failed to create ldapSearch "
1060 					"request for "
1061 					"NIS key (%s) for %s (%s) "
1062 					"for base %s",
1063 					myself, skey, t->dbId, map,
1064 					objectDN->read.base);
1065 				continue;
1066 			}
1067 			ls->timeout.tv_sec = SINGLE_ACCESS_TIMEOUT_SEC;
1068 			ls->timeout.tv_usec = SINGLE_ACCESS_TIMEOUT_USEC;
1069 			/* Query LDAP */
1070 			nr = (ls->isDN)?0:-1;
1071 			rv_result = ldapSearch(ls, &nr, 0, statP);
1072 			freeLdapSearch(ls);
1073 			if (rv_result == 0) {
1074 				if (*statP == LDAP_NO_SUCH_OBJECT) {
1075 					/* Entry does not exist in */
1076 					/* the ldap server */
1077 				}
1078 				continue;
1079 			}
1080 			freeRuleValue(rv_request, 1);
1081 			rv_request = 0;
1082 
1083 			/* if result > 1, first match will be returned */
1084 			if (nr > 1) {
1085 				logmsg(MSG_NOTIMECHECK, LOG_INFO,
1086 					"%s: %d ldapSearch results "
1087 					"for NIS key (%s) "
1088 					"for %s (%s) for base %s. "
1089 					"First match will be returned ",
1090 					myself, nr, skey, t->dbId, map,
1091 					objectDN->read.base);
1092 			}
1093 
1094 			for (i = 0; i < nr; i++) {
1095 				/* Convert LDAP data to NIS equivalents */
1096 				*statP = buildNISRuleValue(t, &rv_result[i],
1097 								domain);
1098 				if (*statP == MAP_INDEXLIST_ERROR)
1099 					continue;
1100 
1101 				if (*statP != SUCCESS) {
1102 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1103 					"%s: Conversion error %d (LDAP to "
1104 					"name=value pairs) for NIS key (%s) "
1105 					"for %s (%s) for base %s", myself,
1106 					*statP, skey,
1107 					t->dbId, map, objectDN->read.base);
1108 					continue;
1109 				}
1110 
1111 			/*
1112 			 * Check if 'key' from the ldap result matches the key
1113 			 * provided by our caller
1114 			 */
1115 				if ((rc = verifyKey(skey, &rv_result[i]))
1116 						== -1) {
1117 					logmsg(MSG_NOTIMECHECK, LOG_INFO,
1118 					"%s: Cannot verify key from ldap "
1119 					"result for NIS key (%s) for %s (%s) "
1120 					"for base %s",
1121 					myself, skey, t->dbId, map,
1122 					objectDN->read.base);
1123 					continue;
1124 				}
1125 
1126 				if (rc == 1) {
1127 					datval = ruleValueToDatum(t,
1128 							&rv_result[i], statP);
1129 					if (datval == 0) {
1130 						logmsg(MSG_NOTIMECHECK,
1131 						LOG_WARNING,
1132 						"%s: Conversion error %d "
1133 						"(name=value pairs to NIS) "
1134 						"for NIS key (%s) for %s (%s)"
1135 						" for base %s",
1136 						myself,
1137 						*statP, skey, t->dbId, map,
1138 						objectDN->read.base);
1139 						continue;
1140 					}
1141 					if (value) {
1142 						value->dptr = datval->dptr;
1143 						value->dsize = datval->dsize;
1144 					}
1145 					sfree(datval);
1146 					sfree(skey);
1147 					freeRuleValue(rv_result, nr);
1148 					rv_result = 0;
1149 					*statP = SUCCESS;
1150 
1151 					/* Free full map name */
1152 					sfree(str);
1153 
1154 					return (TRUE);
1155 				}
1156 			}
1157 			freeRuleValue(rv_result, nr);
1158 			rv_result = 0;
1159 		}   /* end of for over objectDN */
1160 
1161 		if (rv_request != 0) {
1162 			freeRuleValue(rv_request, 1);
1163 			rv_request = 0;
1164 		}
1165 		if (rv_result != 0) {
1166 			freeRuleValue(rv_result, nr);
1167 			rv_result = 0;
1168 		}
1169 	}
1170 	sfree(skey);
1171 	*statP = MAP_NO_MATCHING_KEY;
1172 
1173 	/* Free full map name */
1174 	sfree(str);
1175 
1176 	return (FALSE);
1177 }
1178 
1179 
1180 /*
1181  * Maps and writes a single NIS entry to the LDAP DIT
1182  */
1183 int
1184 singleWriteToDIT(char *map, char *domain, datum *key, datum *value,
1185 						bool_t replace) {
1186 	__nis_table_mapping_t	*t;
1187 	__nis_rule_value_t	*rv, *frv;
1188 	__nis_ldap_search_t	*ls;
1189 	int			statP = SUCCESS, flag;
1190 	int			nv, nr, i, rc, collapse;
1191 	char			*dn = 0, *skey, *svalue, *str;
1192 	char			*myself = "singleWriteToDIT";
1193 
1194 	if (!map || !domain || !key || !value) {
1195 		return (MAP_PARAM_ERROR);
1196 	}
1197 
1198 	/* Return SUCCESS for empty or whitespace key */
1199 	for (i = 0; i < key->dsize && (key->dptr[i] == 0 ||
1200 		key->dptr[i] == ' ' || key->dptr[i] == '\t'); i++);
1201 	if (i >= key->dsize)
1202 		return (SUCCESS);
1203 
1204 	/* Get the mapping information for the map */
1205 	if ((t = mappingFromMap(map, domain, &statP)) == 0) {
1206 		/*
1207 		 * No problem. We don't handle this map and domain. Maybe it's
1208 		 * handled by a service other than NIS.
1209 		 */
1210 		return (statP);
1211 	}
1212 
1213 	/* NULL-terminated version of key and value for logging */
1214 	if ((skey = am(myself, key->dsize + 1)) == 0)
1215 		return (MAP_NO_MEMORY);
1216 	(void) memcpy(skey, key->dptr, key->dsize);
1217 
1218 	if ((svalue = am(myself, value->dsize + 1)) == 0) {
1219 		sfree(skey);
1220 		return (MAP_NO_MEMORY);
1221 	}
1222 	(void) memcpy(svalue, value->dptr, value->dsize);
1223 
1224 	if ((str = getFullMapName(map, domain)) == 0) {
1225 		sfree(skey);
1226 		sfree(svalue);
1227 		return (MAP_NO_MEMORY);
1228 	}
1229 
1230 	/* For each alternate mapping */
1231 	for (flag = 0; t != 0; t = t->next) {
1232 		/* Verify objName */
1233 		if (strcmp(str, t->objName) != 0) {
1234 			continue;
1235 		}
1236 
1237 		/* Verify if key matches the index */
1238 		if (verifyIndexMatch(t, 0, 0, N2LKEY, skey) == 0 ||
1239 			verifyIndexMatch(t, 0, 0, N2LIPKEY, skey) == 0)
1240 			continue;
1241 
1242 		/* Check the writespecs */
1243 		if (t->objectDN->write.base == 0) {
1244 			logmsg(MSG_NOTIMECHECK, LOG_INFO,
1245 				"%s: No baseDN in writespec. Write disabled "
1246 				"for %s (%s)", myself, t->dbId, map);
1247 			continue;
1248 		}
1249 
1250 		/* Check if rulesToLDAP are provided */
1251 		if (t->numRulesToLDAP == 0) {
1252 			logmsg(MSG_NOTIMECHECK, LOG_INFO,
1253 				"%s: No rulesToLDAP. Write disabled for "
1254 				"%s (%s)", myself, t->dbId, map);
1255 			continue;
1256 		}
1257 
1258 		/* Set flag to indicate write is enabled */
1259 		flag = 1;
1260 
1261 		/* Convert key  and value into an array of rule-values */
1262 		if ((rv = datumToRuleValue(key, value, t, &nv, domain, FALSE,
1263 								&statP)) == 0) {
1264 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
1265 				"%s: Conversion error %d (NIS to name=value "
1266 				"pairs) for NIS data (key=%s, value=%s) "
1267 				"for %s (%s)",
1268 				myself, statP, skey, svalue, t->dbId, map);
1269 			sfree(skey);
1270 			sfree(svalue);
1271 
1272 			/* Free full map name */
1273 			sfree(str);
1274 
1275 			return (statP);
1276 		}
1277 
1278 		/* Convert NIS data to LDAP equivalents for each rule-value */
1279 		for (i = 0; i < nv; i++) {
1280 			/* Verify indexlist with name=value pairs */
1281 			if (verifyIndexMatch(t, 0, &rv[i], 0, 0) == 0)
1282 				break;
1283 
1284 			/* Create LDAP request and LDAP name=value pairs */
1285 			if ((ls = createLdapRequest(t, &rv[i],
1286 			    0, 0, NULL, NULL)) == 0) {
1287 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
1288 					"%s: Conversion error (name=value pairs"
1289 					" to LDAP) for NIS data "
1290 					"(key=%s, value=%s) for %s (%s)",
1291 					myself, skey, svalue, t->dbId, map);
1292 				freeRuleValue(rv, nv);
1293 				sfree(skey);
1294 				sfree(svalue);
1295 
1296 				/* Free full map name */
1297 				sfree(str);
1298 
1299 				return (MAP_CREATE_LDAP_REQUEST_ERROR);
1300 			}
1301 			freeLdapSearch(ls);
1302 			/* printRuleValue(&rv[i]); */
1303 		}
1304 
1305 		/* If i < nv then this alternate mapping isn't the one */
1306 		if (i < nv)
1307 			continue;
1308 
1309 		/*
1310 		 * Merge rule-values with the same DN so that we have
1311 		 * one ldap write request for each DN
1312 		 */
1313 		nr = nv;
1314 		frv = mergeRuleValueWithSameDN(rv, &nr);
1315 		freeRuleValue(rv, nv);
1316 		if (frv == 0) {
1317 			if (nr == -1) {
1318 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
1319 					"%s: Unable to merge LDAP write "
1320 					"requests to same DN for NIS data "
1321 					"(key=%s, value=%s) for %s (%s)",
1322 					myself, skey, svalue, t->dbId, map);
1323 				statP = MAP_INTERNAL_ERROR;
1324 			} else if (nr == 0) {
1325 				logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1326 					"%s: Cannot generate write DN due to "
1327 					"missing information for NIS data "
1328 					"(key=%s, value=%s) for %s (%s)",
1329 					myself, skey, svalue, t->dbId, map);
1330 				statP = MAP_NO_DN;
1331 			}
1332 			sfree(skey);
1333 			sfree(svalue);
1334 
1335 			/* Free full map name */
1336 			sfree(str);
1337 
1338 			return (statP);
1339 		}
1340 
1341 		/* Write to the LDAP server */
1342 		for (collapse = 0, i = 0; i < nr; i++) {
1343 			if ((dn = findVal("dn", &frv[i], mit_ldap)) != 0) {
1344 				if (replace == FALSE) {
1345 					/* ldap add */
1346 					rc = ldapAdd(dn, &frv[i],
1347 						t->objectDN->write.attrs, 0);
1348 				} else {
1349 					/* ldap modify with addFirst set */
1350 					rc = ldapModify(dn, &frv[i],
1351 						t->objectDN->write.attrs, 1);
1352 				}
1353 
1354 				/* if we get err=20, collapse and try again */
1355 				if (!collapse &&
1356 					(rc == LDAP_TYPE_OR_VALUE_EXISTS) &&
1357 					(collapseRuleValue(&frv[i]) == 1)) {
1358 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1359 						"%s: Ignoring values differing "
1360 						"in case from NIS data (key=%s,"
1361 						" value=%s) for (dn: %s) for "
1362 						"%s (%s)", myself, skey,
1363 						svalue, dn, t->dbId, map);
1364 					collapse = 1;
1365 					i--;
1366 					continue;
1367 				}
1368 
1369 				collapse = 0;
1370 				if (rc != LDAP_SUCCESS) {
1371 					/* Log error */
1372 					logmsg(MSG_NOTIMECHECK, LOG_ERR,
1373 						"%s: %s error %d (%s) for "
1374 						"(dn: %s) for NIS data "
1375 						"(key=%s, value=%s) "
1376 						"for %s (%s)",
1377 						myself, (replace == TRUE) ?
1378 						"ldapModify" : "ldapAdd", rc,
1379 						ldap_err2string(rc), dn, skey,
1380 						svalue, t->dbId, map);
1381 
1382 					/* Dumping failed call may be useful */
1383 					/* printRuleValue(&frv[i]); */
1384 
1385 					/*
1386 					 * Return the error code and let wrapper
1387 					 * sort out if mapping should continue
1388 					 * or abort.
1389 					 */
1390 					statP = rc;
1391 					sfree(skey);
1392 					sfree(svalue);
1393 					freeRuleValue(frv, nr);
1394 
1395 					/* Free full map name */
1396 					sfree(str);
1397 
1398 					return (statP);
1399 				}
1400 			}
1401 		}
1402 
1403 		freeRuleValue(frv, nr);
1404 	}
1405 
1406 	sfree(skey);
1407 	sfree(svalue);
1408 
1409 	/* Free full map name */
1410 	sfree(str);
1411 
1412 	return ((flag)?SUCCESS:MAP_WRITE_DISABLED);
1413 }
1414 
1415 suc_code
1416 collapseRuleValue(__nis_rule_value_t *rv) {
1417 	int		i, j, k, flag;
1418 
1419 	/* Using 'val' to appease cstyle's 80 chars/line limit */
1420 	__nis_value_t	*val;
1421 
1422 	for (i = 0, flag = 0; i < rv->numAttrs; i++) {
1423 		val = &rv->attrVal[i];
1424 		for (j = 1; j < val->numVals; j++) {
1425 			for (k = 0; k < j; k++) {
1426 				if (val->val[j].length != val->val[k].length)
1427 					continue;
1428 				if (val->val[k].length == 0)
1429 					continue;
1430 				if (strncasecmp(val->val[j].value,
1431 						val->val[k].value,
1432 						val->val[j].length) != 0)
1433 					continue;
1434 				flag = 1;
1435 				sfree(val->val[j].value);
1436 
1437 #ifdef	ORDER_NOT_IMPORTANT
1438 				val->val[j--] = val->val[--val->numVals];
1439 #else
1440 				/* Order needs to be maintained */
1441 				for (k = j + 1; k < val->numVals; k++)
1442 					val->val[k - 1] = val->val[k];
1443 				j--;
1444 				val->numVals--;
1445 #endif
1446 				break;
1447 			}
1448 		}
1449 	}
1450 	return (flag);
1451 }
1452 
1453 /* ObjectClass lookup table */
1454 static struct {
1455 	const char *attrType;
1456 	const char *objectClass;
1457 } oc_lookup[] = {
1458 	{ "o",				"objectclass=organization"},
1459 	{ "organizationname",		"objectclass=organization"},
1460 	{ "2.5.4.10",			"objectclass=organization"},
1461 	{ "ou",				"objectclass=organizationalunit"},
1462 	{ "organizationalunitname",	"objectclass=organizationalunit"},
1463 	{ "2.5.4.11",			"objectclass=organizationalunit"},
1464 	{ "c",				"objectclass=country"},
1465 	{ "countryname",		"objectclass=country"},
1466 	{ "2.5.4.6",			"objectclass=country"},
1467 	{ "dc",				"objectclass=domain"},
1468 	{ "domaincomponent",		"objectclass=domain"},
1469 	{ "0.9.2342.19200300.100.1.25",	"objectclass=domain"},
1470 	{ "nismapname",			"objectclass=nismap"},
1471 	{ "1.3.6.1.1.1.1.26",		"objectclass=nismap"},
1472 	{ "automountmapname",		"objectclass=automountmap"},
1473 	{ "1.3.6.1.1.1.1.31",		"objectclass=automountmap"},
1474 	{ 0,				0}
1475 };
1476 
1477 /*
1478  * Returns the name of the objectclass to which the object
1479  * represented by the given 'rdn' will most likely belong to.
1480  * The return value is in static memory so it should not be
1481  * freed
1482  */
1483 const char *
1484 getObjectClass(char *rdn) {
1485 
1486 	char *attrtype, *p;
1487 	int len, i;
1488 
1489 	/* Skip leading whitespaces */
1490 	for (p = rdn; *p == ' ' || *p == '\t'; p++);
1491 	if (*p == '\0')
1492 		return (0);
1493 	attrtype = p;
1494 
1495 	/* Find '=' */
1496 	if ((p = strchr(attrtype, '=')) == 0 || p == attrtype ||
1497 						*(p - 1) == '\\')
1498 		return (0);
1499 
1500 	/*
1501 	 * Skip trailing whitespaces in attrtype
1502 	 * Don't worry, p won't decrease beyond attrtype
1503 	 */
1504 	for (--p; *p == ' ' || *p == '\t'; p--);
1505 	len = p - attrtype + 1;
1506 
1507 	for (i = 0; oc_lookup[i].attrType; i++)
1508 		if (!strncasecmp(oc_lookup[i].attrType, attrtype, len))
1509 			/* Check length is right */
1510 			if (len == strlen(oc_lookup[i].attrType))
1511 				return (oc_lookup[i].objectClass);
1512 
1513 	return (0);
1514 }
1515 
1516 /*
1517  * Split 'dn' into rdn and parentdn based on the first
1518  * occurrence of unescaped 'comma' or 'semicolon'. rdn
1519  * lies on the LHS while parentdn lies on the RHS of the
1520  * split. If none found, then an empty string ("") is
1521  * assigned to parentdn
1522  */
1523 int
1524 splitDN(char *dn, char **rdn, char **parentdn) {
1525 	char	*value, *name;
1526 	char	*myself = "splitDN";
1527 
1528 	if ((name = sdup(myself, T, dn)) == 0)
1529 		return (-1);
1530 
1531 	for (value = name; *value != '\0'; value++) {
1532 		if (*value == ',' || *value == ';')
1533 			if (value == name || *(value - 1) != '\\')
1534 				break;
1535 	}
1536 
1537 	if (*value != '\0') {
1538 		*value = '\0';
1539 		value++;
1540 	} else
1541 		value = 0;
1542 
1543 	if (parentdn) {
1544 		if ((*parentdn = sdup(myself, T, value)) == 0) {
1545 			sfree(name);
1546 			return (-1);
1547 		}
1548 	}
1549 	if (rdn)
1550 		*rdn = name;
1551 	else
1552 		sfree(name);
1553 
1554 	return (1);
1555 }
1556 
1557 /*
1558  * FUNCTION :	makeNISObject()
1559  *
1560  * DESCRIPTION: Sets up a nis Object in the DIT.
1561  *
1562  * GIVEN :
1563  *		Case 1: Both 'domain' and 'dn' are non-NULL
1564  *			Create nisDomainObject with the given information
1565  *		Case 2: Only 'domain' is  non-NULL
1566  *			Obtain the 'dn' from the nisLDAPdomainContext list
1567  *			Create nisDomainObject with the above information
1568  *		Case 3: Only 'dn' is  non-NULL
1569  *			Create an object with the 'dn'
1570  *			Here we guess the objectclass attribute, based on
1571  *			oc_lookup table
1572  *		Case 4: Both 'domain' and 'dn' are NULL
1573  *			Error
1574  *
1575  * RETURNS :	SUCCESS = It worked
1576  *		FAILURE = There was a problem.
1577  */
1578 suc_code
1579 makeNISObject(char *domain, char *dn) {
1580 	__nis_rule_value_t	*rv;
1581 	__nis_ldap_search_t	*ls;
1582 	int			i, rc, nr, add_rc;
1583 	char			*val;
1584 	char			*myself = "makeNISObject";
1585 
1586 	if (!dn && !domain)
1587 		return (FAILURE);
1588 
1589 	/*
1590 	 * If only 'domain' name is provided, then
1591 	 * try to find dn from the nisLDAPdomainContext
1592 	 * list generated by the parser
1593 	 */
1594 	if (!dn) {
1595 		for (i = 0; i < ypDomains.numDomains; i++) {
1596 			if (ypDomains.domainLabels[i] == 0)
1597 				continue;
1598 			if (strcasecmp(domain, ypDomains.domainLabels[i])
1599 								== 0) {
1600 				dn = ypDomains.domains[i];
1601 				break;
1602 			}
1603 		}
1604 		if (!dn)
1605 			return (FAILURE);
1606 	}
1607 
1608 	/*
1609 	 * If only 'dn' is given, then it means that the
1610 	 * caller simply wants to a create an entry for
1611 	 * that 'dn'.
1612 	 *
1613 	 * If 'domain' is given, then check if the 'dn'
1614 	 * has already been set up as a nis domain object.
1615 	 * If not, see if we can make it become one.
1616 	 */
1617 	if (domain) {
1618 		/*
1619 		 * Check to see if the nis domain object has
1620 		 * already been set up
1621 		 */
1622 		ls = buildLdapSearch(dn, LDAP_SCOPE_BASE, 0, 0,
1623 			"objectclass=*", 0, 0, 0);
1624 		if (ls == 0) {
1625 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
1626 				"%s: Unable to create ldapSearch "
1627 				"request for dn: %s", myself, dn);
1628 			return (FAILURE);
1629 		}
1630 		nr = -1;
1631 		rv = ldapSearch(ls, &nr, 0, &rc);
1632 		freeLdapSearch(ls);
1633 		if (rc == LDAP_SUCCESS) {
1634 			val = findVal("nisDomain", rv, mit_ldap);
1635 			if (val != NULL) {
1636 				/*
1637 				 * Yes, nis domain object found. Check
1638 				 * to see if the domain names match.
1639 				 * If so, we are done. If not, log
1640 				 * a warning message, and return SUCCESS.
1641 				 */
1642 				if (strcasecmp(val, domain) == 0) {
1643 					freeRuleValue(rv, nr);
1644 					return (SUCCESS);
1645 				} else {
1646 					logmsg(MSG_NOTIMECHECK,
1647 						LOG_WARNING,
1648 						"%s: Entry (dn: %s) already "
1649 						"contains a nis domain name "
1650 						"(%s). The domain name (%s) "
1651 						"is not added.",
1652 						myself, dn, val, domain);
1653 					freeRuleValue(rv, nr);
1654 					return (SUCCESS);
1655 				}
1656 			} else {
1657 				freeRuleValue(rv, nr);
1658 				/*
1659 				 * Entry for the 'dn' exists, but it
1660 				 * is not a nis domain object yet.
1661 				 * Add the nisDoamin attribute and
1662 				 * the nisDomainObject objectclass to
1663 				 * the entry.
1664 				 */
1665 				if ((rv = initRuleValue(1, 0)) == 0)
1666 					return (FAILURE);
1667 
1668 				if (addSAttr2RuleValue("nisDomain",
1669 						domain, rv) == -1) {
1670 					freeRuleValue(rv, 1);
1671 					return (FAILURE);
1672 				}
1673 				rc = ldapModify(dn, rv,
1674 					"objectclass=nisDomainObject",
1675 					0);
1676 				freeRuleValue(rv, 1);
1677 				if (rc == LDAP_SUCCESS) {
1678 					logmsg(MSG_NOTIMECHECK,
1679 						LOG_INFO,
1680 						"%s: entry (dn: %s) "
1681 						"modified to be an "
1682 						"nis domain object",
1683 						myself, dn);
1684 					return (SUCCESS);
1685 				} else {
1686 					logmsg(MSG_NOTIMECHECK,
1687 						LOG_ERR,
1688 						"%s: unable to modify "
1689 						"entry (dn: %s) to be "
1690 						"a nis domain object: "
1691 						"ldapModify error %d (%s)",
1692 						myself, dn, rc,
1693 						ldap_err2string(rc));
1694 					return (FAILURE);
1695 				}
1696 			}
1697 		} else { /* search for 'dn' failed */
1698 			freeRuleValue(rv, nr);
1699 
1700 			/*
1701 			 * It is OK if no such object, otherwise
1702 			 * log an error.
1703 			 */
1704 			if (rc != LDAP_NO_SUCH_OBJECT) {
1705 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
1706 					"%s: unable to retrieve "
1707 					"entry (dn: %s): "
1708 					"ldapSearch error %d (%s)",
1709 					myself, dn, rc,
1710 					ldap_err2string(rc));
1711 				return (FAILURE);
1712 			}
1713 		}
1714 
1715 		/*
1716 		 * If the 'dn' is actually the naming context of
1717 		 * the DIT, we should be able to make it a nis domain
1718 		 * object without worrying about missing parent
1719 		 * entries. If unable to add the entry for the 'dn'
1720 		 * due to missing parent entries, fall through
1721 		 * to create them and then add the nis domain object.
1722 		 */
1723 		if (addNISObject(domain, dn, &add_rc) == SUCCESS)
1724 			return (SUCCESS);
1725 		else if (add_rc != LDAP_NO_SUCH_OBJECT)
1726 			return (FAILURE);
1727 	}
1728 
1729 	/* Create parent */
1730 	if (addParent(dn, NULL) == FAILURE)
1731 		return (FAILURE);
1732 
1733 	if (addNISObject(domain, dn, NULL) == FAILURE)
1734 		return (FAILURE);
1735 
1736 	return (SUCCESS);
1737 }
1738 
1739 suc_code
1740 addParent(char *dn, char **attr) {
1741 	__nis_rule_value_t	*rv;
1742 	__nis_ldap_search_t	*ls;
1743 	int			rc, nr;
1744 	char			*parentdn = 0, *rdn = 0;
1745 	char			*myself = "addParent";
1746 
1747 	/* Obtain parentdn */
1748 	if (splitDN(dn, &rdn, &parentdn) == -1)
1749 		return (FAILURE);
1750 	if (!parentdn) {
1751 		sfree(rdn);
1752 		return (FAILURE);
1753 	}
1754 
1755 	/* Check if parentdn exists */
1756 	ls = buildLdapSearch(parentdn, LDAP_SCOPE_BASE, 0, 0,
1757 					"objectclass=*", 0, 0, 0);
1758 	if (ls == 0) {
1759 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
1760 			"%s: Unable to create ldapSearch request for "
1761 			"parent (dn: %s) of (dn: %s)",
1762 			myself, parentdn, dn);
1763 		sfree(parentdn);
1764 		sfree(rdn);
1765 		return (FAILURE);
1766 	}
1767 	nr = -1;
1768 	rv = ldapSearch(ls, &nr, 0, &rc);
1769 	freeLdapSearch(ls);
1770 	freeRuleValue(rv, nr);
1771 
1772 	/* Create parent if it doesn't exists */
1773 	if (rc == LDAP_NO_SUCH_OBJECT) {
1774 		if (makeNISObject(0, parentdn) == FAILURE) {
1775 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
1776 				"%s: Unable to create parent (dn: %s) of "
1777 				"(dn: %s) in the DIT", myself, parentdn, dn);
1778 			sfree(parentdn);
1779 			sfree(rdn);
1780 			return (FAILURE);
1781 		}
1782 	}
1783 	sfree(parentdn);
1784 
1785 	if (attr && rdn)
1786 		*attr = (char *)getObjectClass(rdn);
1787 	sfree(rdn);
1788 
1789 	return (SUCCESS);
1790 }
1791 
1792 
1793 
1794 /*
1795  * FUNCTION :	is_fatal_error()
1796  *
1797  * DESCRIPTION:	Works out if a failed mapping operation should be retried.
1798  *
1799  * INPUTS :	Result code from operation
1800  *
1801  * OUTPUTS :	TRUE = Fatal error, don't retry.
1802  *		FALSE = Temporary error, retry.
1803  */
1804 bool_t
1805 is_fatal_error(int res)
1806 {
1807 
1808 	if (0 > res)
1809 		/* An internal mapping error. Not going to go away. */
1810 		return (TRUE);
1811 
1812 	switch (res) {
1813 		case (LDAP_PROTOCOL_ERROR):
1814 		case (LDAP_TIMELIMIT_EXCEEDED):
1815 		case (LDAP_PARTIAL_RESULTS):
1816 		case (LDAP_BUSY):
1817 		case (LDAP_UNAVAILABLE):
1818 		case (LDAP_UNWILLING_TO_PERFORM):
1819 		case (LDAP_OTHER):
1820 		case (LDAP_SERVER_DOWN):
1821 		case (LDAP_LOCAL_ERROR):
1822 		case (LDAP_TIMEOUT):
1823 		case (LDAP_NO_MEMORY):
1824 			/* Probably worth a retry */
1825 			return (FALSE);
1826 
1827 		default:
1828 			return (TRUE);
1829 	}
1830 }
1831 
1832 /*
1833  * FUNCTION :	addNISObject()
1834  *
1835  * DESCRIPTION: Add a nis Object in the DIT.
1836  *
1837  * GIVEN :
1838  *		Case 1: 'dn' is NULL
1839  *			Error
1840  *		Case 2: 'domain' is non-NULL
1841  *			Create nisDomainObject with the given information
1842  *		Case 3: 'domain' is NULL
1843  *			Create an object with the 'dn'
1844  *			Here we guess the objectclass attribute, based on
1845  *			oc_lookup table
1846  *
1847  * RETURNS :	SUCCESS = It worked
1848  *		FAILURE = There was a problem. If the ldap add
1849  *                        operation failed, ldap_rc will be set
1850  *			  to the ldap error code.
1851  */
1852 suc_code
1853 addNISObject(char *domain, char *dn, int *ldap_rc) {
1854 	__nis_rule_value_t	*rv;
1855 	int			rc;
1856 	char			*objClassAttrs = NULL, *attrs;
1857 	char			*value, *svalue, *rdn = NULL;
1858 	char			*myself = "addNISObject";
1859 
1860 	if (!dn)
1861 		return (FAILURE);
1862 
1863 	if ((rv = initRuleValue(1, 0)) == 0)
1864 		return (FAILURE);
1865 
1866 	if (ldap_rc)
1867 		*ldap_rc = -1;
1868 
1869 	/*
1870 	 * Add name=value pairs from RDN. Although this is not required
1871 	 * for SunOne Directory Server, during openldap interoperabilty
1872 	 * tests, it was found out that openldap server returned object
1873 	 * class violation errors if MUST attributes were not specified
1874 	 * explicitly.
1875 	 */
1876 	if (splitDN(dn, &rdn, 0) == -1)
1877 		return (FAILURE);
1878 	if (rdn != NULL) {
1879 		objClassAttrs = (char *)getObjectClass(rdn);
1880 		if (objClassAttrs == NULL) {
1881 			sfree(rdn);
1882 			return (FAILURE);
1883 		}
1884 
1885 		/*
1886 		 * RDN can be composed of multiple name=value pairs
1887 		 * concatenated by '+'. Hence, we need to determine each
1888 		 * pair and add it to 'rv'
1889 		 */
1890 		for (value = rdn, svalue = NULL; *value != '\0'; value++) {
1891 			if (*value == '+') {
1892 				/* Make sure it's not escaped */
1893 				if (value == rdn || *(value - 1) != '\\') {
1894 					/*
1895 					 * We are at the start of the new
1896 					 * pair. 'svalue' now contains the
1897 					 * value for the previous pair. Add
1898 					 * the previous pair to 'rv'
1899 					 */
1900 					*value = '\0';
1901 					if (svalue &&
1902 					addSAttr2RuleValue(rdn, svalue, rv)
1903 									== -1) {
1904 						sfree(rdn);
1905 						freeRuleValue(rv, 1);
1906 						return (FAILURE);
1907 					}
1908 					svalue = NULL;
1909 					rdn = value + 1;
1910 					continue;
1911 				}
1912 			}
1913 
1914 			if (*value == '=') {
1915 				if (value == rdn || *(value - 1) != '\\') {
1916 					/*
1917 					 * 'rdn' now contains the name.
1918 					 * Whatever follows till the next
1919 					 * unescaped '+' or '\0' is the
1920 					 * value for this pair.
1921 					 */
1922 					*value = '\0';
1923 					svalue = value + 1;
1924 					continue;
1925 				}
1926 			}
1927 		}
1928 
1929 		/*
1930 		 * End of String. Add the previous name=value pair to 'rv'
1931 		 */
1932 		if (svalue && addSAttr2RuleValue(rdn, svalue, rv) == -1) {
1933 			sfree(rdn);
1934 			freeRuleValue(rv, 1);
1935 			return (FAILURE);
1936 		}
1937 		sfree(rdn);
1938 	} else /* rdn  == NULL */
1939 		return (FAILURE);
1940 
1941 	/* Create the entry */
1942 	if (domain) {
1943 		if (addSAttr2RuleValue("nisDomain", domain, rv) == -1) {
1944 			freeRuleValue(rv, 1);
1945 			return (FAILURE);
1946 		}
1947 		attrs = scat(myself, F, "objectclass=nisdomainobject,",
1948 					objClassAttrs);
1949 		if (!attrs) {
1950 			freeRuleValue(rv, 1);
1951 			return (FAILURE);
1952 		}
1953 		rc = ldapAdd(dn, rv, attrs, 0);
1954 		sfree(attrs);
1955 	} else {
1956 		rc = ldapAdd(dn, rv, objClassAttrs, 0);
1957 	}
1958 
1959 	if (rc == LDAP_SUCCESS)
1960 		logmsg(MSG_NOTIMECHECK, LOG_INFO,
1961 				"%s: Entry (dn: %s) added to DIT",
1962 				myself, dn);
1963 	else if (rc == LDAP_ALREADY_EXISTS)
1964 		/* Treat this as success */
1965 		rc = LDAP_SUCCESS;
1966 	else
1967 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
1968 				"%s: ldapAdd error %d (%s) for (dn: %s)",
1969 				myself, rc, ldap_err2string(rc), dn);
1970 
1971 	freeRuleValue(rv, 1);
1972 	if (ldap_rc)
1973 		*ldap_rc = rc;
1974 	return ((rc == LDAP_SUCCESS)?SUCCESS:FAILURE);
1975 }
1976